Quellcode durchsuchen

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

zqbao vor 7 Monaten
Ursprung
Commit
8ac30c8a8b

+ 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
+}

+ 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 {

+ 6 - 0
controllers/data_manage/multiple_graph_config.go

@@ -634,6 +634,12 @@ func (this *ChartInfoController) MultipleGraphConfigSaveChart() {
 		_ = utils.Rc.Delete(cacheKey)
 	}()
 
+	if req.MultipleGraphConfigId <= 0 {
+		br.Msg = "配置未保存"
+		br.IsSendEmail = false
+		return
+	}
+
 	if req.IsSaveAs {
 		CopyMultipleGraphConfigSaveChart(req, this, br, sysUser)
 		return

+ 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)
 	}
 

+ 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")

+ 48 - 1
models/data_manage/chart_info.go

@@ -263,6 +263,11 @@ type EditChartInfoReq struct {
 	UnitEn               string                  `description:"英文单位名称"`
 }
 
+type EditFutureGoodChartInfoReq struct {
+	EditChartInfoReq
+	BarChartInfo FutureGoodBarChartInfoReq
+}
+
 type MarkersLine struct {
 	Axis             int             `json:"axis" description:"1左轴 2右轴 3横轴"`
 	AxisName         string          `json:"axisName" description:"轴的名称,例如'左轴'"`
@@ -757,6 +762,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 +793,7 @@ type ExcelChartEdbView struct {
 type XData struct {
 	Name   string `description:"别名"`
 	NameEn string `description:"英文别名"`
+	IsHide int    `description:"是否隐藏,0不隐藏,1隐藏"`
 }
 
 // YData 柱方图的y轴数据
@@ -1134,7 +1151,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 +1230,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 = ? `
@@ -1467,6 +1489,11 @@ type AddChartInfoReq struct {
 	UnitEn               string                  `description:"英文单位名称"`
 }
 
+type AddFutureGoodChartInfoReq struct {
+	AddChartInfoReq
+	BarChartInfo FutureGoodBarChartInfoReq
+}
+
 type PreviewChartInfoReq struct {
 	ChartEdbInfoList  []*ChartSaveItem `description:"指标及配置信息"`
 	DateType          int              `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今 20:最近N年"`
@@ -1798,6 +1825,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=? `
@@ -2101,6 +2140,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 +2158,7 @@ type BarChartInfoEdbItemReq struct {
 	ConvertValue  float64 `description:"数据转换值"`
 	ConvertUnit   string  `description:"数据转换单位"`
 	ConvertEnUnit string  `description:"数据转换单位"`
+	IsHide        int     `description:"是否隐藏该项,0不隐藏,1隐藏"`
 }
 
 // BarChartInfoDateReq 柱方图预览请求数据(日期相关)

+ 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:"别名"`
+}

+ 56 - 2
models/data_manage/edb_info.go

@@ -930,9 +930,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)

+ 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

+ 4 - 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"
@@ -534,6 +535,9 @@ func initAi() {
 		new(aimod.AiChatTopic),
 		new(aimod.AiChat),
 		new(aimod.FileUploadRecord),
+		new(ai_summary.AiSummaryClassify),
+		new(ai_summary.AiSummary),
+		new(ai_summary.AiPrompt),
 	)
 }
 

+ 171 - 0
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",

+ 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
+}

+ 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 {

+ 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
 		}
-
-	}()
+	}
 }