Browse Source

Merge branch 'master' of http://8.136.199.33:3000/eta_server/eta_api into bzq1/sci_hq_custom_mcf

zqbao 6 months ago
parent
commit
c1db875ad7
66 changed files with 11517 additions and 1165 deletions
  1. 413 0
      controllers/ai/ai_pormpt.go
  2. 1607 0
      controllers/ai/ai_summary.go
  3. 8 0
      controllers/data_manage/chart_classify.go
  4. 92 63
      controllers/data_manage/chart_info.go
  5. 280 0
      controllers/data_manage/chart_info_section.go
  6. 26 2
      controllers/data_manage/chart_theme.go
  7. 1 1
      controllers/data_manage/data_manage_permission/data_move.go
  8. 2 0
      controllers/data_manage/edb_info_calculate.go
  9. 551 6
      controllers/data_manage/excel/custom_analysis.go
  10. 18 5
      controllers/data_manage/excel/excel_classify.go
  11. 15 11
      controllers/data_manage/excel/excel_info.go
  12. 320 61
      controllers/data_manage/future_good/future_good_chart_info.go
  13. 163 105
      controllers/data_manage/future_good/future_good_profit_chart_info.go
  14. 1526 0
      controllers/data_manage/manual_edb.go
  15. 15 12
      controllers/report_chapter.go
  16. 1 1
      controllers/report_v2.go
  17. 621 482
      controllers/target.go
  18. 133 0
      models/ai_summary/ai_pormpt.go
  19. 176 0
      models/ai_summary/ai_summary.go
  20. 331 0
      models/ai_summary/ai_summary_classify.go
  21. 14 0
      models/data_manage/chart_edb_mapping.go
  22. 199 1
      models/data_manage/chart_info.go
  23. 21 0
      models/data_manage/chart_info_future_good.go
  24. 374 0
      models/data_manage/chart_series.go
  25. 29 0
      models/data_manage/chart_series_edb_mapping.go
  26. 15 2
      models/data_manage/chart_theme/chart_theme_type.go
  27. 38 0
      models/data_manage/chart_type.go
  28. 78 5
      models/data_manage/data_manage_permission/excel.go
  29. 90 2
      models/data_manage/edb_info.go
  30. 3 0
      models/data_manage/edb_info_calculate.go
  31. 28 0
      models/data_manage/excel/excel_classify.go
  32. 14 4
      models/data_manage/excel/excel_info.go
  33. 7 0
      models/data_manage/excel/request/excel_info.go
  34. 7 0
      models/data_manage/excel/response/excel_info.go
  35. 18 3
      models/data_manage/excel_style.go
  36. 19 0
      models/data_manage/future_good/future_good_edb_info_data.go
  37. 5 12
      models/data_manage/future_good/request/future_good_chart.go
  38. 65 40
      models/data_manage/future_good_chart_info.go
  39. 1 0
      models/data_manage/predict_edb_info_calculate.go
  40. 7 0
      models/db.go
  41. 17 2
      models/edbdata_import_fail.go
  42. 392 0
      models/manual_edb.go
  43. 191 24
      models/target.go
  44. 351 9
      routers/commentsRouter.go
  45. 1 0
      routers/router.go
  46. 184 0
      services/ai_summary/ai_summary.go
  47. 711 2
      services/data/chart_extra_config.go
  48. 497 3
      services/data/chart_info.go
  49. 2 2
      services/data/chart_info_excel_balance.go
  50. 51 0
      services/data/chart_theme.go
  51. 109 3
      services/data/data_manage_permission/data_move.go
  52. 2 8
      services/data/edb_classify.go
  53. 45 7
      services/data/edb_info.go
  54. 35 2
      services/data/excel/excel_info.go
  55. 266 128
      services/data/future_good/chart_info.go
  56. 137 43
      services/data/future_good/profit_chart_info.go
  57. 1001 0
      services/data/manual.go
  58. 41 0
      services/eta_forum/eta_forum_hub.go
  59. 1 1
      services/report_approve.go
  60. 8 3
      services/report_v2.go
  61. 101 105
      services/smart_report.go
  62. 1 1
      services/target.go
  63. BIN
      static/template/导入模板1.xlsx
  64. BIN
      static/template/导入模板2.xlsx
  65. 38 4
      utils/common.go
  66. 4 0
      utils/constants.go

+ 413 - 0
controllers/ai/ai_pormpt.go

@@ -0,0 +1,413 @@
+package ai
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_api/models"
+	"eta/eta_api/models/ai_summary"
+	"eta/eta_api/models/system"
+	aiSummaryService "eta/eta_api/services/ai_summary"
+	"eta/eta_api/utils"
+	"time"
+)
+
+// AiPromptList
+// @Title 获取所有ai纪要分类接口-包含沙盘
+// @Description 获取所有ai纪要分类接口-包含沙盘
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /prompt/list [get]
+func (this *AiController) AiPromptList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	list, err := ai_summary.GetAiPromptList(this.SysUser.AdminId, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// PromptGroupList
+// @Title 获取可见的目录列表
+// @Description 获取可见的目录列表接口
+// @Success 200 {object} ppt_english.RespGroupList
+// @router /prompt/groups [get]
+func (this *AiController) PromptGroupList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	list, err := ai_summary.GetAiPromptShareList()
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+	resp := make([]*ai_summary.RespGroupListItem, 0)
+	publicAdminIdList := make([]int, 0)
+	publicPromptListMap := make(map[int][]*ai_summary.AiPromptItem)
+	systemAdminMap := make(map[int]*system.Admin)
+
+	for _, v := range list {
+		publicPromptList, ok := publicPromptListMap[v.SysAdminId]
+		if !ok {
+			publicPromptList = make([]*ai_summary.AiPromptItem, 0)
+			publicAdminIdList = append(publicAdminIdList, v.SysAdminId)
+
+		}
+		systemAdminMap[v.SysAdminId] = &system.Admin{
+			AdminId:  v.SysAdminId,
+			RealName: v.SysAdminName,
+		}
+		publicPromptList = append(publicPromptList, v)
+		publicPromptListMap[v.SysAdminId] = publicPromptList
+	}
+	for _, v := range publicAdminIdList {
+		systemAdmin, ok := systemAdminMap[v]
+		if !ok {
+			continue
+		}
+		respGroupPptNameListItemList, ok := publicPromptListMap[v]
+		if !ok {
+			respGroupPptNameListItemList = make([]*ai_summary.AiPromptItem, 0)
+		}
+
+		//  分组信息
+		tmpRespGroupListItem := &ai_summary.RespGroupListItem{
+			GroupId:    int64(systemAdmin.AdminId),
+			GroupName:  systemAdmin.RealName,
+			AdminId:    systemAdmin.AdminId,
+			IsShare:    1,
+			PromptList: respGroupPptNameListItemList,
+		}
+		resp = append(resp, tmpRespGroupListItem)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = resp
+	return
+}
+
+// AddPpt
+// @Title 新增提示词
+// @Description 新增提示词接口
+// @Param	request	body ppt_english.AddPptEnglishReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /prompt/add [post]
+func (this *AiController) AddPrompt() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req ai_summary.AiPromptAddReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.Title == "" {
+		br.Msg = "标题不能为空"
+		return
+	}
+
+	var msg string
+	//获取该层级下最大的排序数
+	maxSort, err := ai_summary.GetAiPromptMaxSort(this.SysUser.AdminId)
+
+	prompt := &ai_summary.AiPrompt{
+		CreateTime:    time.Now(),
+		ModifyTime:    time.Now(),
+		PromptContent: req.PromptContent,
+		SysAdminId:    this.SysUser.AdminId,
+		SysAdminName:  this.SysUser.RealName,
+		Title:         req.Title,
+		Sort:          maxSort + 1,
+	}
+	_, err = ai_summary.AddPropmt(prompt)
+	if err != nil {
+		br.Msg = "新增失败"
+		br.ErrMsg = "新增失败,Err:" + err.Error()
+		return
+	}
+
+	msg = "新增成功"
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+}
+
+// EditPpt
+// @Title 编辑提示词
+// @Description 编辑提示词接口
+// @Param	request	body ppt_english.AddPptEnglishReq true "type json string"
+// @Success 200 Ret=200 编辑成功
+// @router /prompt/edit [post]
+func (this *AiController) EditPrompt() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req ai_summary.AiPromptEditReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.Title == "" {
+		br.Msg = "标题不能为空"
+		return
+	}
+
+	promptItem := &ai_summary.AiPrompt{
+		AiPromptId:    req.AiPromptId,
+		PromptContent: req.PromptContent,
+		Title:         req.Title,
+		CreateTime:    time.Time{},
+		ModifyTime:    time.Now(),
+	}
+
+	err = promptItem.Update([]string{"ModifyTime", "PromptContent", "Title"})
+	if err != nil {
+		br.Msg = "编辑失败"
+		br.ErrMsg = "编辑失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "编辑成功"
+	br.IsAddLog = true
+}
+
+// DeletePpt
+// @Title 删除提示词
+// @Description 删除ppt接口
+// @Param	request	body ppt_english.DeletePptEnglishReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /prompt/delete [post]
+func (this *AiController) DeletePrompt() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req ai_summary.DeleteAipromptReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.AiPromptId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	promptInfo, err := ai_summary.GetAiPromptById(req.AiPromptId)
+	if err != nil {
+		br.Msg = "提示词不存在"
+		return
+	}
+	if promptInfo.SysAdminId != this.SysUser.AdminId {
+		br.Msg = "无权删除"
+		return
+	}
+	err = ai_summary.DelAiPromptyId(req.AiPromptId)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+	br.Msg = "删除成功"
+}
+
+// DetailPrompt
+// @Title 获取提示词详情
+// @Description 获取提示词详情接口
+// @Param   AiPromptId   query   int  true       "提示词id"
+// @Success 200 {object} ppt_english.PptEnglish
+// @router /prompt/detail [get]
+func (this *AiController) DetailPrompt() {
+	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
+	}
+	aiPromptId, _ := this.GetInt("AiPromptId")
+
+	promptInfo, err := ai_summary.GetAiPromptById(aiPromptId)
+	if err != nil {
+		br.Msg = "提示词不存在"
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = promptInfo
+}
+
+// MoveAiPrompt
+// @Title 移动提示词
+// @Description 移动提示词接口
+// @Param	request	body models.ReqMoveGroupPpt true "type json string"
+// @Success 200 Ret=200 移动成功
+// @router /prompt/move [post]
+func (this *AiController) MoveAiPrompt() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req ai_summary.ReqMovePrompt
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.AiPromptId <= 0 {
+		br.Msg = "请输入提示词ID"
+		return
+	}
+
+	prompt, err := ai_summary.GetAiPromptById(req.AiPromptId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "当前提示词不存在"
+			br.ErrMsg = "当前提示词不存在"
+			return
+		}
+		br.Msg = "提示词查询出错"
+		br.ErrMsg = "提示词查询出错:" + err.Error()
+		return
+	}
+	var updateStr []string
+
+	var currentSort, prevSort, nextSort int
+	currentSort = prompt.Sort
+
+	var prevPrompt *ai_summary.AiPrompt
+	var nextPrompt *ai_summary.AiPrompt
+	if req.PrevAiPromptId > 0 {
+		prevPrompt, err = ai_summary.GetAiPromptById(req.PrevAiPromptId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				err = errors.New("目录下的提示词不存在")
+				return
+			}
+			err = errors.New("目录下的提示词查询出错:" + err.Error())
+			return
+		}
+		prevSort = prevPrompt.Sort
+	}
+
+	if req.NextAiPromptId > 0 {
+		nextPrompt, err = ai_summary.GetAiPromptById(req.NextAiPromptId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				err = errors.New("目录下的提示词不存在")
+				return
+			}
+			err = errors.New("目录下的提示词查询出错:" + err.Error())
+			return
+		}
+		nextSort = nextPrompt.Sort
+	}
+
+	updateStr = append(updateStr, "sort")
+
+	//移到两个排序值中间操作
+	if prevSort >= currentSort {
+		//往下移动
+		err = ai_summary.MoveDownPromptBySort(this.SysUser.AdminId, prevSort, currentSort)
+		if err != nil {
+			err = errors.New("向下移动提示词出错:" + err.Error())
+			return
+		}
+		prompt.Sort = prevSort
+	} else if nextSort <= currentSort && nextSort != 0 {
+		//往上移动
+		err = ai_summary.MoveUpPromptBySort(this.SysUser.AdminId, nextSort, currentSort)
+		if err != nil {
+			err = errors.New("向上移动提示词出错:" + err.Error())
+			return
+		}
+		prompt.Sort = nextSort
+	}
+	//更新当前排序
+	err = prompt.Update(updateStr)
+	if err != nil {
+		err = errors.New("移动提示词出错:" + err.Error())
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "移动成功"
+}
+
+// SharePrompt
+// @Title 共享/取消共享单个提示词
+// @Description 共享/取消共享单个提示词接口
+// @Param	request	body models.ReqPptShare true "type json string"
+// @Success 200 {object} models.RespPptShare
+// @router /prompt/share [post]
+func (this *AiController) SharePrompt() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req ai_summary.DeleteAipromptReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.AiPromptId <= 0 {
+		br.Msg = "请输入提示词序号"
+		return
+	}
+
+	err = aiSummaryService.SharePrompt(req.AiPromptId, this.SysUser.AdminId)
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+
+	return
+}

+ 1607 - 0
controllers/ai/ai_summary.go

@@ -0,0 +1,1607 @@
+package ai
+
+import (
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/models/ai_summary"
+	"eta/eta_api/models/aimod"
+	"eta/eta_api/models/sandbox"
+	saModel "eta/eta_api/models/semantic_analysis"
+	aiSummaryService "eta/eta_api/services/ai_summary"
+	"eta/eta_api/services/aiser"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strconv"
+	"time"
+)
+
+// AiSummaryClassifyItems
+// @Title 获取所有ai纪要分类接口
+// @Description 获取所有ai纪要分类接口
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /summary/classify/list [get]
+func (this *AiController) AiSummaryClassifyItems() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	resp := new(ai_summary.AiSummaryClassifyListResp)
+	aiSummaryClassifyId, _ := this.GetInt("AiSummaryClassifyId")
+
+	isShowMe, _ := this.GetBool("IsShowMe")
+	if isShowMe {
+		errMsg, err := aiSummaryService.GetAiSummaryClassifyListForMe(*this.SysUser, resp, aiSummaryClassifyId)
+		if err != nil {
+			br.Msg = errMsg
+			br.ErrMsg = err.Error()
+			return
+		}
+
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		fmt.Println("source my classify")
+		return
+	}
+
+	rootList, err := ai_summary.GetAiSummaryClassifyAndInfoByParentId(aiSummaryClassifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	for k := range rootList {
+		rootList[k].UniqueCode = strconv.Itoa(rootList[k].AiSummaryClassifyId) + "_" + strconv.Itoa(rootList[k].AiSummaryId)
+	}
+
+	classifyAll, err := ai_summary.GetAiSummaryClassifyAndInfoByParentId(aiSummaryClassifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	for k := range classifyAll {
+		classifyAll[k].UniqueCode = strconv.Itoa(classifyAll[k].AiSummaryClassifyId) + "_" + strconv.Itoa(classifyAll[k].AiSummaryId)
+	}
+
+	nodeAll := make([]*ai_summary.AiSummaryClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		aiSummaryService.AiSummaryClassifyItemsMakeTreeV2(this.SysUser, classifyAll, rootNode)
+		nodeAll = append(nodeAll, rootNode)
+	}
+
+	resp.AllNodes = nodeAll
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 新增ai纪要分类
+// @Description 新增ai纪要分类接口
+// @Param	request	body data_manage.AddChartClassifyReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /summary/classify/add [post]
+func (this *AiController) AddAiSummaryClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req ai_summary.AddAiSummaryClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+	if req.ParentId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	count, err := ai_summary.GetAiSummaryClassifyCount(req.ClassifyName, req.ParentId)
+	if err != nil {
+		br.Msg = "判断名称是否已存在失败"
+		br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+		return
+	}
+	if count > 0 {
+		br.Msg = "分类名称已存在,请重新输入"
+		br.IsSendEmail = false
+		return
+	}
+	//获取该层级下最大的排序数
+	maxSort, err := ai_summary.GetAiSummaryClassifyMaxSort(req.ParentId)
+
+	classify := new(ai_summary.AiSummaryClassify)
+	classify.ParentId = req.ParentId
+	classify.ClassifyName = req.ClassifyName
+	classify.HasData = 0
+	classify.CreateTime = time.Now()
+	classify.ModifyTime = time.Now()
+	classify.SysUserId = this.SysUser.AdminId
+	classify.SysUserRealName = this.SysUser.RealName
+	classify.Level = req.Level + 1
+	classify.Sort = maxSort + 1
+
+	_, err = ai_summary.AddAiSummaryClassify(classify)
+	if err != nil {
+		br.Msg = "保存分类失败"
+		br.ErrMsg = "保存分类失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+}
+
+// @Title 修改ai纪要分类
+// @Description 修改ai纪要分类接口
+// @Param	request	body data_manage.EditChartClassifyReq true "type json string"
+// @Success 200 Ret=200 修改成功
+// @router /summary/classify/edit [post]
+func (this *AiController) EditAiSummaryClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req ai_summary.EditAiSummaryClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.AiSummaryClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	err = ai_summary.EditSandboxClassify(req.AiSummaryClassifyId, req.ClassifyName)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// @Title 删除纪要分类检测接口
+// @Description 删除纪要分类检测接口
+// @Param	request	body data_manage.ChartClassifyDeleteCheckResp true "type json string"
+// @Success 200 Ret=200 检测成功
+// @router /summary/classify/delete/check [post]
+func (this *AiController) DeleteSandboxClassifyCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req ai_summary.AiSummaryClassifyDeleteCheckReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.AiSummaryClassifyId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+	var deleteStatus int
+	var tipsMsg string
+	//删除分类
+	if req.AiSummaryClassifyId > 0 {
+		//判断分类下,是否含有纪要
+		count, err := ai_summary.GetAiSummaryInfoCountByClassifyId(req.AiSummaryClassifyId)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "分类下是否含有指标失败,Err:" + err.Error()
+			return
+		}
+
+		if count > 0 {
+			deleteStatus = 1
+			tipsMsg = "该分类下关联沙盘不可删除"
+		}
+	}
+
+	if deleteStatus != 1 {
+		classifyCount, err := ai_summary.GetAiSummaryInfoCountByClassifyId(req.AiSummaryClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "删除失败"
+			br.ErrMsg = "分类下是否含有沙盘失败,Err:" + err.Error()
+			return
+		}
+		if classifyCount > 0 {
+			deleteStatus = 2
+			tipsMsg = "确认删除当前目录及包含的子目录吗"
+		}
+	}
+	if deleteStatus == 0 {
+		tipsMsg = "可删除,进行删除操作"
+	}
+
+	resp := new(sandbox.SandboxClassifyDeleteCheckResp)
+	resp.DeleteStatus = deleteStatus
+	resp.TipsMsg = tipsMsg
+	br.Ret = 200
+	br.Msg = "检测成功"
+	br.Success = true
+	br.Data = resp
+}
+
+// @Title 删除纪要分类/纪要
+// @Description 删除纪要分类/纪要接口
+// @Param	request	body data_manage.DeleteChartClassifyReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /summary/classify/delete [post]
+func (this *AiController) DeleteAiSummaryClassify() {
+	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 ai_summary.DeleteAiSummaryClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.AiSummaryClassifyId < 0 && req.AiSummaryId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	//删除分类
+	if req.AiSummaryClassifyId > 0 && req.AiSummaryId == 0 {
+		//判断是否含有纪要
+		count, err := ai_summary.GetAiSummaryInfoCountByClassifyId(req.AiSummaryClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "删除失败"
+			br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+			return
+		}
+
+		if count > 0 {
+			br.Msg = "该目录下存在纪要,不可删除"
+			br.IsSendEmail = false
+			return
+		}
+
+		err = ai_summary.DeleteAiSummaryClassify(req.AiSummaryClassifyId)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	//删除纪要
+	if req.AiSummaryId > 0 {
+		summaryInfo, err := ai_summary.GetAiSummaryById(req.AiSummaryId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "纪要已删除,请刷新页面"
+				br.ErrMsg = "指标不存在,Err:" + err.Error()
+				return
+			} else {
+				br.Msg = "删除失败"
+				br.ErrMsg = "删除失败,获取指标信息失败,Err:" + err.Error()
+				return
+			}
+		}
+		if summaryInfo == nil {
+			br.Msg = "纪要已删除,请刷新页面"
+			return
+		}
+		if summaryInfo.SysUserId != sysUser.AdminId {
+			br.Msg = "仅纪要创建人可以删除"
+			return
+		}
+		err = ai_summary.DelAiSummaryById(req.AiSummaryId)
+		if err != nil {
+			br.Msg = err.Error()
+			return
+		}
+	}
+	br.Ret = 200
+	br.Msg = "删除成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// AiSummaryClassifyMove
+// @Title 纪要分类移动接口
+// @Description 纪要分类移动接口
+// @Success 200 {object} data_manage.MoveChartClassifyReq
+// @router /summary/classify/move [post]
+func (this *AiController) AiSummaryClassifyMove() {
+	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 ai_summary.MoveAiSummaryClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.AiSummaryClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "分类id小于等于0"
+		return
+	}
+	//判断分类是否存在
+	aiSummaryClassifyInfo, err := ai_summary.GetAiSummaryClassifyById(req.AiSummaryClassifyId)
+	if err != nil {
+		br.Msg = "移动失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+
+	updateCol := make([]string, 0)
+
+	// 判断移动的是分类还是沙盘
+	if req.AiSummaryId > 0 {
+		//判断分类是否存在
+		count, _ := ai_summary.GetAiSummaryClassifyCountById(req.AiSummaryClassifyId)
+		if count <= 0 {
+			br.Msg = "分类已被删除,不可移动,请刷新页面"
+			return
+		}
+
+		aiSummaryInfo, err := ai_summary.GetAiSummaryById(req.AiSummaryId)
+		if err != nil {
+			br.Msg = "移动失败"
+			br.ErrMsg = "获取沙盘信息失败,Err:" + err.Error()
+			return
+		}
+
+		//如果改变了分类,那么移动该图表数据
+		// 11/22 ETA逻辑优化去除名称重复限制
+		if aiSummaryInfo.ClassifyId != req.ParentClassifyId {
+			////查询需要修改的分类下是否存在同一个图表名称
+			//tmpSandboxInfo, tmpErr := sandbox.GetSandboxByClassifyIdAndName(req.ParentClassifyId, aiSummaryInfo.Name)
+			//if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			//	br.Msg = "移动失败"
+			//	br.ErrMsg = "移动失败,Err:" + tmpErr.Error()
+			//	return
+			//}
+			//if tmpSandboxInfo != nil {
+			//	br.Msg = "移动失败,同一个分类下沙盘名称不允许重复"
+			//	br.ErrMsg = "移动失败,同一个分类下沙盘名称不允许重复"
+			//	return
+			//}
+			err = ai_summary.MoveAiSummary(req.AiSummaryId, req.ParentClassifyId)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "移动失败,Err:" + err.Error()
+				return
+			}
+		}
+
+		//移动排序
+		updateCol := make([]string, 0)
+		//如果有传入 上一个兄弟节点分类id
+		if req.PrevId > 0 {
+			if req.PrevType == 1 {
+				//上一个兄弟节点
+				prevClassify, err := ai_summary.GetAiSummaryClassifyById(req.PrevId)
+				if err != nil {
+					br.Msg = "移动失败"
+					br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+					return
+				}
+
+				//如果是移动在两个兄弟节点之间
+				if req.NextId > 0 {
+					if req.NextType == 1 {
+						//上一个节点是分类 下一个节点是分类的情况
+						//下一个兄弟节点
+						nextClassify, err := ai_summary.GetAiSummaryClassifyById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+						if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == aiSummaryClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevClassify.ParentId, prevClassify.AiSummaryClassifyId, prevClassify.Sort, updateSortStr)
+							_ = ai_summary.UpdateAiSummarySortByClassifyId(prevClassify.AiSummaryClassifyId, prevClassify.Sort, 0, updateSortStr)
+						} else {
+							//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+							if nextClassify.Sort-prevClassify.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr)
+								_ = ai_summary.UpdateAiSummarySortByClassifyId(prevClassify.AiSummaryClassifyId, prevClassify.Sort, 0, updateSortStr)
+							}
+						}
+					} else {
+						//上一个节点是分类 下一个节点是沙盘的情况
+						//下一个兄弟节点
+						nextChartInfo, err := ai_summary.GetAiSummaryById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(分类)与下一个兄弟(沙盘)的排序权重是一致的,那么需要将下一个兄弟(沙盘)(以及下个兄弟(沙盘)的同样排序权重)的排序权重+2,自己变成上一个兄弟(分类)的排序权重+1
+						if prevClassify.Sort == nextChartInfo.Sort || prevClassify.Sort == aiSummaryInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevClassify.ParentId, prevClassify.AiSummaryClassifyId, prevClassify.Sort, updateSortStr)
+							_ = ai_summary.UpdateAiSummarySortByClassifyId(prevClassify.AiSummaryClassifyId, prevClassify.Sort, 0, updateSortStr)
+						} else {
+							//如果下一个兄弟(沙盘)的排序权重正好是上个兄弟节点(分类)的下一层,那么需要再加一层了
+							if nextChartInfo.Sort-prevClassify.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.AiSummaryClassifyId, updateSortStr)
+								_ = ai_summary.UpdateAiSummarySortByClassifyId(prevClassify.AiSummaryClassifyId, prevClassify.Sort, 0, updateSortStr)
+							}
+						}
+					}
+
+				}
+
+				aiSummaryInfo.Sort = prevClassify.Sort + 1
+				aiSummaryInfo.ModifyTime = time.Now()
+				updateCol = append(updateCol, "Sort", "ModifyTime")
+
+			} else {
+				prevAiSumary, err := ai_summary.GetAiSummaryById(req.PrevId)
+				if err != nil {
+					br.Msg = "移动失败"
+					br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+					return
+				}
+
+				//如果是移动在两个兄弟节点之间
+				if req.NextId > 0 {
+					if req.NextType == 1 {
+						//上一个节点是沙盘 下一个节点是分类的情况
+						//下一个兄弟节点
+						nextClassify, err := ai_summary.GetAiSummaryClassifyById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(沙盘)与下一个兄弟(分类)的排序权重是一致的,那么需要将下一个兄弟(分类)(以及下个兄弟(分类)的同样排序权重)的排序权重+2,自己变成上一个兄弟(沙盘)的排序权重+1
+						if prevAiSumary.Sort == nextClassify.Sort || prevAiSumary.Sort == aiSummaryClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevAiSumary.ClassifyId, 0, prevAiSumary.Sort, updateSortStr)
+							_ = ai_summary.UpdateAiSummarySortByClassifyId(prevAiSumary.ClassifyId, prevAiSumary.Sort, prevAiSumary.AiSummaryId, updateSortStr)
+						} else {
+							//如果下一个兄弟(分类)的排序权重正好是上个兄弟(沙盘)节点的下一层,那么需要再加一层了
+							if nextClassify.Sort-prevAiSumary.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevAiSumary.ClassifyId, 0, prevAiSumary.Sort, updateSortStr)
+								_ = ai_summary.UpdateAiSummarySortByClassifyId(prevAiSumary.ClassifyId, prevAiSumary.Sort, prevAiSumary.AiSummaryId, updateSortStr)
+							}
+						}
+					} else {
+						//上一个节点是沙盘 下一个节点是沙盘的情况
+						//下一个兄弟节点
+						nextChartInfo, err := ai_summary.GetAiSummaryById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(沙盘)与下一个兄弟(分类)的排序权重是一致的,那么需要将下一个兄弟(分类)(以及下个兄弟(分类)的同样排序权重)的排序权重+2,自己变成上一个兄弟(沙盘)的排序权重+1
+						if prevAiSumary.Sort == nextChartInfo.Sort || prevAiSumary.Sort == aiSummaryInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevAiSumary.ClassifyId, 0, prevAiSumary.Sort, updateSortStr)
+							_ = ai_summary.UpdateAiSummarySortByClassifyId(prevAiSumary.ClassifyId, prevAiSumary.Sort, prevAiSumary.AiSummaryId, updateSortStr)
+						} else {
+							//如果下一个兄弟(分类)的排序权重正好是上个兄弟(沙盘)节点的下一层,那么需要再加一层了
+							if nextChartInfo.Sort-prevAiSumary.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevAiSumary.ClassifyId, 0, prevAiSumary.Sort, updateSortStr)
+								_ = ai_summary.UpdateAiSummarySortByClassifyId(prevAiSumary.ClassifyId, prevAiSumary.Sort, prevAiSumary.AiSummaryId, updateSortStr)
+							}
+						}
+					}
+
+				}
+
+				aiSummaryInfo.Sort = prevAiSumary.Sort + 1
+				aiSummaryInfo.ModifyTime = time.Now()
+				updateCol = append(updateCol, "Sort", "ModifyTime")
+			}
+
+		} else {
+			// prevId为0,也就是沙盘移到最前端
+			firstClassify, err := ai_summary.GetFirstAiSummaryByClassifyId(req.AiSummaryClassifyId)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "移动失败"
+				br.ErrMsg = "获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + err.Error()
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = ai_summary.UpdateAiSummarySortByClassifyId(firstClassify.ClassifyId, 0, firstClassify.AiSummaryId-1, updateSortStr)
+			}
+
+			aiSummaryInfo.Sort = 0 //那就是排在第一位
+			aiSummaryInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = aiSummaryInfo.Update(updateCol)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "修改失败,Err:" + err.Error()
+				return
+			}
+		}
+	} else {
+		//移动的是分类
+		//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+		if aiSummaryClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId != 0 {
+			parentChartClassifyInfo, err := ai_summary.GetAiSummaryClassifyById(req.ParentClassifyId)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
+				return
+			}
+			aiSummaryClassifyInfo.ParentId = parentChartClassifyInfo.AiSummaryClassifyId
+			aiSummaryClassifyInfo.Level = parentChartClassifyInfo.Level + 1
+			aiSummaryClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+		} else if aiSummaryClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId == 0 {
+			//改为一级分类
+			aiSummaryClassifyInfo.ParentId = req.ParentClassifyId
+			aiSummaryClassifyInfo.Level = 1
+			aiSummaryClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+		}
+
+		//如果有传入 上一个兄弟节点分类id
+		if req.PrevId > 0 {
+			if req.PrevType == 1 {
+				//上一个节点是分类
+				//上一个兄弟节点
+				prevClassify, err := ai_summary.GetAiSummaryClassifyById(req.PrevId)
+				if err != nil {
+					br.Msg = "移动失败"
+					br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+					return
+				}
+
+				//如果是移动在两个兄弟节点之间
+				if req.NextId > 0 {
+					if req.NextType == 1 {
+						//上一个节点是分类 下一个节点是分类的情况
+						//下一个兄弟节点
+						nextClassify, err := ai_summary.GetAiSummaryClassifyById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+						if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == aiSummaryClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevClassify.ParentId, prevClassify.AiSummaryClassifyId, prevClassify.Sort, updateSortStr)
+							_ = ai_summary.UpdateAiSummarySortByClassifyId(prevClassify.AiSummaryClassifyId, prevClassify.Sort, 0, updateSortStr)
+						} else {
+							//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+							if nextClassify.Sort-prevClassify.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr)
+								_ = ai_summary.UpdateAiSummarySortByClassifyId(prevClassify.AiSummaryClassifyId, prevClassify.Sort, 0, updateSortStr)
+							}
+						}
+					} else {
+						//上一个节点是分类 下一个节点是沙盘的情况
+						//下一个兄弟节点
+						nextChartInfo, err := ai_summary.GetAiSummaryById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(分类)与下一个兄弟(沙盘)的排序权重是一致的,那么需要将下一个兄弟(沙盘)(以及下个兄弟(沙盘)的同样排序权重)的排序权重+2,自己变成上一个兄弟(分类)的排序权重+1
+						if prevClassify.Sort == nextChartInfo.Sort || prevClassify.Sort == aiSummaryClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevClassify.ParentId, prevClassify.AiSummaryClassifyId, prevClassify.Sort, updateSortStr)
+							_ = ai_summary.UpdateAiSummarySortByClassifyId(prevClassify.AiSummaryClassifyId, prevClassify.Sort, 0, updateSortStr)
+						} else {
+							//如果下一个兄弟(沙盘)的排序权重正好是上个兄弟节点(分类)的下一层,那么需要再加一层了
+							if nextChartInfo.Sort-prevClassify.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.AiSummaryClassifyId, updateSortStr)
+								_ = ai_summary.UpdateAiSummarySortByClassifyId(prevClassify.AiSummaryClassifyId, prevClassify.Sort, 0, updateSortStr)
+							}
+						}
+					}
+
+				}
+
+				aiSummaryClassifyInfo.Sort = prevClassify.Sort + 1
+				aiSummaryClassifyInfo.ModifyTime = time.Now()
+				updateCol = append(updateCol, "Sort", "ModifyTime")
+
+			} else {
+				//上一个节点是沙盘
+				prevAiSumary, err := ai_summary.GetAiSummaryById(req.PrevId)
+				if err != nil {
+					br.Msg = "移动失败"
+					br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+					return
+				}
+
+				//如果是移动在两个兄弟节点之间
+				if req.NextId > 0 {
+					if req.NextType == 1 {
+						//上一个节点是沙盘 下一个节点是分类的情况
+						//下一个兄弟节点
+						nextClassify, err := ai_summary.GetAiSummaryClassifyById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(沙盘)与下一个兄弟(分类)的排序权重是一致的,那么需要将下一个兄弟(分类)(以及下个兄弟(分类)的同样排序权重)的排序权重+2,自己变成上一个兄弟(沙盘)的排序权重+1
+						if prevAiSumary.Sort == nextClassify.Sort || prevAiSumary.Sort == aiSummaryClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevAiSumary.ClassifyId, 0, prevAiSumary.Sort, updateSortStr)
+							_ = ai_summary.UpdateAiSummarySortByClassifyId(prevAiSumary.ClassifyId, prevAiSumary.Sort, prevAiSumary.AiSummaryId, updateSortStr)
+						} else {
+							//如果下一个兄弟(分类)的排序权重正好是上个兄弟(沙盘)节点的下一层,那么需要再加一层了
+							if nextClassify.Sort-prevAiSumary.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevAiSumary.ClassifyId, 0, prevAiSumary.Sort, updateSortStr)
+								_ = ai_summary.UpdateAiSummarySortByClassifyId(prevAiSumary.ClassifyId, prevAiSumary.Sort, prevAiSumary.AiSummaryId, updateSortStr)
+							}
+						}
+					} else {
+						//上一个节点是沙盘 下一个节点是沙盘的情况
+						//下一个兄弟节点
+						nextChartInfo, err := ai_summary.GetAiSummaryById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(沙盘)与下一个兄弟(分类)的排序权重是一致的,那么需要将下一个兄弟(分类)(以及下个兄弟(分类)的同样排序权重)的排序权重+2,自己变成上一个兄弟(沙盘)的排序权重+1
+						if prevAiSumary.Sort == nextChartInfo.Sort || prevAiSumary.Sort == aiSummaryClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevAiSumary.ClassifyId, 0, prevAiSumary.Sort, updateSortStr)
+							_ = ai_summary.UpdateAiSummarySortByClassifyId(prevAiSumary.ClassifyId, prevAiSumary.Sort, prevAiSumary.AiSummaryId, updateSortStr)
+						} else {
+							//如果下一个兄弟(分类)的排序权重正好是上个兄弟(沙盘)节点的下一层,那么需要再加一层了
+							if nextChartInfo.Sort-prevAiSumary.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = ai_summary.UpdateAiSummaryClassifySortByParentId(prevAiSumary.ClassifyId, 0, prevAiSumary.Sort, updateSortStr)
+								_ = ai_summary.UpdateAiSummarySortByClassifyId(prevAiSumary.ClassifyId, prevAiSumary.Sort, prevAiSumary.AiSummaryId, updateSortStr)
+							}
+						}
+					}
+
+				}
+				aiSummaryClassifyInfo.Sort = prevAiSumary.Sort + 1
+				aiSummaryClassifyInfo.ModifyTime = time.Now()
+				updateCol = append(updateCol, "Sort", "ModifyTime")
+
+			}
+
+		} else {
+			firstClassify, err := ai_summary.GetFirstAiSummaryClassifyByParentId(aiSummaryClassifyInfo.ParentId)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "移动失败"
+				br.ErrMsg = "获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + err.Error()
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = ai_summary.UpdateAiSummaryClassifySortByParentId(firstClassify.ParentId, firstClassify.AiSummaryClassifyId-1, 0, updateSortStr)
+			}
+
+			aiSummaryClassifyInfo.Sort = 0 //那就是排在第一位
+			aiSummaryClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = aiSummaryClassifyInfo.Update(updateCol)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "修改失败,Err:" + err.Error()
+				return
+			}
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "移动成功"
+}
+
+// @Title Ai纪要列表接口
+// @Description Ai纪要列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   AiSummaryClassifyId   query   int  true       "分类id"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} data_manage.ChartListResp
+// @router /summary/list [get]
+func (this *AiController) AiSummaryList() {
+	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
+	}
+
+	aiSummaryClassifyId, _ := this.GetInt("AiSummaryClassifyId")
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyWord := this.GetString("KeyWord")
+
+	var total int
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	if aiSummaryClassifyId > 0 {
+		sandboxClassifyIds, err := ai_summary.GetAiSummaryClassify(aiSummaryClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取图表信息失败"
+			br.ErrMsg = "获取信息失败,GetChartClassify,Err:" + err.Error()
+			return
+		}
+		condition += " AND ai_summary_classify_id IN(" + sandboxClassifyIds + ") "
+		//pars = append(pars, chartClassifyId)
+	}
+	if keyWord != "" {
+		condition += ` AND  ( title LIKE '%` + keyWord + `%' )`
+	}
+
+	//只看我的
+	isShowMe, _ := this.GetBool("IsShowMe")
+	if isShowMe {
+		condition += ` AND sys_user_id = ? `
+		pars = append(pars, sysUser.AdminId)
+	}
+
+	//获取图表信息
+	list, err := ai_summary.GetAiSummaryListByCondition(condition, pars, startSize, pageSize)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Success = true
+		br.Msg = "获取纪要信息失败"
+		br.ErrMsg = "获取纪要信息失败,Err:" + err.Error()
+		return
+	}
+
+	for i, v := range list {
+		ids, err := ai_summary.GetAiSummaryAllParentByClassifyId(v.ClassifyId)
+		if err != nil {
+			br.Msg = "获取父级信息错误!"
+			br.ErrMsg = "获取父级信息错误,Err:" + err.Error()
+			return
+		}
+		list[i].ParentIds = ids
+	}
+	resp := new(ai_summary.AiSummaryListResp)
+	if list == nil || len(list) <= 0 || (err != nil && err.Error() == utils.ErrNoRow()) {
+		items := make([]*ai_summary.AiSummaryItems, 0)
+		resp.Paging = page
+		resp.List = items
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	dataCount, err := ai_summary.GetAiSummaryListCountByCondition(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标数据总数失败,Err:" + err.Error()
+		return
+	}
+	page = paging.GetPaging(currentIndex, pageSize, dataCount)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title Ai纪要详情接口
+// @Description Ai纪要详情接口
+// @Param   AiSummaryId   query   int  true       "详情id"
+// @Success 200 {object} data_manage.ChartListResp
+// @router /summary/detail [get]
+func (this *AiController) AiSummaryDetail() {
+	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
+	}
+
+	aiSummaryId, _ := this.GetInt("AiSummaryId")
+
+	detail, err := ai_summary.GetAiSummaryItemById(aiSummaryId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Success = true
+		br.Msg = "获取沙盘信息失败"
+		br.ErrMsg = "获取沙盘信息失败,Err:" + err.Error()
+		return
+	}
+	resp := ai_summary.AiSummaryDetailResp{
+		AiSummaryItems: detail,
+	}
+
+	if detail.SaDocId >0 {
+		item := new(saModel.SaDoc)
+		e := item.GetItemById(detail.SaDocId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "文档已被删除, 请刷新页面"
+				return
+			}
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取文档信息失败, Err: " + e.Error()
+			return
+		}
+		resp.SaDocTitle = item.Title
+		resp.SaDocClassifyId = item.ClassifyId
+	}
+
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// @Title 新增ai纪要
+// @Description 新增ai纪要接口
+// @Param	request	body data_manage.AddChartClassifyReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /summary/add [post]
+func (this *AiController) AddAiSummary() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req ai_summary.AddAiSummaryReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.Title == "" {
+		br.Msg = "请输入名称"
+		br.IsSendEmail = false
+		return
+	}
+	if req.SummaryContent == "" {
+		br.Msg = "纪要内容为空"
+		br.IsSendEmail = false
+		return
+	}
+	if req.ClassifyId < 0 {
+		br.Msg = "分类id错误"
+		br.IsSendEmail = false
+		return
+	}
+	//获取该层级下最大的排序数
+	maxSort, err := ai_summary.GetAiSummaryMaxSort(req.ClassifyId)
+
+	summary := &ai_summary.AiSummary{
+		SaDocId:         req.SaDocId,
+		OriginContent:   req.OriginContent,
+		SummaryContent:  req.SummaryContent,
+		ClassifyId:      req.ClassifyId,
+		SysUserId:       this.SysUser.AdminId,
+		SysUserRealName: this.SysUser.RealName,
+		Title:           req.Title,
+		CreateTime:      time.Now(),
+		ModifyTime:      time.Now(),
+		OpenaiFileName:  req.OpenaiFileName,
+		OpenaiFilePath:  req.OpenaiFilePath,
+		OriginTitle:     req.OriginTitle,
+		Sort:            maxSort + 1,
+	}
+
+	id, err := ai_summary.AddAiSummary(summary)
+	if err != nil {
+		br.Msg = "保存分类失败"
+		br.ErrMsg = "保存分类失败,Err:" + err.Error()
+		return
+	}
+	classify ,err := ai_summary.GetAiSummaryClassifyById(req.ClassifyId)
+	if err != nil {
+		br.Msg = "获取分类信息失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+
+	resp := ai_summary.AiSummaryAddResp{
+		AiSummaryId: int(id),
+		UniqueCode:  strconv.Itoa(req.ClassifyId) + "_" + strconv.Itoa(int(id)),
+		ParentId:    classify.ParentId,
+	}
+
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+	br.Data = resp
+}
+
+// @Title 生成ai纪要
+// @Description 生成ai纪要接口
+// @Param	request	body data_manage.AddChartClassifyReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /summary/generate [post]
+func (this *AiController) GenerateAiSummary() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req ai_summary.GenerateAiSummaryReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.Prompt == "" {
+		br.Msg = "请输入提示词"
+		br.IsSendEmail = false
+		return
+	}
+	if req.OriginContent == "" && len(req.OpenaiFileId) == 0 && req.SaDocId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+	if req.SaDocId > 0 {
+		// 获取段落
+		sectionOB := new(saModel.SaDocSection)
+		sectionCond := fmt.Sprintf(` AND %s = ?`, saModel.SaDocSectionColumns.DocId)
+		sectionPars := make([]interface{}, 0)
+		sectionPars = append(sectionPars, req.SaDocId)
+		sectionList, e := sectionOB.GetItemsByCondition(sectionCond, sectionPars, []string{}, "")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取段落信息失败, Err: " + e.Error()
+			return
+		}
+		secIds := make([]int, 0)
+		for _, s := range sectionList {
+			secIds = append(secIds, s.SaDocSectionId)
+		}
+
+		for _, v := range sectionList {
+			req.OriginContent += v.Content + "<br>"
+		}
+	}
+
+	if utils.Re == nil {
+		key := "CACHE_CHAT_" + strconv.Itoa(this.SysUser.AdminId)
+		cacheVal, err := utils.Rc.RedisInt(key)
+		fmt.Println("RedisString:", cacheVal, "err:", err)
+		if err != nil && (err.Error() != "redigo: nil returned" && err.Error() != "redis: nil") {
+			br.Msg = "获取数据失败!"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		putVal := 0
+		if cacheVal <= 0 {
+			putVal = utils.AiChatLimit
+		} else {
+			putVal = cacheVal - 1
+		}
+
+		if putVal <= 0 {
+			br.Msg = "您今日" + strconv.Itoa(utils.AiChatLimit) + "次问答已达上限,请明天再来!"
+			br.ErrMsg = "您今日" + strconv.Itoa(utils.AiChatLimit) + "次问答已达上限,请明天再来!"
+			return
+		}
+		lastSecond := utils.GetTodayLastSecond()
+		utils.Rc.Put(key, putVal, lastSecond)
+	}
+
+	resp := new(aimod.ChatResp)
+
+	if req.OriginContent != "" {
+		// 直接提问的方式
+
+		//根据提问,获取信息
+		ask := req.Prompt + req.OriginContent
+
+		var answer string
+		//answerArr := []string{
+		//	"周度数据显示,成品油现货市场价格跟随原油下跌,但近期相对抗跌,裂解价差走扩。批零价差方面汽油收窄,柴油走扩",
+		//	"出口利润在原油下跌海外成品油矛盾更大的情况下汽柴油出口窗口完全关闭",
+		//	"汽油需求在经历五一假期的一段高峰后将回归平稳,总体没有明显矛盾,后期我们担心更多的还是柴油。"}
+		//获取主题下的所有信息
+		//AiChatTopicId
+
+		//获取主题下的所有信息
+		//AiChatTopicId
+		historyList, err := aimod.GetAiChatList(req.AiChatTopicId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取主题历史数据失败!"
+			br.ErrMsg = "获取主题历史数据失败,Err:" + err.Error()
+			return
+		}
+
+		historyChatList := make([]aimod.HistoryChat, 0)
+		for _, v := range historyList {
+			historyChat := new(aimod.HistoryChat)
+			historyChat.Ask = v.Ask
+			historyChat.Answer = v.Answer
+			historyChatList = append(historyChatList, *historyChat)
+		}
+
+		answer, err = aiser.ChatAutoMsg(ask, historyChatList, req.Model)
+		if err != nil {
+			br.Msg = "获取数据失败!"
+			br.ErrMsg = "获取数据失败,ChatAutoMsg,Err:" + err.Error()
+			return
+		}
+
+		resp.Ask = ask
+		resp.Answer = answer
+		resp.Model = req.Model
+
+		if req.AiChatTopicId <= 0 { //新增
+			topic := new(aimod.AiChatTopic)
+			topic.TopicName = ask
+			topic.SysUserId = this.SysUser.AdminId
+			topic.SysUserRealName = this.SysUser.RealName
+			topic.CreateTime = time.Now()
+			topic.ModifyTime = time.Now()
+			topicId, err := aimod.AddAiChatTopic(topic)
+			if err != nil {
+				br.Msg = "获取数据失败!"
+				br.ErrMsg = "生成话题失败,Err:" + err.Error()
+				return
+			}
+			resp.AiChatTopicId = int(topicId)
+			chatItem := new(aimod.AiChat)
+			chatItem.AiChatTopicId = resp.AiChatTopicId
+			chatItem.Ask = ask
+			chatItem.AskUuid = utils.MD5(ask)
+			chatItem.Answer = answer
+			chatItem.Model = EnabledModelsForMap[req.Model]
+			chatItem.SysUserId = this.SysUser.AdminId
+			chatItem.SysUserRealName = this.SysUser.RealName
+			chatItem.CreateTime = time.Now()
+			chatItem.ModifyTime = time.Now()
+			_, err = aimod.AddAiChat(chatItem)
+			if err != nil {
+				br.Msg = "获取数据失败!"
+				br.ErrMsg = "生成话题记录失败,Err:" + err.Error()
+				return
+			}
+		} else {
+			resp.AiChatTopicId = req.AiChatTopicId
+			chatItem := new(aimod.AiChat)
+			chatItem.AiChatTopicId = resp.AiChatTopicId
+			chatItem.Ask = ask
+			chatItem.AskUuid = utils.MD5(ask)
+			chatItem.Answer = answer
+			chatItem.Model = EnabledModelsForMap[req.Model]
+			chatItem.SysUserId = this.SysUser.AdminId
+			chatItem.SysUserRealName = this.SysUser.RealName
+			chatItem.CreateTime = time.Now()
+			chatItem.ModifyTime = time.Now()
+			_, err = aimod.AddAiChat(chatItem)
+			if err != nil {
+				br.Msg = "获取数据失败!"
+				br.ErrMsg = "生成话题记录失败,Err:" + err.Error()
+				return
+			}
+		}
+
+	} else if len(req.OpenaiFileId) > 0 {
+		// 走文件上传的方式
+		//根据提问,获取信息
+
+		ask := req.Prompt
+		askUuid := utils.MD5(ask)
+
+		var assistantId, threadId string
+		if req.AiChatTopicId > 0 {
+			aiChatTopicObj := new(aimod.AiChatTopic)
+			aiChatTopicObj.AiChatTopicId = req.AiChatTopicId
+			topic, err := aiChatTopicObj.GetAiChatTopicById()
+			if err != nil {
+				if err.Error() == utils.ErrNoRow() {
+					br.Msg = "获取数据失败!"
+					br.ErrMsg = "获取数据失败,主题不存在,Err:" + err.Error()
+					return
+				}
+				br.Msg = "获取数据失败!"
+				br.ErrMsg = "获取数据失败,GetAiChatTopicById,Err:" + err.Error()
+				return
+			}
+			assistantId = topic.AssistantId
+			threadId = topic.ThreadId
+		}
+
+		var answer string
+		//获取主题下的所有信息
+		//AiChatTopicId
+		//historyList, err := aimod.GetAiChatList(req.AiChatTopicId)
+		//if err != nil && err.Error() != utils.ErrNoRow() {
+		//	br.Msg = "获取主题历史数据失败!"
+		//	br.ErrMsg = "获取主题历史数据失败,Err:" + err.Error()
+		//	return
+		//}
+
+		frList := make([]aimod.HistoryChat, 0)
+		tmpFileIdList := make([]string, 0)
+		tmpFileIdList = append(tmpFileIdList, req.OpenaiFileId...)
+		//// 历史消息
+		//for _, v := range historyList {
+		//	if v.OpenaiFileId != "" {
+		//		tmpFileIdList = append(tmpFileIdList, v.OpenaiFileId)
+		//	} else {
+		//		historyFr := new(aimod.HistoryChat)
+		//		historyFr.Ask = v.Ask
+		//		historyFr.Answer = v.Answer
+		//		historyFr.OpenaiFileId = tmpFileIdList
+		//		frList = append(frList, *historyFr)
+		//		tmpFileIdList = []string{}
+		//	}
+		//}
+		// 当前的消息
+		{
+			frItem := new(aimod.HistoryChat)
+			frItem.Ask = ask
+			frItem.Answer = ""
+			frItem.OpenaiFileId = tmpFileIdList
+			frList = append(frList, *frItem)
+		}
+
+		fileRetrieveResp, err := aiser.FileRetrieve(assistantId, threadId, req.Model, frList, req.OpenaiFileId)
+		if err != nil {
+			br.Msg = "获取数据失败!"
+			br.ErrMsg = "获取数据失败,FileRetrieve,Err:" + err.Error()
+			return
+		}
+
+		if fileRetrieveResp != nil {
+			if fileRetrieveResp.Ret == 200 {
+				assistantId = fileRetrieveResp.Data.AssistantId
+				threadId = fileRetrieveResp.Data.ThreadId
+				answer = fileRetrieveResp.Data.Answer
+			} else {
+				br.Msg = "获取数据失败!"
+				br.ErrMsg = fileRetrieveResp.Msg
+				return
+			}
+		}
+
+		if req.AiChatTopicId <= 0 { //新增
+			topic := new(aimod.AiChatTopic)
+			topic.TopicName = ask
+			topic.SysUserId = this.SysUser.AdminId
+			topic.SysUserRealName = this.SysUser.RealName
+			topic.CreateTime = time.Now()
+			topic.ModifyTime = time.Now()
+			topic.AssistantId = assistantId
+			topic.ThreadId = threadId
+			topicId, err := aimod.AddAiChatTopic(topic)
+			if err != nil {
+				br.Msg = "获取数据失败!"
+				br.ErrMsg = "生成话题失败,Err:" + err.Error()
+				return
+			}
+			chatItem := new(aimod.AiChat)
+			chatItem.AiChatTopicId = int(topicId)
+			chatItem.Ask = ask
+			chatItem.AskUuid = utils.MD5(ask)
+			chatItem.Answer = answer
+			chatItem.Model = EnabledModelsForMap[req.Model]
+			chatItem.SysUserId = this.SysUser.AdminId
+			chatItem.SysUserRealName = this.SysUser.RealName
+			chatItem.CreateTime = time.Now()
+			chatItem.ModifyTime = time.Now()
+			_, err = aimod.AddAiChat(chatItem)
+			if err != nil {
+				br.Msg = "获取数据失败!"
+				br.ErrMsg = "生成话题记录失败,Err:" + err.Error()
+				return
+			}
+			req.AiChatTopicId = int(topicId)
+		} else {
+			chatItem := new(aimod.AiChat)
+			chatItem.AiChatTopicId = req.AiChatTopicId
+			chatItem.Ask = ask
+			chatItem.AskUuid = askUuid
+			chatItem.Answer = answer
+			chatItem.Model = EnabledModelsForMap[req.Model]
+			chatItem.SysUserId = this.SysUser.AdminId
+			chatItem.SysUserRealName = this.SysUser.RealName
+			chatItem.CreateTime = time.Now()
+			chatItem.ModifyTime = time.Now()
+			_, err = aimod.AddAiChat(chatItem)
+			if err != nil {
+				br.Msg = "获取数据失败!"
+				br.ErrMsg = "生成话题记录失败,Err:" + err.Error()
+				return
+			}
+			//更新	assistantId,threadId
+			aiChatTopicObj := new(aimod.AiChatTopic)
+
+			updateParams := make(map[string]interface{})
+			updateParams["assistant_id"] = assistantId
+			updateParams["thread_id"] = threadId
+			updateParams["modify_time"] = time.Now()
+
+			whereParam := make(map[string]interface{})
+			whereParam["ai_chat_topic_id"] = req.AiChatTopicId
+
+			err = aiChatTopicObj.Update(updateParams, whereParam)
+			if err != nil {
+				br.Msg = "获取失败!"
+				br.ErrMsg = "修改助手标识失败,Err:" + err.Error()
+				return
+			}
+		}
+		resp.Model = aimod.ModelViewMap[req.Model]
+		resp.AiChatTopicId = req.AiChatTopicId
+		resp.Ask = ask
+		resp.Answer = answer
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+//// @Title 文件上传
+//// @Description 文件上传接口
+//// @Param   File   query   file  true       "文件"
+//// @Param   AiChatTopicId   query   int  true       "主题id"
+//// @Success 200 {object} models.ResourceResp
+//// @router /file/upload [post]
+//func (this *AiFileController) FileUpload2() {
+//	br := new(models.BaseResponse).Init()
+//	defer func() {
+//		this.Data["json"] = br
+//		this.ServeJSON()
+//	}()
+//
+//	f, h, err := this.GetFile("File")
+//	if err != nil {
+//		br.Msg = "获取资源信息失败"
+//		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
+//		return
+//	}
+//	model := this.GetString("Model")
+//
+//	aiChatTopicId, _ := this.GetInt("AiChatTopicId")
+//
+//	uploadFileName := h.Filename //上传的文件名
+//	ext := path.Ext(h.Filename)
+//	dateDir := time.Now().Format("20060102")
+//	uploadDir := utils.STATIC_DIR + "ai/" + dateDir
+//	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
+//	if err != nil {
+//		br.Msg = "存储目录创建失败"
+//		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
+//		return
+//	}
+//	randStr := utils.GetRandStringNoSpecialChar(28)
+//	fileName := randStr + ext
+//	fpath := uploadDir + "/" + fileName
+//	defer f.Close() //关闭上传文件
+//	err = this.SaveToFile("File", fpath)
+//	if err != nil {
+//		br.Msg = "文件上传失败"
+//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//		return
+//	}
+//	resourceUrl := ``
+//	ossClient := services.NewOssClient()
+//	if ossClient == nil {
+//		br.Msg = "上传失败"
+//		br.ErrMsg = "初始化OSS服务失败"
+//		return
+//	}
+//	resourceUrl, err = ossClient.UploadFile(fileName, fpath, "")
+//	if err != nil {
+//		br.Msg = "文件上传失败"
+//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	defer func() {
+//		os.Remove(fpath)
+//	}()
+//
+//	item := new(models.Resource)
+//	item.ResourceUrl = resourceUrl
+//	item.ResourceType = 1
+//	item.CreateTime = time.Now()
+//	newId, err := models.AddResource(item)
+//	if err != nil {
+//		br.Msg = "资源上传失败"
+//		br.ErrMsg = "资源上传失败,Err:" + err.Error()
+//		return
+//	}
+//	//调用AI接口,上传文件
+//	resp := models.ResourceResp{
+//		Id:           newId,
+//		ResourceUrl:  resourceUrl,
+//		ResourceName: uploadFileName,
+//	}
+//	uploadResult, err := aiser.OpenAiFileUpload(resourceUrl, uploadFileName, model)
+//	if err != nil {
+//		br.Msg = "文件上传失败"
+//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	var assistantId, threadId string
+//	if aiChatTopicId > 0 {
+//		aiChatTopicObj := new(aimod.AiChatTopic)
+//		aiChatTopicObj.AiChatTopicId = aiChatTopicId
+//		topic, err := aiChatTopicObj.GetAiChatTopicById()
+//		if err != nil {
+//			if err.Error() == utils.ErrNoRow() {
+//				br.Msg = "获取数据失败!"
+//				br.ErrMsg = "获取数据失败,主题不存在,Err:" + err.Error()
+//				return
+//			}
+//			br.Msg = "获取数据失败!"
+//			br.ErrMsg = "获取数据失败,GetAiChatTopicById,Err:" + err.Error()
+//			return
+//		}
+//		assistantId = topic.AssistantId
+//		threadId = topic.ThreadId
+//	}
+//
+//	if aiChatTopicId <= 0 { //新增
+//		topic := new(aimod.AiChatTopic)
+//		var filenameWithSuffix string
+//		filenameWithSuffix = path.Base(uploadFileName)
+//		var fileSuffix string
+//		fileSuffix = path.Ext(filenameWithSuffix)
+//		var filenameOnly string
+//		filenameOnly = strings.TrimSuffix(filenameWithSuffix, fileSuffix)
+//		topic.TopicName = filenameOnly
+//		topic.SysUserId = this.SysUser.AdminId
+//		topic.SysUserRealName = this.SysUser.RealName
+//		topic.CreateTime = time.Now()
+//		topic.ModifyTime = time.Now()
+//		topic.AssistantId = assistantId
+//		topic.ThreadId = threadId
+//		topicId, err := aimod.AddAiChatTopic(topic)
+//		if err != nil {
+//			br.Msg = "获取数据失败!"
+//			br.ErrMsg = "生成话题失败,Err:" + err.Error()
+//			return
+//		}
+//		aiChatTopicId = int(topicId)
+//		chatItem := new(aimod.AiChat)
+//		chatItem.AiChatTopicId = aiChatTopicId
+//		chatItem.Ask = uploadFileName
+//		chatItem.AskUuid = utils.MD5(uploadFileName)
+//		chatItem.Model = EnabledModelsForMap[model]
+//		chatItem.SysUserId = this.SysUser.AdminId
+//		chatItem.SysUserRealName = this.SysUser.RealName
+//		if uploadResult != nil && uploadResult.Data != nil {
+//			chatItem.OpenaiFileId = uploadResult.Data.ID
+//			chatItem.OpenaiFileName = uploadFileName
+//		}
+//		chatItem.OpenaiFilePath = resourceUrl
+//		chatItem.CreateTime = time.Now()
+//		chatItem.ModifyTime = time.Now()
+//		_, err = aimod.AddAiChat(chatItem)
+//		if err != nil {
+//			br.Msg = "获取数据失败!"
+//			br.ErrMsg = "生成话题记录失败,Err:" + err.Error()
+//			return
+//		}
+//	} else {
+//		chatItem := new(aimod.AiChat)
+//		chatItem.AiChatTopicId = aiChatTopicId
+//		chatItem.Ask = uploadFileName
+//		chatItem.AskUuid = utils.MD5(fileName)
+//		chatItem.Model = EnabledModelsForMap[model]
+//		chatItem.SysUserId = this.SysUser.AdminId
+//		chatItem.SysUserRealName = this.SysUser.RealName
+//		if uploadResult != nil && uploadResult.Data != nil {
+//			chatItem.OpenaiFileId = uploadResult.Data.ID
+//			chatItem.OpenaiFileName = uploadFileName
+//		}
+//		chatItem.OpenaiFilePath = resourceUrl
+//		chatItem.CreateTime = time.Now()
+//		chatItem.ModifyTime = time.Now()
+//		_, err = aimod.AddAiChat(chatItem)
+//		if err != nil {
+//			br.Msg = "获取数据失败!"
+//			br.ErrMsg = "生成话题记录失败,Err:" + err.Error()
+//			return
+//		}
+//	}
+//
+//	if uploadResult != nil && uploadResult.Data != nil && uploadResult.Data.ID != "" {
+//		uploadObj := new(aimod.FileUploadRecord)
+//		uploadObj.AdminId = this.SysUser.AdminId
+//		uploadObj.FileUrl = resourceUrl
+//		uploadObj.FileName = uploadFileName
+//		uploadObj.OpenaiFileId = uploadResult.Data.ID
+//		uploadObj.OpenaiFileName = uploadResult.Data.FileName
+//		uploadObj.OpenaiObject = uploadResult.Data.Object
+//		uploadObj.OpenaiStatus = uploadResult.Data.Status
+//		uploadObj.OpenaiPurpose = uploadResult.Data.Purpose
+//		uploadObj.OpenaiStatusDetails = uploadResult.Data.StatusDetails
+//		uploadObj.OpenaiCreatedAt = uploadResult.Data.CreatedAt
+//		uploadObj.CreateTime = time.Now()
+//		uploadObj.ModifyTime = time.Now()
+//		_, err = uploadObj.AddFileUploadRecord()
+//		if err != nil {
+//			br.Msg = "上传失败"
+//			br.ErrMsg = "上传失败,Err:" + err.Error()
+//			return
+//		}
+//		resp.OpenaiFileId = uploadObj.OpenaiFileId
+//	}
+//	resp.AiChatTopicId = aiChatTopicId
+//	br.Msg = "上传成功"
+//	br.Ret = 200
+//	br.Success = true
+//	br.Data = resp
+//	return
+//}
+
+
+// AiSummaryClassifyList
+// @Title 获取所有纪要分类接口-不包含沙盘
+// @Description 获取所有纪要分类接口-不包含沙盘
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /summary/classifyList [get]
+func (this *AiController) AiSummaryClassifyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	resp := new(ai_summary.AiSummaryClassifyListResp)
+
+	rootList, err := ai_summary.GetAiSummaryClassifyByParentId(0)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	classifyAll, err := ai_summary.GetAiSummaryClassifyAll()
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	nodeAll := make([]*ai_summary.AiSummaryClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		aiSummaryService.AiSummaryClassifyItemsMakeTree(this.SysUser, classifyAll, rootNode)
+		nodeAll = append(nodeAll, rootNode)
+	}
+
+	resp.AllNodes = nodeAll
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 8 - 0
controllers/data_manage/chart_classify.go

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

+ 92 - 63
controllers/data_manage/chart_info.go

@@ -1379,6 +1379,8 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 			return
 		}
 		extraConfigStr = chartInfo.BarConfig
+	} else if chartInfo != nil && chartInfo.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
+		extraConfigStr = req.ExtraConfig
 	}
 
 	// 获取图表中的指标数据
@@ -1433,42 +1435,44 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 			return
 		}
 		for i := range markerLines {
-			if markerLines[i].EdbType == 0 && markerLines[i].TimeIntervalType == 0 {
+			if markerLines[i].EdbType == 0 && markerLines[i].TimeIntervalType == 0 && markerLines[i].Axis != 3 {
 				// 图上第一个指标且时间区间跟随图表
-				if edbList[0].IsAxis == 1 {
-					value, err := data.MarkerLineCalculate(markerLines[i], edbList[0].DataList, chartInfo)
-					if err != nil {
-						br.Msg = "标识线配置异常"
-						br.ErrMsg = "标识线配置异常" + err.Error()
-						return
-					}
-					markerLines[i].Value = value
-				} else {
-					// 其他的都走指标计算
-					edbInfo, err := data_manage.GetEdbInfoById(markerLines[i].EdbInfoId)
-					if err != nil {
-						br.Msg = "指标计算标识线获取指标信息异常"
-						br.ErrMsg = "指标计算标识线获取指标信息异常" + err.Error()
-						return
-					}
-					// 判断时间区间不为跟随图表的情况
-					if markerLines[i].TimeIntervalType != 0 {
-						startDate = markerLines[i].StartDate.Date
-						endDate = markerLines[i].EndDate.Date
-					}
-					dataList, err := data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, startDate, endDate)
-					if err != nil {
-						br.Msg = "指标计算标识线获取指标数据异常"
-						br.ErrMsg = "指标计算标识线获取指标数据异常" + err.Error()
-						return
-					}
-					value, err := data.MarkerLineCalculate(markerLines[i], dataList, chartInfo)
-					if err != nil {
-						br.Msg = "标识线配置异常"
-						br.ErrMsg = "标识线配置异常" + err.Error()
-						return
+				if markerLines[i].MarkLineType == 2 {
+					if edbList[0].IsAxis == 1 {
+						value, err := data.MarkerLineCalculate(markerLines[i], edbList[0].DataList, chartInfo)
+						if err != nil {
+							br.Msg = "标识线配置异常"
+							br.ErrMsg = "标识线配置异常" + err.Error()
+							return
+						}
+						markerLines[i].Value = value
+					} else {
+						// 其他的都走指标计算
+						edbInfo, err := data_manage.GetEdbInfoById(markerLines[i].EdbInfoId)
+						if err != nil {
+							br.Msg = "指标计算标识线获取指标信息异常"
+							br.ErrMsg = "指标计算标识线获取指标信息异常" + err.Error()
+							return
+						}
+						// 判断时间区间不为跟随图表的情况
+						if markerLines[i].TimeIntervalType != 0 {
+							startDate = markerLines[i].StartDate.Date
+							endDate = markerLines[i].EndDate.Date
+						}
+						dataList, err := data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, startDate, endDate)
+						if err != nil {
+							br.Msg = "指标计算标识线获取指标数据异常"
+							br.ErrMsg = "指标计算标识线获取指标数据异常" + err.Error()
+							return
+						}
+						value, err := data.MarkerLineCalculate(markerLines[i], dataList, chartInfo)
+						if err != nil {
+							br.Msg = "标识线配置异常"
+							br.ErrMsg = "标识线配置异常" + err.Error()
+							return
+						}
+						markerLines[i].Value = value
 					}
-					markerLines[i].Value = value
 				}
 			}
 		}
@@ -3219,6 +3223,28 @@ func (this *ChartInfoController) CopyChartInfo() {
 			return
 		}
 	}
+	if chartInfo.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
+		// 查询系列信息并复制
+		chartSeriesList, e := data_manage.GetChartSeriesByChartInfoId(oldChartInfo.ChartInfoId)
+		if e != nil {
+			br.Msg = "获取图表关联的系列信息失败"
+			br.ErrMsg = "获取图表关联的系列信息失败,Err:" + e.Error()
+			return
+		}
+
+		chartSeriesEdbList, e := data_manage.GetChartSeriesEdbByChartInfoId(oldChartInfo.ChartInfoId)
+		if err != nil {
+			br.Msg = "获取图表关联的系列指标信息失败"
+			br.ErrMsg = "获取图表关联的系列指标信息失败,Err:" + err.Error()
+			return
+		}
+		err = data_manage.CopyChartSeriesAndEdbMapping(chartSeriesList, chartSeriesEdbList, chartInfo.ChartInfoId)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "保存图表系列失败,Err:" + err.Error()
+			return
+		}
+	}
 
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartInfo.ChartInfoId)
@@ -3392,37 +3418,40 @@ func (this *ChartInfoController) PreviewBarChartInfo() {
 			return
 		}
 		for i := range markerLines {
-			if markerLines[i].EdbType == 0 && markerLines[i].TimeIntervalType == 0 {
+			if markerLines[i].EdbType == 0 && markerLines[i].TimeIntervalType == 0 && markerLines[i].Axis != 3 {
 				// 图上第一个指标且时间区间跟随图表
-				if edbList[0].IsAxis == 1 {
-					value, err := data.MarkerLineCalculate(markerLines[i], edbList[0].DataList, chartInfo)
-					if err != nil {
-						br.Msg = "标识线配置异常"
-						br.ErrMsg = "标识线配置异常" + err.Error()
-						return
-					}
-					markerLines[i].Value = value
-				} else {
-					// 其他的都走指标计算
-					edbInfo, err := data_manage.GetEdbInfoById(markerLines[i].EdbInfoId)
-					if err != nil {
-						br.Msg = "指标计算标识线获取指标信息异常"
-						br.ErrMsg = "指标计算标识线获取指标信息异常" + err.Error()
-						return
-					}
-					dataList, err := data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, "", "")
-					if err != nil {
-						br.Msg = "指标计算标识线获取指标数据异常"
-						br.ErrMsg = "指标计算标识线获取指标数据异常" + err.Error()
-						return
-					}
-					value, err := data.MarkerLineCalculate(markerLines[i], dataList, chartInfo)
-					if err != nil {
-						br.Msg = "标识线配置异常"
-						br.ErrMsg = "标识线配置异常" + err.Error()
-						return
+				if markerLines[i].MarkLineType == 2 {
+					// 图上第一个指标且时间区间跟随图表
+					if edbList[0].IsAxis == 1 {
+						value, err := data.MarkerLineCalculate(markerLines[i], edbList[0].DataList, chartInfo)
+						if err != nil {
+							br.Msg = "标识线配置异常"
+							br.ErrMsg = "标识线配置异常" + err.Error()
+							return
+						}
+						markerLines[i].Value = value
+					} else {
+						// 其他的都走指标计算
+						edbInfo, err := data_manage.GetEdbInfoById(markerLines[i].EdbInfoId)
+						if err != nil {
+							br.Msg = "指标计算标识线获取指标信息异常"
+							br.ErrMsg = "指标计算标识线获取指标信息异常" + err.Error()
+							return
+						}
+						dataList, err := data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, "", "")
+						if err != nil {
+							br.Msg = "指标计算标识线获取指标数据异常"
+							br.ErrMsg = "指标计算标识线获取指标数据异常" + err.Error()
+							return
+						}
+						value, err := data.MarkerLineCalculate(markerLines[i], dataList, chartInfo)
+						if err != nil {
+							br.Msg = "标识线配置异常"
+							br.ErrMsg = "标识线配置异常" + err.Error()
+							return
+						}
+						markerLines[i].Value = value
 					}
-					markerLines[i].Value = value
 				}
 			}
 		}

+ 280 - 0
controllers/data_manage/chart_info_section.go

@@ -0,0 +1,280 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/services/data"
+	"eta/eta_api/services/data/data_manage_permission"
+	"eta/eta_api/utils"
+	"fmt"
+	"strings"
+)
+
+// GetChartTypeList
+// @Title 获取所有图表类型
+// @Description 获取所有图表类型
+// @Success 200 {object} data_manage.AddChartInfoResp
+// @router /chart_info/type_list [get]
+func (this *ChartInfoController) GetChartTypeList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	listTmp, err := data_manage.GetChartTypeList()
+	if err != nil {
+		br.Msg = "获取图表类型失败"
+		br.ErrMsg = "获取图表类型失败,Err:" + err.Error()
+		return
+	}
+	//遍历list,将id和name组成map
+	chartTypeMap := make(map[int][]data_manage.ChartType)
+	for _, v := range listTmp {
+		if v.ParentId > 0 {
+			chartTypeMap[v.ParentId] = append(chartTypeMap[v.ParentId], v)
+		}
+	}
+	list := make([]data_manage.ChartTypeList, 0)
+	for _, v := range listTmp {
+		if v.ParentId == 0 {
+			tmp := data_manage.ChartTypeList{
+				ChartTypeId:     v.ChartTypeId,
+				ChartTypeName:   v.ChartTypeName,
+				ChartTypeNameEn: v.ChartTypeNameEn,
+				ParentId:        v.ParentId,
+			}
+			child, ok := chartTypeMap[v.ChartTypeId]
+			if ok {
+				tmp.Child = child
+			}
+			list = append(list, tmp)
+		}
+	}
+
+	resp := data_manage.ChartTypeListResp{List: list}
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取图表类型成功"
+
+}
+
+// PreviewSectionCombineChartInfo
+// @Title 图表-获取预览的截面组合图数据
+// @Description 图表-获取预览的截面组合图数据
+// @Param	request	body data_manage.PreviewSectionCombineChartReq true "type json string"
+// @Success 200 {object} data_manage.ChartEdbInfoDetailResp
+// @router /chart_info/preview/section_combine [post]
+func (this *ChartInfoController) PreviewSectionCombineChartInfo() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req data_manage.PreviewSectionCombineChartReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	chartInfo := new(data_manage.ChartInfoView)
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	chartType := utils.CHART_TYPE_SECTION_COMBINE
+	edbInfoIdArr, err, errMsg := data.CheckChartExtraConfig(chartType, req.ExtraConfig)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = "添加失败:" + err.Error()
+		return
+	}
+	chartInfo.ChartType = chartType
+	mappingList, err := data_manage.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdArr)
+	if err != nil {
+		br.Msg = "获取图表,指标信息失败"
+		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+		return
+	}
+	// 获取图表中的指标数据
+	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(0, chartType, "", "", "", mappingList, req.ExtraConfig, "")
+	if err != nil {
+		br.Msg = "获取失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+		return
+	}
+	// 指标权限
+	{
+
+		classifyMap := make(map[int]*data_manage.EdbClassify)
+
+		// 分类
+		{
+			classifyIdList := make([]int, 0)
+			for _, v := range edbList {
+				classifyIdList = append(classifyIdList, v.ClassifyId)
+			}
+			classifyList, tmpErr := data_manage.GetEdbClassifyByIdList(classifyIdList)
+			if tmpErr != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取分类列表失败,Err:" + tmpErr.Error()
+				return
+			}
+			for _, v := range classifyList {
+				classifyMap[v.ClassifyId] = v
+			}
+		}
+		// 获取所有有权限的指标和分类
+		permissionEdbIdList, permissionClassifyIdList, err := data_manage_permission.GetUserEdbAndClassifyPermissionList(sysUser.AdminId, 0, 0)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取所有有权限的指标和分类失败,Err:" + err.Error()
+			return
+		}
+
+		for _, item := range edbList {
+			// 数据权限
+			if currClassify, ok := classifyMap[item.ClassifyId]; ok {
+				item.HaveOperaAuth = data_manage_permission.CheckEdbPermissionByPermissionIdList(item.IsJoinPermission, currClassify.IsJoinPermission, item.EdbInfoId, item.ClassifyId, permissionEdbIdList, permissionClassifyIdList)
+			}
+		}
+	}
+	warnEdbList := make([]string, 0)
+	for _, v := range edbList {
+		if v.IsNullData {
+			warnEdbList = append(warnEdbList, v.EdbName+"("+v.EdbCode+")")
+		}
+		v.HaveOperaAuth = true
+	}
+	if len(warnEdbList) > 0 {
+		chartInfo.WarnMsg = `图表引用指标异常,异常指标:` + strings.Join(warnEdbList, ",")
+	}
+
+	// 图表的指标来源
+	sourceNameList, sourceNameEnList := data.GetEdbSourceByEdbInfoIdList(edbList)
+	chartInfo.ChartSource = strings.Join(sourceNameList, ",")
+	chartInfo.ChartSourceEn = strings.Join(sourceNameEnList, ",")
+
+	//图表操作权限
+	chartInfo.IsEdit = data.CheckOpChartPermission(sysUser, chartInfo.SysUserId, true)
+	//判断是否需要展示英文标识
+	chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, edbList, utils.CHART_SOURCE_DEFAULT, chartType)
+	chartInfo.Button = data_manage.ChartViewButton{
+		IsEdit:    chartInfo.IsEdit,
+		IsEnChart: chartInfo.IsEnChart,
+		IsAdd:     chartInfo.IsEdit,
+		IsCopy:    true,
+		IsSetName: chartInfo.IsSetName,
+	}
+
+	resp := new(data_manage.ChartInfoDetailResp)
+	resp.ChartInfo = chartInfo
+	resp.EdbInfoList = edbList
+	resp.XEdbIdValue = xEdbIdValue
+	resp.YDataList = yDataList
+	resp.DataResp = dataResp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// PreviewSectionCombineChartInfo
+// @Title 图表-获取预览的截面组合图数据
+// @Description 图表-获取预览的截面组合图数据
+// @Param	request	body data_manage.ChartSectionDateConfItem true "type json string"
+// @Success 200 {object} data_manage.PreviewSectionCombineDateCalculateResp
+// @router /chart_info/section_combine/date_calculate [post]
+func (this *ChartInfoController) PreviewSectionCombineDateCalculate() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req data_manage.PreviewSectionCombineDateCalculateReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	//查询指标细信息
+	dataList := make([]*data_manage.EdbDataList, 0)
+	if req.EdbInfoId > 0 {
+		edbInfo, e := data_manage.GetEdbInfoById(req.EdbInfoId)
+		if e != nil {
+			br.Msg = "指标查询失败!"
+			br.ErrMsg = "指标查询失败,Err:" + e.Error()
+			return
+		}
+		startDateReal := ""
+		endDate := ""
+
+		switch edbInfo.EdbInfoType {
+		case 0:
+			dataList, err = data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, startDateReal, endDate)
+		case 1:
+			_, dataList, _, _, err, _ = data.GetPredictDataListByPredictEdbInfoId(edbInfo.EdbInfoId, startDateReal, endDate, true)
+		default:
+			err = errors.New(fmt.Sprint("获取失败,指标类型异常", edbInfo.EdbInfoType))
+		}
+		if err != nil {
+			br.Msg = "指标数据查询失败!"
+			br.ErrMsg = "指标数据查询失败,Err:" + err.Error()
+			return
+		}
+	}
+	var findDate string
+	if req.DateType == 1 {
+		req.EdbInfoId = 0
+	}
+	findDate, err = data.GetChartSectionSeriesDateByDateChange(req.EdbInfoId, dataList, req.DateChange, req.MoveForward)
+	if err != nil {
+		br.Msg = "指标数据查询失败!"
+		br.ErrMsg = "指标数据查询失败,Err:" + err.Error()
+		return
+	}
+
+	if findDate == "" {
+		br.Msg = "日期为空!"
+		return
+	}
+	resp := &data_manage.PreviewSectionCombineDateCalculateResp{
+		Date: findDate,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 26 - 2
controllers/data_manage/chart_theme.go

@@ -87,13 +87,32 @@ func (c *ChartThemeController) TypeList() {
 		return
 	}
 
-	list, err := chart_theme.GetAllChartThemeTypeList()
+	listTmp, err := chart_theme.GetAllChartThemeTypeList()
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取图表样式类型信息失败,Err:" + err.Error()
 		return
 	}
 
+	//遍历list,将id和name组成map
+	chartTypeMap := make(map[int][]*chart_theme.ChartThemeTypeList)
+	for _, v := range listTmp {
+		if v.ParentId > 0 {
+			chartTypeMap[v.ParentId] = append(chartTypeMap[v.ParentId], v)
+		}
+	}
+	list := make([]*chart_theme.ChartThemeTypeList, 0)
+	for _, v := range listTmp {
+		if v.ParentId == 0 {
+			tmp := v
+			child, ok := chartTypeMap[v.ChartType]
+			if ok {
+				tmp.Child = child
+			}
+			list = append(list, tmp)
+		}
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
@@ -182,6 +201,12 @@ func (c *ChartThemeController) GetThemePreviewData() {
 		chartInfo.LeftMax = "60000"
 		extraConfigStr = `{"DateList":[{"Type":3,"Date":"2023-11-01","Value":0,"Color":"","Name":""},{"Type":1,"Date":"","Value":0,"Color":"","Name":""}]}`
 		chartInfo.ChartName = "雷达图"
+	case utils.CHART_TYPE_SECTION_COMBINE:
+		edbInfoIdList = []int{19, 20, 21, 22, 23, 24, 25, 28}
+		chartInfo.LeftMin = "0"
+		chartInfo.LeftMax = "4000"
+		extraConfigStr = `{"DateConfList":[],"IsHeap":0,"XDataList":[{"Name":"内销"},{"Name":"出口"},{"Name":"销量"},{"Name":"产量"}],"UnitList":{"LeftName":"万台","RightName":"%","RightTwoName":""},"BaseChartSeriesName":"增量","SortType":0,"SeriesList":[{"ChartSeriesId":0,"SeriesName":"增量","ChartStyle":"column","ChartColor":"rgba(0, 0, 255, 1)","ChartWidth":1,"IsPoint":0,"IsNumber":0,"IsAxis":1,"EdbInfoList":[{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":19,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":20,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":21,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":22,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}}]},{"ChartSeriesId":0,"SeriesName":"增速","ChartStyle":"spline","ChartColor":"rgba(255, 0, 200, 1)","ChartWidth":1,"IsPoint":0,"IsNumber":0,"IsAxis":0,"EdbInfoList":[{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":23,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":24,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":25,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":26,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}}]}]}`
+		chartInfo.ChartName = "图表标题"
 	default:
 		br.Msg = "暂不支持该类型"
 		br.IsSendEmail = false
@@ -679,7 +704,6 @@ func (c *ChartThemeController) ListBySource() {
 		list[i].Config = newConfig
 	}
 
-
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"

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

@@ -271,7 +271,7 @@ func (c *DataMangePermissionController) MoveEdbChartUser() {
 		return
 	}
 
-	err, errMsg := data_manage_permission.MoveEdbChart(req.Source, req.SubSource, req.UserId, req.NewUserId, req.IsSelectAll, req.DataIdList, req.NoDataIdList, req.Keyword, req.Classify, sysUser.AdminId)
+	err, errMsg := data_manage_permission.MoveEdbChart(req.Source, req.SubSource, req.UserId, req.NewUserId, req.IsSelectAll, req.DataIdList, req.NoDataIdList, req.Keyword, req.Classify, sysUser.AdminId, sysUser.RealName)
 	if err != nil {
 		//br.Success = true
 		br.Msg = "移动失败"

+ 2 - 0
controllers/data_manage/edb_info_calculate.go

@@ -649,6 +649,7 @@ func (this *ChartInfoController) CalculateBatchSave() {
 		EdbInfoIdArr: req.EdbInfoIdArr,
 		Calendar:     req.Calendar,
 		Extra:        req.Extra,
+		EmptyType:    req.EmptyType,
 	}
 
 	// 调用指标库去更新
@@ -856,6 +857,7 @@ func (this *ChartInfoController) CalculateBatchEdit() {
 		EdbInfoIdArr:  req.EdbInfoIdArr,
 		Calendar:      req.Calendar,
 		Extra:         req.Extra,
+		EmptyType:     req.EmptyType,
 	}
 
 	// 调用指标库去更新

+ 551 - 6
controllers/data_manage/excel/custom_analysis.go

@@ -4,13 +4,19 @@ import (
 	"encoding/json"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
+	excelPermissionModel "eta/eta_api/models/data_manage/data_manage_permission"
 	excelModel "eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/models/data_manage/excel/request"
 	"eta/eta_api/models/data_manage/excel/response"
+	"eta/eta_api/models/system"
 	"eta/eta_api/services"
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/services/data/excel"
+	excel2 "eta/eta_api/services/data/excel"
 	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"sort"
 	"strconv"
 	"strings"
 	"time"
@@ -53,7 +59,7 @@ func (c *CustomAnalysisController) ExcelByName() {
 
 	excelName = utils.TrimLRStr(excelName)
 	// 获取数据详情
-	excelDetail, err := excelModel.GetNoContentExcelInfoByName(excelName, utils.CUSTOM_ANALYSIS_TABLE)
+	excelDetail, err := excelModel.GetNoContentExcelInfoByName(excelName, utils.CUSTOM_ANALYSIS_TABLE, sysUser.AdminId)
 	if err != nil {
 		if err.Error() == utils.ErrNoRow() {
 			br.Ret = 200
@@ -173,6 +179,9 @@ func (c *CustomAnalysisController) Add() {
 		condition += " AND excel_name=? "
 		pars = append(pars, req.ExcelName)
 
+		condition += " AND sys_user_id = ? "
+		pars = append(pars, sysUser.AdminId)
+
 		count, err := excelModel.GetExcelInfoCountByCondition(condition, pars)
 		if err != nil {
 			br.Msg = "判断表格名称是否存在失败"
@@ -413,12 +422,25 @@ func (c *CustomAnalysisController) BaseExcelDetail() {
 	}
 
 	// 数据权限
-	haveOperaAuth, err := data_manage_permission.CheckExcelPermissionByExcelInfoId(excelDetail.ExcelInfoId, excelDetail.ExcelClassifyId, excelDetail.IsJoinPermission, c.SysUser.AdminId)
-	if err != nil {
-		br.Msg = "获取ETA表格失败"
-		br.ErrMsg = "获取ETA表格权限失败,Err:" + err.Error()
+	//haveOperaAuth, err := data_manage_permission.CheckExcelPermissionByExcelInfoId(excelDetail.ExcelInfoId, excelDetail.ExcelClassifyId, excelDetail.IsJoinPermission, c.SysUser.AdminId)
+	//if err != nil {
+	//	br.Msg = "获取ETA表格失败"
+	//	br.ErrMsg = "获取ETA表格权限失败,Err:" + err.Error()
+	//	return
+	//}
+
+	// 自定义分析权限与其他表格略有差别...
+	permissions, e := excelPermissionModel.GetExcelPermissionByExcelIdAndUserId(excelDetail.ExcelInfoId, sysUser.AdminId)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取表格权限失败, %v", e)
 		return
 	}
+	var permissionType []int
+	for _, v := range permissions {
+		permissionType = append(permissionType, v.PermissionType)
+	}
+	opButton := excel.GetCustomAnalysisOpButton(sysUser, excelDetail.SysUserId, permissionType)
 
 	resp.IsFind = true
 	resp.ExcelInfo = response.FindExcelInfo{
@@ -435,7 +457,7 @@ func (c *CustomAnalysisController) BaseExcelDetail() {
 		Sort:            excelDetail.Sort,
 		ModifyTime:      excelDetail.ModifyTime,
 		CreateTime:      excelDetail.CreateTime,
-		Button:          excel.GetExcelInfoOpButton(sysUser, excelDetail.SysUserId, excelDetail.Source, haveOperaAuth),
+		Button:          opButton,
 		HaveOperaAuth:   true,
 	}
 	if markStatus.Status == 0 {
@@ -443,6 +465,7 @@ func (c *CustomAnalysisController) BaseExcelDetail() {
 	} else {
 		resp.ExcelInfo.Editor = markStatus.Editor
 	}
+
 	if excelDetail != nil {
 		sheetList, err := excelModel.GetAllSheetItemList(excelDetail.ExcelInfoId)
 		if err != nil {
@@ -607,3 +630,525 @@ func (c *CustomAnalysisController) ExcelDataList() {
 	br.Msg = "获取成功"
 	br.Data = sheetList
 }
+
+// ClassifyList
+// @Title 分类列表
+// @Description 分类列表
+// @Param   IsShare  query  bool  false  "是否共享表格: true-是; false-否(默认)"
+// @Success 200 {object} response.ExcelClassifyListResp
+// @router /excel_classify/list [get]
+func (c *CustomAnalysisController) ClassifyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	source := utils.CUSTOM_ANALYSIS_TABLE
+	isShare, _ := c.GetBool("IsShare")
+
+	resp := new(response.ExcelClassifyListResp)
+
+	// 查询我分享的/分享给我的表格
+	excels, e := excelPermissionModel.GetAdminAuthExcelInfoPermission(source, sysUser.AdminId, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取我分享的/分享给我的表格失败, %v", e)
+		return
+	}
+	hasShareMap := make(map[int]bool) // 是否已分享标记
+	for _, v := range excels {
+		if v.CreateUserId == sysUser.AdminId {
+			hasShareMap[int(v.ExcelInfoId)] = true
+		}
+	}
+
+	// 共享
+	if isShare {
+		shareExcel := new(excelModel.ExcelClassifyItems)
+		shareExcel.ExcelClassifyId = excelModel.CustomAnalysisMenuShareId
+		shareExcel.ExcelClassifyName = "我共享的"
+		shareExcel.Level = 1
+		shareExcel.Sort = 1
+		shareExcel.UniqueCode = fmt.Sprintf("SHARE%d", shareExcel.ExcelClassifyId)
+		shareExcel.HaveOperaAuth = true
+		shareExcel.Children = make([]*excelModel.ExcelClassifyItems, 0)
+
+		sharedExcel := new(excelModel.ExcelClassifyItems)
+		sharedExcel.ExcelClassifyId = excelModel.CustomAnalysisMenuSharedId
+		sharedExcel.ExcelClassifyName = "收到共享"
+		sharedExcel.Level = 1
+		sharedExcel.Sort = 2
+		sharedExcel.UniqueCode = fmt.Sprintf("SHARED%d", sharedExcel.ExcelClassifyId)
+		sharedExcel.HaveOperaAuth = true
+		sharedExcel.Children = make([]*excelModel.ExcelClassifyItems, 0)
+
+		// 收到共享细分目录
+		sharedUserClassify := make(map[int]*excelModel.ExcelClassifyItems)
+		adminIdName := make(map[int]string)
+		{
+			adminOb := new(system.Admin)
+			adminList, e := adminOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("获取用户列表失败, %v", e)
+				return
+			}
+			for _, v := range adminList {
+				adminIdName[v.AdminId] = v.RealName
+			}
+		}
+
+		sortUser := make([]int, 0) // 分享人目录排序
+		shareExists := make(map[int]bool)
+		for _, v := range excels {
+			item := new(excelModel.ExcelClassifyItems)
+			item.ExcelInfoId = int(v.ExcelInfoId)
+			item.ExcelClassifyName = v.ExcelName
+			item.UniqueCode = v.UniqueCode
+			item.HaveOperaAuth = true
+			item.HasShare = hasShareMap[int(v.ExcelInfoId)]
+			// 我共享的
+			if v.CreateUserId == sysUser.AdminId && !shareExists[int(v.ExcelInfoId)] {
+				shareExists[int(v.ExcelInfoId)] = true
+				item.Level = 2
+				item.ParentId = shareExcel.ExcelClassifyId
+				item.ShowShareBtn = true
+				shareExcel.Children = append(shareExcel.Children, item)
+				continue
+			}
+
+			// 收到共享
+			if int(v.SysUserId) == sysUser.AdminId {
+				if adminIdName[v.CreateUserId] == "" {
+					// 分享人用户不存在, 那么不显示该分类
+					continue
+				}
+				if !utils.InArrayByInt(sortUser, v.CreateUserId) {
+					sortUser = append(sortUser, v.CreateUserId)
+				}
+				if sharedUserClassify[v.CreateUserId] == nil {
+					us := new(excelModel.ExcelClassifyItems)
+					us.ExcelClassifyId = v.CreateUserId // 用户ID作为分类ID
+					us.ExcelClassifyName = fmt.Sprintf("%s的表格", adminIdName[v.CreateUserId])
+					us.Level = 2
+					us.UniqueCode = fmt.Sprintf("%d-%d", sharedExcel.ExcelClassifyId, v.CreateUserId)
+					us.HaveOperaAuth = true
+					us.ShowShareBtn = false
+					us.Children = make([]*excelModel.ExcelClassifyItems, 0)
+					sharedUserClassify[v.CreateUserId] = us
+				}
+				item.Level = 3
+				item.ParentId = sharedUserClassify[v.CreateUserId].ExcelClassifyId
+				sharedUserClassify[v.CreateUserId].Children = append(sharedUserClassify[v.CreateUserId].Children, item)
+			}
+		}
+		// 按照分享人第一次分享时间降序=_=!, 查询的时候是升序, 这里反过来
+		for i, j := 0, len(sortUser)-1; i < j; i, j = i+1, j-1 {
+			sortUser[i], sortUser[j] = sortUser[j], sortUser[i]
+		}
+		for _, v := range sortUser {
+			if sharedUserClassify[v] != nil {
+				sharedExcel.Children = append(sharedExcel.Children, sharedUserClassify[v])
+			}
+		}
+
+		resp.AllNodes = make([]*excelModel.ExcelClassifyItems, 0)
+		resp.AllNodes = append(resp.AllNodes, shareExcel, sharedExcel)
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+
+	// 我的表格
+	classifyList, err := excelModel.GetAdminExcelClassifyBySource(source, sysUser.AdminId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	classifyMap := make(map[int]*excelModel.ExcelClassifyItems)
+	for _, v := range classifyList {
+		v.HaveOperaAuth = true
+		classifyMap[v.ExcelClassifyId] = v
+	}
+	allExcelInfo, err := excelModel.GetNoContentExcelInfoAll(source, sysUser.AdminId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取表格信息失败,Err:" + err.Error()
+		return
+	}
+
+	excelInfoMap := make(map[int][]*excelModel.ExcelClassifyItems)
+	for _, v := range allExcelInfo {
+		v.HaveOperaAuth = true
+		v.ShowShareBtn = true
+		v.HasShare = hasShareMap[v.ExcelInfoId]
+		excelInfoMap[v.ExcelClassifyId] = append(excelInfoMap[v.ExcelClassifyId], v)
+	}
+
+	classifyListMap := make(map[int][]*excelModel.ExcelClassifyItems)
+	for _, v := range classifyList {
+		if v.ParentId > 0 {
+			classifyListMap[v.ParentId] = append(classifyListMap[v.ParentId], v)
+		}
+		if existItems, ok := excelInfoMap[v.ExcelClassifyId]; ok {
+			v.Children = existItems
+		}
+	}
+	for key, classify := range classifyList {
+		subList, ok := classifyListMap[classify.ExcelClassifyId]
+		if ok {
+			classifyList[key].Children = append(classifyList[key].Children, subList...)
+			sort.Slice(classifyList[key].Children, func(i, j int) bool {
+				return excelModel.ExcelClassifyItemBySort(classifyList[key].Children[i], classifyList[key].Children[j])
+			})
+		}
+	}
+
+	nodeAll := make([]*excelModel.ExcelClassifyItems, 0)
+	for _, v := range classifyList {
+		if v.ParentId == 0 {
+			sort.Slice(v.Children, func(i, j int) bool {
+				return excelModel.ExcelClassifyItemBySort(v.Children[i], v.Children[j])
+			})
+			nodeAll = append(nodeAll, v)
+		}
+	}
+
+	resp.AllNodes = nodeAll
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// List
+// @Title 自定义分析列表
+// @Description ETA表格列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ExcelClassifyId   query   int  true       "分类id"
+// @Param   Keyword   query   string  true       "搜索关键词"
+// @Param   IsShare  query  bool  false  "是否共享表格: true-是; false-否(默认)"
+// @Success 200 {object} response.ExcelListResp
+// @router /excel/list [get]
+func (c *CustomAnalysisController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	excelClassifyId, _ := c.GetInt("ExcelClassifyId")
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	keyword := c.GetString("Keyword")
+	keyword = strings.TrimSpace(keyword)
+	adminId := sysUser.AdminId
+	isShare, _ := c.GetBool("IsShare")
+	source := utils.CUSTOM_ANALYSIS_TABLE
+
+	var condition string
+	var pars []interface{}
+	condition += " AND source = ?"
+	pars = append(pars, source)
+	// 共享的
+	//hasShareMap := make(map[int]bool)
+	if isShare {
+		if keyword != "" {
+			keyword = fmt.Sprint("%", keyword, "%")
+		}
+		// 查询我分享的/分享给我的表格
+		excels, e := excelPermissionModel.GetAdminAuthExcelInfoPermission(source, adminId, keyword)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取我分享的/分享给我的表格失败, %v", e)
+			return
+		}
+
+		var excelIds, shareIds, sharedIds, userIds []int
+		for _, v := range excels {
+			id := int(v.ExcelInfoId)
+			// 无分类ID
+			if excelClassifyId <= 0 && !utils.InArrayByInt(excelIds, id) {
+				excelIds = append(excelIds, id)
+				continue
+			}
+			// 用户分类
+			if excelClassifyId == v.CreateUserId {
+				userIds = append(userIds, id)
+				continue
+			}
+			// 我共享的
+			if excelClassifyId == excelModel.CustomAnalysisMenuShareId && v.CreateUserId == sysUser.AdminId {
+				shareIds = append(shareIds, id)
+				//hasShareMap[id] = true
+				continue
+			}
+			// 收到共享
+			if excelClassifyId == excelModel.CustomAnalysisMenuSharedId && int(v.SysUserId) == sysUser.AdminId {
+				sharedIds = append(sharedIds, id)
+				continue
+			}
+		}
+		if excelClassifyId > 0 {
+			switch excelClassifyId {
+			case excelModel.CustomAnalysisMenuShareId: // 我共享的
+				excelIds = shareIds
+			case excelModel.CustomAnalysisMenuSharedId: // 收到共享
+				excelIds = sharedIds
+			default: // 用户目录
+				excelIds = userIds
+			}
+		}
+		if len(excelIds) == 0 {
+			page := paging.GetPaging(currentIndex, pageSize, 0)
+			resp := response.ExcelListResp{
+				Paging: page,
+				List:   make([]*excelModel.MyExcelInfoList, 0),
+			}
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "获取成功"
+			br.Data = resp
+			return
+		}
+
+		condition += fmt.Sprintf(` AND excel_info_id IN (%s)`, utils.GetOrmInReplace(len(excelIds)))
+		pars = append(pars, excelIds)
+
+	} else {
+		condition += ` AND sys_user_id = ?`
+		pars = append(pars, adminId)
+	}
+
+	// 筛选分类
+	if !isShare && excelClassifyId > 0 {
+		_, err := excelModel.GetExcelClassifyById(excelClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取表格信息失败"
+			br.ErrMsg = "获取信息失败,GetExcelClassify,Err:" + err.Error()
+			return
+		}
+
+		childClassify, e, _ := excel2.GetChildClassifyByClassifyId(excelClassifyId, source)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取分类信息失败, GetEdbClassify,Err:" + e.Error()
+			return
+		}
+		if len(childClassify) == 0 {
+			condition += " AND excel_classify_id = ? "
+			pars = append(pars, excelClassifyId)
+		} else {
+			classifyIds := []int{excelClassifyId}
+			for _, v := range childClassify {
+				classifyIds = append(classifyIds, v.ExcelClassifyId)
+			}
+			condition += fmt.Sprintf(` AND excel_classify_id IN (%s) `, utils.GetOrmInReplace(len(classifyIds)))
+			pars = append(pars, classifyIds)
+		}
+	}
+	if keyword != "" {
+		condition += ` AND (excel_name LIKE ?)`
+		pars = utils.GetLikeKeywordPars(pars, keyword, 1)
+	}
+
+	// 获取表格信息
+	var total int
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+	list, err := excelModel.GetNoContentExcelListByCondition(condition, pars, startSize, pageSize)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Success = true
+		br.Msg = "获取表格信息失败"
+		br.ErrMsg = "获取表格信息失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range list {
+		v.HaveOperaAuth = true
+		//v.Button.CancelShareButton = hasShareMap[v.ExcelInfoId]
+	}
+
+	// 总数据量
+	dataCount, err := excelModel.GetExcelListCountByCondition(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取表格列表信息失败"
+		br.ErrMsg = "获取表格列表数据总数失败,Err:" + err.Error()
+		return
+	}
+	page = paging.GetPaging(currentIndex, pageSize, dataCount)
+
+	resp := response.ExcelListResp{
+		Paging: page,
+		List:   list,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Share
+// @Title 分享表格
+// @Description 分享表格
+// @Param	request	body request.ShareExcelInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /excel/share [post]
+func (c *CustomAnalysisController) Share() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg != "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.ShareExcelInfoReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	if req.ExcelInfoId <= 0 {
+		br.Msg = "请选择表格"
+		return
+	}
+
+	excelInfo, e := excelModel.GetExcelInfoById(req.ExcelInfoId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "表格不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格失败, %v", e)
+		return
+	}
+	if excelInfo.SysUserId != sysUser.AdminId {
+		br.Msg = "无权操作"
+		br.ErrMsg = fmt.Sprintf("无权设置表格权限, ExcelInfoId: %d, SysAdminId: %d", req.ExcelInfoId, sysUser.AdminId)
+		return
+	}
+
+	source := utils.CUSTOM_ANALYSIS_TABLE
+	newPermissions := make([]*excelPermissionModel.ExcelInfoPermission, 0)
+	for _, v := range req.ViewUserIds {
+		newPermissions = append(newPermissions, &excelPermissionModel.ExcelInfoPermission{
+			ExcelInfoId:    int32(req.ExcelInfoId),
+			Source:         int32(source),
+			SysUserId:      int32(v),
+			CreateTime:     time.Now().Local(),
+			ModifyTime:     time.Now().Local(),
+			PermissionType: 1,
+		})
+	}
+	for _, v := range req.EditUserIds {
+		newPermissions = append(newPermissions, &excelPermissionModel.ExcelInfoPermission{
+			ExcelInfoId:    int32(req.ExcelInfoId),
+			Source:         int32(source),
+			SysUserId:      int32(v),
+			CreateTime:     time.Now().Local(),
+			ModifyTime:     time.Now().Local(),
+			PermissionType: 2,
+		})
+	}
+	if e := excelPermissionModel.ClearAndSetExcelInfoPermission(source, req.ExcelInfoId, newPermissions); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("设置表格权限失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ShareDetail
+// @Title 分享表格-详情
+// @Description 分享表格-详情
+// @Param   ExcelInfoId  query  int  true  "表格ID"
+// @Success 200 {object} response.ShareExcelInfoDetail
+// @router /excel/share_detail [get]
+func (c *CustomAnalysisController) ShareDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg != "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	excelInfoId, _ := c.GetInt("ExcelInfoId")
+	if excelInfoId <= 0 {
+		br.Msg = "请选择表格"
+		return
+	}
+
+	permissions, e := excelPermissionModel.GetExcelPermissionBySourceAndId(excelInfoId, utils.CUSTOM_ANALYSIS_TABLE)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取表格权限失败, %v", e)
+		return
+	}
+	resp := new(response.ShareExcelInfoDetail)
+	resp.ExcelInfoId = excelInfoId
+	for _, v := range permissions {
+		if v.PermissionType == 1 {
+			resp.ViewUserIds = append(resp.ViewUserIds, int(v.SysUserId))
+		}
+		if v.PermissionType == 2 {
+			resp.EditUserIds = append(resp.EditUserIds, int(v.SysUserId))
+		}
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 18 - 5
controllers/data_manage/excel/excel_classify.go

@@ -392,11 +392,24 @@ func (this *ExcelClassifyController) AddExcelClassify() {
 	}
 
 	// 获取同级分类下存在同名分类的数量
-	count, err := excel.GetExcelClassifyCount(req.ExcelClassifyName, req.ParentId, source)
-	if err != nil {
-		br.Msg = "判断名称是否已存在失败"
-		br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
-		return
+	var count int
+	if source == utils.CUSTOM_ANALYSIS_TABLE {
+		// 自定义分析分类私有化了
+		ct, e := excel.GetCustomAnalysisExcelClassifyCount(req.ExcelClassifyName, req.ParentId, source, this.SysUser.AdminId)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取自定义分析同级分类下同名分类失败, %v", e)
+			return
+		}
+		count = ct
+	} else {
+		ct, e := excel.GetExcelClassifyCount(req.ExcelClassifyName, req.ParentId, source)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取同级分类下同名分类失败, %v", e)
+			return
+		}
+		count = ct
 	}
 	if count > 0 {
 		br.Msg = "分类名称已存在,请重新输入"

+ 15 - 11
controllers/data_manage/excel/excel_info.go

@@ -128,6 +128,12 @@ func (c *ExcelInfoController) Add() {
 	condition += " AND excel_name=? "
 	pars = append(pars, req.ExcelName)
 
+	// 自定义分析私有化了
+	if req.Source == utils.CUSTOM_ANALYSIS_TABLE {
+		condition += ` AND sys_user_id = ? `
+		pars = append(pars, sysUser.AdminId)
+	}
+
 	// 获取分类下是否存在该表格名称
 	count, err := excel3.GetExcelInfoCountByCondition(condition, pars)
 	if err != nil {
@@ -728,7 +734,6 @@ func (c *ExcelInfoController) Detail() {
 		excelDetail.ExcelSourceEn = strings.Join(sourceNameEnList, ",")
 	}
 
-
 	// excel表格按钮权限
 	if excelDetail.Source != utils.BALANCE_TABLE {
 		excelDetail.Button = excel2.GetExcelInfoOpButton(sysUser, excelDetail.SysUserId, excelDetail.Source, excelDetail.HaveOperaAuth)
@@ -1857,9 +1862,9 @@ func (c *ExcelInfoController) GetFirstEdbData() {
 	br.Success = true
 	br.Msg = "获取成功"
 	br.Data = response.TableDataItem{
-		EdbInfoId: edbInfoId,
-		Data:      dataList,
-		ExcelSource: excelSource,
+		EdbInfoId:     edbInfoId,
+		Data:          dataList,
+		ExcelSource:   excelSource,
 		ExcelSourceEn: excelSourceEn,
 	}
 }
@@ -1936,9 +1941,9 @@ func (c *ExcelInfoController) GetOtherEdbData() {
 	br.Success = true
 	br.Msg = "获取成功"
 	br.Data = response.TableDataItem{
-		EdbInfoId: req.EdbInfoId,
-		Data:      dataList,
-		ExcelSource: excelSource,
+		EdbInfoId:     req.EdbInfoId,
+		Data:          dataList,
+		ExcelSource:   excelSource,
 		ExcelSourceEn: excelSourceEn,
 	}
 }
@@ -2914,7 +2919,6 @@ func (c *ExcelInfoController) GetBatchChartRefreshResult() {
 	br.Success = true
 }
 
-
 // GetBatchChartRefreshResult
 // @Title 获取批量刷新表格结果
 // @Description 获取批量刷新表格结果
@@ -2935,7 +2939,7 @@ func (c *ExcelInfoController) GetEdbSource() {
 		return
 	}
 	edbInfoId, _ := c.GetInt("EdbInfoId")
-	if edbInfoId <= 0  {
+	if edbInfoId <= 0 {
 		br.Msg = "请选择指标"
 		br.ErrMsg = "请选择指标"
 		br.IsSendEmail = false
@@ -2951,7 +2955,7 @@ func (c *ExcelInfoController) GetEdbSource() {
 	excelSourceEn := strings.Join(sourceNameEnList, ",")
 
 	var resp struct {
-		ExcelSource string `description:"表格来源"`
+		ExcelSource   string `description:"表格来源"`
 		ExcelSourceEn string `description:"表格来源(英文)"`
 	}
 
@@ -2961,4 +2965,4 @@ func (c *ExcelInfoController) GetEdbSource() {
 	br.Data = resp
 	br.Ret = 200
 	br.Success = true
-}
+}

+ 320 - 61
controllers/data_manage/future_good/future_good_chart_info.go

@@ -324,7 +324,7 @@ func (this *FutureGoodChartInfoController) ChartInfoAdd() {
 		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
 		return
 	}
-	var req data_manage.AddChartInfoReq
+	var req data_manage.AddFutureGoodChartInfoReq
 	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
 	if err != nil {
 		br.Msg = "参数解析异常!"
@@ -510,7 +510,12 @@ func (this *FutureGoodChartInfoController) ChartInfoAdd() {
 		return
 	}
 	mapList := make([]*data_manage.ChartEdbMapping, 0)
+	hasAddMap := make(map[int]struct{})
 	for _, v := range barChartConfReq.EdbInfoIdList {
+		if _, ok := hasAddMap[v.EdbInfoId]; ok {
+			continue
+		}
+		hasAddMap[v.EdbInfoId] = struct{}{}
 		mapItem := new(data_manage.ChartEdbMapping)
 		mapItem.ChartInfoId = int(newId)
 		mapItem.EdbInfoId = v.EdbInfoId
@@ -609,7 +614,7 @@ func (this *FutureGoodChartInfoController) ChartInfoEdit() {
 		return
 	}
 
-	var req data_manage.EditChartInfoReq
+	var req data_manage.EditFutureGoodChartInfoReq
 	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
 	if err != nil {
 		br.Msg = "参数解析异常!"
@@ -1295,8 +1300,8 @@ func (this *FutureGoodChartInfoController) ChartInfoDetail() {
 func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, chartType, dateType, startYear int, startDate, endDate string, sysUser *system.Admin, br *models.BaseResponse) {
 	chartInfoId := chartInfo.ChartInfoId
 
-	var edbInfoMapping, futureGoodEdbInfoMapping *data_manage.ChartEdbInfoMapping
-	edbInfoMapping, err := data_manage.GetEtaEdbChartEdbMapping(chartInfoId)
+	var futureGoodEdbInfoMapping *data_manage.ChartEdbInfoMapping
+	edbInfoMappingList, err := data_manage.GetEtaEdbChartEdbMappingList(chartInfoId)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
@@ -1304,12 +1309,16 @@ func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, chartType, dat
 	}
 	futureGoodEdbInfoMapping, err = data_manage.GetFutureGoodEdbChartEdbMapping(chartInfoId)
 	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "期货指标不存在"
+			return
+		}
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
 		return
 	}
 
-	if edbInfoMapping == nil {
+	if len(edbInfoMappingList) == 0 {
 		br.Msg = "请选择ETA指标"
 		br.ErrMsg = "请选择ETA指标"
 		br.IsSendEmail = false
@@ -1323,16 +1332,18 @@ func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, chartType, dat
 	}
 
 	maxYear := 0
-	if edbInfoMapping.LatestDate != "" {
-		latestDateT, _ := time.Parse(utils.FormatDate, edbInfoMapping.LatestDate)
-		maxYear = latestDateT.Year()
+	for _, v := range edbInfoMappingList {
+		if v.LatestDate != "" {
+			latestDateT, _ := time.Parse(utils.FormatDate, v.LatestDate)
+			if maxYear < latestDateT.Year() {
+				maxYear = latestDateT.Year()
+			}
+		}
 	}
 	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
 
 	// 商品价格曲线图的一些配置
-	var barConfig data_manage.BarChartInfoReq
-	barChartInfoDateList := make([]data_manage.BarChartInfoDateReq, 0)
-
+	var barConfig data_manage.FutureGoodBarChartInfoReq
 	if chartInfo.BarConfig == `` {
 		br.Msg = "商品价格曲线图未配置"
 		br.ErrMsg = "商品价格曲线图未配置"
@@ -1345,10 +1356,24 @@ func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, chartType, dat
 		return
 	}
 
-	barChartInfoDateList = barConfig.DateList
+	baseEdbInfoId := barConfig.BaseEdbInfoId
+	var baseEdbInfoMapping *data_manage.ChartEdbInfoMapping
+
+	if baseEdbInfoId == 0 {
+		// 默认取第一个现货指标
+		baseEdbInfoId = edbInfoMappingList[0].EdbInfoId
+		baseEdbInfoMapping = edbInfoMappingList[0]
+	} else {
+		baseEdbInfoMapping, err = data_manage.GetChartEdbMappingByEdbInfoId(baseEdbInfoId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+			return
+		}
+	}
 
 	// 获取图表中的指标数据
-	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(chartInfoId, startDate, endDate, edbInfoMapping, futureGoodEdbInfoMapping, barChartInfoDateList, true)
+	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(chartInfoId, startDate, endDate, baseEdbInfoMapping, edbInfoMappingList, futureGoodEdbInfoMapping, barConfig, true)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
@@ -1416,7 +1441,7 @@ func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, chartType, dat
 	chartInfo.ChartSource = strings.Join(sourceNameList, ",")
 	chartInfo.ChartSourceEn = strings.Join(sourceNameEnList, ",")
 
-	resp := new(data_manage.ChartInfoDetailResp)
+	resp := new(data_manage.FutureGoodChartInfoDetailResp)
 	resp.ChartInfo = chartInfo
 	resp.EdbInfoList = edbList
 	resp.XEdbIdValue = xEdbIdValue
@@ -1498,8 +1523,8 @@ func (this *FutureGoodChartInfoController) ChartInfoDetailFromUniqueCode() {
 }
 
 // GetChartInfoDetailFromUniqueCode 根据编码获取图表详情
-func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCache bool, sysUser *system.Admin) (resp *data_manage.ChartInfoDetailFromUniqueCodeResp, isOk bool, msg, errMsg string) {
-	resp = new(data_manage.ChartInfoDetailFromUniqueCodeResp)
+func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCache bool, sysUser *system.Admin) (resp *data_manage.FutureChartInfoDetailFromUniqueCodeResp, isOk bool, msg, errMsg string) {
+	resp = new(data_manage.FutureChartInfoDetailFromUniqueCodeResp)
 
 	adminId := sysUser.AdminId
 	// 指标数据map
@@ -1633,7 +1658,7 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 		}
 	}
 
-	edbInfoMapping, err := data_manage.GetEtaEdbChartEdbMapping(chartInfoId)
+	edbInfoMappingList, err := data_manage.GetEtaEdbChartEdbMappingList(chartInfoId)
 	if err != nil {
 		msg = "获取失败"
 		errMsg = "获取图表,现货指标信息失败,Err:" + err.Error()
@@ -1647,8 +1672,7 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 	}
 
 	// 商品价格曲线图的一些配置
-	var barConfig data_manage.BarChartInfoReq
-	barChartInfoDateList := make([]data_manage.BarChartInfoDateReq, 0)
+	var barConfig data_manage.FutureGoodBarChartInfoReq
 
 	if chartInfo.BarConfig == `` {
 		msg = "商品价格曲线图未配置"
@@ -1662,10 +1686,25 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 		return
 	}
 
-	barChartInfoDateList = barConfig.DateList
+	baseEdbInfoId := barConfig.BaseEdbInfoId
+	var baseEdbInfoMapping *data_manage.ChartEdbInfoMapping
+
+	if baseEdbInfoId == 0 {
+		// 默认取第一个现货指标
+		baseEdbInfoId = edbInfoMappingList[0].EdbInfoId
+		baseEdbInfoMapping = edbInfoMappingList[0]
+		barConfig.BaseEdbInfoId = baseEdbInfoId
+	} else {
+		baseEdbInfoMapping, err = data_manage.GetChartEdbMappingByEdbInfoId(baseEdbInfoId)
+		if err != nil {
+			msg = "获取失败"
+			errMsg = "获取图表,指标信息失败,Err:" + err.Error()
+			return
+		}
+	}
 
 	// 获取图表中的指标数据
-	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(chartInfoId, startDate, endDate, edbInfoMapping, futureGoodEdbInfoMapping, barChartInfoDateList, true)
+	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(chartInfoId, startDate, endDate, baseEdbInfoMapping, edbInfoMappingList, futureGoodEdbInfoMapping, barConfig, true)
 	if err != nil {
 		msg = "获取失败"
 		errMsg = "获取图表,指标信息失败,Err:" + err.Error()
@@ -1677,7 +1716,7 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 		errMsg = "商品价格曲线图表异常"
 		return
 	}
-	baseEdbInfo := edbList[0] //现货指标
+	baseEdbInfo := baseEdbInfoMapping //现货指标
 
 	for _, v := range edbList {
 		if v.IsNullData {
@@ -2079,16 +2118,13 @@ func (this *FutureGoodChartInfoController) BaseChartInfoDetailFromUniqueCode() {
 	switch chartInfo.Source {
 	case utils.CHART_SOURCE_FUTURE_GOOD, utils.CHART_SOURCE_FUTURE_GOOD_PROFIT:
 		// 现货指标
-		edbInfoMapping, err := data_manage.GetEtaEdbChartEdbMapping(chartInfo.ChartInfoId)
+		edbInfoMappingList, err := data_manage.GetEtaEdbChartEdbMappingList(chartInfo.ChartInfoId)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取图表,现货指标信息失败,Err:" + err.Error()
 			return
 		}
-		edbList := []*data_manage.ChartEdbInfoMapping{
-			edbInfoMapping,
-		}
-
+		edbList := edbInfoMappingList
 		// 期货指标
 		futureGoodEdbInfoMapping, err := data_manage.GetFutureGoodEdbChartEdbMapping(chartInfo.ChartInfoId)
 		if err != nil {
@@ -2099,8 +2135,7 @@ func (this *FutureGoodChartInfoController) BaseChartInfoDetailFromUniqueCode() {
 		edbList = append(edbList, futureGoodEdbInfoMapping)
 
 		// 商品价格曲线图的一些配置
-		var barConfig data_manage.BarChartInfoReq
-		barChartInfoDateList := make([]data_manage.BarChartInfoDateReq, 0)
+		var barConfig data_manage.FutureGoodBarChartInfoReq
 
 		if chartInfo.BarConfig == `` {
 			br.Msg = "商品价格曲线图未配置"
@@ -2114,8 +2149,6 @@ func (this *FutureGoodChartInfoController) BaseChartInfoDetailFromUniqueCode() {
 			return
 		}
 
-		barChartInfoDateList = barConfig.DateList
-
 		startDate := chartInfo.StartDate
 		endDate := chartInfo.EndDate
 
@@ -2129,8 +2162,23 @@ func (this *FutureGoodChartInfoController) BaseChartInfoDetailFromUniqueCode() {
 			}
 		}
 
+		baseEdbInfoId := barConfig.BaseEdbInfoId
+		var baseEdbInfoMapping *data_manage.ChartEdbInfoMapping
+		if baseEdbInfoId == 0 {
+			// 默认取第一个现货指标
+			baseEdbInfoId = edbInfoMappingList[0].EdbInfoId
+			baseEdbInfoMapping = edbInfoMappingList[0]
+		} else {
+			baseEdbInfoMapping, err = data_manage.GetChartEdbMappingByEdbInfoId(baseEdbInfoId)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+				return
+			}
+		}
+
 		// 获取图表中的指标数据
-		barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(chartInfo.ChartInfoId, startDate, endDate, edbInfoMapping, futureGoodEdbInfoMapping, barChartInfoDateList, false)
+		barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(chartInfo.ChartInfoId, startDate, endDate, baseEdbInfoMapping, edbInfoMappingList, futureGoodEdbInfoMapping, barConfig, false)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
@@ -2142,7 +2190,7 @@ func (this *FutureGoodChartInfoController) BaseChartInfoDetailFromUniqueCode() {
 			br.ErrMsg = "商品价格曲线图表异常"
 			return
 		}
-		baseEdbInfo := edbList[0] //现货指标
+		baseEdbInfo := baseEdbInfoMapping //现货指标
 
 		for _, v := range edbList {
 			if v.IsNullData {
@@ -2188,10 +2236,12 @@ func (this *FutureGoodChartInfoController) BaseChartInfoDetailFromUniqueCode() {
 		chartInfo.Button.IsEdit = chartInfo.IsEdit
 		chartInfo.Button.IsCopy = true
 		//判断是否需要展示英文标识
-		chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, edbList[0:1], chartInfo.Source, chartInfo.ChartType)
+		edbListTmp := make([]*data_manage.ChartEdbInfoMapping, 0)
+		edbListTmp = append(edbListTmp, baseEdbInfo)
+		chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, edbListTmp, chartInfo.Source, chartInfo.ChartType)
 		chartInfo.UnitEn = baseEdbInfo.UnitEn
 
-		resp := data_manage.ChartInfoDetailFromUniqueCodeResp{}
+		resp := data_manage.FutureChartInfoDetailFromUniqueCodeResp{}
 		resp.ChartInfo = chartInfo
 		resp.EdbInfoList = edbList
 		resp.XEdbIdValue = xEdbIdValue
@@ -2901,7 +2951,7 @@ func (this *FutureGoodChartInfoController) PreviewBarChartInfo() {
 		this.ServeJSON()
 	}()
 
-	var req data_manage.BarChartInfoReq
+	var req data_manage.FutureGoodBarChartInfoReq
 	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
 	if err != nil {
 		br.Msg = "参数解析异常!"
@@ -2925,21 +2975,43 @@ func (this *FutureGoodChartInfoController) PreviewBarChartInfo() {
 		br.Msg = "请选择日期"
 		return
 	}
+	if req.BaseEdbInfoId == 0 {
+		br.Msg = "请选择日期基准指标"
+		return
+	}
 	chartInfo := new(data_manage.ChartInfoView)
 
-	if len(req.EdbInfoIdList) != 2 {
-		br.Msg = "指标数量异常"
+	var futureGoodEdbInfoMapping *data_manage.ChartEdbInfoMapping
+	edbInfoMappingList := make([]*data_manage.ChartEdbInfoMapping, 0)
+	edbIds := make([]int, 0)
+	goodEdbIds := make([]int, 0)
+	for _, v := range req.EdbInfoIdList {
+		if v.Source == utils.CHART_SOURCE_DEFAULT {
+			edbIds = append(edbIds, v.EdbInfoId)
+		} else if v.Source == utils.CHART_SOURCE_FUTURE_GOOD {
+			goodEdbIds = append(goodEdbIds, v.EdbInfoId)
+		}
+	}
+	edbInfoMappingListTmp, err := data_manage.GetChartEdbMappingListByEdbInfoIdList(edbIds)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
 		return
 	}
-	var edbInfoMapping, futureGoodEdbInfoMapping *data_manage.ChartEdbInfoMapping
+	edbInfoMappingListMap := make(map[int]*data_manage.ChartEdbInfoMapping)
+	for _, v := range edbInfoMappingListTmp {
+		edbInfoMappingListMap[v.EdbInfoId] = v
+	}
+
 	for _, v := range req.EdbInfoIdList {
 		if v.Source == utils.CHART_SOURCE_DEFAULT {
-			edbInfoMapping, err = data_manage.GetChartEdbMappingByEdbInfoId(v.EdbInfoId)
-			if err != nil {
+			edbInfoMapping, ok := edbInfoMappingListMap[v.EdbInfoId]
+			if !ok {
 				br.Msg = "获取失败"
-				br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+				br.ErrMsg = "获取图表,指标信息失败 "
 				return
 			}
+			edbInfoMappingList = append(edbInfoMappingList, edbInfoMapping)
 		} else if v.Source == utils.CHART_SOURCE_FUTURE_GOOD {
 			futureGoodEdbInfoMapping, err = data_manage.GetChartEdbMappingByFutureGoodEdbInfoId(v.EdbInfoId)
 			if err != nil {
@@ -2955,7 +3027,7 @@ func (this *FutureGoodChartInfoController) PreviewBarChartInfo() {
 		}
 	}
 
-	if edbInfoMapping == nil {
+	if len(edbInfoMappingList) == 0 {
 		br.Msg = "请选择ETA指标"
 		br.ErrMsg = "请选择ETA指标"
 		br.IsSendEmail = false
@@ -2968,8 +3040,17 @@ func (this *FutureGoodChartInfoController) PreviewBarChartInfo() {
 		return
 	}
 
+	baseEdbInfoId := req.BaseEdbInfoId
+
+	baseEdbInfoMapping, err := data_manage.GetChartEdbMappingByEdbInfoId(baseEdbInfoId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+		return
+	}
+
 	// 获取图表中的指标数据
-	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(0, "", "", edbInfoMapping, futureGoodEdbInfoMapping, req.DateList, true)
+	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(0, "", "", baseEdbInfoMapping, edbInfoMappingList, futureGoodEdbInfoMapping, req, true)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
@@ -2998,7 +3079,7 @@ func (this *FutureGoodChartInfoController) PreviewBarChartInfo() {
 	//判断是否需要展示英文标识
 	chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, edbList, chartInfo.Source, chartInfo.ChartType)
 
-	resp := new(data_manage.ChartInfoDetailResp)
+	resp := new(data_manage.FutureGoodChartInfoDetailResp)
 	resp.ChartInfo = chartInfo
 	resp.EdbInfoList = edbList
 	resp.XEdbIdValue = xEdbIdValue
@@ -3031,7 +3112,7 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 		return
 	}
 
-	var req request.EditChartInfoBaseReq
+	var req data_manage.EditFutureGoodChartInfoBaseReq
 	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
 	if err != nil {
 		br.Msg = "参数解析异常!"
@@ -3071,19 +3152,22 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 	//	return
 	//}
 
-	edbInfoMapping, err := data_manage.GetEtaEdbChartEdbMapping(chartItem.ChartInfoId)
+	edbInfoMappingList, err := data_manage.GetEtaEdbChartEdbMappingList(req.ChartInfoId)
 	if err != nil {
-		br.Msg = "修改失败"
-		br.ErrMsg = "获取图表现货价格指标信息失败,指标信息失败,Err:" + err.Error()
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
 		return
 	}
-
+	edbIds := make([]int, 0)
+	for _, edbInfoMapping := range edbInfoMappingList {
+		edbIds = append(edbIds, edbInfoMapping.EdbInfoId)
+	}
 	//校验指标信息是否存在
-	edbInfo, err := data_manage.GetEdbInfoById(edbInfoMapping.EdbInfoId)
+	edbInfoList, err := data_manage.GetEdbInfoByIdList(edbIds)
 	if err != nil {
 		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "图表不存在!"
-			br.ErrMsg = "图表指标不存在,ChartInfoId:" + strconv.Itoa(edbInfoMapping.EdbInfoId)
+			br.ErrMsg = "图表指标不存在,ChartInfoId:" + strconv.Itoa(req.ChartInfoId)
 			return
 		} else {
 			br.Msg = "获取图表信息失败!"
@@ -3091,10 +3175,53 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 			return
 		}
 	}
-	if edbInfo == nil {
-		br.Msg = "指标不存在!"
-		br.ErrMsg = "指标不存在,ChartInfoId:" + strconv.Itoa(edbInfoMapping.EdbInfoId)
-		return
+	edbInfoListMap := make(map[int]*data_manage.EdbInfo)
+	for _, v := range edbInfoList {
+		edbInfoListMap[v.EdbInfoId] = v
+	}
+	var edbCondition string
+	var edbPars []interface{}
+	for _, v := range req.ChartEdbInfoList {
+		_, ok := edbInfoListMap[v.EdbInfoId]
+		if !ok {
+			br.Msg = "指标不存在!"
+			br.ErrMsg = "指标不存在,ChartInfoId:" + strconv.Itoa(v.EdbInfoId)
+			return
+		}
+		//校验指标名称是否已存在
+		//判断指标名称是否重复
+		if v.EdbName != "" {
+			edbCondition = ""
+			edbPars = make([]interface{}, 0)
+
+			edbCondition += " AND edb_info_id<>? "
+			edbPars = append(edbPars, v.EdbInfoId)
+
+			switch this.Lang {
+			case utils.EnLangVersion:
+				edbCondition += " AND edb_name_en =? "
+			default:
+				edbCondition += " AND edb_name =? "
+			}
+
+			edbPars = append(edbPars, v.EdbName)
+
+			edbExist, e := data_manage.GetEdbInfoByCondition(edbCondition, edbPars)
+			if e != nil {
+				if e.Error() != utils.ErrNoRow() {
+					br.Msg = "判断英文指标名称是否存在失败"
+					br.ErrMsg = "判断英文指标名称是否存在失败,Err:" + e.Error()
+					return
+				}
+			}
+
+			if e == nil && edbExist.EdbInfoId > 0 {
+				br.Msg = edbExist.EdbName + ":" + v.EdbName + "指标名称已存在"
+				br.ErrMsg = "指标名称已存在,请重新填写"
+				br.IsSendEmail = false
+				return
+			}
+		}
 	}
 
 	if req.ChartName != "" {
@@ -3128,7 +3255,59 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 
 	switch chartItem.Source {
 	case utils.CHART_SOURCE_FUTURE_GOOD:
-		err = data_manage.EditBaseFutureGoodChartInfoAndEdbEnInfo(chartItem, req.ChartName, edbInfo.EdbInfoId, req.EdbName, req.Unit, this.Lang)
+		if len(req.XDataList) > 0 {
+			// 处理横轴名称
+			// 商品价格曲线图的一些配置
+			var barConfig data_manage.FutureGoodBarChartInfoReq
+			if chartItem.BarConfig == `` {
+				br.Msg = "商品价格曲线图未配置"
+				br.ErrMsg = "商品价格曲线图未配置"
+				return
+			}
+			err = json.Unmarshal([]byte(chartItem.BarConfig), &barConfig)
+			if err != nil {
+				br.Msg = "商品价格曲线图配置异常"
+				br.ErrMsg = "商品价格曲线图配置异常"
+				return
+			}
+			xDataList := barConfig.XDataList
+			length := len(xDataList)
+			switch this.Lang {
+			case utils.EnLangVersion:
+				for k, v := range req.XDataList {
+					v = strings.TrimPrefix(v, " ")
+					if v != `` {
+						if length-1 >= k {
+							xDataList[k].NameEn = v
+						} else {
+							tmp := data_manage.XData{Name: v, NameEn: v}
+							xDataList = append(xDataList, tmp)
+						}
+					}
+				}
+			default:
+				for k, v := range req.XDataList {
+					v = strings.TrimPrefix(v, " ")
+					if v != `` {
+						if length-1 >= k {
+							xDataList[k].Name = v
+						} else {
+							tmp := data_manage.XData{Name: v, NameEn: v}
+							xDataList = append(xDataList, tmp)
+						}
+					}
+				}
+			}
+			barConfig.XDataList = xDataList
+			barConfigByte, e := json.Marshal(barConfig)
+			if e != nil {
+				br.Msg = "商品价格曲线图配置异常"
+				br.ErrMsg = "商品价格曲线图配置异常 Err:" + e.Error()
+				return
+			}
+			chartItem.BarConfig = string(barConfigByte)
+		}
+		err = data_manage.EditBaseFutureGoodChartInfoAndEdbEnInfo(chartItem, &req, this.Lang)
 		if req.FutureGoodName != `` {
 			futureGoodEdbInfoMapping, err := data_manage.GetFutureGoodEdbChartEdbMapping(chartItem.ChartInfoId)
 			if err != nil {
@@ -3140,7 +3319,7 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 			if err != nil {
 				if err.Error() == utils.ErrNoRow() {
 					br.Msg = "图表不存在!"
-					br.ErrMsg = "图表指标不存在,ChartInfoId:" + strconv.Itoa(edbInfoMapping.EdbInfoId)
+					br.ErrMsg = "图表指标不存在,futureGoodEdbInfo:" + strconv.Itoa(futureGoodEdbInfo.FutureGoodEdbInfoId)
 					return
 				} else {
 					br.Msg = "获取图表信息失败!"
@@ -3150,7 +3329,7 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 			}
 			if futureGoodEdbInfo == nil {
 				br.Msg = "期货商品指标不存在!"
-				br.ErrMsg = "期货商品指标不存在,ChartInfoId:" + strconv.Itoa(edbInfoMapping.EdbInfoId)
+				br.ErrMsg = "期货商品指标不存在,futureGoodEdbInfo:" + strconv.Itoa(futureGoodEdbInfo.FutureGoodEdbInfoId)
 				return
 			}
 
@@ -3179,7 +3358,85 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 			}
 		}
 	case utils.CHART_SOURCE_FUTURE_GOOD_PROFIT:
-		err = data_manage.EditBaseFutureGoodProfitChartInfoAndEdbEnInfo(chartItem, req.ChartName, edbInfo.EdbInfoId, req.EdbName, req.Unit, req.ProfitName, this.Lang)
+		if len(req.XDataList) > 0 {
+			// 处理横轴名称
+			// 商品价格曲线图的一些配置
+			var barConfig request.ChartInfoReq
+			if chartItem.ExtraConfig == `` {
+				br.Msg = "商品价格曲线图未配置"
+				br.ErrMsg = "商品价格曲线图未配置"
+				return
+			}
+			err = json.Unmarshal([]byte(chartItem.ExtraConfig), &barConfig)
+			if err != nil {
+				br.Msg = "商品价格曲线图配置异常"
+				br.ErrMsg = "商品价格曲线图配置异常"
+				return
+			}
+			xDataList := barConfig.XDataList
+			length := len(xDataList)
+			switch this.Lang {
+			case utils.EnLangVersion:
+				for k, v := range req.XDataList {
+					v = strings.TrimPrefix(v, " ")
+					if v != `` {
+						if length-1 >= k {
+							xDataList[k].NameEn = v
+						} else {
+							tmp := data_manage.XData{Name: v, NameEn: v}
+							xDataList = append(xDataList, tmp)
+						}
+					}
+				}
+			default:
+				for k, v := range req.XDataList {
+					v = strings.TrimPrefix(v, " ")
+					if v != `` {
+						if length-1 >= k {
+							xDataList[k].Name = v
+						} else {
+							tmp := data_manage.XData{Name: v, NameEn: v}
+							xDataList = append(xDataList, tmp)
+						}
+					}
+				}
+			}
+			barConfig.XDataList = xDataList
+			barConfigByte, e := json.Marshal(barConfig)
+			if e != nil {
+				br.Msg = "商品价格曲线图配置异常"
+				br.ErrMsg = "商品价格曲线图配置异常 Err:" + e.Error()
+				return
+			}
+			chartItem.ExtraConfig = string(barConfigByte)
+
+			xDataByte, e := json.Marshal(xDataList)
+			if e != nil {
+				br.Msg = "商品价格曲线图配置异常"
+				br.ErrMsg = "商品价格曲线图配置异常 Err:" + e.Error()
+				return
+			}
+			//同步修改利润表扩展表
+			chartInfoFutureGoodProfit := new(future_good.ChartInfoFutureGoodProfit)
+			if e = chartInfoFutureGoodProfit.GetItemById(chartItem.ChartInfoId); e != nil {
+				br.Msg = "商品利润曲线图不存在"
+				br.ErrMsg = "商品利润曲线图查询出错 Err:" + e.Error()
+				return
+			}
+			chartInfoFutureGoodProfit.XValue = string(xDataByte)
+			// 更改扩展图表信息
+			// 查找商品利润图表的扩展信息
+			chartInfoFutureGoodProfit.ModifyTime = time.Now()
+			updateStr := []string{"ModifyTime", "XValue"}
+			err = chartInfoFutureGoodProfit.Update(updateStr)
+			if err != nil {
+				br.Msg = "商品利润曲线图更新失败"
+				br.ErrMsg = "商品利润曲线图更新失败 Err:" + e.Error()
+				return
+			}
+
+		}
+		err = data_manage.EditBaseFutureGoodProfitChartInfoAndEdbEnInfo(chartItem, &req, this.Lang)
 	default:
 		br.Msg = "错误的图表类型"
 		br.ErrMsg = "错误的图表类型"
@@ -3198,7 +3455,9 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 	go data.EsAddOrEditMyChartInfoByChartInfoId(chartItem.ChartInfoId)
 
 	//指标 修改es信息
-	go data.AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
+	for _, v := range edbIds {
+		go data.AddOrEditEdbInfoToEs(v)
+	}
 
 	//新增操作日志
 	{

+ 163 - 105
controllers/data_manage/future_good/future_good_profit_chart_info.go

@@ -99,6 +99,11 @@ func (this *FutureGoodChartInfoController) ProfitChartInfoAdd() {
 		br.IsSendEmail = false
 		return
 	}
+	if len(extraReq.EdbInfoIdList) == 0 {
+		br.Msg = "请选择ETA指标"
+		br.IsSendEmail = false
+		return
+	}
 	if len(extraReq.FutureGoodEdbInfoIdList) <= 0 {
 		br.Msg = "请选择期货商品指标"
 		br.IsSendEmail = false
@@ -113,34 +118,41 @@ func (this *FutureGoodChartInfoController) ProfitChartInfoAdd() {
 	extraConf := string(extraConfByte)
 
 	var edbInfoIdArr []int
+	baseEdbInfo := new(data_manage.EdbInfo)
 	// ETA指标
-	edbInfo, err := data_manage.GetEdbInfoById(extraReq.BaseEdbInfoId)
+	edbInfoListTmp, err := data_manage.GetEdbInfoByIdList(extraReq.EdbInfoIdList)
 	if err != nil {
-		if err.Error() == utils.ErrNoRow() {
-			br.Msg = "指标不存在!"
-			br.ErrMsg = "指标不存在,edbInfoId:" + strconv.Itoa(extraReq.BaseEdbInfoId)
-			return
-		} else {
-			br.Msg = "获取指标信息失败!"
-			br.ErrMsg = "获取图表的指标信息失败,Err:" + err.Error()
-			return
+		br.Msg = "获取指标信息失败!"
+		br.ErrMsg = "获取图表的指标信息失败,Err:" + err.Error()
+		return
+	}
+	//按照请求顺序排序
+	edbInfoMap := make(map[int]*data_manage.EdbInfo)
+	for _, v := range edbInfoListTmp {
+		edbInfoMap[v.EdbInfoId] = v
+	}
+	edbInfoList := make([]*data_manage.EdbInfo, 0)
+	for _, v := range extraReq.EdbInfoIdList {
+		edbInfoList = append(edbInfoList, edbInfoMap[v])
+	}
+
+	edbInfoListMap := make(map[int]*data_manage.EdbInfo)
+	for k, v := range edbInfoList {
+		edbInfoList[k].EdbNameSource = v.EdbName
+		edbInfoListMap[v.EdbInfoId] = v
+		edbInfoIdArr = append(edbInfoIdArr, v.EdbInfoId)
+		if v.EdbInfoId == extraReq.BaseEdbInfoId {
+			baseEdbInfo = v
 		}
 	}
-	if edbInfo == nil {
-		br.Msg = "指标已被删除,请重新选择!"
-		br.ErrMsg = "指标不存在,ChartInfoId:" + strconv.Itoa(extraReq.BaseEdbInfoId)
-		return
-	} else {
-		if edbInfo.EdbInfoId <= 0 {
+	for _, v := range extraReq.EdbInfoIdList {
+		if _, ok := edbInfoListMap[v]; !ok {
 			br.Msg = "指标已被删除,请重新选择!"
-			br.ErrMsg = "指标不存在,ChartInfoId:" + strconv.Itoa(extraReq.BaseEdbInfoId)
+			br.ErrMsg = "指标不存在,edbInfoId:" + strconv.Itoa(v)
 			return
 		}
 	}
 
-	edbInfoIdArr = append(edbInfoIdArr, extraReq.BaseEdbInfoId)
-	edbInfo.EdbNameSource = edbInfo.EdbName
-
 	// 期货商品指标(主力合约)
 	futureGoodEdbInfoMap := make(map[int]*future_good.FutureGoodEdbInfo)
 	zlFutureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
@@ -234,25 +246,27 @@ func (this *FutureGoodChartInfoController) ProfitChartInfoAdd() {
 	// 关联指标
 	mapList := make([]*data_manage.ChartEdbMapping, 0)
 	{
-		mapList = append(mapList, &data_manage.ChartEdbMapping{
-			ChartEdbMappingId: 0,
-			EdbInfoId:         extraReq.BaseEdbInfoId,
-			CreateTime:        time.Now(),
-			ModifyTime:        time.Now(),
-			UniqueCode:        utils.MD5(utils.CHART_PREFIX + "_" + strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + strconv.Itoa(extraReq.BaseEdbInfoId)),
-			//MaxData:           0,
-			//MinData:           0,
-			IsOrder:     true,
-			IsAxis:      1,
-			EdbInfoType: 1,
-			//LeadValue:         0,
-			//LeadUnit:          "",
-			//ChartStyle:        "",
-			//ChartColor:        "",
-			//PredictChartColor: "",
-			//ChartWidth:        0,
-			Source: 1,
-		})
+		for _, v := range edbInfoList {
+			mapList = append(mapList, &data_manage.ChartEdbMapping{
+				ChartEdbMappingId: 0,
+				EdbInfoId:         v.EdbInfoId,
+				CreateTime:        time.Now(),
+				ModifyTime:        time.Now(),
+				UniqueCode:        utils.MD5(utils.CHART_PREFIX + "_" + strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + strconv.Itoa(v.EdbInfoId)),
+				//MaxData:           0,
+				//MinData:           0,
+				IsOrder:     true,
+				IsAxis:      1,
+				EdbInfoType: 1,
+				//LeadValue:         0,
+				//LeadUnit:          "",
+				//ChartStyle:        "",
+				//ChartColor:        "",
+				//PredictChartColor: "",
+				//ChartWidth:        0,
+				Source: 1,
+			})
+		}
 
 		for _, v := range futureGoodEdbInfoMap {
 			mapList = append(mapList, &data_manage.ChartEdbMapping{
@@ -278,7 +292,7 @@ func (this *FutureGoodChartInfoController) ProfitChartInfoAdd() {
 	}
 
 	// 获取图表中的指标数据
-	_, _, _, xDataList, yDataList, err := future_goodServ.GetProfitChartEdbData(edbInfo, zlFutureGoodEdbInfoList, extraReq.DateList, extraReq.CalculateFormula, extraReq.FutureGoodEdbInfoIdList)
+	_, _, _, xDataList, yDataList, err := future_goodServ.GetProfitChartEdbData(baseEdbInfo, edbInfoList, zlFutureGoodEdbInfoList, extraReq.DateList, extraReq.CalculateFormula, extraReq.FutureGoodEdbInfoIdList, extraReq.XDataList)
 	if err != nil {
 		br.Msg = "保存失败"
 		br.ErrMsg = "保存商品利润失败,指标信息失败,Err:" + err.Error()
@@ -447,6 +461,9 @@ func (this *FutureGoodChartInfoController) ProfitChartInfoEdit() {
 		br.IsSendEmail = false
 		return
 	}
+	if len(extraReq.EdbInfoIdList) == 0 {
+		extraReq.EdbInfoIdList = append(extraReq.EdbInfoIdList, extraReq.BaseEdbInfoId)
+	}
 	extraConfByte, err := json.Marshal(extraReq)
 	if err != nil {
 		br.Msg = "商品利润曲线图信息异常"
@@ -456,34 +473,41 @@ func (this *FutureGoodChartInfoController) ProfitChartInfoEdit() {
 	extraConf := string(extraConfByte)
 
 	var edbInfoIdArr []int
+	baseEdbInfo := new(data_manage.EdbInfo)
 	// ETA指标
-	edbInfo, err := data_manage.GetEdbInfoById(extraReq.BaseEdbInfoId)
+	edbInfoListTmp, err := data_manage.GetEdbInfoByIdList(extraReq.EdbInfoIdList)
 	if err != nil {
-		if err.Error() == utils.ErrNoRow() {
-			br.Msg = "指标不存在!"
-			br.ErrMsg = "指标不存在,edbInfoId:" + strconv.Itoa(extraReq.BaseEdbInfoId)
-			return
-		} else {
-			br.Msg = "获取指标信息失败!"
-			br.ErrMsg = "获取图表的指标信息失败,Err:" + err.Error()
-			return
+		br.Msg = "获取指标信息失败!"
+		br.ErrMsg = "获取图表的指标信息失败,Err:" + err.Error()
+		return
+	}
+	//按照请求顺序排序
+	edbInfoMap := make(map[int]*data_manage.EdbInfo)
+	for _, v := range edbInfoListTmp {
+		edbInfoMap[v.EdbInfoId] = v
+	}
+	edbInfoList := make([]*data_manage.EdbInfo, 0)
+	for _, v := range extraReq.EdbInfoIdList {
+		edbInfoList = append(edbInfoList, edbInfoMap[v])
+	}
+	edbInfoListMap := make(map[int]*data_manage.EdbInfo)
+	for k, v := range edbInfoList {
+		edbInfoList[k].EdbNameSource = v.EdbName
+		edbInfoListMap[v.EdbInfoId] = v
+		edbInfoIdArr = append(edbInfoIdArr, v.EdbInfoId)
+		if v.EdbInfoId == extraReq.BaseEdbInfoId {
+			baseEdbInfo = v
 		}
 	}
-	if edbInfo == nil {
-		br.Msg = "指标已被删除,请重新选择!"
-		br.ErrMsg = "指标不存在,ChartInfoId:" + strconv.Itoa(extraReq.BaseEdbInfoId)
-		return
-	} else {
-		if edbInfo.EdbInfoId <= 0 {
+
+	for _, v := range extraReq.EdbInfoIdList {
+		if _, ok := edbInfoListMap[v]; !ok {
 			br.Msg = "指标已被删除,请重新选择!"
-			br.ErrMsg = "指标不存在,ChartInfoId:" + strconv.Itoa(extraReq.BaseEdbInfoId)
+			br.ErrMsg = "指标不存在,edbInfoId:" + strconv.Itoa(v)
 			return
 		}
 	}
 
-	edbInfoIdArr = append(edbInfoIdArr, extraReq.BaseEdbInfoId)
-	edbInfo.EdbNameSource = edbInfo.EdbName
-
 	// 期货商品指标(主力合约)
 	futureGoodEdbInfoMap := make(map[int]*future_good.FutureGoodEdbInfo)
 	zlFutureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
@@ -551,26 +575,27 @@ func (this *FutureGoodChartInfoController) ProfitChartInfoEdit() {
 	// 关联指标
 	mapList := make([]*data_manage.ChartEdbMapping, 0)
 	{
-		mapList = append(mapList, &data_manage.ChartEdbMapping{
-			ChartEdbMappingId: 0,
-			EdbInfoId:         extraReq.BaseEdbInfoId,
-			CreateTime:        time.Now(),
-			ModifyTime:        time.Now(),
-			UniqueCode:        utils.MD5(utils.CHART_PREFIX + "_" + strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + strconv.Itoa(extraReq.BaseEdbInfoId)),
-			//MaxData:           0,
-			//MinData:           0,
-			IsOrder:     true,
-			IsAxis:      1,
-			EdbInfoType: 1,
-			//LeadValue:         0,
-			//LeadUnit:          "",
-			//ChartStyle:        "",
-			//ChartColor:        "",
-			//PredictChartColor: "",
-			//ChartWidth:        0,
-			Source: 1,
-		})
-
+		for _, v := range edbInfoList {
+			mapList = append(mapList, &data_manage.ChartEdbMapping{
+				ChartEdbMappingId: 0,
+				EdbInfoId:         v.EdbInfoId,
+				CreateTime:        time.Now(),
+				ModifyTime:        time.Now(),
+				UniqueCode:        utils.MD5(utils.CHART_PREFIX + "_" + strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + strconv.Itoa(v.EdbInfoId)),
+				//MaxData:           0,
+				//MinData:           0,
+				IsOrder:     true,
+				IsAxis:      1,
+				EdbInfoType: 1,
+				//LeadValue:         0,
+				//LeadUnit:          "",
+				//ChartStyle:        "",
+				//ChartColor:        "",
+				//PredictChartColor: "",
+				//ChartWidth:        0,
+				Source: 1,
+			})
+		}
 		for _, v := range futureGoodEdbInfoMap {
 			mapList = append(mapList, &data_manage.ChartEdbMapping{
 				ChartEdbMappingId: 0,
@@ -595,7 +620,7 @@ func (this *FutureGoodChartInfoController) ProfitChartInfoEdit() {
 	}
 
 	// 获取图表中的指标数据
-	_, _, _, xDataList, yDataList, err := future_goodServ.GetProfitChartEdbData(edbInfo, zlFutureGoodEdbInfoList, extraReq.DateList, extraReq.CalculateFormula, extraReq.FutureGoodEdbInfoIdList)
+	_, _, _, xDataList, yDataList, err := future_goodServ.GetProfitChartEdbData(baseEdbInfo, edbInfoList, zlFutureGoodEdbInfoList, extraReq.DateList, extraReq.CalculateFormula, extraReq.FutureGoodEdbInfoIdList, extraReq.XDataList)
 	if err != nil {
 		br.Msg = "保存失败"
 		br.ErrMsg = "保存商品利润失败,指标信息失败,Err:" + err.Error()
@@ -923,9 +948,29 @@ func (this *FutureGoodChartInfoController) PreviewProfitChartInfo() {
 		br.IsSendEmail = false
 		return
 	}
-
+	if len(req.EdbInfoIdList) == 0 {
+		br.Msg = "请选择ETA指标"
+		br.ErrMsg = "请选择ETA指标"
+		br.IsSendEmail = false
+		return
+	}
+	//查询基本指标信息
+	edbInfoListTmp, err := data_manage.GetEdbInfoByIdList(req.EdbInfoIdList)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+		return
+	}
+	edbInfoMap := make(map[int]*data_manage.EdbInfo)
+	for _, v := range edbInfoListTmp {
+		edbInfoMap[v.EdbInfoId] = v
+	}
+	edbInfoList := make([]*data_manage.EdbInfo, 0)
+	for _, v := range req.EdbInfoIdList {
+		edbInfoList = append(edbInfoList, edbInfoMap[v])
+	}
 	// 获取图表中的指标数据
-	barConfigEdbInfoIdList, _, _, xDataList, yDataList, err := future_goodServ.GetProfitChartEdbData(baseEdbInfo, zlFutureGoodEdbInfoList, req.DateList, req.CalculateFormula, req.FutureGoodEdbInfoIdList)
+	barConfigEdbInfoIdList, _, _, xDataList, yDataList, err := future_goodServ.GetProfitChartEdbData(baseEdbInfo, edbInfoList, zlFutureGoodEdbInfoList, req.DateList, req.CalculateFormula, req.FutureGoodEdbInfoIdList, req.XDataList)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
@@ -933,25 +978,27 @@ func (this *FutureGoodChartInfoController) PreviewProfitChartInfo() {
 	}
 
 	edbList := make([]*data_manage.ChartEdbInfoMapping, 0)
-	edbList = append(edbList, &data_manage.ChartEdbInfoMapping{
-		EdbInfoId:      baseEdbInfo.EdbInfoId,
-		SourceName:     baseEdbInfo.SourceName,
-		Source:         baseEdbInfo.Source,
-		EdbCode:        baseEdbInfo.EdbCode,
-		EdbName:        baseEdbInfo.EdbName,
-		EdbAliasName:   baseEdbInfo.EdbName,
-		EdbNameEn:      baseEdbInfo.EdbNameEn,
-		EdbAliasNameEn: baseEdbInfo.EdbNameEn,
-		EdbType:        baseEdbInfo.EdbType,
-		Frequency:      baseEdbInfo.Frequency,
-		FrequencyEn:    data.GetFrequencyEn(baseEdbInfo.Frequency),
-		Unit:           baseEdbInfo.Unit,
-		UnitEn:         baseEdbInfo.UnitEn,
-		StartDate:      baseEdbInfo.StartDate,
-		EndDate:        baseEdbInfo.EndDate,
-		ModifyTime:     baseEdbInfo.ModifyTime.Format(utils.FormatDateTime),
-		MappingSource:  1,
-	})
+	for _, v := range edbInfoList {
+		edbList = append(edbList, &data_manage.ChartEdbInfoMapping{
+			EdbInfoId:      v.EdbInfoId,
+			SourceName:     v.SourceName,
+			Source:         v.Source,
+			EdbCode:        v.EdbCode,
+			EdbName:        v.EdbName,
+			EdbAliasName:   v.EdbName,
+			EdbNameEn:      v.EdbNameEn,
+			EdbAliasNameEn: v.EdbNameEn,
+			EdbType:        v.EdbType,
+			Frequency:      v.Frequency,
+			FrequencyEn:    data.GetFrequencyEn(v.Frequency),
+			Unit:           v.Unit,
+			UnitEn:         v.UnitEn,
+			StartDate:      v.StartDate,
+			EndDate:        v.EndDate,
+			ModifyTime:     v.ModifyTime.Format(utils.FormatDateTime),
+			MappingSource:  1,
+		})
+	}
 
 	warnEdbList := make([]string, 0)
 	for _, v := range edbList {
@@ -1003,15 +1050,18 @@ func getFutureGoodProfitChartInfo(chartInfo *data_manage.ChartInfoView, sysUser
 		br.ErrMsg = "商品利润曲线图配置异常"
 		return
 	}
+	if len(extraConf.EdbInfoIdList) == 0 {
+		extraConf.EdbInfoIdList = append(extraConf.EdbInfoIdList, extraConf.BaseEdbInfoId)
+	}
 
 	edbList := make([]*data_manage.ChartEdbInfoMapping, 0)
-	edbInfoMapping, err := data_manage.GetEtaEdbChartEdbMapping(chartInfoId)
+	edbInfoMappingList, err := data_manage.GetEtaEdbChartEdbMappingList(chartInfoId)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取商品利润图表,基础指标信息失败,Err:" + err.Error()
 		return
 	}
-	edbList = append(edbList, edbInfoMapping)
+	edbList = edbInfoMappingList
 	futureGoodEdbInfoMappingList, err := data_manage.GetFutureGoodEdbChartEdbMappingList(chartInfoId)
 	if err != nil {
 		br.Msg = "获取失败"
@@ -1247,15 +1297,24 @@ func GetFutureGoodProfitChartInfoDetailFromUniqueCode(chartInfo *data_manage.Cha
 		errMsg = "商品利润曲线图配置异常,Err:" + err.Error()
 		return
 	}
+	if len(extraConf.EdbInfoIdList) == 0 {
+		extraConf.EdbInfoIdList = append(extraConf.EdbInfoIdList, extraConf.BaseEdbInfoId)
+	}
 
 	edbList := make([]*data_manage.ChartEdbInfoMapping, 0)
-	edbInfoMapping, err := data_manage.GetEtaEdbChartEdbMapping(chartInfoId)
+	edbInfoMappingList, err := data_manage.GetEtaEdbChartEdbMappingList(chartInfoId)
 	if err != nil {
 		msg = "获取失败"
 		errMsg = "获取商品利润图表,基础指标信息失败,Err:" + err.Error()
 		return
 	}
-	edbList = append(edbList, edbInfoMapping)
+	baseEdbInfo := new(data_manage.ChartEdbInfoMapping)
+	for _, v := range edbInfoMappingList {
+		if v.EdbInfoId == extraConf.BaseEdbInfoId {
+			baseEdbInfo = v
+		}
+	}
+	edbList = edbInfoMappingList
 	futureGoodEdbInfoMappingList, err := data_manage.GetFutureGoodEdbChartEdbMappingList(chartInfoId)
 	if err != nil {
 		msg = "获取失败"
@@ -1295,7 +1354,6 @@ func GetFutureGoodProfitChartInfoDetailFromUniqueCode(chartInfo *data_manage.Cha
 	}
 
 	warnEdbList := make([]string, 0)
-	baseEdbInfo := edbList[0] //现货指标
 
 	for _, v := range edbList {
 		if v.IsNullData {

+ 1526 - 0
controllers/data_manage/manual_edb.go

@@ -0,0 +1,1526 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/shopspring/decimal"
+	"os"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// ManualEdbController 手工指标服务(鉴权)
+type ManualEdbController struct {
+	controllers.BaseAuthController
+}
+
+// EdbDetail
+// @Title 指标列表
+// @Description 指标列表
+// @Param   TradeCode   query   string  true       "指标编码"
+// @Success 200 {object} models.TargetDetailResp
+// @router /target/edb/detail [get]
+func (c *ManualEdbController) EdbDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	// 指标编码
+	tradeCode := c.GetString("TradeCode")
+	if tradeCode == `` {
+		br.Msg = "请输入指标编码"
+		br.IsSendEmail = false
+		return
+	}
+
+	manualEdbInfo, err := models.GetTargetByTradeCode(tradeCode)
+	if err != nil {
+		br.Msg = "获取指标失败"
+		br.ErrMsg = "获取指标失败,err:" + err.Error()
+		return
+	}
+
+	// 如果不是超管账号,那么得校验下当前用户是否有该指标的权限
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		//获取账户所拥有权限的分类id集合
+		count, err := models.GetCountManualUserClassify(sysUser.AdminId, manualEdbInfo.ClassifyId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.ErrMsg = "获取分类数据失败,err:" + err.Error()
+			return
+		}
+
+		if count <= 0 {
+			br.Msg = "无权访问"
+			br.ErrMsg = "无权访问"
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	classifyList := make([]*models.EdbdataClassify, 0)
+	{
+		classify, err := models.GetManualClassifyByClassifyId(manualEdbInfo.ClassifyId)
+		if err != nil {
+			br.Msg = "获取分类信息失败"
+			br.ErrMsg = "获取分类信息失败,err:" + err.Error()
+			return
+		}
+		parentClassify, err := models.GetManualClassifyByClassifyId(classify.ParentId)
+		if err != nil {
+			br.Msg = "获取分类信息失败"
+			br.ErrMsg = "获取父级分类信息失败,err:" + err.Error()
+			return
+		}
+		classifyList = []*models.EdbdataClassify{
+			parentClassify, classify,
+		}
+	}
+
+	// 明细数据
+	{
+		dataList, err := models.GetEdbDataListByCode(manualEdbInfo.TradeCode)
+		if err != nil {
+			br.Msg = "获取明细数据失败"
+			br.ErrMsg = "获取明细数据失败,err:" + err.Error()
+			return
+		}
+
+		manualEdbInfo.DataList = dataList
+	}
+
+	resp := models.TargetDetailResp{
+		Detail:       manualEdbInfo,
+		ClassifyList: classifyList,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	//br.Data = resp
+	br.Data = resp
+}
+
+// ClassifyEdbList
+// @Title 分类指标列表
+// @Description 指标列表
+// @Param   ClassifyId   query   string  true       "分类id"
+// @Success 200 {object} models.TargetDetailResp
+// @router /target/classify/edb/list [get]
+func (c *ManualEdbController) ClassifyEdbList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.Ret = 408
+		return
+	}
+	resp := new(models.EdbdataClassifyResp)
+
+	classifyId, _ := c.GetInt("ClassifyId") //分类
+	if classifyId <= 0 {
+		br.Msg = "请传入分类"
+		br.ErrMsg = "请传入分类"
+		br.IsSendEmail = false
+	}
+
+	var condition string
+	var pars []interface{}
+
+	// 如果不是超管账号,那么得校验下当前用户是否有该指标的权限
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		//获取账户所拥有权限的分类id集合
+		count, err := models.GetCountManualUserClassify(sysUser.AdminId, classifyId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.ErrMsg = "获取分类数据失败,err:" + err.Error()
+			return
+		}
+
+		if count <= 0 {
+			br.Msg = "无权访问"
+			br.ErrMsg = "无权访问"
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	list := make([]*models.EdbdataClassifyList, 0)
+
+	condition += ` AND a.classify_id = ? `
+	pars = append(pars, classifyId)
+
+	tmpList, err := models.GetEdbInfoList(condition, pars, 0, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	for _, v := range tmpList {
+		list = append(list, &models.EdbdataClassifyList{
+			ClassifyId:   0,
+			ClassifyName: v.SecName,
+			ParentId:     v.ClassifyId,
+			Child:        nil,
+			TradeCode:    v.TradeCode,
+			UniqueCode:   utils.MD5(v.TradeCode),
+		})
+	}
+
+	resp.List = list
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// EdbSearch
+// @Title 指标搜索
+// @Description 指标搜索
+// @Param   Keyword   query   string  false       "关键字搜索"
+// @Success 200 {object} models.TargetItemListResp
+// @router /target/edb/search [get]
+func (c *ManualEdbController) EdbSearch() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	//关键字
+	keyword := c.GetString("Keyword")
+
+	var condition string
+	var pars []interface{}
+
+	//userId := sysUser.AdminId
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyIdList, err := data.GetUserManualClassifyIdList(sysUser.AdminId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+		num := len(classifyIdList)
+		if num > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+
+	}
+
+	if keyword != "" {
+		condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+		pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+	}
+
+	total, err := models.GetCountEdbInfoList(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	list, err := models.GetEdbInfoList(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	for _, v := range list {
+		v.UniqueCode = utils.MD5(v.TradeCode)
+	}
+
+	resp := models.EdbListResp{
+		List:   list,
+		Paging: paging.GetPaging(currentIndex, pageSize, total),
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// EdbList
+// @Title 获取研究员指标
+// @Description 获取研究员指标
+// @Param   Frequency   query   string  false       "频度;枚举值:日度、周度、月度、季度、半年度、年度"
+// @Param   Keyword   query   string  false       "关键字搜索"
+// @Param   TradeCode   query   string  false       "指标唯一编码"
+// @Param   ClassifyId   query   int  false       "分类id"
+// @Param   UserId   query   int  false       "用户id"
+// @Success 200 {object} models.TargetItemListResp
+// @router /target/edb/list [get]
+func (c *ManualEdbController) EdbList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	// 频度
+	frequency := c.GetString("Frequency")
+	//关键字
+	keyword := c.GetString("Keyword")
+	//关键字
+	classifyId, _ := c.GetInt("ClassifyId", 0)
+	//用户ID
+	userId, _ := c.GetInt("UserId", 0)
+
+	var condition string
+	var pars []interface{}
+
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyIdList, err := data.GetUserManualClassifyIdList(sysUser.AdminId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+		num := len(classifyIdList)
+		if num > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+
+	}
+
+	// 关键词
+	if keyword != "" {
+		condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+		pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+	}
+	if frequency != "" {
+		condition += ` AND a.frequency = ?`
+		pars = append(pars, frequency)
+	}
+
+	// 所属分类
+	if classifyId > 0 {
+		// 获取有用权限的分类
+		classifyList, err := models.GetEdbdataClassify(int64(userId))
+		if err != nil {
+			return
+		}
+
+		var isParent bool
+		classifyIdList := make([]int, 0)
+		for _, v := range classifyList {
+			if v.ClassifyId == classifyId {
+				isParent = true
+				classifyIdList = append(classifyIdList, v.ClassifyId)
+				if v.Child != nil && len(v.Child) > 0 {
+					for _, vv := range v.Child {
+						classifyIdList = append(classifyIdList, vv.ClassifyId)
+					}
+				}
+				break
+			}
+		}
+
+		num := len(classifyIdList)
+		if num > 0 && isParent {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		} else {
+			condition += ` AND a.classify_id = ? `
+			pars = append(pars, classifyId)
+		}
+	}
+
+	// 所属用户
+	if userId > 0 {
+		condition += ` AND a.user_id = ? `
+		pars = append(pars, userId)
+	}
+
+	total, err := models.GetCountEdbInfoList(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	list, err := models.GetEdbInfoList(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	resp := models.EdbListResp{
+		List:   list,
+		Paging: paging.GetPaging(currentIndex, pageSize, total),
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// EditExcelData
+// @Title 根据excel的样式去编辑指标
+// @Description 根据excel的样式去编辑指标
+// @Param  request	body models.ManualEdbExcelStyleEditReq true "type json string"
+// @Success 200
+// @router /target/edb/excel_style/edit [post]
+func (c *ManualEdbController) EditExcelData() {
+	br := new(models.BaseResponse).Init()
+	var err error
+	errs := make([]string, 0)
+	defer func() {
+		if len(errs) > 0 {
+			utils.FileLog.Info("编辑EXCEL数据 新增或修改数据失败,Err:" + strings.Join(errs, "\n"))
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	var req data_manage.ManualEdbExcelStyleEditReq
+	err = json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.TradeCode == `` {
+		br.Msg = "指标编码异常!"
+		br.ErrMsg = "指标编码异常!"
+		return
+	}
+	if req.Unit == `` {
+		br.Msg = "单位填写异常!"
+		br.ErrMsg = "单位填写异常!"
+		return
+	}
+	if req.Frequency == `` {
+		br.Msg = "频度填写异常!"
+		br.ErrMsg = "频度填写异常!"
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "品种填写异常!"
+		br.ErrMsg = "品种填写异常!"
+		return
+	}
+
+	// 获取指标信息
+	manualEdbInfo, err := models.GetEdbinfoByTradeCode(req.TradeCode)
+	if err != nil {
+		br.Msg = `找不到该指标`
+		br.Msg = `找不到该指标,ERR:` + err.Error()
+		return
+	}
+
+	// 如果不是超管账号,那么得校验下当前用户是否有该指标的权限
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		count, err := models.GetCountManualUserClassify(sysUser.AdminId, manualEdbInfo.ClassifyId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.ErrMsg = "获取分类数据失败,err:" + err.Error()
+			return
+		}
+
+		if count <= 0 {
+			br.Msg = "无权访问"
+			br.ErrMsg = "无权访问"
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	dateValueMap := make(map[string]string)
+	//取到所有数据
+	for _, dateValue := range req.Data {
+		dateValueMap[dateValue.Date] = strconv.FormatFloat(dateValue.Value, 'f', -1, 64)
+	}
+
+	//操作指标,新增指标及数据等
+	{
+		var isUpdateEdb, isUpdateData bool
+
+		secName := strings.TrimSpace(req.EdbName)
+		frequency := strings.TrimSpace(req.Frequency)
+		unit := strings.TrimSpace(req.Unit)
+
+		// 指标基础信息变更
+		{
+			updateCols := make([]string, 0)
+
+			if manualEdbInfo.SecName != secName {
+				// 校验是否存在相同指标名称
+				var condition string
+				var pars []interface{}
+				condition += " AND SEC_NAME=? AND left(TRADE_CODE,1)='W' AND REMARK='手动' AND TRADE_CODE != ? "
+				pars = append(pars, secName, req.TradeCode)
+				count, err := models.GetManualEdbCountByCondition(condition, pars)
+				if err != nil {
+					br.Msg = `指标保存失败`
+					br.ErrMsg = `获取同名指标信息异常,ERR:` + err.Error()
+					return
+				}
+				if count > 0 {
+					br.Msg = `已存在同名指标`
+					br.ErrMsg = `已存在同名指标`
+					br.IsSendEmail = false
+					return
+				}
+
+				updateCols = append(updateCols, "SecName")
+				manualEdbInfo.SecName = secName
+			}
+
+			if manualEdbInfo.ClassifyId != req.ClassifyId {
+				updateCols = append(updateCols, "ClassifyId")
+				manualEdbInfo.ClassifyId = req.ClassifyId
+			}
+
+			if manualEdbInfo.Frequency != frequency {
+				updateCols = append(updateCols, "Frequency")
+				manualEdbInfo.Frequency = frequency
+			}
+
+			if manualEdbInfo.Unit != unit {
+				updateCols = append(updateCols, "Unit")
+				manualEdbInfo.Unit = unit
+			}
+
+			if len(updateCols) > 0 {
+				isUpdateEdb = true
+				manualEdbInfo.ModifyTime = time.Now().Format(utils.FormatDateTime)
+				updateCols = append(updateCols, "ModifyTime")
+				err = manualEdbInfo.Update(updateCols)
+				if err != nil {
+					br.Msg = `指标保存失败`
+					br.ErrMsg = `指标保存失败,ERR:` + err.Error()
+					return
+				}
+			}
+		}
+
+		// 当前已经存在的指标明细数据
+		targetDataList, tmpErr := models.GetTargetsDataList(manualEdbInfo.TradeCode)
+		if tmpErr != nil {
+			err = tmpErr
+		}
+		existDataMap := make(map[string]string)
+		deleteDataMap := make(map[string]string)
+		for _, tmpData := range targetDataList {
+			existDataMap[tmpData.Dt] = tmpData.Close
+			deleteDataMap[tmpData.Dt] = tmpData.Dt
+		}
+
+		addDataList := make([]*models.Edbdata, 0)
+
+		for createDate, closeVal := range dateValueMap {
+			if createDate == "" || closeVal == "" {
+				continue
+			}
+			//判断数据是否已经存在
+			tmpVal, ok3 := existDataMap[createDate]
+			// 不存在,那么后面进行插入
+			if !ok3 {
+				addDataList = append(addDataList, &models.Edbdata{
+					TradeCode:  manualEdbInfo.TradeCode,
+					Dt:         createDate,
+					Close:      closeVal,
+					ModifyTime: time.Now(),
+				})
+				continue
+			}
+
+			delete(deleteDataMap, createDate)
+
+			// 库里面的数据
+			tmpValDecimal, tmpErr := decimal.NewFromString(tmpVal)
+			if tmpErr != nil {
+				fmt.Println("tmpVal Parse err:", tmpErr.Error())
+				err = tmpErr
+				continue
+			}
+			// 用户填写的数据
+			closeValDecimal, tmpErr := decimal.NewFromString(closeVal)
+			if tmpErr != nil {
+				fmt.Println("closeVal Parse err:", tmpErr.Error())
+				err = tmpErr
+				continue
+			}
+			if !tmpValDecimal.Equal(closeValDecimal) {
+				isUpdateData = true
+				fmt.Println("更新数值")
+				err = models.ModifyTargetsDataByImport(manualEdbInfo.TradeCode, createDate, closeVal)
+				if err != nil {
+					fmt.Println("ModifyTargetsDataByImport err:", err.Error())
+					errs = append(errs, err.Error())
+					//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 修改数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
+				}
+			}
+		}
+
+		// 新增指标
+		if len(addDataList) > 0 {
+			isUpdateData = true
+			err = models.OnlyMultiAddEdbdata(addDataList)
+			if err != nil {
+				fmt.Println("MultiAddEdbdata err:", err.Error())
+				errs = append(errs, err.Error())
+			}
+		}
+
+		// 删除不需要的日期数据
+		num := len(deleteDataMap)
+		if num > 0 {
+			isUpdateData = true
+			deleteDateList := make([]string, 0)
+			for date := range deleteDataMap {
+				deleteDateList = append(deleteDateList, date)
+			}
+
+			err = models.DelEdbdataByCodeAndDateList(manualEdbInfo.TradeCode, deleteDateList)
+			if err != nil {
+				fmt.Println("DelEdbdataByCodeAndDateList err:", err.Error())
+				errs = append(errs, err.Error())
+			}
+		}
+
+		// 添加记录
+		if isUpdateEdb {
+			record := &models.EdbinfoOpRecord{
+				TradeCode:  manualEdbInfo.TradeCode,
+				Remark:     "编辑指标",
+				UserId:     sysUser.AdminId,
+				UserName:   sysUser.RealName,
+				CreateTime: time.Now(),
+			}
+			go func() {
+				_ = record.Create()
+			}()
+		}
+
+		// 更新数据
+		if isUpdateData {
+			record := &models.EdbinfoOpRecord{
+				TradeCode:  manualEdbInfo.TradeCode,
+				Remark:     "更新数据",
+				UserId:     sysUser.AdminId,
+				UserName:   sysUser.RealName,
+				CreateTime: time.Now(),
+			}
+			go func() {
+				data.ModifyManualEdbMaxMinDate(manualEdbInfo.TradeCode)
+				_ = record.Create()
+			}()
+		}
+
+		//将该指标的code加入到 “手工数据导入后刷新” 缓存
+		if utils.Re == nil {
+			err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, manualEdbInfo.TradeCode)
+			if err != nil {
+				fmt.Println("CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
+			}
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+}
+
+// BatchEdbList
+// @Title 获取研究员指标
+// @Description 获取研究员指标
+// @Param	request	body models.BatchAddEdbReq true "type json string"
+// @Success 200 {object} models.TargetItemListResp
+// @router /target/edb/batch/list [post]
+func (c *ManualEdbController) BatchEdbList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	var req models.BatchManualEdbReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+
+	pageSize := req.PageSize
+	currentIndex := req.CurrentIndex
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	req.Keyword = strings.TrimSpace(req.Keyword)
+
+	var condition string
+	var pars []interface{}
+
+	// 是否加到指标库
+	condition += ` AND a.is_join_edb = ? `
+	pars = append(pars, 0)
+
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyIdList, err := data.GetUserManualClassifyIdList(sysUser.AdminId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+		num := len(classifyIdList)
+		if num > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+
+	}
+
+	// 关键词
+	if req.Keyword != "" {
+		condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+		pars = utils.GetLikeKeywordPars(pars, req.Keyword, 2)
+	}
+
+	// 所属分类
+	lenFrequency := len(req.FrequencyList)
+	if lenFrequency > 0 {
+		condition += ` AND a.frequency in (` + utils.GetOrmInReplace(lenFrequency) + `) `
+		pars = append(pars, req.FrequencyList)
+	}
+
+	// 所属分类
+	lenClassify := len(req.ClassifyIdList)
+	if lenClassify > 0 {
+		condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(lenClassify) + `) `
+		pars = append(pars, req.ClassifyIdList)
+	}
+
+	// 所属用户
+	lenUser := len(req.UserIdList)
+	if lenUser > 0 {
+		condition += ` AND a.user_id in (` + utils.GetOrmInReplace(lenUser) + `) `
+		pars = append(pars, req.UserIdList)
+	}
+
+	total, err := models.GetCountEdbInfoList(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	list, err := models.GetEdbInfoList(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	resp := models.EdbListResp{
+		List:   list,
+		Paging: paging.GetPaging(currentIndex, pageSize, total),
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+	br.IsAddLog = false
+}
+
+// BatchAddEdbCheck
+// @Title 新增校验
+// @Description 新增校验
+// @Param	request	body data_manage.BatchManualEdbReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /target/edb/batch/add/check [post]
+func (c *ManualEdbController) BatchAddEdbCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 最大批量添加的数量
+	codeMax := 30
+
+	var req models.BatchManualEdbReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+
+	req.Keyword = strings.TrimSpace(req.Keyword)
+
+	var condition string
+	var pars []interface{}
+
+	//
+	condition += ` AND a.is_join_edb = 0 `
+
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyIdList, err := data.GetUserManualClassifyIdList(sysUser.AdminId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+		num := len(classifyIdList)
+		if num > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+
+	}
+
+	if req.ListAll {
+		// 关键词
+		if req.Keyword != "" {
+			condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+			pars = utils.GetLikeKeywordPars(pars, req.Keyword, 2)
+		}
+
+		// 所属分类
+		lenFrequency := len(req.FrequencyList)
+		if lenFrequency > 0 {
+			condition += ` AND a.frequency in (` + utils.GetOrmInReplace(lenFrequency) + `) `
+			pars = append(pars, req.FrequencyList)
+		}
+
+		// 所属分类
+		lenClassify := len(req.ClassifyIdList)
+		if lenClassify > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(lenClassify) + `) `
+			pars = append(pars, req.ClassifyIdList)
+		}
+
+		// 所属用户
+		lenUser := len(req.UserIdList)
+		if lenUser > 0 {
+			condition += ` AND a.user_id in (` + utils.GetOrmInReplace(lenUser) + `) `
+			pars = append(pars, req.UserIdList)
+		}
+
+		lenTradeList := len(req.TradeCodeList)
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE not in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	} else {
+		lenTradeList := len(req.TradeCodeList)
+		if lenTradeList <= 0 {
+			br.Msg = "请选择指标"
+			br.ErrMsg = "请选择指标"
+			return
+		}
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	}
+
+	list, err := models.GetEdbInfoList(condition, pars, 0, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	if len(list) > codeMax {
+		br.Msg = fmt.Sprintf("最多只能选择%d个指标", codeMax)
+		return
+	}
+
+	br.Data = list
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// NameCheck
+// @Title 重名校验
+// @Description 批量新增
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /target/edb/batch/add/name_check [post]
+func (c *ManualEdbController) NameCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req []*data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+
+	type NameCheckResult struct {
+		EdbCode string
+		EdbName string
+		Exist   bool
+	}
+	indexNames := make([]string, 0)
+	resp := make([]*NameCheckResult, 0)
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		indexNames = append(indexNames, v.EdbName)
+		resp = append(resp, &NameCheckResult{
+			EdbCode: v.EdbCode,
+			EdbName: v.EdbName,
+		})
+	}
+
+	// 重名校验
+	edbList, e := data_manage.GetEdbInfoByNameArr(indexNames, utils.EDB_INFO_TYPE)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取重名指标失败, Err: " + e.Error()
+		return
+	}
+	nameExists := make(map[string]bool)
+	for _, v := range edbList {
+		nameExists[v.EdbName] = true
+	}
+	if len(nameExists) > 0 {
+		for _, v := range resp {
+			v.Exist = nameExists[v.EdbName]
+		}
+	}
+
+	br.Data = resp
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// BatchAdd2Edb
+// @Title 批量新增到指标库
+// @Description 批量新增到指标库
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /target/edb/batch/add [post]
+func (c *ManualEdbController) BatchAdd2Edb() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 最大批量添加的数量
+	codeMax := 30
+
+	deleteCache := true
+	cacheKey := "CACHE_EDB_INFO_BATCH_ADD_MANUAL_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(c.Ctx.Input.RequestBody)
+		return
+	}
+	var req []*data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if len(req) > codeMax {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+	indexNames := make([]string, 0)
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		indexNames = append(indexNames, v.EdbName)
+		v.Frequency = strings.TrimSpace(v.Frequency)
+		if v.Frequency == "" {
+			br.Msg = "请选择频度"
+			return
+		}
+		v.Unit = strings.TrimSpace(v.Unit)
+		if v.Unit == "" {
+			br.Msg = "请输入单位"
+			return
+		}
+		if v.ClassifyId <= 0 {
+			br.Msg = "请选择分类"
+			return
+		}
+	}
+
+	// 限定同一时间最多批量新增30个指标
+	for _, v := range req {
+		var r data.ManualIndexSource2EdbReq
+		r.EdbCode = v.EdbCode
+		r.EdbName = v.EdbName
+		r.Frequency = v.Frequency
+		r.Unit = v.Unit
+		r.ClassifyId = v.ClassifyId
+		r.AdminId = sysUser.AdminId
+		r.AdminRealName = sysUser.RealName
+
+		edbInfo, e, errMsg, skip := data.ManualIndexSource2Edb(r, c.Lang)
+		if e != nil {
+			br.Msg = "操作失败"
+			if errMsg != "" {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = e.Error()
+			return
+		}
+		if skip {
+			continue
+		}
+
+		// 试用平台更新用户累计新增指标数
+		if utils.BusinessCode == utils.BusinessCodeSandbox {
+			go func() {
+				adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+				if e != nil {
+					tips := fmt.Sprintf("试用平台更新用户累计新增指标数-获取用户失败, Err: " + e.Error())
+					utils.FileLog.Info(tips)
+					return
+				}
+				if adminItem.DepartmentName != "ETA试用客户" {
+					return
+				}
+				var ur etaTrialService.EtaTrialUserReq
+				ur.Mobile = adminItem.Mobile
+				_, _ = etaTrialService.UpdateUserIndexNum(ur)
+			}()
+		}
+
+		// 新增操作日志
+		{
+			edbLog := new(data_manage.EdbInfoLog)
+			edbLog.EdbInfoId = edbInfo.EdbInfoId
+			edbLog.SourceName = edbInfo.SourceName
+			edbLog.Source = edbInfo.Source
+			edbLog.EdbCode = edbInfo.EdbCode
+			edbLog.EdbName = edbInfo.EdbName
+			edbLog.ClassifyId = edbInfo.ClassifyId
+			edbLog.SysUserId = sysUser.AdminId
+			edbLog.SysUserRealName = sysUser.RealName
+			edbLog.CreateTime = time.Now()
+			edbLog.Content = string(c.Ctx.Input.RequestBody)
+			edbLog.Status = "新增指标"
+			edbLog.Method = c.Ctx.Input.URI()
+			go data_manage.AddEdbInfoLog(edbLog)
+		}
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// BatchDelEdbCheck
+// @Title 新增校验
+// @Description 新增校验
+// @Param	request	body data_manage.BatchManualEdbReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /target/edb/batch/del/check [post]
+func (c *ManualEdbController) BatchDelEdbCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.BatchManualEdbReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+
+	req.Keyword = strings.TrimSpace(req.Keyword)
+
+	var condition string
+	var pars []interface{}
+
+	// 就是为了找出已经加入指标库的指标
+	condition += ` AND a.is_join_edb = ? `
+	pars = append(pars, 1)
+
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyIdList, err := data.GetUserManualClassifyIdList(sysUser.AdminId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+		num := len(classifyIdList)
+		if num > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+
+	}
+
+	if req.ListAll {
+		// 关键词
+		if req.Keyword != "" {
+			condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+			pars = utils.GetLikeKeywordPars(pars, req.Keyword, 2)
+		}
+
+		// 所属分类
+		lenFrequency := len(req.FrequencyList)
+		if lenFrequency > 0 {
+			condition += ` AND a.frequency in (` + utils.GetOrmInReplace(lenFrequency) + `) `
+			pars = append(pars, req.FrequencyList)
+		}
+
+		// 所属分类
+		lenClassify := len(req.ClassifyIdList)
+		if lenClassify > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(lenClassify) + `) `
+			pars = append(pars, req.ClassifyIdList)
+		}
+
+		// 所属用户
+		lenUser := len(req.UserIdList)
+		if lenUser > 0 {
+			condition += ` AND a.user_id in (` + utils.GetOrmInReplace(lenUser) + `) `
+			pars = append(pars, req.UserIdList)
+		}
+
+		lenTradeList := len(req.TradeCodeList)
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE not in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	} else {
+		lenTradeList := len(req.TradeCodeList)
+		if lenTradeList <= 0 {
+			br.Msg = "请选择指标"
+			br.ErrMsg = "请选择指标"
+			return
+		}
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	}
+
+	list, err := models.GetEdbInfoList(condition, pars, 0, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	br.Data = list
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// BatchDel
+// @Title 批量删除
+// @Description 批量新增
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /target/edb/batch/del [post]
+func (c *ManualEdbController) BatchDel() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	deleteCache := true
+	cacheKey := "CACHE_EDB_INFO_BATCH_ADD_MANUAL_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(c.Ctx.Input.RequestBody)
+		return
+	}
+	var req models.BatchManualEdbReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+
+	req.Keyword = strings.TrimSpace(req.Keyword)
+
+	var condition string
+	var pars []interface{}
+
+	// 就是为了找出已经加入指标库的指标
+	condition += ` AND a.is_join_edb = ? `
+	pars = append(pars, 0)
+
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyIdList, err := data.GetUserManualClassifyIdList(sysUser.AdminId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+		num := len(classifyIdList)
+		if num > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+	}
+
+	if req.ListAll {
+		// 关键词
+		if req.Keyword != "" {
+			condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+			pars = utils.GetLikeKeywordPars(pars, req.Keyword, 2)
+		}
+
+		// 所属分类
+		lenFrequency := len(req.FrequencyList)
+		if lenFrequency > 0 {
+			condition += ` AND a.frequency in (` + utils.GetOrmInReplace(lenFrequency) + `) `
+			pars = append(pars, req.FrequencyList)
+		}
+
+		// 所属分类
+		lenClassify := len(req.ClassifyIdList)
+		if lenClassify > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(lenClassify) + `) `
+			pars = append(pars, req.ClassifyIdList)
+		}
+
+		// 所属用户
+		lenUser := len(req.UserIdList)
+		if lenUser > 0 {
+			condition += ` AND a.user_id in (` + utils.GetOrmInReplace(lenUser) + `) `
+			pars = append(pars, req.UserIdList)
+		}
+
+		lenTradeList := len(req.TradeCodeList)
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE not in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	} else {
+		lenTradeList := len(req.TradeCodeList)
+		if lenTradeList <= 0 {
+			br.Msg = "请选择指标"
+			br.ErrMsg = "请选择指标"
+			return
+		}
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	}
+
+	list, err := models.GetEdbInfoList(condition, pars, 0, 0)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	// TODO 二次校验指标是否已经加到指标库
+	{
+		codeList := make([]string, 0)
+		// 限定同一时间最多批量新增30个指标
+		for _, v := range list {
+			codeList = append(codeList, v.TradeCode)
+		}
+		err = models.DelManualIndexByCodeList(codeList)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	br.Msg = "删除成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// ImportData
+// @Title 导入数据
+// @Description 导入数据
+// @Param   EntryFile   query   file  true       "文件"
+// @Success 200 Ret=200 录入成功
+// @router /import/data [post]
+func (c *ManualEdbController) ImportData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+	file, _, err := c.GetFile("EntryFile")
+	if err != nil {
+		br.Msg = "获取文件失败"
+		br.ErrMsg = "获取文件失败,Err:" + err.Error()
+		return
+	}
+	path := "./static/数据导入_" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	defer file.Close()
+	err = c.SaveToFile("EntryFile", path)
+	if err != nil {
+		br.Msg = "文件保存失败"
+		br.ErrMsg = "文件保存失败,Err:" + err.Error()
+		return
+	}
+	if utils.RunMode == "debug" {
+		defer os.Remove(path)
+	}
+
+	successCount, failCount, err, errMsg := data.ImportManualData(path, sysUser)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	resp := models.EdbdataImportResp{
+		SuccessCount: successCount,
+		FailCount:    failCount,
+	}
+	if failCount > 0 {
+		if successCount == 0 {
+			resp.Status = -1
+			resp.Msg = "导入失败"
+		} else {
+			resp.Status = 1
+			resp.Msg = "存在部分导入失败"
+		}
+	} else {
+		resp.Status = 0
+	}
+	br.Msg = "导入成功"
+	br.Ret = 200
+	br.Success = true
+	br.Data = resp
+}
+
+// RecordList
+// @Title 获取指标操作记录列表
+// @Description 获取指标操作记录列表
+// @Param   TradeCode   query   string  false       "指标编码"
+// @Success 200 {object} models.EdbinfoOpRecordListResp
+// @router /target/edb/op/record/list [get]
+func (c *ManualEdbController) RecordList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	// 频度
+	tradeCode := c.GetString("TradeCode")
+	if tradeCode == `` {
+		br.Msg = `请选择指标`
+		br.ErrMsg = `请选择指标`
+		br.IsSendEmail = false
+		return
+	}
+
+	total, list, err := models.GetEdbinfoOpRecordPageList(tradeCode, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	resp := models.EdbinfoOpRecordListResp{
+		List:   list,
+		Paging: paging.GetPaging(currentIndex, pageSize, total),
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 15 - 12
controllers/report_chapter.go

@@ -7,8 +7,6 @@ import (
 	"eta/eta_api/services"
 	"eta/eta_api/services/data"
 	"eta/eta_api/utils"
-	"fmt"
-	"github.com/kgiannakakis/mp3duration/src/mp3duration"
 	"html"
 	"os"
 	"path"
@@ -1198,15 +1196,20 @@ func (this *ReportController) VoiceUpload() {
 		return
 	}
 
-	var playSeconds float64
-	playSeconds, err = mp3duration.Calculate(fPath)
-	if playSeconds <= 0 {
-		playSeconds, err = utils.GetVideoPlaySeconds(fPath)
-		if err != nil {
-			br.Msg = "获取音频时间失败"
-			br.ErrMsg = "获取音频时间失败,Err:" + err.Error()
-			return
-		}
+	//var playSeconds float64
+	//playSeconds, err = mp3duration.Calculate(fPath)
+	//if playSeconds <= 0 {
+	//	playSeconds, err = utils.GetVideoPlaySeconds(fPath)
+	//	if err != nil {
+	//		br.Msg = "获取音频时间失败"
+	//		br.ErrMsg = "获取音频时间失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
+
+	playSecondsStr, err := utils.GetDuration(fPath) //mp3duration.Calculate(fPath)
+	if err != nil {
+		utils.FileLog.Info("获取音频时间失败,Err:" + err.Error())
 	}
 
 	fileBody, err := os.ReadFile(fPath)
@@ -1226,7 +1229,7 @@ func (this *ReportController) VoiceUpload() {
 
 		reportChapterInfo.VideoUrl = resourceUrl
 		reportChapterInfo.VideoName = videoName
-		reportChapterInfo.VideoPlaySeconds = fmt.Sprint(playSeconds)
+		reportChapterInfo.VideoPlaySeconds = playSecondsStr //fmt.Sprint(playSeconds)
 		reportChapterInfo.VideoSize = sizeStr
 		reportChapterInfo.VideoKind = 1
 		reportChapterInfo.LastModifyAdminId = this.SysUser.AdminId

+ 1 - 1
controllers/report_v2.go

@@ -1426,7 +1426,7 @@ func (this *ReportController) PrePublishReport() {
 
 	// 生成报告pdf和长图
 	{
-		reportPdfUrl := services.GetGeneralPdfUrl(reportDetail.ReportCode, reportDetail.ReportLayout)
+		reportPdfUrl := services.GetGeneralPdfUrl(reportDetail.ReportCode, reportDetail.ClassifyNameFirst, reportDetail.ReportLayout)
 		go services.Report2pdfAndJpeg(reportPdfUrl, reportDetail.Id, 1)
 	}
 

+ 621 - 482
controllers/target.go

@@ -18,9 +18,6 @@ import (
 	"eta/eta_api/services/data"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
-	"github.com/shopspring/decimal"
-	"github.com/tealeg/xlsx"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -28,6 +25,10 @@ import (
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/shopspring/decimal"
+	"github.com/tealeg/xlsx"
 )
 
 // TargetController 手工指标服务(鉴权)
@@ -183,6 +184,28 @@ func (this *TargetController) DataAdd() {
 		br.ErrMsg = "新增失败,Err:" + err.Error()
 		return
 	}
+
+	// 添加操作记录
+	go func() {
+		record := &models.EdbinfoOpRecord{
+			TradeCode:  edbdata.TradeCode,
+			Remark:     "更新数据",
+			UserId:     this.SysUser.AdminId,
+			UserName:   this.SysUser.RealName,
+			CreateTime: time.Now(),
+		}
+		data.ModifyManualEdbMaxMinDate(edbdata.TradeCode)
+		_ = record.Create()
+
+		//将该指标的code加入到 “手工数据导入后刷新” 缓存
+		if utils.Re == nil {
+			err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, req.TradeCode)
+			if err != nil {
+				fmt.Println("CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
+			}
+		}
+	}()
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "新增成功"
@@ -314,6 +337,21 @@ func (this *TargetController) DataEdit() {
 			}
 		}
 	}
+
+	// 数据操作完后的最大最小值/指标库更新
+	{
+		go func() {
+			data.ModifyManualEdbMaxMinDate(req.TradeCode)
+			//将该指标的code加入到 “手工数据导入后刷新” 缓存
+			if utils.Re == nil {
+				err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, req.TradeCode)
+				if err != nil {
+					fmt.Println("CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
+				}
+			}
+		}()
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "编辑成功"
@@ -443,7 +481,7 @@ func (this *TargetController) TargetList() {
 		return
 	}
 	total := 0
-	list := make([]*models.Edbinfo, 0)
+	list := make([]*models.EdbinfoItem, 0)
 
 	//有分类数据权限才查询
 	if len(classifyIdStrList) > 0 {
@@ -482,14 +520,14 @@ func (this *TargetController) TargetList() {
 			br.ErrMsg = "获取失败,Err:" + err.Error()
 			return
 		}
-		tmpList, err := models.GetEdbinfoList(condition, pars, startSize, pageSize, mobile, sysUser.RoleType)
+		tmpList, err := models.GetEdbinfoItemList(condition, pars, startSize, pageSize, mobile, sysUser.RoleType)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取失败,Err:" + err.Error()
 			return
 		}
 		if len(tmpList) <= 0 {
-			list = make([]*models.Edbinfo, 0)
+			list = make([]*models.EdbinfoItem, 0)
 		} else {
 			list = tmpList
 		}
@@ -576,7 +614,8 @@ func (this *TargetController) TargetAdd() {
 		br.Msg = "指标名称已存在"
 		return
 	}
-	err = models.AddEdbinfo(maxTradeCode, req.SecName, req.Unit, "手动", req.Frequency, req.NoticeTime, req.ClassifyId, sysUser.AdminId)
+
+	err = models.AddEdbinfo(maxTradeCode, req.SecName, req.Unit, "手动", req.Frequency, req.NoticeTime, req.ClassifyId, sysUser.AdminId, sysUser.RealName)
 	if err != nil {
 		br.Msg = "新增失败"
 		br.ErrMsg = "新增失败,Err:" + err.Error()
@@ -749,10 +788,12 @@ func (this *TargetController) ClassifyList() {
 			edbInfoGroupCountMap[edbInfoGroupCount.ClassifyId] = edbInfoGroupCount.Count
 		}
 		for _, classifyList := range list {
+			classifyList.UniqueCode = utils.MD5(fmt.Sprint(classifyList.ClassifyId))
 			if classifyList.Child != nil {
 				for _, classify := range classifyList.Child {
 					if total, ok := edbInfoGroupCountMap[classify.ClassifyId]; ok {
 						classify.EdbInfoTotal = total
+						classify.UniqueCode = utils.MD5(fmt.Sprint(classify.ClassifyId))
 					}
 				}
 			}
@@ -770,6 +811,7 @@ func (this *TargetController) ClassifyList() {
 // @Title 下载模板
 // @Description 下载模板
 // @Success 200 {object} models.EdbdataClassifyResp
+// @Param   Source   query   int  false       "来源:1:模板1;2:模板2"
 // @router /template [get]
 func (this *TargetCommonController) TemplateDownload() {
 	br := new(models.BaseResponse).Init()
@@ -777,8 +819,15 @@ func (this *TargetCommonController) TemplateDownload() {
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
+	source, _ := this.GetInt("Source")
+	switch source {
+	case 2:
+		this.Ctx.Output.Download("./static/template/导入模板2.xlsx", "数据导入模板2.xlsx")
+	default:
+		this.Ctx.Output.Download("./static/template/导入模板1.xlsx", "数据导入模板1.xlsx")
+	}
 
-	this.Ctx.Output.Download("./static/数据导入模板.xlsx", "数据导入模板.xlsx")
+	//this.Ctx.Output.Download("./static/数据导入模板.xlsx", "数据导入模板.xlsx")
 
 	br.Ret = 200
 	br.Success = true
@@ -804,460 +853,6 @@ func (this *TargetCommonController) TemplateDownloadEn() {
 	br.Msg = "下载成功"
 }
 
-// ImportData
-// @Title 导入数据
-// @Description 导入数据
-// @Param   EntryFile   query   file  true       "文件"
-// @Success 200 Ret=200 录入成功
-// @router /import/data [post]
-func (this *TargetController) ImportData() {
-	br := new(models.BaseResponse).Init()
-	defer func() {
-		this.Data["json"] = br
-		this.ServeJSON()
-	}()
-	sysUser := this.SysUser
-	if sysUser == nil {
-		br.Msg = "请重新登录"
-		return
-	}
-	file, _, err := this.GetFile("EntryFile")
-	if err != nil {
-		br.Msg = "获取文件失败"
-		br.ErrMsg = "获取文件失败,Err:" + err.Error()
-		return
-	}
-	path := "./static/数据导入_" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
-	defer file.Close()
-	err = this.SaveToFile("EntryFile", path)
-	if err != nil {
-		br.Msg = "文件保存失败"
-		br.ErrMsg = "文件保存失败,Err:" + err.Error()
-		return
-	}
-	if utils.RunMode == "debug" {
-		defer os.Remove(path)
-	}
-	xlFile, err := xlsx.OpenFile(path)
-	if err != nil {
-		fmt.Println(err.Error())
-		return
-	}
-
-	//超管账号可以查看分类下的所有频度数据
-	userId := sysUser.AdminId
-	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN {
-		userId = 0
-	}
-	//获取账户所拥有权限的分类id集合
-	classifyNameStrList, edbDataClassifyMap, err := data.GetEdbClassifyNameListByAdminId(int64(userId))
-	if err != nil {
-		br.Msg = "获取分类数据失败"
-		return
-	}
-
-	//导入成功数量
-	successCount := 0
-	//导入失败数据
-	failDatas := make([]*models.EdbdataImportFail, 0)
-
-	//指标map
-	targetMap := make(map[string]*models.Edbinfo)
-	defer func() {
-		for _, target := range targetMap {
-			//结束后,清除掉对应的缓存
-			key := "import:edbinfo:data:" + target.TradeCode
-			utils.Rc.Delete(key)
-
-			//将该指标的code加入到 “手工数据导入后刷新” 缓存
-			if utils.Re == nil {
-				err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, target.TradeCode)
-				if err != nil {
-					fmt.Println("CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
-				}
-			}
-		}
-	}()
-
-	// 所有的指标数据map
-	edbCodeDataMap := make(map[string]map[string]string)
-
-	// 遍历sheet页读取
-	for _, sheet := range xlFile.Sheets {
-		fmt.Println("sheet name: ", sheet.Name)
-		//遍历行读取
-		maxRow := sheet.MaxRow
-		fmt.Println("maxRow:", maxRow)
-		fmt.Println("maxRow")
-		for i := 0; i < maxRow; i++ {
-			if i == 1 {
-				row := sheet.Row(i)
-				cells := row.Cells
-				if len(cells) < 6 {
-					br.ErrMsg = "导入文件异常,请下载最新导入模板文件"
-					br.Msg = "导入文件异常,请下载最新导入模板文件"
-					return
-				}
-				templateFail := false
-				if cells[0].Value != "品种分类" && cells[0].Value != "Species Category" {
-					templateFail = true
-				}
-				if cells[1].Value != "录入日期" && cells[1].Value != "Input Date" {
-					templateFail = true
-				}
-				if cells[2].Value != "指标名称" && cells[2].Value != "Indicator Name Indicator Name" {
-					templateFail = true
-				}
-				if cells[3].Value != "值" && cells[3].Value != "Value" {
-					templateFail = true
-				}
-				if cells[4].Value != "频度" && cells[4].Value != "Frequency" {
-					templateFail = true
-				}
-				if cells[5].Value != "单位" && cells[5].Value != "Unit" {
-					templateFail = true
-				}
-				if templateFail {
-					br.ErrMsg = "导入文件异常,请下载最新导入模板文件"
-					br.Msg = "导入文件异常,请下载最新导入模板文件"
-					return
-				}
-			}
-			if i > 1 {
-				row := sheet.Row(i)
-				cells := row.Cells
-				if len(cells) >= 6 {
-					classifyName := cells[0].Value //分类
-					if classifyName == "" {        //过滤空白行
-						continue
-					}
-					cell1 := cells[1].Value
-					//createDate := utils.ConvertToFormatDay(cell1) //录入日期
-					createDate := cell1
-					cell2 := cells[2].Value //指标名称
-					secName := utils.TrimStr(cell2)
-					frequency := cells[4].Value //频度
-					unit := cells[5].Value      //单位
-
-					closeVal := cells[3].Value //值
-					if strings.Contains(closeVal, "#N/A") {
-						continue
-					}
-
-					//校验表格中的日期格式
-					if strings.Contains(createDate, "-") {
-						//如果是带有 - 的普通日期格式文本
-						_, timeErr := time.Parse("2006-1-2", createDate)
-						if timeErr != nil {
-							failItem := new(models.EdbdataImportFail)
-							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-							failItem.ClassifyName = classifyName
-							failItem.CreateDate = createDate
-							failItem.SecName = secName
-							failItem.Close = closeVal
-							failItem.Remark = "日期格式异常"
-							failItem.Frequency = frequency
-							failItem.Unit = unit
-							failDatas = append(failDatas, failItem)
-							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
-							continue
-						}
-					} else if strings.Contains(createDate, "/") {
-						//如果是带有 / 的普通日期格式文本
-						createDateTime, timeErr := time.Parse("2006/1/2", createDate)
-						if timeErr != nil {
-							failItem := new(models.EdbdataImportFail)
-							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-							failItem.ClassifyName = classifyName
-							failItem.CreateDate = createDate
-							failItem.SecName = secName
-							failItem.Close = closeVal
-							failItem.Remark = "日期格式异常"
-							failItem.Frequency = frequency
-							failItem.Unit = unit
-							failDatas = append(failDatas, failItem)
-							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
-							continue
-						}
-						createDate = createDateTime.Format("2006-01-02")
-					} else {
-						//可能是excel的日期格式
-						_, tmpErr := strconv.Atoi(createDate)
-						if tmpErr != nil {
-							failItem := new(models.EdbdataImportFail)
-							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-							failItem.ClassifyName = classifyName
-							failItem.CreateDate = createDate
-							failItem.SecName = secName
-							failItem.Close = closeVal
-							failItem.Remark = "日期格式异常"
-							failItem.Frequency = frequency
-							failItem.Unit = unit
-							failDatas = append(failDatas, failItem)
-							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
-							continue
-						}
-						createDate = utils.ConvertToFormatDay(createDate) //录入日期
-					}
-
-					closeValFloat, err := cells[3].Float() //值
-
-					if err != nil {
-						failItem := new(models.EdbdataImportFail)
-						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-						failItem.ClassifyName = classifyName
-						failItem.CreateDate = createDate
-						failItem.SecName = secName
-						failItem.Close = cells[3].Value
-						failItem.Remark = "值类型异常"
-						failItem.Frequency = frequency
-						failItem.Unit = unit
-						failDatas = append(failDatas, failItem)
-						continue
-					}
-					newDecimal := decimal.NewFromFloat(closeValFloat)
-					newDecimal.Round(4)
-					closeVal = newDecimal.String()
-					if strings.Contains(closeVal, "#N/A") {
-						continue
-					}
-					//closeVal := closeValFloat.
-
-					//判断指标,类型,等数据是否正常
-					classifyName = strings.Trim(classifyName, " ")
-					frequency = utils.TrimStr(frequency)
-					unit = utils.TrimStr(unit)
-
-					// 成功数量超过20个指标,那就不导入了
-					if len(targetMap) >= 150 {
-						failItem := new(models.EdbdataImportFail)
-						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-						failItem.ClassifyName = classifyName
-						failItem.CreateDate = createDate
-						failItem.SecName = secName
-						failItem.Close = closeVal
-						failItem.Remark = "导入指标数量过多"
-						failItem.Frequency = frequency
-						failItem.Unit = unit
-						failDatas = append(failDatas, failItem)
-						//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
-						continue
-					}
-
-					if !strings.Contains(strings.Join(classifyNameStrList, ","), classifyName) {
-						failItem := new(models.EdbdataImportFail)
-						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-						failItem.ClassifyName = classifyName
-						failItem.CreateDate = createDate
-						failItem.SecName = secName
-						failItem.Close = closeVal
-						failItem.Remark = "没有该品种分类权限"
-						failItem.Frequency = frequency
-						failItem.Unit = unit
-						failDatas = append(failDatas, failItem)
-						continue
-					}
-					//fmt.Println(classifyName, createDate, secName, closeVal)
-
-					//获取指标分类信息
-					classify, ok := edbDataClassifyMap[classifyName]
-					if !ok {
-						failItem := new(models.EdbdataImportFail)
-						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-						failItem.ClassifyName = classifyName
-						failItem.CreateDate = createDate
-						failItem.SecName = secName
-						failItem.Close = closeVal
-						failItem.Remark = "指标分类不存在"
-						failItem.Frequency = frequency
-						failItem.Unit = unit
-						failDatas = append(failDatas, failItem)
-						//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
-						continue
-					}
-
-					//判断指标是否存在
-					target, ok := targetMap[secName]
-					if !ok {
-						tmpTarget, err := models.GetTargetBySecName(secName)
-						if err != nil {
-							//如果是找不到该指标,那么新增指标
-							if err.Error() == utils.ErrNoRow() {
-								if frequency == "" {
-									failItem := new(models.EdbdataImportFail)
-									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-									failItem.ClassifyName = classifyName
-									failItem.CreateDate = createDate
-									failItem.SecName = secName
-									failItem.Close = closeVal
-									failItem.Remark = "新增指标失败,频度字段为空"
-									failItem.Frequency = frequency
-									failItem.Unit = unit
-									failDatas = append(failDatas, failItem)
-									continue
-								}
-								if unit == "" {
-									failItem := new(models.EdbdataImportFail)
-									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-									failItem.ClassifyName = classifyName
-									failItem.CreateDate = createDate
-									failItem.SecName = secName
-									failItem.Close = closeVal
-									failItem.Remark = "新增指标失败,单位字段为空"
-									failItem.Frequency = frequency
-									failItem.Unit = unit
-									failDatas = append(failDatas, failItem)
-									continue
-								}
-								tmpErr := data.AddEdbInfo(secName, unit, frequency, "", sysUser.Mobile, classify.ClassifyId, sysUser.AdminId)
-								if tmpErr != nil {
-									fmt.Println("line 158")
-									failItem := new(models.EdbdataImportFail)
-									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-									failItem.ClassifyName = classifyName
-									failItem.CreateDate = createDate
-									failItem.SecName = secName
-									failItem.Close = closeVal
-									failItem.Remark = "新增指标失败"
-									failItem.Frequency = frequency
-									failItem.Unit = unit
-									failDatas = append(failDatas, failItem)
-									continue
-								}
-								tmpTarget, tmpErr := models.GetTargetBySecName(secName)
-								target = tmpTarget
-								targetMap[secName] = target
-							} else {
-								fmt.Println("导入数据 获取指标:Err:" + err.Error())
-							}
-						} else {
-							target = tmpTarget
-							targetMap[secName] = target
-						}
-
-						//设置10分钟缓存,不允许其他地方删除
-						key := "import:edbinfo:data:" + target.TradeCode
-						utils.Rc.SetNX(key, 1, time.Second*600)
-					}
-
-					if target == nil {
-						fmt.Println("指标不存在")
-						failItem := new(models.EdbdataImportFail)
-						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-						failItem.ClassifyName = classifyName
-						failItem.CreateDate = createDate
-						failItem.SecName = secName
-						failItem.Close = closeVal
-						failItem.Remark = "指标不存在"
-						failItem.Frequency = frequency
-						failItem.Unit = unit
-						failDatas = append(failDatas, failItem)
-						continue
-					}
-
-					//更新指标信息
-					updateCols := make([]string, 0)
-					//更新指标分类
-					if target.ClassifyId <= 0 && classify.ClassifyId > 0 {
-						target.ClassifyId = classify.ClassifyId
-						updateCols = append(updateCols, "ClassifyId")
-					}
-					if target.Frequency != frequency {
-						target.Frequency = frequency
-						target.NoticeTime = ""
-						updateCols = append(updateCols, "Frequency", "NoticeTime")
-					}
-					if target.Unit != unit {
-						target.Unit = unit
-						updateCols = append(updateCols, "Unit")
-					}
-					if len(updateCols) > 0 {
-						_ = target.Update(updateCols)
-					}
-
-					// 判断指标数据列表是否已经存在
-					tmpDataMap, ok := edbCodeDataMap[target.TradeCode]
-					if !ok {
-						tmpDataMap = make(map[string]string)
-						dataList, tmpErr := models.GetTargetsDataList(target.TradeCode)
-						if tmpErr != nil {
-							go alarm_msg.SendAlarmMsg("导入数据"+target.TradeCode+" 获取指标的数据失败,Err:"+err.Error(), 3)
-						}
-						for _, tmpData := range dataList {
-							tmpDataMap[tmpData.Dt] = tmpData.Close
-						}
-						edbCodeDataMap[target.TradeCode] = tmpDataMap
-					}
-
-					//判断数据是否已经存在
-					tmpVal, ok := tmpDataMap[createDate]
-					//数据已存在,进行更新操作
-					if ok {
-						if tmpVal != closeVal {
-							err = models.ModifyTargetsDataByImport(target.TradeCode, createDate, closeVal)
-							if err != nil {
-								go alarm_msg.SendAlarmMsg("导入数据 修改数据失败,Err:"+err.Error(), 3)
-								//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 修改数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
-							}
-						}
-					} else { //数据不存在,进行新增操作
-						if target.TradeCode != "" && createDate != "" && closeVal != "" {
-							models.AddTargetsDataByImport(target.TradeCode, createDate, closeVal)
-							if err != nil {
-								go alarm_msg.SendAlarmMsg("导入数据 新增数据失败,Err:"+err.Error(), 3)
-								//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 新增数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
-							}
-							tmpDataMap[createDate] = closeVal
-							edbCodeDataMap[target.TradeCode] = tmpDataMap
-						}
-					}
-					successCount++
-				} else {
-					//br.ErrMsg = "请填写导入内容"
-					//br.Msg = "请填写导入内容"
-					//return
-				}
-			}
-		}
-	}
-	resp := models.EdbdataImportResp{
-		SuccessCount: successCount,
-		FailCount:    len(failDatas),
-	}
-	fmt.Println("failDatas:", len(failDatas))
-	if len(failDatas) > 0 {
-		//先删除导入失败记录
-		_ = models.DelEdbDataImportFail(sysUser.AdminId)
-		//错误信息字符串切片,最后作为发送邮件通知使用
-		failContents := make([]string, 0)
-		for _, v := range failDatas {
-			failContents = append(failContents, fmt.Sprint(v.SecName, "导入失败:", v.Remark))
-			err = models.AddEdbdataImportFail(v)
-			if err != nil {
-				go alarm_msg.SendAlarmMsg("导入数据 新增失败记录失败,Err:"+err.Error(), 3)
-				//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 新增失败记录失败:Err:"+err.Error(), utils.EmailSendToUsers)
-			}
-		}
-		//导入失败的话,最后统一邮件提醒就好啦,不需要每次都去提醒
-		go alarm_msg.SendAlarmMsg("导入数据 存在部分数据导入失败:"+strings.Join(failContents, ";"), 3)
-		//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 存在部分数据导入失败:"+strings.Join(failContents, ";"), utils.EmailSendToUsers)
-
-		if successCount == 0 {
-			resp.Status = -1
-			resp.Msg = "导入失败"
-		} else {
-			resp.Status = 1
-			resp.Msg = "存在部分导入失败"
-		}
-	} else {
-		resp.Status = 0
-	}
-	br.Msg = "导入成功"
-	br.Ret = 200
-	br.Success = true
-	br.Data = resp
-}
-
 // ImportFailListDownload
 // @Title 下载失败列表
 // @Description 下载失败列表
@@ -1287,29 +882,19 @@ func (this *TargetController) ImportFailListDownload() {
 	}
 	sheet, err := xlsxFile.AddSheet(SheetName)
 	headRow := sheet.AddRow()
-	if this.Lang == utils.EnLangVersion {
-		headRow.AddCell().SetValue("Species Category")
-		headRow.AddCell().SetValue("Input Date")
-		headRow.AddCell().SetValue("Indicator Name Indicator Name")
-		headRow.AddCell().SetValue("Value")
-		headRow.AddCell().SetValue("Frequency")
-		headRow.AddCell().SetValue("Unit")
-		headRow.AddCell().SetValue("Remark")
-	} else {
-		headRow.AddCell().SetValue("品种分类")
-		headRow.AddCell().SetValue("录入日期")
-		headRow.AddCell().SetValue("指标名称")
-		headRow.AddCell().SetValue("值")
-		headRow.AddCell().SetValue("频度")
-		headRow.AddCell().SetValue("单位")
-		headRow.AddCell().SetValue("备注")
-	}
+	headRow.AddCell().SetValue("品种/Variety")
+	headRow.AddCell().SetValue("指标名称/Indicator Name")
+	headRow.AddCell().SetValue("指标日期/Indicator Date")
+	headRow.AddCell().SetValue("值/Value")
+	headRow.AddCell().SetValue("频度/Frequency")
+	headRow.AddCell().SetValue("单位/Unit")
+	headRow.AddCell().SetValue("备注/Note")
 
 	for _, v := range item {
 		row := sheet.AddRow()
 		row.AddCell().SetValue(v.ClassifyName)
-		row.AddCell().SetValue(v.CreateDate)
 		row.AddCell().SetValue(v.SecName)
+		row.AddCell().SetValue(v.CreateDate)
 		row.AddCell().SetString(v.Close)
 		row.AddCell().SetValue(v.Frequency)
 		row.AddCell().SetValue(v.Unit)
@@ -3243,7 +2828,7 @@ func (this *TargetController) ImportTarget() {
 						failDatas = append(failDatas, failItem)
 						continue
 					}
-					err = models.AddEdbinfo(maxTradeCode, secName, unit, "手动", frequency, "", classify.ClassifyId, sysUser.AdminId)
+					err = models.AddEdbinfo(maxTradeCode, secName, unit, "手动", frequency, "", classify.ClassifyId, sysUser.AdminId, sysUser.RealName)
 					if err != nil {
 						failItem := &models.EdbImportFail{
 							ClassifyName: classifyName,
@@ -3659,6 +3244,88 @@ func (this *TargetController) ExcelDataAdd() {
 	// 所有的指标数据map
 	edbCodeDataMap := make(map[string]map[string]string)
 
+	addEdbTradeMap := make(map[string]bool)
+	updateEdbTradeMap := make(map[string]bool)
+	updateDataTradeMap := make(map[string]bool)
+	defer func() {
+		go func(addEdbTradeMap, updateEdbTradeMap, updateDataTradeMap map[string]bool) {
+			addRecordList := make([]*models.EdbinfoOpRecord, 0)
+			for tradeCode := range addEdbTradeMap {
+				addRecordList = append(addRecordList, &models.EdbinfoOpRecord{
+					TradeCode:  tradeCode,
+					Remark:     "创建指标",
+					UserId:     sysUser.AdminId,
+					UserName:   sysUser.RealName,
+					CreateTime: time.Now(),
+				})
+			}
+			for tradeCode := range updateEdbTradeMap {
+				addRecordList = append(addRecordList, &models.EdbinfoOpRecord{
+					TradeCode:  tradeCode,
+					Remark:     "编辑指标",
+					UserId:     sysUser.AdminId,
+					UserName:   sysUser.RealName,
+					CreateTime: time.Now(),
+				})
+			}
+			for tradeCode := range updateDataTradeMap {
+				addRecordList = append(addRecordList, &models.EdbinfoOpRecord{
+					TradeCode:  tradeCode,
+					Remark:     "更新数据",
+					UserId:     sysUser.AdminId,
+					UserName:   sysUser.RealName,
+					CreateTime: time.Now(),
+				})
+			}
+			if len(addRecordList) > 0 {
+				obj := models.EdbinfoOpRecord{}
+				_ = obj.MulCreate(addRecordList)
+			}
+
+			//将该指标的code加入到 “手工数据导入后刷新” 缓存
+			if utils.Re == nil {
+				for tradeCode := range updateDataTradeMap {
+					// 更新手工数据的最大最小值
+					data.ModifyManualEdbMaxMinDate(tradeCode)
+					//将该指标的code加入到 “手工数据导入后刷新” 缓存
+					err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, tradeCode)
+					if err != nil {
+						fmt.Println("CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
+					}
+				}
+
+			}
+		}(addEdbTradeMap, updateEdbTradeMap, updateDataTradeMap)
+	}()
+
+	//超管账号可以查看分类下的所有频度数据
+	userId := sysUser.AdminId
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN {
+		userId = 0
+
+		// 校验下当前用户是否有该指标的权限
+		count, err := models.GetCountManualUserClassify(sysUser.AdminId, req.ClassifyId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.ErrMsg = "获取分类数据失败,err:" + err.Error()
+			return
+		}
+
+		if count <= 0 {
+			br.Msg = "无权访问"
+			br.ErrMsg = "无权访问"
+			br.IsSendEmail = false
+			return
+		}
+	}
+	//获取账户所拥有权限的分类id集合
+	classifyIdList, err := data.GetUserManualClassifyIdList(userId)
+	if err != nil {
+		br.Msg = "获取分类数据失败"
+		br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+		return
+	}
+
 	//操作指标,新增指标及数据等
 	{
 		for i := 0; i < len(secNameList); i++ {
@@ -3720,7 +3387,7 @@ func (this *TargetController) ExcelDataAdd() {
 								//	continue
 								//}
 
-								tmpErr := data.AddEdbInfo(secName, unit, frequency, "", sysUser.Mobile, req.ClassifyId, sysUser.AdminId)
+								tmpErr := data.AddEdbInfo(secName, unit, frequency, "", sysUser.Mobile, req.ClassifyId, sysUser.AdminId, sysUser.RealName)
 								if tmpErr != nil {
 									//fmt.Println("AddEdbInfo err:", err.Error())
 									utils.FileLogData.Error("AddEdbInfo err :%s", tmpErr.Error())
@@ -3729,6 +3396,9 @@ func (this *TargetController) ExcelDataAdd() {
 								tmpTarget, tmpErr := models.GetTargetBySecName(secName)
 								target = tmpTarget
 								targetMap[secName] = target
+
+								// 指标新增
+								addEdbTradeMap[target.TradeCode] = true
 							} else {
 								fmt.Println("导入数据 获取指标:Err:" + err.Error())
 							}
@@ -3738,6 +3408,11 @@ func (this *TargetController) ExcelDataAdd() {
 						}
 					}
 
+					// 没有该分类品种权限的话,那么就过滤
+					if !utils.InArrayByInt(classifyIdList, target.ClassifyId) {
+						continue
+					}
+
 					//判断指标数据是否已经存在
 					tmpDataMap, ok2 := edbCodeDataMap[target.TradeCode]
 					if !ok2 {
@@ -3779,6 +3454,8 @@ func (this *TargetController) ExcelDataAdd() {
 									errs = append(errs, err.Error())
 									//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 修改数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
 								}
+								// 指标数据变更
+								updateDataTradeMap[target.TradeCode] = true
 							}
 						} else {
 							fmt.Println("删除数值")
@@ -3788,6 +3465,8 @@ func (this *TargetController) ExcelDataAdd() {
 								errs = append(errs, err.Error())
 								//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 修改数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
 							}
+							// 指标数据变更
+							updateDataTradeMap[target.TradeCode] = true
 						}
 
 						if frequency != target.Frequency || unit != target.Unit || req.ClassifyId != target.ClassifyId {
@@ -3797,6 +3476,10 @@ func (this *TargetController) ExcelDataAdd() {
 								fmt.Println("EditEdbinfo err:", err.Error())
 								return
 							}
+							// 指标信息变更
+							if _, isAdd := addEdbTradeMap[target.TradeCode]; !isAdd {
+								updateEdbTradeMap[target.TradeCode] = true
+							}
 						}
 					} else { //数据不存在,进行新增操作
 						if target.TradeCode != "" && createDate != "" && closeVal != "" {
@@ -3810,6 +3493,8 @@ func (this *TargetController) ExcelDataAdd() {
 
 							tmpDataMap[createDate] = closeVal
 							edbCodeDataMap[target.TradeCode] = tmpDataMap
+							// 指标数据变更
+							updateDataTradeMap[target.TradeCode] = true
 						}
 					}
 
@@ -3900,3 +3585,457 @@ func (this *TargetController) ExcelDataEdit() {
 	//br.Data = resp
 	br.Data = item
 }
+
+//// ImportData
+//// @Title 导入数据
+//// @Description 导入数据
+//// @Param   EntryFile   query   file  true       "文件"
+//// @Success 200 Ret=200 录入成功
+//// @router /import/data [post]
+//func (c *ManualEdbController) ImportData() {
+//	br := new(models.BaseResponse).Init()
+//	defer func() {
+//		c.Data["json"] = br
+//		c.ServeJSON()
+//	}()
+//	sysUser := c.SysUser
+//	if sysUser == nil {
+//		br.Msg = "请重新登录"
+//		return
+//	}
+//	file, _, err := c.GetFile("EntryFile")
+//	if err != nil {
+//		br.Msg = "获取文件失败"
+//		br.ErrMsg = "获取文件失败,Err:" + err.Error()
+//		return
+//	}
+//	path := "./static/数据导入_" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+//	defer file.Close()
+//	err = c.SaveToFile("EntryFile", path)
+//	if err != nil {
+//		br.Msg = "文件保存失败"
+//		br.ErrMsg = "文件保存失败,Err:" + err.Error()
+//		return
+//	}
+//	if utils.RunMode == "debug" {
+//		defer os.Remove(path)
+//	}
+//	xlFile, err := xlsx.OpenFile(path)
+//	if err != nil {
+//		fmt.Println(err.Error())
+//		return
+//	}
+//
+//	//超管账号可以查看分类下的所有频度数据
+//	userId := sysUser.AdminId
+//	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN {
+//		userId = 0
+//	}
+//	//获取账户所拥有权限的分类id集合
+//	classifyNameStrList, edbDataClassifyMap, err := data.GetEdbClassifyNameListByAdminId(int64(userId))
+//	if err != nil {
+//		br.Msg = "获取分类数据失败"
+//		return
+//	}
+//
+//	//导入成功数量
+//	successCount := 0
+//	//导入失败数据
+//	failDatas := make([]*models.EdbdataImportFail, 0)
+//
+//	//指标map
+//	targetMap := make(map[string]*models.Edbinfo)
+//	defer func() {
+//		for _, target := range targetMap {
+//			//结束后,清除掉对应的缓存
+//			key := "import:edbinfo:data:" + target.TradeCode
+//			utils.Rc.Delete(key)
+//
+//			//将该指标的code加入到 “手工数据导入后刷新” 缓存
+//			if utils.Re == nil {
+//				err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, target.TradeCode)
+//				if err != nil {
+//					fmt.Println("CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
+//				}
+//			}
+//		}
+//	}()
+//
+//	// 所有的指标数据map
+//	edbCodeDataMap := make(map[string]map[string]string)
+//
+//	// 遍历sheet页读取
+//	for _, sheet := range xlFile.Sheets {
+//		fmt.Println("sheet name: ", sheet.Name)
+//		//遍历行读取
+//		maxRow := sheet.MaxRow
+//		fmt.Println("maxRow:", maxRow)
+//		fmt.Println("maxRow")
+//		for i := 0; i < maxRow; i++ {
+//			if i == 1 {
+//				row := sheet.Row(i)
+//				cells := row.Cells
+//				if len(cells) < 6 {
+//					br.ErrMsg = "导入文件异常,请下载最新导入模板文件"
+//					br.Msg = "导入文件异常,请下载最新导入模板文件"
+//					return
+//				}
+//				templateFail := false
+//				if cells[0].Value != "品种分类" && cells[0].Value != "Species Category" {
+//					templateFail = true
+//				}
+//				if cells[1].Value != "录入日期" && cells[1].Value != "Input Date" {
+//					templateFail = true
+//				}
+//				if cells[2].Value != "指标名称" && cells[2].Value != "Indicator Name Indicator Name" {
+//					templateFail = true
+//				}
+//				if cells[3].Value != "值" && cells[3].Value != "Value" {
+//					templateFail = true
+//				}
+//				if cells[4].Value != "频度" && cells[4].Value != "Frequency" {
+//					templateFail = true
+//				}
+//				if cells[5].Value != "单位" && cells[5].Value != "Unit" {
+//					templateFail = true
+//				}
+//				if templateFail {
+//					br.ErrMsg = "导入文件异常,请下载最新导入模板文件"
+//					br.Msg = "导入文件异常,请下载最新导入模板文件"
+//					return
+//				}
+//			}
+//			if i > 1 {
+//				row := sheet.Row(i)
+//				cells := row.Cells
+//				if len(cells) >= 6 {
+//					classifyName := cells[0].Value //分类
+//					if classifyName == "" {        //过滤空白行
+//						continue
+//					}
+//					cell1 := cells[1].Value
+//					//createDate := utils.ConvertToFormatDay(cell1) //录入日期
+//					createDate := cell1
+//					cell2 := cells[2].Value //指标名称
+//					secName := utils.TrimStr(cell2)
+//					frequency := cells[4].Value //频度
+//					unit := cells[5].Value      //单位
+//
+//					closeVal := cells[3].Value //值
+//					if strings.Contains(closeVal, "#N/A") {
+//						continue
+//					}
+//
+//					//校验表格中的日期格式
+//					if strings.Contains(createDate, "-") {
+//						//如果是带有 - 的普通日期格式文本
+//						_, timeErr := time.Parse("2006-1-2", createDate)
+//						if timeErr != nil {
+//							failItem := new(models.EdbdataImportFail)
+//							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//							failItem.ClassifyName = classifyName
+//							failItem.CreateDate = createDate
+//							failItem.SecName = secName
+//							failItem.Close = closeVal
+//							failItem.Remark = "日期格式异常"
+//							failItem.Frequency = frequency
+//							failItem.Unit = unit
+//							failDatas = append(failDatas, failItem)
+//							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+//							continue
+//						}
+//					} else if strings.Contains(createDate, "/") {
+//						//如果是带有 / 的普通日期格式文本
+//						createDateTime, timeErr := time.Parse("2006/1/2", createDate)
+//						if timeErr != nil {
+//							failItem := new(models.EdbdataImportFail)
+//							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//							failItem.ClassifyName = classifyName
+//							failItem.CreateDate = createDate
+//							failItem.SecName = secName
+//							failItem.Close = closeVal
+//							failItem.Remark = "日期格式异常"
+//							failItem.Frequency = frequency
+//							failItem.Unit = unit
+//							failDatas = append(failDatas, failItem)
+//							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+//							continue
+//						}
+//						createDate = createDateTime.Format("2006-01-02")
+//					} else {
+//						//可能是excel的日期格式
+//						_, tmpErr := strconv.Atoi(createDate)
+//						if tmpErr != nil {
+//							failItem := new(models.EdbdataImportFail)
+//							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//							failItem.ClassifyName = classifyName
+//							failItem.CreateDate = createDate
+//							failItem.SecName = secName
+//							failItem.Close = closeVal
+//							failItem.Remark = "日期格式异常"
+//							failItem.Frequency = frequency
+//							failItem.Unit = unit
+//							failDatas = append(failDatas, failItem)
+//							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+//							continue
+//						}
+//						createDate = utils.ConvertToFormatDay(createDate) //录入日期
+//					}
+//
+//					closeValFloat, err := cells[3].Float() //值
+//
+//					if err != nil {
+//						failItem := new(models.EdbdataImportFail)
+//						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//						failItem.ClassifyName = classifyName
+//						failItem.CreateDate = createDate
+//						failItem.SecName = secName
+//						failItem.Close = cells[3].Value
+//						failItem.Remark = "值类型异常"
+//						failItem.Frequency = frequency
+//						failItem.Unit = unit
+//						failDatas = append(failDatas, failItem)
+//						continue
+//					}
+//					newDecimal := decimal.NewFromFloat(closeValFloat)
+//					newDecimal.Round(4)
+//					closeVal = newDecimal.String()
+//					if strings.Contains(closeVal, "#N/A") {
+//						continue
+//					}
+//					//closeVal := closeValFloat.
+//
+//					//判断指标,类型,等数据是否正常
+//					classifyName = strings.Trim(classifyName, " ")
+//					frequency = utils.TrimStr(frequency)
+//					unit = utils.TrimStr(unit)
+//
+//					// 成功数量超过20个指标,那就不导入了
+//					if len(targetMap) >= 150 {
+//						failItem := new(models.EdbdataImportFail)
+//						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//						failItem.ClassifyName = classifyName
+//						failItem.CreateDate = createDate
+//						failItem.SecName = secName
+//						failItem.Close = closeVal
+//						failItem.Remark = "导入指标数量过多"
+//						failItem.Frequency = frequency
+//						failItem.Unit = unit
+//						failDatas = append(failDatas, failItem)
+//						//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+//						continue
+//					}
+//
+//					if !strings.Contains(strings.Join(classifyNameStrList, ","), classifyName) {
+//						failItem := new(models.EdbdataImportFail)
+//						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//						failItem.ClassifyName = classifyName
+//						failItem.CreateDate = createDate
+//						failItem.SecName = secName
+//						failItem.Close = closeVal
+//						failItem.Remark = "没有该品种分类权限"
+//						failItem.Frequency = frequency
+//						failItem.Unit = unit
+//						failDatas = append(failDatas, failItem)
+//						continue
+//					}
+//					//fmt.Println(classifyName, createDate, secName, closeVal)
+//
+//					//获取指标分类信息
+//					classify, ok := edbDataClassifyMap[classifyName]
+//					if !ok {
+//						failItem := new(models.EdbdataImportFail)
+//						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//						failItem.ClassifyName = classifyName
+//						failItem.CreateDate = createDate
+//						failItem.SecName = secName
+//						failItem.Close = closeVal
+//						failItem.Remark = "指标分类不存在"
+//						failItem.Frequency = frequency
+//						failItem.Unit = unit
+//						failDatas = append(failDatas, failItem)
+//						//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+//						continue
+//					}
+//
+//					//判断指标是否存在
+//					target, ok := targetMap[secName]
+//					if !ok {
+//						tmpTarget, err := models.GetTargetBySecName(secName)
+//						if err != nil {
+//							//如果是找不到该指标,那么新增指标
+//							if err.Error() == utils.ErrNoRow() {
+//								if frequency == "" {
+//									failItem := new(models.EdbdataImportFail)
+//									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//									failItem.ClassifyName = classifyName
+//									failItem.CreateDate = createDate
+//									failItem.SecName = secName
+//									failItem.Close = closeVal
+//									failItem.Remark = "新增指标失败,频度字段为空"
+//									failItem.Frequency = frequency
+//									failItem.Unit = unit
+//									failDatas = append(failDatas, failItem)
+//									continue
+//								}
+//								if unit == "" {
+//									failItem := new(models.EdbdataImportFail)
+//									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//									failItem.ClassifyName = classifyName
+//									failItem.CreateDate = createDate
+//									failItem.SecName = secName
+//									failItem.Close = closeVal
+//									failItem.Remark = "新增指标失败,单位字段为空"
+//									failItem.Frequency = frequency
+//									failItem.Unit = unit
+//									failDatas = append(failDatas, failItem)
+//									continue
+//								}
+//								tmpErr := data.AddEdbInfo(secName, unit, frequency, "", sysUser.Mobile, classify.ClassifyId, sysUser.AdminId)
+//								if tmpErr != nil {
+//									fmt.Println("line 158")
+//									failItem := new(models.EdbdataImportFail)
+//									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//									failItem.ClassifyName = classifyName
+//									failItem.CreateDate = createDate
+//									failItem.SecName = secName
+//									failItem.Close = closeVal
+//									failItem.Remark = "新增指标失败"
+//									failItem.Frequency = frequency
+//									failItem.Unit = unit
+//									failDatas = append(failDatas, failItem)
+//									continue
+//								}
+//								tmpTarget, tmpErr := models.GetTargetBySecName(secName)
+//								target = tmpTarget
+//								targetMap[secName] = target
+//							} else {
+//								fmt.Println("导入数据 获取指标:Err:" + err.Error())
+//							}
+//						} else {
+//							target = tmpTarget
+//							targetMap[secName] = target
+//						}
+//
+//						//设置10分钟缓存,不允许其他地方删除
+//						key := "import:edbinfo:data:" + target.TradeCode
+//						utils.Rc.SetNX(key, 1, time.Second*600)
+//					}
+//
+//					if target == nil {
+//						fmt.Println("指标不存在")
+//						failItem := new(models.EdbdataImportFail)
+//						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//						failItem.ClassifyName = classifyName
+//						failItem.CreateDate = createDate
+//						failItem.SecName = secName
+//						failItem.Close = closeVal
+//						failItem.Remark = "指标不存在"
+//						failItem.Frequency = frequency
+//						failItem.Unit = unit
+//						failDatas = append(failDatas, failItem)
+//						continue
+//					}
+//
+//					//更新指标信息
+//					updateCols := make([]string, 0)
+//					//更新指标分类
+//					if target.ClassifyId <= 0 && classify.ClassifyId > 0 {
+//						target.ClassifyId = classify.ClassifyId
+//						updateCols = append(updateCols, "ClassifyId")
+//					}
+//					if target.Frequency != frequency {
+//						target.Frequency = frequency
+//						target.NoticeTime = ""
+//						updateCols = append(updateCols, "Frequency", "NoticeTime")
+//					}
+//					if target.Unit != unit {
+//						target.Unit = unit
+//						updateCols = append(updateCols, "Unit")
+//					}
+//					if len(updateCols) > 0 {
+//						_ = target.Update(updateCols)
+//					}
+//
+//					// 判断指标数据列表是否已经存在
+//					tmpDataMap, ok := edbCodeDataMap[target.TradeCode]
+//					if !ok {
+//						tmpDataMap = make(map[string]string)
+//						dataList, tmpErr := models.GetTargetsDataList(target.TradeCode)
+//						if tmpErr != nil {
+//							go alarm_msg.SendAlarmMsg("导入数据"+target.TradeCode+" 获取指标的数据失败,Err:"+err.Error(), 3)
+//						}
+//						for _, tmpData := range dataList {
+//							tmpDataMap[tmpData.Dt] = tmpData.Close
+//						}
+//						edbCodeDataMap[target.TradeCode] = tmpDataMap
+//					}
+//
+//					//判断数据是否已经存在
+//					tmpVal, ok := tmpDataMap[createDate]
+//					//数据已存在,进行更新操作
+//					if ok {
+//						if tmpVal != closeVal {
+//							err = models.ModifyTargetsDataByImport(target.TradeCode, createDate, closeVal)
+//							if err != nil {
+//								go alarm_msg.SendAlarmMsg("导入数据 修改数据失败,Err:"+err.Error(), 3)
+//								//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 修改数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
+//							}
+//						}
+//					} else { //数据不存在,进行新增操作
+//						if target.TradeCode != "" && createDate != "" && closeVal != "" {
+//							models.AddTargetsDataByImport(target.TradeCode, createDate, closeVal)
+//							if err != nil {
+//								go alarm_msg.SendAlarmMsg("导入数据 新增数据失败,Err:"+err.Error(), 3)
+//								//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 新增数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
+//							}
+//							tmpDataMap[createDate] = closeVal
+//							edbCodeDataMap[target.TradeCode] = tmpDataMap
+//						}
+//					}
+//					successCount++
+//				} else {
+//					//br.ErrMsg = "请填写导入内容"
+//					//br.Msg = "请填写导入内容"
+//					//return
+//				}
+//			}
+//		}
+//	}
+//	resp := models.EdbdataImportResp{
+//		SuccessCount: successCount,
+//		FailCount:    len(failDatas),
+//	}
+//	fmt.Println("failDatas:", len(failDatas))
+//	if len(failDatas) > 0 {
+//		//先删除导入失败记录
+//		_ = models.DelEdbDataImportFail(sysUser.AdminId)
+//		//错误信息字符串切片,最后作为发送邮件通知使用
+//		failContents := make([]string, 0)
+//		for _, v := range failDatas {
+//			failContents = append(failContents, fmt.Sprint(v.SecName, "导入失败:", v.Remark))
+//			err = models.AddEdbdataImportFail(v)
+//			if err != nil {
+//				go alarm_msg.SendAlarmMsg("导入数据 新增失败记录失败,Err:"+err.Error(), 3)
+//				//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 新增失败记录失败:Err:"+err.Error(), utils.EmailSendToUsers)
+//			}
+//		}
+//		//导入失败的话,最后统一邮件提醒就好啦,不需要每次都去提醒
+//		go alarm_msg.SendAlarmMsg("导入数据 存在部分数据导入失败:"+strings.Join(failContents, ";"), 3)
+//		//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 存在部分数据导入失败:"+strings.Join(failContents, ";"), utils.EmailSendToUsers)
+//
+//		if successCount == 0 {
+//			resp.Status = -1
+//			resp.Msg = "导入失败"
+//		} else {
+//			resp.Status = 1
+//			resp.Msg = "存在部分导入失败"
+//		}
+//	} else {
+//		resp.Status = 0
+//	}
+//	br.Msg = "导入成功"
+//	br.Ret = 200
+//	br.Success = true
+//	br.Data = resp
+//}

+ 133 - 0
models/ai_summary/ai_pormpt.go

@@ -0,0 +1,133 @@
+package ai_summary
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type AiPrompt struct {
+	AiPromptId    int       `orm:"column(ai_prompt_id);pk"` // ai纪要提示词id
+	PromptContent string    // 原始内容
+	SysAdminId    int       // 创建人ID
+	SysAdminName  string    // 创建人姓名
+	Title         string    // 文档标题
+	CreateTime    time.Time // 创建时间
+	ModifyTime    time.Time // 更新时间
+	IsShare       int       // 是否分享,0:不分享,1:分享
+	Sort          int       // 排序
+}
+
+type AiPromptItem struct {
+	AiPromptId    int    `orm:"column(ai_prompt_id);pk"` // ai纪要提示词id
+	PromptContent string // 原始内容
+	SysAdminId    int    // 创建人ID
+	SysAdminName  string // 创建人姓名
+	Title         string // 文档标题
+	CreateTime    string // 创建时间
+	ModifyTime    string // 更新时间
+	IsShare       int    // 是否分享,0:不分享,1:分享
+	Sort          int    // 排序
+}
+
+func GetAiPromptList(sysUserId, isShare int) (items []*AiPromptItem, err error) {
+	sql := ``
+	if isShare == 1 {
+		sql = ` SELECT * FROM ai_prompt WHERE sys_admin_id=? and is_share=1 ORDER BY sort  `
+	} else {
+		sql = ` SELECT * FROM ai_prompt WHERE sys_admin_id=? ORDER BY sort  `
+	}
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, sysUserId).QueryRows(&items)
+	return
+}
+
+func GetAiPromptShareList() (items []*AiPromptItem, err error) {
+	sql := ``
+	sql = ` SELECT * FROM ai_prompt WHERE is_share=1 ORDER BY create_time DESC `
+
+	o := orm.NewOrm()
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type RespGroupListItem struct {
+	GroupId    int64  `description:"目录id"`
+	GroupName  string `description:"目录名称"`
+	AdminId    int    `description:"目录创建者账号ID"`
+	IsShare    int8   `description:"是否共享,0私有,1共享"`
+	PromptList []*AiPromptItem
+}
+
+type AiPromptAddReq struct {
+	PromptContent string // 原始内容
+	Title         string // 文档标题
+}
+
+func AddPropmt(item *AiPrompt) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+type AiPromptEditReq struct {
+	PromptContent string // 原始内容
+	Title         string // 文档标题
+	AiPromptId    int    // ai纪要提示词id
+}
+
+// Update
+func (aiPrompt *AiPrompt) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(aiPrompt, cols...)
+	return
+}
+
+// DelAiSummaryById 根据纪要id删除纪要
+func DelAiPromptyId(aiPromptId int) (err error) {
+	o := orm.NewOrm()
+	sql := `delete from ai_prompt where ai_prompt_id = ? `
+	_, err = o.Raw(sql, aiPromptId).Exec()
+	return
+}
+
+func GetAiPromptById(promptId int) (item *AiPrompt, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM ai_prompt WHERE 1=1 AND ai_prompt_id=? `
+	err = o.Raw(sql, promptId).QueryRow(&item)
+	return
+}
+
+type DeleteAipromptReq struct {
+	AiPromptId int // ai纪要提示词id
+}
+
+type ReqMovePrompt struct {
+	AiPromptId     int `description:"当前被移动的目录里的ppt绑定序号"`
+	PrevAiPromptId int `description:"上一个目录里的ppt绑定序号,ppt最终在目录中置顶,则传0值"`
+	NextAiPromptId int `description:"下一个目录里的ppt绑定序号,ppt最终在目录末尾,则传0值"`
+}
+
+// GetAiPromptMaxSort 获取ai提示词下最大的排序数
+func GetAiPromptMaxSort(adminId int) (sort int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT Max(sort) AS sort FROM ai_prompt WHERE sys_admin_id=? `
+	err = o.Raw(sql, adminId).QueryRow(&sort)
+	return
+}
+
+
+// MoveDownPromptBySort 往下移动提示词
+func MoveDownPromptBySort(sysAdminId, prevSort, currentSort int) (err error) {
+	o := orm.NewOrm()
+	sql := `update ai_prompt set sort = sort - 1 where sys_admin_id = ? and sort <= ? and sort> ? `
+	_, err = o.Raw(sql, sysAdminId, prevSort, currentSort).Exec()
+	return
+}
+
+// MoveUpPromptBySort 往上移动提示词
+func MoveUpPromptBySort(sysAdminId, nextSort, currentSort int) (err error) {
+	o := orm.NewOrm()
+	sql := `update ai_prompt set sort = sort + 1 where sys_admin_id = ? and sort >= ? and sort< ?`
+	_, err = o.Raw(sql, sysAdminId, nextSort, currentSort).Exec()
+	return
+}

+ 176 - 0
models/ai_summary/ai_summary.go

@@ -0,0 +1,176 @@
+package ai_summary
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type AiSummary struct {
+	AiSummaryId     int       `orm:"column(ai_summary_id);pk"` // ai纪要id
+	SaDocId         int       // 文档ID
+	OriginContent   string    // 原始内容
+	SummaryContent  string    // 纪要内容
+	ClassifyId      int       // 分类id
+	SysUserId       int       // 创建人ID
+	SysUserRealName string    // 创建人姓名
+	Title           string    // 文档标题
+	CreateTime      time.Time // 创建时间
+	ModifyTime      time.Time // 更新时间
+	OpenaiFileName  string    // 文件名称
+	OpenaiFilePath  string    // 文件路径
+	OriginTitle     string    // 原文标题
+	Sort            int       // 排序字段,越小越靠前,默认值:10
+}
+
+type AiSummaryItems struct {
+	AiSummaryId     int    `orm:"column(ai_summary_id);pk"` // ai纪要id
+	SaDocId         int    // 文档ID
+	OriginContent   string // 原始内容
+	OriginTitle     string // 原文标题
+	SummaryContent  string // 纪要内容
+	ClassifyId      int    // 分类id
+	SysUserId       int    // 创建人ID
+	SysUserRealName string // 创建人姓名
+	Title           string // 文档标题
+	CreateTime      string // 创建时间
+	ModifyTime      string // 更新时间
+	OpenaiFileName  string // 文件名称
+	OpenaiFilePath  string // 文件路径
+	Sort            int    // 排序字段,越小越靠前,默认值:10
+	ParentIds       string
+}
+
+func GetAiChatTopicList(sysUserId int) (item []*AiSummary, err error) {
+	sql := ` SELECT * FROM ai_summary WHERE classify_id=? ORDER BY create_time DESC `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, sysUserId).QueryRows(&item)
+	return
+}
+
+// GetAiSummaryById 根据纪要id获取详情
+func GetAiSummaryById(aiSummaryId int) (sandboxInfo *AiSummary, err error) {
+	o := orm.NewOrm()
+	sql := `select * from ai_summary where ai_summary_id = ? `
+	err = o.Raw(sql, aiSummaryId).QueryRow(&sandboxInfo)
+	return
+}
+
+// GetAiSummaryById 根据纪要id获取详情
+func GetAiSummaryItemById(aiSummaryId int) (sandboxInfo *AiSummaryItems, err error) {
+	o := orm.NewOrm()
+	sql := `select * from ai_summary where ai_summary_id = ? `
+	err = o.Raw(sql, aiSummaryId).QueryRow(&sandboxInfo)
+	return
+}
+
+// DelAiSummaryById 根据纪要id删除纪要
+func DelAiSummaryById(aiSummaryId int) (err error) {
+	o := orm.NewOrm()
+	sql := `delete from ai_summary where ai_summary_id = ? `
+	_, err = o.Raw(sql, aiSummaryId).Exec()
+	return
+}
+
+func MoveAiSummary(aiSummaryId, classifyId int) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE  ai_summary
+			SET
+			  classify_id = ?
+			WHERE ai_summary_id = ?`
+	_, err = o.Raw(sql, classifyId, aiSummaryId).Exec()
+	return
+}
+
+// GetFirstAiSummaryByClassifyId 获取当前分类下,且排序数相同 的排序第一条的数据
+func GetFirstAiSummaryByClassifyId(classifyId int) (item *AiSummary, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM ai_summary WHERE classify_id=? order by sort asc,ai_summary_id asc limit 1`
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+// Update
+func (aiSummary *AiSummary) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(aiSummary, cols...)
+	return
+}
+
+// UpdateSandboxSortByClassifyId 根据纪要id更新排序
+func UpdateAiSummarySortByClassifyId(classifyId, nowSort, prevAiSummaryId int, updateSort string) (err error) {
+	o := orm.NewOrm()
+	sql := ` update ai_summary set sort = ` + updateSort + ` WHERE classify_id=?  AND `
+	if prevAiSummaryId > 0 {
+		sql += ` (sort > ? or (ai_summary_id > ` + fmt.Sprint(prevAiSummaryId) + ` and sort = ` + fmt.Sprint(nowSort) + `))`
+	}
+	_, err = o.Raw(sql, classifyId, nowSort).Exec()
+	return
+}
+
+func GetAiSummaryListByCondition(condition string, pars []interface{}, startSize, pageSize int) (item []*AiSummaryItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM ai_summary WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += " ORDER BY create_time DESC LIMIT ?,? "
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&item)
+	return
+}
+
+func GetAiSummaryListCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS count FROM ai_summary WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func AddAiSummary(item *AiSummary) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+type AddAiSummaryReq struct {
+	SaDocId        int    // 文档ID
+	OriginContent  string // 原始内容
+	SummaryContent string // 纪要内容
+	ClassifyId     int    // 分类id
+	Title          string // 文档标题
+	OpenaiFileName string // 文件名称
+	OpenaiFilePath string // 文件路径
+	OriginTitle    string // 原文标题
+}
+
+type GenerateAiSummaryReq struct {
+	AiChatTopicId int      `description:"主题id"`
+	OriginContent string   // 原始内容
+	OpenaiFileId  []string `description:"openai返回的文件id"`
+	Model         string   `description:"模型名称"`
+	Prompt        string   `description:"提示词"`
+	SaDocId       int      // 文档ID
+}
+
+// GetAiSummaryMaxSort 获取ai纪要下最大的排序数
+func GetAiSummaryMaxSort(classifyId int) (sort int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT Max(sort) AS sort FROM ai_summary WHERE parent_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&sort)
+	return
+}
+
+type AiSummaryAddResp struct {
+	AiSummaryId int
+	UniqueCode  string // 唯一编码
+	ParentId    int
+}
+
+type AiSummaryDetailResp struct {
+	*AiSummaryItems
+	SaDocTitle      string `description:"文档库标题"`
+	SaDocClassifyId int    `description:"文档库分类id"`
+}

+ 331 - 0
models/ai_summary/ai_summary_classify.go

@@ -0,0 +1,331 @@
+package ai_summary
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type AiSummaryClassify struct {
+	AiSummaryClassifyId int       `orm:"column(ai_summary_classify_id);pk"` // ai纪要分类id
+	ClassifyName        string    // 分类名称
+	ParentId            int       // 父级id
+	CreateTime          time.Time // 创建时间
+	ModifyTime          time.Time // 修改时间
+	SysUserId           int       // 创建人id,使用指针表示可为空
+	SysUserRealName     string    // 创建人姓名
+	Level               int       // 层级
+	Sort                int       // 排序字段,越小越靠前,默认值:10
+	RootId              int       // 顶级ID
+	HasData             int       `description:"是否含有指标数据"`
+}
+
+func AddAiSummaryClassify(item *AiSummaryClassify) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+type AiSummaryClassifyItems struct {
+	AiSummaryClassifyId int
+	ClassifyName        string    `description:"分类名称"`
+	ParentId            int       `description:"父级id"`
+	HasData             int       `description:"是否含有指标数据"`
+	CreateTime          time.Time `description:"创建时间"`
+	ModifyTime          time.Time `description:"修改时间"`
+	SysUserId           int       `description:"创建人id"`
+	SysUserRealName     string    `description:"创建人姓名"`
+	Level               int       `description:"层级"`
+	Sort                int       `description:"排序字段,越小越靠前,默认值:10"`
+	AiSummaryId         int       `description:"纪要id"`
+	UniqueCode          string    `description:"唯一编码"`
+	Children            []*AiSummaryClassifyItems
+}
+
+type AiSummaryClassifyListResp struct {
+	AllNodes []*AiSummaryClassifyItems
+}
+
+// GetAiSummaryClassifyByParentId
+func GetAiSummaryClassifyByParentId(parentId int) (items []*AiSummaryClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM ai_summary_classify WHERE parent_id=? order by sort asc,ai_summary_classify_id asc`
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+// GetAiSummaryInfoByAdminId 获取所有我创建的纪要,用于分类展示
+func GetAiSummaryInfoByAdminId(adminId int) (items []*AiSummaryClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT
+	a.*,
+	b.ai_summary_classify_id,
+	b.classify_name 
+FROM
+	ai_summary AS a
+	JOIN ai_summary_classify AS b ON a.classify_id = b.ai_summary_classify_id 
+WHERE
+	a.sys_user_id = ? 
+ORDER BY
+	a.sort ASC,
+	a.create_time ASC `
+	_, err = o.Raw(sql, adminId).QueryRows(&items)
+	return
+}
+
+// GetAiSummaryClassifyAndInfoByParentId
+func GetAiSummaryClassifyAndInfoByParentId(parentId int) (items []*AiSummaryClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT
+	0 AS ai_summary_id,
+	ai_summary_classify_id,
+	classify_name,
+	parent_id,
+	create_time,
+	modify_time,
+	sys_user_id,
+	sys_user_real_name AS sys_user_real_name,
+	sort,
+	level
+FROM
+	ai_summary_classify 
+WHERE
+	parent_id = ? UNION ALL
+SELECT
+	ai_summary_id,
+	classify_id as ai_summary_classify_id,
+	title AS classify_name,
+	0 AS parent_id,
+	create_time,
+	modify_time,
+	sys_user_id,
+	sys_user_real_name,
+	sort,
+	0 AS level
+FROM
+	ai_summary 
+WHERE
+	classify_id = ? 
+ORDER BY
+	sort ASC,
+	ai_summary_classify_id ASC`
+	_, err = o.Raw(sql, parentId, parentId).QueryRows(&items)
+	return
+}
+
+type AddAiSummaryClassifyReq struct {
+	ClassifyName string `description:"分类名称"`
+	ParentId     int    `description:"父级id,第一级传0"`
+	Level        int    `description:"层级,第一级传0,其余传上一级的层级"`
+}
+
+func GetAiSummaryClassifyCount(classifyName string, parentId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS count FROM ai_summary_classify WHERE parent_id=? AND classify_name=? `
+	err = o.Raw(sql, parentId, classifyName).QueryRow(&count)
+	return
+}
+
+// GetAiSummaryClassifyMaxSort 获取ai纪要下最大的排序数
+func GetAiSummaryClassifyMaxSort(parentId int) (sort int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT Max(sort) AS sort FROM ai_summary_classify WHERE parent_id=? `
+	err = o.Raw(sql, parentId).QueryRow(&sort)
+	return
+}
+
+type EditAiSummaryClassifyReq struct {
+	ClassifyName        string `description:"分类名称"`
+	AiSummaryClassifyId int    `description:"分类id"`
+}
+
+func EditSandboxClassify(classifyId int, classifyName string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE ai_summary_classify SET classify_name=?, modify_time=NOW() WHERE ai_summary_classify_id=? `
+	_, err = o.Raw(sql, classifyName, classifyId).Exec()
+	return
+}
+
+type AiSummaryClassifyDeleteCheckReq struct {
+	AiSummaryClassifyId int `description:"分类id"`
+}
+
+func GetAiSummaryInfoCountByClassifyId(classifyId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS count FROM ai_summary AS a
+				WHERE a.classify_id IN(
+				SELECT t.ai_summary_classify_id FROM 
+				(
+				SELECT rd.*
+				FROM (SELECT * FROM ai_summary_classify WHERE parent_id IS NOT NULL) rd,
+					 (SELECT @pid := ?) pd 
+				WHERE FIND_IN_SET(parent_id, @pid) > 0 
+				  AND @pid := CONCAT(@pid, ',', ai_summary_classify_id) 
+				UNION SELECT * FROM ai_summary_classify WHERE ai_summary_classify_id = @pid 
+				)AS t
+				) `
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+type DeleteAiSummaryClassifyReq struct {
+	AiSummaryClassifyId int `description:"分类id"`
+	AiSummaryId         int `description:"纪要id"`
+}
+
+func DeleteAiSummaryClassify(classifyId int) (err error) {
+	o := orm.NewOrm()
+	sql := ` DELETE FROM ai_summary_classify
+				WHERE ai_summary_classify_id IN(
+				SELECT t.ai_summary_classify_id FROM
+				(
+				SELECT rd.*
+				FROM (SELECT * FROM ai_summary_classify WHERE parent_id IS NOT NULL) rd,
+				(SELECT @pid := ?) pd
+				WHERE FIND_IN_SET(parent_id, @pid) > 0
+				AND @pid := CONCAT(@pid, ',', ai_summary_classify_id)
+				UNION SELECT * FROM ai_summary_classify WHERE ai_summary_classify_id = @pid
+				)AS t
+				) `
+	_, err = o.Raw(sql, classifyId).Exec()
+	return
+}
+
+// MoveAiSummaryClassifyReq 移动纪要分类请求参数
+type MoveAiSummaryClassifyReq struct {
+	AiSummaryClassifyId int `description:"分类id"`
+	AiSummaryId         int `description:"纪要ID"`
+	ParentClassifyId    int `description:"父级分类id 移动纪要时为目标分类id"`
+	PrevId              int `description:"上一个兄弟节点分类id"`
+	NextId              int `description:"下一个兄弟节点分类id"`
+	PrevType            int `description:"上一个兄弟节点类型 1分类 2纪要 "`
+	NextType            int `description:"上一个兄弟节点类型 1分类 2纪要 "`
+}
+
+func GetAiSummaryClassifyById(classifyId int) (item *AiSummaryClassify, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM ai_summary_classify WHERE ai_summary_classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+func GetAiSummaryClassifyCountById(classifyId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT count(1) AS count FROM ai_summary_classify WHERE ai_summary_classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+// Update 更新沙盘分类基础信息
+func (aiSummaryClassify *AiSummaryClassify) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(aiSummaryClassify, cols...)
+	return
+}
+
+// GetFirstAiSummaryClassifyByParentId 获取当前父级沙盘分类下的排序第一条的数据
+func GetFirstAiSummaryClassifyByParentId(parentId int) (item *AiSummaryClassify, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM ai_summary_classify WHERE parent_id=? order by sort asc,ai_summary_classify_id asc limit 1`
+	err = o.Raw(sql, parentId).QueryRow(&item)
+	return
+}
+
+// UpdateSandboxClassifySortByParentId 根据沙盘父类id更新排序
+func UpdateAiSummaryClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrm()
+	sql := ` update ai_summary_classify set sort = ` + updateSort + ` WHERE parent_id=? and sort > ? `
+	if classifyId > 0 {
+		sql += ` or ( ai_summary_classify_id > ` + fmt.Sprint(classifyId) + ` and sort= ` + fmt.Sprint(nowSort) + `)`
+	}
+	_, err = o.Raw(sql, parentId, nowSort).Exec()
+	return
+}
+
+// 获取所有子级分类id
+func GetAiSummaryClassifySubcategories(classifyId int) (Ids string, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT GROUP_CONCAT(ai_summary_classify_id) AS ids
+FROM (
+SELECT @pv := ? AS ai_summary_classify_id
+UNION ALL
+SELECT sc.ai_summary_classify_id
+FROM ai_summary_classify sc
+JOIN (SELECT @pv := ?) initial
+WHERE sc.parent_id = @pv
+) subcategories; `
+	err = o.Raw(sql, classifyId, classifyId).QueryRow(&Ids)
+	return
+}
+
+func GetAiSummaryClassify(aiSummaryClassify int) (aiSummaryClassifyIds string, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT GROUP_CONCAT(t.ai_summary_classify_id) AS ai_summary_classify_id FROM (
+			SELECT a.ai_summary_classify_id FROM ai_summary_classify AS a 
+			WHERE a.ai_summary_classify_id=?
+			UNION ALL
+			SELECT a.ai_summary_classify_id FROM ai_summary_classify AS a 
+			WHERE a.parent_id=? UNION ALL
+	SELECT
+		ai_summary_classify_id 
+	FROM
+		ai_summary_classify 
+WHERE
+	parent_id IN ( SELECT ai_summary_classify_id FROM ai_summary_classify WHERE parent_id = ? )
+			)AS t`
+	err = o.Raw(sql, aiSummaryClassify, aiSummaryClassify, aiSummaryClassify).QueryRow(&aiSummaryClassifyIds)
+	return
+}
+
+func GetAiSummaryAllParentByClassifyId(aiSummaryClassifyId int) (ids string, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+	GROUP_CONCAT(DISTINCT m.ai_summary_classify_id  ORDER BY m.level) AS ids 
+FROM
+	(
+	SELECT
+		@id AS _id,(
+		SELECT
+			@id := parent_id 
+		FROM
+			ai_summary_classify 
+		WHERE
+			ai_summary_classify_id = _id 
+		) 
+	FROM
+		(
+		SELECT
+			@id :=(
+			SELECT
+				parent_id 
+			FROM
+				ai_summary_classify 
+			WHERE
+				ai_summary_classify_id = ? 
+			)) vm,
+		ai_summary_classify m 
+	WHERE
+		@id IS NOT NULL 
+	) vm
+	INNER JOIN ai_summary_classify m 
+WHERE
+	ai_summary_classify_id = vm._id `
+	err = o.Raw(sql, aiSummaryClassifyId).QueryRow(&ids)
+	return
+}
+
+type AiSummaryListResp struct {
+	Paging *paging.PagingItem
+	List   []*AiSummaryItems
+}
+
+// GetAiSummaryClassifyAll
+func GetAiSummaryClassifyAll() (items []*AiSummaryClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM ai_summary_classify WHERE parent_id<>0 order by sort asc,ai_summary_classify_id asc`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+

+ 14 - 0
models/data_manage/chart_edb_mapping.go

@@ -164,6 +164,20 @@ func GetEtaEdbChartEdbMapping(chartInfoId int) (item *ChartEdbInfoMapping, err e
 	return
 }
 
+// GetEtaEdbChartEdbMappingList       商品曲线图查询对应的普通指标
+func GetEtaEdbChartEdbMappingList(chartInfoId int) (items []*ChartEdbInfoMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	aField := `a.chart_edb_mapping_id,a.chart_info_id,a.edb_info_id,a.create_time,a.modify_time,a.unique_code,a.max_data,a.min_data,a.is_order,a.is_axis,a.edb_info_type,a.lead_value,a.lead_unit,a.chart_style,a.chart_color,a.predict_chart_color,a.chart_width,a.source as mapping_source`
+
+	sql := ` SELECT ` + aField + `,b.source_name,b.source,b.sub_source,b.edb_code,b.edb_name,b.edb_name_en,b.frequency,b.unit,b.unit_en,b.start_date,b.end_date,b.modify_time,b.latest_date,b.latest_value,b.unique_code,b.edb_info_type AS edb_info_category_type,b.classify_id,b.is_join_permission
+             FROM chart_edb_mapping AS a
+			 INNER JOIN edb_info AS b ON a.edb_info_id=b.edb_info_id
+			 WHERE a.chart_info_id=? AND a.source = ?
+             ORDER BY chart_edb_mapping_id ASC `
+	_, err = o.Raw(sql, chartInfoId, utils.CHART_SOURCE_DEFAULT).QueryRows(&items)
+	return
+}
+
 // GetFutureGoodEdbChartEdbMapping       商品曲线图查询对应的商品指标
 func GetFutureGoodEdbChartEdbMapping(chartInfoId int) (item *ChartEdbInfoMapping, err error) {
 	o := orm.NewOrmUsingDB("data")

+ 199 - 1
models/data_manage/chart_info.go

@@ -58,6 +58,7 @@ type ChartInfo struct {
 	UnitEn            string `description:"英文单位名称"`
 	IsJoinPermission  int    `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	ForumChartInfoId  int    `description:"社区的图表ID"`
+	ChartAlias        string `description:"图表别名"`
 }
 
 type ChartInfoMore struct {
@@ -261,6 +262,12 @@ type EditChartInfoReq struct {
 	MarkersAreas         string                  `description:"标识区"`
 	Unit                 string                  `description:"中文单位名称"`
 	UnitEn               string                  `description:"英文单位名称"`
+	ChartAlias           string                  `description:"图表别名"`
+}
+
+type EditFutureGoodChartInfoReq struct {
+	EditChartInfoReq
+	BarChartInfo FutureGoodBarChartInfoReq
 }
 
 type MarkersLine struct {
@@ -316,6 +323,7 @@ type EditChartEnInfoReq struct {
 	ChartNameEn      string                    `description:"英文图表名称"`
 	ChartEdbInfoList []*EditChartEnInfoEdbItem `description:"指标及配置信息"`
 	ExtraConfig      string                    `description:"图表额外配置信息,json字符串"`
+	ChartAlias       string                    `description:"图表别名"`
 }
 type EditChartEnInfoEdbItem struct {
 	EdbInfoId int    `description:"指标ID"`
@@ -330,6 +338,7 @@ type EditChartInfoBaseReq struct {
 	ChartName        string                      `description:"图表名称(根据语言版本区分)"`
 	ChartEdbInfoList []*EditChartInfoEdbBaseItem `description:"指标及配置信息"`
 	ExtraConfig      string                      `description:"图表额外配置信息,json字符串"`
+	ChartAlias       string                      `description:"图表别名"`
 }
 type EditChartInfoEdbBaseItem struct {
 	EdbInfoId int    `description:"指标ID"`
@@ -370,6 +379,7 @@ type ChartInfoList struct {
 	CreateTime           time.Time
 	ModifyTime           time.Time
 	EdbInfoList          []*EdbInfoList
+	ChartAlias           string `description:"图表别名"`
 }
 
 type ChartInfoListResp struct {
@@ -757,6 +767,17 @@ type ChartInfoDetailResp struct {
 	ClassifyLevels       []string         `description:"图表分类的UniqueCode-从最顶级到当前分类(给前端回显定位用的=_=!)"`
 }
 
+type FutureGoodChartInfoDetailResp struct {
+	ChartInfo            *ChartInfoView
+	EdbInfoList          []*ChartEdbInfoMapping
+	XEdbIdValue          []int                     `description:"柱方图的x轴数据,指标id"`
+	YDataList            []YData                   `description:"柱方图的y轴数据"`
+	XDataList            []XData                   `description:"商品价格曲线的X轴数据"`
+	BarChartInfo         FutureGoodBarChartInfoReq `description:"柱方图的配置"`
+	CorrelationChartInfo *CorrelationInfo          `description:"相关性图表信息"`
+	DataResp             interface{}               `description:"图表数据,根据图的类型而定的,没有确定的数据格式"`
+}
+
 type BalanceTableChartListResp struct {
 	List []*BalanceChartInfoDetailResp
 }
@@ -777,6 +798,7 @@ type ExcelChartEdbView struct {
 type XData struct {
 	Name   string `description:"别名"`
 	NameEn string `description:"英文别名"`
+	IsHide int    `description:"是否隐藏,0不隐藏,1隐藏"`
 }
 
 // YData 柱方图的y轴数据
@@ -1041,6 +1063,9 @@ func EditChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calenda
 	sql += `,min_max_save = ? `
 	pars = append(pars, req.MinMaxSave)
 
+	sql += `,chart_alias = ? `
+	pars = append(pars, req.ChartAlias)
+
 	sql += `WHERE chart_info_id = ?`
 
 	pars = append(pars, req.ChartInfoId)
@@ -1134,7 +1159,7 @@ func EditChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calenda
 }
 
 // EditFutureGoodChartInfoAndMapping 修改商品价格曲线的 图表与指标 的关系
-func EditFutureGoodChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calendar string, dateType, disabled int, barChartConf string) (err error) {
+func EditFutureGoodChartInfoAndMapping(req *EditFutureGoodChartInfoReq, edbInfoIdStr string, calendar string, dateType, disabled int, barChartConf string) (err error) {
 	o := orm.NewOrmUsingDB("data")
 	to, err := o.Begin()
 	if err != nil {
@@ -1213,7 +1238,12 @@ func EditFutureGoodChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr strin
 		return err
 	}
 	chartEdbMappingIdList := make([]string, 0)
+	hasAddMap := make(map[int]struct{})
 	for _, v := range req.BarChartInfo.EdbInfoIdList {
+		if _, ok := hasAddMap[v.EdbInfoId]; ok {
+			continue
+		}
+		hasAddMap[v.EdbInfoId] = struct{}{}
 		// 查询该指标是否存在,如果存在的话,那么就去修改,否则新增
 		var tmpChartEdbMapping *ChartEdbMapping
 		csql := `SELECT *  FROM chart_edb_mapping WHERE chart_info_id=? AND edb_info_id=? AND source = ? `
@@ -1310,6 +1340,9 @@ func EditChartEnInfoAndEdbEnInfo(req *EditChartEnInfoReq) (err error) {
 		pars = append(pars, req.ExtraConfig)
 	}
 
+	updateField += ` chart_alias =?, `
+	pars = append(pars, req.ChartAlias)
+
 	sql := ` UPDATE  chart_info
 			SET ` + updateField + ` modify_time = NOW()
 			WHERE chart_info_id = ?`
@@ -1383,6 +1416,9 @@ func EditChartBaseInfoAndEdbEnInfo(req *EditChartInfoBaseReq, chartItem *ChartIn
 		chartItem.ExtraConfig = req.ExtraConfig
 		updateChartCols = append(updateChartCols, "ExtraConfig")
 	}
+
+
+
 	chartItem.ModifyTime = time.Now()
 	updateChartCols = append(updateChartCols, "ModifyTime")
 	_, err = to.Update(chartItem, updateChartCols...)
@@ -1465,6 +1501,12 @@ type AddChartInfoReq struct {
 	MarkersAreas         string                  `description:"标识区"`
 	Unit                 string                  `description:"中文单位名称"`
 	UnitEn               string                  `description:"英文单位名称"`
+	ChartAlias           string                  `description:"图表别名"`
+}
+
+type AddFutureGoodChartInfoReq struct {
+	AddChartInfoReq
+	BarChartInfo FutureGoodBarChartInfoReq
 }
 
 type PreviewChartInfoReq struct {
@@ -1477,6 +1519,7 @@ type PreviewChartInfoReq struct {
 	SeasonExtraConfig SeasonExtraItem  `description:"季节性图表中的配置,json数据"`
 	StartYear         int              `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
 	ChartSource       int
+	ExtraConfig       string `description:"图表额外配置信息,json字符串"`
 	MarkersLines      string `description:"标识线"`
 }
 
@@ -1798,6 +1841,18 @@ type ChartInfoDetailFromUniqueCodeResp struct {
 	DataResp             interface{}      `description:"图表数据,根据图的类型而定的,没有确定的数据格式"`
 }
 
+type FutureChartInfoDetailFromUniqueCodeResp struct {
+	ChartInfo            *ChartInfoView
+	Status               bool `description:"true:图表存在,false:图表不存在"`
+	EdbInfoList          []*ChartEdbInfoMapping
+	XEdbIdValue          []int                     `description:"柱方图的x轴数据,指标id"`
+	YDataList            []YData                   `description:"柱方图的y轴数据"`
+	XDataList            []XData                   `description:"商品价格曲线的X轴数据"`
+	BarChartInfo         FutureGoodBarChartInfoReq `description:"柱方图的配置"`
+	CorrelationChartInfo *CorrelationInfo          `description:"相关性图表信息"`
+	DataResp             interface{}               `description:"图表数据,根据图的类型而定的,没有确定的数据格式"`
+}
+
 func GetChartInfoByUniqueCode(uniqueCode string) (item *ChartInfo, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT * FROM chart_info WHERE unique_code=? `
@@ -1902,6 +1957,7 @@ type ChartInfoView struct {
 	IsJoinPermission  int             `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	HaveOperaAuth     bool            `description:"是否有数据权限,默认:false"`
 	ForumChartInfoId  int             `description:"社区的图表ID"`
+	ChartAlias        string          `description:"图表别名"`
 }
 
 type ChartViewButton struct {
@@ -2101,6 +2157,13 @@ type BarChartInfoReq struct {
 	MarkersLines  string                   `description:"标识线"`
 }
 
+type FutureGoodBarChartInfoReq struct {
+	EdbInfoIdList []BarChartInfoEdbItemReq `description:"指标信息"`
+	DateList      []BarChartInfoDateReq    `description:"日期配置"`
+	XDataList     []XData                  `description:"横轴配置"`
+	BaseEdbInfoId int                      `description:"日期基准指标id"`
+}
+
 // BarChartInfoEdbItemReq 柱方图预览请求数据(指标相关)
 type BarChartInfoEdbItemReq struct {
 	EdbInfoId     int     `description:"指标ID"`
@@ -2112,6 +2175,7 @@ type BarChartInfoEdbItemReq struct {
 	ConvertValue  float64 `description:"数据转换值"`
 	ConvertUnit   string  `description:"数据转换单位"`
 	ConvertEnUnit string  `description:"数据转换单位"`
+	IsHide        int     `description:"是否隐藏该项,0不隐藏,1隐藏"`
 }
 
 // BarChartInfoDateReq 柱方图预览请求数据(日期相关)
@@ -2529,6 +2593,140 @@ type RadarYData struct {
 	Value []float64 `description:"每个指标的值"`
 }
 
+// 截面组合图额外配置
+type ChartSectionExtraConf struct {
+	DateConfList        []*ChartSectionDateConfItem
+	IsHeap              int                      `description:"是否堆积(1.堆积,0不堆积)"`
+	XDataList           []XData                  `description:"横轴名称设置"`
+	UnitList            *ChartSectionCombineUnit `description:"纵轴单位设置"`
+	BaseChartSeriesName string                   `description:"基准系列名称"`
+	SortType            int                      `description:"排序类型,0默认,1升序,2降序"`
+}
+
+// 截面组合图额外配置
+type ChartSectionAllExtraConf struct {
+	ChartSectionExtraConf
+	SeriesList []*ChartSectionSeriesItem
+}
+
+type ChartSectionDateConfItem struct {
+	MoveForward    int    `description:"前移的期数"`
+	EdbInfoId      int    `description:"指标ID"`
+	EdbName        string `description:"指标名称"`
+	EdbNameEn      string `description:"指标名称英文"`
+	EdbInfoType    int    `description:"指标类型"`
+	Frequency      string `description:"频度"`
+	EndDate        string `description:"最新日期"`
+	DateType       int    `description:"日期类型:0 指标日期,1系统日期"`
+	DateConfName   string `description:"引用日期名称"` // 引用日期名称不能重复
+	DateConfNameEn string `description:"引用日期英文名称"`
+	DateChange     []*ChartSectionDateChange
+}
+
+// 截面组合图引用日期配置
+type ChartSectionDateChange struct {
+	Year         int
+	Month        int
+	Day          int
+	Frequency    string `description:"频度变换"`
+	FrequencyDay string `description:"频度的固定日期"`
+	ChangeType   int    `description:"日期变换类型1日期位移,2指定频率"`
+}
+
+// 截面组合图系列配置
+type ChartSectionSeriesItem struct {
+	ChartSeriesId int     `description:"系列ID"`
+	SeriesName    string  `description:"系列名称"` //系列名称不可同名
+	SeriesNameEn  string  `description:"系列英文名称"`
+	ChartStyle    string  `description:"图表类型"`
+	ChartColor    string  `description:"颜色"`
+	ChartWidth    int     `description:"线条大小"`
+	IsPoint       int     `description:"是否用数据点展示(0 否,1是)"`
+	IsNumber      int     `description:"是否用数值展示(0 否,1是)"`
+	IsAxis        int     `description:"1:左轴,0:右轴"`
+	MaxData       float64 `description:"上限"`
+	MinData       float64 `description:"下限"`
+	//IsOrder         bool    `description:"true:正序,false:逆序"`
+	EdbInfoList    []*ChartSectionSeriesEdbConf
+	DataList       []float64
+	NoDataEdbIndex []int
+}
+type ChartSectionSeriesEdbConf struct {
+	ChartSeriesEdbMappingId int `description:"映射ID"`
+	ChartSeriesId           int `description:"系列ID"`
+	//ChartInfoId             int `description:"图表ID"`
+	EdbInfoId    int `description:"指标id"`
+	DateConf     *ChartSectionSeriesDateConfItem
+	EdbName      string `description:"中文别名"`
+	EdbNameEn    string `description:"英文别名"`
+	EdbInfoType  int    `description:"指标类型"`
+	Unit         string `description:"单位"`
+	UnitEn       string `description:"英文单位"`
+	DateConfName string `description:"引用日期名称"`
+	DateConfType int    `description:"日期类型,0指标最新日期, 1引用日期"`
+}
+
+type ChartSectionCombineDataResp struct {
+	DateConfList        []*ChartSectionDateConfItem
+	IsHeap              int                      `description:"是否堆积(1.堆积,0不堆积)"`
+	XDataList           []XData                  `description:"横轴名称设置"`
+	UnitList            *ChartSectionCombineUnit `description:"纵轴单位设置"`
+	BaseChartSeriesName string                   `description:"基准系列名称"`
+	SortType            int                      `description:"排序类型,0默认,1升序,2降序"`
+	SeriesList          []*ChartSectionSeriesItem
+	LeftMin             string `description:"图表左侧最小值"`
+	LeftMax             string `description:"图表左侧最大值"`
+	RightMin            string `description:"图表右侧最小值"`
+	RightMax            string `description:"图表右侧最大值"`
+	Right2Min           string `description:"图表右侧最小值"`
+	Right2Max           string `description:"图表右侧最大值"`
+}
+
+// 系列里的指标日期配置
+type ChartSectionSeriesDateConfItem struct {
+	MoveForward int `description:"前移的期数"`
+	DateChange  []*ChartSectionDateChange
+}
+
+// PreviewSectionCombineChartReq 预览截面组合图的请求
+type PreviewSectionCombineChartReq struct {
+	ChartName       string `description:"图表名称"`
+	ChartClassifyId int    `description:"分类id"`
+	ExtraConfig     string `description:"图表额外配置信息,json字符串"`
+}
+
+type ChartSectionCombineUnit struct {
+	LeftName       string `description:"左轴单位"`
+	LeftNameEn     string `description:"左轴英文单位"`
+	RightName      string `description:"右轴单位"`
+	RightNameEn    string `description:"右轴英文单位"`
+	RightTwoName   string `description:"右2轴单位"`
+	RightTwoNameEn string `description:"右2轴英文单位"`
+}
+
+// 时序组合图额外配置
+type ChartTimeCombineExtraConf struct {
+	IsHeap int `description:"是否堆积(1.堆积,0不堆积)"`
+}
+
+type ChartTimeCombineDataResp struct {
+	IsHeap int `description:"是否堆积(1.堆积,0不堆积)"`
+}
+
+type PreviewSectionCombineDateCalculateResp struct {
+	Date string
+}
+
+type PreviewSectionCombineDateCalculateReq struct {
+	MoveForward  int    `description:"前移的期数"`
+	EdbInfoId    int    `description:"指标ID"`
+	Frequency    string `description:"频度"`
+	DateConfName string `description:"引用日期名称"` // 引用日期名称不能重复
+	DateChange   []*ChartSectionDateChange
+	DateConfList []*ChartSectionDateConfItem
+	DateType     int `description:"日期类型,0指标最新日期, 1引用日期"`
+}
+
 // ChartInfoSourcesFrom 图表来源
 type ChartInfoSourcesFrom struct {
 	IsShow   bool   `description:"是否展示" json:"isShow"`

+ 21 - 0
models/data_manage/chart_info_future_good.go

@@ -0,0 +1,21 @@
+package data_manage
+
+// FutureGoodProfitChartInfoReq 图表预览请求数据
+type FutureGoodProfitChartInfoReq struct {
+	FutureGoodEdbInfoIdList []EdbInfoFromTag                   `description:"指标信息"`
+	CalculateFormula        string                             `description:"计算公式"`
+	BaseEdbInfoId           int                                `description:"基础的指标id"`
+	DateList                []FutureGoodProfitChartInfoDateReq `description:"日期配置"`
+	ProfitNameEn            string                             `description:"利润英文名称"`
+	EdbInfoIdList           []int                              `description:"现货指标ID列表"`
+	XDataList               []XData                            `description:"横轴配置"`
+}
+
+// FutureGoodProfitChartInfoDateReq 图表的日期数据(日期相关)
+type FutureGoodProfitChartInfoDateReq struct {
+	Type  int    `description:"配置类型"`
+	Date  string `description:"固定日期"`
+	Value int    `description:"N天的值"`
+	Color string `description:"颜色"`
+	Name  string `description:"别名"`
+}

+ 374 - 0
models/data_manage/chart_series.go

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

+ 29 - 0
models/data_manage/chart_series_edb_mapping.go

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

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

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

+ 38 - 0
models/data_manage/chart_type.go

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

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

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

+ 90 - 2
models/data_manage/edb_info.go

@@ -940,9 +940,63 @@ func ReplaceChartEdb(oldEdbInfo, newEdbInfo *EdbInfo) (relationEdbInfoIdList []i
 					chartInfo.EdbInfoIds = strings.Join(edbInfoIds, ",")
 					updateStr = append(updateStr, "EdbInfoIds")
 				}
-				if chartInfo.ExtraConfig != "" {
+				if chartInfo.ExtraConfig != "" || chartInfo.BarConfig != "" {
 					//判断是否是拟合方程或者散点图截面图
-					if chartInfo.Source == utils.CHART_SOURCE_LINE_EQUATION {
+					if chartInfo.Source == utils.CHART_SOURCE_FUTURE_GOOD {
+						// 商品价格曲线图的一些配置
+						var barConfig FutureGoodBarChartInfoReq
+						err = json.Unmarshal([]byte(chartInfo.BarConfig), &barConfig)
+						if err != nil {
+							errmsg = "商品价格曲线图配置异常 json.Unmarshal:Err:" + err.Error()
+							return
+						}
+						if barConfig.BaseEdbInfoId == oldEdbInfo.EdbInfoId {
+							barConfig.BaseEdbInfoId = newEdbInfo.EdbInfoId
+						}
+						for k, item := range barConfig.EdbInfoIdList {
+							if item.EdbInfoId == oldEdbInfo.EdbInfoId && item.Source == 1 {
+								barConfig.EdbInfoIdList[k].EdbInfoId = newEdbInfo.EdbInfoId
+							}
+						}
+						// 更新图表配置信息
+						// 重新序列化
+						configJson, e := json.Marshal(barConfig)
+						if e != nil {
+							err = e
+							errmsg = "图表配置信息序列化失败:json.Marshal Err: " + e.Error()
+							return
+						}
+						configStr := string(configJson)
+						chartInfo.BarConfig = configStr
+						updateStr = append(updateStr, "BarConfig")
+					} else if chartInfo.Source == utils.CHART_SOURCE_FUTURE_GOOD_PROFIT {
+						// 商品利润曲线图的一些配置
+						var extraConf FutureGoodProfitChartInfoReq
+						err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConf)
+						if err != nil {
+							errmsg = "商品利润曲线图配置异常 json.Unmarshal:Err:" + err.Error()
+							return
+						}
+						if extraConf.BaseEdbInfoId == oldEdbInfo.EdbInfoId {
+							extraConf.BaseEdbInfoId = newEdbInfo.EdbInfoId
+						}
+						for k, v := range extraConf.EdbInfoIdList {
+							if v == oldEdbInfo.EdbInfoId {
+								extraConf.EdbInfoIdList[k] = newEdbInfo.EdbInfoId
+							}
+						}
+						// 更新图表配置信息
+						// 重新序列化
+						configJson, e := json.Marshal(extraConf)
+						if e != nil {
+							err = e
+							errmsg = "图表配置信息序列化失败:json.Marshal Err: " + e.Error()
+							return
+						}
+						configStr := string(configJson)
+						chartInfo.ExtraConfig = configStr
+						updateStr = append(updateStr, "ExtraConfig")
+					} else if chartInfo.Source == utils.CHART_SOURCE_LINE_EQUATION {
 						//解析配置内容
 						var lineChartInfoConfig request.LineChartInfoReq
 						err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &lineChartInfoConfig)
@@ -1014,6 +1068,33 @@ func ReplaceChartEdb(oldEdbInfo, newEdbInfo *EdbInfo) (relationEdbInfoIdList []i
 						// 更新图表配置信息
 						chartInfo.ExtraConfig = sectionScatterConfigStr
 						updateStr = append(updateStr, "ExtraConfig")
+					} else if chartInfo.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
+						//解析配置内容
+						var tmpExtraConfig ChartSectionExtraConf
+						err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &tmpExtraConfig)
+						if err != nil {
+							errmsg = "获取截面散点图配置信息失败 json.Unmarshal:Err:" + err.Error()
+							return
+						}
+						//找到指标信息,并替换
+						if len(tmpExtraConfig.DateConfList) > 0 {
+							for k1, item := range tmpExtraConfig.DateConfList {
+								if item.EdbInfoId > 0 && item.EdbInfoId == oldEdbInfo.EdbInfoId {
+									tmpExtraConfig.DateConfList[k1].EdbInfoId = newEdbInfo.EdbInfoId
+								}
+							}
+						}
+						// 重新序列化
+						configJson, e := json.Marshal(tmpExtraConfig)
+						if e != nil {
+							err = e
+							errmsg = "图表配置信息序列化失败:json.Marshal Err: " + e.Error()
+							return
+						}
+						configStr := string(configJson)
+						// 更新图表配置信息
+						chartInfo.ExtraConfig = configStr
+						updateStr = append(updateStr, "ExtraConfig")
 					}
 				}
 				if len(updateStr) > 0 {
@@ -1063,6 +1144,13 @@ func ReplaceChartEdb(oldEdbInfo, newEdbInfo *EdbInfo) (relationEdbInfoIdList []i
 					return
 				}
 
+				rsql = ` UPDATE chart_series_edb_mapping SET edb_info_id=? WHERE edb_info_id=? and chart_info_id=?`
+				_, err = to.Raw(rsql, newEdbInfo.EdbInfoId, oldEdbInfo.EdbInfoType, mv.ChartInfoId).Exec()
+				if err != nil {
+					errmsg = "更新图库系列指标信息失败:Err:" + err.Error()
+					return
+				}
+
 				chartInfoIdList = append(chartInfoIdList, fmt.Sprint(mv.ChartInfoId))
 			}
 			logMsg += `涉及到的图表id:` + strings.Join(chartInfoIdList, ",") + ";"

+ 3 - 0
models/data_manage/edb_info_calculate.go

@@ -208,6 +208,7 @@ type EdbInfoCalculateBatchSaveReq struct {
 	MoveFrequency    string           `description:"移动频度:天/周/月/季/年"`
 	Extra            string           `description:"指标的额外配置"`
 	Calendar         string           `description:"公历/农历"`
+	EmptyType        int              `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
 }
 
 type EdbInfoCalculateBatchEditReq struct {
@@ -224,6 +225,7 @@ type EdbInfoCalculateBatchEditReq struct {
 	Calendar      string `description:"公历/农历"`
 	Extra         string `description:"指标的额外配置"`
 	EdbInfoIdArr  []EdbInfoFromTag
+	EmptyType     int `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
 }
 
 // EdbInfoCalculateBatchSaveReqByEdbLib edb指标库的请求逻辑
@@ -265,6 +267,7 @@ type EdbInfoCalculateBatchEditReqByEdbLib struct {
 	EdbInfoIdArr  []EdbInfoFromTag
 	Data          interface{} `description:"数据列"`
 	Extra         string      `description:"指标的额外配置"`
+	EmptyType     int         `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
 }
 
 func GetEdbInfoCalculateMap(edbInfoId, source int) (list []*EdbInfo, err error) {

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

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

+ 14 - 4
models/data_manage/excel/excel_info.go

@@ -452,11 +452,11 @@ func UpdateExcelInfoClassifyId(classifyId, excelInfoId int) (err error) {
 }
 
 // GetNoContentExcelInfoByName 根据名称 获取eta表格详情
-func GetNoContentExcelInfoByName(excelName string, source int) (item *MyExcelInfoList, err error) {
+func GetNoContentExcelInfoByName(excelName string, source, adminId int) (item *MyExcelInfoList, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission,update_user_id,update_user_real_name 
- FROM excel_info WHERE excel_name = ? AND source = ? AND is_delete=0 `
-	err = o.Raw(sql, excelName, source).QueryRow(&item)
+ FROM excel_info WHERE excel_name = ? AND source = ? AND is_delete=0 AND sys_user_id = ? `
+	err = o.Raw(sql, excelName, source, adminId).QueryRow(&item)
 
 	return
 }
@@ -672,7 +672,7 @@ func GetNoContentExcelListByUserId(userIdList []int) (items []*MyExcelInfoList,
 		return
 	}
 	o := orm.NewOrmUsingDB("data")
-	sql := ` SELECT * FROM excel_info WHERE excel_info_id in (` + utils.GetOrmInReplace(num) + `) order by excel_info_id DESC `
+	sql := ` SELECT * FROM excel_info WHERE sys_user_id in (` + utils.GetOrmInReplace(num) + `) order by excel_info_id DESC `
 	_, err = o.Raw(sql, userIdList).QueryRows(&items)
 
 	return
@@ -803,3 +803,13 @@ type ExcelInfoDetailButton struct {
 	RefreshEdbButton bool `description:"是否可刷新指标"`
 	OpWorkerButton   bool `description:"是否修改协作人"`
 }
+
+func UpdateExcelInfoClassifyIdByIds(classifyId int, excelIds []int) (err error) {
+	if len(excelIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`UPDATE excel_info SET excel_classify_id = ? WHERE excel_info_id IN (%s)`, utils.GetOrmInReplace(len(excelIds)))
+	_, err = o.Raw(sql, classifyId, excelIds).Exec()
+	return
+}

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

@@ -153,3 +153,10 @@ type SaveExcelInfoWorkerReq struct {
 	ExcelInfoId int    `description:"ETA表格ID"`
 	SysUserIds  string `description:"协作人ID 用英文逗号拼接"`
 }
+
+// ShareExcelInfoReq 分享表格请求
+type ShareExcelInfoReq struct {
+	ExcelInfoId int   `description:"表格ID"`
+	ViewUserIds []int `description:"查看权限用户IDs"`
+	EditUserIds []int `description:"编辑权限用户IDs"`
+}

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

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

+ 18 - 3
models/data_manage/excel_style.go

@@ -1,8 +1,9 @@
 package data_manage
 
 import (
-	"github.com/beego/beego/v2/client/orm"
 	"time"
+
+	"github.com/beego/beego/v2/client/orm"
 )
 
 type ExcelStyle struct {
@@ -187,7 +188,7 @@ func GetExcelEdbdataMappingByTradeCode(classifyId int, frequency string) (excelI
 func GetExcelStyleById(excelId int) (item *ExcelStyle, err error) {
 	o := orm.NewOrmUsingDB("edb")
 	sql := `SELECT * FROM excel_style WHERE excel_id = ? `
-	err = o.Raw(sql,excelId).QueryRow(&item)
+	err = o.Raw(sql, excelId).QueryRow(&item)
 	return
 }
 
@@ -208,4 +209,18 @@ func GetExcelEdbdataMappingCount(classifyId int, tradeCode, frequency string) (c
 type Ct struct {
 	Fa string `json:"fa"`
 	T  string `json:"t"`
-}
+}
+
+// ManualEdbExcelStyleEditReq
+// @Description: 手工数据录入编辑(Excel样式)
+type ManualEdbExcelStyleEditReq struct {
+	EdbName string
+	Data    []struct {
+		Date  string
+		Value float64
+	}
+	TradeCode  string `description:"指标编码"`
+	Unit       string `description:"单位"`
+	Frequency  string `description:"频度"`
+	ClassifyId int    `description:"分类id"`
+}

+ 19 - 0
models/data_manage/future_good/future_good_edb_info_data.go

@@ -1,6 +1,7 @@
 package future_good
 
 import (
+	"eta/eta_api/utils"
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"time"
@@ -101,3 +102,21 @@ func GetFutureGoodEdbDataListByDate(futureGoodEdbInfoId int, startDate, endDate
 	_, err = o.Raw(sql, futureGoodEdbInfoId, pars).QueryRows(&list)
 	return
 }
+
+func GetFutureGoodEdbDataListByIdsAndDate(futureGoodEdbInfoIds []int, startDate, endDate string) (list []*FutureGoodEdbData, err error) {
+	var pars []interface{}
+	sql := `SELECT * FROM future_good_edb_data WHERE 1=1 AND future_good_edb_info_id in (` + utils.GetOrmInReplace(len(futureGoodEdbInfoIds)) + `)  `
+	if startDate != "" {
+		sql += ` AND data_time>=? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		sql += ` AND data_time<=? `
+		pars = append(pars, endDate)
+	}
+
+	sql += ` ORDER BY data_time ASC `
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, futureGoodEdbInfoIds, pars).QueryRows(&list)
+	return
+}

+ 5 - 12
models/data_manage/future_good/request/future_good_chart.go

@@ -1,6 +1,8 @@
 package request
 
-import "eta/eta_api/models/data_manage"
+import (
+	"eta/eta_api/models/data_manage"
+)
 
 type EditChartEnInfoReq struct {
 	ChartInfoId      int    `description:"图表ID"`
@@ -56,6 +58,8 @@ type ChartInfoReq struct {
 	BaseEdbInfoId           int                          `description:"基础的指标id"`
 	DateList                []ChartInfoDateReq           `description:"日期配置"`
 	ProfitNameEn            string                       `description:"利润英文名称"`
+	EdbInfoIdList           []int                        `description:"现货指标ID列表"`
+	XDataList               []data_manage.XData          `description:"横轴配置"`
 }
 
 // ChartInfoDateReq 图表的日期数据(日期相关)
@@ -110,14 +114,3 @@ type SectionScatterEdbItemReq struct {
 	YDateValue int    `description:"Y轴的日期N天的值"`
 	IsShow     bool   `description:"是否展示"`
 }
-
-// EditChartInfoBaseReq
-// @Description: 修改商品图表的基础信息
-type EditChartInfoBaseReq struct {
-	ChartInfoId    int    `description:"图表ID"`
-	ChartName      string `description:"图表名称(根据当前语言版本不同而不同)"`
-	EdbName        string `description:"指标名称(根据当前语言版本不同而不同)"`
-	Unit           string `description:"指标单位(根据当前语言版本不同而不同)"`
-	ProfitName     string `description:"利润名称(根据当前语言版本不同而不同)"`
-	FutureGoodName string `description:"期货合约名称(根据当前语言版本不同而不同)"`
-}

+ 65 - 40
models/data_manage/future_good_chart_info.go

@@ -56,6 +56,17 @@ func EditFutureGoodChartEnInfoAndEdbEnInfo(chartInfoId int, chartNameEn string,
 	return
 }
 
+type EditFutureGoodChartInfoBaseReq struct {
+	ChartInfoId int    `description:"图表ID"`
+	ChartName   string `description:"图表名称(根据当前语言版本不同而不同)"`
+	/*	EdbName        string `description:"指标名称(根据当前语言版本不同而不同)"`
+		Unit           string `description:"指标单位(根据当前语言版本不同而不同)"`*/
+	ProfitName       string                      `description:"利润名称(根据当前语言版本不同而不同)"`
+	FutureGoodName   string                      `description:"期货合约名称(根据当前语言版本不同而不同)"`
+	ChartEdbInfoList []*EditChartInfoEdbBaseItem `description:"指标及配置信息"`
+	XDataList        []string                    `description:"商品价格曲线的X轴数据"`
+}
+
 // EditBaseFutureGoodChartInfoAndEdbEnInfo
 // @Description:  编辑期货商品基础的图表信息及指标信息
 // @author: Roc
@@ -67,7 +78,7 @@ func EditFutureGoodChartEnInfoAndEdbEnInfo(chartInfoId int, chartNameEn string,
 // @param edbUnit string
 // @param lang string
 // @return err error
-func EditBaseFutureGoodChartInfoAndEdbEnInfo(chartInfo *ChartInfo, chartName string, edbInfoId int, edbName, edbUnit, lang string) (err error) {
+func EditBaseFutureGoodChartInfoAndEdbEnInfo(chartInfo *ChartInfo, req *EditFutureGoodChartInfoBaseReq, lang string) (err error) {
 	o := orm.NewOrmUsingDB("data")
 	to, err := o.Begin()
 	if err != nil {
@@ -85,63 +96,54 @@ func EditBaseFutureGoodChartInfoAndEdbEnInfo(chartInfo *ChartInfo, chartName str
 	updateChartCols := make([]string, 0)
 	switch lang {
 	case utils.EnLangVersion:
-		chartInfo.ChartNameEn = chartName
+		chartInfo.ChartNameEn = req.ChartName
 		updateChartCols = append(updateChartCols, "ChartNameEn")
 	default:
-		chartInfo.ChartName = chartName
+		chartInfo.ChartName = req.ChartName
 		updateChartCols = append(updateChartCols, "ChartName")
 	}
 	chartInfo.ModifyTime = time.Now()
-	updateChartCols = append(updateChartCols, "ModifyTime")
+	updateChartCols = append(updateChartCols, "ModifyTime", "BarConfig")
 	_, err = to.Update(chartInfo, updateChartCols...)
 	if err != nil {
 		fmt.Println("UPDATE  chart_info Err:", err.Error())
 		return err
 	}
-
-	var count int
-	csql := `SELECT COUNT(1) AS count FROM chart_edb_mapping WHERE chart_info_id=? AND edb_info_id=? AND source = 1 `
-	err = to.Raw(csql, chartInfo.ChartInfoId, edbInfoId).QueryRow(&count)
-	if err != nil {
-		fmt.Println("QueryRow Err:", err.Error())
-		return err
-	}
-	if count > 0 {
+	for _, v := range req.ChartEdbInfoList {
 		msql := ` UPDATE edb_info SET modify_time = NOW()  `
 		pars := make([]interface{}, 0)
 		switch lang {
 		case utils.EnLangVersion:
-			if edbName != `` {
+			if v.EdbName != `` {
 				msql += ` ,edb_name_en = ? `
-				pars = append(pars, edbName)
+				pars = append(pars, v.EdbName)
 			}
 
-			if edbUnit != `` {
+			if v.Unit != `` {
 				msql += ` ,unit_en = ? `
-				pars = append(pars, edbUnit)
+				pars = append(pars, v.Unit)
 			}
 
 		default:
 
-			if edbName != `` {
+			if v.EdbName != `` {
 				msql += ` ,edb_name = ? `
-				pars = append(pars, edbName)
+				pars = append(pars, v.EdbName)
 			}
 
-			if edbUnit != `` {
+			if v.Unit != `` {
 				msql += ` ,unit = ? `
-				pars = append(pars, edbUnit)
+				pars = append(pars, v.Unit)
 			}
 		}
 		msql += ` WHERE edb_info_id = ? `
-		pars = append(pars, edbInfoId)
+		pars = append(pars, v.EdbInfoId)
 		_, err = to.Raw(msql, pars...).Exec()
 		if err != nil {
 			fmt.Println("edb_info Err:" + err.Error())
 			return err
 		}
 	}
-
 	return
 }
 
@@ -205,7 +207,7 @@ func EditFutureGoodProfitChartEnInfoAndEdbEnInfo(chartInfoId int, chartNameEn st
 // @param profitName string
 // @param lang string
 // @return err error
-func EditBaseFutureGoodProfitChartInfoAndEdbEnInfo(chartInfo *ChartInfo, chartName string, edbInfoId int, edbName, edbUnit, profitName, lang string) (err error) {
+func EditBaseFutureGoodProfitChartInfoAndEdbEnInfo(chartInfo *ChartInfo, req *EditFutureGoodChartInfoBaseReq, lang string) (err error) {
 	o := orm.NewOrmUsingDB("data")
 	to, err := o.Begin()
 	if err != nil {
@@ -223,32 +225,55 @@ func EditBaseFutureGoodProfitChartInfoAndEdbEnInfo(chartInfo *ChartInfo, chartNa
 	updateChartCols := make([]string, 0)
 	switch lang {
 	case utils.EnLangVersion:
-		chartInfo.ChartNameEn = chartName
+		chartInfo.ChartNameEn = req.ChartName
 		updateChartCols = append(updateChartCols, "ChartNameEn")
 	default:
-		chartInfo.ChartName = chartName
+		chartInfo.ChartName = req.ChartName
 		updateChartCols = append(updateChartCols, "ChartName")
 	}
 	chartInfo.ModifyTime = time.Now()
-	updateChartCols = append(updateChartCols, "ModifyTime")
+	updateChartCols = append(updateChartCols, "ModifyTime", "ExtraConfig")
 	_, err = to.Update(chartInfo, updateChartCols...)
 	if err != nil {
 		fmt.Println("UPDATE  chart_info Err:", err.Error())
 		return err
 	}
-
-	// 更改指标英文信息
 	var sql string
-	switch lang {
-	case utils.EnLangVersion:
-		sql = ` UPDATE  edb_info SET edb_name_en = ?,unit_en = ?,modify_time = NOW() WHERE edb_info_id = ? `
-	default:
-		sql = ` UPDATE  edb_info SET edb_name = ?,unit = ?,modify_time = NOW() WHERE edb_info_id = ? `
-	}
-	_, err = to.Raw(sql, edbName, edbUnit, edbInfoId).Exec()
-	if err != nil {
-		fmt.Println("edb_info Err:" + err.Error())
-		return
+	// 更改指标英文信息
+	for _, v := range req.ChartEdbInfoList {
+		msql := ` UPDATE edb_info SET modify_time = NOW()  `
+		pars := make([]interface{}, 0)
+		switch lang {
+		case utils.EnLangVersion:
+			if v.EdbName != `` {
+				msql += ` ,edb_name_en = ? `
+				pars = append(pars, v.EdbName)
+			}
+
+			if v.Unit != `` {
+				msql += ` ,unit_en = ? `
+				pars = append(pars, v.Unit)
+			}
+
+		default:
+
+			if v.EdbName != `` {
+				msql += ` ,edb_name = ? `
+				pars = append(pars, v.EdbName)
+			}
+
+			if v.Unit != `` {
+				msql += ` ,unit = ? `
+				pars = append(pars, v.Unit)
+			}
+		}
+		msql += ` WHERE edb_info_id = ? `
+		pars = append(pars, v.EdbInfoId)
+		_, err = to.Raw(msql, pars...).Exec()
+		if err != nil {
+			fmt.Println("edb_info Err:" + err.Error())
+			return err
+		}
 	}
 
 	// 更改指标英文信息
@@ -259,7 +284,7 @@ func EditBaseFutureGoodProfitChartInfoAndEdbEnInfo(chartInfo *ChartInfo, chartNa
 	default:
 		sql = ` UPDATE  chart_info_future_good_profit SET profit_name = ?,modify_time = NOW() WHERE chart_info_id = ? `
 	}
-	_, err = to.Raw(sql, profitName, chartInfo.ChartInfoId).Exec()
+	_, err = to.Raw(sql, req.ProfitName, chartInfo.ChartInfoId).Exec()
 	if err != nil {
 		fmt.Println("chart_info_future_good_profit Err:" + err.Error())
 		return

+ 1 - 0
models/data_manage/predict_edb_info_calculate.go

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

+ 7 - 0
models/db.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"eta/eta_api/models/ai_summary"
 	"eta/eta_api/models/aimod"
 	"eta/eta_api/models/company"
 	"eta/eta_api/models/data_manage"
@@ -298,6 +299,7 @@ func initShEdbData() {
 		new(Edbinfo),                         //edb库的edbinfo表
 		new(data_manage.ExcelStyle),          //在线excel样式表
 		new(data_manage.ExcelEdbdataMapping), //excel样式和指标映射表
+		new(EdbinfoOpRecord),                 // 手工数据的操作日志
 	)
 }
 
@@ -367,6 +369,8 @@ func initChart() {
 		new(data_manage.MyChartClassifyMapping),
 		new(data_manage.ChartInfoLog),
 		new(data_manage.ChartInfoCorrelation),
+		new(data_manage.ChartSeries),
+		new(data_manage.ChartSeriesEdbMapping),
 	)
 }
 
@@ -538,6 +542,9 @@ func initAi() {
 		new(aimod.AiChatTopic),
 		new(aimod.AiChat),
 		new(aimod.FileUploadRecord),
+		new(ai_summary.AiSummaryClassify),
+		new(ai_summary.AiSummary),
+		new(ai_summary.AiPrompt),
 	)
 }
 

+ 17 - 2
models/edbdata_import_fail.go

@@ -1,6 +1,9 @@
 package models
 
-import "github.com/beego/beego/v2/client/orm"
+import (
+	"eta/eta_api/utils"
+	"github.com/beego/beego/v2/client/orm"
+)
 
 type EdbdataImportFail struct {
 	Id           int `orm:"column(id);pk"`
@@ -20,6 +23,19 @@ func AddEdbdataImportFail(item *EdbdataImportFail) (err error) {
 	return err
 }
 
+// MultiAddEdbdataImportFail
+// @Description: 批量添加导入失败的信息
+// @author: Roc
+// @datetime 2024-07-30 19:48:06
+// @param items []*EdbdataImportFail
+// @return err error
+func MultiAddEdbdataImportFail(items []*EdbdataImportFail) (err error) {
+	o := orm.NewOrmUsingDB("edb")
+	_, err = o.InsertMulti(utils.MultiAddNum, items)
+
+	return err
+}
+
 // DelEdbDataImportFail 删除导入指标失败记录
 func DelEdbDataImportFail(userId int) (err error) {
 	o := orm.NewOrmUsingDB("edb")
@@ -27,4 +43,3 @@ func DelEdbDataImportFail(userId int) (err error) {
 	_, err = o.Raw(sql, userId).Exec()
 	return err
 }
-

+ 392 - 0
models/manual_edb.go

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

+ 191 - 24
models/target.go

@@ -1,15 +1,17 @@
 package models
 
 import (
+	"context"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/beego/beego/v2/client/orm"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 type DataList struct {
@@ -138,18 +140,49 @@ func DeleteAllEdbData(tradeCode string) (err error) {
 }
 
 type Edbinfo struct {
-	TradeCode    string `orm:"column(TRADE_CODE);pk" description:"指标code"`
-	SecName      string `orm:"column(SEC_NAME);" description:"指标名称"`
-	Unit         string `orm:"column(UNIT);" description:"单位"`
-	Remark       string `orm:"column(REMARK);" description:"备注"`
-	Frequency    string `description:"频度"`
-	ClassifyId   int    `description:"分类id"`
-	ClassifyName string `description:"分类名称"`
-	CreateDate   string `description:"创建时间"`
-	UserId       int    `description:"录入用户id"`
-	UserName     string `description:"录入用户名称"`
-	NoticeTime   string `description:"通知时间"`
-	Mobile       string `description:"录入者手机号"`
+	TradeCode    string  `orm:"column(TRADE_CODE);pk" description:"指标code"`
+	SecName      string  `orm:"column(SEC_NAME);" description:"指标名称"`
+	Unit         string  `orm:"column(UNIT);" description:"单位"`
+	Remark       string  `orm:"column(REMARK);" description:"备注"`
+	Frequency    string  `orm:"column(frequency)" description:"频度"`
+	ClassifyId   int     `orm:"column(classify_id)" description:"分类id"`
+	ClassifyName string  `orm:"-" description:"分类名称"`
+	CreateDate   string  `orm:"column(create_date)" description:"创建时间"`
+	UserId       int     `orm:"column(user_id)" description:"录入用户id"`
+	UserName     string  `orm:"column(user_name)" description:"录入用户名称"`
+	NoticeTime   string  `orm:"column(notice_time)" description:"通知时间"`
+	Mobile       string  `orm:"column(mobile)" description:"录入者手机号"`
+	Sort         int     `orm:"column(sort)" description:"排序"`
+	ModifyTime   string  `description:"最近一次更新时间"`
+	IsJoinEdb    int8    `description:"指标库是否已添加:0-否;1-是"`
+	StartDate    string  `description:"数据开始日期"`
+	EndDate      string  `description:"数据结束日期"`
+	LatestValue  float64 `description:"指标最新值"`
+}
+
+func DeleteEdbinfoByTraceCodeList(tradeCodeList []string) (err error) {
+	if len(tradeCodeList) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("edb")
+	err = o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
+		var holder []string
+		for range tradeCodeList {
+			holder = append(holder, "?")
+		}
+		sql := ` DELETE FROM edbdata WHERE TRADE_CODE in (` + strings.Join(holder, ",") + `) `
+		_, err := txOrm.Raw(sql, tradeCodeList).Exec()
+		if err != nil {
+			return err
+		}
+		sql = ` DELETE FROM edbinfo WHERE TRADE_CODE in (` + strings.Join(holder, ",") + `)`
+		_, err = txOrm.Raw(sql, tradeCodeList).Exec()
+		if err != nil {
+			return err
+		}
+		return nil
+	})
+	return
 }
 
 func GetEdbinfoListCount(condition string, pars []interface{}, mobile string, roleType int) (count int, err error) {
@@ -175,7 +208,26 @@ func GetEdbinfoListCount(condition string, pars []interface{}, mobile string, ro
 	return
 }
 
-func GetEdbinfoList(condition string, pars []interface{}, startSize, pageSize int, mobile string, roleType int) (items []*Edbinfo, err error) {
+type EdbinfoItem struct {
+	TradeCode    string  `orm:"column(TRADE_CODE);pk" description:"指标code"`
+	SecName      string  `orm:"column(SEC_NAME);" description:"指标名称"`
+	Unit         string  `orm:"column(UNIT);" description:"单位"`
+	Remark       string  `orm:"column(REMARK);" description:"备注"`
+	Frequency    string  `description:"频度"`
+	ClassifyId   int     `description:"分类id"`
+	ClassifyName string  `description:"分类名称"`
+	CreateDate   string  `description:"创建时间"`
+	UserId       int     `description:"录入用户id"`
+	UserName     string  `description:"录入用户名称"`
+	NoticeTime   string  `description:"通知时间"`
+	Mobile       string  `description:"录入者手机号"`
+	ModifyTime   string  `description:"最近一次更新时间"`
+	StartDate    string  `description:"数据开始日期"`
+	EndDate      string  `description:"数据结束日期"`
+	LatestValue  float64 `description:"指标最新值"`
+}
+
+func GetEdbinfoItemList(condition string, pars []interface{}, startSize, pageSize int, mobile string, roleType int) (items []*EdbinfoItem, err error) {
 	o := orm.NewOrmUsingDB("edb")
 	sql := ``
 	if mobile != "" && roleType == 1 {
@@ -230,7 +282,7 @@ join edbdata b on a.TRADE_CODE=b.TRADE_CODE
 }
 
 type TargetListResp struct {
-	List   []*Edbinfo
+	List   []*EdbinfoItem
 	Paging *paging.PagingItem `description:"分页数据"`
 }
 
@@ -267,11 +319,25 @@ func GetEdbinfoByTradeCode(tradeCode string) (item *Edbinfo, err error) {
 	return
 }
 
-func AddEdbinfo(tradeCode, secName, unit, remark, frequency, noticeTime string, classifyId int, userId int) (err error) {
-	sql := `INSERT INTO edbinfo(TRADE_CODE, SEC_NAME,UNIT, REMARK,frequency, classify_id,notice_time,user_id,create_date) 
-			VALUES(?,?,?,?,?,?,?,?,now()) `
-	o := orm.NewOrmUsingDB("edb")
-	_, err = o.Raw(sql, tradeCode, secName, unit, remark, frequency, classifyId, noticeTime, userId).Exec()
+func AddEdbinfo(tradeCode, secName, unit, remark, frequency, noticeTime string, classifyId int, userId int, userName string) (err error) {
+	o := orm.NewOrmUsingDB("edb")
+	currTime := time.Now().Format(utils.FormatDateTime)
+	edbInfo := &Edbinfo{
+		TradeCode:  tradeCode,
+		SecName:    secName,
+		Unit:       unit,
+		Remark:     remark,
+		Frequency:  frequency,
+		ClassifyId: classifyId,
+		CreateDate: currTime,
+		UserId:     userId,
+		UserName:   userName,
+		NoticeTime: noticeTime,
+		Mobile:     "",
+		ModifyTime: currTime,
+		IsJoinEdb:  0,
+	}
+	_, err = o.Insert(edbInfo)
 	return
 }
 
@@ -322,6 +388,8 @@ type EdbdataClassify struct {
 	ClassifyName string
 	ParentId     int
 	EdbInfoTotal int
+	TradeCode    string
+	UniqueCode   string
 }
 
 func GetEdbdataClassifyByClassifyName(classifyName string) (item *EdbdataClassify, err error) {
@@ -336,6 +404,8 @@ type EdbdataClassifyList struct {
 	ClassifyName string
 	ParentId     int
 	Child        []*EdbdataClassify
+	TradeCode    string
+	UniqueCode   string
 }
 
 func GetEdbdataClassify(userId int64) (items []*EdbdataClassifyList, err error) {
@@ -1185,10 +1255,16 @@ type EdbInfoItem struct {
 	ClassifyName string     `description:"分类名称"`
 	CreateDate   string     `description:"创建时间"`
 	UserId       int        `description:"录入用户id"`
+	UserName     string     `description:"录入用户名称"`
 	NoticeTime   string     `description:"通知时间"`
 	Mobile       string     `description:"录入者手机号"`
 	ModifyDate   string     `description:"待更新日期"`
+	ModifyTime   string     `description:"最近一次更新时间"`
 	Status       string     `description:"状态:未完成/完成"`
+	IsJoinEdb    int8       `description:"指标库是否已添加:0-否;1-是"`
+	StartDate    string     `description:"数据开始日期"`
+	EndDate      string     `description:"数据结束日期"`
+	LatestValue  float64    `description:"指标最新值"`
 	DataList     []*Edbdata `description:"指标数据列表"`
 }
 
@@ -1466,14 +1542,14 @@ func GetEdbinfoListByCodeListByUserId(userIdList []int) (items []*Edbinfo, err e
 }
 
 // ModifyEdbinfoUserIdByCodeList 根据指标code列表修改创建人
-func ModifyEdbinfoUserIdByCodeList(edbCodeList []string, userId int) (err error) {
+func ModifyEdbinfoUserIdByCodeList(edbCodeList []string, userId int, userName string) (err error) {
 	num := len(edbCodeList)
 	if num <= 0 {
 		return
 	}
 	o := orm.NewOrmUsingDB("edb")
-	sql := `UPDATE edbinfo SET user_id=? WHERE TRADE_CODE in (` + utils.GetOrmInReplace(num) + `) `
-	_, err = o.Raw(sql, userId, edbCodeList).Exec()
+	sql := `UPDATE edbinfo SET user_id = ?,user_name = ? WHERE TRADE_CODE in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, userId, userName, edbCodeList).Exec()
 	return
 }
 
@@ -1502,3 +1578,94 @@ func GetEdbInfoAdminList() (list []int, err error) {
 	_, err = o.Raw(sql).QueryRows(&list)
 	return
 }
+
+// GetAllChildManualEdbClassify
+// @Description: 获取手工数据中所有的子分类
+// @author: Roc
+// @datetime 2024-07-16 13:27:28
+// @return items []*EdbdataClassify
+// @return err error
+func GetAllChildManualEdbClassify() (items []*EdbdataClassify, err error) {
+	o := orm.NewOrmUsingDB("edb")
+
+	sql := ` SELECT classify_id,classify_name,parent_id FROM edbdata_classify WHERE parent_id > 0 `
+	_, err = o.Raw(sql).QueryRows(&items)
+
+	return
+}
+
+// GetChildManualEdbClassifyByIdList
+// @Description: 获取手工数据中所有的子分类
+// @author: Roc
+// @datetime 2024-07-16 13:33:57
+// @param idList []int
+// @return items []*EdbdataClassify
+// @return err error
+func GetChildManualEdbClassifyByIdList(idList []int) (items []*EdbdataClassify, err error) {
+	num := len(idList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("edb")
+
+	sql := ` SELECT classify_id,classify_name,parent_id FROM edbdata_classify WHERE classify_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, idList).QueryRows(&items)
+
+	return
+}
+
+// EdbinfoMaxMinDate
+// @Description: 手工指标的最小最大日期
+type EdbinfoMaxMinDate struct {
+	MinDate string
+	MaxDate string
+}
+
+// GetEdbdataMaxMinDate
+// @Description: 获取手工指标的最小最大日期
+// @author: Roc
+// @datetime 2024-08-01 14:27:20
+// @param tradeCode string
+// @return item EdbinfoMaxMinDate
+// @return err error
+func GetEdbdataMaxMinDate(tradeCode string) (item EdbinfoMaxMinDate, err error) {
+	o := orm.NewOrmUsingDB("edb")
+
+	sql := ` SELECT MIN(DT) min_date,MAX(DT) max_date FROM edbdata WHERE TRADE_CODE = ? `
+	err = o.Raw(sql, tradeCode).QueryRow(&item)
+
+	return
+}
+
+// GetEdbdataLatestValue
+// @Description: 获取手工数据的最新值
+// @author: Roc
+// @datetime 2024-08-02 10:33:22
+// @param tradeCode string
+// @return latestValue float64
+// @return err error
+func GetEdbdataLatestValue(tradeCode string) (latestValue float64, err error) {
+	o := orm.NewOrmUsingDB("edb")
+
+	sql := ` SELECT CLOSE FROM edbdata WHERE TRADE_CODE = ? ORDER BY DT DESC LIMIT 1`
+	err = o.Raw(sql, tradeCode).QueryRow(&latestValue)
+
+	return
+}
+
+// ModifyEdbinfoMaxMinDate
+// @Description: 修改手工指标的最小最大日期
+// @author: Roc
+// @datetime 2024-08-01 15:33:45
+// @param startDate string
+// @param endDate string
+// @param tradeCode string
+// @return err error
+func ModifyEdbinfoMaxMinDate(tradeCode, startDate, endDate string, latestValue float64) (err error) {
+	o := orm.NewOrmUsingDB("edb")
+
+	sql := ` UPDATE edbinfo SET start_date = ?, end_date = ?, latest_value = ? , modify_time = now() WHERE TRADE_CODE = ? `
+	_, err = o.Raw(sql, startDate, endDate, latestValue, tradeCode).Exec()
+
+	return
+}

+ 351 - 9
routers/commentsRouter.go

@@ -16,6 +16,177 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "AddPrompt",
+            Router: `/prompt/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "DeletePrompt",
+            Router: `/prompt/delete`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "DetailPrompt",
+            Router: `/prompt/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "EditPrompt",
+            Router: `/prompt/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "PromptGroupList",
+            Router: `/prompt/groups`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "AiPromptList",
+            Router: `/prompt/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "MoveAiPrompt",
+            Router: `/prompt/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "SharePrompt",
+            Router: `/prompt/share`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "AddAiSummary",
+            Router: `/summary/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "AddAiSummaryClassify",
+            Router: `/summary/classify/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "DeleteAiSummaryClassify",
+            Router: `/summary/classify/delete`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "DeleteSandboxClassifyCheck",
+            Router: `/summary/classify/delete/check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "EditAiSummaryClassify",
+            Router: `/summary/classify/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "AiSummaryClassifyItems",
+            Router: `/summary/classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "AiSummaryClassifyMove",
+            Router: `/summary/classify/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "AiSummaryClassifyList",
+            Router: `/summary/classifyList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "AiSummaryDetail",
+            Router: `/summary/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "GenerateAiSummary",
+            Router: `/summary/generate`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
+        beego.ControllerComments{
+            Method: "AiSummaryList",
+            Router: `/summary/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/ai:AiController"],
         beego.ControllerComments{
             Method: "TopicDelete",
@@ -763,6 +934,33 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/excel/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
+        beego.ControllerComments{
+            Method: "Share",
+            Router: `/excel/share`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
+        beego.ControllerComments{
+            Method: "ShareDetail",
+            Router: `/excel/share_detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
         beego.ControllerComments{
             Method: "ExcelByName",
@@ -772,6 +970,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
+        beego.ControllerComments{
+            Method: "ClassifyList",
+            Router: `/excel_classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
         beego.ControllerComments{
             Method: "Save",
@@ -2671,6 +2878,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
+        beego.ControllerComments{
+            Method: "PreviewSectionCombineChartInfo",
+            Router: `/chart_info/preview/section_combine`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
         beego.ControllerComments{
             Method: "PreviewSectionScatterChartInfo",
@@ -2716,6 +2932,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: "PreviewSectionCombineDateCalculate",
+            Router: `/chart_info/section_combine/date_calculate`,
+            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: "GetChartTypeList",
+            Router: `/chart_info/type_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
         beego.ControllerComments{
             Method: "SaveAdjustEdbInfo",
@@ -4804,6 +5038,123 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "ImportData",
+            Router: `/import/data`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "ClassifyEdbList",
+            Router: `/target/classify/edb/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "BatchAdd2Edb",
+            Router: `/target/edb/batch/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "BatchAddEdbCheck",
+            Router: `/target/edb/batch/add/check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "NameCheck",
+            Router: `/target/edb/batch/add/name_check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "BatchDel",
+            Router: `/target/edb/batch/del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "BatchDelEdbCheck",
+            Router: `/target/edb/batch/del/check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "BatchEdbList",
+            Router: `/target/edb/batch/list`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "EdbDetail",
+            Router: `/target/edb/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "EditExcelData",
+            Router: `/target/edb/excel_style/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "EdbList",
+            Router: `/target/edb/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "RecordList",
+            Router: `/target/edb/op/record/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "EdbSearch",
+            Router: `/target/edb/search`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:MyChartController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:MyChartController"],
         beego.ControllerComments{
             Method: "MyChartAdd",
@@ -9700,15 +10051,6 @@ func init() {
             Filters: nil,
             Params: nil})
 
-    beego.GlobalControllerRouter["eta/eta_api/controllers:TargetController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:TargetController"],
-        beego.ControllerComments{
-            Method: "ImportData",
-            Router: `/import/data`,
-            AllowHTTPMethods: []string{"post"},
-            MethodParams: param.Make(),
-            Filters: nil,
-            Params: nil})
-
     beego.GlobalControllerRouter["eta/eta_api/controllers:TargetController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:TargetController"],
         beego.ControllerComments{
             Method: "ImportFailListDownload",

+ 1 - 0
routers/router.go

@@ -105,6 +105,7 @@ func init() {
 		web.NSNamespace("/entry",
 			web.NSInclude(
 				&controllers.TargetController{},
+				&data_manage.ManualEdbController{},
 			),
 			web.NSInclude(
 				&controllers.TargetCommonController{},

+ 184 - 0
services/ai_summary/ai_summary.go

@@ -0,0 +1,184 @@
+package ai_summary
+
+import (
+	"errors"
+	"eta/eta_api/models/ai_summary"
+	"eta/eta_api/models/system"
+	"eta/eta_api/utils"
+	"strconv"
+)
+
+// GetAiSummaryClassifyListForMe 获取我创建的纪要
+func GetAiSummaryClassifyListForMe(adminInfo system.Admin, resp *ai_summary.AiSummaryClassifyListResp, ai_summaryClassifyId int) (errMsg string, err error) {
+	rootList, err := ai_summary.GetAiSummaryClassifyByParentId(ai_summaryClassifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	for k := range rootList {
+		rootList[k].UniqueCode = strconv.Itoa(rootList[k].AiSummaryClassifyId) + "_" + strconv.Itoa(rootList[k].AiSummaryId)
+	}
+
+	classifyAll, err := ai_summary.GetAiSummaryClassifyByParentId(ai_summaryClassifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	for k := range classifyAll {
+		classifyAll[k].UniqueCode = strconv.Itoa(classifyAll[k].AiSummaryClassifyId) + "_" + strconv.Itoa(classifyAll[k].AiSummaryId)
+	}
+
+	ai_summaryAll, err := ai_summary.GetAiSummaryInfoByAdminId(adminInfo.AdminId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	for k := range ai_summaryAll {
+		ai_summaryAll[k].UniqueCode = strconv.Itoa(ai_summaryAll[k].AiSummaryClassifyId) + "_" + strconv.Itoa(ai_summaryAll[k].AiSummaryId)
+	}
+
+	sandListMap := make(map[int][]*ai_summary.AiSummaryClassifyItems)
+	for _, v := range ai_summaryAll {
+		if _, ok := sandListMap[v.AiSummaryClassifyId]; !ok {
+			list := make([]*ai_summary.AiSummaryClassifyItems, 0)
+			list = append(list, v)
+			sandListMap[v.AiSummaryClassifyId] = list
+		} else {
+			sandListMap[v.AiSummaryClassifyId] = append(sandListMap[v.AiSummaryClassifyId], v)
+		}
+	}
+
+	nodeAll := make([]*ai_summary.AiSummaryClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		AiSummaryClassifyItemsMakeTree(&adminInfo, classifyAll, rootNode)
+		nodeAll = append(nodeAll, rootNode)
+	}
+
+	newAll := AiSummaryItemsMakeTree(nodeAll, sandListMap, ai_summaryClassifyId)
+	resp.AllNodes = newAll
+
+	return
+}
+
+func aiSummaryClassifyHaveChild(allNode []*ai_summary.AiSummaryClassifyItems, node *ai_summary.AiSummaryClassifyItems) (childs []*ai_summary.AiSummaryClassifyItems, yes bool) {
+	for _, v := range allNode {
+		if v.ParentId == node.AiSummaryClassifyId {
+			childs = append(childs, v)
+		}
+	}
+	if len(childs) > 0 {
+		yes = true
+	}
+	return
+}
+
+func AiSummaryClassifyItemsMakeTree(sysUser *system.Admin, allNode []*ai_summary.AiSummaryClassifyItems, node *ai_summary.AiSummaryClassifyItems) {
+
+	childs, _ := aiSummaryClassifyHaveChild(allNode, node) //判断节点是否有子节点并返回
+	if len(childs) > 0 {
+
+		node.Children = append(node.Children, childs[0:]...) //添加子节点
+		for _, v := range childs {                           //查询子节点的子节点,并添加到子节点
+			_, has := aiSummaryClassifyHaveChild(allNode, v)
+			if has {
+				AiSummaryClassifyItemsMakeTree(sysUser, allNode, v) //递归添加节点
+			} else {
+				childrenArr := make([]*ai_summary.AiSummaryClassifyItems, 0)
+				v.Children = childrenArr
+			}
+		}
+	} else {
+		childrenArr := make([]*ai_summary.AiSummaryClassifyItems, 0)
+		node.Children = childrenArr
+	}
+}
+
+func AiSummaryItemsMakeTree(allNode []*ai_summary.AiSummaryClassifyItems, sandListMap map[int][]*ai_summary.AiSummaryClassifyItems, sandboxClassifyId int) (nodeAll []*ai_summary.AiSummaryClassifyItems) {
+	for k := range allNode {
+		if len(allNode[k].Children) > 0 {
+			AiSummaryItemsMakeTree(allNode[k].Children, sandListMap, sandboxClassifyId)
+			allNode = append(allNode, sandListMap[allNode[k].ParentId]...)
+			nodeAll = allNode
+		} else if k == len(allNode)-1 {
+			allNode = append(allNode, sandListMap[allNode[k].ParentId]...)
+			nodeAll = allNode
+		}
+	}
+	if len(allNode) == 0 {
+		nodeAll = append(nodeAll, sandListMap[sandboxClassifyId]...)
+	}
+	return
+}
+
+func AiSummaryClassifyItemsMakeTreeV2(sysUser *system.Admin, allNode []*ai_summary.AiSummaryClassifyItems, node *ai_summary.AiSummaryClassifyItems) {
+
+	childs, _ := aiSummaryClassifyHaveChildV2(allNode, node) //判断节点是否有子节点并返回
+	if len(childs) > 0 {
+
+		node.Children = append(node.Children, childs[0:]...) //添加子节点
+		for _, v := range childs {                           //查询子节点的子节点,并添加到子节点
+			_, has := aiSummaryClassifyHaveChildV2(allNode, v)
+			if has {
+				AiSummaryClassifyItemsMakeTreeV2(sysUser, allNode, v) //递归添加节点
+			} else {
+				//childrenArr := make([]*ai_summary.AiSummaryClassifyItems, 0)
+				//v.Children = childrenArr
+			}
+		}
+	} else {
+		//childrenArr := make([]*ai_summary.AiSummaryClassifyItems, 0)
+		//node.Children = childrenArr
+	}
+}
+
+func aiSummaryClassifyHaveChildV2(allNode []*ai_summary.AiSummaryClassifyItems, node *ai_summary.AiSummaryClassifyItems) (childs []*ai_summary.AiSummaryClassifyItems, yes bool) {
+	for _, v := range allNode {
+		if v.ParentId == node.AiSummaryClassifyId && node.AiSummaryId == 0 {
+			childs = append(childs, v)
+		}
+	}
+	if len(childs) > 0 {
+		yes = true
+	}
+	return
+}
+
+// SharePrompt
+func SharePrompt(promptId int, adminId int) (err error) {
+	//判断当前登录者是否有共享的权限
+	prompt, err := ai_summary.GetAiPromptById(promptId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = errors.New("当前目录下的提示词不存在")
+			return
+		}
+		err = errors.New("目录下的提示词查询出错:" + err.Error())
+		return
+	}
+	if prompt.SysAdminId != adminId {
+		err = errors.New("该提示词不是本人创建")
+		return
+	}
+
+	//判断当前的共享状态
+	if prompt.IsShare > 0 {
+		prompt.IsShare = 0
+		err = prompt.Update([]string{"IsShare"})
+		if err != nil {
+			err = errors.New(err.Error())
+			return
+		}
+	} else {
+		prompt.IsShare = 1
+		err = prompt.Update([]string{"IsShare"})
+		if err != nil {
+			err = errors.New(err.Error())
+			return
+		}
+	}
+	return
+}

+ 711 - 2
services/data/chart_extra_config.go

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

+ 497 - 3
services/data/chart_info.go

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

+ 2 - 2
services/data/chart_info_excel_balance.go

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

+ 51 - 0
services/data/chart_theme.go

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

+ 109 - 3
services/data/data_manage_permission/data_move.go

@@ -230,7 +230,7 @@ func GetMoveEdbChartList(source, subSource, userId int, keyword, classify string
 		if err != nil {
 			return
 		}
-		tmpList, tmpErr := models.GetEdbinfoList(condition, pars, startSize, pageSize, "", 0)
+		tmpList, tmpErr := models.GetEdbinfoItemList(condition, pars, startSize, pageSize, "", 0)
 		if tmpErr != nil {
 			err = tmpErr
 			return
@@ -487,7 +487,7 @@ func GetMoveEdbChartList(source, subSource, userId int, keyword, classify string
 
 // MoveEdbChart 转移指标/图表创建人
 // @param source 来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
-func MoveEdbChart(source, subSource, oldUserId, newUserId int, isSelectAll bool, dataId, noDataId []string, keyword, classify string, opUserId int) (err error, errMsg string) {
+func MoveEdbChart(source, subSource, oldUserId, newUserId int, isSelectAll bool, dataId, noDataId []string, keyword, classify string, opUserId int, opUserName string) (err error, errMsg string) {
 	adminInfo, err := system.GetSysAdminById(newUserId)
 	if err != nil {
 		return
@@ -556,7 +556,12 @@ func MoveEdbChart(source, subSource, oldUserId, newUserId int, isSelectAll bool,
 				})
 			}
 			// 修改创建人
-			err = models.ModifyEdbinfoUserIdByCodeList(dataId, newUserId)
+			err = models.ModifyEdbinfoUserIdByCodeList(dataId, newUserId, adminInfo.RealName)
+
+			// 如果移动成功了,需要记录该指标的操作记录
+			if err == nil && len(tmpList) > 0 {
+				go afterMoveManualEdb(tmpList, adminInfo, opUserId, opUserName)
+			}
 		}
 
 	case 2: //钢联化工数据库
@@ -682,6 +687,7 @@ func MoveEdbChart(source, subSource, oldUserId, newUserId int, isSelectAll bool,
 			err = tmpErr
 			return
 		}
+		var customAnalysisIds []int
 		if len(tmpList) > 0 {
 			for _, v := range tmpList {
 				//if v.SysUserId == newUserId {
@@ -704,9 +710,21 @@ func MoveEdbChart(source, subSource, oldUserId, newUserId int, isSelectAll bool,
 					NewUserName:                adminInfo.RealName,
 					CreateTime:                 time.Now(),
 				})
+
+				if v.Source == utils.CUSTOM_ANALYSIS_TABLE {
+					customAnalysisIds = append(customAnalysisIds, v.ExcelInfoId)
+				}
 			}
 			// 修改创建人
 			err = excel.ModifyExcelInfoUserIdByCodeList(dataId, adminInfo.AdminId, adminInfo.RealName)
+			if err != nil {
+				return
+			}
+
+			// 由于自定义分析表分类私有化, 如果转移的是自定义分析表, 那么需要将excel转移至未分类下
+			if len(customAnalysisIds) > 0 {
+				err = MoveCustomAnalysisExcel2DefaultClassify(customAnalysisIds, adminInfo.AdminId, adminInfo.RealName)
+			}
 		}
 	default:
 		return
@@ -1029,6 +1047,7 @@ func MoveAllEdbChart(sourceList, oldUserIdList []int, userId, opUserId int) (err
 	sourceStrList := make([]string, 0)
 
 	var isMoveManual, isMoveMysteelChemical, isMoveEdb, isMovePredictEdb, isMoveChart, isMoveExcel bool
+	var customAnalysisIds []int
 
 	// 遍历需要转移的模块,1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格,并找出当前需要转移的资产
 	for _, source := range sourceList {
@@ -1206,6 +1225,10 @@ func MoveAllEdbChart(sourceList, oldUserIdList []int, userId, opUserId int) (err
 						NewUserName:                adminInfo.RealName,
 						CreateTime:                 time.Now(),
 					})
+
+					if v.Source == utils.CUSTOM_ANALYSIS_TABLE {
+						customAnalysisIds = append(customAnalysisIds, v.ExcelInfoId)
+					}
 				}
 				isMoveExcel = true
 			}
@@ -1220,6 +1243,15 @@ func MoveAllEdbChart(sourceList, oldUserIdList []int, userId, opUserId int) (err
 		return
 	}
 
+	// 由于自定义分析表分类私有化, 如果转移的含自定义分析表, 那么需要将excel转移至未分类下
+	if len(customAnalysisIds) > 0 {
+		err = MoveCustomAnalysisExcel2DefaultClassify(customAnalysisIds, adminInfo.AdminId, adminInfo.RealName)
+		if err != nil {
+			err = fmt.Errorf("MoveCustomAnalysisExcel2DefaultClassify, err: %v", err)
+			return
+		}
+	}
+
 	// 添加资产转移的记录
 	content += fmt.Sprintf("(%s)", strings.Join(sourceStrList, ","))
 	dataPermissionMessage := &data_manage_permission.DataPermissionMessage{
@@ -1383,3 +1415,77 @@ func GetMoveEdbChartCount(userId, countType int) (sourceMap map[int]int, err err
 
 	return
 }
+
+// afterMoveManualEdb
+// @Description: 手工数据移动后的操作
+// @author: Roc
+// @datetime 2024-08-01 13:55:59
+// @param tmpList []*models.Edbinfo
+// @param adminInfo *system.Admin
+// @param opUserId int
+// @param opUserName string
+func afterMoveManualEdb(tmpList []*models.Edbinfo, adminInfo *system.Admin, opUserId int, opUserName string) {
+	recordList := make([]*models.EdbinfoOpRecord, 0)
+	for _, tmpManualInfo := range tmpList {
+		remark := fmt.Sprintf("数据资产由%s转移给%s", tmpManualInfo.UserName, adminInfo.RealName)
+		recordList = append(recordList, &models.EdbinfoOpRecord{
+			TradeCode:  tmpManualInfo.TradeCode,
+			Remark:     remark,
+			UserId:     opUserId,
+			UserName:   opUserName,
+			CreateTime: time.Now(),
+		})
+	}
+	obj := models.EdbinfoOpRecord{}
+	_ = obj.MulCreate(recordList)
+}
+
+// MoveCustomAnalysisExcel2DefaultClassify 移动表格至用户未分类目录下
+func MoveCustomAnalysisExcel2DefaultClassify(excelIds []int, adminId int, realName string) (err error) {
+	if len(excelIds) == 0 {
+		return
+	}
+	var moveClassifyId int
+	// 查询默认分类
+	cond := ` AND excel_classify_name = ? AND sys_user_id = ?`
+	pars := make([]interface{}, 0)
+	pars = append(pars, "未分类", adminId)
+	classify, e := excel.GetExcelClassifyByCondition(cond, pars)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		err = fmt.Errorf("获取默认分类失败, %v", e)
+		return
+	}
+	if classify != nil {
+		moveClassifyId = classify.ExcelClassifyId
+	}
+
+	// 新增一个新的分类
+	if classify == nil {
+		classifyNew := &excel.ExcelClassify{
+			ExcelClassifyName: "未分类",
+			Source:            utils.CUSTOM_ANALYSIS_TABLE,
+			SysUserId:         adminId,
+			SysUserRealName:   realName,
+			Level:             1,
+			UniqueCode:        utils.MD5(utils.EXCEL_DATA_PREFIX + "_" + strconv.FormatInt(time.Now().UnixNano(), 10)),
+			CreateTime:        time.Now(),
+			ModifyTime:        time.Now(),
+		}
+		if _, e = excel.AddExcelClassify(classifyNew); e != nil {
+			err = fmt.Errorf("新增默认分类失败, %v", e)
+			return
+		}
+		moveClassifyId = classifyNew.ExcelClassifyId
+	}
+	if moveClassifyId <= 0 {
+		err = fmt.Errorf("移动分类ID有误")
+		return
+	}
+
+	// 更新表格分类ID
+	if e = excel.UpdateExcelInfoClassifyIdByIds(moveClassifyId, excelIds); e != nil {
+		err = fmt.Errorf("移动多表格分类失败, %v", e)
+		return
+	}
+	return
+}

+ 2 - 8
services/data/edb_classify.go

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

+ 45 - 7
services/data/edb_info.go

@@ -6,6 +6,7 @@ import (
 	"eta/eta_api/cache"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/services/elastic"
@@ -711,7 +712,7 @@ func EdbInfoRefresh(edbInfo *data_manage.EdbInfoView, refreshAll bool) (err erro
 }
 
 // AddEdbInfo 新增手工指标数据
-func AddEdbInfo(secName, unit, frequency, noticeTime, mobile string, classifyId, userId int) (err error) {
+func AddEdbInfo(secName, unit, frequency, noticeTime, mobile string, classifyId, userId int, userName string) (err error) {
 	tradeCode, err := models.GetMaxTradeCode()
 	if err != nil {
 		return
@@ -746,8 +747,10 @@ func AddEdbInfo(secName, unit, frequency, noticeTime, mobile string, classifyId,
 	}
 	// 先删除该指标编码的历史数据(避免上海删除指标时,没有删除该指标数据)
 	_ = models.DeleteAllEdbData(maxTradeCode)
+	// 先删除历史的操作日志
+	_ = models.DelEdbinfoOpRecordByTradeCode(maxTradeCode)
 	// 再次新增指标
-	err = models.AddEdbinfo(maxTradeCode, secName, unit, "手动", frequency, noticeTime, classifyId, userId)
+	err = models.AddEdbinfo(maxTradeCode, secName, unit, "手动", frequency, noticeTime, classifyId, userId, userName)
 	if err != nil {
 		err = errors.New("新增失败,Err:" + err.Error())
 		return
@@ -1910,11 +1913,7 @@ func EdbInfoAdd(source, subSource, classifyId int, edbCode, edbName, frequency,
 	//添加es
 	AddOrEditEdbInfoToEs(int(edbInfoId))
 
-	// 更新钢联化工状态为启用
-	if edbInfo.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
-		// 启动钢联的刷新
-		_ = data_manage.UpdateMysteelChemicalRefreshStatus(edbCode, 0)
-	}
+	go handleByAddEdbInfo(edbInfo)
 
 	return
 }
@@ -2928,3 +2927,42 @@ func GetEdbTerminalCodeBySource(source int, edbCode, stockCode string) (terminal
 	}
 	return
 }
+
+// handleByAddEdbInfo
+// @Description: 添加指标后的处理操作
+// @author: Roc
+// @datetime 2024-07-22 13:06:36
+// @param edbInfo *data_manage.EdbInfo
+func handleByAddEdbInfo(edbInfo *data_manage.EdbInfo) {
+	// 更新钢联化工状态为启用
+	if edbInfo.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+		// 启动钢联的刷新
+		_ = data_manage.UpdateMysteelChemicalRefreshStatus(edbInfo.EdbCode, 0)
+	}
+
+	// 如果是手工数据,那么需要标记手工数据为已加入指标库
+	if edbInfo.Source == utils.DATA_SOURCE_MANUAL {
+		_ = models.UpdateManualIsJoinEdbStatus(edbInfo.EdbCode, 1)
+	}
+
+}
+
+// handleByDelEdbInfo
+// @Description: 删除指标后的处理操作
+// @author: Roc
+// @datetime 2024-07-22 13:06:36
+// @param edbInfo *data_manage.EdbInfo
+func handleByDelEdbInfo(edbInfo *data_manage.EdbInfo) {
+	// 如果删除的指标是自定义分析的来源,那么还需要删除指标与excel的关系
+	if edbInfo.Source == utils.DATA_SOURCE_CALCULATE_ZDYFX {
+		err := excel.DeleteCustomAnalysisExcelEdbMappingByEdbInfoId(edbInfo.EdbInfoId)
+		if err != nil {
+			utils.FileLog.Error(fmt.Sprintf("删除指标时,需要删除与自定义分析的关系失败,指标ID:%d,Err:%s", edbInfo.EdbInfoId, err.Error()), 3)
+		}
+	}
+
+	// 如果是手工数据,那么需要标记手工数据为未加入指标库
+	if edbInfo.Source == utils.DATA_SOURCE_MANUAL {
+		_ = models.UpdateManualIsJoinEdbStatus(edbInfo.EdbCode, 0)
+	}
+}

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

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

+ 266 - 128
services/data/future_good/chart_info.go

@@ -15,42 +15,46 @@ import (
 )
 
 // GetChartEdbData 获取图表的指标数据
-func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping, futureGoodEdbInfoMapping *data_manage.ChartEdbInfoMapping, barChartInfoDateList []data_manage.BarChartInfoDateReq, needData bool) (barConfigEdbInfoIdList []data_manage.BarChartInfoEdbItemReq, edbList []*data_manage.ChartEdbInfoMapping, xEdbIdValue []int, xDataList []data_manage.XData, yDataList []data_manage.YData, err error) {
+func GetChartEdbData(chartInfoId int, startDate, endDate string, baseEdbInfoMapping *data_manage.ChartEdbInfoMapping, edbInfoMappingList []*data_manage.ChartEdbInfoMapping, futureGoodEdbInfoMapping *data_manage.ChartEdbInfoMapping, barChartInfoConf data_manage.FutureGoodBarChartInfoReq, needData bool) (barConfigEdbInfoIdList []data_manage.BarChartInfoEdbItemReq, edbList []*data_manage.ChartEdbInfoMapping, xEdbIdValue []int, xDataList []data_manage.XData, yDataList []data_manage.YData, err error) {
 	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
-
+	barChartInfoDateList := barChartInfoConf.DateList
 	if futureGoodEdbInfoMapping == nil {
 		err = errors.New("商品指标未选取")
 		return
 	}
-	if edbInfoMapping == nil {
+	if len(edbInfoMappingList) == 0 {
 		err = errors.New("ETA指标未选取")
 		return
 	}
+
 	// 指标对应的所有数据
 	edbDataListMap := make(map[int][]*data_manage.EdbDataList)
 
-	item := new(data_manage.ChartEdbInfoMapping)
-	edbInfoMapping.FrequencyEn = data.GetFrequencyEn(edbInfoMapping.Frequency)
+	// todo item
+	//item := new(data_manage.ChartEdbInfoMapping)
 
-	if edbInfoMapping.Unit == `无` {
-		edbInfoMapping.Unit = ``
-	}
 	if futureGoodEdbInfoMapping.Unit == `无` {
 		futureGoodEdbInfoMapping.Unit = ``
 	}
 
 	if chartInfoId <= 0 {
-		edbInfoMapping.IsAxis = 1
-		edbInfoMapping.LeadValue = 0
-		edbInfoMapping.LeadUnit = ""
-		edbInfoMapping.ChartEdbMappingId = 0
-		edbInfoMapping.ChartInfoId = 0
-		edbInfoMapping.IsOrder = false
-		edbInfoMapping.EdbInfoType = 1
-		edbInfoMapping.ChartStyle = ""
-		edbInfoMapping.ChartColor = ""
-		edbInfoMapping.ChartWidth = 0
-
+		for k, edbInfoMapping := range edbInfoMappingList {
+			edbInfoMapping.FrequencyEn = data.GetFrequencyEn(edbInfoMapping.Frequency)
+			if edbInfoMapping.Unit == `无` {
+				edbInfoMapping.Unit = ``
+			}
+			edbInfoMapping.IsAxis = 1
+			edbInfoMapping.LeadValue = 0
+			edbInfoMapping.LeadUnit = ""
+			edbInfoMapping.ChartEdbMappingId = 0
+			edbInfoMapping.ChartInfoId = 0
+			edbInfoMapping.IsOrder = false
+			edbInfoMapping.EdbInfoType = 1
+			edbInfoMapping.ChartStyle = ""
+			edbInfoMapping.ChartColor = ""
+			edbInfoMapping.ChartWidth = 0
+			edbInfoMappingList[k] = edbInfoMapping
+		}
 		futureGoodEdbInfoMapping.IsAxis = 1
 		futureGoodEdbInfoMapping.LeadValue = 0
 		futureGoodEdbInfoMapping.LeadUnit = ""
@@ -62,21 +66,43 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 		futureGoodEdbInfoMapping.ChartColor = ""
 		futureGoodEdbInfoMapping.ChartWidth = 0
 	} else {
-		edbInfoMapping.LeadUnitEn = data.GetLeadUnitEn(edbInfoMapping.LeadUnit)
+		for k, edbInfoMapping := range edbInfoMappingList {
+			edbInfoMapping.FrequencyEn = data.GetFrequencyEn(edbInfoMapping.Frequency)
+			if edbInfoMapping.Unit == `无` {
+				edbInfoMapping.Unit = ``
+			}
+			edbInfoMapping.LeadUnitEn = data.GetLeadUnitEn(edbInfoMapping.LeadUnit)
+			edbInfoMappingList[k] = edbInfoMapping
+		}
 		futureGoodEdbInfoMapping.LeadUnitEn = data.GetLeadUnitEn(futureGoodEdbInfoMapping.LeadUnit)
 	}
 
+	//查询横轴配置项
+	xDataItemMap := make(map[int]data_manage.XData)
+	for k, v := range barChartInfoConf.XDataList {
+		xDataItemMap[k] = v
+	}
+
 	// 普通的指标数据
 	{
-		edbList = append(edbList, edbInfoMapping)
-
-		barConfigEdbInfoIdList = append(barConfigEdbInfoIdList, data_manage.BarChartInfoEdbItemReq{
-			EdbInfoId: edbInfoMapping.EdbInfoId,
-			//Name:      edbInfoMapping.EdbName,
-			Name:   "现货价",
-			NameEn: "Spot Price",
-			Source: edbInfoMapping.Source,
-		})
+		edbList = edbInfoMappingList
+
+		for k, edbInfoMapping := range edbInfoMappingList {
+			tmp := data_manage.BarChartInfoEdbItemReq{
+				EdbInfoId: edbInfoMapping.EdbInfoId,
+				Name:      edbInfoMapping.EdbName,
+				NameEn:    edbInfoMapping.EdbNameEn,
+				Source:    edbInfoMapping.Source,
+			}
+			// 如果有配置,则用配置中的指标名称替换
+			xItem, ok := xDataItemMap[k]
+			if ok && xItem.Name != "" {
+				tmp.Name = xItem.Name
+				tmp.NameEn = xItem.NameEn
+				tmp.IsHide = xItem.IsHide
+			}
+			barConfigEdbInfoIdList = append(barConfigEdbInfoIdList, tmp)
+		}
 	}
 
 	// 获取主力合约和最新日期
@@ -88,6 +114,7 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 		// 寻找主力合约
 		zlFutureGoodEdbInfo, err = future_good2.GetFutureGoodEdbInfo(futureGoodEdbInfoMapping.EdbInfoId)
 		if err != nil {
+			err = fmt.Errorf("查不到期货指标,GetFutureGoodEdbInfo err: %v", err)
 			return
 		}
 		regionType = zlFutureGoodEdbInfo.RegionType
@@ -97,7 +124,7 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 		if zlFutureGoodEdbInfo.DateSourceId != zlFutureGoodEdbInfo.FutureGoodEdbInfoId {
 			sourceDateFutureGoodEdbInfo, tmpErr := future_good2.GetFutureGoodEdbInfo(zlFutureGoodEdbInfo.DateSourceId)
 			if tmpErr != nil {
-				err = tmpErr
+				err = fmt.Errorf("获取期货指标 %d 的日期来源的指标信息失败,错误:%s", zlFutureGoodEdbInfo.DateSourceId, tmpErr.Error())
 				return
 			}
 			latestDate = sourceDateFutureGoodEdbInfo.EndDate // 最新日期是这个
@@ -119,6 +146,7 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 	latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)
 	_, futureGoodEdbInfoList, err := getFutureGoodEdbInfoList(latestDateTime, tmpFutureGoodEdbInfoList, barChartInfoDateList)
 	if err != nil {
+		err = fmt.Errorf("获取期货指标列表失败,getFutureGoodEdbInfoList 错误:%s", err.Error())
 		return
 	}
 
@@ -132,30 +160,30 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 			EdbName:             v.FutureGoodEdbName,
 			EdbAliasName:        v.FutureGoodEdbName,
 			EdbNameEn:           v.FutureGoodEdbNameEn,
-			EdbType:             edbInfoMapping.EdbType,
-			Frequency:           edbInfoMapping.Frequency,
-			FrequencyEn:         edbInfoMapping.FrequencyEn,
-			Unit:                edbInfoMapping.Unit,
-			UnitEn:              edbInfoMapping.UnitEn,
+			EdbType:             baseEdbInfoMapping.EdbType,
+			Frequency:           baseEdbInfoMapping.Frequency,
+			FrequencyEn:         baseEdbInfoMapping.FrequencyEn,
+			Unit:                baseEdbInfoMapping.Unit,
+			UnitEn:              baseEdbInfoMapping.UnitEn,
 			StartDate:           v.StartDate,
 			EndDate:             v.EndDate,
 			ModifyTime:          v.ModifyTime.Format(utils.FormatDateTime),
 			ChartEdbMappingId:   v.FutureGoodEdbInfoId,
-			ChartInfoId:         edbInfoMapping.ChartInfoId,
+			ChartInfoId:         baseEdbInfoMapping.ChartInfoId,
 			MaxData:             v.MaxValue,
 			MinData:             v.MinValue,
-			IsOrder:             edbInfoMapping.IsOrder,
-			IsAxis:              edbInfoMapping.IsAxis,
-			EdbInfoType:         edbInfoMapping.EdbInfoType,
-			EdbInfoCategoryType: edbInfoMapping.EdbInfoCategoryType,
-			LeadValue:           edbInfoMapping.LeadValue,
-			LeadUnit:            edbInfoMapping.LeadUnit,
-			LeadUnitEn:          edbInfoMapping.LeadUnitEn,
-			ChartStyle:          edbInfoMapping.ChartStyle,
-			ChartColor:          edbInfoMapping.ChartColor,
-			PredictChartColor:   edbInfoMapping.PredictChartColor,
-			ChartWidth:          edbInfoMapping.ChartWidth,
-			ChartType:           edbInfoMapping.ChartType,
+			IsOrder:             baseEdbInfoMapping.IsOrder,
+			IsAxis:              baseEdbInfoMapping.IsAxis,
+			EdbInfoType:         baseEdbInfoMapping.EdbInfoType,
+			EdbInfoCategoryType: baseEdbInfoMapping.EdbInfoCategoryType,
+			LeadValue:           baseEdbInfoMapping.LeadValue,
+			LeadUnit:            baseEdbInfoMapping.LeadUnit,
+			LeadUnitEn:          baseEdbInfoMapping.LeadUnitEn,
+			ChartStyle:          baseEdbInfoMapping.ChartStyle,
+			ChartColor:          baseEdbInfoMapping.ChartColor,
+			PredictChartColor:   baseEdbInfoMapping.PredictChartColor,
+			ChartWidth:          baseEdbInfoMapping.ChartWidth,
+			ChartType:           baseEdbInfoMapping.ChartType,
 			LatestDate:          v.LatestDate.Format(utils.FormatDateTime),
 			LatestValue:         v.LatestValue,
 			UniqueCode:          futureGoodEdbInfoMapping.UniqueCode + strconv.Itoa(k),
@@ -166,12 +194,21 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 		}
 		futureGoodMappingList = append(futureGoodMappingList, newMappingInfo)
 
-		barConfigEdbInfoIdList = append(barConfigEdbInfoIdList, data_manage.BarChartInfoEdbItemReq{
+		tmp := data_manage.BarChartInfoEdbItemReq{
 			EdbInfoId: newMappingInfo.EdbInfoId,
 			Name:      fmt.Sprint("M+", v.Month),
 			NameEn:    fmt.Sprint("M+", v.Month),
 			Source:    newMappingInfo.Source,
-		})
+		}
+		// 判断如果有配置,需要替换为配置内容
+		xItem, ok := xDataItemMap[k]
+		if ok && xItem.Name != "" {
+			tmp.Name = xItem.Name
+			tmp.NameEn = xItem.NameEn
+			tmp.IsHide = xItem.IsHide
+		}
+		barConfigEdbInfoIdList = append(barConfigEdbInfoIdList, tmp)
+
 	}
 
 	if regionType == `海外` {
@@ -183,30 +220,30 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 			EdbName:             zlFutureGoodEdbInfo.FutureGoodEdbName,
 			EdbAliasName:        zlFutureGoodEdbInfo.FutureGoodEdbName,
 			EdbNameEn:           zlFutureGoodEdbInfo.FutureGoodEdbNameEn,
-			EdbType:             edbInfoMapping.EdbType,
-			Frequency:           edbInfoMapping.Frequency,
-			FrequencyEn:         edbInfoMapping.FrequencyEn,
-			Unit:                edbInfoMapping.Unit,
-			UnitEn:              edbInfoMapping.UnitEn,
+			EdbType:             baseEdbInfoMapping.EdbType,
+			Frequency:           baseEdbInfoMapping.Frequency,
+			FrequencyEn:         baseEdbInfoMapping.FrequencyEn,
+			Unit:                baseEdbInfoMapping.Unit,
+			UnitEn:              baseEdbInfoMapping.UnitEn,
 			StartDate:           zlFutureGoodEdbInfo.StartDate,
 			EndDate:             zlFutureGoodEdbInfo.EndDate,
 			ModifyTime:          zlFutureGoodEdbInfo.ModifyTime.Format(utils.FormatDateTime),
 			ChartEdbMappingId:   zlFutureGoodEdbInfo.FutureGoodEdbInfoId,
-			ChartInfoId:         edbInfoMapping.ChartInfoId,
+			ChartInfoId:         baseEdbInfoMapping.ChartInfoId,
 			MaxData:             zlFutureGoodEdbInfo.MaxValue,
 			MinData:             zlFutureGoodEdbInfo.MinValue,
-			IsOrder:             edbInfoMapping.IsOrder,
-			IsAxis:              edbInfoMapping.IsAxis,
-			EdbInfoType:         edbInfoMapping.EdbInfoType,
-			EdbInfoCategoryType: edbInfoMapping.EdbInfoCategoryType,
-			LeadValue:           edbInfoMapping.LeadValue,
-			LeadUnit:            edbInfoMapping.LeadUnit,
-			LeadUnitEn:          edbInfoMapping.LeadUnitEn,
-			ChartStyle:          edbInfoMapping.ChartStyle,
-			ChartColor:          edbInfoMapping.ChartColor,
-			PredictChartColor:   edbInfoMapping.PredictChartColor,
-			ChartWidth:          edbInfoMapping.ChartWidth,
-			ChartType:           edbInfoMapping.ChartType,
+			IsOrder:             baseEdbInfoMapping.IsOrder,
+			IsAxis:              baseEdbInfoMapping.IsAxis,
+			EdbInfoType:         baseEdbInfoMapping.EdbInfoType,
+			EdbInfoCategoryType: baseEdbInfoMapping.EdbInfoCategoryType,
+			LeadValue:           baseEdbInfoMapping.LeadValue,
+			LeadUnit:            baseEdbInfoMapping.LeadUnit,
+			LeadUnitEn:          baseEdbInfoMapping.LeadUnitEn,
+			ChartStyle:          baseEdbInfoMapping.ChartStyle,
+			ChartColor:          baseEdbInfoMapping.ChartColor,
+			PredictChartColor:   baseEdbInfoMapping.PredictChartColor,
+			ChartWidth:          baseEdbInfoMapping.ChartWidth,
+			ChartType:           baseEdbInfoMapping.ChartType,
 			LatestDate:          zlFutureGoodEdbInfo.LatestDate.Format(utils.FormatDateTime),
 			LatestValue:         zlFutureGoodEdbInfo.LatestValue,
 			UniqueCode:          futureGoodEdbInfoMapping.UniqueCode + strconv.Itoa(0),
@@ -228,24 +265,45 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 	// 获取数据
 
 	// 现货数据
-	{
+	for _, edbInfoMapping := range edbInfoMappingList {
 		dataList := make([]*data_manage.EdbDataList, 0)
 		dataList, err = data_manage.GetEdbDataList(edbInfoMapping.Source, edbInfoMapping.SubSource, edbInfoMapping.EdbInfoId, startDate, endDate)
 		if err != nil {
 			return
 		}
 		edbDataListMap[edbInfoMapping.EdbInfoId] = dataList
-		item.DataList = dataList
+		// todo item
+		//item.DataList = dataList
 	}
-
-	// 期货数据
+	futureEdbInfoIds := make([]int, 0)
 	for _, v := range futureGoodMappingList {
-		dataList := make([]*data_manage.EdbDataList, 0)
-
-		tmpDataList, tmpErr := future_good2.GetFutureGoodEdbDataListByDate(v.EdbInfoId, startDate, endDate)
+		futureEdbInfoIds = append(futureEdbInfoIds, v.EdbInfoId)
+	}
+	tmpDataListMap := make(map[int][]*future_good2.FutureGoodEdbData)
+	if len(futureEdbInfoIds) > 0 {
+		// 期货数据
+		tmpDataList, tmpErr := future_good2.GetFutureGoodEdbDataListByIdsAndDate(futureEdbInfoIds, startDate, endDate)
 		if tmpErr != nil {
+			err = tmpErr
 			return
 		}
+
+		for _, v := range tmpDataList {
+			if _, ok := tmpDataListMap[v.FutureGoodEdbInfoId]; !ok {
+				tmpDataListMap[v.FutureGoodEdbInfoId] = make([]*future_good2.FutureGoodEdbData, 0)
+			}
+			tmpDataListMap[v.FutureGoodEdbInfoId] = append(tmpDataListMap[v.FutureGoodEdbInfoId], v)
+		}
+	}
+
+	for _, v := range futureGoodMappingList {
+		dataList := make([]*data_manage.EdbDataList, 0)
+		tmpDataList, ok := tmpDataListMap[v.EdbInfoId]
+		if !ok {
+			/*err = fmt.Errorf("期货数据不存在 FutureGoodEdbInfoId: %d", v.EdbInfoId)
+			return*/
+			continue
+		}
 		for _, tmpData := range tmpDataList {
 			dataList = append(dataList, &data_manage.EdbDataList{
 				EdbDataId:     tmpData.FutureGoodEdbDataId,
@@ -259,14 +317,20 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 		v.DataList = dataList
 	}
 
-	xEdbIdValue, yDataList, err = BarChartData(edbList[0], futureGoodEdbInfoList, edbDataListMap, barChartInfoDateList, regionType, edbInfoMapping.EndDate)
+	xEdbIdValue, yDataList, err = BarChartData(baseEdbInfoMapping, edbInfoMappingList, futureGoodEdbInfoList, edbDataListMap, barChartInfoDateList, regionType, baseEdbInfoMapping.EndDate)
 
-	xDataList = []data_manage.XData{
-		{
-			Name:   "现货价",
-			NameEn: "Spot Price",
-		},
+	if len(barChartInfoConf.XDataList) > 0 {
+		xDataList = barChartInfoConf.XDataList
+	} else {
+		for _, v := range edbInfoMappingList {
+			xDataList = append(xDataList, data_manage.XData{
+				Name:   v.EdbName,
+				NameEn: v.EdbNameEn,
+				IsHide: 0,
+			})
+		}
 	}
+
 	//yDataList =make([]data_manage.YData,0)
 	//data_manage.YData{
 	//	Date:           "",
@@ -296,14 +360,15 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 		}
 
 		// 基础指标
-		{
-			baseEdbInfId := edbList[0].EdbInfoId
-			edbInfoIdList = append(edbInfoIdList, baseEdbInfId)
-			tmpVal, ok := yDataMap[baseEdbInfId]
+		baseEdbIds := make([]int, 0)
+		for _, tmp := range edbInfoMappingList {
+			edbInfoIdList = append(edbInfoIdList, tmp.EdbInfoId)
+			baseEdbIds = append(baseEdbIds, tmp.EdbInfoId)
+			tmpVal, ok := yDataMap[tmp.EdbInfoId]
 			valueList = append(valueList, tmpVal)
 			if !ok || tmpVal == 0 {
-				noDataEdbInfoIdList = append(noDataEdbInfoIdList, baseEdbInfId)
-				noDataEdbIdMap[baseEdbInfId] = baseEdbInfId
+				noDataEdbInfoIdList = append(noDataEdbInfoIdList, tmp.EdbInfoId)
+				noDataEdbIdMap[tmp.EdbInfoId] = tmp.EdbInfoId
 			}
 		}
 
@@ -326,8 +391,8 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 
 		for i := lenEdbId - 1; i >= 0; i-- {
 			// 如果没有在无数据的指标列表中找到,那么就找到了最大x轴的下标
-			if _, ok := noDataEdbIdMap[edbInfoIdList[i]]; !ok {
-				// 如果最大x轴的下标 小于 当前下标,那么就重新赋值
+			if _, ok := noDataEdbIdMap[edbInfoIdList[i]]; !ok || utils.InArrayByInt(baseEdbIds, edbInfoIdList[i]) { //以往的逻辑是碰到第一个无数据的期货指标,那后续的月份都是无数据的,因此需要特殊处理,改成多个现货指标之后需要排除无值的现货指标
+				// 如果最大x轴的下标 小于 当前下标,那么就重新赋值
 				if maxIndex < i-1 {
 					maxIndex = i - 1
 				}
@@ -362,22 +427,44 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 			yDataList[k].Value = v.Value[0 : maxIndex+1]
 		}
 	}
-
-	tmpXDataList, newYDataList, err := handleResultData(regionType, futureGoodEdbType, yDataList, futureGoodEdbInfoList, maxIndex)
+	baseEdbLen := len(edbInfoMappingList)
+	tmpXDataList, newYDataList, err := handleResultData(regionType, futureGoodEdbType, baseEdbLen, yDataList, futureGoodEdbInfoList, maxIndex)
 	if err != nil {
 		return
 	}
-	xDataList = append(xDataList, tmpXDataList...)
+	if len(barChartInfoConf.XDataList) == 0 {
+		xDataList = append(xDataList, tmpXDataList...)
+	}
+
 	yDataList = newYDataList
 
 	return
 }
 
 // BarChartData 获取数据
-func BarChartData(edbInfoMapping *data_manage.ChartEdbInfoMapping, futureGoodMappingList []*future_good2.FutureGoodEdbInfo, edbDataListMap map[int][]*data_manage.EdbDataList, barChartInfoDateList []data_manage.BarChartInfoDateReq, regionType, latestDate string) (edbIdList []int, yDataList []data_manage.YData, err error) {
+func BarChartData(baseEdbInfoMapping *data_manage.ChartEdbInfoMapping, edbInfoMappingList []*data_manage.ChartEdbInfoMapping, futureGoodMappingList []*future_good2.FutureGoodEdbInfo, edbDataListMap map[int][]*data_manage.EdbDataList, barChartInfoDateList []data_manage.BarChartInfoDateReq, regionType, latestDate string) (edbIdList []int, yDataList []data_manage.YData, err error) {
 	// 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3})
+	// 现货指标数据map
+	baseEdbDataMap := make(map[int]map[string]float64)
+	edbInfoMappingMap := make(map[int]struct{})
+	for _, v := range edbInfoMappingList {
+		edbInfoMappingMap[v.EdbInfoId] = struct{}{}
+	}
+	for edbInfoId, edbDataList := range edbDataListMap {
+		if _, ok1 := edbInfoMappingMap[edbInfoId]; ok1 {
+			edbDateData := make(map[string]float64)
+			for _, edbData := range edbDataList {
+				edbDateData[edbData.DataTime] = edbData.Value
+			}
+			baseEdbDataMap[edbInfoId] = edbDateData
+		}
+	}
+
 	edbDataMap := make(map[int]map[string]float64)
 	for edbInfoId, edbDataList := range edbDataListMap {
+		if _, ok := edbInfoMappingMap[edbInfoId]; ok {
+			continue
+		}
 		edbDateData := make(map[string]float64)
 		for _, edbData := range edbDataList {
 			edbDateData[edbData.DataTime] = edbData.Value
@@ -387,7 +474,10 @@ func BarChartData(edbInfoMapping *data_manage.ChartEdbInfoMapping, futureGoodMap
 
 	// edbIdList 指标展示顺序;x轴的指标顺序
 	edbIdList = make([]int, 0)
-	edbIdList = append(edbIdList, edbInfoMapping.EdbInfoId)
+	for _, v := range edbInfoMappingList {
+		edbIdList = append(edbIdList, v.EdbInfoId)
+	}
+
 	for _, v := range futureGoodMappingList {
 		edbIdList = append(edbIdList, v.FutureGoodEdbInfoId)
 	}
@@ -428,23 +518,43 @@ func BarChartData(edbInfoMapping *data_manage.ChartEdbInfoMapping, futureGoodMap
 		noDataIdMap := make(map[int]int, 0) // 没有数据的指标map
 		xEdbInfoIdList := make([]int, 0)    // 当前数据的指标id列表
 
-		// 现货指标
-		realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, edbDataListMap[edbInfoMapping.EdbInfoId], edbDataMap[edbInfoMapping.EdbInfoId])
+		// 先找到基准日期
+		var realDateTime time.Time
+		realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, edbDataListMap[baseEdbInfoMapping.EdbInfoId], baseEdbDataMap[baseEdbInfoMapping.EdbInfoId], edbDataMap)
 		if tmpErr != nil {
 			err = tmpErr
 			return
 		}
-		findDataList = append(findDataList, findDataValue)
-		yDataMap[edbInfoMapping.EdbInfoId] = findDataValue
-		if isFind {
-			maxDate = realDateTime
-		} else {
-			noDataIdList = append(noDataIdList, edbInfoMapping.EdbInfoId)
-			noDataIdMap[edbInfoMapping.EdbInfoId] = edbInfoMapping.EdbInfoId
+		// 处理其余现货指标
+		for _, v := range edbInfoMappingList {
+			if v.EdbInfoId == baseEdbInfoMapping.EdbInfoId {
+				findDataList = append(findDataList, findDataValue)
+				yDataMap[baseEdbInfoMapping.EdbInfoId] = findDataValue
+				if isFind {
+					maxDate = realDateTime
+				} else {
+					noDataIdList = append(noDataIdList, baseEdbInfoMapping.EdbInfoId)
+					noDataIdMap[baseEdbInfoMapping.EdbInfoId] = baseEdbInfoMapping.EdbInfoId
+				}
+				xEdbInfoIdList = append(xEdbInfoIdList, v.EdbInfoId)
+				continue
+			}
+			findDataValueTmp, isFindTmp := baseEdbDataMap[v.EdbInfoId][realDateTime.Format(utils.FormatDate)]
+			findDataList = append(findDataList, findDataValueTmp)
+			yDataMap[v.EdbInfoId] = findDataValueTmp
+			if !isFindTmp {
+				noDataIdList = append(noDataIdList, v.EdbInfoId)
+				noDataIdMap[v.EdbInfoId] = v.EdbInfoId
+			}
+			xEdbInfoIdList = append(xEdbInfoIdList, v.EdbInfoId)
 		}
-		currMonth := findDateTime.Month() // 当前月份
-		currYear := findDateTime.Year()   // 当前年份
-		xEdbInfoIdList = append(xEdbInfoIdList, edbInfoMapping.EdbInfoId)
+		//currMonth := findDateTime.Month() // 当前月份
+		//currYear := findDateTime.Year()   // 当前年份
+
+		// 用实际日期的月份作为基准,往前推12个月(2024-5-13 16:26:43修改)
+		currMonth := realDateTime.Month() // 当前月份
+		currYear := realDateTime.Year()   // 当前年份
+
 		mList := make([]int, 0) // 间隔月份
 		indexList := make([]int, 0)
 		if regionType == `国内` {
@@ -564,10 +674,9 @@ func BarChartData(edbInfoMapping *data_manage.ChartEdbInfoMapping, futureGoodMap
 }
 
 // handleResultData 处理成最终的结果数据
-func handleResultData(regionType string, futureGoodEdbType int, yDataList []data_manage.YData, futureGoodEdbInfoList []*future_good2.FutureGoodEdbInfo, maxIndex int) (xDataList []data_manage.XData, newYDataList []data_manage.YData, err error) {
+func handleResultData(regionType string, futureGoodEdbType, baseEdbLen int, yDataList []data_manage.YData, futureGoodEdbInfoList []*future_good2.FutureGoodEdbInfo, maxIndex int) (xDataList []data_manage.XData, newYDataList []data_manage.YData, err error) {
 	xDataList = make([]data_manage.XData, 0)
 	newYDataList = yDataList
-
 	if regionType == `国内` {
 		for i := 1; i < 12; i++ {
 			if i > maxIndex {
@@ -601,11 +710,11 @@ func handleResultData(regionType string, futureGoodEdbType int, yDataList []data
 			tmpNList := nList
 
 			//当前的主力合约
-			newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, yData.XEdbInfoIdList[0])
-			newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, yData.Value[0])
+			newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, yData.XEdbInfoIdList[0:baseEdbLen]...)
+			newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, yData.Value[0:baseEdbLen]...)
 
-			xEdbInfoIdList := yData.XEdbInfoIdList[1:]
-			valIndex := 1
+			xEdbInfoIdList := yData.XEdbInfoIdList[baseEdbLen:]
+			valIndex := baseEdbLen
 			needNum := 0
 			for _, n := range tmpNList {
 				if len(xEdbInfoIdList) > 0 {
@@ -677,8 +786,8 @@ func handleResultData(regionType string, futureGoodEdbType int, yDataList []data
 		currYear := findDateTime.Year()   // 当前年份
 		//v.XEdbInfoIdList
 		for edbInfoIndex, edbInfoId := range v.XEdbInfoIdList {
-			// 第一个不处理
-			if edbInfoIndex == 0 {
+			// 现货指标不处理
+			if edbInfoIndex <= baseEdbLen-1 {
 				continue
 			}
 
@@ -717,12 +826,12 @@ func handleResultData(regionType string, futureGoodEdbType int, yDataList []data
 		tmpNList := nList
 
 		//当前的主力合约
-		newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, yData.XEdbInfoIdList[0])
-		newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, yData.Value[0])
+		newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, yData.XEdbInfoIdList[0:baseEdbLen]...)
+		newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, yData.Value[0:baseEdbLen]...)
 
-		xEdbInfoIdList := yData.XEdbInfoIdList[1:]
+		xEdbInfoIdList := yData.XEdbInfoIdList[baseEdbLen:]
 		currDataTime := yData.ConfigDate
-		valIndex := 1
+		valIndex := baseEdbLen
 		needNum := 0
 		for _, n := range tmpNList {
 			if len(xEdbInfoIdList) > 0 {
@@ -860,7 +969,7 @@ func getFutureGoodEdbInfoList(latestDateTime time.Time, tmpFutureGoodEdbInfoList
 }
 
 // GetNeedDateData 获取合约内需要的日期数据
-func GetNeedDateData(needDateTime time.Time, dataList []*data_manage.EdbDataList, edbDataMap map[string]float64) (findDateTime time.Time, findDataValue float64, isFind bool, err error) {
+func GetNeedDateData(needDateTime time.Time, dataList []*data_manage.EdbDataList, edbDataMap map[string]float64, allEdbDataMap map[int]map[string]float64) (findDateTime time.Time, findDataValue float64, isFind bool, err error) {
 	//dataList := edbDataListMap[edbInfoId] //指标的所有数据值
 	if len(dataList) <= 0 {
 		// 没有数据的指标id
@@ -873,18 +982,43 @@ func GetNeedDateData(needDateTime time.Time, dataList []*data_manage.EdbDataList
 		return
 	}
 
+	// 该日期存在数据的期货指标的最小数量,目前是现货和期货各1个,总共2个
+	maxCount := 1
+
 	for tmpDateTime := needDateTime; tmpDateTime.After(minDateTime) || tmpDateTime.Equal(minDateTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
 		tmpDate := tmpDateTime.Format(utils.FormatDate)
-		if tmpValue, ok := edbDataMap[tmpDate]; ok { //如果能找到数据,那么就返回
-			// 数据为0,也直接返回,做无值处理
-			if tmpValue == 0 {
-				return
+		tmpValue, ok := edbDataMap[tmpDate]
+		if !ok {
+			continue
+		}
+
+		// 该日期存在数据的指标数量
+		count := 0
+
+		for _, currEdbDataMap := range allEdbDataMap {
+			_, tmpIsFind := currEdbDataMap[tmpDate]
+			if tmpIsFind {
+				count++
+				if count >= maxCount {
+					continue
+				}
 			}
-			findDateTime, _ = time.ParseInLocation(utils.FormatDate, tmpDate, time.Local)
-			findDataValue = tmpValue
-			isFind = true
+		}
+
+		// 该日期存在数据的期货指标数量小于2个,那么要继续往前找
+		if count < maxCount {
+			continue
+		}
+
+		//如果能找到数据,那么就返回
+		// 数据为0,也直接返回,做无值处理
+		if tmpValue == 0 {
 			return
 		}
+		findDateTime, _ = time.ParseInLocation(utils.FormatDate, tmpDate, time.Local)
+		findDataValue = tmpValue
+		isFind = true
+		return
 	}
 
 	return
@@ -903,11 +1037,15 @@ func FutureGoodChartInfoRefresh(chartInfoId int) (err error) {
 		}
 	}()
 
-	edbInfoMapping, err := data_manage.GetEtaEdbChartEdbMapping(chartInfoId)
+	edbInfoMappingList, err := data_manage.GetEtaEdbChartEdbMappingList(chartInfoId)
 	if err != nil {
 		errMsg = "获取需要刷新的ETA指标失败:Err:" + err.Error()
 		return
 	}
+	edbInfoIds := make([]int, 0)
+	for _, edbInfoMapping := range edbInfoMappingList {
+		edbInfoIds = append(edbInfoIds, edbInfoMapping.EdbInfoId)
+	}
 	// 获取期货指标
 	futureGoodEdbInfoMapping, err := data_manage.GetFutureGoodEdbChartEdbMapping(chartInfoId)
 	if err != nil {
@@ -922,7 +1060,7 @@ func FutureGoodChartInfoRefresh(chartInfoId int) (err error) {
 	}
 
 	// 批量刷新ETA指标
-	err, _ = data.EdbInfoRefreshAllFromBaseV3([]int{edbInfoMapping.EdbInfoId}, false, true, false)
+	err, _ = data.EdbInfoRefreshAllFromBaseV3(edbInfoIds, false, true, false)
 	if err != nil {
 		return
 	}

+ 137 - 43
services/data/future_good/profit_chart_info.go

@@ -17,13 +17,16 @@ import (
 )
 
 // GetProfitChartEdbData 获取利润图表的指标数据
-func GetProfitChartEdbData(baseEdbInfo *data_manage.EdbInfo, zlFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, chartInfoDateList []request.ChartInfoDateReq, formulaStr string, edbInfoFromTagList []data_manage.EdbInfoFromTag) (barConfigEdbInfoIdList []data_manage.BarChartInfoEdbItemReq, edbList []*data_manage.ChartEdbInfoMapping, xEdbIdValue []int, xDataList []data_manage.XData, yDataList []data_manage.YData, err error) {
+func GetProfitChartEdbData(baseEdbInfo *data_manage.EdbInfo, edbInfoList []*data_manage.EdbInfo, zlFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, chartInfoDateList []request.ChartInfoDateReq, formulaStr string, edbInfoFromTagList []data_manage.EdbInfoFromTag, reqXDataList []data_manage.XData) (barConfigEdbInfoIdList []data_manage.BarChartInfoEdbItemReq, edbList []*data_manage.ChartEdbInfoMapping, xEdbIdValue []int, xDataList []data_manage.XData, yDataList []data_manage.YData, err error) {
 	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
 
 	if baseEdbInfo == nil {
 		err = errors.New("ETA指标未选取")
 		return
 	}
+	if len(edbInfoList) == 0 {
+		edbInfoList = append(edbInfoList, baseEdbInfo)
+	}
 	if len(zlFutureGoodEdbInfoList) <= 0 {
 		err = errors.New("商品指标未选取")
 		return
@@ -55,10 +58,14 @@ func GetProfitChartEdbData(baseEdbInfo *data_manage.EdbInfo, zlFutureGoodEdbInfo
 	}
 
 	// 普通的指标数据
-	baseDataList := make([]*data_manage.EdbDataList, 0)
-	baseDataList, err = data_manage.GetEdbDataList(baseEdbInfo.Source, baseEdbInfo.SubSource, baseEdbInfo.EdbInfoId, "", "")
-	if err != nil {
-		return
+	baseDataListMap := make(map[int][]*data_manage.EdbDataList)
+	for _, v := range edbInfoList {
+		baseDataList := make([]*data_manage.EdbDataList, 0)
+		baseDataList, err = data_manage.GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, "", "")
+		if err != nil {
+			return
+		}
+		baseDataListMap[v.EdbInfoId] = baseDataList
 	}
 
 	latestDate := zlFutureGoodEdbInfoList[0].EndDate
@@ -170,33 +177,47 @@ func GetProfitChartEdbData(baseEdbInfo *data_manage.EdbInfo, zlFutureGoodEdbInfo
 	sort.Slice(dateList, func(i, j int) bool {
 		return dateList[i] < dateList[j]
 	})
+	var reqEdbInfoIds []int
+	for _, v := range edbInfoList {
+		reqEdbInfoIds = append(reqEdbInfoIds, v.EdbInfoId)
+		tmp := data_manage.XData{
+			Name:   v.EdbName,
+			NameEn: v.EdbNameEn,
+		}
+		xDataList = append(xDataList, tmp)
+	}
+	var edbIdList []int
 
-	_, yDataList, err = ProfitChartChartData(baseDataList, futureGoodEdbInfoDateMap, futureGoodDataListMap, chartInfoDateList, baseEdbInfo.EndDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateList, maxN)
+	edbIdList, yDataList, err = ProfitChartChartData(baseEdbInfo, baseDataListMap, futureGoodEdbInfoDateMap, futureGoodDataListMap, chartInfoDateList, baseEdbInfo.EndDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateList, maxN, reqEdbInfoIds)
 
-	tmpXDataList, newYDataList, err := handleProfitResultData(baseEdbInfo, yDataList, earliestDateTime)
+	// todo 最后处理数据
+	tmpXDataList, newYDataList, err := handleProfitResultData(xDataList, baseEdbInfo, yDataList, earliestDateTime, edbIdList)
 	if err != nil {
 		return
 	}
-	xDataList = []data_manage.XData{
-		{
-			Name:   "现货利润",
-			NameEn: "Spot Price",
-		},
+	if len(reqXDataList) == 0 {
+		xDataList = tmpXDataList
+	} else {
+		xDataList = reqXDataList
 	}
-	xDataList = append(xDataList, tmpXDataList...)
+
 	yDataList = newYDataList
 
 	return
 }
 
 // ProfitChartChartData 获取数据
-func ProfitChartChartData(baseDataList []*data_manage.EdbDataList, futureGoodEdbInfoMap map[int]map[string]*future_good.FutureGoodEdbInfo, futureGoodEdbDataListMap map[int][]*data_manage.EdbDataList, chartInfoDateList []request.ChartInfoDateReq, latestDate string, specialFutureGoodEdbInfoMap map[int]map[int]*future_good.FutureGoodEdbInfo, formulaStr string, tagEdbIdMap map[string]int, dateList []string, maxN int) (edbIdList []int, yDataList []data_manage.YData, err error) {
+func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[int][]*data_manage.EdbDataList, futureGoodEdbInfoMap map[int]map[string]*future_good.FutureGoodEdbInfo, futureGoodEdbDataListMap map[int][]*data_manage.EdbDataList, chartInfoDateList []request.ChartInfoDateReq, latestDate string, specialFutureGoodEdbInfoMap map[int]map[int]*future_good.FutureGoodEdbInfo, formulaStr string, tagEdbIdMap map[string]int, dateList []string, maxN int, reqEdbInfoIds []int) (edbIdList []int, yDataList []data_manage.YData, err error) {
 	// 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3})
 	//earliestDateTime time.Time
 	// ETA指标数据
-	baseEdbDateData := make(map[string]float64)
-	for _, edbData := range baseDataList {
-		baseEdbDateData[edbData.DataTime] = edbData.Value
+	allBaseEdbDateDataMap := make(map[int]map[string]float64)
+	for edbInfoId, baseDataList := range baseDataListMap {
+		baseEdbDateData := make(map[string]float64)
+		for _, edbData := range baseDataList {
+			baseEdbDateData[edbData.DataTime] = edbData.Value
+		}
+		allBaseEdbDateDataMap[edbInfoId] = baseEdbDateData
 	}
 
 	// 商品指标数据
@@ -243,23 +264,55 @@ func ProfitChartChartData(baseDataList []*data_manage.EdbDataList, futureGoodEdb
 			return
 		}
 
-		findDataList := make([]float64, 0) // 当前日期的数据值
-		noDataIdList := make([]int, 0)     // 没有数据的指标id
-		xEdbInfoIdList := make([]int, 0)   // 当前数据的指标id列表
+		findDataList := make([]float64, 0)  // 当前日期的数据值
+		noDataIdList := make([]int, 0)      // 没有数据的指标id
+		noDataIdMap := make(map[int]int, 0) // 没有数据的指标map
+		xEdbInfoIdList := make([]int, 0)    // 当前数据的指标id列表
 
 		// 现货指标
-		realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, baseDataList, baseEdbDateData)
+		index := 0
+		var realDateTime time.Time
+		// 现货指标
+		baseEdbDateData, ok := allBaseEdbDateDataMap[baseEdbInfo.EdbInfoId]
+		if !ok {
+			err = fmt.Errorf("指标id: %d 没有数据", baseEdbInfo.EdbInfoId)
+			return
+		}
+		realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, baseDataListMap[baseEdbInfo.EdbInfoId], baseEdbDateData, edbDataMap)
 		if tmpErr != nil {
 			err = tmpErr
 			return
 		}
-		findDataList = append(findDataList, findDataValue)
-		yDataMap[0] = findDataValue
 		if isFind {
 			maxDate = realDateTime
 		}
+		edbIdList = make([]int, 0) //普通指标ID
+		for _, edbInfoId := range reqEdbInfoIds {
+			if edbInfoId == baseEdbInfo.EdbInfoId {
+				findDataList = append(findDataList, findDataValue)
+				yDataMap[index] = findDataValue
+				xEdbInfoIdList = append(xEdbInfoIdList, edbInfoId)
+				edbIdList = append(edbIdList, edbInfoId)
+				index += 1
+				continue
+			}
+			baseEdbDateDataTmp, ok := allBaseEdbDateDataMap[edbInfoId]
+			if !ok {
+				err = fmt.Errorf("指标id: %d 没有数据", edbInfoId)
+				return
+			}
+			findDataValueTmp, isFindTmp := baseEdbDateDataTmp[realDateTime.Format(utils.FormatDate)]
+			if !isFindTmp {
+				noDataIdList = append(noDataIdList, edbInfoId)
+				noDataIdMap[edbInfoId] = edbInfoId
+			}
+			findDataList = append(findDataList, findDataValueTmp)
+			yDataMap[index] = findDataValueTmp
 
-		xEdbInfoIdList = append(xEdbInfoIdList, 0)
+			xEdbInfoIdList = append(xEdbInfoIdList, edbInfoId)
+			edbIdList = append(edbIdList, edbInfoId)
+			index += 1
+		}
 
 		mList := make([]int, 0) // 间隔月份
 
@@ -274,7 +327,7 @@ func ProfitChartChartData(baseDataList []*data_manage.EdbDataList, futureGoodEdb
 			//findDateTime
 
 			// 获取当前日期相对开始日期的期数
-			tmpN := (currDate.Year()-findDateTime.Year())*12 + int(currDate.Month()-findDateTime.Month())
+			tmpN := (currDate.Year()-realDateTime.Year())*12 + int(currDate.Month()-realDateTime.Month())
 			if tmpN <= 0 {
 				continue
 			}
@@ -329,7 +382,7 @@ func ProfitChartChartData(baseDataList []*data_manage.EdbDataList, futureGoodEdb
 			//计算公式异常,那么就移除该指标
 			if formulaFormStr == `` {
 				//removeDateList = append(removeDateList, sk)
-				//fmt.Println("异常了")
+				fmt.Println("异常了")
 				continue
 			}
 
@@ -382,6 +435,33 @@ func ProfitChartChartData(baseDataList []*data_manage.EdbDataList, futureGoodEdb
 			yDate = maxDate.Format(utils.FormatDate)
 		}
 
+		{
+			hasDataIndexList := make([]int, 0)
+			for dataK, edbInfoId := range xEdbInfoIdList {
+				if _, ok := noDataIdMap[edbInfoId]; !ok { // 如果是没有数据的指标id
+					hasDataIndexList = append(hasDataIndexList, dataK)
+				}
+			}
+			lenHasDataIndex := len(hasDataIndexList)
+			if lenHasDataIndex > 0 {
+				for lenHasDataI := 1; lenHasDataI < lenHasDataIndex; lenHasDataI++ {
+					perK := hasDataIndexList[lenHasDataI-1] //上一个有数据的指标下标
+					currK := hasDataIndexList[lenHasDataI]  //当前有数据的指标下标
+					preVal := findDataList[perK]            //上一个有数据的坐标的值
+					currVal := findDataList[currK]          //当前有数据的指标的值
+
+					// 环差值
+					hcValDeci := decimal.NewFromFloat(currVal).Sub(decimal.NewFromFloat(preVal)).Div(decimal.NewFromInt(int64(currK - perK)))
+					var tmpI int64
+					// 将两个中间的数据做平均值补全
+					for hcI := perK + 1; hcI < currK; hcI++ {
+						tmpI++
+						findDataList[hcI], _ = decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(tmpI))).RoundCeil(4).Float64()
+					}
+				}
+			}
+		}
+
 		yDataList = append(yDataList, data_manage.YData{
 			Date:           yDate,
 			ConfigDate:     realDateTime,
@@ -473,29 +553,37 @@ func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbI
 }
 
 // handleProfitResultData 处理成最终的结果数据
-func handleProfitResultData(baseEdbInfo *data_manage.EdbInfo, yDataList []data_manage.YData, earliestDateTime time.Time) (xDataList []data_manage.XData, newYDataList []data_manage.YData, err error) {
-	xDataList = make([]data_manage.XData, 0)
+func handleProfitResultData(xDataListInit []data_manage.XData, baseEdbInfo *data_manage.EdbInfo, yDataList []data_manage.YData, earliestDateTime time.Time, allEdbInfoIds []int) (xDataList []data_manage.XData, newYDataList []data_manage.YData, err error) {
 	newYDataList = yDataList
+	xDataList = xDataListInit
 
 	nMap := make(map[int]int)
-
+	nList := make([]int, 0)
+	nListEdbMap := make(map[int]struct{})
 	for _, v := range yDataList {
 		for _, n := range v.XEdbInfoIdList {
-			nMap[n] = n
+			if utils.InArrayByInt(allEdbInfoIds, n) {
+				if _, ok := nListEdbMap[n]; !ok {
+					nList = append(nList, n)
+					nListEdbMap[n] = struct{}{}
+				}
+			} else {
+				nMap[n] = n
+			}
 		}
 	}
 
 	// 找出所有的N值,并进行正序排列
-	nList := make([]int, 0)
+	nListTmp := make([]int, 0)
 	for _, n := range nMap {
-		nList = append(nList, n)
+		nListTmp = append(nListTmp, n)
 	}
-	sort.Slice(nList, func(i, j int) bool {
-		return nList[i] < nList[j]
+	sort.Slice(nListTmp, func(i, j int) bool {
+		return nListTmp[i] < nListTmp[j]
 	})
-
+	nList = append(nList, nListTmp...)
 	for _, n := range nList {
-		if n == 0 {
+		if utils.InArrayByInt(allEdbInfoIds, n) {
 			continue
 		}
 		xDataList = append(xDataList, data_manage.XData{
@@ -516,7 +604,7 @@ func handleProfitResultData(baseEdbInfo *data_manage.EdbInfo, yDataList []data_m
 			if len(xEdbInfoIdList) > 0 {
 				currN := xEdbInfoIdList[0]
 				// 当前距离最早的日期相差的N数
-				if n == currN {
+				if n == currN { // todo 改成所有的基础现货指标
 					if needNum > 0 {
 						currVal := yData.Value[valIndex]
 						preVal := yData.Value[valIndex-1]
@@ -530,8 +618,12 @@ func handleProfitResultData(baseEdbInfo *data_manage.EdbInfo, yDataList []data_m
 							newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, tmpVal)
 						}
 					}
+					if utils.InArrayByInt(allEdbInfoIds, currN) {
+						newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN)
+					} else {
+						newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN+1)
+					}
 
-					newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN+1)
 					newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, yData.Value[valIndex])
 					valIndex++
 					needNum = 0
@@ -558,7 +650,7 @@ func handleProfitResultData(baseEdbInfo *data_manage.EdbInfo, yDataList []data_m
 	}
 
 	earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local)
-	xDataList = xDataList[0:maxI]
+	xDataList = xDataList[0 : maxI+1]
 	for yIndex, yData := range newYDataList {
 		if len(yData.XEdbInfoIdList) > maxI+1 {
 			newYDataList[yIndex].XEdbInfoIdList = yData.XEdbInfoIdList[0 : maxI+1]
@@ -571,10 +663,11 @@ func handleProfitResultData(baseEdbInfo *data_manage.EdbInfo, yDataList []data_m
 
 		nameList := make([]string, 0)
 		enNameList := make([]string, 0)
-		for _, n := range newYDataList[yIndex].XEdbInfoIdList {
-			if n == 1 { // 现货价不处理
-				nameList = append(nameList, baseEdbInfo.EdbName)
-				enNameList = append(enNameList, baseEdbInfo.EdbNameEn)
+		for k1, n := range newYDataList[yIndex].XEdbInfoIdList {
+			if utils.InArrayByInt(allEdbInfoIds, n) { // 现货价不处理
+				tmpItem := xDataListInit[k1]
+				nameList = append(nameList, tmpItem.Name)
+				enNameList = append(enNameList, tmpItem.NameEn)
 				continue
 			}
 			if n <= 0 {
@@ -639,6 +732,7 @@ func ReplaceFormula(tagEdbIdMap map[string]int, valArr map[int]float64, formulaS
 	for k, v := range funMap {
 		formulaStr = strings.Replace(formulaStr, v, k, -1)
 	}
+	fmt.Println(formulaStr)
 	if replaceCount == len(tagEdbIdMap) {
 		return formulaStr
 	} else {

+ 1001 - 0
services/data/manual.go

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

+ 41 - 0
services/eta_forum/eta_forum_hub.go

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

+ 1 - 1
services/report_approve.go

@@ -841,7 +841,7 @@ func AfterReportApprovePass(reportType, reportId int) (err error) {
 
 		// 生成报告pdf和长图
 		{
-			reportPdfUrl := GetGeneralPdfUrl(reportInfo.ReportCode, reportInfo.ReportLayout)
+			reportPdfUrl := GetGeneralPdfUrl(reportInfo.ReportCode, reportInfo.ClassifyNameFirst, reportInfo.ReportLayout)
 			go Report2pdfAndJpeg(reportPdfUrl, reportId, 1)
 		}
 

+ 8 - 3
services/report_v2.go

@@ -1119,7 +1119,7 @@ func PublishReport(reportId int, reportUrl string, sysUser *system.Admin) (tips
 
 	// 生成报告pdf和长图
 	{
-		reportPdfUrl := GetGeneralPdfUrl(reportInfo.ReportCode, reportInfo.ReportLayout)
+		reportPdfUrl := GetGeneralPdfUrl(reportInfo.ReportCode, reportInfo.ClassifyNameFirst, reportInfo.ReportLayout)
 		go Report2pdfAndJpeg(reportPdfUrl, reportId, 1)
 	}
 
@@ -1246,7 +1246,7 @@ func PublishChapterReport(reportInfo *models.Report, reportUrl string, sysUser *
 
 	// 生成报告pdf和长图
 	{
-		reportPdfUrl := GetGeneralPdfUrl(reportInfo.ReportCode, reportInfo.ReportLayout)
+		reportPdfUrl := GetGeneralPdfUrl(reportInfo.ReportCode, reportInfo.ClassifyNameFirst, reportInfo.ReportLayout)
 		go Report2pdfAndJpeg(reportPdfUrl, reportId, 1)
 	}
 
@@ -1428,7 +1428,12 @@ func UpdateReportVideo(reportInfo *models.Report) {
 // @param reportCode string
 // @param reportLayout int8
 // @return pdfUrl string
-func GetGeneralPdfUrl(reportCode string, reportLayout int8) (pdfUrl string) {
+func GetGeneralPdfUrl(reportCode, classifyFirstName string, reportLayout int8) (pdfUrl string) {
+	// 如果是弘则,且是晨、周报,那么就不返回
+	if utils.InArrayByStr([]string{utils.BusinessCodeRelease, utils.BusinessCodeSandbox, utils.BusinessCodeDebug}, utils.BusinessCode) && utils.InArrayByStr([]string{"晨报", "周报"}, classifyFirstName) {
+		return
+	}
+
 	conf, e := models.GetBusinessConfByKey("ReportViewUrl")
 	if e != nil {
 		return

+ 101 - 105
services/smart_report.go

@@ -156,11 +156,11 @@ async def main():
     })
     await page.goto('%s', {
         'waitUntil': 'networkidle0',
-        'timeout': 1000000  # 设置超时时间为 100 秒
+        'timeout': 3000000  # 设置超时时间为 100 秒
     })
 
     # 在生成PDF之前等待2秒
-    await asyncio.sleep(5)
+    await asyncio.sleep(10)
 
     await page.pdf({
         'path': "%s",
@@ -229,7 +229,7 @@ async def main():
         # 导航到页面
         await page.goto('%s', {
             'waitUntil': 'networkidle0',
-            'timeout': 1000000  # 设置超时时间为 100 秒
+            'timeout': 3000000  # 设置超时时间为 100 秒
         })
         # Customizing footer for page numbers starting from page 2
 
@@ -239,7 +239,7 @@ async def main():
         await page.screenshot({
             'path': "%s",
             'fullPage': True,
-			'quality':100
+			'quality':80
         })
         
     except errors.BrowserError as e:
@@ -327,127 +327,123 @@ func Report2pdfAndJpeg(reportUrl string, reportId, reportType int) {
 	reportCode := utils.MD5(strconv.Itoa(reportId))
 
 	pdfPath := `./static/` + reportCode + ".pdf"
-	jpegPath := `./static/` + reportCode + ".jpeg"
+	jpegPath := `./static/` + reportCode + ".jpg"
 
-	go func() {
-		err := ReportToPdf(reportUrl, pdfPath)
-		if err != nil {
-			utils.FileLog.Info("ReportToPdf failed: , error: \n" + err.Error())
-			go alarm_msg.SendAlarmMsg("ReportToPdf failed:"+err.Error(), 3)
-		}
-
-		file, err := os.Open(pdfPath)
-		if err != nil {
-			utils.FileLog.Info("Open failed: , error: \n" + err.Error())
-			go alarm_msg.SendAlarmMsg("Open failed:"+err.Error(), 3)
-			return
-		}
+	err = ReportToPdf(reportUrl, pdfPath)
+	if err != nil {
+		utils.FileLog.Info("ReportToPdf failed: , error: \n" + err.Error())
+		go alarm_msg.SendAlarmMsg("ReportToPdf failed:"+err.Error(), 3)
+	}
 
-		ext := path.Ext(file.Name())
+	file, err := os.Open(pdfPath)
+	if err != nil {
+		utils.FileLog.Info("Open failed: , error: \n" + err.Error())
+		go alarm_msg.SendAlarmMsg("Open failed:"+err.Error(), 3)
+		return
+	}
 
-		randStr := utils.GetRandStringNoSpecialChar(28)
-		fileName := randStr + ext
-		defer file.Close() //关闭上传文件
+	ext := path.Ext(file.Name())
 
-		resourceUrl := ``
-		ossClient := NewOssClient()
-		if ossClient == nil {
-			utils.FileLog.Info("初始化OSS服务失败")
-			return
-		}
-		resourceUrl, err = ossClient.UploadFile(fileName, pdfPath, "")
-		if err != nil {
-			utils.FileLog.Info("文件上传失败, Err: \n" + err.Error())
-			go alarm_msg.SendAlarmMsg("文件上传失败:"+err.Error(), 3)
-			return
-		}
-		defer func() {
-			_ = os.Remove(pdfPath)
-		}()
-
-		if reportType == 3 {
-			// 更新pdf url
-			ob := new(smart_report.SmartReport)
-			ob.SmartReportId = reportId
-			ob.DetailPdfUrl = resourceUrl
-			if err = ob.Update([]string{"DetailPdfUrl"}); err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
-				return
-			}
-		} else if reportType == 2 {
-			err = models.ModifyEnglishReportPdfUrl(reportId, resourceUrl)
-			if err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
-				return
-			}
-		} else if reportType == 1 {
-			err = models.ModifyReportPdfUrl(reportId, resourceUrl)
-			if err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
-				return
-			}
-		}
+	randStr := utils.GetRandStringNoSpecialChar(28)
+	fileName := randStr + ext
+	defer file.Close() //关闭上传文件
 
+	resourceUrl := ``
+	ossClient := NewOssClient()
+	if ossClient == nil {
+		utils.FileLog.Info("初始化OSS服务失败")
+		return
+	}
+	resourceUrl, err = ossClient.UploadFile(fileName, pdfPath, "")
+	if err != nil {
+		utils.FileLog.Info("文件上传失败, Err: \n" + err.Error())
+		go alarm_msg.SendAlarmMsg("文件上传失败:"+err.Error(), 3)
+		return
+	}
+	defer func() {
+		_ = os.Remove(pdfPath)
 	}()
 
-	go func() {
-		width := 1200
-		if reportType == 3 {
-			width = 800
+	if reportType == 3 {
+		// 更新pdf url
+		ob := new(smart_report.SmartReport)
+		ob.SmartReportId = reportId
+		ob.DetailPdfUrl = resourceUrl
+		if err = ob.Update([]string{"DetailPdfUrl"}); err != nil {
+			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+			return
 		}
-		err := ReportToJpeg(width, reportUrl, jpegPath)
+	} else if reportType == 2 {
+		err = models.ModifyEnglishReportPdfUrl(reportId, resourceUrl)
 		if err != nil {
-			utils.FileLog.Info("ReportToJpeg failed: , error: \n" + err.Error())
+			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+			return
 		}
-		file, err := os.Open(jpegPath)
+	} else if reportType == 1 {
+		err = models.ModifyReportPdfUrl(reportId, resourceUrl)
 		if err != nil {
-			utils.FileLog.Info("open file failed: , error: \n" + err.Error())
+			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
 			return
 		}
+	}
 
-		ext := path.Ext(file.Name())
+	time.Sleep(1 * time.Minute)
 
-		randStr := utils.GetRandStringNoSpecialChar(28)
-		fileName := randStr + ext
-		defer file.Close() //关闭上传文件
+	width := 1200
+	if reportType == 3 {
+		width = 800
+	}
+	err = ReportToJpeg(width, reportUrl, jpegPath)
+	if err != nil {
+		utils.FileLog.Info("ReportToJpeg failed: , error: \n" + err.Error())
+	}
+	file, err = os.Open(jpegPath)
+	if err != nil {
+		utils.FileLog.Info("open file failed: , error: \n" + err.Error())
+		return
+	}
+
+	ext = path.Ext(file.Name())
+
+	randStr = utils.GetRandStringNoSpecialChar(28)
+	fileName = randStr + ext
+	defer file.Close() //关闭上传文件
+
+	resourceUrl = ``
+	ossClient = NewOssClient()
+	if ossClient == nil {
+		utils.FileLog.Info("初始化OSS服务失败")
+		return
+	}
+	resourceUrl, err = ossClient.UploadFile(fileName, jpegPath, "")
+	if err != nil {
+		utils.FileLog.Info("文件上传失败, Err: \n" + err.Error())
+		return
+	}
+	defer func() {
+		_ = os.Remove(jpegPath)
+	}()
 
-		resourceUrl := ``
-		ossClient := NewOssClient()
-		if ossClient == nil {
-			utils.FileLog.Info("初始化OSS服务失败")
+	if reportType == 3 {
+		// 更新jpeg url
+		ob := new(smart_report.SmartReport)
+		ob.SmartReportId = reportId
+		ob.DetailImgUrl = resourceUrl
+		if err = ob.Update([]string{"DetailImgUrl"}); err != nil {
+			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
 			return
 		}
-		resourceUrl, err = ossClient.UploadFile(fileName, jpegPath, "")
+	} else if reportType == 2 {
+		err = models.ModifyEnglishReportImgUrl(reportId, resourceUrl)
 		if err != nil {
-			utils.FileLog.Info("文件上传失败, Err: \n" + err.Error())
+			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
 			return
 		}
-		defer func() {
-			_ = os.Remove(jpegPath)
-		}()
-
-		if reportType == 3 {
-			// 更新jpeg url
-			ob := new(smart_report.SmartReport)
-			ob.SmartReportId = reportId
-			ob.DetailImgUrl = resourceUrl
-			if err = ob.Update([]string{"DetailImgUrl"}); err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
-				return
-			}
-		} else if reportType == 2 {
-			err = models.ModifyEnglishReportImgUrl(reportId, resourceUrl)
-			if err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
-				return
-			}
-		} else if reportType == 1 {
-			err = models.ModifyReportImgUrl(reportId, resourceUrl)
-			if err != nil {
-				utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
-				return
-			}
+	} else if reportType == 1 {
+		err = models.ModifyReportImgUrl(reportId, resourceUrl)
+		if err != nil {
+			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
+			return
 		}
-
-	}()
+	}
 }

+ 1 - 1
services/target.go

@@ -41,7 +41,7 @@ func AddEdbItem(secName, unit, frequency string, classifyId int, sysUser *system
 		errMsg = "指标名称已存在"
 		return
 	}
-	err = models.AddEdbinfo(maxTradeCode, secName, unit, "手动", frequency, "", classifyId, sysUser.AdminId)
+	err = models.AddEdbinfo(maxTradeCode, secName, unit, "手动", frequency, "", classifyId, sysUser.AdminId, sysUser.RealName)
 	if err != nil {
 		errMsg = "新增失败,Err:" + err.Error()
 		return

BIN
static/template/导入模板1.xlsx


BIN
static/template/导入模板2.xlsx


+ 38 - 4
utils/common.go

@@ -12,10 +12,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/PuerkitoBio/goquery"
-	"github.com/microcosm-cc/bluemonday"
-	"github.com/shopspring/decimal"
-	xhtml "golang.org/x/net/html"
 	"html"
 	"image"
 	"image/png"
@@ -34,6 +30,11 @@ import (
 	"strings"
 	"time"
 	"unicode"
+
+	"github.com/PuerkitoBio/goquery"
+	"github.com/microcosm-cc/bluemonday"
+	"github.com/shopspring/decimal"
+	xhtml "golang.org/x/net/html"
 )
 
 // 随机数种子
@@ -622,6 +623,20 @@ func GetMaxTradeCode(tradeCode string) (maxTradeCode string, err error) {
 	return
 }
 
+func GetTradeCodeList(tradeCode string, num int) (tradeCodeList []string, err error) {
+	tradeCode = strings.Replace(tradeCode, "W", "", -1)
+	tradeCode = strings.Trim(tradeCode, " ")
+	tradeCodeInt, err := strconv.Atoi(tradeCode)
+	if err != nil {
+		return
+	}
+	for i := 0; i < num; i++ {
+		tradeCodeInt = tradeCodeInt + 1
+		tradeCodeList = append(tradeCodeList, fmt.Sprintf("W%06d", tradeCodeInt))
+	}
+	return
+}
+
 // excel日期字段格式化 yyyy-mm-dd
 func ConvertToFormatDay(excelDaysString string) string {
 	// 2006-01-02 距离 1900-01-01的天数
@@ -2468,3 +2483,22 @@ func DateConvMysqlConvMongo(dateCon string) string {
 	}
 	return cond
 }
+
+func GetDuration(filePath string) (duration string, err error) {
+	// 构建 FFmpeg 命令,使用 ffprobe 来获取媒体文件信息
+	cmd := exec.Command("ffprobe", "-i", filePath, "-show_entries", "format=duration", "-v", "quiet", "-of", "csv=p=0")
+	var out bytes.Buffer
+	cmd.Stdout = &out
+
+	// 执行命令并捕获输出
+	err = cmd.Run()
+	if err != nil {
+		return "", err
+	}
+
+	// 使用正则表达式匹配输出中的时长信息
+	re := regexp.MustCompile(`\d+\.\d+`)
+	duration = re.FindString(out.String())
+
+	return duration, nil
+}

+ 4 - 0
utils/constants.go

@@ -315,6 +315,7 @@ const (
 	CHART_TYPE_BAR             = 7  //柱形图
 	CHART_TYPE_SECTION_SCATTER = 10 //截面散点图样式
 	CHART_TYPE_RADAR           = 11 //雷达图
+	CHART_TYPE_SECTION_COMBINE = 14 //截面组合图
 )
 
 // 指标类型
@@ -467,3 +468,6 @@ const (
 	EdbBaseCalculateRjz                  = 16 // 日均值->16
 
 )
+
+// MultiAddNum 批量插入的数据量
+const MultiAddNum = 500