Ver Fonte

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

zqbao há 8 meses atrás
pai
commit
51fa7ed1ea
70 ficheiros alterados com 8781 adições e 364 exclusões
  1. 22 0
      cache/replace_edb_info.go
  2. 1 0
      controllers/commodity_trade_base_index.go
  3. 599 0
      controllers/data_manage/ccf_data.go
  4. 60 0
      controllers/data_manage/chart_common.go
  5. 41 4
      controllers/data_manage/chart_info.go
  6. 7 7
      controllers/data_manage/data_manage_permission/data_manage_permission.go
  7. 4 4
      controllers/data_manage/data_manage_permission/data_move.go
  8. 9 2
      controllers/data_manage/edb_classify.go
  9. 71 40
      controllers/data_manage/edb_info.go
  10. 1731 0
      controllers/data_manage/excel/balance_table.go
  11. 40 6
      controllers/data_manage/excel/excel_classify.go
  12. 368 82
      controllers/data_manage/excel/excel_info.go
  13. 2 0
      controllers/data_manage/my_chart.go
  14. 3 2
      controllers/data_manage/predict_edb_classify.go
  15. 75 9
      controllers/ppt_english.go
  16. 68 2
      controllers/ppt_v2.go
  17. 28 6
      controllers/sandbox/sandbox.go
  18. 144 0
      models/data_manage/base_from_ccf.go
  19. 55 0
      models/data_manage/base_from_ccf_classify.go
  20. 3 3
      models/data_manage/base_from_trade_index.go
  21. 155 0
      models/data_manage/ccf_stock_classify.go
  22. 171 0
      models/data_manage/ccf_stock_excel.go
  23. 9 0
      models/data_manage/chart_edb_mapping.go
  24. 25 4
      models/data_manage/chart_info.go
  25. 15 0
      models/data_manage/chart_theme/chart_theme_type.go
  26. 16 0
      models/data_manage/cross_variety/chart_info_cross_variety.go
  27. 11 0
      models/data_manage/cross_variety/chart_tag_variety.go
  28. 13 3
      models/data_manage/edb_classify.go
  29. 138 1
      models/data_manage/edb_info.go
  30. 126 0
      models/data_manage/excel/excel_chart_data.go
  31. 455 0
      models/data_manage/excel/excel_chart_edb.go
  32. 32 2
      models/data_manage/excel/excel_edb_mapping.go
  33. 223 44
      models/data_manage/excel/excel_info.go
  34. 82 0
      models/data_manage/excel/excel_worker.go
  35. 77 0
      models/data_manage/excel/request/balance_table.go
  36. 13 1
      models/data_manage/excel/request/excel_info.go
  37. 40 0
      models/data_manage/excel/request/time_table.go
  38. 44 30
      models/data_manage/excel/response/excel_info.go
  39. 17 17
      models/data_manage/excel/response/sheet.go
  40. 126 0
      models/data_manage/multiple_graph_config.go
  41. 136 0
      models/data_manage/multiple_graph_config_chart_mapping.go
  42. 13 2
      models/data_manage/my_chart.go
  43. 2 2
      models/data_manage/trade_analysis/trade_analysis.go
  44. 3 0
      models/db.go
  45. 3 1
      models/ppt_english/ppt_english.go
  46. 27 1
      models/ppt_english/ppt_english_group_mapping.go
  47. 4 1
      models/ppt_v2.go
  48. 19 1
      models/ppt_v2_group_mapping.go
  49. 2 2
      models/report.go
  50. 26 0
      models/sandbox/sandbox.go
  51. 12 5
      models/sandbox/sandbox_classify.go
  52. 180 0
      routers/commentsRouter.go
  53. 135 9
      services/data/chart_info.go
  54. 1756 0
      services/data/chart_info_excel_balance.go
  55. 46 0
      services/data/chart_info_interface.go
  56. 4 0
      services/data/data_manage_permission/data_move.go
  57. 7 1
      services/data/data_manage_permission/edb_permission.go
  58. 27 0
      services/data/data_manage_permission/excel.go
  59. 88 16
      services/data/edb_classify.go
  60. 30 18
      services/data/edb_info.go
  61. 105 0
      services/data/edb_refresh.go
  62. 583 0
      services/data/excel/balance_table.go
  63. 50 22
      services/data/excel/excel_info.go
  64. 50 10
      services/data/excel/excel_op.go
  65. 254 0
      services/edb_info_replace.go
  66. 18 1
      services/ppt/ppt_group.go
  67. 65 0
      services/sandbox/sandbox.go
  68. 5 2
      services/task.go
  69. 8 1
      static/ErrMsgConfig.json
  70. 4 0
      utils/constants.go

+ 22 - 0
cache/replace_edb_info.go

@@ -0,0 +1,22 @@
+package cache
+
+import (
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/utils"
+	"fmt"
+)
+
+// 将替换指标操作加入到队列中
+func AddReplaceEdbInfo(oldEdbInfo, newEdbInfo *data_manage.EdbInfo) bool {
+	record := new(data_manage.ReplaceEdbInfoItem)
+	record.OldEdbInfo = oldEdbInfo
+	record.NewEdbInfo = newEdbInfo
+	if utils.Re == nil {
+		err := utils.Rc.LPush(utils.CACHE_KEY_REPLACE_EDB, record)
+		if err != nil {
+			fmt.Println("AddReplaceEdbInfo LPush Err:" + err.Error())
+		}
+		return true
+	}
+	return false
+}

+ 1 - 0
controllers/commodity_trade_base_index.go

@@ -74,6 +74,7 @@ func (this *TradeCommonController) ExchangeClassify() {
 	latestData, err := data_manage.GetLatestDate(exchange)
 	if err != nil {
 		br.Msg = "获取失败"
+		br.ErrMsg = "获取最新日期失败,Err:" + err.Error()
 		return
 	}
 

+ 599 - 0
controllers/data_manage/ccf_data.go

@@ -0,0 +1,599 @@
+package data_manage
+
+import (
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+// CCFClassify
+// @Title CCF化纤分类
+// @Description CCF化纤分类接口
+// @Success 200 {object} data_manage.BaseFromCCFClassifyItem
+// @router /ccf/classify [get]
+func (this *EdbInfoController) CCFClassify() {
+	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
+	}
+
+	classifies, e := data_manage.GetBaseFromCCFClassify()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取CCF化纤分类失败, Err: " + e.Error()
+		return
+	}
+
+	classifyOb := new(data_manage.BaseFromCCFClassify)
+	resp := make([]*data_manage.BaseFromCCFClassifyItem, 0)
+	parentMap := make(map[int][]*data_manage.BaseFromCCFClassifyItem)
+	for _, v := range classifies {
+		t := classifyOb.Format2Item(v)
+		if v.ParentId == 0 {
+			resp = append(resp, t)
+		}
+		if v.ParentId > 0 {
+			if parentMap[v.ParentId] == nil {
+				parentMap[v.ParentId] = make([]*data_manage.BaseFromCCFClassifyItem, 0)
+			}
+			parentMap[v.ParentId] = append(parentMap[v.ParentId], t)
+		}
+	}
+	for _, v := range resp {
+		v.Child = parentMap[v.ClassifyId]
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// CCFIndexData
+// @Title 获取CCF化纤数据
+// @Description 获取CCF化纤接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ClassifyId   query   string  true       "分类id"
+// @Success 200 {object} data_manage.LzFrequency
+// @router /ccf/index/data [get]
+func (this *EdbInfoController) CCFIndexData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	classifyId, _ := this.GetInt("ClassifyId")
+	if classifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+
+	// 获取指标
+	var condition string
+	var pars []interface{}
+	if classifyId >= 0 {
+		condition += ` AND classify_id=? `
+		pars = append(pars, classifyId)
+	}
+
+	indexes, err := data_manage.GetCCFIndex(condition, pars)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	indexCodes := make([]string, 0)
+	for _, v := range indexes {
+		indexCodes = append(indexCodes, v.IndexCode)
+	}
+	indexCounts, e := data_manage.GetCCFIndexDataCountGroup(indexCodes)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取指标数据总量失败, Err:" + err.Error()
+		return
+	}
+	countMap := make(map[string]int)
+	for _, v := range indexCounts {
+		countMap[v.IndexCode] = v.Count
+	}
+
+	resultList := make([]*data_manage.BaseFromCCFIndexList, 0)
+	for _, v := range indexes {
+		product := new(data_manage.BaseFromCCFIndexList)
+		product.BaseFromCcfIndexId = v.BaseFromCcfIndexId
+		product.ClassifyId = v.ClassifyId
+		product.Unit = v.Unit
+		product.IndexCode = v.IndexCode
+		product.IndexName = v.IndexName
+		product.Frequency = v.Frequency
+		product.CreateTime = v.CreateTime
+		product.ModifyTime = v.ModifyTime
+
+		total := countMap[v.IndexCode]
+		page := paging.GetPaging(currentIndex, pageSize, total)
+		dataList, e := data_manage.GetCCFIndexData(v.IndexCode, startSize, pageSize)
+		if e != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + e.Error()
+			return
+		}
+		if dataList == nil {
+			dataList = make([]*data_manage.BaseFromCCFData, 0)
+		}
+		product.DataList = dataList
+		product.Paging = page
+		resultList = append(resultList, product)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resultList
+}
+
+// CCFSearchList
+// @Title CCF模糊搜索
+// @Description CCF模糊搜索
+// @Param   Keyword   query   string  ture       "关键字搜索"
+// @Success 200 {object} data_manage.BaseFromCCFIndexSearchItem
+// @router /ccf/search_list [get]
+func (this *EdbInfoController) CCFSearchList() {
+	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
+	}
+
+	list := make([]*data_manage.BaseFromCCFIndexSearchItem, 0)
+	var err error
+	// 关键字
+	keyword := this.GetString("Keyword")
+	if keyword != "" {
+		keyWordArr := strings.Split(keyword, " ")
+
+		if len(keyWordArr) > 0 {
+			condition := ""
+			for _, v := range keyWordArr {
+				condition += ` AND CONCAT(index_name,index_code) LIKE '%` + v + `%'`
+			}
+			list, err = data_manage.GetCCFItemList(condition)
+			if err != nil {
+				br.ErrMsg = "获取失败,Err:" + err.Error()
+				br.Msg = "获取失败"
+				return
+			}
+		}
+	} else {
+		list, err = data_manage.GetCCFItemList("")
+		if err != nil {
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			br.Msg = "获取失败"
+			return
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// CCFSingleData
+// @Title 获取CCF数据
+// @Description 获取CCF单条数据接口
+// @Param   IndexCode   query   string  true       "指标唯一编码"
+// @Success 200 {object} models.BaseResponse
+// @router /ccf/single_data [get]
+func (this *EdbInfoController) CCFSingleData() {
+	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
+	}
+	indexCode := this.GetString("IndexCode")
+	indexInfo, err := data_manage.GetBaseFromCCFIndexByIndexCode(indexCode)
+	if err != nil {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+		return
+	}
+	dataTmpList, err := data_manage.GetCCFIndexDataByCode(indexCode)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	var ret data_manage.CCFSingleDataResp
+	var dataList []*data_manage.CCFSingleData
+
+	ret.ClassifyId = indexInfo.ClassifyId
+	ret.BaseFromCcfIndexId = indexInfo.BaseFromCcfIndexId
+	ret.IndexCode = indexInfo.IndexCode
+	ret.IndexName = indexInfo.IndexName
+	ret.Frequency = indexInfo.Frequency
+	ret.CreateTime = indexInfo.CreateTime.Format(utils.FormatDateTime)
+	ret.ModifyTime = indexInfo.ModifyTime.Format(utils.FormatDateTime)
+	ret.Unit = indexInfo.Unit
+	for _, v := range dataTmpList {
+		tmp := &data_manage.CCFSingleData{
+			Value:    v.Value,
+			DataTime: v.DataTime,
+		}
+		dataList = append(dataList, tmp)
+	}
+	ret.Data = dataList
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = ret
+}
+
+// ExportCCFList
+// @Title 导出CCF数据
+// @Description 导出CCF数据
+// @Param   IndexName   query   string  false       "名称关键词"
+// @Param   IndexCode   query   string  false       "指标唯一编码"
+// @Param   ClassifyId   query   string  true       "分类"
+// @Success 200  导出成功
+// @router /ccf/export [get]
+func (this *EdbInfoController) ExportCCFList() {
+	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 = "请重新登录"
+		return
+	}
+
+	indexCode := this.GetString("IndexCode") //指标唯一编码
+	classifyId, _ := this.GetInt("ClassifyId")
+
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+
+	downFile := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+
+	var pars []interface{}
+	condition := ""
+	if classifyId > 0 {
+		//获取指标
+		condition += " AND classify_id=?"
+		pars = append(pars, classifyId)
+	}
+	if indexCode != "" {
+		//获取指标
+		condition += " AND index_code=?"
+		pars = append(pars, indexCode)
+	}
+	indexList, err := data_manage.GetCCFIndex(condition, pars)
+	if err != nil {
+		fmt.Println("获取数据失败,Err:" + err.Error())
+		return
+	}
+	if len(indexList) <= 0 {
+		fmt.Println("indexList 为空")
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "success"
+		return
+	}
+	sheetNew := new(xlsx.Sheet)
+	sheetNew, err = xlsxFile.AddSheet("CCF化纤")
+
+	//sheetNew.SetColWidth()
+	//获取指标数据
+	windRow := sheetNew.AddRow()
+	secNameRow := sheetNew.AddRow()
+	indexCodeRow := sheetNew.AddRow()
+	frequencyRow := sheetNew.AddRow()
+	unitRow := sheetNew.AddRow()
+	lastModifyDateRow := sheetNew.AddRow()
+	//获取分类下指标最大数据量
+	var dataMax int
+	setRowIndex := 6
+	indexCodeList := make([]string, 0)
+	for _, v := range indexList {
+		indexCodeList = append(indexCodeList, v.IndexCode)
+	}
+	dataListMap := make(map[string][]*data_manage.BaseFromCCFData)
+	if len(indexList) > 0 {
+		allDataList, e := data_manage.GetCCFIndexDataByCodes(indexCodeList)
+		if e != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取数据失败,Err:" + e.Error()
+			return
+		}
+		for _, v := range allDataList {
+			dataListMap[v.IndexCode] = append(dataListMap[v.IndexCode], v)
+		}
+		for _, v := range dataListMap {
+			if len(v) > dataMax {
+				dataMax = len(v)
+			}
+		}
+	}
+
+	for k, sv := range indexList {
+		//获取数据
+		dataList, ok := dataListMap[sv.IndexCode]
+		if !ok {
+			continue
+		}
+		if len(dataList) > 0 {
+			windRow.AddCell().SetValue("CCF化纤")
+			secNameRow.AddCell().SetValue("指标名称")
+			indexCodeRow.AddCell().SetValue("指标ID")
+			frequencyRow.AddCell().SetValue("频率")
+			unitRow.AddCell().SetValue("单位")
+			lastModifyDateRow.AddCell().SetValue("更新时间")
+
+			secNameRow.AddCell().SetValue(sv.IndexName)
+			indexCodeRow.AddCell().SetValue(sv.IndexCode)
+			frequencyRow.AddCell().SetValue(sv.Frequency)
+
+			unitRow.AddCell().SetValue(sv.Unit)
+			lastModifyDateRow.AddCell().SetValue(sv.ModifyTime)
+
+			windRow.AddCell()
+			windRow.AddCell()
+			secNameRow.AddCell()
+			indexCodeRow.AddCell()
+			frequencyRow.AddCell()
+			unitRow.AddCell()
+			lastModifyDateRow.AddCell()
+			minCol := k * 3
+			sheetNew.SetColWidth(minCol, minCol, 15)
+
+			if len(dataList) <= 0 {
+				for n := 0; n < dataMax; n++ {
+					rowIndex := setRowIndex + n
+					row := sheetNew.Row(rowIndex)
+					row.AddCell()
+					row.AddCell()
+					row.AddCell()
+				}
+			} else {
+				endRowIndex := 0
+				for rk, dv := range dataList {
+					rowIndex := setRowIndex + rk
+					row := sheetNew.Row(rowIndex)
+					displayDate, _ := time.Parse(utils.FormatDate, dv.DataTime)
+					displayDateCell := row.AddCell()
+					style := new(xlsx.Style)
+					style.ApplyAlignment = true
+					style.Alignment.WrapText = true
+					displayDateCell.SetStyle(style)
+					displayDateCell.SetDate(displayDate)
+
+					row.AddCell().SetValue(dv.Value)
+					row.AddCell()
+					endRowIndex = rowIndex
+				}
+				if len(dataList) < dataMax {
+					dataLen := dataMax - len(dataList)
+					for n := 0; n < dataLen; n++ {
+						rowIndex := (endRowIndex + 1) + n
+						row := sheetNew.Row(rowIndex)
+						row.AddCell()
+						row.AddCell()
+						row.AddCell()
+					}
+				}
+			}
+		}
+	}
+
+	err = xlsxFile.Save(downFile)
+	if err != nil {
+		//有指标无数据时先导出一遍空表
+		sheet, err := xlsxFile.AddSheet("无数据")
+		if err != nil {
+			br.Msg = "新增Sheet失败"
+			br.ErrMsg = "新增Sheet失败,Err:" + err.Error()
+			return
+		}
+		rowSecName := sheet.AddRow()
+		celSecName := rowSecName.AddCell()
+		celSecName.SetValue("")
+		err = xlsxFile.Save(downFile)
+		if err != nil {
+			br.Msg = "保存文件失败"
+			br.ErrMsg = "保存文件失败"
+			return
+		}
+	}
+	fileName := `CCF化纤信息`
+	//if len(indexList) > 0 {
+	//	fileName = indexList[0].IndexName
+	//}
+	fileName += time.Now().Format("06.01.02") + `.xlsx` //文件名称
+	this.Ctx.Output.Download(downFile, fileName)
+	defer func() {
+		os.Remove(downFile)
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "success"
+
+}
+
+// CCFStockClassify
+// @Title CCF化纤信息-装置分类
+// @Description CCF化纤信息-装置分类
+// @Success 200 {object} data_manage.CCFStockClassify
+// @router /ccf/stock/classify [get]
+func (this *EdbInfoController) CCFStockClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	classifyOb := new(data_manage.CCFStockClassify)
+	items, e := classifyOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "ccf_stock_classify_id ASC")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取CCF化纤分类列表失败, Err: " + e.Error()
+		return
+	}
+	resp := make([]*data_manage.CCFStockClassifyItem, 0)
+	for _, v := range items {
+		resp = append(resp, v.Format2Item())
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// CCFStockTable
+// @Title CCF化纤信息-装置表格详情
+// @Description CCF化纤信息-装置表格详情
+// @Param   ClassifyId   query   int  true       "分类ID"
+// @Param   TableDate   query   string  false       "表格日期"
+// @Success 200 {object} data_manage.CCFStockExcel
+// @router /ccf/stock/table [get]
+func (this *EdbInfoController) CCFStockTable() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	classifyId, _ := this.GetInt("ClassifyId", 0)
+	tableDate := this.GetString("TableDate")
+	if tableDate != "" {
+		_, e := time.ParseInLocation(utils.FormatDate, tableDate, time.Local)
+		if e != nil {
+			br.Msg = "日期格式有误"
+			return
+		}
+	}
+
+	excelOb := new(data_manage.CCFStockExcel)
+	cond := ``
+	pars := make([]interface{}, 0)
+	if classifyId > 0 {
+		cond += fmt.Sprintf(` AND %s = ?`, excelOb.Cols().ClassifyId)
+		pars = append(pars, classifyId)
+	}
+	if tableDate != "" {
+		cond += fmt.Sprintf(` AND %s = ?`, excelOb.Cols().ExcelDate)
+		pars = append(pars, tableDate)
+	}
+
+	// 若无tableDate默认取最近的有数据的表格
+	item, e := excelOb.GetItemByCondition(cond, pars, fmt.Sprintf("%s DESC", excelOb.Cols().ExcelDate))
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "该日期暂无数据"
+			br.Data = data_manage.CCFStockExcel{}
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取CCF化纤装置表格失败, Err: " + e.Error()
+		return
+	}
+	resp := item.Format2Item()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 60 - 0
controllers/data_manage/chart_common.go

@@ -1,6 +1,7 @@
 package data_manage
 
 import (
+	"encoding/json"
 	"eta/eta_api/controllers/data_manage/correlation"
 	"eta/eta_api/controllers/data_manage/cross_variety"
 	"eta/eta_api/controllers/data_manage/future_good"
@@ -8,7 +9,12 @@ import (
 	"eta/eta_api/controllers/data_manage/line_feature"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	"eta/eta_api/services/data/excel"
 	"eta/eta_api/utils"
+	"fmt"
+	"time"
 )
 
 // CommonChartInfoDetailFromUniqueCode
@@ -150,9 +156,63 @@ func (this *ChartInfoController) CommonChartInfoDetailFromUniqueCode() {
 		br.Success = true
 		br.Msg = "获取成功"
 		br.Data = resp
+	case utils.CHART_SOURCE_BALANCE_EXCEL:
+		resp, isOk, msg, errMsg := getBalanceChartInfoDetailFromUniqueCode(chartInfo, isCache, sysUser)
+		if !isOk {
+			br.Msg = msg
+			br.ErrMsg = errMsg
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
 	default:
 		br.Msg = "错误的图表"
 		br.ErrMsg = "错误的图表"
 		return
 	}
 }
+
+func getBalanceChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCache bool, sysUser *system.Admin) (resp *data_manage.ChartInfoDetailFromUniqueCodeResp, isOk bool, msg, errMsg string) {
+	resp = new(data_manage.ChartInfoDetailFromUniqueCodeResp)
+	resp, isOk, msg, errMsg = data.CheckBalanceChartCacheAndPermission(chartInfo, isCache, sysUser)
+	if isOk {
+		return
+	}
+	msg = `获取失败`
+	// 相关联指标
+	mappingListTmp, dataListMap, err, errMsg := excel.GetBalanceExcelChartSingle(chartInfo.ChartInfoId, 0, "")
+	if err != nil {
+		errMsg = "获取失败"
+		err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+		return
+	}
+	var chartInfoResp *data_manage.ChartInfoDetailResp
+	chartInfoResp, err, errMsg = data.GetBalanceExcelChartDetail(chartInfo, mappingListTmp, sysUser, dataListMap)
+	if err != nil {
+		msg = "查询图表详情失败"
+		errMsg = "查询图表详情失败,Err:" + err.Error()
+		return
+	}
+	resp = &data_manage.ChartInfoDetailFromUniqueCodeResp{
+		ChartInfo:            chartInfoResp.ChartInfo,
+		Status:               true,
+		EdbInfoList:          chartInfoResp.EdbInfoList,
+		XEdbIdValue:          chartInfoResp.XEdbIdValue,
+		YDataList:            chartInfoResp.YDataList,
+		XDataList:            chartInfoResp.XDataList,
+		BarChartInfo:         chartInfoResp.BarChartInfo,
+		CorrelationChartInfo: chartInfoResp.CorrelationChartInfo,
+		DataResp:             chartInfoResp.DataResp,
+	}
+	// 将数据加入缓存
+	if utils.Re == nil {
+		cacheData, _ := json.Marshal(resp)
+		key := data.GetChartInfoDataKey(chartInfo.ChartInfoId)
+		utils.Rc.Put(key, cacheData, 2*time.Hour)
+	}
+	isOk = true
+
+	return
+}

+ 41 - 4
controllers/data_manage/chart_info.go

@@ -10,6 +10,7 @@ import (
 	"eta/eta_api/services"
 	"eta/eta_api/services/data"
 	"eta/eta_api/services/data/data_manage_permission"
+	"eta/eta_api/services/data/excel"
 	"eta/eta_api/services/eta_forum"
 	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
@@ -1276,7 +1277,27 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 		edbInfoIdList = append(edbInfoIdList, v.EdbInfoId)
 		edbInfoIdMapping[v.EdbInfoId] = v
 	}
-	mappingList, err := data_manage.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList)
+
+	// 区分是否是来自平衡表图表的数据
+	var chartInfoDataShow data.ChartInfoDataShow
+
+	if req.ChartSource == utils.CHART_SOURCE_BALANCE_EXCEL { //来自平衡表图表的数据
+		excelChartInfoDataShow := new(data.ExcelChartInfoDataShow)
+		// 相关联指标
+		mappingListTmp, dataListMap, err, errMsg := excel.GetBalanceExcelChartSingle(chartInfo.ChartInfoId, req.ChartEdbInfoList[0].EdbInfoId, "")
+		if err != nil {
+			br.Msg = errMsg
+			br.ErrMsg = err.Error()
+			return
+		}
+		excelChartInfoDataShow.DataListMap = dataListMap
+		excelChartInfoDataShow.MappingListTmp = mappingListTmp
+		chartInfoDataShow = excelChartInfoDataShow
+	} else {
+		chartInfoDataShow = &data.BaseChartInfoDataShow{}
+	}
+
+	mappingList, err := chartInfoDataShow.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
@@ -1360,7 +1381,7 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 	}
 
 	// 获取图表中的指标数据
-	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(0, req.ChartType, calendar, startDate, endDate, mappingList, extraConfigStr, seasonExtraConfig)
+	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbDataV2(0, req.ChartType, calendar, startDate, endDate, mappingList, extraConfigStr, seasonExtraConfig, chartInfoDataShow)
 	if err != nil {
 		br.Msg = "获取失败"
 		if errMsg != `` {
@@ -2919,6 +2940,14 @@ func (this *EdbInfoController) BatchChartInfoRefresh() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
+	if req.Source == "" {
+		br.Msg = "刷新来源有误"
+		return
+	}
+	if req.PrimaryId <= 0 {
+		br.Msg = "刷新对象有误"
+		return
+	}
 
 	//获取所有的图表列表
 	_, chartInfoList, err := data_manage.GetChartInfoListByUniqueCodeSlice(req.ChartInfoCode)
@@ -2929,7 +2958,7 @@ func (this *EdbInfoController) BatchChartInfoRefresh() {
 		return
 	}
 
-	redisKey := data.GetBatchChartRefreshKey(req.Source, req.ReportId, req.ReportChapterId)
+	redisKey := data.GetBatchChartRefreshKey(req.Source, req.PrimaryId, req.SubId)
 
 	// 图表中的指标刷新
 	err, isAsync := data.BatchChartInfoRefreshV2(chartInfoList, redisKey)
@@ -3441,9 +3470,17 @@ func (this *EdbInfoController) GetBatchChartRefreshResult() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
+	if req.Source == "" {
+		br.Msg = "刷新来源有误"
+		return
+	}
+	if req.PrimaryId <= 0 {
+		br.Msg = "刷新对象有误"
+		return
+	}
 
 	// 刷新结果,默认是刷新完成了
-	refreshResult := data.CheckBatchChartRefreshResult(req.Source, req.ReportId, req.ReportChapterId)
+	refreshResult := data.CheckBatchChartRefreshResult(req.Source, req.PrimaryId, req.SubId)
 
 	resp := response.ChartRefreshResp{
 		RefreshResult: refreshResult,

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

@@ -51,7 +51,7 @@ func (c *DataMangePermissionController) SetEdbChartPermission() {
 	}
 
 	// 子来源(目前作用于ETA表格,2024-3-26 14:12:09)
-	if req.Source == 6 && (req.SubSource <= utils.EXCEL_DEFAULT || req.SubSource > utils.CUSTOM_ANALYSIS_TABLE) {
+	if req.Source == 6 && (req.SubSource <= utils.EXCEL_DEFAULT || req.SubSource > utils.BALANCE_TABLE) {
 		br.Msg = "错误的子来源"
 		br.IsSendEmail = false
 		return
@@ -114,7 +114,7 @@ func (c *DataMangePermissionController) SetPermissionEdbChartClassifyIsPermissio
 	}
 
 	// 子来源(目前作用于ETA表格,2024-3-26 14:12:09)
-	if req.Source == 6 && (req.SubSource <= utils.EXCEL_DEFAULT || req.SubSource > utils.CUSTOM_ANALYSIS_TABLE) {
+	if req.Source == 6 && (req.SubSource <= utils.EXCEL_DEFAULT || req.SubSource > utils.BALANCE_TABLE) {
 		br.Msg = "错误的子来源"
 		br.IsSendEmail = false
 		return
@@ -172,7 +172,7 @@ func (c *DataMangePermissionController) SetEdbChartClassifyPermission() {
 	}
 
 	// 子来源(目前作用于ETA表格,2024-3-26 14:12:09)
-	if req.Source == 6 && (req.SubSource <= utils.EXCEL_DEFAULT || req.SubSource > utils.CUSTOM_ANALYSIS_TABLE) {
+	if req.Source == 6 && (req.SubSource <= utils.EXCEL_DEFAULT || req.SubSource > utils.BALANCE_TABLE) {
 		br.Msg = "错误的子来源"
 		br.IsSendEmail = false
 		return
@@ -231,7 +231,7 @@ func (c *DataMangePermissionController) GetEdbChartPermission() {
 
 	// 子来源(目前作用于ETA表格,2024-3-26 14:12:09)
 	subSource, _ := c.GetInt("SubSource")
-	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.CUSTOM_ANALYSIS_TABLE) {
+	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.BALANCE_TABLE) {
 		br.Msg = "错误的子来源"
 		br.IsSendEmail = false
 		return
@@ -290,7 +290,7 @@ func (c *DataMangePermissionController) GetEdbChartClassifyPermission() {
 
 	// 子来源(目前作用于ETA表格,2024-3-26 14:12:09)
 	subSource, _ := c.GetInt("SubSource")
-	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.CUSTOM_ANALYSIS_TABLE) {
+	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.BALANCE_TABLE) {
 		br.Msg = "错误的子来源"
 		br.IsSendEmail = false
 		return
@@ -306,7 +306,7 @@ func (c *DataMangePermissionController) GetEdbChartClassifyPermission() {
 	}
 
 	// 子来源(目前作用于ETA表格,2024-3-26 14:12:09)
-	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.CUSTOM_ANALYSIS_TABLE) {
+	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.BALANCE_TABLE) {
 		br.Msg = "错误的子来源"
 		br.IsSendEmail = false
 		return
@@ -364,7 +364,7 @@ func (c *DataMangePermissionController) GetEdbChartNoPermission() {
 
 	// 子来源(目前作用于ETA表格,2024-3-26 14:12:09)
 	subSource, _ := c.GetInt("SubSource")
-	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.CUSTOM_ANALYSIS_TABLE) {
+	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.BALANCE_TABLE) {
 		br.Msg = "错误的子来源"
 		br.IsSendEmail = false
 		return

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

@@ -40,7 +40,7 @@ func (c *DataMangePermissionController) EdbChartClassifyList() {
 
 	// 子来源(目前作用于ETA表格,2024-3-26 14:12:09)
 	subSource, _ := c.GetInt("SubSource")
-	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.CUSTOM_ANALYSIS_TABLE) {
+	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.BALANCE_TABLE) {
 		br.Msg = "错误的子来源"
 		br.IsSendEmail = false
 		return
@@ -90,7 +90,7 @@ func (c *DataMangePermissionController) SecretEdbChartClassifyList() {
 
 	// 子来源(目前作用于ETA表格,2024-3-26 14:12:09)
 	subSource, _ := c.GetInt("SubSource")
-	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.CUSTOM_ANALYSIS_TABLE) {
+	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.BALANCE_TABLE) {
 		br.Msg = "错误的子来源"
 		br.IsSendEmail = false
 		return
@@ -163,7 +163,7 @@ func (c *DataMangePermissionController) MoveEdbChartList() {
 
 	// 子来源(目前作用于ETA表格,2024-3-26 14:12:09)
 	subSource, _ := c.GetInt("SubSource")
-	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.CUSTOM_ANALYSIS_TABLE) {
+	if source == 6 && (subSource <= utils.EXCEL_DEFAULT || subSource > utils.BALANCE_TABLE) {
 		br.Msg = "错误的子来源"
 		br.IsSendEmail = false
 		return
@@ -254,7 +254,7 @@ func (c *DataMangePermissionController) MoveEdbChartUser() {
 	}
 
 	// 子来源(目前作用于ETA表格,2024-3-26 14:12:09)
-	if req.Source == 6 && (req.SubSource <= utils.EXCEL_DEFAULT || req.SubSource > utils.CUSTOM_ANALYSIS_TABLE) {
+	if req.Source == 6 && (req.SubSource <= utils.EXCEL_DEFAULT || req.SubSource > utils.BALANCE_TABLE) {
 		br.Msg = "错误的子来源"
 		br.IsSendEmail = false
 		return

+ 9 - 2
controllers/data_manage/edb_classify.go

@@ -356,7 +356,7 @@ func (this *EdbClassifyController) DeleteEdbClassifyCheck() {
 		br.IsSendEmail = false
 		return
 	}
-	deleteStatus, tipsMsg, err, errMsg := data.DeleteCheck(req.ClassifyId, req.EdbInfoId, this.SysUser)
+	deleteStatus, tipsMsg, tableList, err, errMsg := data.DeleteCheck(req.ClassifyId, req.EdbInfoId, this.SysUser)
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg
@@ -367,6 +367,12 @@ func (this *EdbClassifyController) DeleteEdbClassifyCheck() {
 		}
 		return
 	}
+	if this.Lang == "en" {
+		if utils.ViperConfig.InConfig(tipsMsg) {
+			tipsMsg = utils.ViperConfig.GetString(tipsMsg)
+		}
+	}
+
 	//var deleteStatus int
 	//var tipsMsg string
 	////删除分类
@@ -429,6 +435,7 @@ func (this *EdbClassifyController) DeleteEdbClassifyCheck() {
 	resp := new(data_manage.ClassifyDeleteCheckResp)
 	resp.DeleteStatus = deleteStatus
 	resp.TipsMsg = tipsMsg
+	resp.TableList = tableList
 	br.Ret = 200
 	br.Msg = "检测成功"
 	br.Success = true
@@ -467,7 +474,7 @@ func (this *EdbClassifyController) DeleteEdbClassify() {
 		return
 	}
 
-	nextItem, err, errMsg := data.Delete(req.ClassifyId, req.EdbInfoId, sysUser, string(this.Ctx.Input.RequestBody), this.Ctx.Input.URI())
+	nextItem, _, err, errMsg := data.Delete(req.ClassifyId, req.EdbInfoId, sysUser, string(this.Ctx.Input.RequestBody), this.Ctx.Input.URI())
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg

+ 71 - 40
controllers/data_manage/edb_info.go

@@ -2,10 +2,12 @@ package data_manage
 
 import (
 	"encoding/json"
+	"eta/eta_api/cache"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/company"
 	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/data_manage/cross_variety"
 	request2 "eta/eta_api/models/data_manage/excel/request"
 	"eta/eta_api/models/data_manage/request"
 	"eta/eta_api/models/data_manage/response"
@@ -3881,41 +3883,17 @@ func (this *ChartInfoController) EdbInfoReplace() {
 	//replaceChartTotal, replaceCalculateTotal, err := data.EdbInfoReplace(oldEdbInfo, newEdbInfo, sysAdminId, sysUser.RealName)
 	_, _, err = data.EdbInfoReplace(oldEdbInfo, newEdbInfo, sysAdminId, sysUser.RealName)
 	//msgContent := ``
-	isFail := false
-	var errmsg string
 	if err != nil {
-		//msgContent = oldEdbInfo.EdbName + "指标替换" + newEdbInfo.EdbName + "指标,全部替换失败,涉及" + strconv.Itoa(replaceChartTotal) + "张图表,\n\n " + strconv.Itoa(replaceCalculateTotal) + "个计算指标"
-		isFail = true
-		errmsg = "replace err:" + err.Error()
-	} else {
-		//msgContent = oldEdbInfo.EdbName + "指标替换" + newEdbInfo.EdbName + "指标,全部替换成功,涉及" + strconv.Itoa(replaceChartTotal) + "张图表,\n\n" + strconv.Itoa(replaceCalculateTotal) + "个计算指标"
-		isFail = false
-	}
-
-	// 添加站内信息
-	//{
-	//	msgItem := new(company.CompanyApprovalMessage)
-	//	msgItem.CreateUserId = sysAdminId
-	//	msgItem.ReceiveUserId = sysAdminId
-	//	msgItem.MessageStatus = 0
-	//	msgItem.Remark = "指标替换"
-	//	msgItem.OperationStatus = 1
-	//	msgItem.Content = msgContent
-	//	msgItem.CreateTime = time.Now()
-	//	msgItem.ModifyTime = time.Now()
-	//	msgItem.MessageType = 3 //1:申请消息,2:审批结果,3:文字消息
-	//	msgItem.SourceType = 4  //消息来源
-	//	err = company.AddCompanyApprovalMessage(msgItem)
-	//}
-
-	if isFail {
 		br.Msg = "替换失败"
-		br.ErrMsg = "替换失败 err:" + errmsg
-	} else {
-		br.Msg = "替换成功"
-		br.ErrMsg = "替换成功"
-		br.Ret = 200
+		br.ErrMsg = "替换失败 replace err:" + err.Error()
+		return
 	}
+
+	//加入到缓存队列中处理
+	go cache.AddReplaceEdbInfo(oldEdbInfo, newEdbInfo)
+	br.Msg = "替换成功"
+	br.ErrMsg = "替换成功"
+	br.Ret = 200
 	br.IsAddLog = true
 	return
 }
@@ -3970,9 +3948,9 @@ func (this *EdbInfoController) RelationChartList() {
 		return
 	}
 
-	// 关联指标
+	/*// 关联指标
 	condition += ` AND b.edb_info_id = ? `
-	pars = append(pars, edbInfoId)
+	pars = append(pars, edbInfoId)*/
 
 	//只看我的
 	isShowMe, _ := this.GetBool("IsShowMe")
@@ -3981,8 +3959,47 @@ func (this *EdbInfoController) RelationChartList() {
 		pars = append(pars, sysUser.AdminId)
 	}
 
+	chartIds := make([]int, 0)
+	chartIdMap := make(map[int]bool)
+	// 查询指标绑定的图表
+	edbListTemp, err := data_manage.GetEdbMappingListByEdbInfoId(edbInfoId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取图表信息失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range edbListTemp {
+		if _, ok := chartIdMap[v.ChartInfoId]; !ok {
+			chartIdMap[v.ChartInfoId] = true
+		}
+	}
+	// 查询跨品种的图表
+	tagXList, err := cross_variety.GetChartInfoCrossVarietyByXEdbInfoId(edbInfoId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取图表信息失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+
+	for _, v := range tagXList {
+		if _, ok := chartIdMap[v.ChartInfoId]; !ok {
+			chartIdMap[v.ChartInfoId] = true
+		}
+	}
+
+	tagYList, err := cross_variety.GetChartInfoCrossVarietyByYEdbInfoId(edbInfoId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取图表信息失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range tagYList {
+		if _, ok := chartIdMap[v.ChartInfoId]; !ok {
+			chartIdMap[v.ChartInfoId] = true
+		}
+	}
+
 	// 获取当前账号的不可见图表
-	noPermissionChartIdList := make([]int, 0)
 	{
 		obj := data_manage.EdbInfoNoPermissionAdmin{}
 		confList, err := obj.GetAllChartListByAdminId(this.SysUser.AdminId)
@@ -3992,15 +4009,29 @@ func (this *EdbInfoController) RelationChartList() {
 			return
 		}
 		for _, v := range confList {
-			noPermissionChartIdList = append(noPermissionChartIdList, v.ChartInfoId)
+			if _, ok := chartIdMap[v.ChartInfoId]; ok {
+				delete(chartIdMap, v.ChartInfoId)
+			}
 		}
 	}
-	noPermissionChartIdNum := len(noPermissionChartIdList)
-	if noPermissionChartIdNum > 0 {
-		condition += ` AND a.chart_info_id not in  (` + utils.GetOrmInReplace(noPermissionChartIdNum) + `) `
-		pars = append(pars, noPermissionChartIdList)
+	for k, _ := range chartIdMap {
+		chartIds = append(chartIds, k)
 	}
 
+	// 关联指标
+	if len(chartIds) > 0 {
+		condition += `  AND a.chart_info_id in  (` + utils.GetOrmInReplace(len(chartIds)) + `)`
+		pars = append(pars, chartIds)
+	} else {
+		items := make([]*data_manage.ChartInfoView, 0)
+		resp.Paging = page
+		resp.List = items
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+	// 得到图表ID
 	//获取关联图表列表
 	list, err := data_manage.GetRelationChartListByCondition(condition, pars, startSize, pageSize)
 	if err != nil && err.Error() != utils.ErrNoRow() {

+ 1731 - 0
controllers/data_manage/excel/balance_table.go

@@ -0,0 +1,1731 @@
+package excel
+
+import (
+	"archive/zip"
+	"encoding/json"
+	"errors"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/data_manage/excel"
+	"eta/eta_api/models/data_manage/excel/request"
+	"eta/eta_api/models/data_manage/excel/response"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	"eta/eta_api/services/data/data_manage_permission"
+	excelService "eta/eta_api/services/data/excel"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	excel2 "eta/eta_api/services/excel"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/tealeg/xlsx"
+	"io/ioutil"
+	"os"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// GetChildTable
+// @Title 获取子表
+// @Description 获取子表
+// @Param	request	body request.MixedTableCellDataReq true "type json string"
+// @router /excel_info/child_table [get]
+func (c *ExcelInfoController) GetChildTable() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	parentId, _ := c.GetInt("ParentId")
+	if parentId <= 0 {
+		br.Msg = "请选择父表"
+		return
+	}
+	list := make([]*excel.ExcelInfo, 0)
+	// 查询所有子表
+	childList, err := excel.GetChildExcelInfoByParentId(parentId)
+	if err != nil {
+		br.Msg = "查询子表失败"
+		return
+	}
+	if len(childList) > 0 {
+		list = childList
+	}
+
+	resp := &response.BalanceChildTableResp{List: list}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = resp
+}
+
+// Rename
+// @Title 表格重命名接口
+// @Description 表格重命名接口
+// @Param	request	body request.EditExcelInfoReq true "type json string"
+// @Success 200 {object} response.AddExcelInfoResp
+// @router /excel_info/rename [post]
+func (c *ExcelInfoController) Rename() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req request.RenameExcelInfoReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ExcelInfoId <= 0 {
+		br.Msg = "请传入ETA表格标识!"
+		return
+	}
+
+	req.ExcelName = strings.Trim(req.ExcelName, " ")
+	if req.ExcelName == "" {
+		br.Msg = "请填写表格名称!"
+		br.IsSendEmail = false
+		return
+	}
+
+	excelInfo, err := excel.GetExcelInfoById(req.ExcelInfoId)
+	if err != nil {
+		br.Msg = "获取ETA表格失败"
+		br.ErrMsg = "获取ETA表格失败,Err:" + err.Error()
+		return
+	}
+	//判断表格是否存在
+	var condition string
+	var pars []interface{}
+	condition += " AND excel_info_id != ? AND excel_classify_id=? AND excel_name=?  AND parent_id=?"
+	pars = append(pars, req.ExcelInfoId, excelInfo.ExcelClassifyId, req.ExcelName, excelInfo.ParentId)
+
+	// 获取分类下是否存在该表格名称
+	count, err := excel.GetExcelInfoCountByCondition(condition, pars)
+	if err != nil {
+		br.Msg = "ETA判断表格名称是否存在失败"
+		br.ErrMsg = "判断ETA表格名称是否存在失败,Err:" + err.Error()
+		return
+	}
+	if count > 0 {
+		br.Msg = "ETA表格名称已存在,请重新填写"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 操作权限校验, 增加协作人判断
+	{
+		checkExcelInfo := excelInfo
+		if excelInfo.Source == utils.BALANCE_TABLE {
+			checkExcelInfoId := excelInfo.ExcelInfoId
+			if excelInfo.BalanceType == 1 {
+				checkExcelInfoId = excelInfo.RelExcelInfoId
+			} else {
+				if excelInfo.ParentId > 0 {
+					checkExcelInfoId = excelInfo.ParentId
+				}
+			}
+			if checkExcelInfoId != excelInfo.ExcelInfoId {
+				checkExcelInfo, err = excel.GetExcelInfoById(checkExcelInfoId)
+				if err != nil {
+					br.Msg = "获取平衡表格信息失败"
+					br.ErrMsg = "获取平衡表格信息失败,Err:" + err.Error()
+					return
+				}
+			}
+		}
+		// 数据权限
+		haveOperaAuth, e := data_manage_permission.CheckExcelPermissionByExcelInfoId(checkExcelInfo.ExcelInfoId, checkExcelInfo.ExcelClassifyId, checkExcelInfo.IsJoinPermission, c.SysUser.AdminId)
+		if e != nil {
+			br.Msg = "获取ETA表格权限失败"
+			br.ErrMsg = "获取表格权限信息失败,Err" + e.Error()
+			return
+		}
+
+		button := excelService.GetBalanceExcelInfoOpButton(sysUser.AdminId, checkExcelInfo.SysUserId, haveOperaAuth, checkExcelInfo.ExcelInfoId)
+		if !button.OpButton {
+			br.Msg = "无操作权限"
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	excelInfo.ModifyTime = time.Now()
+	excelInfo.ExcelName = req.ExcelName
+	excelInfo.UpdateUserId = sysUser.AdminId
+	excelInfo.UpdateUserRealName = sysUser.RealName
+	// 自动保存时不会传缩略图,也就不更新这个字段
+	updateExcelInfoParams := []string{"ModifyTime", "ExcelName", "UpdateUserId", "UpdateUserRealName"}
+
+	err = excelInfo.Update(updateExcelInfoParams)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	resp := response.AddExcelInfoResp{
+		ExcelInfoId: excelInfo.ExcelInfoId,
+		UniqueCode:  excelInfo.UniqueCode,
+	}
+
+	//删除公共图库那边的缓存
+	_ = utils.Rc.Delete(utils.HZ_CHART_LIB_EXCEL_TABLE_DETAIL + ":" + excelInfo.UniqueCode)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "修改成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// SaveExcelWorker
+// @Title 新增编辑表格协作人接口
+// @Description 新增编辑表格协作人接口
+// @Param	request	body request.EditExcelInfoReq true "type json string"
+// @Success 200 {object} response.AddExcelInfoResp
+// @router /excel_info/worker/save [post]
+func (c *ExcelInfoController) SaveExcelWorker() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req request.SaveExcelInfoWorkerReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ExcelInfoId <= 0 {
+		br.Msg = "请传入ETA表格标识!"
+		return
+	}
+
+	excelInfo, err := excel.GetExcelInfoById(req.ExcelInfoId)
+	if err != nil {
+		br.Msg = "获取ETA表格失败"
+		br.ErrMsg = "获取ETA表格失败,Err:" + err.Error()
+		return
+	}
+	if excelInfo.SysUserId != sysUser.AdminId {
+		br.Msg = "您没有权限操作!"
+		br.ErrMsg = "您没有权限操作!"
+		return
+	}
+	// 操作权限校验, 增加协作人判断
+	{
+		// 数据权限
+		haveOperaAuth, err := data_manage_permission.CheckExcelPermissionByExcelInfoId(excelInfo.ExcelInfoId, excelInfo.ExcelClassifyId, excelInfo.IsJoinPermission, c.SysUser.AdminId)
+		if err != nil {
+			br.Msg = "获取ETA表格失败"
+			br.ErrMsg = "获取ETA表格权限失败,Err:" + err.Error()
+			return
+		}
+		button := excelService.GetExcelInfoOpButton(sysUser, excelInfo.SysUserId, excelInfo.Source, haveOperaAuth)
+		if !button.OpButton {
+			br.Msg = "无操作权限"
+			br.IsSendEmail = false
+			return
+		}
+	}
+	obj := new(excel.ExcelWorker)
+	notDeleteWorkerIds := make([]string, 0)
+	workerMap := make(map[int]struct{})
+	if req.SysUserIds != "" {
+		notDeleteWorkerIds = strings.Split(req.SysUserIds, ",")
+	}
+	existList, err := obj.GetByExcelInfoId(req.ExcelInfoId)
+	if err != nil {
+		br.Msg = "获取表格协作人失败!"
+		br.ErrMsg = "获取表格协作人失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range existList {
+		workerMap[v.SysUserId] = struct{}{}
+	}
+	addIds := make([]int, 0)
+	for _, v := range notDeleteWorkerIds {
+		id, _ := strconv.Atoi(v)
+		if _, ok := workerMap[id]; !ok {
+			addIds = append(addIds, id)
+		}
+	}
+	// 查询协作人姓名
+	adminList, err := system.GetAdminListByIdListWithoutEnable(addIds)
+	addList := make([]*excel.ExcelWorker, 0)
+	for _, v := range adminList {
+		addList = append(addList, &excel.ExcelWorker{
+			ExcelInfoId:     req.ExcelInfoId,
+			SysUserId:       v.AdminId,
+			SysUserRealName: v.RealName,
+			CreateTime:      time.Now(),
+			ModifyTime:      time.Now(),
+		})
+	}
+	// 保存协作人
+	err = obj.AddWorker(req.ExcelInfoId, addList, notDeleteWorkerIds)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// GetWorker
+// @Title 查询协作人接口
+// @Description 新增编辑表格协作人接口
+// @Param	request	body request.EditExcelInfoReq true "type json string"
+// @Success 200 {object} response.AddExcelInfoResp
+// @router /excel_info/worker [get]
+func (c *ExcelInfoController) GetWorker() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	excelInfoId, _ := c.GetInt("ExcelInfoId")
+	if excelInfoId <= 0 {
+		br.Msg = "请选择表格"
+		br.ErrMsg = "ExcelInfoId未传"
+		br.IsSendEmail = false
+		return
+	}
+
+	obj := new(excel.ExcelWorker)
+
+	list, err := obj.GetByExcelInfoId(excelInfoId)
+	if err != nil {
+		br.Msg = "获取表格协作人失败!"
+		br.ErrMsg = "获取表格协作人失败,Err:" + err.Error()
+		return
+	}
+	ret := &response.BalanceTableWorkerResp{List: list}
+	br.Data = ret
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// BalanceChartInfoAdd
+// @Title 新增图表接口
+// @Description 新增图表接口
+// @Param	request	body data_manage.AddChartInfoReq true "type json string"
+// @Success 200 {object} data_manage.AddChartInfoResp
+// @router /excel_info/balance/chart_add [post]
+func (c *ExcelInfoController) BalanceChartInfoAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	deleteCache := true
+	cacheKey := "CACHE_EXCEL_CHART_INFO_ADD_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(c.Ctx.Input.RequestBody)
+		return
+	}
+	var req request.AddBalanceTableChartReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.ChartName = strings.Trim(req.ChartName, " ")
+	if req.ChartName == "" {
+		br.Msg = "请填写图表名称!"
+		return
+	}
+	// 获取表格信息
+	excelInfo, err := excel.GetExcelInfoById(req.ExcelInfoId)
+	if err != nil {
+		br.Msg = "获取ETA表格失败"
+		return
+	}
+
+	if excelInfo.Source != utils.BALANCE_TABLE {
+		br.Msg = "EXCEL类型错误!"
+		return
+	}
+	// 操作权限校验, 增加协作人判断
+	{
+		checkExcelInfo := excelInfo
+		if excelInfo.Source == utils.BALANCE_TABLE {
+			checkExcelInfoId := excelInfo.ExcelInfoId
+			if excelInfo.BalanceType == 1 {
+				checkExcelInfoId = excelInfo.RelExcelInfoId
+			} else {
+				if excelInfo.ParentId > 0 {
+					checkExcelInfoId = excelInfo.ParentId
+				}
+			}
+			if checkExcelInfoId != excelInfo.ExcelInfoId {
+				checkExcelInfo, err = excel.GetExcelInfoById(checkExcelInfoId)
+				if err != nil {
+					br.Msg = "获取平衡表格信息失败"
+					br.ErrMsg = "获取平衡表格信息失败,Err:" + err.Error()
+					return
+				}
+			}
+		}
+		// 数据权限
+		haveOperaAuth, e := data_manage_permission.CheckExcelPermissionByExcelInfoId(checkExcelInfo.ExcelInfoId, checkExcelInfo.ExcelClassifyId, checkExcelInfo.IsJoinPermission, c.SysUser.AdminId)
+		if e != nil {
+			br.Msg = "获取ETA表格权限失败"
+			br.ErrMsg = "获取表格权限信息失败,Err" + e.Error()
+			return
+		}
+
+		button := excelService.GetBalanceExcelInfoOpButton(sysUser.AdminId, checkExcelInfo.SysUserId, haveOperaAuth, checkExcelInfo.ExcelInfoId)
+		if !button.OpButton {
+			br.Msg = "无操作权限"
+			br.IsSendEmail = false
+			return
+		}
+	}
+	// 判断如果是静态表,则查询表数据,落库
+	dataListMap := make(map[int][]*data_manage.EdbDataList)
+	if excelInfo.BalanceType == 1 {
+		newExcelDataMap, excelAllRows, excelAllCols, e, errMsg := excelService.GetBalanceExcelData(excelInfo, c.Lang)
+		if e != nil {
+			br.Msg = "获取表格数据失败"
+			if errMsg != "" {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = "获取表格数据失败,Err:" + e.Error()
+			return
+		}
+
+		for k, mapping := range req.ChartEdbInfoList {
+			tmpMapping := &excel.ExcelChartEdb{
+				ExcelChartEdbId: k,
+				DateSequence:    mapping.DateSequenceStr,
+				DataSequence:    mapping.DataSequenceStr,
+			}
+			er, msg := excelService.GetBalanceExcelEdbData(tmpMapping, newExcelDataMap, dataListMap, excelAllRows, excelAllCols)
+			if er != nil {
+				utils.FileLog.Info(fmt.Sprintf(" 获取图表,指标信息失败 Err:%s, %s", msg, er.Error()))
+				continue
+			}
+		}
+	}
+	chartInfo, err, errMsg, isSendEmail := data.AddBalanceExcelChart(excelInfo, req, sysUser, dataListMap)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+	resp := new(data_manage.AddChartInfoResp)
+	resp.ChartInfoId = chartInfo.ChartInfoId
+	resp.UniqueCode = chartInfo.UniqueCode
+	resp.ChartType = req.ChartType
+
+	//新增操作日志
+	{
+		chartLog := new(data_manage.ChartInfoLog)
+		chartLog.ChartInfoId = chartInfo.ChartInfoId
+		chartLog.ChartName = req.ChartName
+		chartLog.ChartClassifyId = 0
+		chartLog.SysUserId = sysUser.AdminId
+		chartLog.SysUserRealName = sysUser.RealName
+		chartLog.UniqueCode = chartInfo.UniqueCode
+		chartLog.CreateTime = time.Now()
+		chartLog.Content = string(c.Ctx.Input.RequestBody)
+		chartLog.Status = "新增图表"
+		chartLog.Method = c.Ctx.Input.URI()
+		go data_manage.AddChartInfoLog(chartLog)
+	}
+
+	// 试用平台更新用户累计新增图表数
+	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取系统用户数据失败,Err:" + err.Error()
+		return
+	}
+	if utils.BusinessCode == utils.BusinessCodeSandbox && adminItem.DepartmentName == "ETA试用客户" {
+		go func() {
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = adminItem.Mobile
+			_, _ = etaTrialService.UpdateUserChartNum(r)
+		}()
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// BalanceChartInfoEdit
+// @Title 编辑图表接口
+// @Description 新增图表接口
+// @Param	request	body data_manage.AddChartInfoReq true "type json string"
+// @Success 200 {object} data_manage.AddChartInfoResp
+// @router /excel_info/balance/chart_edit [post]
+func (c *ExcelInfoController) BalanceChartInfoEdit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	deleteCache := true
+	cacheKey := "CACHE_EXCEL_CHART_INFO_ADD_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(c.Ctx.Input.RequestBody)
+		return
+	}
+	var req request.AddBalanceTableChartReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.ChartName = strings.Trim(req.ChartName, " ")
+	if req.ChartName == "" {
+		br.Msg = "请填写图表名称!"
+		return
+	}
+	if req.ChartInfoId <= 0 {
+		br.Msg = "请选择图表"
+		return
+	}
+	// 获取表格信息
+	excelInfo, err := excel.GetExcelInfoById(req.ExcelInfoId)
+	if err != nil {
+		br.Msg = "获取ETA表格失败"
+		return
+	}
+
+	if excelInfo.Source != utils.BALANCE_TABLE {
+		br.Msg = "EXCEL类型错误!"
+		return
+	}
+	// 操作权限校验, 增加协作人判断
+	{
+		checkExcelInfo := excelInfo
+		if excelInfo.Source == utils.BALANCE_TABLE {
+			checkExcelInfoId := excelInfo.ExcelInfoId
+			if excelInfo.BalanceType == 1 {
+				checkExcelInfoId = excelInfo.RelExcelInfoId
+			} else {
+				if excelInfo.ParentId > 0 {
+					checkExcelInfoId = excelInfo.ParentId
+				}
+			}
+			if checkExcelInfoId != excelInfo.ExcelInfoId {
+				checkExcelInfo, err = excel.GetExcelInfoById(checkExcelInfoId)
+				if err != nil {
+					br.Msg = "获取平衡表格信息失败"
+					br.ErrMsg = "获取平衡表格信息失败,Err:" + err.Error()
+					return
+				}
+			}
+		}
+		// 数据权限
+		haveOperaAuth, e := data_manage_permission.CheckExcelPermissionByExcelInfoId(checkExcelInfo.ExcelInfoId, checkExcelInfo.ExcelClassifyId, checkExcelInfo.IsJoinPermission, c.SysUser.AdminId)
+		if e != nil {
+			br.Msg = "获取ETA表格权限失败"
+			br.ErrMsg = "获取表格权限信息失败,Err" + e.Error()
+			return
+		}
+
+		button := excelService.GetBalanceExcelInfoOpButton(sysUser.AdminId, checkExcelInfo.SysUserId, haveOperaAuth, checkExcelInfo.ExcelInfoId)
+		if !button.OpButton {
+			br.Msg = "无操作权限"
+			br.IsSendEmail = false
+			return
+		}
+	}
+	// 判断如果是静态表,则查询表数据,落库
+	dataListMap := make(map[int][]*data_manage.EdbDataList)
+	if excelInfo.BalanceType == 1 {
+		newExcelDataMap, excelAllRows, excelAllCols, e, errMsg := excelService.GetBalanceExcelData(excelInfo, c.Lang)
+		if e != nil {
+			br.Msg = "获取表格数据失败"
+			if errMsg != "" {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = "获取表格数据失败,Err:" + e.Error()
+			return
+		}
+
+		for k, mapping := range req.ChartEdbInfoList {
+			tmpMapping := &excel.ExcelChartEdb{
+				ExcelChartEdbId: k,
+				DateSequence:    mapping.DateSequenceStr,
+				DataSequence:    mapping.DataSequenceStr,
+			}
+			er, msg := excelService.GetBalanceExcelEdbData(tmpMapping, newExcelDataMap, dataListMap, excelAllRows, excelAllCols)
+			if er != nil {
+				utils.FileLog.Info(fmt.Sprintf(" 获取图表,指标信息失败 Err:%s, %s", msg, er.Error()))
+				continue
+			}
+		}
+	}
+	chartItem, err, errMsg, isSendEmail := data.EditBalanceExcelChart(excelInfo, req, sysUser, dataListMap)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	resp := new(data_manage.AddChartInfoResp)
+	resp.ChartInfoId = chartItem.ChartInfoId
+	resp.UniqueCode = chartItem.UniqueCode
+	resp.ChartType = req.ChartType
+
+	//新增操作日志
+	{
+		chartLog := new(data_manage.ChartInfoLog)
+		chartLog.ChartName = chartItem.ChartName
+		chartLog.ChartInfoId = req.ChartInfoId
+		chartLog.ChartClassifyId = chartItem.ChartClassifyId
+		chartLog.SysUserId = sysUser.AdminId
+		chartLog.SysUserRealName = sysUser.RealName
+		chartLog.UniqueCode = chartItem.UniqueCode
+		chartLog.CreateTime = time.Now()
+		chartLog.Content = string(c.Ctx.Input.RequestBody)
+		chartLog.Status = "编辑图表"
+		chartLog.Method = c.Ctx.Input.URL()
+		go data_manage.AddChartInfoLog(chartLog)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// GetBalanceChartList
+// @Title 获取平衡表表关联的图表
+// @Description 获取平衡表表关联的图表
+// @Param	request	body request.MixedTableCellDataReq true "type json string"
+// @router /excel_info/balance/chart_list [get]
+func (c *ExcelInfoController) GetBalanceChartList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	excelInfoId, _ := c.GetInt("ExcelInfoId")
+	if excelInfoId <= 0 {
+		br.Msg = "请选择平衡表"
+		return
+	}
+	// 查询所有子表
+	excelInfo, err := excel.GetExcelInfoById(excelInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "表格不存在"
+			return
+		}
+		br.Msg = "查询子表失败"
+		br.ErrMsg = "查询子表失败,Err:" + err.Error()
+		return
+	}
+	list := make([]*data_manage.BalanceChartInfoDetailResp, 0)
+	chartInfoList, mappingListMap, dataListMap, err, errMsg := excelService.GetBalanceExcelChartList(excelInfo, "")
+	if err != nil {
+		if errMsg != "" {
+			br.Msg = errMsg
+			br.ErrMsg = err.Error()
+			return
+		} else {
+			br.Msg = "查询图表失败"
+			br.ErrMsg = "查询图表失败,Err:" + err.Error()
+		}
+		return
+	}
+	for _, chartInfo := range chartInfoList {
+		mappingList, ok := mappingListMap[chartInfo.ChartInfoId]
+		if !ok {
+			br.Msg = "未找到图表关联的指标信息"
+			return
+		}
+		var chartInfoResp *data_manage.ChartInfoDetailResp
+		chartInfoResp, err, errMsg = data.GetBalanceExcelChartDetail(chartInfo, mappingList, sysUser, dataListMap)
+		if err != nil {
+			br.Msg = "查询图表详情失败"
+			br.ErrMsg = "查询图表详情失败,Err:" + err.Error()
+			return
+		}
+		chartEdbList := make([]*data_manage.ExcelChartEdbView, 0)
+		for _, v := range mappingList {
+			tmp := &data_manage.ExcelChartEdbView{
+				ExcelChartEdbId: v.ExcelChartEdbId,
+				DateSequenceStr: v.DateSequence,
+				DataSequenceStr: v.DataSequence,
+				FromTag:         v.FromTag,
+			}
+			chartEdbList = append(chartEdbList, tmp)
+		}
+
+		balanceChartInfoResp := &data_manage.BalanceChartInfoDetailResp{
+			ChartInfoDetailResp: chartInfoResp,
+			ExcelEdbList:        chartEdbList,
+		}
+		list = append(list, balanceChartInfoResp)
+	}
+
+	ret := &data_manage.BalanceTableChartListResp{List: list}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = ret
+}
+
+// DeleteBalanceChart
+// @Title 删除平衡表图表
+// @Description 删除平衡表图表
+// @Param	request	body data_manage.DeleteChartClassifyReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /excel_info/balance/chart_del [post]
+func (c *ExcelInfoController) DeleteBalanceChart() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req data_manage.DeleteChartClassifyReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ChartInfoId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+	//删除图表
+	if req.ChartInfoId > 0 {
+		chartInfo, err := data_manage.GetChartInfoById(req.ChartInfoId)
+		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 chartInfo == nil {
+			br.Msg = "图表已删除,请刷新页面"
+			return
+		}
+
+		// 操作权限校验, 增加协作人判断
+		{
+			// 获取表格信息
+			excelInfo, e := excel.GetExcelInfoByChartInfoId(req.ChartInfoId)
+			if e != nil {
+				br.Msg = "获取ETA表格失败"
+				return
+			}
+			checkExcelInfo := excelInfo
+			if excelInfo.Source == utils.BALANCE_TABLE {
+				checkExcelInfoId := excelInfo.ExcelInfoId
+				if excelInfo.BalanceType == 1 {
+					checkExcelInfoId = excelInfo.RelExcelInfoId
+				} else {
+					if excelInfo.ParentId > 0 {
+						checkExcelInfoId = excelInfo.ParentId
+					}
+				}
+				if checkExcelInfoId != excelInfo.ExcelInfoId {
+					checkExcelInfo, e = excel.GetExcelInfoById(checkExcelInfoId)
+					if e != nil {
+						br.Msg = "获取平衡表格信息失败"
+						br.ErrMsg = "获取平衡表格信息失败,Err:" + e.Error()
+						return
+					}
+				}
+			}
+			// 数据权限
+			haveOperaAuth, e := data_manage_permission.CheckExcelPermissionByExcelInfoId(checkExcelInfo.ExcelInfoId, checkExcelInfo.ExcelClassifyId, checkExcelInfo.IsJoinPermission, c.SysUser.AdminId)
+			if e != nil {
+				br.Msg = "获取ETA表格权限失败"
+				br.ErrMsg = "获取表格权限信息失败,Err" + e.Error()
+				return
+			}
+
+			button := excelService.GetBalanceExcelInfoOpButton(sysUser.AdminId, checkExcelInfo.SysUserId, haveOperaAuth, checkExcelInfo.ExcelInfoId)
+			if !button.OpButton {
+				br.Msg = "无操作权限"
+				br.IsSendEmail = false
+				return
+			}
+		}
+
+		// 获取引用该图表的MyCharts, 用于ES删除
+		var myCond string
+		var myPars []interface{}
+		myCond += ` AND a.chart_info_id = ? `
+		myPars = append(myPars, req.ChartInfoId)
+		myCharts, e := data_manage.GetMyChartListGroupByCharyInfoIdAndAdminIdByCondition(myCond, myPars)
+		if e != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "获取引用图表的MyChats失败, Err: " + e.Error()
+			return
+		}
+		myIds := make([]int, 0)
+		for _, m := range myCharts {
+			myIds = append(myIds, m.MyChartId)
+		}
+
+		//删除图表及关联指标
+		e = excel.DeleteBalanceExcelChartInfoAndData(req.ChartInfoId)
+		if e != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除失败,Err:" + e.Error()
+			return
+		}
+		//删除ES
+		{
+			go data.EsDeleteChartInfo(req.ChartInfoId)
+			// 删除MY ETA 图表 es数据
+			//go data.EsDeleteMyChartInfoByChartInfoId(req.ChartInfoId)
+			go data.EsDeleteMyChartInfoByMyChartIds(myIds)
+		}
+
+		//新增操作日志
+		{
+			chartLog := new(data_manage.ChartInfoLog)
+			chartLog.ChartName = chartInfo.ChartName
+			chartLog.ChartInfoId = req.ChartInfoId
+			chartLog.ChartClassifyId = chartInfo.ChartClassifyId
+			chartLog.SysUserId = sysUser.AdminId
+			chartLog.SysUserRealName = sysUser.RealName
+			chartLog.UniqueCode = chartInfo.UniqueCode
+			chartLog.CreateTime = time.Now()
+			chartLog.Content = string(c.Ctx.Input.RequestBody)
+			chartLog.Status = "删除图表"
+			chartLog.Method = c.Ctx.Input.URI()
+			go data_manage.AddChartInfoLog(chartLog)
+		}
+	}
+	br.Ret = 200
+	br.Msg = "删除成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// BalanceSeasonChartLegendPreview
+// @Title 季节性图例预览接口
+// @Description 季节性图例预览接口
+// @Param	request	body request.BalanceSeasonChartLegendPreviewReq true "type json string"
+// @Success 200 {object} data_manage.BalanceSeasonChartLegendPreviewResp
+// @router /excel_info/balance/chartLegend/preview [post]
+func (c *ExcelInfoController) BalanceSeasonChartLegendPreview() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req request.BalanceSeasonChartLegendPreviewReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if len(req.DateArr) == 0 || len(req.DateArr) == 0 || len(req.DateArr) != len(req.DataArr) {
+		br.Msg = "请输入正确的日期和数据"
+		return
+	}
+	newDataMap := make(map[int]float64)
+	for i, v := range req.DataArr {
+		fv, e := strconv.ParseFloat(v, 64)
+		if e != nil {
+			br.Msg = "数据格式错误"
+			return
+		}
+		newDataMap[i] = fv
+	}
+	//组装成excelEdbData
+	dataList := make([]*data_manage.EdbDataList, 0)
+
+	for i, v := range req.DateArr {
+		dataTime, e := time.ParseInLocation(utils.FormatDate, v, time.Local)
+		if e != nil {
+			br.Msg = "日期格式错误"
+			br.ErrMsg = v + "日期格式错误,Err:" + e.Error()
+			return
+		}
+		timestamp := dataTime.UnixNano() / 1e6
+		tmp := &data_manage.EdbDataList{
+			EdbDataId:     i,
+			DataTime:      v,
+			DataTimestamp: timestamp,
+			Value:         newDataMap[i],
+		}
+		dataList = append(dataList, tmp)
+	}
+
+	// 对dataList 根据dataTimestamp 进行排序
+	sort.Slice(dataList, func(i, j int) bool {
+		return dataList[i].DataTimestamp < dataList[j].DataTimestamp
+	})
+
+	list, err, errMsg := data.GetBalanceExcelSeasonChartLegendPreview(dataList, req.Calendar, req.SeasonExtraConfig)
+	if err != nil {
+		if errMsg != "" {
+			br.Msg = errMsg
+		} else {
+			br.Msg = "预览失败"
+		}
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	ret := &data_manage.BalanceSeasonChartLegendPreviewResp{List: list}
+	br.Data = ret
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// AddStaticExcel
+// @Title 新增静态表
+// @Description 新增静态表
+// @Param	request	body request.EditExcelInfoReq true "type json string"
+// @Success 200 {object} response.AddExcelInfoResp
+// @router /excel_info/balance/static/add [post]
+func (c *ExcelInfoController) AddStaticExcel() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req request.AddBalanceStaticExcelInfoReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ExcelInfoId <= 0 {
+		br.Msg = "请传入ETA表格标识!"
+		return
+	}
+
+	cacheKey := "CACHE_TABLE_INFO_BALANCE_STATIC_ADD_" + strconv.Itoa(req.ExcelInfoId)
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!"
+		return
+	}
+	defer func() {
+		_ = utils.Rc.Delete(cacheKey)
+	}()
+
+	req.VersionName = strings.Trim(req.VersionName, " ")
+	if req.VersionName == "" {
+		br.Msg = "请填写版本名称!"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 获取原ETA表格信息
+	oldExcelInfo, err := excel.GetExcelInfoById(req.ExcelInfoId)
+	if err != nil {
+		br.Msg = "获取ETA表格失败"
+		return
+	}
+	// 操作权限校验, 增加协作人判断
+	{
+		// 数据权限
+		haveOperaAuth, err := data_manage_permission.CheckExcelPermissionByExcelInfoId(oldExcelInfo.ExcelInfoId, oldExcelInfo.ExcelClassifyId, oldExcelInfo.IsJoinPermission, c.SysUser.AdminId)
+		if err != nil {
+			br.Msg = "获取ETA表格失败"
+			br.ErrMsg = "获取ETA表格权限失败,Err:" + err.Error()
+			return
+		}
+		button := excelService.GetExcelInfoOpButton(sysUser, oldExcelInfo.SysUserId, oldExcelInfo.Source, haveOperaAuth)
+		if !button.OpButton {
+			br.Msg = "无操作权限"
+			br.IsSendEmail = false
+			return
+		}
+	}
+	// 查询出每个子表的内容,并将内容转为静态版本
+	excelDataMap := make(map[int]map[int][]*data_manage.EdbDataList)
+	//查询动态表所有的子表,并复制为静态表
+	condition := " AND parent_id = ? AND balance_type = 0 "
+	var pars []interface{}
+	pars = append(pars, req.ExcelInfoId)
+	childExcelList, err := excel.GetExcelInfoListByCondition(condition, pars)
+	if err != nil {
+		br.Msg = "获取子表失败"
+		br.ErrMsg = fmt.Sprintf("获取子表失败 %s", err.Error())
+		return
+	}
+	for k, childExcelInfo := range childExcelList {
+		//得到表格数据并落库
+
+		newExcelDataMap, excelAllRows, excelAllCols, e, errMsg := excelService.GetBalanceExcelData(childExcelInfo, c.Lang)
+		if e != nil {
+			br.Msg = "获取表格数据失败"
+			if errMsg != "" {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = "获取表格数据失败,Err:" + e.Error()
+			return
+		}
+		tmpMappingList, e := excel.GetExcelChartEdbMappingByExcelInfoId(childExcelInfo.ExcelInfoId)
+		if e != nil {
+			err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+			return
+		}
+		if len(tmpMappingList) > 0 {
+			for _, mapping := range tmpMappingList {
+				child, ok := excelDataMap[mapping.ChartInfoId]
+				if !ok {
+					child = make(map[int][]*data_manage.EdbDataList)
+				}
+				err, errMsg = excelService.GetBalanceExcelEdbData(mapping, newExcelDataMap, child, excelAllRows, excelAllCols)
+				if err != nil {
+					err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+					return
+				}
+				excelDataMap[mapping.ChartInfoId] = child
+			}
+		}
+
+		content, e := excelService.TransferBalanceExcelContentToStatic(childExcelInfo, c.Lang)
+		if e != nil {
+			br.Msg = "动态内容转成静态失败"
+			br.ErrMsg = fmt.Sprintf("动态内容转成静态失败 %s", e.Error())
+			return
+		}
+		childExcelList[k].Content = content
+	}
+
+	excelInfo, err, errMsg, isSendEmail := data.AddBalanceStaticExcel(oldExcelInfo, oldExcelInfo.ExcelClassifyId, req.VersionName, sysUser, 0, req.ExcelInfoId, 1, childExcelList, true, excelDataMap)
+	if err != nil {
+		br.Msg = "复制失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "复制失败,Err:" + err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	resp := new(response.AddExcelInfoResp)
+	resp.ExcelInfoId = excelInfo.ExcelInfoId
+	resp.UniqueCode = excelInfo.UniqueCode
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// BalanceVersionList
+// @Title 查询平衡表版本号列表
+// @Description 查询平衡表版本号列表
+// @Param	request	body request.EditExcelInfoReq true "type json string"
+// @Success 200 {object} response.AddExcelInfoResp
+// @router /excel_info/balance/version [get]
+func (c *ExcelInfoController) BalanceVersionList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	excelInfoId, _ := c.GetInt("ExcelInfoId")
+	if excelInfoId <= 0 {
+		br.Msg = "请选择表格"
+		br.ErrMsg = "ExcelInfoId未传"
+		br.IsSendEmail = false
+		return
+	}
+	excelInfo, err := excel.GetExcelInfoById(excelInfoId)
+	if err != nil {
+		br.Msg = "获取ETA表格失败"
+		br.ErrMsg = "获取ETA表格失败,Err:" + err.Error()
+		return
+	}
+	if excelInfo.Source != utils.BALANCE_TABLE {
+		br.Msg = "请选择平衡表"
+		return
+	}
+	if excelInfo.BalanceType != 0 && excelInfo.ParentId != 0 {
+		br.Msg = "请选择动态平衡表"
+		return
+	}
+	list := make([]*response.BalanceTableVersionListItem, 0)
+	list = append(list, &response.BalanceTableVersionListItem{
+		ExcelInfoId:    excelInfo.ExcelInfoId,
+		UniqueCode:     excelInfo.UniqueCode,
+		BalanceType:    excelInfo.BalanceType,
+		RelExcelInfoId: excelInfo.RelExcelInfoId,
+		VersionName:    "动态表", //todo 有个默认的版本名称
+	})
+	//查询动态表所有的子表,并复制为静态表
+	condition := " AND rel_excel_info_id=? AND parent_id = 0 AND balance_type = 1 "
+	var pars []interface{}
+	pars = append(pars, excelInfoId)
+
+	staticList, err := excel.GetNoContentExcelInfoListByConditionNoPage(condition, pars)
+	if err != nil {
+		br.Msg = "获取子表失败"
+		br.ErrMsg = fmt.Sprintf("获取子表失败 %s", err.Error())
+		return
+	}
+
+	for _, staticInfo := range staticList {
+		tmp := &response.BalanceTableVersionListItem{
+			ExcelInfoId:    staticInfo.ExcelInfoId,
+			UniqueCode:     staticInfo.UniqueCode,
+			BalanceType:    staticInfo.BalanceType,
+			RelExcelInfoId: staticInfo.RelExcelInfoId,
+			VersionName:    staticInfo.VersionName,
+		}
+		list = append(list, tmp)
+	}
+	ret := &response.BalanceTableVersionListResp{List: list}
+	br.Data = ret
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ModifyBalanceExcelVersion
+// @Title 版本号重命名
+// @Description 版本号重命名
+// @Param	request	body request.AddBalanceStaticExcelInfoReq true "type json string"
+// @Success 200 {object} response.AddExcelInfoResp
+// @router /excel_info/balance/version/modify [post]
+func (c *ExcelInfoController) ModifyBalanceExcelVersion() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req request.AddBalanceStaticExcelInfoReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ExcelInfoId <= 0 {
+		br.Msg = "请传入ETA表格标识!"
+		return
+	}
+
+	req.VersionName = strings.Trim(req.VersionName, " ")
+	if req.VersionName == "" {
+		br.Msg = "请填写版本名称!"
+		br.IsSendEmail = false
+		return
+	}
+	excelInfo, err := excel.GetExcelInfoById(req.ExcelInfoId)
+	if err != nil {
+		br.Msg = "获取ETA表格失败"
+		br.ErrMsg = "获取ETA表格失败,Err:" + err.Error()
+		return
+	}
+	if excelInfo.Source != utils.BALANCE_TABLE {
+		br.Msg = "请选择平衡表"
+		return
+	}
+	if excelInfo.BalanceType != 1 && excelInfo.ParentId != 0 {
+		br.Msg = "请选择静态表"
+		return
+	}
+
+	// 操作权限校验, 增加协作人判断
+	{
+		checkExcelInfo := excelInfo
+		if excelInfo.Source == utils.BALANCE_TABLE {
+			checkExcelInfoId := excelInfo.ExcelInfoId
+			if excelInfo.BalanceType == 1 {
+				checkExcelInfoId = excelInfo.RelExcelInfoId
+			} else {
+				if excelInfo.ParentId > 0 {
+					checkExcelInfoId = excelInfo.ParentId
+				}
+			}
+			if checkExcelInfoId != excelInfo.ExcelInfoId {
+				checkExcelInfo, err = excel.GetExcelInfoById(checkExcelInfoId)
+				if err != nil {
+					br.Msg = "获取平衡表格信息失败"
+					br.ErrMsg = "获取平衡表格信息失败,Err:" + err.Error()
+					return
+				}
+			}
+		}
+		// 数据权限
+		haveOperaAuth, e := data_manage_permission.CheckExcelPermissionByExcelInfoId(checkExcelInfo.ExcelInfoId, checkExcelInfo.ExcelClassifyId, checkExcelInfo.IsJoinPermission, c.SysUser.AdminId)
+		if e != nil {
+			br.Msg = "获取ETA表格权限失败"
+			br.ErrMsg = "获取表格权限信息失败,Err" + e.Error()
+			return
+		}
+
+		button := excelService.GetBalanceExcelInfoOpButton(sysUser.AdminId, checkExcelInfo.SysUserId, haveOperaAuth, checkExcelInfo.ExcelInfoId)
+		if !button.OpButton {
+			br.Msg = "无操作权限"
+			br.IsSendEmail = false
+			return
+		}
+	}
+	// 检验分类下是否存在该版本号
+	{
+		var condition string
+		var pars []interface{}
+		condition += " AND rel_excel_info_id=? AND parent_id=0"
+		pars = append(pars, req.ExcelInfoId)
+
+		condition += " AND version_name=? "
+		pars = append(pars, req.VersionName)
+
+		count, tmpErr := excel.GetExcelInfoCountByCondition(condition, pars)
+		if tmpErr != nil {
+			br.Msg = "查询版本名称失败"
+			br.ErrMsg = tmpErr.Error()
+			return
+		}
+		if count > 0 {
+			br.Msg = "表格版本名称已存在,请重新填写版本名称"
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	excelInfo.ModifyTime = time.Now()
+	excelInfo.VersionName = req.VersionName
+	updateExcelInfoParams := []string{"ModifyTime", "VersionName"}
+	// todo 同步修改静态表中的图表和指标名称
+	// ETA表格信息变更
+	err = excelInfo.Update(updateExcelInfoParams)
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+	br.IsAddLog = true
+}
+
+func downloadBalanceTable(excelInfo *excel.ExcelInfo, lang string) (savePath, zipName string, uploadDir string, err error, errMsg string) {
+	dateDir := time.Now().Format("20060102")
+	randStr := time.Now().Format(utils.FormatDateTimeUnSpace)
+	uploadDir = "static/xls/" + dateDir + "/" + randStr
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
+	if err != nil {
+		return
+	}
+	fileList := make([]string, 0)
+	if excelInfo.ParentId != 0 && excelInfo.BalanceType != 0 {
+		errMsg = "平衡表类型错误"
+		err = fmt.Errorf("平衡表类型错误 ")
+		return
+	}
+	//复制静态表
+	staticCondition := " AND parent_id = 0 AND balance_type = 1 AND rel_excel_info_id=? "
+	var staticPars []interface{}
+	staticPars = append(staticPars, excelInfo.ExcelInfoId)
+	excelList, err := excel.GetExcelInfoListByCondition(staticCondition, staticPars)
+	if err != nil {
+		errMsg = "获取表格失败"
+		err = fmt.Errorf("获取表格失败 %s", err.Error())
+		return
+	}
+	excelList = append(excelList, excelInfo)
+	for _, staticExcelInfo := range excelList {
+		cCondition := " AND parent_id = ?"
+		var cPars []interface{}
+		cPars = append(cPars, staticExcelInfo.ExcelInfoId)
+		childList, e := excel.GetExcelInfoListByCondition(cCondition, cPars)
+		if e != nil {
+			errMsg = "获取子表失败"
+			err = fmt.Errorf("获取子表失败 %s", err.Error())
+			return
+		}
+		xlsxFile := xlsx.NewFile()
+		fileName := staticExcelInfo.ExcelName + ".xlsx"
+		fpath := uploadDir + "/" + fileName
+		for _, childExcelInfo := range childList {
+			var result request.MixedTableReq
+			err = json.Unmarshal([]byte(childExcelInfo.Content), &result)
+			if err != nil {
+				errMsg = "获取失败"
+				err = fmt.Errorf("表格json转结构体失败,Err:" + err.Error())
+				return
+			}
+			newResult, er, msg := excelService.GetMixedTableCellData(result, lang)
+			if er != nil {
+				err = er
+				errMsg = msg
+				return
+			}
+			tableData, er := excel2.GetTableDataByMixedTableData(newResult)
+			if er != nil {
+				errMsg = "获取失败"
+				err = fmt.Errorf("转换成table失败,Err:" + err.Error())
+				return
+			}
+
+			// 将单个sheet的数据写入到excel
+			err = tableData.WriteExcelSheetData(xlsxFile, childExcelInfo.ExcelName)
+			if err != nil {
+				return
+			}
+		}
+		//处理excel文件
+		//return
+		err = xlsxFile.Save(fpath)
+		if err != nil {
+			return
+		}
+		fileList = append(fileList, fileName)
+	}
+	// 创建zip
+	zipName = excelInfo.ExcelName + ".zip"
+
+	savePath = uploadDir + "/" + zipName
+	fmt.Println(savePath)
+	zipFile, err := os.Create(savePath)
+	if err != nil {
+		return
+	}
+	zipWriter := zip.NewWriter(zipFile)
+	// 生成zip过程中报错关闭
+	defer func() {
+		if err != nil {
+			zipWriter.Close()
+			zipFile.Close()
+		}
+		//os.Remove(savePath)
+	}()
+	//写入zip
+	for i := 0; i < len(fileList); i++ {
+		ioWriter, e := zipWriter.Create(fileList[i])
+		if e != nil {
+			err = fmt.Errorf("创建zip失败,Err:" + e.Error())
+			if os.IsPermission(e) {
+				fmt.Println("权限不足: ", e)
+				return
+			}
+			return
+		}
+		fullPath := uploadDir + "/" + fileList[i]
+		content, e := ioutil.ReadFile(fullPath)
+		if e != nil {
+			err = fmt.Errorf("读取文件失败,Err:" + e.Error())
+			return
+		}
+		ioWriter.Write(content)
+	}
+	// 生成zip后关闭,否则下载文件会损坏
+	zipWriter.Close()
+	zipFile.Close()
+	//this.Ctx.Output.Download(downLoadnFilePath, downloadFileName)
+	return
+}
+
+func refreshBalanceTable(excelDetail response.ExcelInfoDetail, lang string) (err error) {
+	edbInfoIds := make([]int, 0)
+	edbInfoIdExist := make(map[int]bool)
+	if excelDetail.ParentId > 0 {
+		newResult := excelDetail.TableData.(request.MixedTableReq)
+		newData := newResult.Data
+		if len(newData) > 0 {
+			for _, t := range newData {
+				for _, v := range t {
+					if v.EdbInfoId > 0 && !edbInfoIdExist[v.EdbInfoId] {
+						edbInfoIdExist[v.EdbInfoId] = true
+						edbInfoIds = append(edbInfoIds, v.EdbInfoId)
+					}
+				}
+			}
+		}
+
+		// 清除缓存
+		key := utils.HZ_CHART_LIB_EXCEL_TABLE_DETAIL + ":" + excelDetail.UniqueCode
+		if utils.Re == nil {
+			_ = utils.Rc.Delete(key)
+		}
+	} else {
+		//查询父表和子表
+		cCondition := " AND parent_id = ? AND balance_type=0"
+		var cPars []interface{}
+		cPars = append(cPars, excelDetail.ExcelInfoId)
+		childList, e := excel.GetExcelInfoListByCondition(cCondition, cPars)
+		if e != nil {
+			err = fmt.Errorf("获取子表失败 %s", err.Error())
+			return
+		}
+		// 遍历
+		for _, child := range childList {
+			var result request.MixedTableReq
+			err = json.Unmarshal([]byte(child.Content), &result)
+			if err != nil {
+				err = errors.New("表格json转结构体失败,Err:" + err.Error())
+				return
+			}
+			var newData [][]request.MixedTableCellDataReq
+			newData, err, _ = excelService.GetMixedTableCellData(result, lang)
+			if err != nil {
+				return
+			}
+
+			if len(newData) > 0 {
+				for _, t := range newData {
+					for _, v := range t {
+						if v.EdbInfoId > 0 && !edbInfoIdExist[v.EdbInfoId] {
+							edbInfoIdExist[v.EdbInfoId] = true
+							edbInfoIds = append(edbInfoIds, v.EdbInfoId)
+						}
+					}
+				}
+			}
+
+			// 清除缓存
+			key := utils.HZ_CHART_LIB_EXCEL_TABLE_DETAIL + ":" + child.UniqueCode
+			if utils.Re == nil {
+				_ = utils.Rc.Delete(key)
+			}
+		}
+	}
+
+	if len(edbInfoIds) > 0 {
+		err, _ = data.EdbInfoRefreshAllFromBaseV3(edbInfoIds, false, true, false)
+		if err != nil {
+			err = fmt.Errorf("刷新混合表格数据失败, Err: " + err.Error())
+			return
+		}
+	}
+	return
+}
+
+// BalanceChartInfoBaseEdit
+// @Title 编辑图表基础信息接口
+// @Description 编辑图表基础信息接口
+// @Param	request	body data_manage.EditChartEnInfoBaseReq true "type json string"
+// @Success Ret=200 编辑成功
+// @router /excel_info/balance/chart_base_edit [post]
+func (this *ExcelInfoController) BalanceChartInfoBaseEdit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req data_manage.EditChartInfoBaseReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.ChartName = strings.Trim(req.ChartName, " ")
+	if req.ChartInfoId <= 0 {
+		br.Msg = "请选择图表"
+		return
+	}
+
+	//判断指标名称是否存在
+	chartItem, err := data_manage.GetChartInfoById(req.ChartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "图表已被删除,请刷新页面"
+			br.ErrMsg = "图表已被删除,请刷新页面"
+			return
+		}
+		br.Msg = "获取图表信息失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+
+	// 判断是否有传入对应的指标配置
+	noEdbInfoType := []int{10}
+	if len(req.ChartEdbInfoList) <= 0 && !utils.InArrayByInt(noEdbInfoType, chartItem.ChartType) {
+		br.Msg = "请选择指标!"
+		return
+	}
+
+	var edbCondition string
+	var edbPars []interface{}
+	for _, v := range req.ChartEdbInfoList {
+		edbInfoId := v.EdbInfoId
+		edbInfo, err := excel.GetExcelChartEdbById(edbInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "图表不存在!"
+				br.ErrMsg = "图表指标不存在,ChartInfoId:" + strconv.Itoa(edbInfoId)
+				return
+			} else {
+				br.Msg = "获取图表信息失败!"
+				br.ErrMsg = "获取图表的指标信息失败,Err:" + err.Error()
+				return
+			}
+		}
+		if edbInfo == nil {
+			br.Msg = "指标不存在!"
+			br.ErrMsg = "指标不存在,ChartInfoId:" + strconv.Itoa(edbInfoId)
+			return
+		}
+
+		//判断指标名称是否重复
+		if v.EdbName != "" {
+			edbCondition = ""
+			edbPars = make([]interface{}, 0)
+
+			edbCondition += " AND excel_chart_edb_id<>?  AND chart_info_id=?"
+			edbPars = append(edbPars, edbInfo.ExcelChartEdbId, chartItem.ChartInfoId)
+
+			switch this.Lang {
+			case utils.EnLangVersion:
+				edbCondition += " AND edb_name_en =? "
+			default:
+				edbCondition += " AND edb_name =? "
+			}
+
+			edbPars = append(edbPars, v.EdbName)
+
+			edbExist, err := excel.GetBalanceChartEdbByCondition(edbCondition, edbPars)
+			if err != nil {
+				if err.Error() != utils.ErrNoRow() {
+					br.Msg = "判断英文指标名称是否存在失败"
+					br.ErrMsg = "判断英文指标名称是否存在失败,Err:" + err.Error()
+					return
+				}
+			}
+
+			if err == nil && edbExist.ExcelChartEdbId > 0 {
+				br.Msg = edbExist.EdbName + ":" + v.EdbName + "指标名称已存在"
+				br.ErrMsg = "指标名称已存在,请重新填写"
+				br.IsSendEmail = false
+				return
+			}
+		}
+	}
+	if req.ChartName != "" || req.ExtraConfig != `` {
+		var condition string
+		var pars []interface{}
+		condition += " AND chart_info_id<>? "
+		pars = append(pars, req.ChartInfoId)
+
+		switch this.Lang {
+		case utils.EnLangVersion:
+			condition += " AND chart_name_en = ? "
+		default:
+			condition += " AND chart_name = ? "
+		}
+
+		pars = append(pars, req.ChartName)
+
+		existItem, err := data_manage.GetChartInfoByCondition(condition, pars)
+		if err != nil {
+			if err.Error() != utils.ErrNoRow() {
+				br.Msg = "判断英文图表名称是否存在失败"
+				br.ErrMsg = "判断英文图表名称是否存在失败,Err:" + err.Error()
+				return
+			}
+		}
+
+		if err == nil && existItem.ChartInfoId > 0 {
+			br.Msg = existItem.ChartName + ":" + req.ChartName + "图表名称已存在"
+			return
+		}
+	}
+
+	err = excel.EditBalanceChartBaseInfoAndEdbEnInfo(&req, chartItem, this.Lang)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+	//添加es数据
+	go data.EsAddOrEditChartInfo(chartItem.ChartInfoId)
+	//修改my eta es数据
+	go data.EsAddOrEditMyChartInfoByChartInfoId(chartItem.ChartInfoId)
+
+	//新增操作日志
+	{
+		chartLog := new(data_manage.ChartInfoLog)
+		chartLog.ChartName = chartItem.ChartName
+		chartLog.ChartInfoId = req.ChartInfoId
+		chartLog.ChartClassifyId = chartItem.ChartClassifyId
+		chartLog.SysUserId = sysUser.AdminId
+		chartLog.SysUserRealName = sysUser.RealName
+		chartLog.UniqueCode = chartItem.UniqueCode
+		chartLog.CreateTime = time.Now()
+		chartLog.Content = string(this.Ctx.Input.RequestBody)
+		chartLog.Status = "编辑图表英文信息"
+		chartLog.Method = this.Ctx.Input.URL()
+		go data_manage.AddChartInfoLog(chartLog)
+	}
+	//清除缓存
+	if utils.Re == nil && utils.Rc != nil {
+		utils.Rc.Delete(utils.HZ_CHART_LIB_DETAIL + chartItem.UniqueCode) //图表分享链接缓存
+		utils.Rc.Delete(data.GetChartInfoDataKey(req.ChartInfoId))
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "编辑成功"
+	br.IsAddLog = true
+}

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

@@ -67,11 +67,38 @@ func (this *ExcelClassifyController) List() {
 	// 获取二级分类
 	// 获取三级分类
 	// 根据来源获取所有excel表格(无内容)
-	allExcelInfo, err := excel.GetNoContentExcelInfoAll(source, showUserId)
-	if err != nil && err.Error() != utils.ErrNoRow() {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取表格信息失败,Err:" + err.Error()
-		return
+	allExcelInfo := make([]*excel.ExcelClassifyItems, 0)
+	if source == utils.BALANCE_TABLE {
+		//找到当前协作人相关的表格ID
+		var excelIds []int
+		if isShowMe {
+			obj := new(excel.ExcelWorker)
+			existList, err := obj.GetBySysUserId(this.SysUser.AdminId)
+			if err != nil {
+				br.Msg = "获取表格协作人失败!"
+				br.ErrMsg = "获取表格协作人失败,Err:" + err.Error()
+				return
+			}
+			if len(existList) > 0 {
+				for _, v := range existList {
+					excelIds = append(excelIds, v.ExcelInfoId)
+				}
+			}
+		}
+
+		allExcelInfo, err = excel.GetBalanceNoContentExcelInfoAll(source, excelIds, showUserId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取表格信息失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		allExcelInfo, err = excel.GetNoContentExcelInfoAll(source, showUserId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取表格信息失败,Err:" + err.Error()
+			return
+		}
 	}
 
 	// 获取所有有权限的指标和分类
@@ -88,7 +115,9 @@ func (this *ExcelClassifyController) List() {
 		if classifyInfo, ok := classifyMap[v.ExcelClassifyId]; ok {
 			v.HaveOperaAuth = data_manage_permission.CheckExcelPermissionByPermissionIdList(v.IsJoinPermission, classifyInfo.IsJoinPermission, v.ExcelInfoId, v.ExcelClassifyId, permissionEdbIdList, permissionClassifyIdList)
 		}
-
+		if source == utils.BALANCE_TABLE && isShowMe && !v.HaveOperaAuth { // 过滤我不可编辑的表格
+			continue
+		}
 		ExcelInfoMap[v.ExcelClassifyId] = append(ExcelInfoMap[v.ExcelClassifyId], v)
 	}
 
@@ -755,8 +784,13 @@ func (this *ExcelClassifyController) DeleteExcelClassify() {
 			condition += " AND excel_classify_id=? "
 			pars = append(pars, excelInfo.ExcelClassifyId)
 
+			if excelInfo.Source == utils.BALANCE_TABLE {
+				condition += " AND parent_id=0 AND balance_type=0 "
+			}
+
 			condition += " AND (sort>? OR (sort=? AND excel_info_id<?) ) "
 			pars = append(pars, excelInfo.Sort, excelInfo.Sort, excelInfo.ExcelInfoId)
+
 			nextItem, err = excel.GetNextExcelInfoByCondition(condition, pars)
 			if err != nil && err.Error() != utils.ErrNoRow() {
 				br.Msg = "删除失败"

+ 368 - 82
controllers/data_manage/excel/excel_info.go

@@ -79,35 +79,51 @@ func (c *ExcelInfoController) Add() {
 		return
 	}
 
-	if req.ExcelClassifyId <= 0 {
+	if req.ExcelClassifyId <= 0 && req.ParentId == 0 {
 		br.Msg = "分类参数错误!"
 		br.IsSendEmail = false
 		return
 	}
 
-	excelClassify, err := excel3.GetExcelClassifyById(req.ExcelClassifyId)
-	if err != nil {
-		if err.Error() == utils.ErrNoRow() {
+	if req.ExcelClassifyId > 0 {
+		excelClassify, e := excel3.GetExcelClassifyById(req.ExcelClassifyId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "分类不存在"
+				br.ErrMsg = "分类不存在"
+				br.IsSendEmail = false
+				return
+			}
+			br.Msg = "获取分类信息失败"
+			br.ErrMsg = "获取分类信息失败,Err:" + e.Error()
+			return
+		}
+		if excelClassify == nil {
 			br.Msg = "分类不存在"
 			br.ErrMsg = "分类不存在"
 			br.IsSendEmail = false
 			return
 		}
-		br.Msg = "获取分类信息失败"
-		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
-		return
 	}
-	if excelClassify == nil {
-		br.Msg = "分类不存在"
-		br.ErrMsg = "分类不存在"
-		br.IsSendEmail = false
-		return
+
+	if req.ParentId > 0 {
+		parentExcelInfo, e := excel3.GetExcelInfoById(req.ParentId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "父级ETA表格被删除,请刷新页面"
+				return
+			}
+			br.Msg = "获取父级ETA表格信息失败"
+			br.ErrMsg = "获取父级ETA表格信息失败,Err:" + e.Error()
+			return
+		}
+		req.ExcelClassifyId = parentExcelInfo.ExcelClassifyId
 	}
 
 	var condition string
 	var pars []interface{}
-	condition += " AND excel_classify_id=? "
-	pars = append(pars, req.ExcelClassifyId)
+	condition += " AND excel_classify_id=? AND parent_id=?"
+	pars = append(pars, req.ExcelClassifyId, req.ParentId)
 
 	condition += " AND excel_name=? "
 	pars = append(pars, req.ExcelName)
@@ -167,7 +183,7 @@ func (c *ExcelInfoController) Add() {
 	}
 
 	// 混合表格
-	if req.Source == 3 {
+	if req.Source == 3 || req.Source == 5 {
 		contentByte, err := json.Marshal(req.TableData)
 		if err != nil {
 			br.Msg = "自定义表格数据获取失败"
@@ -216,19 +232,22 @@ func (c *ExcelInfoController) Add() {
 	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
 	excelInfo := &excel3.ExcelInfo{
 		//ExcelInfoId:     0,
-		ExcelName:       req.ExcelName,
-		Source:          req.Source,
-		ExcelType:       req.ExcelType,
-		UniqueCode:      utils.MD5(utils.EXCEL_DATA_PREFIX + "_" + timestamp),
-		ExcelClassifyId: req.ExcelClassifyId,
-		SysUserId:       sysUser.AdminId,
-		SysUserRealName: sysUser.RealName,
-		Content:         content,
-		ExcelImage:      req.ExcelImage,
-		Sort:            maxSort + 1,
-		IsDelete:        0,
-		ModifyTime:      time.Now(),
-		CreateTime:      time.Now(),
+		ExcelName:          req.ExcelName,
+		Source:             req.Source,
+		ExcelType:          req.ExcelType,
+		UniqueCode:         utils.MD5(utils.EXCEL_DATA_PREFIX + "_" + timestamp),
+		ExcelClassifyId:    req.ExcelClassifyId,
+		SysUserId:          sysUser.AdminId,
+		SysUserRealName:    sysUser.RealName,
+		Content:            content,
+		ExcelImage:         req.ExcelImage,
+		Sort:               maxSort + 1,
+		IsDelete:           0,
+		ModifyTime:         time.Now(),
+		CreateTime:         time.Now(),
+		ParentId:           req.ParentId,
+		UpdateUserId:       sysUser.AdminId,
+		UpdateUserRealName: sysUser.RealName,
 	}
 
 	excelEdbMappingList := make([]*excel3.ExcelEdbMapping, 0)
@@ -244,7 +263,30 @@ func (c *ExcelInfoController) Add() {
 			})
 		}
 	}
-	err = excel3.AddExcelInfo(excelInfo, excelEdbMappingList)
+	var childExcel *excel3.ExcelInfo
+	if excelInfo.Source == utils.BALANCE_TABLE && req.ParentId == 0 && excelInfo.BalanceType == 0 { //首次创建平衡表时需要添加一个默认的子表
+		timestamp = strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + utils.GetRandString(10)
+		childExcel = &excel3.ExcelInfo{
+			//ExcelInfoId:     0,
+			ExcelName:       "平衡表",
+			Source:          excelInfo.Source,
+			ExcelType:       excelInfo.ExcelType,
+			UniqueCode:      utils.MD5(utils.EXCEL_DATA_PREFIX + "_" + timestamp),
+			ExcelClassifyId: req.ExcelClassifyId,
+			SysUserId:       sysUser.AdminId,
+			SysUserRealName: sysUser.RealName,
+			Content:         excelInfo.Content,
+			//ExcelImage:         req.ExcelImage,
+			Sort:       excelInfo.Sort + 1,
+			IsDelete:   0,
+			ModifyTime: time.Now(),
+			CreateTime: time.Now(),
+			//ParentId:           req.ParentId,
+			UpdateUserId:       sysUser.AdminId,
+			UpdateUserRealName: sysUser.RealName,
+		}
+	}
+	err = excel3.AddExcelInfo(excelInfo, excelEdbMappingList, childExcel)
 	if err != nil {
 		br.Msg = "保存失败"
 		br.ErrMsg = "保存失败,Err:" + err.Error()
@@ -346,6 +388,9 @@ func (c *ExcelInfoController) List() {
 	} else {
 		condition += " AND source = ? "
 		pars = append(pars, source)
+		if source == utils.BALANCE_TABLE { //平衡表的列表只显示动态表的一级表(不显示子表和静态表)
+			condition += " AND parent_id = 0 AND balance_type=0 "
+		}
 	}
 
 	// 筛选分类
@@ -385,9 +430,95 @@ func (c *ExcelInfoController) List() {
 	}
 	//只看我的
 	isShowMe, _ := c.GetBool("IsShowMe")
+	// 获取所有有权限的指标和分类
+	permissionEdbIdList, permissionClassifyIdList, err := data_manage_permission.GetUserExcelAndClassifyPermissionList(c.SysUser.AdminId, 0, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取所有有权限的指标和分类失败,Err:" + err.Error()
+		return
+	}
+	hasCheck := make(map[int]bool)
 	if isShowMe {
-		condition += " AND sys_user_id = ? "
-		pars = append(pars, sysUser.AdminId)
+		if source == utils.BALANCE_TABLE { //平衡表的,显示同时需要显示协作人相关的图表
+			//找到当前协作人相关的表格ID
+			obj := new(excel3.ExcelWorker)
+			existList, err := obj.GetBySysUserId(sysUser.AdminId)
+			if err != nil {
+				br.Msg = "获取表格协作人失败!"
+				br.ErrMsg = "获取表格协作人失败,Err:" + err.Error()
+				return
+			}
+			var excelIds []int
+			newCondition := condition
+			newPars := pars
+			if len(existList) > 0 {
+				for _, v := range existList {
+					excelIds = append(excelIds, v.ExcelInfoId)
+				}
+				newCondition += fmt.Sprintf(` AND  ( excel_info_id IN (%s)  or sys_user_id = ?)`, utils.GetOrmInReplace(len(excelIds)))
+				newPars = append(newPars, excelIds, sysUser.AdminId)
+			} else {
+				newCondition += ` AND  sys_user_id = ? `
+				newPars = append(newPars, sysUser.AdminId)
+			}
+
+			//获取表格信息
+			tmpList, e := excel3.GetNoContentExcelListByConditionNoPage(newCondition, newPars)
+			if e != nil && e.Error() != utils.ErrNoRow() {
+				br.Success = true
+				br.Msg = "获取表格信息失败"
+				br.ErrMsg = "获取表格信息失败,Err:" + e.Error()
+				return
+			}
+			classifyIdListTmp := make([]int, 0)
+			for _, v := range tmpList {
+				classifyIdListTmp = append(classifyIdListTmp, v.ExcelClassifyId)
+			}
+			classifyMap := make(map[int]*excel3.ExcelClassify)
+
+			// 分类信息
+			if len(classifyIdListTmp) > 0 {
+				classifyListTmp, e := excel3.GetClassifyByIdList(classifyIdListTmp)
+				if e != nil {
+					br.Msg = "获取表格分类信息失败"
+					br.ErrMsg = "获取表格分类列表数据失败,Err:" + e.Error()
+					return
+				}
+				for _, v := range classifyListTmp {
+					classifyMap[v.ExcelClassifyId] = v
+				}
+			}
+			excelIds = make([]int, 0)
+			for _, v := range tmpList {
+				// 数据权限
+				if classifyInfo, ok := classifyMap[v.ExcelClassifyId]; ok {
+					v.HaveOperaAuth = data_manage_permission.CheckExcelPermissionByPermissionIdList(v.IsJoinPermission, classifyInfo.IsJoinPermission, v.ExcelInfoId, v.ExcelClassifyId, permissionEdbIdList, permissionClassifyIdList)
+					if v.HaveOperaAuth {
+						excelIds = append(excelIds, v.ExcelInfoId)
+					}
+					hasCheck[v.ExcelInfoId] = v.HaveOperaAuth
+				}
+			}
+			if len(excelIds) > 0 {
+				condition += fmt.Sprintf(` AND  excel_info_id IN (%s)`, utils.GetOrmInReplace(len(excelIds)))
+				pars = append(pars, excelIds)
+			} else {
+				list := make([]*excel3.MyExcelInfoList, 0)
+				resp := response.ExcelListResp{
+					Paging: page,
+					List:   list,
+				}
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				br.Data = resp
+				return
+			}
+
+		} else {
+			condition += " AND sys_user_id = ? "
+			pars = append(pars, sysUser.AdminId)
+		}
 	}
 	//获取表格信息
 	list, err := excel3.GetNoContentExcelListByCondition(condition, pars, startSize, pageSize)
@@ -407,6 +538,7 @@ func (c *ExcelInfoController) List() {
 		classifyIdList := make([]int, 0)
 		for _, v := range list {
 			classifyIdList = append(classifyIdList, v.ExcelClassifyId)
+
 		}
 		classifyMap := make(map[int]*excel3.ExcelClassify)
 
@@ -422,18 +554,32 @@ func (c *ExcelInfoController) List() {
 				classifyMap[v.ExcelClassifyId] = v
 			}
 		}
-		// 获取所有有权限的指标和分类
-		permissionEdbIdList, permissionClassifyIdList, err := data_manage_permission.GetUserExcelAndClassifyPermissionList(c.SysUser.AdminId, 0, 0)
-		if err != nil {
-			br.Msg = "获取失败"
-			br.ErrMsg = "获取所有有权限的指标和分类失败,Err:" + err.Error()
-			return
-		}
 
-		for _, v := range list {
+		for k, v := range list {
 			// 数据权限
-			if classifyInfo, ok := classifyMap[v.ExcelClassifyId]; ok {
-				v.HaveOperaAuth = data_manage_permission.CheckExcelPermissionByPermissionIdList(v.IsJoinPermission, classifyInfo.IsJoinPermission, v.ExcelInfoId, v.ExcelClassifyId, permissionEdbIdList, permissionClassifyIdList)
+			if authCheck, ok1 := hasCheck[v.ExcelInfoId]; ok1 {
+				v.HaveOperaAuth = authCheck
+			} else {
+				if classifyInfo, ok := classifyMap[v.ExcelClassifyId]; ok {
+					v.HaveOperaAuth = data_manage_permission.CheckExcelPermissionByPermissionIdList(v.IsJoinPermission, classifyInfo.IsJoinPermission, v.ExcelInfoId, v.ExcelClassifyId, permissionEdbIdList, permissionClassifyIdList)
+				}
+			}
+			if v.Source == utils.BALANCE_TABLE {
+				//处理按钮权限和编辑状态
+				markStatus, err := services.UpdateExcelEditMark(v.ExcelInfoId, sysUser.AdminId, 2, sysUser.RealName)
+				if err != nil {
+					br.Msg = "查询标记状态失败"
+					br.ErrMsg = "查询标记状态失败,Err:" + err.Error()
+					return
+				}
+				if markStatus.Status == 0 {
+					list[k].CanEdit = true
+				} else {
+					list[k].Editor = markStatus.Editor
+				}
+
+				// excel表格按钮权限
+				list[k].Button = excel2.GetBalanceExcelInfoOpButton(sysUser.AdminId, v.SysUserId, v.HaveOperaAuth, v.ExcelInfoId)
 			}
 		}
 
@@ -493,9 +639,18 @@ func (c *ExcelInfoController) Detail() {
 		br.ErrMsg = err.Error()
 		return
 	}
-
+	checkExcelInfoId := excelInfoId
+	if excelDetail.Source == utils.BALANCE_TABLE {
+		if excelDetail.BalanceType == 1 { // 平衡表静态表编辑状态以动态表的编辑状态为准
+			checkExcelInfoId = excelDetail.RelExcelInfoId
+		} else {
+			if excelDetail.ParentId > 0 { //  子表编辑状态以父表的编辑状态为准
+				checkExcelInfoId = excelDetail.ParentId
+			}
+		}
+	}
 	// 编辑状态
-	markStatus, err := services.UpdateExcelEditMark(excelInfoId, sysUser.AdminId, 2, sysUser.RealName)
+	markStatus, err := services.UpdateExcelEditMark(checkExcelInfoId, sysUser.AdminId, 2, sysUser.RealName)
 	if err != nil {
 		br.Msg = "查询标记状态失败"
 		br.ErrMsg = "查询标记状态失败,Err:" + err.Error()
@@ -508,7 +663,9 @@ func (c *ExcelInfoController) Detail() {
 	}
 
 	// excel表格按钮权限
-	excelDetail.Button = excel2.GetExcelInfoOpButton(sysUser, excelDetail.SysUserId, excelDetail.Source, excelDetail.HaveOperaAuth)
+	if excelDetail.Source != utils.BALANCE_TABLE {
+		excelDetail.Button = excel2.GetExcelInfoOpButton(sysUser, excelDetail.SysUserId, excelDetail.Source, excelDetail.HaveOperaAuth)
+	}
 
 	br.Ret = 200
 	br.Success = true
@@ -568,14 +725,42 @@ func (c *ExcelInfoController) Edit() {
 		return
 	}
 
-	if req.ExcelClassifyId <= 0 {
+	excelInfo, err := excel3.GetExcelInfoById(req.ExcelInfoId)
+	if err != nil {
+		br.Msg = "获取ETA表格失败"
+		br.ErrMsg = "获取ETA表格失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ExcelClassifyId <= 0 && excelInfo.ParentId == 0 {
 		br.Msg = "分类参数错误!"
 		br.IsSendEmail = false
 		return
 	}
 
+	checkExcelInfoId := excelInfo.ExcelInfoId
+	checkExcelInfo := excelInfo
+	if excelInfo.Source == utils.BALANCE_TABLE {
+		checkExcelInfoId = excelInfo.ExcelInfoId
+		if excelInfo.BalanceType == 1 {
+			checkExcelInfoId = excelInfo.RelExcelInfoId
+		} else {
+			if excelInfo.ParentId > 0 {
+				checkExcelInfoId = excelInfo.ParentId
+			}
+		}
+		if checkExcelInfoId != excelInfo.ExcelInfoId {
+			checkExcelInfo, err = excel3.GetExcelInfoById(checkExcelInfoId)
+			if err != nil {
+				br.Msg = "获取平衡表格信息失败"
+				br.ErrMsg = "获取平衡表格信息失败,Err:" + err.Error()
+				return
+			}
+		}
+	}
+
 	// 标记编辑状态
-	markRet, err := services.UpdateExcelEditMark(req.ExcelInfoId, sysUser.AdminId, 1, sysUser.RealName)
+	markRet, err := services.UpdateExcelEditMark(checkExcelInfoId, sysUser.AdminId, 1, sysUser.RealName)
 	if err != nil {
 		br.Msg = "查询标记状态失败"
 		br.ErrMsg = "查询标记状态失败,Err:" + err.Error()
@@ -587,33 +772,47 @@ func (c *ExcelInfoController) Edit() {
 		br.Data = markRet
 		return
 	}
-	excelClassify, err := excel3.GetExcelClassifyById(req.ExcelClassifyId)
-	if err != nil {
-		if err.Error() == utils.ErrNoRow() {
+	if req.ExcelClassifyId > 0 {
+		excelClassify, e := excel3.GetExcelClassifyById(req.ExcelClassifyId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "分类不存在"
+				br.ErrMsg = "分类不存在"
+				br.IsSendEmail = false
+				return
+			}
+			br.Msg = "获取分类信息失败"
+			br.ErrMsg = "获取分类信息失败,Err:" + e.Error()
+			return
+		}
+		if excelClassify == nil {
 			br.Msg = "分类不存在"
 			br.ErrMsg = "分类不存在"
 			br.IsSendEmail = false
 			return
 		}
-		br.Msg = "获取分类信息失败"
-		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
-		return
 	}
-	if excelClassify == nil {
-		br.Msg = "分类不存在"
-		br.ErrMsg = "分类不存在"
-		br.IsSendEmail = false
-		return
+	if excelInfo.ParentId > 0 {
+		parentExcelInfo, e := excel3.GetExcelInfoById(excelInfo.ParentId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "父级ETA表格被删除,请刷新页面"
+				return
+			}
+			br.Msg = "获取父级ETA表格信息失败"
+			br.ErrMsg = "获取父级ETA表格信息失败,Err:" + e.Error()
+			return
+		}
+		req.ExcelClassifyId = parentExcelInfo.ExcelClassifyId
 	}
-
 	//判断表格是否存在
 	var condition string
 	var pars []interface{}
 	condition += " AND excel_info_id != ? "
 	pars = append(pars, req.ExcelInfoId)
 
-	condition += " AND excel_classify_id=? "
-	pars = append(pars, req.ExcelClassifyId)
+	condition += " AND excel_classify_id=? AND parent_id=?"
+	pars = append(pars, req.ExcelClassifyId, excelInfo.ParentId)
 
 	condition += " AND excel_name=? "
 	pars = append(pars, req.ExcelName)
@@ -631,25 +830,22 @@ func (c *ExcelInfoController) Edit() {
 		return
 	}
 
-	excelInfo, err := excel3.GetExcelInfoById(req.ExcelInfoId)
-	if err != nil {
-		br.Msg = "获取ETA表格失败"
-		br.ErrMsg = "获取ETA表格失败,Err:" + err.Error()
-		return
-	}
-
 	// 操作权限校验
 	{
 		// 数据权限
-		haveOperaAuth, err := data_manage_permission.CheckExcelPermissionByExcelInfoId(excelInfo.ExcelInfoId, excelInfo.ExcelClassifyId, excelInfo.IsJoinPermission, c.SysUser.AdminId)
+		haveOperaAuth, err := data_manage_permission.CheckExcelPermissionByExcelInfoId(checkExcelInfo.ExcelInfoId, checkExcelInfo.ExcelClassifyId, checkExcelInfo.IsJoinPermission, c.SysUser.AdminId)
 		if err != nil {
 			br.Msg = "获取ETA表格失败"
 			br.ErrMsg = "获取ETA表格权限失败,Err:" + err.Error()
 			return
 		}
-		button := excel2.GetExcelInfoOpButton(sysUser, excelInfo.SysUserId, excelInfo.Source, haveOperaAuth)
+		var button excel3.ExcelInfoDetailButton
+		if checkExcelInfo.Source == utils.BALANCE_TABLE {
+			button = excel2.GetBalanceExcelInfoOpButton(sysUser.AdminId, checkExcelInfo.SysUserId, haveOperaAuth, checkExcelInfo.ExcelInfoId)
+		} else {
+			button = excel2.GetExcelInfoOpButton(sysUser, excelInfo.SysUserId, excelInfo.Source, haveOperaAuth)
+		}
 		if !button.OpButton {
-			br.Msg = "无操作权限"
 			br.Msg = "无操作权限"
 			br.IsSendEmail = false
 			return
@@ -659,6 +855,11 @@ func (c *ExcelInfoController) Edit() {
 	// 引用的指标id
 	edbInfoIdList := make([]int, 0)
 	content := req.Content
+	contentFlag := false
+	if req.Content != excelInfo.Content {
+		contentFlag = true
+	}
+	balanceTableData := make([][]request.MixedTableCellDataReq, 0)
 	switch excelInfo.Source {
 	case utils.TIME_TABLE: // 自定义表格
 		jsonStrByte, err := json.Marshal(req.TableData)
@@ -688,7 +889,7 @@ func (c *ExcelInfoController) Edit() {
 			return
 		}
 		content = string(contentByte)
-	case utils.MIXED_TABLE: // 混合表格
+	case utils.MIXED_TABLE, utils.BALANCE_TABLE: // 混合表格, 平衡表
 		contentByte, err := json.Marshal(req.TableData)
 		if err != nil {
 			br.Msg = "混合表格数据获取失败"
@@ -713,6 +914,7 @@ func (c *ExcelInfoController) Edit() {
 			br.ErrMsg = "获取最新的数据失败,Err:" + err.Error()
 			return
 		}
+		balanceTableData = newResult
 		edbInfoIdMap := make(map[int]int)
 		for _, tmpV := range newResult {
 			for _, v := range tmpV {
@@ -731,14 +933,15 @@ func (c *ExcelInfoController) Edit() {
 	excelInfo.ExcelType = req.ExcelType
 	excelInfo.ExcelClassifyId = req.ExcelClassifyId
 	excelInfo.ExcelImage = req.ExcelImage
+	excelInfo.UpdateUserId = sysUser.AdminId
+	excelInfo.UpdateUserRealName = sysUser.RealName
 	excelInfo.Content = content
-
 	// 自动保存时不会传缩略图,也就不更新这个字段
 	var updateExcelInfoParams []string
 	if req.ExcelImage != "" {
-		updateExcelInfoParams = []string{"ModifyTime", "ExcelName", "ExcelType", "ExcelClassifyId", "ExcelImage", "Content"}
+		updateExcelInfoParams = []string{"ModifyTime", "ExcelName", "ExcelType", "ExcelClassifyId", "ExcelImage", "Content", "UpdateUserId", "UpdateUserRealName"}
 	} else {
-		updateExcelInfoParams = []string{"ModifyTime", "ExcelName", "ExcelType", "ExcelClassifyId", "Content"}
+		updateExcelInfoParams = []string{"ModifyTime", "ExcelName", "ExcelType", "ExcelClassifyId", "Content", "UpdateUserId", "UpdateUserRealName"}
 	}
 
 	excelEdbMappingList := make([]*excel3.ExcelEdbMapping, 0)
@@ -766,7 +969,15 @@ func (c *ExcelInfoController) Edit() {
 	if excelInfo.Source == 1 {
 		go excel2.UpdateExcelInfoFileUrl(excelInfo)
 	}
-
+	// 更新平衡表图表指标
+	if excelInfo.Source == utils.BALANCE_TABLE && excelInfo.BalanceType == 1 && contentFlag == true { //静态表更新表数据
+		err = excel2.SyncBalanceEdbData(excelInfo, balanceTableData)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = err.Error()
+			return
+		}
+	}
 	// 加入草稿
 	{
 		excelDraftInfo := &excel3.ExcelDraft{
@@ -1048,7 +1259,7 @@ func (c *ExcelInfoController) Delete() {
 		var nextItem *excel3.ExcelInfo
 		var condition string
 		var pars []interface{}
-		condition += " AND excel_classify_id=? "
+		condition += " AND excel_classify_id=? AND parent_id=0"
 		pars = append(pars, excelInfo.ExcelClassifyId)
 
 		condition += " AND sort>=? "
@@ -1915,13 +2126,25 @@ func (c *ExcelInfoController) Refresh() {
 	}
 
 	excelInfoId, _ := c.GetInt("ExcelInfoId")
-	if excelInfoId <= 0 {
+	chartInfoId, _ := c.GetInt("ChartInfoId")
+	if excelInfoId <= 0 && chartInfoId <= 0 {
 		br.Msg = "请选择表格"
 		br.ErrMsg = "ExcelInfoId未传"
 		br.IsSendEmail = false
 		return
 	}
 
+	// todo 如果请求入参是chart_info_id,则需要获取excel_info_id
+	if chartInfoId > 0 && excelInfoId == 0 {
+		excelInfo, err := excel3.GetExcelInfoByChartInfoId(chartInfoId)
+		if err != nil {
+			br.Msg = "请选择表格"
+			br.ErrMsg = "未找到对应的表格"
+			br.IsSendEmail = false
+			return
+		}
+		excelInfoId = excelInfo.ExcelInfoId
+	}
 	// 获取数据详情
 	excelDetail, errMsg, err := excel2.GetExcelDetailInfoByExcelInfoId(excelInfoId, c.SysUser.AdminId, c.Lang)
 	if err != nil {
@@ -1932,7 +2155,10 @@ func (c *ExcelInfoController) Refresh() {
 
 	// 操作权限校验
 	{
-		button := excel2.GetExcelInfoOpButton(sysUser, excelDetail.SysUserId, excelDetail.Source, excelDetail.HaveOperaAuth)
+		button := excelDetail.Button
+		if excelDetail.Source != utils.BALANCE_TABLE {
+			button = excel2.GetExcelInfoOpButton(sysUser, excelDetail.SysUserId, excelDetail.Source, excelDetail.HaveOperaAuth)
+		}
 		if !button.RefreshButton {
 			br.Msg = "无操作权限"
 			br.IsSendEmail = false
@@ -1968,6 +2194,7 @@ func (c *ExcelInfoController) Refresh() {
 
 	// 数据刷新-混合表格
 	if excelDetail.Source == utils.MIXED_TABLE {
+		// todo 刷新动态表的所有子表中关联的指标数据
 		jsonByte, e := json.Marshal(excelDetail.TableData)
 		if e != nil {
 			br.Msg = "刷新失败"
@@ -2002,6 +2229,15 @@ func (c *ExcelInfoController) Refresh() {
 		}
 	}
 
+	if excelDetail.Source == utils.BALANCE_TABLE {
+		err = refreshBalanceTable(excelDetail, c.Lang)
+		if err != nil {
+			br.Msg = "刷新失败"
+			br.ErrMsg = "刷新失败,Err:" + err.Error()
+			return
+		}
+	}
+
 	// 清除缓存
 	key := utils.HZ_CHART_LIB_EXCEL_TABLE_DETAIL + ":" + excelDetail.UniqueCode
 	if utils.Re == nil {
@@ -2114,6 +2350,26 @@ func (c *ExcelInfoController) Download() {
 			br.ErrMsg = "转换成table失败,Err:" + err.Error()
 			return
 		}
+	case utils.BALANCE_TABLE: // 混合表格
+		savePath, fileName, uploadDir, err, errMsg := downloadBalanceTable(excelInfo, c.Lang)
+		if err != nil {
+			br.Msg = "下载失败"
+			if errMsg != `` {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = "获取最新的数据失败,Err:" + err.Error()
+			return
+		}
+		defer func() {
+			_ = os.RemoveAll(uploadDir)
+		}()
+		// todo 删除文件
+		c.Ctx.Output.Download(savePath, fileName)
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = excelInfo
+		return
 	}
 
 	downloadFilePath, err := tableData.ToExcel()
@@ -2188,8 +2444,22 @@ func (c *ExcelInfoController) Copy() {
 		br.IsSendEmail = false
 		return
 	}
-
-	excelInfo, err, errMsg, isSendEmail := excel2.Copy(req.ExcelInfoId, req.ExcelClassifyId, req.ExcelName, sysUser)
+	// 获取原ETA表格信息
+	oldExcelInfo, err := excel3.GetExcelInfoById(req.ExcelInfoId)
+	if err != nil {
+		br.Msg = "获取ETA表格失败"
+		return
+	}
+	var (
+		excelInfo   *excel3.ExcelInfo
+		errMsg      string
+		isSendEmail bool
+	)
+	if oldExcelInfo.Source == utils.BALANCE_TABLE {
+		excelInfo, err, errMsg, isSendEmail = data.CopyBalanceExcel(oldExcelInfo, req.ExcelClassifyId, req.ExcelName, sysUser)
+	} else {
+		excelInfo, err, errMsg, isSendEmail = excel2.Copy(oldExcelInfo, req.ExcelClassifyId, req.ExcelName, sysUser)
+	}
 	if err != nil {
 		br.Msg = "复制失败"
 		if errMsg != `` {
@@ -2424,6 +2694,14 @@ func (c *ExcelInfoController) BatchRefresh() {
 		br.Msg = "刷新成功"
 		return
 	}
+	if req.Source == "" {
+		br.Msg = "刷新来源有误"
+		return
+	}
+	if req.PrimaryId <= 0 {
+		br.Msg = "刷新对象有误"
+		return
+	}
 
 	// 获取表格关联的指标IDs
 	edbIds, e := excel2.GetEdbIdsFromExcelCodes(req.ExcelCodes, sysUser.AdminId, c.Lang)
@@ -2433,7 +2711,7 @@ func (c *ExcelInfoController) BatchRefresh() {
 		return
 	}
 
-	redisKey := data.GetBatchChartRefreshKey(req.Source, req.ReportId, req.ReportChapterId)
+	redisKey := data.GetBatchChartRefreshKey(req.Source, req.PrimaryId, req.SubId)
 	refreshKeys := make([]string, 0)
 	for _, v := range req.ExcelCodes {
 		refreshKeys = append(refreshKeys, fmt.Sprint(utils.HZ_CHART_LIB_EXCEL_TABLE_DETAIL, v))
@@ -2481,10 +2759,18 @@ func (c *ExcelInfoController) GetBatchChartRefreshResult() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
+	if req.Source == "" {
+		br.Msg = "刷新来源有误"
+		return
+	}
+	if req.PrimaryId <= 0 {
+		br.Msg = "刷新对象有误"
+		return
+	}
 
 	// 校验缓存是否存在, 存在说明还在刷新中
 	result := true
-	redisKey := excel2.GetExcelEdbBatchRefreshKey(req.Source, req.ReportId, req.ReportChapterId)
+	redisKey := excel2.GetExcelEdbBatchRefreshKey(req.Source, req.PrimaryId, req.SubId)
 	if redisKey != `` {
 		// 如果找到了key,那么就是还在更新中
 		ok := utils.Rc.IsExist(redisKey)

+ 2 - 0
controllers/data_manage/my_chart.go

@@ -1357,6 +1357,8 @@ func (this *MyChartController) MyChartList() {
 			// 数据权限
 			if currClassify, ok := classifyMap[chartViewInfo.ChartClassifyId]; ok {
 				list[i].HaveOperaAuth = data_manage_permission.CheckChartPermissionByPermissionIdList(chartViewInfo.IsJoinPermission, currClassify.IsJoinPermission, chartViewInfo.ChartInfoId, chartViewInfo.ChartClassifyId, permissionChartIdList, permissionClassifyIdList)
+			} else if chartViewInfo.ChartClassifyId == 0 {
+				list[i].HaveOperaAuth = data_manage_permission.CheckChartPermissionByPermissionIdList(chartViewInfo.IsJoinPermission, 0, chartViewInfo.ChartInfoId, chartViewInfo.ChartClassifyId, permissionChartIdList, permissionClassifyIdList)
 			}
 		}
 

+ 3 - 2
controllers/data_manage/predict_edb_classify.go

@@ -302,7 +302,7 @@ func (this *PredictEdbClassifyController) DeleteCheck() {
 		return
 	}
 
-	deleteStatus, tipsMsg, err, errMsg := data.DeleteCheck(req.ClassifyId, req.EdbInfoId, this.SysUser)
+	deleteStatus, tipsMsg, tableList, err, errMsg := data.DeleteCheck(req.ClassifyId, req.EdbInfoId, this.SysUser)
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg
@@ -317,6 +317,7 @@ func (this *PredictEdbClassifyController) DeleteCheck() {
 	resp := new(data_manage.ClassifyDeleteCheckResp)
 	resp.DeleteStatus = deleteStatus
 	resp.TipsMsg = tipsMsg
+	resp.TableList = tableList
 	br.Ret = 200
 	br.Msg = "检测成功"
 	br.Success = true
@@ -356,7 +357,7 @@ func (this *PredictEdbClassifyController) Delete() {
 		return
 	}
 
-	nextItem, err, errMsg := data.Delete(req.ClassifyId, req.EdbInfoId, sysUser, string(this.Ctx.Input.RequestBody), this.Ctx.Input.URI())
+	nextItem, _, err, errMsg := data.Delete(req.ClassifyId, req.EdbInfoId, sysUser, string(this.Ctx.Input.RequestBody), this.Ctx.Input.URI())
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg

+ 75 - 9
controllers/ppt_english.go

@@ -8,7 +8,6 @@ import (
 	"eta/eta_api/services"
 	"eta/eta_api/services/ppt"
 	"eta/eta_api/utils"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	_ "image/gif"
 	_ "image/jpeg"
 	_ "image/png"
@@ -17,6 +16,8 @@ import (
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // PptEnglishController 新版ppt模块
@@ -277,6 +278,34 @@ func (this *PptEnglishController) EditPpt() {
 		return
 	}
 
+	pptMap, err := ppt_english.GetPptMappingByPptId(req.PptId)
+	if err != nil {
+		br.Msg = `该PPT信息不存在, 保存失败`
+		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+		br.IsSendEmail = false
+		return
+	}
+	pptMapList, err := ppt_english.GetPptMappingListByGroupIdDesc(pptMap.GroupId)
+	if err != nil {
+		br.ErrMsg = "PPT目录信息异常"
+		return
+	}
+	count, err := ppt_english.GetPptMappingCountByGroupPptId(pptMap.GroupPptId, this.SysUser.AdminId)
+	if err != nil {
+		br.Msg = "获取英文PPT和用户权限关系失败"
+		br.ErrMsg = "获取英文PPT和用户权限关系失败, Err:" + err.Error()
+		return
+	}
+	if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
+		// 如果没有人为移动位置, 默认将当前ppt置顶
+		err = ppt.MoveGroupPptEnglish(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+		if err != nil {
+			br.Msg = err.Error()
+			br.ErrMsg = "移动失败,Err:" + err.Error()
+			return
+		}
+	}
+
 	// 日志记录
 	{
 		logInfo := &ppt_english.PptEnglishSaveLog{
@@ -638,6 +667,13 @@ func (this *PptEnglishController) SaveLog() {
 		br.Msg = "标题不能为空"
 		return
 	}
+	var pageContent []services.PPTContent
+	err = json.Unmarshal([]byte(req.Content), &pageContent)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
 
 	// 获取ppt
 	item, err := ppt_english.GetPptEnglishByTitleAndId(req.FirstPage.Title, this.SysUser.AdminId)
@@ -673,14 +709,44 @@ func (this *PptEnglishController) SaveLog() {
 			return
 		}
 	}
-	//pptInfo.TemplateType = req.FirstPage.TemplateType
-	//pptInfo.BackgroundImg = req.FirstPage.ImgUrl
-	//pptInfo.Title = req.FirstPage.Title
-	//pptInfo.ReportType = req.FirstPage.ReportType
-	//pptInfo.PptDate = req.FirstPage.PptDate
-	//pptInfo.Content = req.Content
-	//pptInfo.ModifyTime = time.Now()
-	//err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime"})
+
+	pptItem.TemplateType = req.FirstPage.TemplateType
+	pptItem.BackgroundImg = req.FirstPage.ImgUrl
+	pptItem.Title = req.FirstPage.Title
+	pptItem.ReportType = req.FirstPage.ReportType
+	pptItem.PptDate = req.FirstPage.PptDate
+	pptItem.Content = req.Content
+	pptItem.ModifyTime = time.Now()
+	pptItem.PptPage = len(pageContent)
+	err = pptItem.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "ppt_page"})
+
+	pptMap, err := ppt_english.GetPptMappingByPptId(req.PptId)
+	if err != nil {
+		br.Msg = `该PPT信息不存在, 保存失败`
+		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+		br.IsSendEmail = false
+		return
+	}
+	pptMapList, err := ppt_english.GetPptMappingListByGroupIdDesc(pptMap.GroupId)
+	if err != nil {
+		br.ErrMsg = "PPT目录信息异常"
+		return
+	}
+	count, err := ppt_english.GetPptMappingCountByGroupPptId(pptMap.GroupPptId, this.SysUser.AdminId)
+	if err != nil {
+		br.Msg = "获取英文PPT和用户权限关系失败"
+		br.ErrMsg = "获取英文PPT和用户权限关系失败, Err:" + err.Error()
+		return
+	}
+	if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
+		// 如果没有人为移动位置, 且当前用户有权限, 默认将当前ppt置顶
+		err = ppt.MoveGroupPptEnglish(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+		if err != nil {
+			br.Msg = err.Error()
+			br.ErrMsg = "移动失败,Err:" + err.Error()
+			return
+		}
+	}
 
 	//日志记录
 	logInfo := &ppt_english.PptEnglishSaveLog{

+ 68 - 2
controllers/ppt_v2.go

@@ -9,7 +9,6 @@ import (
 	"eta/eta_api/services/ppt"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	_ "image/gif"
 	_ "image/jpeg"
 	_ "image/png"
@@ -18,6 +17,8 @@ import (
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // PptV2Controller 新版ppt模块
@@ -278,6 +279,34 @@ func (this *PptV2Controller) EditPpt() {
 		return
 	}
 
+	pptMap, err := models.GetPptMappingByPptId(req.PptId)
+	if err != nil {
+		br.Msg = `该PPT信息不存在, 保存失败`
+		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+		br.IsSendEmail = false
+		return
+	}
+	pptMapList, err := models.GetPptMappingListByGroupId(pptMap.GroupId)
+	if err != nil {
+		br.ErrMsg = "PPT目录信息异常"
+		return
+	}
+	count, err := models.GetPptMappingByGroupPptCountId(pptMap.GroupPptId, this.SysUser.AdminId)
+	if err != nil {
+		br.Msg = "查询映射关系失败"
+		br.ErrMsg = "查询映射关系失败, 保存失败, Err:" + err.Error()
+		return
+	}
+	if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
+		// 如果没有人为移动位置, 默认将当前ppt置顶
+		err = ppt.MoveGroupPpt(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+		if err != nil {
+			br.Msg = err.Error()
+			br.ErrMsg = "移动失败,Err:" + err.Error()
+			return
+		}
+	}
+
 	// 日志记录
 	{
 		logInfo := &models.PptV2SaveLog{
@@ -608,6 +637,13 @@ func (this *PptV2Controller) SaveLog() {
 		br.Msg = "标题不能为空"
 		return
 	}
+	var pptContent []services.PPTContent
+	err = json.Unmarshal([]byte(req.Content), &pptContent)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
 
 	// 获取ppt
 	item, err := models.GetPptV2ByTitleAndId(req.FirstPage.Title, this.SysUser.AdminId)
@@ -651,7 +687,37 @@ func (this *PptV2Controller) SaveLog() {
 	pptItem.Content = req.Content
 	pptItem.ModifyTime = time.Now()
 	pptItem.TitleSetting = req.TitleSetting
-	err = pptItem.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "TitleSetting"})
+	pptItem.PptPage = len(pptContent)
+	err = pptItem.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "TitleSetting", "ppt_page"})
+
+	// 将更新后的PPT, 置顶
+	pptMap, err := models.GetPptMappingByPptId(int64(req.PptId))
+	if err != nil {
+		br.Msg = `该PPT信息不存在, 保存失败`
+		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+		br.IsSendEmail = false
+		return
+	}
+	pptMapList, err := models.GetPptMappingListByGroupId(pptMap.GroupId)
+	if err != nil {
+		br.ErrMsg = "PPT目录信息异常"
+		return
+	}
+	count, err := models.GetPptMappingByGroupPptCountId(pptMap.GroupPptId, this.SysUser.AdminId)
+	if err != nil {
+		br.Msg = "查询映射关系失败"
+		br.ErrMsg = "查询映射关系失败, 保存失败, Err:" + err.Error()
+		return
+	}
+	if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
+		// 如果没有人为移动位置, 且当前用户有权限, 默认将当前ppt置顶
+		err = ppt.MoveGroupPpt(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+		if err != nil {
+			br.Msg = err.Error()
+			br.ErrMsg = "移动失败,Err:" + err.Error()
+			return
+		}
+	}
 
 	//日志记录
 	logInfo := &models.PptV2SaveLog{

+ 28 - 6
controllers/sandbox/sandbox.go

@@ -1029,7 +1029,7 @@ func (this *SandboxController) EditSandboxClassify() {
 		br.ErrMsg = "查询子级分类id失败,Err:" + err.Error()
 		return
 	}
-	err  = sandbox.UpdateSandboxClassifyChartPermissionById(req.ChartPermissionId, req.ChartPermissionName, ids)
+	err = sandbox.UpdateSandboxClassifyChartPermissionById(req.ChartPermissionId, req.ChartPermissionName, ids)
 	if err != nil {
 		br.Msg = "修改子级分类错误"
 		br.ErrMsg = "修改子级分类错误,Err:" + err.Error()
@@ -1646,7 +1646,7 @@ func (this *SandboxController) ChartClassifyMove() {
 					br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
 					return
 				}
-				err  = sandbox.UpdateSandboxClassifyChartPermissionById(parentChartClassifyInfo.ChartPermissionId, parentChartClassifyInfo.ChartPermissionName, ids)
+				err = sandbox.UpdateSandboxClassifyChartPermissionById(parentChartClassifyInfo.ChartPermissionId, parentChartClassifyInfo.ChartPermissionName, ids)
 				if err != nil {
 					br.Msg = "修改子级分类错误"
 					br.ErrMsg = "修改子级分类错误,Err:" + err.Error()
@@ -2376,8 +2376,15 @@ func (this *SandboxController) LinkEdbInfoCheck() {
 		br.ErrMsg = "获取指标信息失败,err:" + err.Error()
 		return
 	}
+	edbList := make([]*sandbox.SandboxLinkCheckItem, 0)
 	for _, v := range edbInfoList {
-		resp.EdbInfoIdList = append(resp.EdbInfoIdList, v.EdbInfoId)
+		tmp := &sandbox.SandboxLinkCheckItem{
+			Id:         v.EdbInfoId,
+			Name:       v.EdbName,
+			UniqueCode: v.UniqueCode,
+			ClassifyId: v.ClassifyId,
+		}
+		edbList = append(edbList, tmp)
 	}
 
 	chartList, err := data_manage.GetChartInfoByIdList(req.ChartInfoIdList)
@@ -2386,8 +2393,15 @@ func (this *SandboxController) LinkEdbInfoCheck() {
 		br.ErrMsg = `获取图表列表失败,ERR:` + err.Error()
 		return
 	}
+	chartListTmp := make([]*sandbox.SandboxLinkCheckItem, 0)
 	for _, v := range chartList {
-		resp.ChartInfoIdList = append(resp.ChartInfoIdList, v.ChartInfoId)
+		tmp := &sandbox.SandboxLinkCheckItem{
+			Id:         v.ChartInfoId,
+			Name:       v.ChartName,
+			UniqueCode: v.UniqueCode,
+			ClassifyId: v.ChartClassifyId,
+		}
+		chartListTmp = append(chartListTmp, tmp)
 	}
 
 	reportList, err := models.GetSimpleReportByIds(req.ReportIdList)
@@ -2396,10 +2410,18 @@ func (this *SandboxController) LinkEdbInfoCheck() {
 		br.ErrMsg = `获取报告列表失败,ERR:` + err.Error()
 		return
 	}
+	reportListTmp := make([]*sandbox.SandboxLinkCheckItem, 0)
 	for _, v := range reportList {
-		resp.ReportIdList = append(resp.ReportIdList, v.Id)
+		tmp := &sandbox.SandboxLinkCheckItem{
+			Id:         v.Id,
+			Name:       v.Title,
+			UniqueCode: v.ReportCode,
+		}
+		reportListTmp = append(reportListTmp, tmp)
 	}
-
+	resp.EdbInfoIdList = edbList
+	resp.ChartInfoIdList = chartListTmp
+	resp.ReportIdList = reportListTmp
 	br.Ret = 200
 	br.Msg = "检测成功"
 	br.Success = true

+ 144 - 0
models/data_manage/base_from_ccf.go

@@ -0,0 +1,144 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type BaseFromCCFIndex struct {
+	BaseFromCcfIndexId int `orm:"column(base_from_ccf_index_id);pk"`
+	ClassifyId         int
+	IndexCode          string
+	IndexName          string
+	Frequency          string
+	Unit               string
+	Sort               int
+	CreateTime         time.Time
+	ModifyTime         time.Time
+}
+
+type BaseFromCCFIndexList struct {
+	BaseFromCcfIndexId int `orm:"column(base_from_ccf_index_id);pk"`
+	ClassifyId         int
+	IndexCode          string
+	IndexName          string
+	Frequency          string
+	Unit               string
+	Sort               int
+	CreateTime         string
+	ModifyTime         string
+	DataList           []*BaseFromCCFData
+	Paging             *paging.PagingItem `description:"分页数据"`
+}
+
+type CCFSingleDataResp struct {
+	BaseFromCcfIndexId int
+	ClassifyId         int
+	IndexCode          string
+	IndexName          string
+	Frequency          string
+	Unit               string
+	CreateTime         string
+	ModifyTime         string
+	Data               []*CCFSingleData
+}
+
+type CCFSingleData struct {
+	Value    string `orm:"column(value)" description:"日期"`
+	DataTime string `orm:"column(data_time)" description:"值"`
+}
+
+func GetCCFIndex(condition string, pars interface{}) (items []*BaseFromCCFIndexList, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_ccf_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC, base_from_ccf_index_id asc`
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func GetCCFIndexDataCount(indexCode string) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count FROM base_from_ccf_data WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&count)
+	return
+}
+
+type CCFIndexDataCountGroup struct {
+	IndexCode string
+	Count     int
+}
+
+func GetCCFIndexDataCountGroup(indexCodes []string) (items []*CCFIndexDataCountGroup, err error) {
+	if len(indexCodes) <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count, index_code FROM base_from_ccf_data WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodes)) + `) GROUP BY index_code`
+	_, err = o.Raw(sql, indexCodes).QueryRows(&items)
+	return
+}
+
+func GetCCFIndexData(indexCode string, startSize, pageSize int) (items []*BaseFromCCFData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_ccf_data WHERE index_code=? ORDER BY data_time DESC LIMIT ?,? `
+	_, err = o.Raw(sql, indexCode, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetCCFIndexDataByCodes(indexCode []string) (items []*BaseFromCCFData, err error) {
+	if len(indexCode) <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_ccf_data WHERE index_code in (` + utils.GetOrmInReplace(len(indexCode)) + `) ORDER BY data_time DESC  `
+	_, err = o.Raw(sql, indexCode).QueryRows(&items)
+	return
+}
+
+type BaseFromCCFData struct {
+	BaseFromCcfDataId  int `orm:"column(base_from_ccf_data_id);pk"`
+	BaseFromCcfIndexId int
+	IndexCode          string
+	DataTime           string
+	Value              string
+	CreateTime         string
+	ModifyTime         string
+	DataTimestamp      int64
+}
+
+type BaseFromCCFIndexSearchItem struct {
+	BaseFromCcfIndexId int `orm:"column(base_from_ccf_index_id);pk"`
+	ClassifyId         int
+	IndexCode          string
+	IndexName          string
+}
+
+// GetCCFItemList 模糊查询CCF数据库指标列表
+func GetCCFItemList(condition string) (items []*BaseFromCCFIndexSearchItem, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM base_from_ccf_index WHERE 1=1"
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetCCFIndexDataByCode(indexCode string) (list []*BaseFromCCFData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_ccf_data WHERE index_code=? `
+	_, err = o.Raw(sql, indexCode).QueryRows(&list)
+	return
+}
+
+func GetBaseFromCCFIndexByIndexCode(indexCode string) (list *BaseFromCCFIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_ccf_index WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&list)
+	return
+}

+ 55 - 0
models/data_manage/base_from_ccf_classify.go

@@ -0,0 +1,55 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// BaseFromCCFClassify CCF原始数据分类表
+type BaseFromCCFClassify struct {
+	BaseFromCcfClassifyId int       `orm:"column(base_from_ccf_classify_id);pk"`
+	ClassifyName          string    `description:"分类名称"`
+	ParentId              int       `description:"父级id"`
+	SysUserId             int       `description:"创建人id"`
+	SysUserRealName       string    `description:"创建人姓名"`
+	Level                 int       `description:"层级"`
+	Sort                  int       `description:"排序字段,越小越靠前,默认值:10"`
+	ModifyTime            time.Time `description:"修改时间"`
+	CreateTime            time.Time `description:"创建时间"`
+}
+
+// GetBaseFromCCFClassify 获取所有分类
+func GetBaseFromCCFClassify() (items []*BaseFromCCFClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_ccf_classify ORDER BY parent_id ASC, sort ASC, base_from_ccf_classify_id ASC`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// BaseFromCCFClassifyItem CCF数据分类信息
+type BaseFromCCFClassifyItem struct {
+	ClassifyId   int                        `description:"分类ID"`
+	ClassifyName string                     `description:"分类名称"`
+	ParentId     int                        `description:"父级id"`
+	Level        int                        `description:"层级"`
+	Sort         int                        `description:"排序字段"`
+	CreateTime   string                     `description:"创建时间"`
+	ModifyTime   string                     `description:"修改时间"`
+	Child        []*BaseFromCCFClassifyItem `description:"子分类"`
+}
+
+func (y *BaseFromCCFClassify) Format2Item(origin *BaseFromCCFClassify) (item *BaseFromCCFClassifyItem) {
+	if origin == nil {
+		return
+	}
+	item = new(BaseFromCCFClassifyItem)
+	item.ClassifyId = origin.BaseFromCcfClassifyId
+	item.ClassifyName = origin.ClassifyName
+	item.ParentId = origin.ParentId
+	item.Level = origin.Level
+	item.Sort = origin.Sort
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}

+ 3 - 3
models/data_manage/base_from_trade_index.go

@@ -121,7 +121,7 @@ func GetBaseFromTradeIndexByParam(exchange, date, classifyName, classifyType str
 		_, err = o.Raw(sql, date).QueryRows(&list)
 		return
 	} else if classifyType == "" {
-		sql := "SELECT * FROM base_from_trade_" + exchange + "_index where data_time=? and classify_name=? ORDER BY classify_type,rank"
+		sql := "SELECT * FROM base_from_trade_" + exchange + "_index where data_time=? and classify_name=? ORDER BY classify_type,`rank`"
 		_, err = o.Raw(sql, date, classifyName).QueryRows(&list)
 		return
 	} else {
@@ -676,14 +676,14 @@ func GetSSOAndFacEicDateV2(date, name string) (data []*BaseFromTradeEicIndexV2,
 
 func GetBaseFromTradeIndexByDate(exchange, startDate, endDate string) (list []*BaseFromTradeShanghaiIndex, err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := "SELECT * FROM base_from_trade_" + exchange + "_index where rank < 50 and data_time between ? and ? order by data_time asc"
+	sql := "SELECT * FROM base_from_trade_" + exchange + "_index where `rank` < 50 and data_time between ? and ? order by data_time asc"
 	_, err = o.Raw(sql, startDate, endDate).QueryRows(&list)
 	return
 }
 
 func GetFirstBaseFromTradeIndexByDate(exchange string) (item *BaseFromTradeShanghaiIndex, err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := "SELECT * FROM base_from_trade_" + exchange + "_index where rank < 50 order by data_time asc"
+	sql := "SELECT * FROM base_from_trade_" + exchange + "_index where `rank` < 50 order by data_time asc"
 	err = o.Raw(sql).QueryRow(&item)
 	return
 }

+ 155 - 0
models/data_manage/ccf_stock_classify.go

@@ -0,0 +1,155 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// CCFStockClassify CCF化纤-装置分类
+type CCFStockClassify struct {
+	CcfStockClassifyId int       `orm:"column(ccf_stock_classify_id);pk"`
+	ClassifyName       string    `description:"分类名称"`
+	CreateTime         time.Time `description:"创建时间"`
+	ModifyTime         time.Time `description:"修改时间"`
+}
+
+func (m *CCFStockClassify) TableName() string {
+	return "ccf_stock_classify"
+}
+
+type CCFStockClassifyCols struct {
+	CcfStockClassifyId string
+	ClassifyName       string
+	CreateTime         string
+	ModifyTime         string
+}
+
+func (m *CCFStockClassify) Cols() CCFStockClassifyCols {
+	return CCFStockClassifyCols{
+		CcfStockClassifyId: "ccf_stock_classify_id",
+		ClassifyName:       "classify_name",
+		CreateTime:         "create_time",
+		ModifyTime:         "modify_time",
+	}
+}
+
+func (m *CCFStockClassify) PrimaryId() string {
+	return m.Cols().CcfStockClassifyId
+}
+
+func (m *CCFStockClassify) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.CcfStockClassifyId = int(id)
+	return
+}
+
+func (m *CCFStockClassify) CreateMulti(items []*CCFStockClassify) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *CCFStockClassify) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *CCFStockClassify) Del() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.CcfStockClassifyId).Exec()
+	return
+}
+
+func (m *CCFStockClassify) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *CCFStockClassify) GetItemById(id int) (item *CCFStockClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *CCFStockClassify) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *CCFStockClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *CCFStockClassify) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *CCFStockClassify) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*CCFStockClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *CCFStockClassify) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*CCFStockClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// CCFStockClassifyItem CCF化纤-装置分类
+type CCFStockClassifyItem struct {
+	ClassifyId   int    `description:"分类ID"`
+	ClassifyName string `description:"分类名称"`
+	CreateTime   string `description:"创建时间"`
+	ModifyTime   string `description:"修改时间"`
+}
+
+func (m *CCFStockClassify) Format2Item() (item *CCFStockClassifyItem) {
+	item = new(CCFStockClassifyItem)
+	item.ClassifyId = m.CcfStockClassifyId
+	item.ClassifyName = m.ClassifyName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, m.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, m.ModifyTime)
+	return
+}

+ 171 - 0
models/data_manage/ccf_stock_excel.go

@@ -0,0 +1,171 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"html"
+	"regexp"
+	"strings"
+	"time"
+)
+
+// CCFStockExcel CCF化纤装置表格
+type CCFStockExcel struct {
+	CcfStockExcelId int       `orm:"column(ccf_stock_excel_id);pk"`
+	ClassifyId      int       `description:"分类ID"`
+	ExcelDate       time.Time `description:"表格日期"`
+	ExcelContent    string    `description:"表格HTML"`
+	FromPage        string    `description:"表格来源"`
+	CreateTime      time.Time `description:"创建时间"`
+	ModifyTime      time.Time `description:"修改时间"`
+}
+
+func (m *CCFStockExcel) TableName() string {
+	return "ccf_stock_excel"
+}
+
+type CCFStockExcelCols struct {
+	CcfStockExcelId string
+	ClassifyId      string
+	ExcelDate       string
+	ExcelContent    string
+	FromPage        string
+	CreateTime      string
+	ModifyTime      string
+}
+
+func (m *CCFStockExcel) Cols() CCFStockExcelCols {
+	return CCFStockExcelCols{
+		CcfStockExcelId: "ccf_stock_excel_id",
+		ClassifyId:      "classify_id",
+		ExcelDate:       "excel_date",
+		ExcelContent:    "excel_content",
+		FromPage:        "from_page",
+		CreateTime:      "create_time",
+		ModifyTime:      "modify_time",
+	}
+}
+
+func (m *CCFStockExcel) PrimaryId() string {
+	return m.Cols().CcfStockExcelId
+}
+
+func (m *CCFStockExcel) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.CcfStockExcelId = int(id)
+	return
+}
+
+func (m *CCFStockExcel) CreateMulti(items []*CCFStockExcel) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *CCFStockExcel) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *CCFStockExcel) Del() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.CcfStockExcelId).Exec()
+	return
+}
+
+func (m *CCFStockExcel) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *CCFStockExcel) GetItemById(id int) (item *CCFStockExcel, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *CCFStockExcel) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *CCFStockExcel, err error) {
+	o := orm.NewOrmUsingDB("data")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *CCFStockExcel) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *CCFStockExcel) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*CCFStockExcel, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *CCFStockExcel) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*CCFStockExcel, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+type CCFStockExcelItem struct {
+	ExcelId      int    `description:"表格ID"`
+	ClassifyId   int    `description:"分类ID"`
+	ExcelDate    string `description:"表格日期"`
+	ExcelContent string `description:"表格HTML"`
+	CreateTime   string `description:"创建时间"`
+	ModifyTime   string `description:"修改时间"`
+}
+
+func (m *CCFStockExcel) Format2Item() (item *CCFStockExcelItem) {
+	item = new(CCFStockExcelItem)
+	item.ExcelId = m.CcfStockExcelId
+	item.ClassifyId = m.ClassifyId
+	item.ExcelDate = utils.TimeTransferString(utils.FormatDate, m.ExcelDate)
+	content := html.UnescapeString(m.ExcelContent)
+	content = regexp.MustCompile(`\n`).ReplaceAllString(content, "")
+	item.ExcelContent = html.UnescapeString(content)
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, m.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, m.ModifyTime)
+	return
+}

+ 9 - 0
models/data_manage/chart_edb_mapping.go

@@ -210,6 +210,15 @@ func GetChartEdbMappingByEdbInfoId(edbInfoId int) (item *ChartEdbInfoMapping, er
 	return
 }
 
+// GetEdbMappingListByEdbInfoId 根据指标id获取edb_mapping
+func GetEdbMappingListByEdbInfoId(edbInfoId int) (item []*ChartEdbInfoMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM chart_edb_mapping
+			 WHERE edb_info_id = ? `
+	_, err = o.Raw(sql, edbInfoId).QueryRows(&item)
+	return
+}
+
 // GetChartEdbMappingByFutureGoodEdbInfoId 根据指标id获取edb_mapping
 func GetChartEdbMappingByFutureGoodEdbInfoId(edbInfoId int) (item *ChartEdbInfoMapping, err error) {
 	o := orm.NewOrmUsingDB("data")

+ 25 - 4
models/data_manage/chart_info.go

@@ -701,6 +701,21 @@ type ChartInfoDetailResp struct {
 	CorrelationChartInfo *CorrelationInfo `description:"相关性图表信息"`
 	DataResp             interface{}      `description:"图表数据,根据图的类型而定的,没有确定的数据格式"`
 }
+type BalanceTableChartListResp struct {
+	List []*BalanceChartInfoDetailResp
+}
+
+type BalanceChartInfoDetailResp struct {
+	*ChartInfoDetailResp
+	ExcelEdbList []*ExcelChartEdbView
+}
+
+type ExcelChartEdbView struct {
+	ExcelChartEdbId int
+	DateSequenceStr string `description:"日期序列选区"`
+	DataSequenceStr string `description:"数据序列选区"`
+	FromTag         string `description:"标签"`
+}
 
 // XData 商品价格曲线的的x轴数据
 type XData struct {
@@ -1401,6 +1416,7 @@ type PreviewChartInfoReq struct {
 	Calendar          string           `description:"公历/农历"`
 	SeasonExtraConfig SeasonExtraItem  `description:"季节性图表中的配置,json数据"`
 	StartYear         int              `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
+	ChartSource       int
 }
 
 type SeasonExtraItem struct {
@@ -1434,6 +1450,11 @@ func (m QuarterDataList) Swap(i, j int) {
 	m[i], m[j] = m[j], m[i]
 }
 
+// BalanceSeasonChartLegendPreviewResp 表格中季节性图表预览接口
+type BalanceSeasonChartLegendPreviewResp struct {
+	List QuarterDataList
+}
+
 // 判断图表指标是否已经存在
 func ChartInfoExist(condition, edbInfoIdStr string) (count int, err error) {
 	sql := `SELECT COUNT(1) AS count FROM (
@@ -1742,10 +1763,10 @@ func GetChartInfoByClassifyIdAndName(classifyId int, chartName string) (item *Ch
 
 // BatchChartRefreshReq 批量刷新图表请求
 type BatchChartRefreshReq struct {
-	ChartInfoCode   []string `description:"图表编码数组"`
-	ReportId        int      `description:"报告id"`
-	ReportChapterId int      `description:"报告章节id"`
-	Source          string   `description:"来源,枚举值:report、english_report、smart_report"`
+	ChartInfoCode []string `description:"图表编码数组"`
+	PrimaryId     int      `description:"研报/智能研报/PPT主键"`
+	SubId         int      `description:"报告章节ID"`
+	Source        string   `description:"来源,枚举值:report、english_report、smart_report、ppt"`
 }
 
 // GetChartInfoListByUniqueCodeSlice 根据图表编码获取图表列表数据

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

@@ -88,3 +88,18 @@ func GetChartThemeTypeByChartTypeAndSource(chartType, source int) (item *ChartTh
 
 	return
 }
+
+// GetChartThemeTypeByChartType
+// @Description: 通过图表类型获取类型
+// @author: Roc
+// @datetime 2023-12-14 09:53:58
+// @param chartThemeTypeId int
+// @return item *ChartThemeType
+// @return err error
+func GetChartThemeTypeByChartType(chartType int) (item *ChartThemeType, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM chart_theme_type where chart_type = ?`
+	err = o.Raw(sql, chartType).QueryRow(&item)
+
+	return
+}

+ 16 - 0
models/data_manage/cross_variety/chart_info_cross_variety.go

@@ -72,6 +72,22 @@ func GetChartInfoCrossVarietyByChartInfoId(id int) (item *ChartInfoCrossVariety,
 	return
 }
 
+func GetChartInfoCrossVarietyByXEdbInfoId(edbInfoId int) (items []*ChartInfoCrossVariety, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT a.* FROM chart_info_cross_variety a 
+         join chart_tag_variety b on a.chart_x_tag_id=b.chart_tag_id WHERE b.edb_info_id = ? `
+	_, err = o.Raw(sql, edbInfoId).QueryRows(&items)
+	return
+}
+
+func GetChartInfoCrossVarietyByYEdbInfoId(edbInfoId int) (items []*ChartInfoCrossVariety, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT a.* FROM chart_info_cross_variety a 
+         join chart_tag_variety b on a.chart_y_tag_id=b.chart_tag_id WHERE b.edb_info_id = ? `
+	_, err = o.Raw(sql, edbInfoId).QueryRows(&items)
+	return
+}
+
 // CreateChart
 // @Description: 新增跨品种图表
 // @author: Roc

+ 11 - 0
models/data_manage/cross_variety/chart_tag_variety.go

@@ -260,3 +260,14 @@ func GetChartTagVarietyListByTagIdList(chartTagIdList []int) (items []*ChartTagV
 
 	return
 }
+
+func DeleteChartTagVarietyByEdbInfoId(edbInfoId int) (err error) {
+	// 删除不存在的品种
+	o := orm.NewOrmUsingDB("data")
+	sql := ` DELETE FROM chart_tag_variety WHERE edb_info_id=?`
+	_, err = o.Raw(sql, edbInfoId, edbInfoId).Exec()
+	if err != nil {
+		return
+	}
+	return
+}

+ 13 - 3
models/data_manage/edb_classify.go

@@ -298,10 +298,20 @@ type EdbClassifyListResp struct {
 }
 
 type ClassifyDeleteCheckResp struct {
-	DeleteStatus int    `description:"检测状态:0:默认值,如果为0,继续走其他校验,1:若目录关联指标不可删除,2:确认删除当前目录及包含的子目录吗,3:当前指标已用作画图,不可删除"`
-	TipsMsg      string `description:"提示信息"`
+	DeleteStatus int              `description:"检测状态:0:默认值,如果为0,继续走其他校验,1:若目录关联指标不可删除,2:确认删除当前目录及包含的子目录吗,3:当前指标已用作画图,不可删除"`
+	TipsMsg      string           `description:"提示信息"`
+	TableList    []*ExcelBaseInfo `description:"关联的表格"`
+}
+type ExcelBaseInfo struct {
+	ExcelInfoId     int    `orm:"column(excel_info_id);pk"`
+	Source          int    `description:"表格来源,1:excel插件的表格,2:自定义表格,3:混合表格,4:自定义分析,默认:1"`
+	ExcelType       int    `description:"表格类型,1:指标列,2:日期列,默认:1"`
+	ExcelName       string `description:"表格名称"`
+	UniqueCode      string `description:"表格唯一编码"`
+	ExcelClassifyId int    `description:"表格分类id"`
+	//SysUserId       int    `description:"操作人id"`
+	//SysUserRealName string `description:"操作人真实姓名"`
 }
-
 type ClassifyDeleteCheckReq struct {
 	ClassifyId int `description:"分类id"`
 	EdbInfoId  int `description:"指标id"`

+ 138 - 1
models/data_manage/edb_info.go

@@ -1,6 +1,8 @@
 package data_manage
 
 import (
+	"encoding/json"
+	"eta/eta_api/models/data_manage/line_equation/request"
 	"eta/eta_api/models/mgo"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
@@ -8,6 +10,7 @@ import (
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"go.mongodb.org/mongo-driver/bson"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -247,10 +250,21 @@ func DeleteEdbInfoAndData(edbInfoId, source, subSource int) (err error) {
 	// 删除计算指标的关系
 	sql = ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id=? `
 	_, err = to.Raw(sql, edbInfoId).Exec()
-
+	if err != nil {
+		return
+	}
 	// 删除预测指标的配置
 	sql = ` DELETE FROM predict_edb_conf WHERE predict_edb_info_id=? `
 	_, err = to.Raw(sql, edbInfoId).Exec()
+	if err != nil {
+		return
+	}
+	// 删除跨品种分析标签绑定的指标
+	sql = ` DELETE FROM chart_tag_variety WHERE edb_info_id=?`
+	_, err = to.Raw(sql, edbInfoId).Exec()
+	if err != nil {
+		return
+	}
 	return
 }
 
@@ -851,7 +865,123 @@ func ReplaceChartEdb(oldEdbInfo, newEdbInfo *EdbInfo) (relationEdbInfoIdList []i
 			return
 		}
 		replaceChartTotal = len(chartEdbMappingList)
+		// 查询所有的相关的图表信息
+		chartInfoIds := make([]string, 0)
+		if len(chartEdbMappingList) > 0 {
+			for _, chartEdbMapping := range chartEdbMappingList {
+				chartInfoIds = append(chartInfoIds, strconv.Itoa(chartEdbMapping.ChartInfoId))
+			}
+		}
 
+		if len(chartInfoIds) > 0 {
+			chartInfoList, e := GetChartInfoListByChartIdList(chartInfoIds)
+			if e != nil {
+				err = e
+				errmsg = "获取图表信息失败:Err:" + e.Error()
+				return
+			}
+			for _, chartInfo := range chartInfoList {
+				updateStr := make([]string, 0)
+				if chartInfo.EdbInfoIds != "" && strings.Contains(chartInfo.EdbInfoIds, strconv.Itoa(oldEdbInfo.EdbInfoId)) {
+					//需要更换
+					//解析字符串
+					edbInfoIds := strings.Split(chartInfo.EdbInfoIds, ",")
+					for i, edbInfoId := range edbInfoIds {
+						if edbInfoId == strconv.Itoa(oldEdbInfo.EdbInfoId) {
+							edbInfoIds[i] = strconv.Itoa(newEdbInfo.EdbInfoId)
+						}
+					}
+					chartInfo.EdbInfoIds = strings.Join(edbInfoIds, ",")
+					updateStr = append(updateStr, "EdbInfoIds")
+				}
+				if chartInfo.ExtraConfig != "" {
+					//判断是否是拟合方程或者散点图截面图
+					if chartInfo.Source == utils.CHART_SOURCE_LINE_EQUATION {
+						//解析配置内容
+						var lineChartInfoConfig request.LineChartInfoReq
+						err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &lineChartInfoConfig)
+						if err != nil {
+							errmsg = "获取图表配置信息失败 json.Unmarshal:Err:" + err.Error()
+							return
+						}
+						xEdbInfoIdList := lineChartInfoConfig.XEdbInfoIdList
+						yEdbInfoIdList := lineChartInfoConfig.YEdbInfoIdList
+						if len(xEdbInfoIdList) > 0 {
+							// 循环判断是否存在
+							for k, xEdbInfoId := range xEdbInfoIdList {
+								if xEdbInfoId == oldEdbInfo.EdbInfoId {
+									xEdbInfoIdList[k] = newEdbInfo.EdbInfoId
+								}
+							}
+						}
+						if len(yEdbInfoIdList) > 0 {
+							// 循环判断是否存在
+							for k, yEdbInfoId := range yEdbInfoIdList {
+								if yEdbInfoId == oldEdbInfo.EdbInfoId {
+									yEdbInfoIdList[k] = newEdbInfo.EdbInfoId
+								}
+							}
+						}
+						// 替换新的指标信息
+						lineChartInfoConfig.XEdbInfoIdList = xEdbInfoIdList
+						lineChartInfoConfig.YEdbInfoIdList = yEdbInfoIdList
+						// 重新序列化
+						lineChartInfoConfigJson, e := json.Marshal(lineChartInfoConfig)
+						if e != nil {
+							err = e
+							errmsg = "图表配置信息序列化失败:json.Marshal Err: " + e.Error()
+							return
+						}
+						lineChartInfoConfigStr := string(lineChartInfoConfigJson)
+						// 更新图表配置信息
+						chartInfo.ExtraConfig = lineChartInfoConfigStr
+						updateStr = append(updateStr, "ExtraConfig")
+					} else if chartInfo.ChartType == utils.CHART_TYPE_SECTION_SCATTER {
+						//解析配置内容
+						var tmpExtraConfig SectionScatterReq
+						err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &tmpExtraConfig)
+						if err != nil {
+							errmsg = "获取截面散点图配置信息失败 json.Unmarshal:Err:" + err.Error()
+							return
+						}
+						//找到指标信息,并替换
+						if len(tmpExtraConfig.SeriesList) > 0 {
+							for k1, series := range tmpExtraConfig.SeriesList {
+								for k2, item := range series.EdbInfoList {
+									if item.XEdbInfoId == oldEdbInfo.EdbInfoId {
+										tmpExtraConfig.SeriesList[k1].EdbInfoList[k2].XEdbInfoId = newEdbInfo.EdbInfoId
+									}
+									if item.YEdbInfoId == oldEdbInfo.EdbInfoId {
+										tmpExtraConfig.SeriesList[k1].EdbInfoList[k2].YEdbInfoId = newEdbInfo.EdbInfoId
+									}
+								}
+							}
+						}
+						// 重新序列化
+						sectionScatterConfigJson, e := json.Marshal(tmpExtraConfig)
+						if e != nil {
+							err = e
+							errmsg = "图表配置信息序列化失败:json.Marshal Err: " + e.Error()
+							return
+						}
+						sectionScatterConfigStr := string(sectionScatterConfigJson)
+						// 更新图表配置信息
+						chartInfo.ExtraConfig = sectionScatterConfigStr
+						updateStr = append(updateStr, "ExtraConfig")
+					}
+				}
+				if len(updateStr) > 0 {
+					updateStr = append(updateStr, "ModifyTime")
+					chartInfo.ModifyTime = time.Now()
+					err = chartInfo.Update(updateStr)
+					if err != nil {
+						errmsg = "更新图表信息失败:chartInfo.Update Err: " + err.Error()
+						return
+					}
+				}
+			}
+		}
+		// 查询所有的图表信息
 		if len(chartEdbMappingList) > 0 {
 			chartInfoIdList := make([]string, 0)
 			for _, mv := range chartEdbMappingList {
@@ -863,6 +993,8 @@ func ReplaceChartEdb(oldEdbInfo, newEdbInfo *EdbInfo) (relationEdbInfoIdList []i
 					errmsg = "获取图表所有指标信息失败:Err:" + err.Error()
 					return
 				}
+				// 查询图表信息
+
 				var minData, maxData float64
 				minData = newEdbInfo.MinValue
 				maxData = newEdbInfo.MaxValue
@@ -1651,3 +1783,8 @@ func getAllDataByMongo(edbInfoId, source, subSource int, startDataTime string) (
 
 	return
 }
+
+type ReplaceEdbInfoItem struct {
+	OldEdbInfo *EdbInfo
+	NewEdbInfo *EdbInfo
+}

+ 126 - 0
models/data_manage/excel/excel_chart_data.go

@@ -0,0 +1,126 @@
+package excel
+
+import (
+	"eta/eta_api/models/data_manage"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type ExcelChartData struct {
+	ExcelChartDataId int `orm:"column(excel_chart_data_id);pk"`
+	ExcelInfoId      int `description:"表格id"`
+	ExcelChartEdbId  int `description:"指标ID"`
+	ChartInfoId      int `description:"图表id"`
+	DataTime         string
+	Value            float64
+	ModifyTime       time.Time `description:"修改时间"`
+	CreateTime       time.Time `description:"创建时间"`
+	DataTimestamp    int64
+}
+
+func (e *ExcelChartData) TableName() string {
+	return "excel_chart_data"
+}
+
+// 新增
+func (e *ExcelChartData) Add() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Insert(e)
+	return
+}
+
+// 修改
+func (e *ExcelChartData) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(e, cols...)
+	return
+}
+
+// 删除
+func (e *ExcelChartData) Delete() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Delete(e)
+	return
+}
+
+// 查询
+func GetExcelChartDataByExcelInfoId(excelInfoId int) (list []*ExcelChartData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *
+             FROM excel_chart_data
+			 WHERE excel_info_id=? 
+             ORDER BY excel_chart_edb_id ASC, data_time desc, excel_chart_data_id ASC `
+	_, err = o.Raw(sql, excelInfoId).QueryRows(&list)
+	return
+}
+
+func BatchUpdateChartEdbData(excelInfoId int, excelEdbMap map[int]*ExcelChartEdb, excelDataMap map[int][]*data_manage.EdbDataList) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+		} else {
+			_ = o.Commit()
+		}
+	}()
+	//如果有数据则删除所有的数据
+	sql := `delete from excel_chart_data where excel_info_id = ?`
+	_, err = o.Raw(sql, excelInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	// 图表指标数据入库
+	addList := make([]*ExcelChartData, 0)
+	for chartEdbId, dataList := range excelDataMap {
+		chartEdb, ok := excelEdbMap[chartEdbId]
+		if !ok {
+			err = fmt.Errorf("chartEdbId:%d not exist", chartEdbId)
+			return
+		}
+		for _, v := range dataList {
+			chartData := &ExcelChartData{
+				ExcelInfoId:     chartEdb.ExcelInfoId,
+				ExcelChartEdbId: chartEdb.ExcelChartEdbId,
+				ChartInfoId:     chartEdb.ChartInfoId,
+				DataTime:        v.DataTime,
+				Value:           v.Value,
+				DataTimestamp:   v.DataTimestamp,
+				ModifyTime:      time.Now(),
+				CreateTime:      time.Now(),
+			}
+			addList = append(addList, chartData)
+			// data信息入库
+			if len(addList) > 1000 {
+				_, err = o.InsertMulti(len(addList), addList)
+				if err != nil {
+					return
+				}
+				addList = addList[:0]
+			}
+		}
+	}
+
+	// data信息入库
+	if len(addList) > 0 {
+		_, err = o.InsertMulti(len(addList), addList)
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func GetExcelChartDataByChartInfoId(chartInfoId int) (list []*ExcelChartData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *
+             FROM excel_chart_Data
+			 WHERE chart_info_id=? 
+             ORDER BY excel_chart_edb_id ASC `
+	_, err = o.Raw(sql, chartInfoId).QueryRows(&list)
+	return
+}

+ 455 - 0
models/data_manage/excel/excel_chart_edb.go

@@ -0,0 +1,455 @@
+package excel
+
+import (
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type ExcelChartEdb struct {
+	ExcelChartEdbId int       `orm:"column(excel_chart_edb_id);pk"`
+	ExcelInfoId     int       `description:"表格id"`
+	ChartInfoId     int       `description:"图表id"`
+	EdbCode         string    `description:"指标编码"`
+	EdbName         string    `description:"指标名称"`
+	EdbNameEn       string    `description:"指标英文名称"`
+	Unit            string    `description:"指标单位"`
+	UnitEn          string    `description:"指标单位"`
+	DateSequence    string    `description:"日期序列选区"`
+	DataSequence    string    `description:"数据序列选区"`
+	SysUserId       int       `description:"创建人"`
+	SysUserRealName string    `description:"创建人姓名"`
+	MaxData         float64   `description:"上限"`
+	MinData         float64   `description:"下限"`
+	IsOrder         bool      `description:"true:正序,false:逆序"`
+	IsAxis          int       `description:"true:左轴,false:右轴"`
+	EdbInfoType     int       `description:"true:标准指标,false:领先指标"`
+	LeadValue       int       `description:"领先值"`
+	LeadUnit        string    `description:"领先单位"`
+	FromTag         string    `description:"标签"`
+	ModifyTime      time.Time `description:"修改时间"`
+	CreateTime      time.Time `description:"创建时间"`
+	ChartWidth      float64   `description:"线条大小"`
+}
+
+type ExcelChartEdbView struct {
+	ExcelChartEdbId int
+	ExcelInfoId     int    `description:"表格id"`
+	ChartInfoId     int    `description:"图表id"`
+	EdbCode         string `description:"指标编码"`
+	EdbName         string `description:"指标名称"`
+	DateSequenceStr string `description:"日期序列选区"`
+	DataSequenceStr string `description:"数据序列选区"`
+	/*MaxData         float64 `description:"上限"`
+	MinData         float64 `description:"下限"`
+	IsOrder         bool    `description:"true:正序,false:逆序"`
+	IsAxis          int     `description:"true:左轴,false:右轴"`
+	EdbInfoType     int     `description:"true:标准指标,false:领先指标"`
+	LeadValue       int     `description:"领先值"`
+	LeadUnit        string  `description:"领先单位"`*/
+	FromTag string `description:"标签"`
+}
+
+type BalanceTableChart struct {
+	ChartInfoId       int    `description:"图表id,新增时传0"`
+	ChartName         string `description:"图表名称"`
+	ChartType         int    `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图"`
+	Calendar          string `description:"公历/农历"`
+	LeftMin           string `description:"图表左侧最小值"`
+	LeftMax           string `description:"图表左侧最大值"`
+	RightMin          string `description:"图表右侧最小值"`
+	RightMax          string `description:"图表右侧最大值"`
+	Right2Min         string `description:"图表右侧2最小值"`
+	Right2Max         string `description:"图表右侧2最大值"`
+	MinMaxSave        int    `description:"是否手动保存过上下限:0-否;1-是"`
+	ExtraConfig       string `description:"图表额外配置信息,json字符串"`
+	ChartImage        string `description:"封面图" json:"-"`
+	SeasonExtraConfig string `description:"季节性图表中的配置,json数据"`
+	SourcesFrom       string `description:"图表来源"`
+	//	ChartEdbInfoList  []ExcelChartEdbView
+}
+
+func (e *ExcelChartEdb) TableName() string {
+	return "excel_chart_edb"
+}
+
+// 新增
+func (e *ExcelChartEdb) Add() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Insert(e)
+	return
+}
+
+// 修改
+func (e *ExcelChartEdb) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(e, cols...)
+	return
+}
+
+// 删除
+func (e *ExcelChartEdb) Delete() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Delete(e)
+	return
+}
+
+type AddChartEdbAndDataItem struct {
+	ChartEdb *ExcelChartEdb
+	DataList []*ExcelChartData `description:"数据列表"`
+}
+
+// 同时添加指标和指标数据
+func (e *ExcelChartEdb) AddChartEdbAndData(list []*AddChartEdbAndDataItem, chartInfo *data_manage.ChartInfo, deleteEdbIds []int) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+		} else {
+			_ = o.Commit()
+		}
+	}()
+
+	// 先删除原先的绑定的指标
+	if len(deleteEdbIds) > 0 && chartInfo.ChartInfoId > 0 {
+		sql := `DELETE FROM excel_chart_edb WHERE chart_info_id = ? AND excel_chart_edb_id in (` + utils.GetOrmInReplace(len(deleteEdbIds)) + `)`
+		_, err = o.Raw(sql, chartInfo.ChartInfoId, deleteEdbIds).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除原先的指标失败:%v", err)
+			return
+		}
+	}
+	// 图表指标信息入库
+	updateIds := make([]int, 0)
+	var edbInfoIdArrStr []string
+	for _, item := range list {
+		err = addChartEdbAndData(o, item.ChartEdb, item.DataList)
+		if err != nil {
+			return
+		}
+		updateIds = append(updateIds, item.ChartEdb.ExcelChartEdbId)
+		edbInfoIdArrStr = append(edbInfoIdArrStr, strconv.Itoa(item.ChartEdb.ExcelChartEdbId))
+	}
+
+	//新增图表
+	chartInfoId := chartInfo.ChartInfoId
+	if chartInfo.ChartInfoId <= 0 {
+		lastId, e := o.Insert(chartInfo)
+		if e != nil {
+			err = fmt.Errorf("新增图表失败,AddChartEdbAndData: %v", e)
+			return
+		}
+		chartInfoId = int(lastId)
+	} else {
+		_, err = o.Update(chartInfo)
+		if err != nil {
+			err = fmt.Errorf("更新图表失败,AddChartEdbAndData: %v", e)
+			return
+		}
+	}
+
+	//更新图表id
+	sql := `update excel_chart_edb set chart_info_id = ? where excel_chart_edb_id in (` + utils.GetOrmInReplace(len(updateIds)) + `) and chart_info_id=0`
+	_, err = o.Raw(sql, chartInfoId, updateIds).Exec()
+	if err != nil {
+		err = fmt.Errorf("更新图表id失败,AddChartEdbAndData: %v", err)
+		return
+	}
+
+	if len(edbInfoIdArrStr) > 0 {
+		edbInfoIdStr := strings.Join(edbInfoIdArrStr, ",")
+		//更新图表关联的指标id
+		sql = `update chart_info set edb_info_ids = ? where chart_info_id = ?`
+		_, err = o.Raw(sql, edbInfoIdStr, chartInfoId).Exec()
+	}
+
+	if len(updateIds) > 0 {
+		//更新图表数据
+		sql = `update excel_chart_data set chart_info_id = ? where excel_chart_edb_id in (` + utils.GetOrmInReplace(len(updateIds)) + `) and chart_info_id=0`
+		_, err = o.Raw(sql, chartInfoId, updateIds).Exec()
+		if err != nil {
+			err = fmt.Errorf("更新图表id失败,AddChartEdbAndData: %v", err)
+			return
+		}
+	}
+	return
+}
+
+func addChartEdbAndData(o orm.TxOrmer, chartEdb *ExcelChartEdb, dataList []*ExcelChartData) (err error) {
+	// 图表指标信息入库
+	excelChartEdbId := chartEdb.ExcelChartEdbId
+	if chartEdb.ExcelChartEdbId <= 0 {
+		lastId, e := o.Insert(chartEdb)
+		if e != nil {
+			err = fmt.Errorf("新增指标失败,addChartEdbAndData: %v", e)
+			return
+		}
+		excelChartEdbId = int(lastId)
+	} else {
+		_, e := o.Update(chartEdb)
+		if e != nil {
+			err = fmt.Errorf("更新指标失败,addChartEdbAndData: %v", e)
+			return
+		}
+		//如果有数据则删除所有的数据
+		sql := `delete from excel_chart_data where excel_chart_edb_id = ?`
+		_, err = o.Raw(sql, excelChartEdbId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	chartEdb.ExcelChartEdbId = excelChartEdbId
+
+	// 图表指标数据入库
+	addList := make([]*ExcelChartData, 0)
+	if len(dataList) > 0 {
+		for _, v := range dataList {
+			chartData := &ExcelChartData{
+				ExcelInfoId:     chartEdb.ExcelInfoId,
+				ExcelChartEdbId: chartEdb.ExcelChartEdbId,
+				ChartInfoId:     chartEdb.ChartInfoId,
+				DataTime:        v.DataTime,
+				Value:           v.Value,
+				DataTimestamp:   v.DataTimestamp,
+				ModifyTime:      time.Now(),
+				CreateTime:      time.Now(),
+			}
+			addList = append(addList, chartData)
+			// data信息入库
+			if len(addList) > 1000 {
+				_, err = o.InsertMulti(len(addList), addList)
+				if err != nil {
+					return
+				}
+				addList = addList[:0]
+			}
+		}
+	}
+
+	// data信息入库
+	if len(addList) > 0 {
+		_, err = o.InsertMulti(len(addList), addList)
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func GetExcelChartEdbMappingByExcelInfoId(excelInfoId int) (list []*ExcelChartEdb, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *
+             FROM excel_chart_edb 
+			 WHERE excel_info_id=? 
+             ORDER BY chart_info_id asc, excel_chart_edb_id ASC `
+	_, err = o.Raw(sql, excelInfoId).QueryRows(&list)
+	return
+}
+
+func GetExcelChartEdbMappingByExcelInfoIds(excelInfoIds []int) (list []*ExcelChartEdb, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *
+             FROM excel_chart_edb 
+			 WHERE excel_info_id in (` + utils.GetOrmInReplace(len(excelInfoIds)) + `)`
+	_, err = o.Raw(sql, excelInfoIds).QueryRows(&list)
+	return
+}
+
+func GetExcelChartEdbById(id int) (item *ExcelChartEdb, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM excel_chart_edb WHERE excel_chart_edb_id=? `
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func GetExcelChartEdbMappingByChartInfoId(chartInfoId int) (list []*ExcelChartEdb, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *
+             FROM excel_chart_edb 
+			 WHERE chart_info_id=? 
+             ORDER BY excel_chart_edb_id ASC `
+	_, err = o.Raw(sql, chartInfoId).QueryRows(&list)
+	return
+}
+
+func GetExcelInfoByChartInfoId(chartInfoId int) (item *ExcelInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT i.*
+             FROM excel_chart_edb e left join excel_info i on e.excel_info_id=i.excel_info_id
+			 WHERE e.chart_info_id=? limit 1`
+	err = o.Raw(sql, chartInfoId).QueryRow(&item)
+	return
+}
+
+// 同时删除指标和指标数据
+func DeleteExcelChartEdbAndData(excelInfoIds []int, chartInfoIds []int) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+		} else {
+			_ = o.Commit()
+		}
+	}()
+	// 把对应的表格状态改成删除状态
+	//更新图表id
+	sql := `update excel_info set is_delete = 1, modify_time = ? where excel_info_id in (` + utils.GetOrmInReplace(len(excelInfoIds)) + `)`
+	_, err = o.Raw(sql, time.Now(), excelInfoIds).Exec()
+	if err != nil {
+		err = fmt.Errorf("更新图表id失败,AddChartEdbAndData: %v", err)
+		return
+	}
+	// 把删除图表状态
+	if len(chartInfoIds) > 0 {
+		sql := `DELETE FROM chart_info WHERE  chart_info_id in (` + utils.GetOrmInReplace(len(chartInfoIds)) + `)`
+		_, err = o.Raw(sql, chartInfoIds).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除原先的指标失败:%v", err)
+			return
+		}
+
+		// todo 如果加入到我的图库中,则删除我的图库中的数据
+	}
+	// 删除原先的绑定的指标
+	sql = `DELETE FROM excel_chart_edb WHERE  excel_info_id in (` + utils.GetOrmInReplace(len(excelInfoIds)) + `)`
+	_, err = o.Raw(sql, excelInfoIds).Exec()
+	if err != nil {
+		err = fmt.Errorf("删除原先的指标失败:%v", err)
+		return
+	}
+	// 删除指标数据
+	sql = `DELETE FROM excel_chart_data WHERE  excel_info_id in (` + utils.GetOrmInReplace(len(excelInfoIds)) + `)`
+	_, err = o.Raw(sql, excelInfoIds).Exec()
+	if err != nil {
+		err = fmt.Errorf("删除原先的指标失败:%v", err)
+		return
+	}
+	return
+}
+
+// 删除平衡表中的指标和数据
+func DeleteBalanceExcelChartInfoAndData(chartInfoId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	sql := ` DELETE FROM chart_info WHERE chart_info_id=? `
+	_, err = to.Raw(sql, chartInfoId).Exec()
+	if err != nil {
+		err = fmt.Errorf("删除平衡表图表失败 %s", err.Error())
+		return
+	}
+	sql = ` DELETE FROM  excel_chart_edb WHERE chart_info_id=? `
+	_, err = to.Raw(sql, chartInfoId).Exec()
+	if err != nil {
+		err = fmt.Errorf("删除平衡表图表指标失败 %s", err.Error())
+		return
+	}
+	// 删除表格里的数据
+	sql = ` DELETE FROM  excel_chart_data WHERE chart_info_id=? `
+	_, err = to.Raw(sql, chartInfoId).Exec()
+	if err != nil {
+		err = fmt.Errorf("删除平衡表图表指标失败 %s", err.Error())
+		return
+	}
+	return
+}
+
+func EditBalanceChartBaseInfoAndEdbEnInfo(req *data_manage.EditChartInfoBaseReq, chartItem *data_manage.ChartInfo, lang string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	updateChartCols := make([]string, 0)
+	switch lang {
+	case utils.EnLangVersion:
+		chartItem.ChartNameEn = req.ChartName
+		updateChartCols = append(updateChartCols, "ChartNameEn")
+	default:
+		chartItem.ChartName = req.ChartName
+		updateChartCols = append(updateChartCols, "ChartName")
+	}
+
+	if req.ExtraConfig != `` {
+		chartItem.ExtraConfig = req.ExtraConfig
+		updateChartCols = append(updateChartCols, "ExtraConfig")
+	}
+	chartItem.ModifyTime = time.Now()
+	updateChartCols = append(updateChartCols, "ModifyTime")
+	_, err = to.Update(chartItem, updateChartCols...)
+	if err != nil {
+		fmt.Println("UPDATE  chart_info Err:", err.Error())
+		return err
+	}
+
+	var edbInfoIdArr []string
+	for _, v := range req.ChartEdbInfoList {
+		edbInfoIdArr = append(edbInfoIdArr, strconv.Itoa(v.EdbInfoId))
+		var count int
+		csql := `SELECT COUNT(1) AS count FROM excel_chart_edb WHERE chart_info_id=? AND excel_chart_edb_id=? `
+		err = to.Raw(csql, req.ChartInfoId, v.EdbInfoId).QueryRow(&count)
+		if err != nil {
+			fmt.Println("QueryRow Err:", err.Error())
+			return err
+		}
+		if count > 0 {
+			msql := ` UPDATE excel_chart_edb SET modify_time = NOW() `
+			pars := make([]interface{}, 0)
+			switch lang {
+			case utils.EnLangVersion:
+				msql += ` ,edb_name_en = ? `
+				pars = append(pars, v.EdbName)
+			default:
+				msql += ` ,edb_name = ? `
+				pars = append(pars, v.EdbName)
+			}
+			msql += ` WHERE excel_chart_edb_id = ? `
+			pars = append(pars, v.EdbInfoId)
+			_, err = to.Raw(msql, pars...).Exec()
+			if err != nil {
+				fmt.Println("edb_info Err:" + err.Error())
+				return err
+			}
+		}
+	}
+	return
+}
+
+func GetBalanceChartEdbByCondition(condition string, pars []interface{}) (item *ExcelChartEdb, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM excel_chart_edb WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}

+ 32 - 2
models/data_manage/excel/excel_edb_mapping.go

@@ -31,12 +31,19 @@ func (e *ExcelEdbMapping) Add() (err error) {
 	return
 }
 
+// Update 更新 excel表格基础信息
+func (e *ExcelEdbMapping) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(e, cols...)
+	return
+}
+
 // GetExcelEdbMappingByEdbInfoId 根据指标id获取配置关系
-func GetExcelEdbMappingByEdbInfoId(edbInfoId int) (item *ExcelEdbMapping, err error) {
+func GetExcelEdbMappingByEdbInfoId(edbInfoId int) (items []*ExcelEdbMapping, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT *  FROM excel_edb_mapping WHERE 1=1 AND edb_info_id = ? `
 
-	err = o.Raw(sql, edbInfoId).QueryRow(&item)
+	_, err = o.Raw(sql, edbInfoId).QueryRows(&items)
 	return
 }
 
@@ -89,6 +96,20 @@ func GetNoCustomAnalysisExcelEdbMappingCount(edbInfoId int) (count int, err erro
 	return
 }
 
+type ExcelEdbMappingWithParentIdItem struct {
+	ExcelInfoId int
+	ParentId    int
+}
+
+func GetNoCustomAnalysisExcelEdbMapping(edbInfoId int) (items []ExcelEdbMappingWithParentIdItem, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT b.excel_info_id, b.parent_id FROM excel_edb_mapping a 
+                          join excel_info b on a.excel_info_id=b.excel_info_id
+                          WHERE edb_info_id=? AND a.source != 4 AND b.is_delete = 0`
+	_, err = o.Raw(sql, edbInfoId).QueryRows(&items)
+	return
+}
+
 // GetAllExcelEdbMappingByExcelInfoId 根据excel的id获取所有的指标
 func GetAllExcelEdbMappingByExcelInfoId(excelInfoId int) (items []*ExcelEdbMapping, err error) {
 	o := orm.NewOrmUsingDB("data")
@@ -98,6 +119,15 @@ func GetAllExcelEdbMappingByExcelInfoId(excelInfoId int) (items []*ExcelEdbMappi
 	return
 }
 
+// GetExcelEdbMappingByEdbInfoIdAndSource 根据指标id获取配置关系
+func GetExcelEdbMappingByEdbInfoIdAndSource(edbInfoId int, sources []int) (items []*ExcelEdbMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM excel_edb_mapping WHERE 1=1 AND edb_info_id = ? AND source in (` + utils.GetOrmInReplace(len(sources)) + `) `
+
+	_, err = o.Raw(sql, edbInfoId, sources).QueryRows(&items)
+	return
+}
+
 // DeleteCustomAnalysisExcelEdbMappingByEdbInfoId
 // @Description: 根据指标id删除与自定义分析表格的关系
 // @author: Roc

+ 223 - 44
models/data_manage/excel/excel_info.go

@@ -1,30 +1,38 @@
 package excel
 
 import (
+	"eta/eta_api/models/data_manage"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
+	"strconv"
 	"time"
 )
 
 // ExcelInfo excel表格详情表
 type ExcelInfo struct {
-	ExcelInfoId      int       `orm:"column(excel_info_id);pk"`
-	Source           int       `description:"表格来源,1:excel插件的表格,2:自定义表格,3:混合表格,4:自定义分析,默认:1"`
-	ExcelType        int       `description:"表格类型,1:指标列,2:日期列,默认:1"`
-	ExcelName        string    `description:"表格名称"`
-	UniqueCode       string    `description:"表格唯一编码"`
-	ExcelClassifyId  int       `description:"表格分类id"`
-	SysUserId        int       `description:"操作人id"`
-	SysUserRealName  string    `description:"操作人真实姓名"`
-	Content          string    `description:"表格内容"`
-	ExcelImage       string    `description:"表格图片"`
-	FileUrl          string    `description:"表格下载地址"`
-	Sort             int       `description:"排序字段,数字越小越排前面"`
-	IsDelete         int       `description:"是否删除,0:未删除,1:已删除"`
-	ModifyTime       time.Time `description:"最近修改日期"`
-	CreateTime       time.Time `description:"创建日期"`
-	IsJoinPermission int       `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	ExcelInfoId        int       `orm:"column(excel_info_id);pk"`
+	Source             int       `description:"表格来源,1:excel插件的表格,2:自定义表格,3:混合表格,4:自定义分析,默认:1"`
+	ExcelType          int       `description:"表格类型,1:指标列,2:日期列,默认:1"`
+	ExcelName          string    `description:"表格名称"`
+	UniqueCode         string    `description:"表格唯一编码"`
+	ExcelClassifyId    int       `description:"表格分类id"`
+	SysUserId          int       `description:"操作人id"`
+	SysUserRealName    string    `description:"操作人真实姓名"`
+	Content            string    `description:"表格内容"`
+	ExcelImage         string    `description:"表格图片"`
+	FileUrl            string    `description:"表格下载地址"`
+	Sort               int       `description:"排序字段,数字越小越排前面"`
+	IsDelete           int       `description:"是否删除,0:未删除,1:已删除"`
+	ModifyTime         time.Time `description:"最近修改日期"`
+	CreateTime         time.Time `description:"创建日期"`
+	IsJoinPermission   int       `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	ParentId           int       `description:"表格的父级id"`
+	BalanceType        int       `description:"平衡表类型:0 动态表,1静态表"`
+	UpdateUserId       int       `description:"更新人id"`
+	UpdateUserRealName string    `description:"更新人真实姓名"`
+	RelExcelInfoId     int       `description:"平衡表里静态表关联的动态表excel id"`
+	VersionName        string    `description:"静态表版本名称"`
 }
 
 // Update 更新 excel表格基础信息
@@ -35,25 +43,30 @@ func (excelInfo *ExcelInfo) Update(cols []string) (err error) {
 }
 
 type MyExcelInfoList struct {
-	ExcelInfoId      int       `orm:"column(excel_info_id);pk"`
-	Source           int       `description:"表格来源,1:excel插件的表格,2:自定义表格,默认:1"`
-	ExcelType        int       `description:"表格类型,1:指标列,2:日期列,默认:1"`
-	ExcelName        string    `description:"表格名称"`
-	UniqueCode       string    `description:"表格唯一编码"`
-	ExcelClassifyId  int       `description:"表格分类id"`
-	SysUserId        int       `description:"操作人id"`
-	SysUserRealName  string    `description:"操作人真实姓名"`
-	ExcelImage       string    `description:"表格图片"`
-	FileUrl          string    `description:"表格下载地址"`
-	Sort             int       `description:"排序字段,数字越小越排前面"`
-	ModifyTime       time.Time `description:"最近修改日期"`
-	CreateTime       time.Time `description:"创建日期"`
-	IsJoinPermission int       `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
-	HaveOperaAuth    bool      `description:"是否有数据权限"`
+	ExcelInfoId        int                   `orm:"column(excel_info_id);pk"`
+	Source             int                   `description:"表格来源,1:excel插件的表格,2:自定义表格,默认:1"`
+	ExcelType          int                   `description:"表格类型,1:指标列,2:日期列,默认:1"`
+	ExcelName          string                `description:"表格名称"`
+	UniqueCode         string                `description:"表格唯一编码"`
+	ExcelClassifyId    int                   `description:"表格分类id"`
+	SysUserId          int                   `description:"操作人id"`
+	SysUserRealName    string                `description:"操作人真实姓名"`
+	ExcelImage         string                `description:"表格图片"`
+	FileUrl            string                `description:"表格下载地址"`
+	Sort               int                   `description:"排序字段,数字越小越排前面"`
+	ModifyTime         time.Time             `description:"最近修改日期"`
+	CreateTime         time.Time             `description:"创建日期"`
+	IsJoinPermission   int                   `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	HaveOperaAuth      bool                  `description:"是否有数据权限"`
+	UpdateUserId       int                   `description:"更新人id"`
+	UpdateUserRealName string                `description:"更新人真实姓名"`
+	Button             ExcelInfoDetailButton `description:"操作权限"`
+	CanEdit            bool                  `description:"是否可编辑"`
+	Editor             string                `description:"编辑人"`
 }
 
 // AddExcelInfo 新增表格
-func AddExcelInfo(excelInfo *ExcelInfo, excelEdbMappingList []*ExcelEdbMapping) (err error) {
+func AddExcelInfo(excelInfo *ExcelInfo, excelEdbMappingList []*ExcelEdbMapping, childExcel *ExcelInfo) (err error) {
 	o, err := orm.NewOrmUsingDB("data").Begin()
 	if err != nil {
 		return
@@ -71,7 +84,16 @@ func AddExcelInfo(excelInfo *ExcelInfo, excelEdbMappingList []*ExcelEdbMapping)
 		return
 	}
 	excelInfo.ExcelInfoId = int(lastId)
-
+	// todo 判断如果是平衡表的父级,则新增一个名叫平衡表的子表, 内容为空
+	if childExcel != nil {
+		// 表格信息入库
+		childExcel.ParentId = excelInfo.ExcelInfoId
+		_, err = o.Insert(childExcel)
+		if err != nil {
+			err = fmt.Errorf("新增子表失败:%v", err)
+			return
+		}
+	}
 	// excel与指标的关联关系
 	dataNum := len(excelEdbMappingList)
 	if dataNum > 0 {
@@ -104,7 +126,12 @@ func EditExcelInfo(excelInfo *ExcelInfo, updateExcelInfoParams []string, excelEd
 	if err != nil {
 		return
 	}
-
+	//更新父级分类时,同时更新子集分类
+	if excelInfo.Source == utils.BALANCE_TABLE && excelInfo.ParentId == 0 {
+		// 同步更新子表分类
+		sql := `UPDATE FROM excel_info set excel_classify_id = ? WHERE parent_id=? `
+		_, err = o.Raw(sql, excelInfo.ExcelClassifyId, excelInfo.ExcelInfoId).Exec()
+	}
 	// 删除关系表
 	sql := `DELETE FROM excel_edb_mapping WHERE excel_info_id=? `
 	_, err = o.Raw(sql, excelInfo.ExcelInfoId).Exec()
@@ -150,6 +177,30 @@ func GetNoContentExcelInfoAll(source, userId int) (items []*ExcelClassifyItems,
 	return
 }
 
+// GetBalanceNoContentExcelInfoAll 获取不含content的平衡表表格列表 用于分类展示
+func GetBalanceNoContentExcelInfoAll(source int, excelInfoIds []int, userId int) (items []*ExcelClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT excel_info_id,excel_classify_id,excel_name AS excel_classify_name,
+             unique_code,sys_user_id,sys_user_real_name,sort,is_join_permission
+            FROM excel_info where is_delete=0 AND source = ?  AND parent_id = 0 AND balance_type=0 `
+
+	pars := []interface{}{source}
+
+	if userId > 0 {
+		if len(excelInfoIds) > 0 {
+			sql += ` AND (excel_info_id in (` + utils.GetOrmInReplace(len(excelInfoIds)) + `) or sys_user_id = ?)`
+			pars = append(pars, excelInfoIds, userId)
+		} else {
+			sql += ` AND sys_user_id = ? `
+			pars = append(pars, userId)
+		}
+	}
+
+	sql += `  ORDER BY sort asc,excel_info_id desc `
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+	return
+}
+
 // GetAllExcelInfoBySource 根据来源获取包含content的表格列表
 func GetAllExcelInfoBySource(source int) (items []*ExcelInfo, err error) {
 	o := orm.NewOrmUsingDB("data")
@@ -204,13 +255,27 @@ func GetNoContentExcelInfoListByCondition(condition string, pars []interface{},
 	return
 }
 
-func GetExcelInfoByCondition(condition string, pars []interface{}) (item *ExcelInfo, err error) {
+func GetNoContentExcelInfoListByConditionNoPage(condition string, pars []interface{}) (items []*ExcelInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT excel_info_id,excel_classify_id,excel_name,
+             unique_code,sys_user_id,sys_user_real_name,sort,is_join_permission, parent_id, balance_type, update_user_id,update_user_real_name,rel_excel_info_id,version_name FROM excel_info WHERE is_delete=0 `
+	if condition != "" {
+		sql += condition
+	}
+
+	sql += ` ORDER BY excel_info_id DESC `
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func GetExcelInfoListByCondition(condition string, pars []interface{}) (items []*ExcelInfo, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT * FROM excel_info WHERE 1=1 AND is_delete=0 `
 	if condition != "" {
 		sql += condition
 	}
-	err = o.Raw(sql, pars).QueryRow(&item)
+	sql += ` ORDER BY sort asc, excel_info_id asc`
+	_, err = o.Raw(sql, pars).QueryRows(&items)
 	return
 }
 
@@ -325,7 +390,7 @@ func GetExcelInfoByClassifyIdAndName(classifyId int, excelName string) (item *Ex
 // GetNoContentExcelListByCondition 获取没有content的excel表格列表数据
 func GetNoContentExcelListByCondition(condition string, pars []interface{}, startSize, pageSize int) (item []*MyExcelInfoList, err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission
+	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission,update_user_id,update_user_real_name
 FROM excel_info WHERE 1=1 AND is_delete=0 `
 	if condition != "" {
 		sql += condition
@@ -336,6 +401,20 @@ FROM excel_info WHERE 1=1 AND is_delete=0 `
 	return
 }
 
+// GetNoContentExcelListByConditionNoPage 获取没有content的excel表格列表数据
+func GetNoContentExcelListByConditionNoPage(condition string, pars []interface{}) (item []*MyExcelInfoList, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission,update_user_id,update_user_real_name
+FROM excel_info WHERE 1=1 AND is_delete=0 `
+	if condition != "" {
+		sql += condition
+	}
+	//sql += " ORDER BY sort ASC,chart_info_id DESC LIMIT ?,? "
+	sql += " ORDER BY create_time DESC"
+	_, err = o.Raw(sql, pars).QueryRows(&item)
+	return
+}
+
 func GetExcelListCountByCondition(condition string, pars []interface{}) (count int, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT COUNT(1) AS count FROM excel_info WHERE 1=1 AND is_delete=0 `
@@ -374,7 +453,7 @@ func UpdateExcelInfoClassifyId(classifyId, excelInfoId int) (err error) {
 // GetNoContentExcelInfoByName 根据名称 获取eta表格详情
 func GetNoContentExcelInfoByName(excelName string, source int) (item *MyExcelInfoList, err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission 
+	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission,update_user_id,update_user_real_name 
  FROM excel_info WHERE excel_name = ? AND source = ? AND is_delete=0 `
 	err = o.Raw(sql, excelName, source).QueryRow(&item)
 
@@ -384,7 +463,7 @@ func GetNoContentExcelInfoByName(excelName string, source int) (item *MyExcelInf
 // GetNoContentExcelInfoByUniqueCode 根据unique_code来获取excel表格详情
 func GetNoContentExcelInfoByUniqueCode(uniqueCode string) (item *MyExcelInfoList, err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission 
+	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission,update_user_id,update_user_real_name 
  FROM excel_info WHERE unique_code=? AND is_delete=0 `
 	err = o.Raw(sql, uniqueCode).QueryRow(&item)
 	return
@@ -393,7 +472,7 @@ func GetNoContentExcelInfoByUniqueCode(uniqueCode string) (item *MyExcelInfoList
 // GetNoContentExcelInfoByExcelId 根据表格id来获取excel表格详情
 func GetNoContentExcelInfoByExcelId(excelInfoId int) (item *MyExcelInfoList, err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission 
+	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission,update_user_id,update_user_real_name 
  FROM excel_info WHERE excel_info_id=? AND is_delete=0 `
 	err = o.Raw(sql, excelInfoId).QueryRow(&item)
 	return
@@ -538,10 +617,10 @@ func SaveExcelInfoAndSheet(excelInfo *ExcelInfo, updateExcelInfoParam []string,
 
 // BatchRefreshExcelReq 批量刷新表格请求
 type BatchRefreshExcelReq struct {
-	ExcelCodes      []string `description:"表格编码"`
-	ReportId        int      `description:"报告ID"`
-	ReportChapterId int      `description:"报告章节ID"`
-	Source          string   `description:"来源,枚举值:report、english_report、smart_report"`
+	ExcelCodes []string `description:"表格编码"`
+	PrimaryId  int      `description:"研报/智能研报/PPT主键"`
+	SubId      int      `description:"报告章节ID"`
+	Source     string   `description:"来源,枚举值:report、english_report、smart_report"`
 }
 
 // GetExcelMaxSortByClassifyId 获取当前分类下,且排序数最大的excel
@@ -569,6 +648,18 @@ func GetNoContentExcelListByExcelInfoIdList(excelInfoIdList []string) (items []*
 	return
 }
 
+func GetNoContentExcelListByExcelInfoIdAndParentId(excelInfoIdList []string) (items []*MyExcelInfoList, err error) {
+	num := len(excelInfoIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM excel_info WHERE excel_info_id in (` + utils.GetOrmInReplace(num) + `) OR parent_id in (` + utils.GetOrmInReplace(num) + `)   order by excel_info_id DESC `
+	_, err = o.Raw(sql, excelInfoIdList, excelInfoIdList).QueryRows(&items)
+
+	return
+}
+
 // GetNoContentExcelListByUserId
 // @Description: 根据ETA表格ID列表获取列表信息
 // @param userIdList []int
@@ -623,3 +714,91 @@ func ModifyExcelInfoUserIdByOldUserId(oldUserIdList []int, userId int, userName
 	_, err = o.Raw(sql, userId, userName, oldUserIdList).Exec()
 	return
 }
+
+func GetExcelBaseInfoByExcelInfoIdList(excelInfoIdList []int) (items []*data_manage.ExcelBaseInfo, err error) {
+	num := len(excelInfoIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name FROM excel_info WHERE excel_info_id in (` + utils.GetOrmInReplace(num) + `) order by excel_info_id DESC `
+	_, err = o.Raw(sql, excelInfoIdList).QueryRows(&items)
+
+	return
+}
+
+// ReplaceEdbInExcel 替换表格中的指标
+func ReplaceEdbInExcel(oldEdbInfoId, newEdbInfoId int, updateExcelList []*ExcelInfo) (err error) {
+	var errmsg string
+	logMsg := `` // 记录替换的日志
+	replaceTotal := 0
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+			if logMsg != `` {
+				utils.FileLog.Info(fmt.Sprintf("替换表格中的指标记录,替换总表格数:%d,旧的指标id:%d,新的指标id:%d;%s", replaceTotal, oldEdbInfoId, newEdbInfoId, logMsg))
+			}
+		}
+		if errmsg != "" {
+			fmt.Println("errmsg:" + errmsg)
+		}
+		if err != nil && errmsg != "" {
+			//go alarm_msg.SendAlarmMsg("替换替换配置中的指标记录失败提醒,errmsg:"+errmsg, 3)
+		}
+	}()
+	// 替换表格中的内容
+	for _, excelInfo := range updateExcelList {
+		//更新配置中的指标A
+		sql := `UPDATE excel_info SET content=?, modify_time=? WHERE excel_info_id=?`
+		_, err = to.Raw(sql, excelInfo.Content, time.Now(), excelInfo.ExcelInfoId).Exec()
+		if err != nil {
+			errmsg = "更新表格内容失败:Err:" + err.Error()
+			return
+		}
+		//更新配置中的指标B
+		// 判断是否已存在绑定,如果存在则删除
+		sql = `DELETE FROM excel_edb_mapping WHERE excel_info_id=? and edb_info_id=?`
+		_, err = to.Raw(sql, excelInfo.ExcelInfoId, newEdbInfoId).Exec()
+		if err != nil {
+			errmsg = "删除指标B关联图表配置信息失败:Err:" + err.Error()
+			return
+		}
+		// 插入新的绑定
+		sql = `UPDATE excel_edb_mapping SET edb_info_id=?, modify_time=? WHERE excel_info_id=? and edb_info_id=?`
+		_, err = to.Raw(sql, newEdbInfoId, time.Now(), excelInfo.ExcelInfoId, oldEdbInfoId).Exec()
+		if err != nil {
+			errmsg = "更新指标B关联图表配置信息失败:Err:" + err.Error()
+			return
+		}
+		logMsg += `涉及到的表格id:` + strconv.Itoa(excelInfo.ExcelInfoId) + ";"
+		replaceTotal += 1
+	}
+	return
+}
+
+// GetChildExcelInfoByParentId 根据id 获取eta表格详情
+func GetChildExcelInfoByParentId(parentId int) (items []*ExcelInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission, parent_id, balance_type, update_user_id,update_user_real_name FROM excel_info WHERE parent_id=? AND is_delete=0 order by sort asc, excel_info_id asc`
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+// ExcelInfoDetailButton 操作按钮
+type ExcelInfoDetailButton struct {
+	RefreshButton    bool `description:"是否可刷新"`
+	CopyButton       bool `description:"是否可另存为"`
+	DownloadButton   bool `description:"是否可下载"`
+	OpButton         bool `description:"是否可编辑"`
+	DeleteButton     bool `description:"是否可删除"`
+	OpEdbButton      bool `description:"是否可生成指标"`
+	RefreshEdbButton bool `description:"是否可刷新指标"`
+	OpWorkerButton   bool `description:"是否修改协作人"`
+}

+ 82 - 0
models/data_manage/excel/excel_worker.go

@@ -0,0 +1,82 @@
+package excel
+
+import (
+	"eta/eta_api/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type ExcelWorker struct {
+	ExcelWorkerId   int       `orm:"column(excel_worker_id);pk"`
+	ExcelInfoId     int       `description:"表格id"`
+	SysUserId       int       `description:"创建人"`
+	SysUserRealName string    `description:"创建人姓名"`
+	ModifyTime      time.Time `description:"修改时间"`
+	CreateTime      time.Time `description:"创建时间"`
+}
+
+func (e *ExcelWorker) TableName() string {
+	return "excel_worker"
+}
+
+// 新增 协作人
+func (e *ExcelWorker) AddWorker(excelInfoId int, addWorkers []*ExcelWorker, notDeleteWorkers []string) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+		} else {
+			_ = o.Commit()
+		}
+	}()
+	if len(notDeleteWorkers) > 0 {
+		sql := `delete from excel_worker where excel_info_id = ? and sys_user_id not in (` + utils.GetOrmInReplace(len(notDeleteWorkers)) + `)`
+		_, err = o.Raw(sql, excelInfoId, notDeleteWorkers).Exec()
+		if err != nil {
+			return
+		}
+	} else if len(notDeleteWorkers) == 0 { // 清空协作人
+		sql := `delete from excel_worker where excel_info_id = ? `
+		_, err = o.Raw(sql, excelInfoId).Exec()
+		if err != nil {
+			return
+		}
+	}
+	if len(addWorkers) > 0 {
+		_, err = o.InsertMulti(len(addWorkers), addWorkers)
+	}
+	return
+}
+
+// 修改
+func (e *ExcelWorker) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(e, cols...)
+	return
+}
+
+// 删除
+func (e *ExcelWorker) Delete() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Delete(e)
+	return
+}
+
+// 查询
+func (e *ExcelWorker) GetByExcelInfoId(excelInfoId int) (items []*ExcelWorker, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `select * from excel_worker where excel_info_id = ? `
+	_, err = o.Raw(sql, excelInfoId).QueryRows(&items)
+	return
+}
+
+// 查询
+func (e *ExcelWorker) GetBySysUserId(sysUserId int) (items []*ExcelWorker, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `select * from excel_worker where sys_user_id = ? `
+	_, err = o.Raw(sql, sysUserId).QueryRows(&items)
+	return
+}

+ 77 - 0
models/data_manage/excel/request/balance_table.go

@@ -0,0 +1,77 @@
+package request
+
+import "eta/eta_api/models/data_manage"
+
+type AddBalanceTableChartEdbItem struct {
+	DateSequenceStr string  `description:"日期序列"`
+	DataSequenceStr string  `description:"数据序列"`
+	EdbName         string  `description:"指标名称"`
+	Unit            string  `description:"指标单位"`
+	MaxData         float64 `description:"上限"`
+	MinData         float64 `description:"下限"`
+	IsOrder         bool    `description:"true:正序,false:逆序"`
+	IsAxis          int     `description:"true:左轴,false:右轴"`
+	EdbInfoType     int     `description:"true:标准指标,false:领先指标"`
+	LeadValue       int     `description:"领先值"`
+	LeadUnit        string  `description:"领先单位"`
+	FromTag         string  `description:"标签"`
+	ExcelChartEdbId int
+
+	/*	ChartStyle        string  `description:"图表类型"`
+		ChartColor        string  `description:"颜色"`
+		PredictChartColor string  `description:"预测数据的颜色"`
+		ChartWidth        float64 `description:"线条大小"`
+		Source            int     `description:"1:ETA图库;2:商品价格曲线"`
+		EdbAliasName      string  `description:"中文别名"`
+		IsConvert         int     `description:"是否数据转换 0不转 1转"`
+		ConvertType       int     `description:"数据转换类型 1乘 2除 3对数"`
+		ConvertValue      float64 `description:"数据转换值"`
+		ConvertUnit       string  `description:"数据转换单位"`
+		ConvertEnUnit     string  `description:"数据转换单位"`*/
+}
+
+type AddBalanceTableChartReq struct {
+	ExcelInfoId int `description:"表格ID"`
+	ChartInfoId int `description:"图表id,新增时传0"`
+	//ChartClassifyId int    `description:"分类id"`
+	ChartName string `description:"图表名称"`
+	ChartType int    `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图"`
+	//DateType        int    `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今"`
+	//StartDate       string `description:"自定义开始日期"`
+	//EndDate         string `description:"自定义结束日期"`
+	Calendar   string `description:"公历/农历"`
+	LeftMin    string `description:"图表左侧最小值"`
+	LeftMax    string `description:"图表左侧最大值"`
+	RightMin   string `description:"图表右侧最小值"`
+	RightMax   string `description:"图表右侧最大值"`
+	Right2Min  string `description:"图表右侧2最小值"`
+	Right2Max  string `description:"图表右侧2最大值"`
+	MinMaxSave int    `description:"是否手动保存过上下限:0-否;1-是"`
+	//BarChartInfo         BarChartInfoReq         `description:"柱方图的配置"`
+	//CorrelationChartInfo CorrelationChartInfoReq `description:"相关性图表配置"`
+	ExtraConfig       string                      `description:"图表额外配置信息,json字符串"`
+	ChartImage        string                      `description:"封面图" json:"-"`
+	SeasonExtraConfig data_manage.SeasonExtraItem `description:"季节性图表中的配置,json数据"`
+	//StartYear         int                         `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
+	//ChartThemeId int    `description:"图表应用主题ID"`
+	SourcesFrom string `description:"图表来源"`
+	//Instructions      string          `description:"图表说明"`
+	//MarkersLines      string          `description:"标识线"`
+	//MarkersAreas      string          `description:"标识区"`
+	//Unit              string          `description:"中文单位名称"`
+	//UnitEn            string          `description:"英文单位名称"`
+	ChartEdbInfoList []AddBalanceTableChartEdbItem
+}
+
+type BalanceSeasonChartLegendPreviewReq struct {
+	Calendar          string                      `description:"公历/农历"`
+	SeasonExtraConfig data_manage.SeasonExtraItem `description:"季节性图表中的配置,json数据"`
+	DataArr           []string
+	DateArr           []string
+}
+
+// AddBalanceStaticExcelInfoReq 添加平衡表静态表
+type AddBalanceStaticExcelInfoReq struct {
+	ExcelInfoId int    `description:"ETA表格ID"`
+	VersionName string `description:"静态表版本名称"`
+}

+ 13 - 1
models/data_manage/excel/request/excel_info.go

@@ -17,12 +17,13 @@ type DeleteExcelInfoReq struct {
 type AddExcelInfoReq struct {
 	ExcelInfoId     int         `description:"表格ID"`
 	ExcelName       string      `description:"表格名称"`
-	Source          int         `description:"表格来源,1:excel插件的表格,2:自定义表格,默认:1"`
+	Source          int         `description:"表格来源,1:excel插件的表格,2:自定义表格,5平衡表,默认:1"`
 	ExcelType       int         `description:"表格类型,1:指标列,2:日期列,默认:1"`
 	ExcelImage      string      `description:"表格截图"`
 	ExcelClassifyId int         `description:"分类id"`
 	Content         string      `description:"Excel表格内容"`
 	TableData       interface{} `description:"自定义表格的数据内容"`
+	ParentId        int         `description:"表格的父级id"`
 }
 
 // EditExcelInfoReq 编辑表格请求
@@ -139,3 +140,14 @@ type MarkEditExcel struct {
 	ExcelInfoId int `description:"表格id"`
 	Status      int `description:"标记状态,1:编辑中,2:编辑完成"`
 }
+
+// RenameExcelInfoReq 表格重命名请求
+type RenameExcelInfoReq struct {
+	ExcelInfoId int    `description:"ETA表格ID"`
+	ExcelName   string `description:"表格名称"`
+}
+
+type SaveExcelInfoWorkerReq struct {
+	ExcelInfoId int    `description:"ETA表格ID"`
+	SysUserIds  string `description:"协作人ID 用英文逗号拼接"`
+}

+ 40 - 0
models/data_manage/excel/request/time_table.go

@@ -0,0 +1,40 @@
+package request
+
+// TimeTableDataConfig
+// @Description: 表格配置
+type TimeTableDataConfig struct {
+	EdbInfoIdList    []int                 `description:"指标id列表,从左至右,从上到下的顺序"`
+	Sort             int                   `description:"日期排序,0:倒序,1:正序"`
+	Data             []TimeTableManualData `description:"数据列表"`
+	Num              int                   `description:"实际数据需要列出来的期数"`
+	RemoveDate       []string              `description:"不展示的日期"`
+	ManualDate       []string              `description:"手动配置的日期(未来的日期)"`
+	TableEdbInfoList []TimeTableEdbInfo    `description:"表格内指标信息"`
+	TextRowData      [][]ManualDataReq     `description:"文本列表"`
+}
+
+// TimeTableEdbInfo
+// @Description: 表格指标信息
+type TimeTableEdbInfo struct {
+	EdbInfoId    int    `description:"指标ID"`
+	Tag          string `description:"标签"`
+	EdbName      string `description:"指标名称"`
+	EdbNameEn    string `description:"英文指标名称"`
+	EdbAliasName string `description:"指标别名"`
+	Frequency    string `description:"频度"`
+	Unit         string `description:"单位"`
+	UnitEn       string `description:"英文单位"`
+}
+
+// ManualData
+// @Description: 手工文本配置
+type TimeTableManualData struct {
+	DataType            int               `description:"数据类型,1:普通的,2:插值法,3:手动输入,4:公式计算"`
+	DataTime            string            `description:"所属日期"`
+	DataTimeType        int               `description:"日期类型,1:实际日期;2:未来日期"`
+	ShowValue           string            `description:"展示值"`
+	Value               string            `description:"实际值(计算公式)"`
+	EdbInfoId           int               `description:"指标id"`
+	Tag                 string            `description:"下标"`
+	RelationEdbInfoList []RelationEdbInfo `description:"关联指标(计算公式中关联的指标,用于计算的时候去匹配)"`
+}

+ 44 - 30
models/data_manage/excel/response/excel_info.go

@@ -56,36 +56,50 @@ type TableDetailResp struct {
 
 // ExcelInfoDetail excel表格详情(前端使用)
 type ExcelInfoDetail struct {
-	ExcelInfoId      int                   `orm:"column(excel_info_id);pk"`
-	Source           int                   `description:"表格来源,1:excel插件的表格,2:自定义表格,默认:1"`
-	ExcelType        int                   `description:"表格类型,1:指标列,2:日期列,默认:1"`
-	ExcelName        string                `description:"表格名称"`
-	UniqueCode       string                `description:"表格唯一编码"`
-	ExcelClassifyId  int                   `description:"表格分类id"`
-	SysUserId        int                   `description:"操作人id"`
-	SysUserRealName  string                `description:"操作人真实姓名"`
-	Content          string                `description:"表格内容"`
-	ExcelImage       string                `description:"表格图片"`
-	FileUrl          string                `description:"表格下载地址"`
-	Sort             int                   `description:"排序字段,数字越小越排前面"`
-	IsDelete         int                   `description:"是否删除,0:未删除,1:已删除"`
-	ModifyTime       time.Time             `description:"最近修改日期"`
-	CreateTime       time.Time             `description:"创建日期"`
-	TableData        interface{}           `description:"表格内容"`
-	Button           ExcelInfoDetailButton `description:"操作权限"`
-	CanEdit          bool                  `description:"是否可编辑"`
-	Editor           string                `description:"编辑人"`
-	IsJoinPermission int                   `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
-	HaveOperaAuth    bool                  `description:"是否有数据权限"`
+	ExcelInfoId        int                          `orm:"column(excel_info_id);pk"`
+	Source             int                          `description:"表格来源,1:excel插件的表格,2:自定义表格,默认:1"`
+	ExcelType          int                          `description:"表格类型,1:指标列,2:日期列,默认:1"`
+	ExcelName          string                       `description:"表格名称"`
+	UniqueCode         string                       `description:"表格唯一编码"`
+	ExcelClassifyId    int                          `description:"表格分类id"`
+	SysUserId          int                          `description:"操作人id"`
+	SysUserRealName    string                       `description:"操作人真实姓名"`
+	Content            string                       `description:"表格内容"`
+	ExcelImage         string                       `description:"表格图片"`
+	FileUrl            string                       `description:"表格下载地址"`
+	Sort               int                          `description:"排序字段,数字越小越排前面"`
+	IsDelete           int                          `description:"是否删除,0:未删除,1:已删除"`
+	ModifyTime         time.Time                    `description:"最近修改日期"`
+	CreateTime         time.Time                    `description:"创建日期"`
+	TableData          interface{}                  `description:"表格内容"`
+	Button             excel2.ExcelInfoDetailButton `description:"操作权限"`
+	CanEdit            bool                         `description:"是否可编辑"`
+	Editor             string                       `description:"编辑人"`
+	IsJoinPermission   int                          `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	HaveOperaAuth      bool                         `description:"是否有数据权限"`
+	ParentId           int                          `description:"表格的父级id"`
+	BalanceType        int                          `description:"平衡表类型:0 动态表,1静态表"`
+	UpdateUserId       int                          `description:"更新人id"`
+	UpdateUserRealName string                       `description:"更新人真实姓名"`
+	RelExcelInfoId     int                          `description:"平衡表里静态表关联的动态表excel id"`
 }
 
-// ExcelInfoDetailButton 操作按钮
-type ExcelInfoDetailButton struct {
-	RefreshButton    bool `description:"是否可刷新"`
-	CopyButton       bool `description:"是否可另存为"`
-	DownloadButton   bool `description:"是否可下载"`
-	OpButton         bool `description:"是否可编辑"`
-	DeleteButton     bool `description:"是否可删除"`
-	OpEdbButton      bool `description:"是否可生成指标"`
-	RefreshEdbButton bool `description:"是否可刷新指标"`
+type BalanceChildTableResp struct {
+	List []*excel2.ExcelInfo
+}
+
+type BalanceTableWorkerResp struct {
+	List []*excel2.ExcelWorker
+}
+
+type BalanceTableVersionListItem struct {
+	ExcelInfoId    int    `description:"表格id"`
+	UniqueCode     string `description:"表格唯一编码"`
+	BalanceType    int    `description:"平衡表类型:0 动态表,1静态表"`
+	RelExcelInfoId int    `description:"平衡表里静态表关联的动态表excel id"`
+	VersionName    string `description:"静态表版本名称"`
+}
+
+type BalanceTableVersionListResp struct {
+	List []*BalanceTableVersionListItem
 }

+ 17 - 17
models/data_manage/excel/response/sheet.go

@@ -14,21 +14,21 @@ type FindExcelInfoResp struct {
 
 // FindExcelInfo excel的数据详情
 type FindExcelInfo struct {
-	ExcelInfoId     int                   `orm:"column(excel_info_id);pk"`
-	Source          int                   `description:"表格来源,1:excel插件的表格,2:自定义表格,默认:1"`
-	ExcelType       int                   `description:"表格类型,1:指标列,2:日期列,默认:1"`
-	ExcelName       string                `description:"表格名称"`
-	UniqueCode      string                `description:"表格唯一编码"`
-	ExcelClassifyId int                   `description:"表格分类id"`
-	SysUserId       int                   `description:"操作人id"`
-	SysUserRealName string                `description:"操作人真实姓名"`
-	ExcelImage      string                `description:"表格图片"`
-	FileUrl         string                `description:"表格下载地址"`
-	Sort            int                   `description:"排序字段,数字越小越排前面"`
-	ModifyTime      time.Time             `description:"最近修改日期"`
-	CreateTime      time.Time             `description:"创建日期"`
-	Button          ExcelInfoDetailButton `description:"操作权限"`
-	CanEdit         bool                  `description:"是否可编辑"`
-	Editor          string                `description:"编辑人"`
-	HaveOperaAuth   bool                  `description:"是否有数据权限,默认:false"`
+	ExcelInfoId     int                         `orm:"column(excel_info_id);pk"`
+	Source          int                         `description:"表格来源,1:excel插件的表格,2:自定义表格,默认:1"`
+	ExcelType       int                         `description:"表格类型,1:指标列,2:日期列,默认:1"`
+	ExcelName       string                      `description:"表格名称"`
+	UniqueCode      string                      `description:"表格唯一编码"`
+	ExcelClassifyId int                         `description:"表格分类id"`
+	SysUserId       int                         `description:"操作人id"`
+	SysUserRealName string                      `description:"操作人真实姓名"`
+	ExcelImage      string                      `description:"表格图片"`
+	FileUrl         string                      `description:"表格下载地址"`
+	Sort            int                         `description:"排序字段,数字越小越排前面"`
+	ModifyTime      time.Time                   `description:"最近修改日期"`
+	CreateTime      time.Time                   `description:"创建日期"`
+	Button          excel.ExcelInfoDetailButton `description:"操作权限"`
+	CanEdit         bool                        `description:"是否可编辑"`
+	Editor          string                      `description:"编辑人"`
+	HaveOperaAuth   bool                        `description:"是否有数据权限,默认:false"`
 }

+ 126 - 0
models/data_manage/multiple_graph_config.go

@@ -1,7 +1,14 @@
 package data_manage
 
 import (
+	"encoding/json"
+	"eta/eta_api/models/data_manage/line_equation/request"
+	"eta/eta_api/services/alarm_msg"
+	"eta/eta_api/utils"
+	"fmt"
 	"github.com/beego/beego/v2/client/orm"
+	"strconv"
+	"strings"
 	"time"
 )
 
@@ -80,3 +87,122 @@ type RollingCorrelationConfig struct {
 	CalculateValue int    `description:"计算窗口"`
 	CalculateUnit  string `description:"计算频度"`
 }
+
+// ReplaceEdbInfoInLineEquationMultipleGraphConfig 获取拟合方程配置
+func ReplaceEdbInfoInLineEquationMultipleGraphConfig(oldEdbInfo, newEdbInfo *EdbInfo) (replaceConfigTotal int, err error) {
+	var errmsg string
+	logMsg := `` // 记录替换的日志
+
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+			if logMsg != `` {
+				utils.FileLog.Info(fmt.Sprintf("替换拟合方程中的指标记录 替换总数%d,旧的指标id:%d,新的指标id:%d;%s", replaceConfigTotal, oldEdbInfo.EdbInfoId, newEdbInfo.EdbInfoId, logMsg))
+			}
+		}
+		if errmsg != "" {
+			fmt.Println("errmsg:" + errmsg)
+		}
+		if err != nil && errmsg != "" {
+			go alarm_msg.SendAlarmMsg("替换拟合方程中的指标记录失败提醒,errmsg:"+errmsg, 3)
+		}
+	}()
+	//替换multiple_graph_config中的指标
+	{
+		multipleGraphConfigList := make([]*MultipleGraphConfig, 0)
+		csql := `SELECT * FROM multiple_graph_config WHERE edb_info_id_a=0 AND edb_info_id_b=0 AND curve !=""`
+		_, err = to.Raw(csql).QueryRows(&multipleGraphConfigList)
+		if err != nil {
+			errmsg = "获取指标关联图表配置信息失败:Err:" + err.Error()
+			return
+		}
+		if len(multipleGraphConfigList) == 0 {
+			return
+		}
+		updateList := make([]*MultipleGraphConfig, 0)
+		configIds := make([]int, 0)
+		configIdStr := make([]string, 0)
+		for _, mv := range multipleGraphConfigList {
+			if !strings.Contains(mv.Curve, strconv.Itoa(oldEdbInfo.EdbInfoId)) {
+				continue
+			}
+			//解析curve内容
+			var lineChartInfoConfig request.LineChartInfoReq
+			if err = json.Unmarshal([]byte(mv.Curve), &lineChartInfoConfig); err != nil {
+				errmsg = "获取跨品种分析配置信息失败:Err:" + err.Error()
+				return
+			}
+			updateFlag := false
+			// 遍历跨品种配置里的指标列表
+			for k, edbInfoId := range lineChartInfoConfig.XEdbInfoIdList {
+				if edbInfoId == oldEdbInfo.EdbInfoId {
+					updateFlag = true
+					lineChartInfoConfig.XEdbInfoIdList[k] = newEdbInfo.EdbInfoId
+				}
+			}
+			for k, edbInfoId := range lineChartInfoConfig.YEdbInfoIdList {
+				if edbInfoId == oldEdbInfo.EdbInfoId {
+					updateFlag = true
+					lineChartInfoConfig.YEdbInfoIdList[k] = newEdbInfo.EdbInfoId
+				}
+			}
+			if !updateFlag {
+				continue
+			}
+			newCurve, _ := json.Marshal(lineChartInfoConfig)
+			mv.Curve = string(newCurve)
+			//判断如果达到1000个数,则执行更新语句
+			updateList = append(updateList, mv)
+			configIds = append(configIds, mv.MultipleGraphConfigId)
+			configIdStr = append(configIdStr, strconv.Itoa(mv.MultipleGraphConfigId))
+			if len(configIds) >= 10 {
+				numStr := utils.GetOrmInReplace(len(configIds))
+				// 准备批量更新的 SQL 语句
+				updateSQL := `UPDATE multiple_graph_config SET   
+    curve = CASE multiple_graph_config_id  `
+				for _, updateItem := range updateList {
+					updateSQL += `WHEN ` + strconv.Itoa(updateItem.MultipleGraphConfigId) + ` THEN '` + updateItem.Curve + `' `
+				}
+				updateSQL += `END, modify_time = ? WHERE multiple_graph_config_id IN (` + numStr + `)`
+				_, err = to.Raw(updateSQL, time.Now(), configIds).Exec()
+				if err != nil {
+					errmsg = "更新指标A关联图表配置信息失败:Err:" + err.Error()
+					return
+				}
+				logMsg += `涉及到的配置id:` + strings.Join(configIdStr, ",") + ";"
+				replaceConfigTotal += len(configIds)
+				configIds = make([]int, 0)
+				updateList = make([]*MultipleGraphConfig, 0)
+				configIdStr = make([]string, 0)
+			}
+		}
+		if len(configIds) > 0 {
+			numStr := utils.GetOrmInReplace(len(configIds))
+			// 准备批量更新的 SQL 语句
+			updateSQL := `UPDATE multiple_graph_config SET   
+    curve = CASE multiple_graph_config_id  `
+			for _, updateItem := range updateList {
+				updateSQL += `WHEN ` + strconv.Itoa(updateItem.MultipleGraphConfigId) + ` THEN '` + updateItem.Curve + `' `
+			}
+			updateSQL += `END, modify_time = ? WHERE multiple_graph_config_id IN (` + numStr + `)`
+			_, err = to.Raw(updateSQL, time.Now(), configIds).Exec()
+			if err != nil {
+				errmsg = "更新指标A关联图表配置信息失败:Err:" + err.Error()
+				return
+			}
+			logMsg += `涉及到的配置id:` + strings.Join(configIdStr, ",") + ";"
+			replaceConfigTotal += len(configIds)
+			configIds = make([]int, 0)
+			updateList = make([]*MultipleGraphConfig, 0)
+			configIdStr = make([]string, 0)
+		}
+	}
+	return
+}

+ 136 - 0
models/data_manage/multiple_graph_config_chart_mapping.go

@@ -1,7 +1,12 @@
 package data_manage
 
 import (
+	"eta/eta_api/services/alarm_msg"
+	"eta/eta_api/utils"
+	"fmt"
 	"github.com/beego/beego/v2/client/orm"
+	"strconv"
+	"strings"
 	"time"
 )
 
@@ -60,3 +65,134 @@ func GetMultipleGraphConfigChartMappingListById(configId int) (items []*Multiple
 
 	return
 }
+
+// ReplaceMultipleGraphConfigChartEdb 替换相关性分析配置中的指标
+func ReplaceMultipleGraphConfigChartEdb(oldEdbInfo, newEdbInfo *EdbInfo) (replaceConfigTotal int, err error) {
+	var errmsg string
+	logMsg := `` // 记录替换的日志
+
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+			if logMsg != `` {
+				utils.FileLog.Info(fmt.Sprintf("替换相关性分析中的指标记录 替换总数%d,旧的指标id:%d,新的指标id:%d;%s", replaceConfigTotal, oldEdbInfo.EdbInfoId, newEdbInfo.EdbInfoId, logMsg))
+			}
+		}
+		if errmsg != "" {
+			fmt.Println("errmsg:" + errmsg)
+		}
+		if err != nil && errmsg != "" {
+			go alarm_msg.SendAlarmMsg("替换统计分析配置中的指标记录失败提醒,errmsg:"+errmsg, 3)
+		}
+	}()
+	//替换multiple_graph_config中的指标
+	multipleGraphConfigList := make([]*MultipleGraphConfig, 0)
+	csql := `SELECT * FROM multiple_graph_config WHERE (edb_info_id_a=? or edb_info_id_b=?)`
+	_, err = to.Raw(csql, oldEdbInfo.EdbInfoId, oldEdbInfo.EdbInfoId).QueryRows(&multipleGraphConfigList)
+	if err != nil {
+		errmsg = "获取指标关联图表配置信息失败:Err:" + err.Error()
+		return
+	}
+	if len(multipleGraphConfigList) > 0 {
+		replaceConfigTotal = len(multipleGraphConfigList)
+		configIdMap := make(map[int]int)
+		configIds := make([]int, 0)
+		configIdStr := make([]string, 0)
+		for _, mv := range multipleGraphConfigList {
+			if _, ok := configIdMap[mv.MultipleGraphConfigId]; !ok {
+				//判断如果达到1000个数,则执行更新语句
+				configIds = append(configIds, mv.MultipleGraphConfigId)
+				configIdStr = append(configIdStr, strconv.Itoa(mv.MultipleGraphConfigId))
+				configIdMap[mv.MultipleGraphConfigId] = mv.MultipleGraphConfigId
+				if len(configIds) >= 1000 {
+					numStr := utils.GetOrmInReplace(len(configIds))
+					//更新配置中的指标A
+					sql := `UPDATE multiple_graph_config SET edb_info_id_a=?, modify_time=? WHERE edb_info_id_a=? and multiple_graph_config_id IN (` + numStr + `)`
+					_, err = to.Raw(sql, newEdbInfo.EdbInfoId, time.Now(), oldEdbInfo.EdbInfoId, configIds).Exec()
+					if err != nil {
+						errmsg = "更新指标A关联图表配置信息失败:Err:" + err.Error()
+						return
+					}
+					//更新配置中的指标B
+					sql = `UPDATE multiple_graph_config SET edb_info_id_b=?, modify_time=? WHERE edb_info_id_b=? and multiple_graph_config_id IN (` + numStr + `)`
+					_, err = to.Raw(sql, newEdbInfo.EdbInfoId, time.Now(), oldEdbInfo.EdbInfoId, configIds).Exec()
+					if err != nil {
+						errmsg = "更新指标B关联图表配置信息失败:Err:" + err.Error()
+						return
+					}
+
+					// 更新
+					// 更新指标id
+					sql = `UPDATE multiple_graph_config_edb_mapping SET edb_info_id=?, modify_time=? WHERE edb_info_id=? and multiple_graph_config_id IN (` + numStr + `)`
+					_, err = to.Raw(sql, newEdbInfo.EdbInfoId, time.Now(), oldEdbInfo.EdbInfoId, configIds).Exec()
+					if err != nil {
+						errmsg = "更新指标id关联图表配置信息失败:Err:" + err.Error()
+						return
+					}
+					logMsg += `涉及到的配置id:` + strings.Join(configIdStr, ",") + ";"
+					configIds = make([]int, 0)
+					configIdStr = make([]string, 0)
+				}
+			}
+		}
+		if len(configIds) > 0 {
+			numStr := utils.GetOrmInReplace(len(configIds))
+			//更新配置中的指标A
+			sql := `UPDATE multiple_graph_config SET edb_info_id_a=?, modify_time=? WHERE edb_info_id_a=? and multiple_graph_config_id IN (` + numStr + `)`
+			_, err = to.Raw(sql, newEdbInfo.EdbInfoId, time.Now(), oldEdbInfo.EdbInfoId, configIds).Exec()
+			if err != nil {
+				errmsg = "更新指标A关联图表配置信息失败:Err:" + err.Error()
+				return
+			}
+			//更新配置中的指标B
+			sql = `UPDATE multiple_graph_config SET edb_info_id_b=?, modify_time=? WHERE edb_info_id_b=? and multiple_graph_config_id IN (` + numStr + `)`
+			_, err = to.Raw(sql, newEdbInfo.EdbInfoId, time.Now(), oldEdbInfo.EdbInfoId, configIds).Exec()
+			if err != nil {
+				errmsg = "更新指标B关联图表配置信息失败:Err:" + err.Error()
+				return
+			}
+			// 更新指标id
+			sql = `UPDATE multiple_graph_config_edb_mapping SET edb_info_id=?, modify_time=? WHERE edb_info_id=? and multiple_graph_config_id IN (` + numStr + `)`
+			_, err = to.Raw(sql, newEdbInfo.EdbInfoId, time.Now(), oldEdbInfo.EdbInfoId, configIds).Exec()
+			if err != nil {
+				errmsg = "更新指标id关联图表配置信息失败:Err:" + err.Error()
+				return
+			}
+
+			logMsg += `涉及到的配置id:` + strings.Join(configIdStr, ",") + ";"
+			configIds = make([]int, 0)
+			configIdStr = make([]string, 0)
+		}
+	}
+
+	// 更新相关性图表中的
+	sql := `UPDATE chart_info_correlation SET edb_info_id_first=?, modify_time=? WHERE edb_info_id_first=?`
+	_, err = to.Raw(sql, newEdbInfo.EdbInfoId, time.Now(), oldEdbInfo.EdbInfoId).Exec()
+	if err != nil {
+		errmsg = "更新相关性图表中的指标id关联失败:Err:" + err.Error()
+		return
+	}
+	sql = `UPDATE chart_info_correlation SET edb_info_id_second=?, modify_time=? WHERE edb_info_id_second=?`
+	_, err = to.Raw(sql, newEdbInfo.EdbInfoId, time.Now(), oldEdbInfo.EdbInfoId).Exec()
+	if err != nil {
+		errmsg = "更新相关性图表中的指标id关联失败:Err:" + err.Error()
+		return
+	}
+
+	// 替换拟合方程曲线替换指标
+	// 替换跨品种标签绑定得到指标ID
+	sql = `UPDATE chart_tag_variety SET edb_info_id=?, modify_time=? WHERE edb_info_id=?`
+	_, err = to.Raw(sql, newEdbInfo.EdbInfoId, time.Now(), oldEdbInfo.EdbInfoId).Exec()
+	if err != nil {
+		errmsg = "更新指标id关联跨品种分析标签失败:Err:" + err.Error()
+		return
+	}
+	return
+}

+ 13 - 2
models/data_manage/my_chart.go

@@ -870,7 +870,7 @@ func GetMyChartListGroupByCharyInfoIdAndAdminIdByCondition(condition string, par
 func GetRelationChartListByCondition(condition string, pars []interface{}, startSize, pageSize int) (item []*ChartInfoView, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT a.* FROM chart_info AS a 
-	JOIN chart_edb_mapping AS b on a.chart_info_id = b.chart_info_id WHERE 1=1 `
+	WHERE 1=1 `
 	if condition != "" {
 		sql += condition
 	}
@@ -883,7 +883,7 @@ func GetRelationChartListByCondition(condition string, pars []interface{}, start
 func GetRelationChartListCountByCondition(condition string, pars []interface{}) (count int, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT COUNT(1) AS count FROM chart_info AS a 
-	JOIN chart_edb_mapping AS b on a.chart_info_id = b.chart_info_id WHERE 1=1 `
+	WHERE 1=1 `
 	if condition != "" {
 		sql += condition
 	}
@@ -903,6 +903,17 @@ func GetChartInfoByIdList(chartInfoIdList []int) (items []*ChartInfo, err error)
 	return
 }
 
+func GetChartInfoViewByIdList(chartInfoIdList []int) (items []*ChartInfoView, err error) {
+	num := len(chartInfoIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM chart_info WHERE chart_info_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, chartInfoIdList).QueryRows(&items)
+	return
+}
+
 // GetMyChartClassifyByClassifyId 主键获取分类
 func GetMyChartClassifyByClassifyId(classifyId int) (item *MyChartClassify, err error) {
 	o := orm.NewOrmUsingDB("data")

+ 2 - 2
models/data_manage/trade_analysis/trade_analysis.go

@@ -94,7 +94,7 @@ func GetExchangeClassify(exchange string) (list []TradeClassifyName, err error)
 	if exchange == "zhengzhou" {
 		orderStr = "classify_name asc"
 	}
-	sql := `SELECT classify_name, classify_type FROM ` + tableName + ` WHERE rank <=20 and rank > 0 GROUP BY classify_name, classify_type  `
+	sql := "SELECT classify_name, classify_type FROM " + tableName + " WHERE `rank` <=20 and `rank` > 0 GROUP BY classify_name, classify_type  "
 	sql += ` ORDER BY ` + orderStr
 
 	o := orm.NewOrmUsingDB("data")
@@ -152,7 +152,7 @@ type GetPositionTopListItem struct {
 
 func GetTradePositionTop(exchange string, classifyName, classifyType, dataTime string) (list []TradePositionTop, err error) {
 	tableName := "trade_position_" + exchange + "_top"
-	sql := `SELECT * FROM ` + tableName + ` WHERE classify_name=? and classify_type=? and data_time=? and rank <=20 and rank > 0 ORDER BY deal_value desc`
+	sql := `SELECT * FROM ` + tableName + " WHERE classify_name=? and classify_type=? and data_time=? and `rank` <=20 and `rank` > 0 ORDER BY deal_value desc"
 
 	o := orm.NewOrmUsingDB("data")
 	_, err = o.Raw(sql, classifyName, classifyType, dataTime).QueryRows(&list)

+ 3 - 0
models/db.go

@@ -491,6 +491,9 @@ func initExcel() {
 		new(excel.ExcelSheet),      //ETA excel sheet
 		new(excel.ExcelSheetData),  //ETA excel sheet data
 		new(excel.ExcelEdbMapping), //ETA excel 与 指标 的关系表
+		new(excel.ExcelWorker),     // 平衡表协作人表格
+		new(excel.ExcelChartEdb),   // 平衡表做图指标
+		new(excel.ExcelChartData),  // 平衡表作图数据
 	)
 }
 

+ 3 - 1
models/ppt_english/ppt_english.go

@@ -1,9 +1,10 @@
 package ppt_english
 
 import (
+	"time"
+
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
-	"time"
 )
 
 // PptEnglish 表
@@ -25,6 +26,7 @@ type PptEnglish struct {
 	IsShare       int8      `description:"是否分享,0:不分享,1:分享"`
 	PublishTime   time.Time `description:"发布时间"`
 	CoverContent  string    `description:"PPT内容-JSON"`
+	PptPage       int       `description:"PPT页数"`
 	TitleSetting  string    `description:"PPT标题设置"`
 }
 

+ 27 - 1
models/ppt_english/ppt_english_group_mapping.go

@@ -1,8 +1,9 @@
 package ppt_english
 
 import (
-	"github.com/beego/beego/v2/client/orm"
 	"time"
+
+	"github.com/beego/beego/v2/client/orm"
 )
 
 // ppt目录和ppt 映射表
@@ -16,6 +17,7 @@ type PptEnglishGroupMapping struct {
 	AdminId         int       `description:"移动ppt到该目录的系统用户id"`
 	AdminRealName   string    `description:"系统用户名称"`
 	ChildGroupPptId int64     `description:"设置共享后的新映射ID"`
+	IsMoved         bool      `description:"true表示改PPT被人为移动过"`
 }
 
 // AddPptGroupMapping 新增目录和ppt的映射关系
@@ -52,6 +54,22 @@ func GetPptMappingListByGroupIds(groupIds []int64) (list []*PptEnglishGroupMappi
 	return
 }
 
+// GetPptMappingByPptId 查询目录下, pptId对应的目录映射关系
+func GetPptMappingByPptId(pptId int64) (item *PptEnglishGroupMapping, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` select * from ppt_english_group_mapping where ppt_id=? `
+	err = o.Raw(sql, pptId).QueryRow(&item)
+	return
+}
+
+// GetPptMappingListByGroupId 查询目录下,ppt列表, 降序排列
+func GetPptMappingListByGroupIdDesc(groupId int64) (list []*PptEnglishGroupMapping, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `select group_ppt_id, group_id, ppt_id, ppt_sort, admin_id, admin_real_name, create_time from ppt_english_group_mapping where group_id=? order by ppt_sort desc, group_ppt_id desc `
+	_, err = o.Raw(sql, groupId).QueryRows(&list)
+	return
+}
+
 // GetPptMappingByGroupPptId 查询根据映射ID,查询单个映射关系
 func GetPptMappingByGroupPptId(groupPptId int64, adminId int) (item *PptEnglishGroupMapping, err error) {
 	o := orm.NewOrmUsingDB("rddp")
@@ -60,6 +78,14 @@ func GetPptMappingByGroupPptId(groupPptId int64, adminId int) (item *PptEnglishG
 	return
 }
 
+// GetPptMappingByGroupPptId 查询根据映射ID,查询单个映射关系数量
+func GetPptMappingCountByGroupPptId(groupPptId int64, adminId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `select COUNT(*) AS count from ppt_english_group_mapping where group_ppt_id=? and admin_id=?`
+	err = o.Raw(sql, groupPptId, adminId).QueryRow(&count)
+	return
+}
+
 // Update 更新ppt目录映射的基本信息
 func (item *PptEnglishGroupMapping) Update(cols []string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")

+ 4 - 1
models/ppt_v2.go

@@ -2,9 +2,10 @@ package models
 
 import (
 	"eta/eta_api/models/ppt_english"
+	"time"
+
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
-	"time"
 )
 
 // PptV2 表
@@ -28,6 +29,7 @@ type PptV2 struct {
 	IsShare       int8      `description:"是否分享,0:不分享,1:分享"`
 	PublishTime   time.Time `description:"发布时间"`
 	CoverContent  string    `description:"PPT内容-JSON"`
+	PptPage       int       `description:"PPT页数"`
 	TitleSetting  string    `description:"PPT标题设置"`
 }
 
@@ -52,6 +54,7 @@ type PptV2Item struct {
 	IsShare       int8      `description:"是否分享,0:不分享,1:分享"`
 	PublishTime   time.Time `description:"发布时间"`
 	CoverContent  string    `description:"PPT内容-JSON"`
+	PptPage       int       `description:"PPT页数"`
 	TitleSetting  string    `description:"PPT标题设置"`
 }
 

+ 19 - 1
models/ppt_v2_group_mapping.go

@@ -1,8 +1,9 @@
 package models
 
 import (
-	"github.com/beego/beego/v2/client/orm"
 	"time"
+
+	"github.com/beego/beego/v2/client/orm"
 )
 
 // ppt目录和ppt 映射表
@@ -16,6 +17,7 @@ type PptV2GroupMapping struct {
 	AdminId         int       `description:"移动ppt到该目录的系统用户id"`
 	AdminRealName   string    `description:"系统用户名称"`
 	ChildGroupPptId int64     `description:"设置共享后的新映射ID"`
+	IsMoved         bool      `description:"true表示改PPT被人为移动过"`
 }
 
 // AddPptGroupMapping 新增目录和ppt的映射关系
@@ -60,6 +62,22 @@ func GetPptMappingByGroupPptId(groupPptId int64, adminId int) (item *PptV2GroupM
 	return
 }
 
+// GetPptMappingByGroupPptId 查询根据映射ID,查询单个映射关系数量
+func GetPptMappingByGroupPptCountId(groupPptId int64, adminId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `select COUNT(*) AS count from ppt_v2_group_mapping where group_ppt_id=? and admin_id=?`
+	err = o.Raw(sql, groupPptId, adminId).QueryRow(&count)
+	return
+}
+
+// GetPptMappingByPptId 查询根据ppt_Id, 查询当映单个映射关系
+func GetPptMappingByPptId(pptId int64) (item *PptV2GroupMapping, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` select * from ppt_v2_group_mapping where ppt_id=? `
+	err = o.Raw(sql, pptId).QueryRow(&item)
+	return
+}
+
 // Update 更新ppt目录映射的基本信息
 func (item *PptV2GroupMapping) Update(cols []string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")

+ 2 - 2
models/report.go

@@ -230,12 +230,12 @@ func GetReportByIds(reportIds string) (list []*ReportDetail, err error) {
 }
 
 // GetSimpleReportByIds 根据报告ID查询报告基本信息
-func GetSimpleReportByIds(reportIds []int) (list []*ReportDetail, err error) {
+func GetSimpleReportByIds(reportIds []int) (list []*Report, err error) {
 	if len(reportIds) == 0 {
 		return
 	}
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT id, title FROM report WHERE id IN (` + utils.GetOrmInReplace(len(reportIds)) + `)`
+	sql := `SELECT id, title, report_code FROM report WHERE id IN (` + utils.GetOrmInReplace(len(reportIds)) + `)`
 	_, err = o.Raw(sql, reportIds).QueryRows(&list)
 	return
 }

+ 26 - 0
models/sandbox/sandbox.go

@@ -402,3 +402,29 @@ func GetFirstSandboxByClassifyId(classifyId int) (item *Sandbox, err error) {
 	err = o.Raw(sql, classifyId).QueryRow(&item)
 	return
 }
+
+// UpdateSandboxContent 更新沙盘内容
+func UpdateSandboxContent(list []Sandbox) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//循环更新沙盘内容
+	for _, sandbox := range list {
+		_, err = to.Update(&sandbox, "Content", "ModifyTime")
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}

+ 12 - 5
models/sandbox/sandbox_classify.go

@@ -247,10 +247,17 @@ type SandboxLinkCheckReq struct {
 	ReportIdList    []int `description:"报告id列表"`
 }
 
+type SandboxLinkCheckItem struct {
+	Id         int    `description:"id"`
+	Name       string `description:"名称"`
+	UniqueCode string `description:"唯一编码"`
+	ClassifyId int    `description:"分类id"`
+}
+
 type SandboxLinkCheckResp struct {
-	EdbInfoIdList   []int `description:"指标id列表"`
-	ChartInfoIdList []int `description:"图库id列表"`
-	ReportIdList    []int `description:"报告id列表"`
+	EdbInfoIdList   []*SandboxLinkCheckItem `description:"指标id列表"`
+	ChartInfoIdList []*SandboxLinkCheckItem `description:"图库id列表"`
+	ReportIdList    []*SandboxLinkCheckItem `description:"报告id列表"`
 }
 
 // 获取所有子级分类id
@@ -272,7 +279,7 @@ WHERE sc.parent_id = @pv
 // UpdateSandboxClassifyChartPermissionById 根据沙盘id更新品种
 func UpdateSandboxClassifyChartPermissionById(ChartPermissionId int, ChartPermissionName, Ids string) (err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := ` UPDATE sandbox_classify SET chart_permission_id = ?, chart_permission_name = ? WHERE sandbox_classify_id IN ( `+Ids +` ) `
+	sql := ` UPDATE sandbox_classify SET chart_permission_id = ?, chart_permission_name = ? WHERE sandbox_classify_id IN ( ` + Ids + ` ) `
 	_, err = o.Raw(sql, ChartPermissionId, ChartPermissionName).Exec()
 	return
-}
+}

+ 180 - 0
routers/commentsRouter.go

@@ -817,6 +817,87 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "BalanceSeasonChartLegendPreview",
+            Router: `/excel_info/balance/chartLegend/preview`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "BalanceChartInfoAdd",
+            Router: `/excel_info/balance/chart_add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "BalanceChartInfoBaseEdit",
+            Router: `/excel_info/balance/chart_base_edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "DeleteBalanceChart",
+            Router: `/excel_info/balance/chart_del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "BalanceChartInfoEdit",
+            Router: `/excel_info/balance/chart_edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "GetBalanceChartList",
+            Router: `/excel_info/balance/chart_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "AddStaticExcel",
+            Router: `/excel_info/balance/static/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "BalanceVersionList",
+            Router: `/excel_info/balance/version`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "ModifyBalanceExcelVersion",
+            Router: `/excel_info/balance/version/modify`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
         beego.ControllerComments{
             Method: "GetBaseEdbInfo",
@@ -826,6 +907,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "GetChildTable",
+            Router: `/excel_info/child_table`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
         beego.ControllerComments{
             Method: "Copy",
@@ -925,6 +1015,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "Rename",
+            Router: `/excel_info/rename`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
         beego.ControllerComments{
             Method: "BatchRefresh",
@@ -1015,6 +1114,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "GetWorker",
+            Router: `/excel_info/worker`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "SaveExcelWorker",
+            Router: `/excel_info/worker/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/future_good:FutureGoodChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/future_good:FutureGoodChartClassifyController"],
         beego.ControllerComments{
             Method: "AddChartClassify",
@@ -3058,6 +3175,69 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "CCFClassify",
+            Router: `/ccf/classify`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "ExportCCFList",
+            Router: `/ccf/export`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "CCFIndexData",
+            Router: `/ccf/index/data`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "CCFSearchList",
+            Router: `/ccf/search_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "CCFSingleData",
+            Router: `/ccf/single_data`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "CCFStockClassify",
+            Router: `/ccf/stock/classify`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "CCFStockTable",
+            Router: `/ccf/stock/table`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
         beego.ControllerComments{
             Method: "BatchChartInfoRefresh",

+ 135 - 9
services/data/chart_info.go

@@ -1350,7 +1350,22 @@ func BatchChartInfoRefreshV2(chartInfoList []*data_manage.ChartInfo, redisKey st
 	if totalEdbInfo > 20 { // 关联指标过多的时候,异步刷新
 		isAsync = true
 		isDeleteCache = false // 不删除缓存
-		go refreshChartEdbInfo(chartInfoList, redisKey, newBaseEdbInfoArr, newBasePredictEdbInfoArr, newCalculateMap, newPredictCalculateMap, calculateArr, predictCalculateArr)
+		//go refreshChartEdbInfo(chartInfoList, redisKey, newBaseEdbInfoArr, newBasePredictEdbInfoArr, newCalculateMap, newPredictCalculateMap, calculateArr, predictCalculateArr)
+
+		// 加入队列刷新
+		var refreshItem EdbQueueRefreshReq
+		refreshItem.RefreshKey = redisKey
+		for _, v := range chartInfoList {
+			key := utils.HZ_CHART_LIB_DETAIL + v.UniqueCode
+			refreshItem.ItemRefreshKeys = append(refreshItem.ItemRefreshKeys, key)
+		}
+		refreshItem.BaseEdbInfoArr = newBaseEdbInfoArr
+		refreshItem.BasePredictEdbInfoArr = newBasePredictEdbInfoArr
+		refreshItem.CalculateMap = newCalculateMap
+		refreshItem.PredictCalculateMap = newPredictCalculateMap
+		refreshItem.CalculateArr = calculateArr
+		refreshItem.PredictCalculateArr = predictCalculateArr
+		go PushEdb2Refresh(refreshItem)
 	} else {
 		err = edbInfoRefreshAll(false, newBaseEdbInfoArr, newBasePredictEdbInfoArr, newCalculateMap, newPredictCalculateMap, calculateArr, predictCalculateArr)
 
@@ -1395,15 +1410,15 @@ func refreshChartEdbInfo(chartInfoList []*data_manage.ChartInfo, redisKey string
 // @author: Roc
 // @datetime 2023-11-30 13:30:26
 // @param source string
-// @param reportId int
-// @param reportChapterId int
+// @param primaryId int
+// @param subId int
 // @return string
-func GetBatchChartRefreshKey(source string, reportId, reportChapterId int) string {
+func GetBatchChartRefreshKey(source string, primaryId, subId int) string {
 	if source == `` {
 		return ``
 	}
 
-	return fmt.Sprint("batch_chart_refresh:", source, ":", reportId, ":", reportChapterId)
+	return fmt.Sprint("batch_chart_refresh:", source, ":", primaryId, ":", subId)
 }
 
 // CheckBatchChartRefreshResult
@@ -1411,12 +1426,12 @@ func GetBatchChartRefreshKey(source string, reportId, reportChapterId int) strin
 // @author: Roc
 // @datetime 2023-11-30 13:30:26
 // @param source string
-// @param reportId int
-// @param reportChapterId int
+// @param primaryId int
+// @param subId int
 // @return string
-func CheckBatchChartRefreshResult(source string, reportId, reportChapterId int) (refreshResult bool) {
+func CheckBatchChartRefreshResult(source string, primaryId, subId int) (refreshResult bool) {
 	refreshResult = true
-	redisKey := GetBatchChartRefreshKey(source, reportId, reportChapterId)
+	redisKey := GetBatchChartRefreshKey(source, primaryId, subId)
 	if redisKey != `` {
 		// 如果找到了key,那么就是还在更新中
 		isOk := utils.Rc.IsExist(redisKey)
@@ -3081,3 +3096,114 @@ func getEdbConvertDataMapList(chartInfoId, chartType int, calendar, startDate, e
 
 	return
 }
+
+// GetChartEdbDataV2 获取图表的指标数据
+func GetChartEdbDataV2(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping, extraConfigStr string, seasonExtraConfig string, chartInfoData ChartInfoDataShow) (edbList []*data_manage.ChartEdbInfoMapping, xEdbIdValue []int, yDataList []data_manage.YData, dataResp interface{}, err error, errMsg string) {
+	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
+	xEdbIdValue = make([]int, 0)
+	yDataList = make([]data_manage.YData, 0)
+
+	var extraConfig interface{}
+	switch chartType {
+	case 7: // 柱形图
+		var barConfig data_manage.BarChartInfoReq
+		if extraConfigStr == `` {
+			errMsg = "柱方图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &barConfig)
+		if err != nil {
+			errMsg = "柱方图配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+		extraConfig = barConfig
+	case 10: // 截面散点图
+		var tmpExtraConfig data_manage.SectionScatterReq
+		if extraConfigStr == `` {
+			errMsg = "截面散点图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &tmpExtraConfig)
+		if err != nil {
+			errMsg = "截面散点配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+
+		extraConfig = tmpExtraConfig
+	case utils.CHART_TYPE_RADAR:
+		var barConfig data_manage.RadarChartInfoReq
+		if extraConfigStr == `` {
+			errMsg = "雷达图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &barConfig)
+		if err != nil {
+			errMsg = "雷达图配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+		extraConfig = barConfig
+	default:
+		xEdbIdValue = make([]int, 0)
+		yDataList = make([]data_manage.YData, 0)
+	}
+
+	// 指标对应的所有数据
+	edbDataListMap, edbList, err := chartInfoData.GetEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList, seasonExtraConfig)
+	if err != nil {
+		return
+	}
+
+	// 特殊图形数据处理
+	switch chartType {
+	case 7: // 柱形图
+		barChartConf := extraConfig.(data_manage.BarChartInfoReq)
+		xEdbIdValue, yDataList, err = BarChartData(mappingList, edbDataListMap, barChartConf.DateList, barChartConf.Sort)
+
+		for k := range yDataList {
+			yDataList[k].Unit = barChartConf.Unit
+			yDataList[k].UnitEn = barChartConf.UnitEn
+		}
+
+		for _, v := range edbList {
+			// 指标别名
+			if barChartConf.EdbInfoIdList != nil && len(barChartConf.EdbInfoIdList) > 0 {
+				for _, reqEdb := range barChartConf.EdbInfoIdList {
+					if v.EdbInfoId == reqEdb.EdbInfoId {
+						v.EdbAliasName = reqEdb.Name
+					}
+				}
+			}
+		}
+	case 10: // 截面散点图
+		sectionScatterConf := extraConfig.(data_manage.SectionScatterReq)
+		xEdbIdValue, dataResp, err = GetSectionScatterChartData(chartInfoId, mappingList, edbDataListMap, sectionScatterConf)
+
+		var tmpExtraConfig data_manage.SectionScatterReq
+		if extraConfigStr == `` {
+			errMsg = "截面散点图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &tmpExtraConfig)
+		if err != nil {
+			errMsg = "截面散点配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+
+		// 这个数据没有必要返回给前端
+		for _, v := range edbList {
+			v.DataList = nil
+		}
+	case utils.CHART_TYPE_RADAR: //雷达图
+		radarConf := extraConfig.(data_manage.RadarChartInfoReq)
+		xEdbIdValue, dataResp, err = RadarChartData(mappingList, edbDataListMap, radarConf)
+	}
+	return
+}

+ 1756 - 0
services/data/chart_info_excel_balance.go

@@ -0,0 +1,1756 @@
+package data
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/data_manage/chart_theme"
+	excelModel "eta/eta_api/models/data_manage/excel"
+	"eta/eta_api/models/data_manage/excel/request"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data/data_manage_permission"
+	"eta/eta_api/utils"
+	"fmt"
+	"math"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AddBalanceExcelChart 添加平衡表图表
+func AddBalanceExcelChart(excelInfo *excelModel.ExcelInfo, req request.AddBalanceTableChartReq, sysUser *system.Admin, dataListMap map[int][]*data_manage.EdbDataList) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
+
+	/*// 操作权限校验, 增加协作人判断
+	{
+		checkExcelInfo := excelInfo
+		checkExcelInfoId := excelInfo.ExcelInfoId
+		if excelInfo.BalanceType == 1 {
+			checkExcelInfoId = excelInfo.RelExcelInfoId
+		} else {
+			if excelInfo.ParentId > 0 {
+				checkExcelInfoId = excelInfo.ParentId
+			}
+		}
+		if checkExcelInfoId != excelInfo.ExcelInfoId {
+			checkExcelInfo, err = excelModel.GetExcelInfoById(checkExcelInfoId)
+			if err != nil {
+				errMsg = "获取平衡表格信息失败"
+				err = fmt.Errorf("获取平衡表格信息失败,Err:" + err.Error())
+				return
+			}
+		}
+		// 数据权限
+		haveOperaAuth, e := data_manage_permission.CheckExcelPermissionByExcelInfoId(checkExcelInfo.ExcelInfoId, checkExcelInfo.ExcelClassifyId, checkExcelInfo.IsJoinPermission, sysUser.AdminId)
+		if e != nil {
+			errMsg = "获取ETA表格权限失败"
+			err = fmt.Errorf("获取表格权限信息失败,Err" + e.Error())
+			return
+		}
+
+		button := excelService.GetBalanceExcelInfoOpButton(sysUser.AdminId, checkExcelInfo.SysUserId, haveOperaAuth, checkExcelInfo.ExcelInfoId)
+		if !button.OpButton {
+			errMsg = "无操作权限"
+			isSendEmail = false
+			return
+		}
+	}*/
+
+	if len(req.ChartEdbInfoList) == 0 {
+		errMsg = "图表数据不能为空!"
+		err = fmt.Errorf("图表数据不能为空!%s", err.Error())
+		return
+	}
+	chartEdbList := make([]*excelModel.AddChartEdbAndDataItem, 0)
+	for k, chartEdb := range req.ChartEdbInfoList {
+		excelEdb := new(excelModel.ExcelChartEdb)
+		excelEdb.EdbName = chartEdb.EdbName
+		excelEdb.EdbNameEn = excelEdb.EdbName
+		excelEdb.Unit = chartEdb.Unit
+		excelEdb.UnitEn = excelEdb.Unit
+		randStr := utils.GetRandDigit(4)
+		excelEdb.EdbCode = `T` + time.Now().Format("060102150405") + "_" + randStr
+		excelEdb.ExcelInfoId = excelInfo.ExcelInfoId
+		excelEdb.DateSequence = chartEdb.DateSequenceStr
+		excelEdb.DataSequence = chartEdb.DataSequenceStr
+		excelEdb.SysUserId = sysUser.AdminId
+		excelEdb.SysUserRealName = sysUser.RealName
+		excelEdb.MaxData = chartEdb.MaxData
+		excelEdb.MinData = chartEdb.MinData
+		excelEdb.IsOrder = chartEdb.IsOrder
+		excelEdb.IsAxis = chartEdb.IsAxis
+		excelEdb.FromTag = chartEdb.FromTag
+		excelEdb.EdbInfoType = chartEdb.EdbInfoType
+		excelEdb.LeadValue = chartEdb.LeadValue
+		excelEdb.LeadUnit = chartEdb.LeadUnit
+		excelEdb.CreateTime = time.Now()
+		excelEdb.ModifyTime = time.Now()
+		excelEdb.ChartWidth = 1
+		var dataList []*excelModel.ExcelChartData
+		if excelInfo.BalanceType == 1 {
+			tmpList, ok := dataListMap[k]
+			if !ok {
+				errMsg = "查询图表数据失败!"
+				err = fmt.Errorf("查询图表数据失败!%s", err.Error())
+				return
+			}
+			for _, l := range tmpList {
+				tmp := &excelModel.ExcelChartData{
+					DataTime:      l.DataTime,
+					Value:         l.Value,
+					DataTimestamp: l.DataTimestamp,
+				}
+				dataList = append(dataList, tmp)
+			}
+		}
+
+		// 处理日期列表和值列表
+		addItem := &excelModel.AddChartEdbAndDataItem{
+			ChartEdb: excelEdb,
+			DataList: dataList,
+		}
+		chartEdbList = append(chartEdbList, addItem)
+	}
+	chartInfo, err, errMsg, isSendEmail = addBalanceExcelChart(req, sysUser.AdminId, sysUser.RealName)
+	if err != nil {
+		if errMsg == "" {
+			errMsg = "新增图表失败!"
+		}
+		err = fmt.Errorf("新增图表失败!%s, %s", errMsg, err.Error())
+		return
+	}
+	obj := new(excelModel.ExcelChartEdb)
+	err = obj.AddChartEdbAndData(chartEdbList, chartInfo, []int{})
+	if err != nil {
+		errMsg = "新增图表失败!"
+		err = fmt.Errorf("新增图表数据失败!%s", err.Error())
+		return
+	}
+	return
+}
+
+func addBalanceExcelChart(req request.AddBalanceTableChartReq, sysUserId int, sysUserRealName string) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true // 默认错误的时候要发送邮件
+
+	req.ChartName = strings.Trim(req.ChartName, " ")
+	if req.ChartName == "" {
+		errMsg = "请填写图表名称!"
+		err = fmt.Errorf(errMsg)
+		isSendEmail = false
+		return
+	}
+	chartType := req.ChartType
+	extraConfig := req.ExtraConfig
+	// 查找默认主题设置
+	// 查找主题类型id
+	chartThemeType, err := chart_theme.GetChartThemeTypeByChartType(chartType)
+	if err != nil {
+		errMsg = "查找主题类型失败!"
+		err = fmt.Errorf("%s, %s", errMsg, err.Error())
+		return
+	}
+	chartThemeId := chartThemeType.DefaultChartThemeId
+	// 季节性图表额外配置信息
+	var seasonExtraConfig string
+
+	if len(req.ChartEdbInfoList) <= 0 {
+		errMsg = "请选择指标!"
+		err = fmt.Errorf(errMsg)
+		return
+	}
+	if chartType == 2 {
+		// 处理季节性图表横轴配置
+		{
+			if req.SeasonExtraConfig.XEndDate != "" {
+				if req.SeasonExtraConfig.XStartDate > req.SeasonExtraConfig.XEndDate && req.SeasonExtraConfig.JumpYear != 1 {
+					errMsg = "季节性图表配置信息异常:横坐标日期配置错误"
+					err = fmt.Errorf("季节性图表配置信息异常: 横坐标日期配置错误")
+					return
+				}
+				seasonExtra, tErr := json.Marshal(req.SeasonExtraConfig)
+				if tErr != nil {
+					errMsg = "季节性图表配置信息异常"
+					err = fmt.Errorf("季节性图表配置信息异常,Err:" + tErr.Error())
+					return
+				}
+
+				seasonExtraConfig = string(seasonExtra)
+			}
+		}
+	}
+
+	// 图表额外配置
+	extraConfig, err, errMsg = HandleExtraConfig(chartType, extraConfig)
+	if err != nil {
+		if errMsg == `` {
+			errMsg = "指标异常!"
+		}
+		err = fmt.Errorf("指标异常!Err:" + err.Error())
+		return
+	}
+
+	//判断图表是否存在
+	var condition string
+	var pars []interface{}
+
+	// 图表名称在不同图分类下不允许重复 需求调整时间:2022年11月07日09:47:07
+	condition += " AND chart_classify_id=0 "
+
+	condition += " AND chart_name=? AND source = ? "
+	pars = append(pars, req.ChartName, utils.CHART_SOURCE_BALANCE_EXCEL)
+
+	count, err := data_manage.GetChartInfoCountByCondition(condition, pars)
+	if err != nil {
+		errMsg = "判断图表名称是否存在失败"
+		err = fmt.Errorf("判断图表名称是否存在失败,Err:" + err.Error())
+		return
+	}
+
+	if count > 0 {
+		errMsg = "图表名称已存在,请重新填写"
+		err = fmt.Errorf("判断图表名称是否存在失败")
+		isSendEmail = false
+		return
+	}
+
+	chartInfo = new(data_manage.ChartInfo)
+	chartInfo.ChartName = req.ChartName
+	chartInfo.ChartNameEn = chartInfo.ChartName
+	//chartInfo.EdbInfoIds = edbInfoIdStr
+	//chartInfo.ChartClassifyId = req.ChartClassifyId
+	chartInfo.SysUserId = sysUserId
+	chartInfo.SysUserRealName = sysUserRealName
+	chartInfo.CreateTime = time.Now()
+	chartInfo.ModifyTime = time.Now()
+	chartInfo.IsSetName = 0
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	chartInfo.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp)
+
+	// todo 判断是否需要重新计算用户的start_date
+	chartInfo.DateType = 3
+
+	if chartType == 0 {
+		chartType = 1
+	}
+	chartInfo.ChartType = chartType
+
+	calendar := req.Calendar
+	if calendar == "" {
+		calendar = "公历"
+	}
+
+	chartInfo.Calendar = calendar
+	/*	chartInfo.StartDate = req.StartDate
+		chartInfo.EndDate = req.EndDate
+		chartInfo.SeasonStartDate = req.StartDate
+		chartInfo.SeasonEndDate = req.EndDate*/
+	chartInfo.LeftMin = req.LeftMin
+	chartInfo.LeftMax = req.LeftMax
+	chartInfo.RightMin = req.RightMin
+	chartInfo.RightMax = req.RightMax
+	chartInfo.Right2Min = req.Right2Min
+	chartInfo.Right2Max = req.Right2Max
+	chartInfo.MinMaxSave = req.MinMaxSave
+	//chartInfo.Disabled = disableVal
+	//chartInfo.BarConfig = barChartConf
+	chartInfo.ExtraConfig = extraConfig
+	chartInfo.SeasonExtraConfig = seasonExtraConfig
+	//chartInfo.StartYear = req.StartYear
+	chartInfo.Source = utils.CHART_SOURCE_BALANCE_EXCEL
+	chartInfo.ChartThemeId = chartThemeId
+	chartInfo.SourcesFrom = req.SourcesFrom
+	/*	chartInfo.Instructions = req.Instructions
+		chartInfo.MarkersLines = req.MarkersLines
+		chartInfo.MarkersAreas = req.MarkersAreas
+		chartInfo.Unit = req.Unit
+		chartInfo.UnitEn = req.UnitEn*/
+	/*newId, err := data_manage.AddChartInfo(chartInfo)
+	if err != nil {
+		errMsg = `保存失败`
+		err = fmt.Errorf("保存失败,Err:" + err.Error())
+		return
+	}
+	chartInfo.ChartInfoId = int(newId)*/
+
+	//添加es数据
+	//go EsAddOrEditChartInfo(chartInfo.ChartInfoId)
+
+	return
+}
+
+// EditBalanceExcelChart 添加平衡表图表
+func EditBalanceExcelChart(excelInfo *excelModel.ExcelInfo, req request.AddBalanceTableChartReq, sysUser *system.Admin, dataListMap map[int][]*data_manage.EdbDataList) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
+	if len(req.ChartEdbInfoList) == 0 {
+		errMsg = "图表数据不能为空!"
+		err = fmt.Errorf("图表数据不能为空!%s", err.Error())
+		return
+	}
+	/*// 操作权限校验, 增加协作人判断
+	{
+		checkExcelInfo := excelInfo
+		checkExcelInfoId := excelInfo.ExcelInfoId
+		if excelInfo.BalanceType == 1 {
+			checkExcelInfoId = excelInfo.RelExcelInfoId
+		} else {
+			if excelInfo.ParentId > 0 {
+				checkExcelInfoId = excelInfo.ParentId
+			}
+		}
+		if checkExcelInfoId != excelInfo.ExcelInfoId {
+			checkExcelInfo, err = excelModel.GetExcelInfoById(checkExcelInfoId)
+			if err != nil {
+				errMsg = "获取平衡表格信息失败"
+				err = fmt.Errorf("获取平衡表格信息失败,Err:" + err.Error())
+				return
+			}
+		}
+		// 数据权限
+		haveOperaAuth, e := data_manage_permission.CheckExcelPermissionByExcelInfoId(checkExcelInfo.ExcelInfoId, checkExcelInfo.ExcelClassifyId, checkExcelInfo.IsJoinPermission, sysUser.AdminId)
+		if e != nil {
+			errMsg = "获取ETA表格权限失败"
+			err = fmt.Errorf("获取表格权限信息失败,Err" + e.Error())
+			return
+		}
+
+		button := excelService.GetBalanceExcelInfoOpButton(sysUser.AdminId, checkExcelInfo.SysUserId, haveOperaAuth, checkExcelInfo.ExcelInfoId)
+		if !button.OpButton {
+			errMsg = "无操作权限"
+			isSendEmail = false
+			return
+		}
+	}
+	*/
+	//查询已有的mapping
+	mapping, err := excelModel.GetExcelChartEdbMappingByChartInfoId(req.ChartInfoId)
+	if err != nil {
+		errMsg = "查询图表mapping失败!"
+		err = fmt.Errorf("查询图表mapping失败!%s", err.Error())
+		return
+	}
+	deleteMap := make(map[int]int)
+	for _, v := range mapping {
+		deleteMap[v.ExcelChartEdbId] = v.ExcelChartEdbId
+	}
+
+	// 查询edbIds
+	chartEdbList := make([]*excelModel.AddChartEdbAndDataItem, 0)
+	for k, chartEdb := range req.ChartEdbInfoList {
+		if _, ok := deleteMap[chartEdb.ExcelChartEdbId]; ok {
+			delete(deleteMap, chartEdb.ExcelChartEdbId)
+		}
+		excelChartEdbId := chartEdb.ExcelChartEdbId
+		excelEdb := new(excelModel.ExcelChartEdb)
+		if excelChartEdbId > 0 {
+			excelEdb, err = excelModel.GetExcelChartEdbById(excelChartEdbId)
+			if err != nil {
+				if err.Error() == utils.ErrNoRow() {
+					errMsg = "指标不存在!"
+					err = errors.New("指标不存在,edbInfoId:" + strconv.Itoa(excelChartEdbId))
+					return
+				} else {
+					errMsg = "获取指标信息失败!"
+					err = errors.New("获取图表的指标信息失败,Err:" + err.Error())
+					return
+				}
+			}
+		} else {
+			excelEdb.ExcelInfoId = excelInfo.ExcelInfoId
+			randStr := utils.GetRandDigit(4)
+			excelEdb.EdbCode = `T` + time.Now().Format("060102150405") + "_" + randStr
+			excelEdb.SysUserId = sysUser.AdminId
+			excelEdb.SysUserRealName = sysUser.RealName
+			excelEdb.CreateTime = time.Now()
+		}
+
+		excelEdb.EdbName = chartEdb.EdbName
+		excelEdb.EdbNameEn = excelEdb.EdbName
+		excelEdb.Unit = chartEdb.Unit
+		excelEdb.UnitEn = excelEdb.Unit
+		excelEdb.DateSequence = chartEdb.DateSequenceStr
+		excelEdb.DataSequence = chartEdb.DataSequenceStr
+		excelEdb.MaxData = chartEdb.MaxData
+		excelEdb.MinData = chartEdb.MinData
+		excelEdb.IsOrder = chartEdb.IsOrder
+		excelEdb.IsAxis = chartEdb.IsAxis
+		excelEdb.FromTag = chartEdb.FromTag
+		excelEdb.EdbInfoType = chartEdb.EdbInfoType
+		excelEdb.LeadValue = chartEdb.LeadValue
+		excelEdb.LeadUnit = chartEdb.LeadUnit
+		excelEdb.ChartWidth = 1
+		excelEdb.ModifyTime = time.Now()
+
+		var dataList []*excelModel.ExcelChartData
+		if excelInfo.BalanceType == 1 {
+			tmpList, ok := dataListMap[k]
+			if !ok {
+				errMsg = "查询图表数据失败!"
+				err = fmt.Errorf("查询图表数据失败!")
+				return
+			}
+			for _, l := range tmpList {
+				tmp := &excelModel.ExcelChartData{
+					DataTime:      l.DataTime,
+					Value:         l.Value,
+					DataTimestamp: l.DataTimestamp,
+				}
+				dataList = append(dataList, tmp)
+			}
+		}
+
+		// 处理日期列表和值列表
+		addItem := &excelModel.AddChartEdbAndDataItem{
+			ChartEdb: excelEdb,
+			DataList: dataList,
+		}
+		chartEdbList = append(chartEdbList, addItem)
+	}
+	chartInfo, err, errMsg, isSendEmail = editBalanceExcelChart(req)
+	if err != nil {
+		//errMsg = "新增图表失败!"
+		err = fmt.Errorf("新增图表失败!%s, %s", errMsg, err.Error())
+		return
+	}
+	obj := new(excelModel.ExcelChartEdb)
+	//删除原先多余的指标
+	deleteEdbIds := make([]int, 0)
+	for k, _ := range deleteMap {
+		deleteEdbIds = append(deleteEdbIds, k)
+	}
+	err = obj.AddChartEdbAndData(chartEdbList, chartInfo, deleteEdbIds)
+	if err != nil {
+		errMsg = "新增图表失败!"
+		err = fmt.Errorf("新增图表数据失败!%s", err.Error())
+		return
+	}
+	return
+}
+
+func editBalanceExcelChart(req request.AddBalanceTableChartReq) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true // 默认错误的时候要发送邮件
+	chartType := req.ChartType
+	extraConfig := req.ExtraConfig
+
+	chartInfo, err = data_manage.GetChartInfoById(req.ChartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "图表已被删除,请刷新页面"
+			err = errors.New(errMsg)
+			isSendEmail = false
+			return
+		}
+		errMsg = "获取图表信息失败"
+		err = errors.New("获取图表信息失败,Err:" + err.Error())
+		return
+	}
+	// 查找主题类型id
+	chartThemeType, err := chart_theme.GetChartThemeTypeByChartType(chartType)
+	if err != nil {
+		errMsg = "查找主题类型失败!"
+		err = fmt.Errorf("%s, %s", errMsg, err.Error())
+		return
+	}
+	chartThemeId := chartThemeType.DefaultChartThemeId
+	// 季节性图表额外配置信息
+	var seasonExtraConfig string
+
+	if chartType == 2 {
+		// 处理季节性图表横轴配置
+		{
+			if req.SeasonExtraConfig.XEndDate != "" {
+				if req.SeasonExtraConfig.XStartDate > req.SeasonExtraConfig.XEndDate && req.SeasonExtraConfig.JumpYear != 1 {
+					errMsg = "季节性图表配置信息异常:横坐标日期配置错误"
+					err = fmt.Errorf("季节性图表配置信息异常: 横坐标日期配置错误")
+					return
+				}
+				seasonExtra, tErr := json.Marshal(req.SeasonExtraConfig)
+				if tErr != nil {
+					errMsg = "季节性图表配置信息异常"
+					err = fmt.Errorf("季节性图表配置信息异常,Err:" + tErr.Error())
+					return
+				}
+
+				seasonExtraConfig = string(seasonExtra)
+			}
+		}
+	}
+
+	/*var extraConfigEdbInfoIdArr []int
+	extraConfigEdbInfoIdArr, err, errMsg = CheckChartExtraConfig(req.ChartType, req.ExtraConfig)
+	if err != nil {
+		err = errors.New("添加失败:" + err.Error())
+		return
+	}*/
+
+	// 图表额外配置
+	extraConfig, err, errMsg = HandleExtraConfig(chartType, extraConfig)
+	if err != nil {
+		if errMsg == `` {
+			errMsg = "指标异常!"
+		}
+		err = fmt.Errorf("指标异常!Err:" + err.Error())
+		return
+	}
+
+	//判断图表是否存在
+	var condition string
+	var pars []interface{}
+	condition += " AND chart_info_id<>? "
+	pars = append(pars, req.ChartInfoId)
+	// 图表名称在不同图分类下不允许重复 需求调整时间:2022年11月07日09:47:07
+	condition += " AND chart_classify_id=0 "
+
+	condition += " AND chart_name=? AND source = ? "
+	pars = append(pars, req.ChartName, utils.CHART_SOURCE_BALANCE_EXCEL)
+
+	count, err := data_manage.GetChartInfoCountByCondition(condition, pars)
+	if err != nil {
+		errMsg = "判断图表名称是否存在失败"
+		err = fmt.Errorf("判断图表名称是否存在失败,Err:" + err.Error())
+		return
+	}
+
+	if count > 0 {
+		errMsg = "图表名称已存在,请重新填写"
+		err = fmt.Errorf("判断图表名称是否存在失败")
+		isSendEmail = false
+		return
+	}
+	// todo 判断是否是禁用的图表
+	//	disableVal := data.CheckIsDisableChart(edbInfoIdArr)
+	chartInfo.ChartName = req.ChartName
+	chartInfo.ChartNameEn = chartInfo.ChartName
+	//chartInfo.EdbInfoIds = edbInfoIdStr
+	//chartInfo.ChartClassifyId = req.ChartClassifyId
+	chartInfo.ModifyTime = time.Now()
+	chartInfo.IsSetName = 0
+	// todo 判断是否需要重新计算用户的start_date
+	chartInfo.DateType = 3
+
+	if chartType == 0 {
+		chartType = 1
+	}
+	chartInfo.ChartType = chartType
+
+	calendar := req.Calendar
+	if calendar == "" {
+		calendar = "公历"
+	}
+
+	chartInfo.Calendar = calendar
+	/*	chartInfo.StartDate = req.StartDate
+		chartInfo.EndDate = req.EndDate
+		chartInfo.SeasonStartDate = req.StartDate
+		chartInfo.SeasonEndDate = req.EndDate*/
+	chartInfo.LeftMin = req.LeftMin
+	chartInfo.LeftMax = req.LeftMax
+	chartInfo.RightMin = req.RightMin
+	chartInfo.RightMax = req.RightMax
+	chartInfo.Right2Min = req.Right2Min
+	chartInfo.Right2Max = req.Right2Max
+	chartInfo.MinMaxSave = req.MinMaxSave
+	//chartInfo.Disabled = disableVal
+	//chartInfo.BarConfig = barChartConf
+	chartInfo.ExtraConfig = extraConfig
+	chartInfo.SeasonExtraConfig = seasonExtraConfig
+	//chartInfo.StartYear = req.StartYear
+	chartInfo.Source = utils.CHART_SOURCE_BALANCE_EXCEL
+	chartInfo.ChartThemeId = chartThemeId
+	chartInfo.SourcesFrom = req.SourcesFrom
+	/*	chartInfo.Instructions = req.Instructions
+		chartInfo.MarkersLines = req.MarkersLines
+		chartInfo.MarkersAreas = req.MarkersAreas
+		chartInfo.Unit = req.Unit
+		chartInfo.UnitEn = req.UnitEn*/
+	/*newId, err := data_manage.AddChartInfo(chartInfo)
+	if err != nil {
+		errMsg = `保存失败`
+		err = fmt.Errorf("保存失败,Err:" + err.Error())
+		return
+	}
+	chartInfo.ChartInfoId = int(newId)*/
+
+	//添加es数据
+	//go EsAddOrEditChartInfo(chartInfo.ChartInfoId)
+
+	return
+}
+
+func GetBalanceExcelChartDetail(chartInfo *data_manage.ChartInfoView, mappingListTmp []*excelModel.ExcelChartEdb, sysUser *system.Admin, dataListMap map[int][]*data_manage.EdbDataList) (resp *data_manage.ChartInfoDetailResp, err error, errMsg string) {
+	// 图表数据权限
+	{
+		// 已授权分类id
+		permissionChartIdList, permissionClassifyIdList, e := data_manage_permission.GetUserChartAndClassifyPermissionList(sysUser.AdminId, chartInfo.ChartInfoId, chartInfo.ChartClassifyId)
+		if e != nil {
+			errMsg = "获取失败"
+			err = fmt.Errorf("获取已授权分类id数据失败,Err:" + err.Error())
+			return
+		}
+		chartInfo.HaveOperaAuth = data_manage_permission.CheckChartPermissionByPermissionIdList(chartInfo.IsJoinPermission, 0, chartInfo.ChartInfoId, chartInfo.ChartClassifyId, permissionChartIdList, permissionClassifyIdList)
+	}
+
+	chartInfoId := chartInfo.ChartInfoId
+	resp = new(data_manage.ChartInfoDetailResp)
+
+	// 获取主题样式
+	chartTheme, err := GetChartThemeConfig(chartInfo.ChartThemeId, 1, chartInfo.ChartType)
+	if err != nil {
+		errMsg = "获取失败"
+		err = fmt.Errorf(" 获取主题信息失败 Err:%s", err.Error())
+		return
+	}
+	chartInfo.ChartThemeStyle = chartTheme.Config
+	chartInfo.ChartThemeId = chartTheme.ChartThemeId
+
+	dateType := chartInfo.DateType
+	fmt.Println("dateType:", dateType)
+
+	chartType := chartInfo.ChartType
+	startDate := chartInfo.StartDate
+	endDate := chartInfo.EndDate
+	seasonStartDate := chartInfo.SeasonStartDate
+	seasonEndDate := chartInfo.SeasonEndDate
+	startYear := chartInfo.StartYear
+
+	calendar := chartInfo.Calendar
+
+	if calendar == "" {
+		calendar = "公历"
+	}
+
+	mappingList, err := TransferChartEdbToEdbMappingFormat(chartInfoId, chartType, mappingListTmp, dataListMap)
+	if err != nil {
+		return
+	}
+	if chartType == 2 {
+		startDate = seasonStartDate
+		endDate = seasonEndDate
+		if dateType <= 0 {
+			if startDate != "" {
+				dateType = 5
+			} else {
+				dateType = utils.DateTypeNYears
+			}
+		}
+	} else {
+		if dateType <= 0 {
+			dateType = 3
+		}
+	}
+	yearMax := 0
+	if dateType == utils.DateTypeNYears {
+		for _, v := range mappingList {
+			if v.LatestDate != "" {
+				lastDateT, tErr := time.Parse(utils.FormatDate, v.LatestDate)
+				if tErr != nil {
+					errMsg = "获取失败"
+					err = fmt.Errorf("获取图表日期信息失败,Err:" + tErr.Error())
+					return
+				}
+				if lastDateT.Year() > yearMax {
+					yearMax = lastDateT.Year()
+				}
+			}
+		}
+	}
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+
+	if chartInfo.ChartType == 2 {
+		chartInfo.StartDate = startDate
+		chartInfo.EndDate = endDate
+	}
+	// 图表额外数据参数
+	extraConfigStr := chartInfo.ExtraConfig
+	// 柱方图的一些配置
+	var barConfig data_manage.BarChartInfoReq
+	if chartInfo != nil && chartInfo.ChartType == 7 {
+		if chartInfo.BarConfig == `` {
+			err = fmt.Errorf("柱方图未配置")
+			errMsg = "柱方图未配置"
+			return
+		}
+		err = json.Unmarshal([]byte(chartInfo.BarConfig), &barConfig)
+		if err != nil {
+			err = fmt.Errorf("柱方图配置异常 json.Unmarshal Err:%s", err.Error())
+			errMsg = "柱方图配置异常"
+			return
+		}
+		extraConfigStr = chartInfo.BarConfig
+	}
+	// 获取表格数据
+
+	excelChartInfoDataShow := new(ExcelChartInfoDataShow)
+	excelChartInfoDataShow.DataListMap = dataListMap
+
+	if chartInfo.HaveOperaAuth {
+		// 获取图表中的指标数据
+		edbList, xEdbIdValue, yDataList, dataResp, e, msg := GetChartEdbDataV2(chartInfoId, chartType, calendar, startDate, endDate, mappingList, extraConfigStr, chartInfo.SeasonExtraConfig, excelChartInfoDataShow)
+		if e != nil {
+			err = fmt.Errorf("获取图表,指标数据失败,Err:%s", e.Error())
+			errMsg = msg
+			return
+		}
+		// 单位
+		if chartType == utils.CHART_TYPE_BAR && len(yDataList) > 0 {
+			chartInfo.Unit = yDataList[0].Unit
+			chartInfo.UnitEn = yDataList[0].UnitEn
+		}
+		warnEdbList := make([]string, 0)
+		for _, v := range edbList {
+			if v.IsNullData {
+				warnEdbList = append(warnEdbList, v.EdbName+"("+v.EdbCode+")")
+			}
+		}
+		if len(warnEdbList) > 0 {
+			chartInfo.WarnMsg = `图表引用指标异常,异常指标:` + strings.Join(warnEdbList, ",")
+		}
+		if chartInfoId > 0 && chartInfo != nil {
+			//判断是否加入我的图库
+			{
+				var myChartCondition string
+				var myChartPars []interface{}
+				myChartCondition += ` AND a.admin_id=? `
+				myChartPars = append(myChartPars, sysUser.AdminId)
+				myChartCondition += ` AND a.chart_info_id=? `
+				myChartPars = append(myChartPars, chartInfo.ChartInfoId)
+
+				myChartList, e := data_manage.GetMyChartByCondition(myChartCondition, myChartPars)
+				if e != nil && e.Error() != utils.ErrNoRow() {
+					errMsg = "获取失败"
+					err = fmt.Errorf("获取我的图表信息失败,GetMyChartByCondition,Err:" + e.Error())
+					return
+				}
+				if myChartList != nil && len(myChartList) > 0 {
+					chartInfo.IsAdd = true
+					chartInfo.MyChartId = myChartList[0].MyChartId
+					chartInfo.MyChartClassifyId = myChartList[0].MyChartClassifyId
+				}
+			}
+		}
+		//判断是否需要展示英文标识
+		chartInfo.IsEnChart = CheckIsEnChart(chartInfo.ChartNameEn, edbList, chartInfo.Source, chartInfo.ChartType)
+
+		// 图表的指标来源
+		sourceNameList, sourceNameEnList := GetEdbSourceByEdbInfoIdList(edbList)
+
+		chartInfo.ChartSource = strings.Join(sourceNameList, ",")
+		chartInfo.ChartSourceEn = strings.Join(sourceNameEnList, ",")
+
+		// todo 指标权限
+		{
+			for _, item := range edbList {
+				// 数据权限
+				item.HaveOperaAuth = true
+			}
+		}
+
+		resp.EdbInfoList = edbList
+		resp.XEdbIdValue = xEdbIdValue
+		resp.YDataList = yDataList
+		resp.DataResp = dataResp
+	} else {
+		resp.EdbInfoList = mappingList
+	}
+
+	// todo 平衡表图表操作权限平衡表表格的操作权限保持一致,前端未用到该按钮权限,故不处理
+	chartInfo.IsEdit, _ = GetBalanceExcelInfoOpButtonByChartInfoId(sysUser.AdminId, chartInfo.ChartInfoId, chartInfo.HaveOperaAuth)
+	chartInfo.Button = data_manage.ChartViewButton{
+		IsEdit:    chartInfo.IsEdit,
+		IsEnChart: chartInfo.IsEnChart,
+		IsAdd:     chartInfo.IsAdd,
+		IsCopy:    true,
+		IsSetName: chartInfo.IsSetName,
+	}
+
+	resp.ChartInfo = chartInfo
+	resp.BarChartInfo = barConfig
+	return
+}
+
+// GetBalanceExcelEdbDataMapList 获取指标最后的基础数据
+func GetBalanceExcelEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping, seasonExtraConfig string, dataListMap map[int][]*data_manage.EdbDataList) (edbDataListMap map[int][]*data_manage.EdbDataList, edbList []*data_manage.ChartEdbInfoMapping, err error) {
+	// 指标对应的所有数据
+	edbDataListMap = make(map[int][]*data_manage.EdbDataList)
+
+	for _, v := range mappingList {
+		//fmt.Println("v:", v.EdbInfoId)
+		item := new(data_manage.ChartEdbInfoMapping)
+		item.EdbInfoId = v.EdbInfoId
+		item.SourceName = v.SourceName
+		item.Source = v.Source
+		item.EdbCode = v.EdbCode
+		item.EdbName = v.EdbName
+		item.EdbNameEn = v.EdbNameEn
+		item.Frequency = v.Frequency
+		item.EdbType = v.EdbType
+		item.FrequencyEn = GetFrequencyEn(v.Frequency)
+		if v.Unit != `无` {
+			item.Unit = v.Unit
+		}
+		item.UnitEn = v.UnitEn
+		item.StartDate = v.StartDate
+		item.EndDate = v.EndDate
+		item.ModifyTime = v.ModifyTime
+		item.EdbInfoCategoryType = v.EdbInfoCategoryType
+		item.PredictChartColor = v.PredictChartColor
+		item.ClassifyId = v.ClassifyId
+		if chartInfoId <= 0 {
+			item.IsAxis = 1
+			item.LeadValue = 0
+			item.LeadUnit = ""
+			item.ChartEdbMappingId = 0
+			item.ChartInfoId = 0
+			item.IsOrder = false
+			item.EdbInfoType = 1
+			item.ChartStyle = ""
+			item.ChartColor = ""
+			item.ChartWidth = 1
+			item.MaxData = v.MaxValue
+			item.MinData = v.MinValue
+		} else {
+			item.IsAxis = v.IsAxis
+			item.EdbInfoType = v.EdbInfoType
+			item.LeadValue = v.LeadValue
+			item.LeadUnit = v.LeadUnit
+			item.LeadUnitEn = GetLeadUnitEn(v.LeadUnit)
+			item.ChartEdbMappingId = v.ChartEdbMappingId
+			item.ChartInfoId = v.ChartInfoId
+			item.ChartStyle = v.ChartStyle
+			item.ChartColor = v.ChartColor
+			item.ChartWidth = v.ChartWidth
+			item.IsOrder = v.IsOrder
+			item.MaxData = v.MaxData
+			item.MinData = v.MinData
+		}
+		item.LatestValue = v.LatestValue
+		item.LatestDate = v.LatestDate
+		item.UniqueCode = v.UniqueCode
+		item.MoveLatestDate = v.LatestDate
+		item.EdbAliasName = v.EdbAliasName
+		item.IsConvert = v.IsConvert
+		item.ConvertType = v.ConvertType
+		item.ConvertValue = v.ConvertValue
+		item.ConvertUnit = v.ConvertUnit
+		item.ConvertEnUnit = v.ConvertEnUnit
+		item.IsJoinPermission = v.IsJoinPermission
+
+		var startDateReal string
+		var diffSeconds int64
+		if chartType == 2 { //季节性图
+			startDateReal = startDate
+		} else {
+			if v.EdbInfoType == 0 && v.LeadUnit != "" && v.LeadValue > 0 { //领先指标
+				var startTimeRealTemp time.Time
+				startDateParse, _ := time.Parse(utils.FormatDate, startDate)
+				switch v.LeadUnit {
+				case "天":
+					startTimeRealTemp = startDateParse.AddDate(0, 0, -v.LeadValue)
+				case "月":
+					startTimeRealTemp = startDateParse.AddDate(0, -v.LeadValue, 0)
+				case "季":
+					startTimeRealTemp = startDateParse.AddDate(0, -3*v.LeadValue, 0)
+				case "周":
+					startTimeRealTemp = startDateParse.AddDate(0, 0, -7*v.LeadValue)
+				case "年":
+					startTimeRealTemp = startDateParse.AddDate(-v.LeadValue, 0, 0)
+				}
+				if startTimeRealTemp.Before(startDateParse) {
+					startDateReal = startTimeRealTemp.Format(utils.FormatDate)
+					diffSeconds = (int64(startTimeRealTemp.UnixNano()) - int64(startDateParse.UnixNano())) / 1e6
+				} else {
+					startDateReal = startDate
+					diffSeconds = 0
+				}
+
+				// 预测指标的开始日期也要偏移
+				{
+					day, tmpErr := utils.GetDaysBetween2Date(utils.FormatDate, startDate, startDateReal)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					moveLatestDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, item.MoveLatestDate, time.Local)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					item.MoveLatestDate = moveLatestDateTime.AddDate(0, 0, day).Format(utils.FormatDate)
+				}
+			} else {
+				startDateReal = startDate
+			}
+		}
+		//fmt.Println("line 1011 chart:", v.Source, v.EdbInfoId, startDateReal, endDate)
+		calendarPreYear := 0
+		if calendar == "农历" {
+			newStartDateReal, e := time.Parse(utils.FormatDate, startDateReal)
+			if e != nil {
+				err = fmt.Errorf("时间解析 time.Parse(%s, %s) error: %v", utils.FormatDate, startDateReal, e)
+				return
+			}
+			calendarPreYear = newStartDateReal.Year() - 1
+			newStartDateReal = newStartDateReal.AddDate(-1, 0, 0)
+			startDateReal = newStartDateReal.Format(utils.FormatDate)
+		}
+		dataList := make([]*data_manage.EdbDataList, 0)
+		dataListTmp, ok := dataListMap[v.EdbInfoId]
+		if ok {
+			dataList = dataListTmp
+			// 对dataList 根据dataTimestamp 进行排序
+			sort.Slice(dataList, func(i, j int) bool {
+				return dataList[i].DataTimestamp < dataList[j].DataTimestamp
+			})
+		} else {
+			//err = errors.New(fmt.Sprint("获取失败,指标类型异常", v.EdbInfoId))
+			utils.FileLog.Info(fmt.Sprintf("获取失败,指标数据异常 %d", v.EdbInfoId))
+		}
+		if v.IsConvert == 1 {
+			switch v.ConvertType {
+			case 1:
+				for i, data := range dataList {
+					dataList[i].Value = data.Value * v.ConvertValue
+				}
+				//item.MaxData = item.MaxData * v.ConvertValue
+				//item.MinData = item.MinData * v.ConvertValue
+			case 2:
+				for i, data := range dataList {
+					dataList[i].Value = data.Value / v.ConvertValue
+				}
+				//item.MaxData = item.MaxData / v.ConvertValue
+				//item.MinData = item.MinData / v.ConvertValue
+			case 3:
+				for i, data := range dataList {
+					if data.Value <= 0 {
+						err = errors.New("数据中含有负数或0,无法对数运算")
+						return
+					}
+					dataList[i].Value = math.Log(data.Value) / math.Log(v.ConvertValue)
+				}
+				//item.MaxData = math.Log(item.MaxData) / math.Log(v.ConvertValue)
+				//item.MinData = math.Log(item.MinData) / math.Log(v.ConvertValue)
+			}
+		}
+
+		edbDataListMap[v.EdbInfoId] = dataList
+
+		if diffSeconds != 0 && v.EdbInfoType == 0 {
+			dataListLen := len(dataList)
+			for i := 0; i < dataListLen; i++ {
+				dataList[i].DataTimestamp = dataList[i].DataTimestamp - diffSeconds
+			}
+		}
+
+		if chartType == 2 {
+			latestDate, tmpErr := time.Parse(utils.FormatDate, v.LatestDate)
+			if tmpErr != nil {
+				//item.DataList = dataList
+				item.IsNullData = true
+				edbList = append(edbList, item)
+				continue
+			}
+
+			if calendar == "农历" {
+				if len(dataList) <= 0 {
+					result := new(data_manage.EdbDataResult)
+					item.DataList = result
+				} else {
+					result, tmpErr := data_manage.AddCalculateQuarterV6(dataList)
+					if tmpErr != nil {
+						err = errors.New("获取农历数据失败,Err:" + tmpErr.Error())
+						return
+					}
+					quarterDataList, tErr := GetSeasonEdbInfoDataListByXDateNong(result, latestDate, seasonExtraConfig, calendarPreYear)
+					if tErr != nil {
+						err = errors.New("获取季节性图表数据失败,Err:" + tErr.Error())
+						return
+					}
+					item.DataList = quarterDataList
+				}
+
+			} else {
+				quarterDataList, tErr := GetSeasonEdbInfoDataListByXDate(dataList, latestDate, seasonExtraConfig)
+				if tErr != nil {
+					err = errors.New("获取季节性图表数据失败,Err:" + tErr.Error())
+					return
+				}
+				item.DataList = quarterDataList
+			}
+
+		} else if chartType == 7 || chartType == utils.CHART_TYPE_RADAR { //柱方图
+			//item.DataList = dataList
+		} else {
+			item.DataList = dataList
+		}
+		edbList = append(edbList, item)
+	}
+
+	return
+}
+
+func CheckBalanceChartCacheAndPermission(chartInfo *data_manage.ChartInfoView, isCache bool, sysUser *system.Admin) (resp *data_manage.ChartInfoDetailFromUniqueCodeResp, isOk bool, msg, errMsg string) {
+	//判断是否存在缓存,如果存在缓存,那么直接从缓存中获取
+	adminId := sysUser.AdminId
+	// todo 图表数据权限
+	// 已授权分类id
+	permissionChartIdList, permissionClassifyIdList, err := data_manage_permission.GetUserChartAndClassifyPermissionList(adminId, chartInfo.ChartInfoId, chartInfo.ChartClassifyId)
+	if err != nil {
+		errMsg = "获取已授权分类id数据失败,Err:" + err.Error()
+		return
+	}
+
+	defer func() {
+		if isOk {
+			// 图表权限校验
+			{
+				resp.ChartInfo.HaveOperaAuth = data_manage_permission.CheckChartPermissionByPermissionIdList(chartInfo.IsJoinPermission, 0, chartInfo.ChartInfoId, chartInfo.ChartClassifyId, permissionChartIdList, permissionClassifyIdList)
+
+				//图表操作权限
+				chartInfo.IsEdit = CheckOpChartPermission(sysUser, chartInfo.SysUserId, resp.ChartInfo.HaveOperaAuth)
+				chartInfo.Button = data_manage.ChartViewButton{
+					IsEdit:    chartInfo.IsEdit,
+					IsEnChart: chartInfo.IsEnChart,
+					IsAdd:     chartInfo.IsAdd,
+					IsCopy:    true,
+					IsSetName: chartInfo.IsSetName,
+				}
+
+				//if !resp.ChartInfo.HaveOperaAuth {
+				//	for _, v := range resp.EdbInfoList {
+				//		v.DataList = nil
+				//	}
+				//	resp.DataResp = nil
+				//	resp.XEdbIdValue = []int{}
+				//	resp.YDataList = []data_manage.YData{}
+				//}
+			}
+
+			// todo 是否需要返回平衡表指标权限即判断平衡表的表格数据权限
+			{
+				// 查询图表关联的表格ID
+				excelInfo, e := excelModel.GetExcelInfoByChartInfoId(chartInfo.ChartInfoId)
+				if e != nil {
+					errMsg = "获取图表关联的表格ID失败,Err:" + e.Error()
+					err = e
+					return
+				}
+				checkExcelInfo := excelInfo
+				if excelInfo.Source == utils.BALANCE_TABLE {
+					checkExcelInfoId := excelInfo.ExcelInfoId
+					if excelInfo.BalanceType == 1 {
+						checkExcelInfoId = excelInfo.RelExcelInfoId
+					} else {
+						if excelInfo.ParentId > 0 {
+							checkExcelInfoId = excelInfo.ParentId
+						}
+					}
+					if checkExcelInfoId != excelInfo.ExcelInfoId {
+						checkExcelInfo, err = excelModel.GetExcelInfoById(checkExcelInfoId)
+						if err != nil {
+							errMsg = "获取平衡表格信息失败"
+							err = errors.New("获取平衡表格信息失败,Err:" + err.Error())
+							return
+						}
+					}
+				}
+
+				haveOperaAuth, e := data_manage_permission.CheckExcelPermissionByExcelInfoId(checkExcelInfo.ExcelInfoId, checkExcelInfo.ExcelClassifyId, checkExcelInfo.IsJoinPermission, adminId)
+				if e != nil {
+					errMsg = "获取平衡表格数据权限失败"
+					err = errors.New("获取表格权限信息失败,Err" + e.Error())
+					return
+				}
+				// 数据权限
+				for _, v := range resp.EdbInfoList {
+					v.HaveOperaAuth = haveOperaAuth
+				}
+			}
+		}
+	}()
+	key := GetChartInfoDataKey(chartInfo.ChartInfoId)
+	if utils.Re == nil && isCache {
+		if utils.Re == nil && utils.Rc.IsExist(key) {
+			if redisData, err1 := utils.Rc.RedisBytes(key); err1 == nil {
+				err := json.Unmarshal(redisData, &resp)
+				if err != nil || resp == nil {
+					return
+				}
+				// 这里跟当前用户相关的信息重新查询写入resp, 不使用缓存中的
+				var myCond string
+				var myPars []interface{}
+				myCond += ` AND a.admin_id=? `
+				myPars = append(myPars, adminId)
+				myCond += ` AND a.chart_info_id=? `
+				myPars = append(myPars, chartInfo.ChartInfoId)
+				myList, err := data_manage.GetMyChartByCondition(myCond, myPars)
+				if err != nil && err.Error() != utils.ErrNoRow() {
+					msg = "获取失败"
+					errMsg = "获取我的图表信息失败,GetMyChartByCondition,Err:" + err.Error()
+					return
+				}
+				resp.ChartInfo.IsAdd = false
+				resp.ChartInfo.MyChartId = 0
+				resp.ChartInfo.MyChartClassifyId = ""
+				if myList != nil && len(myList) > 0 {
+					resp.ChartInfo.IsAdd = true
+					resp.ChartInfo.MyChartId = myList[0].MyChartId
+					resp.ChartInfo.MyChartClassifyId = myList[0].MyChartClassifyId
+				}
+
+				//判断是否加入我的图库
+				{
+					var myChartCondition string
+					var myChartPars []interface{}
+					myChartCondition += ` AND a.admin_id=? `
+					myChartPars = append(myChartPars, adminId)
+					myChartCondition += ` AND a.chart_info_id=? `
+					myChartPars = append(myChartPars, chartInfo.ChartInfoId)
+
+					myChartList, err := data_manage.GetMyChartByCondition(myChartCondition, myChartPars)
+					if err != nil && err.Error() != utils.ErrNoRow() {
+						msg = "获取失败"
+						errMsg = "获取我的图表信息失败,GetMyChartByCondition,Err:" + err.Error()
+						return
+					}
+					if myChartList != nil && len(myChartList) > 0 {
+						chartInfo.IsAdd = true
+						chartInfo.MyChartId = myChartList[0].MyChartId
+						chartInfo.MyChartClassifyId = myChartList[0].MyChartClassifyId
+					}
+				}
+
+				isOk = true
+				fmt.Println("source redis")
+				return
+			}
+		}
+	}
+	return
+}
+
+// GetBalanceExcelSeasonChartLegendPreview 获取预览的季节性图例
+func GetBalanceExcelSeasonChartLegendPreview(dataList []*data_manage.EdbDataList, calendar string, seasonExtraConfigReq data_manage.SeasonExtraItem) (quarterDataList data_manage.QuarterDataList, err error, errMsg string) {
+	seasonExtraConfig := ""
+	if seasonExtraConfigReq.XEndDate != "" {
+		if seasonExtraConfigReq.XStartDate > seasonExtraConfigReq.XEndDate && seasonExtraConfigReq.JumpYear != 1 {
+			errMsg = "季节性图表配置信息异常:横坐标日期配置错误"
+			err = fmt.Errorf("季节性图表配置信息异常: 横坐标日期配置错误")
+			return
+		}
+		seasonExtra, tErr := json.Marshal(seasonExtraConfigReq)
+		if tErr != nil {
+			errMsg = "季节性图表配置信息异常"
+			err = fmt.Errorf("季节性图表配置信息异常,Err:" + tErr.Error())
+			return
+		}
+
+		seasonExtraConfig = string(seasonExtra)
+	}
+	var latestDate time.Time //最新日期
+	startDate, endDate, _, _, _, _ := getBalanceDataListStartDateAndValue(dataList)
+	calendarPreYear := 0
+	startDateReal := startDate
+	latestDate, _ = time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+	if calendar == "农历" {
+		newStartDateReal, e := time.Parse(utils.FormatDate, startDateReal)
+		if e != nil {
+			err = fmt.Errorf("开始时间解析失败 time.Parse:" + e.Error())
+			return
+		}
+		calendarPreYear = newStartDateReal.Year() - 1
+		newStartDateReal = newStartDateReal.AddDate(-1, 0, 0)
+		startDateReal = newStartDateReal.Format(utils.FormatDate)
+	}
+	if calendar == "农历" {
+		if len(dataList) <= 0 {
+			return
+		} else {
+			result, tmpErr := data_manage.AddCalculateQuarterV6(dataList)
+			if tmpErr != nil {
+				err = errors.New("获取农历数据失败,Err:" + tmpErr.Error())
+				return
+			}
+			quarterDataList, err = GetSeasonEdbInfoDataListByXDateNong(result, latestDate, seasonExtraConfig, calendarPreYear)
+			if err != nil {
+				err = errors.New("获取季节性图表数据失败,Err:" + err.Error())
+				return
+			}
+		}
+
+	} else {
+		quarterDataList, err = GetSeasonEdbInfoDataListByXDate(dataList, latestDate, seasonExtraConfig)
+		if err != nil {
+			err = errors.New("获取季节性图表数据失败,Err:" + err.Error())
+			return
+		}
+	}
+	return
+}
+
+func getBalanceDataListStartDateAndValue(dataList []*data_manage.EdbDataList) (startDate, endDate string, startVal, endVal, maxVal, minVal float64) {
+	if len(dataList) == 0 {
+		return
+	}
+	startDate = dataList[0].DataTime
+	startVal = dataList[0].Value
+	maxVal = dataList[0].Value
+	minVal = dataList[0].Value
+	endDate = dataList[len(dataList)-1].DataTime
+	endVal = dataList[len(dataList)-1].Value
+	for _, v := range dataList {
+		if v.DataTime < startDate {
+			startDate = v.DataTime
+			startVal = v.Value
+		}
+		if v.DataTime > endDate {
+			endDate = v.DataTime
+			endVal = v.Value
+		}
+		if v.Value > maxVal {
+			maxVal = v.Value
+		}
+		if v.Value < minVal {
+			minVal = v.Value
+		}
+	}
+	return
+}
+
+// AddBalanceStaticExcel 另存为和存为静态表以及子表的复制都调用该接口
+func AddBalanceStaticExcel(oldExcelInfo *excelModel.ExcelInfo, excelClassifyId int, versionName string, sysUser *system.Admin, parentId, relExcelInfoId, balanceType int, childExcelList []*excelModel.ExcelInfo, pingNameFlag bool, excelDataMap map[int]map[int][]*data_manage.EdbDataList) (excelInfo *excelModel.ExcelInfo, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+
+	versionName = utils.TrimLRStr(versionName)
+	// 检验分类下是否存在该表格名称
+	// todo 检验动态表分类下表格名称是否存在
+	if balanceType == 1 && versionName != "" {
+		var condition string
+		var pars []interface{}
+		condition += " AND rel_excel_info_id=? AND balance_type=1 AND parent_id=0"
+		pars = append(pars, oldExcelInfo.ExcelInfoId)
+
+		condition += " AND version_name=? "
+		pars = append(pars, versionName)
+
+		count, tmpErr := excelModel.GetExcelInfoCountByCondition(condition, pars)
+		if tmpErr != nil {
+			errMsg = "查询版本名称失败"
+			err = tmpErr
+			return
+		}
+		if count > 0 {
+			errMsg = "表格版本名称已存在,请重新填写版本名称"
+			err = errors.New(errMsg)
+			isSendEmail = false
+			return
+		}
+	}
+
+	// 表格信息
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	excelName := oldExcelInfo.ExcelName
+	if pingNameFlag && versionName != "" && parentId == 0 {
+		excelName += "(" + versionName + ")"
+	}
+
+	// 处理表格内容, 静态表去除动态指标关联
+	content := oldExcelInfo.Content
+	excelInfo = &excelModel.ExcelInfo{
+		//ExcelInfoId:     0,
+		ExcelName:          excelName,
+		Source:             oldExcelInfo.Source,
+		ExcelType:          oldExcelInfo.ExcelType,
+		UniqueCode:         utils.MD5(utils.EXCEL_DATA_PREFIX + "_" + timestamp),
+		ExcelClassifyId:    excelClassifyId,
+		SysUserId:          sysUser.AdminId,
+		SysUserRealName:    sysUser.RealName,
+		Content:            content,
+		ExcelImage:         oldExcelInfo.ExcelImage,
+		FileUrl:            oldExcelInfo.FileUrl,
+		ParentId:           parentId,
+		RelExcelInfoId:     relExcelInfoId,
+		VersionName:        versionName,
+		UpdateUserId:       sysUser.AdminId,
+		UpdateUserRealName: sysUser.RealName,
+		BalanceType:        balanceType,
+		Sort:               oldExcelInfo.Sort,
+		IsDelete:           0,
+		ModifyTime:         time.Now(),
+		CreateTime:         time.Now(),
+	}
+
+	excelEdbMappingList := make([]*excelModel.ExcelEdbMapping, 0)
+	if balanceType == 0 {
+		// 复制动态表才需要获取原excel与指标的关系表
+		excelEdbMappingListTmp, e := excelModel.GetAllExcelEdbMappingByExcelInfoId(oldExcelInfo.ExcelInfoId)
+		if e != nil {
+			err = e
+			errMsg = "获取失败"
+			return
+		}
+		for _, v := range excelEdbMappingListTmp {
+			tmp := new(excelModel.ExcelEdbMapping)
+			tmp.EdbInfoId = v.EdbInfoId
+			tmp.Source = utils.BALANCE_TABLE
+			tmp.CreateTime = time.Now()
+			tmp.ModifyTime = time.Now()
+			excelEdbMappingList = append(excelEdbMappingList, tmp)
+		}
+	}
+
+	var childExcel *excelModel.ExcelInfo
+	err = excelModel.AddExcelInfo(excelInfo, excelEdbMappingList, childExcel)
+	if err != nil {
+		errMsg = "保存失败"
+		return
+	}
+	if parentId == 0 && len(childExcelList) > 0 {
+		for _, childExcelInfo := range childExcelList {
+			_, err, errMsg, isSendEmail = AddBalanceStaticExcel(childExcelInfo, excelClassifyId, versionName, sysUser, excelInfo.ExcelInfoId, relExcelInfoId, balanceType, []*excelModel.ExcelInfo{}, pingNameFlag, excelDataMap)
+			if err != nil {
+				return
+			}
+		}
+	} else if parentId > 0 {
+		// 如果复制的是动态表的子表,则同步复制关联的图表
+		// 查出所有的chart_list, 同步复制图表和图表指标
+		// 相关联指标
+		mappingListTmp, e := excelModel.GetExcelChartEdbMappingByExcelInfoId(oldExcelInfo.ExcelInfoId)
+		if e != nil {
+			errMsg = "获取图表指标失败"
+			err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", e.Error())
+			return
+		}
+		mappingListMap := make(map[int][]*excelModel.ExcelChartEdb, 0)
+		charInfoIds := make([]int, 0)
+		for k, _ := range mappingListTmp {
+			mappingListTmp[k].ExcelInfoId = excelInfo.ExcelInfoId
+			mappingListMap[mappingListTmp[k].ChartInfoId] = append(mappingListMap[mappingListTmp[k].ChartInfoId], mappingListTmp[k])
+		}
+		for k, _ := range mappingListMap {
+			charInfoIds = append(charInfoIds, k)
+		}
+		if len(charInfoIds) > 0 {
+			chartInfoList, e := data_manage.GetChartInfoViewByIdList(charInfoIds)
+			if e != nil {
+				errMsg = "获取失败"
+				err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", e.Error())
+				return
+			}
+			for _, chartInfo := range chartInfoList {
+				mappingList, ok := mappingListMap[chartInfo.ChartInfoId]
+				if !ok {
+					err = fmt.Errorf("未找到图表关联的指标信息")
+					return
+				}
+				chartEdbDataMap, ok := excelDataMap[chartInfo.ChartInfoId]
+				if !ok && oldExcelInfo.BalanceType == 1 { //
+					err = fmt.Errorf("未找到图表关联的指标信息")
+					return
+				}
+				err, errMsg, isSendEmail = CopyBalanceExcelChart(chartInfo, mappingList, versionName, sysUser, pingNameFlag, chartEdbDataMap)
+				if err != nil {
+					err = fmt.Errorf("复制图表失败 Err:%s", err.Error())
+					return
+				}
+			}
+		}
+	}
+	return
+}
+
+// CopyBalanceExcelChart 复制平衡表图表
+func CopyBalanceExcelChart(oldChartInfo *data_manage.ChartInfoView, oldChartEdbList []*excelModel.ExcelChartEdb, versionName string, sysUser *system.Admin, pingNameFlag bool, dataListMap map[int][]*data_manage.EdbDataList) (err error, errMsg string, isSendEmail bool) {
+	chartEdbList := make([]*excelModel.AddChartEdbAndDataItem, 0)
+	if pingNameFlag && versionName != "" {
+		oldChartInfo.ChartName += "(" + versionName + ")"
+	}
+	for _, chartEdb := range oldChartEdbList {
+		excelEdb := new(excelModel.ExcelChartEdb)
+		excelEdb.EdbName = chartEdb.EdbName
+		if pingNameFlag && versionName != "" {
+			excelEdb.EdbName += "(" + versionName + ")"
+		}
+		excelEdb.EdbNameEn = excelEdb.EdbName
+		excelEdb.Unit = chartEdb.Unit
+		excelEdb.UnitEn = excelEdb.Unit
+		randStr := utils.GetRandDigit(4)
+		excelEdb.EdbCode = `T` + time.Now().Format("060102150405") + "_" + randStr
+		excelEdb.ExcelInfoId = chartEdb.ExcelInfoId
+		excelEdb.DateSequence = chartEdb.DateSequence
+		excelEdb.DataSequence = chartEdb.DataSequence
+		excelEdb.SysUserId = sysUser.AdminId
+		excelEdb.SysUserRealName = sysUser.RealName
+		excelEdb.MaxData = chartEdb.MaxData
+		excelEdb.MinData = chartEdb.MinData
+		excelEdb.IsOrder = chartEdb.IsOrder
+		excelEdb.IsAxis = chartEdb.IsAxis
+		excelEdb.FromTag = chartEdb.FromTag
+		excelEdb.EdbInfoType = chartEdb.EdbInfoType
+		excelEdb.LeadValue = chartEdb.LeadValue
+		excelEdb.LeadUnit = chartEdb.LeadUnit
+		excelEdb.ChartWidth = 1
+		excelEdb.CreateTime = time.Now()
+		excelEdb.ModifyTime = time.Now()
+		var dataList []*excelModel.ExcelChartData
+		tmpList, ok := dataListMap[chartEdb.ExcelChartEdbId]
+		if ok {
+			for _, l := range tmpList {
+				tmp := &excelModel.ExcelChartData{
+					DataTime:      l.DataTime,
+					Value:         l.Value,
+					DataTimestamp: l.DataTimestamp,
+				}
+				dataList = append(dataList, tmp)
+			}
+		}
+
+		// 处理日期列表和值列表
+		addItem := &excelModel.AddChartEdbAndDataItem{
+			ChartEdb: excelEdb,
+			DataList: dataList,
+		}
+		chartEdbList = append(chartEdbList, addItem)
+	}
+	chartInfo, err, errMsg, isSendEmail := copyBalanceExcelChart(oldChartInfo, sysUser.AdminId, sysUser.RealName)
+	if err != nil {
+		errMsg = "新增图表失败!"
+		err = fmt.Errorf("新增图表失败!%s, %s", errMsg, err.Error())
+		return
+	}
+	obj := new(excelModel.ExcelChartEdb)
+	err = obj.AddChartEdbAndData(chartEdbList, chartInfo, []int{})
+	if err != nil {
+		errMsg = "新增图表失败!"
+		err = fmt.Errorf("新增图表数据失败!%s", err.Error())
+		return
+	}
+	return
+}
+
+func copyBalanceExcelChart(oldChartInfo *data_manage.ChartInfoView, sysUserId int, sysUserRealName string) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true // 默认错误的时候要发送邮件
+
+	oldChartInfo.ChartName = strings.Trim(oldChartInfo.ChartName, " ")
+	if oldChartInfo.ChartName == "" {
+		errMsg = "请填写图表名称!"
+		err = fmt.Errorf(errMsg)
+		isSendEmail = false
+		return
+	}
+	//判断图表是否存在
+	var condition string
+	var pars []interface{}
+
+	// 图表名称在不同图分类下不允许重复 需求调整时间:2022年11月07日09:47:07
+	condition += " AND chart_classify_id=0 "
+
+	condition += " AND chart_name=? AND source = ? "
+	pars = append(pars, oldChartInfo.ChartName, utils.CHART_SOURCE_BALANCE_EXCEL)
+
+	count, err := data_manage.GetChartInfoCountByCondition(condition, pars)
+	if err != nil {
+		errMsg = "判断图表名称是否存在失败"
+		err = fmt.Errorf("判断图表名称是否存在失败,Err:" + err.Error())
+		return
+	}
+
+	if count > 0 {
+		/*errMsg = "图表已存在,请重新填写"
+		err = fmt.Errorf("判断图表名称是否存在失败")
+		isSendEmail = false
+		return*/
+		oldChartInfo.ChartName += " " + strconv.Itoa(count)
+	}
+
+	chartInfo = new(data_manage.ChartInfo)
+	chartInfo.ChartName = oldChartInfo.ChartName
+	chartInfo.ChartNameEn = oldChartInfo.ChartName
+	//chartInfo.EdbInfoIds = edbInfoIdStr
+	//chartInfo.ChartClassifyId = req.ChartClassifyId
+	chartInfo.SysUserId = sysUserId
+	chartInfo.SysUserRealName = sysUserRealName
+	chartInfo.CreateTime = time.Now()
+	chartInfo.ModifyTime = time.Now()
+	chartInfo.IsSetName = 0
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	chartInfo.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp)
+
+	// todo 判断是否需要重新计算用户的start_date
+	chartInfo.DateType = oldChartInfo.DateType
+	chartInfo.ChartType = oldChartInfo.ChartType
+	chartInfo.Calendar = oldChartInfo.Calendar
+	/*	chartInfo.StartDate = req.StartDate
+		chartInfo.EndDate = req.EndDate
+		chartInfo.SeasonStartDate = req.StartDate
+		chartInfo.SeasonEndDate = req.EndDate*/
+	chartInfo.LeftMin = oldChartInfo.LeftMin
+	chartInfo.LeftMax = oldChartInfo.LeftMax
+	chartInfo.RightMin = oldChartInfo.RightMin
+	chartInfo.RightMax = oldChartInfo.RightMax
+	chartInfo.Right2Min = oldChartInfo.Right2Min
+	chartInfo.Right2Max = oldChartInfo.Right2Max
+	chartInfo.MinMaxSave = oldChartInfo.MinMaxSave
+	//chartInfo.Disabled = disableVal
+	//chartInfo.BarConfig = barChartConf
+	chartInfo.ExtraConfig = oldChartInfo.ExtraConfig
+	chartInfo.SeasonExtraConfig = oldChartInfo.SeasonExtraConfig
+	//chartInfo.StartYear = req.StartYear
+	chartInfo.Source = utils.CHART_SOURCE_BALANCE_EXCEL
+	chartInfo.ChartThemeId = oldChartInfo.ChartThemeId
+	chartInfo.SourcesFrom = oldChartInfo.SourcesFrom
+	/*	chartInfo.Instructions = req.Instructions
+		chartInfo.MarkersLines = req.MarkersLines
+		chartInfo.MarkersAreas = req.MarkersAreas
+		chartInfo.Unit = req.Unit
+		chartInfo.UnitEn = req.UnitEn*/
+	/*newId, err := data_manage.AddChartInfo(chartInfo)
+	if err != nil {
+		errMsg = `保存失败`
+		err = fmt.Errorf("保存失败,Err:" + err.Error())
+		return
+	}
+	chartInfo.ChartInfoId = int(newId)*/
+
+	//添加es数据
+	//go EsAddOrEditChartInfo(chartInfo.ChartInfoId)
+
+	return
+}
+
+// CopyBalanceExcel 动态平衡表另存为
+func CopyBalanceExcel(oldExcelInfo *excelModel.ExcelInfo, excelClassifyId int, excelName string, sysUser *system.Admin) (excelInfo *excelModel.ExcelInfo, err error, errMsg string, isSendEmail bool) {
+	if oldExcelInfo.ParentId != 0 && oldExcelInfo.BalanceType != 0 {
+		errMsg = "平衡表类型错误"
+		err = fmt.Errorf("平衡表类型错误 ")
+		return
+	}
+
+	// 查询excel表格名称是否已经存在
+	// 检验分类下是否存在该表格名称
+	{
+		var nameCondition string
+		var namePars []interface{}
+		nameCondition += " AND excel_classify_id=?  AND parent_id=?"
+		namePars = append(namePars, excelClassifyId, 0)
+
+		nameCondition += " AND excel_name=? "
+		namePars = append(namePars, excelName)
+
+		count, tmpErr := excelModel.GetExcelInfoCountByCondition(nameCondition, namePars)
+		if tmpErr != nil {
+			errMsg = "判断表格名称是否存在失败"
+			err = tmpErr
+			return
+		}
+		if count > 0 {
+			errMsg = "表格名称已存在,请重新填写表格名称"
+			err = errors.New(errMsg)
+			isSendEmail = false
+			return
+		}
+	}
+	oldExcelInfo.ExcelName = excelName
+
+	// 先复制动态表,再复制动态子表,复制静态表,再复制静态子表
+	condition := " AND parent_id = ? AND balance_type = 0 "
+	var pars []interface{}
+	pars = append(pars, oldExcelInfo.ExcelInfoId)
+	childExcelList, err := excelModel.GetExcelInfoListByCondition(condition, pars)
+	if err != nil {
+		errMsg = "获取子表失败"
+		err = fmt.Errorf("获取子表失败 %s", err.Error())
+		return
+	}
+	// 查询出每个子表的内容,并将内容转为静态版本
+	excelDataMap := make(map[int]map[int][]*data_manage.EdbDataList)
+	excelInfo, err, errMsg, isSendEmail = AddBalanceStaticExcel(oldExcelInfo, excelClassifyId, "", sysUser, 0, 0, 0, childExcelList, false, excelDataMap)
+	if err != nil {
+		errMsg = "复制动态表失败"
+		err = fmt.Errorf("复制动态表失败 %s", err.Error())
+		return
+	}
+
+	//复制静态表
+	staticCondition := " AND parent_id = 0 AND balance_type = 1 AND rel_excel_info_id=? "
+	var staticPars []interface{}
+	staticPars = append(staticPars, oldExcelInfo.ExcelInfoId)
+	staticExcelList, err := excelModel.GetExcelInfoListByCondition(staticCondition, staticPars)
+	if err != nil {
+		errMsg = "获取子表失败"
+		err = fmt.Errorf("获取子表失败 %s", err.Error())
+		return
+	}
+	for _, staticExcelInfo := range staticExcelList {
+		cCondition := " AND parent_id = ? AND balance_type = 1"
+		var cPars []interface{}
+		cPars = append(cPars, staticExcelInfo.ExcelInfoId)
+		childList, e := excelModel.GetExcelInfoListByCondition(cCondition, cPars)
+		if e != nil {
+			errMsg = "获取子表失败"
+			err = fmt.Errorf("获取子表失败 %s", err.Error())
+			return
+		}
+		excelDataMap = make(map[int]map[int][]*data_manage.EdbDataList)
+		for _, childExcelInfo := range childList {
+			//得到表格数据并落库
+			tmpDataList, e := excelModel.GetExcelChartDataByExcelInfoId(childExcelInfo.ExcelInfoId)
+			if e != nil {
+				err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+				return
+			}
+			if len(tmpDataList) > 0 {
+				for _, v := range tmpDataList {
+					child, ok := excelDataMap[v.ChartInfoId]
+					if !ok {
+						child = make(map[int][]*data_manage.EdbDataList)
+					}
+					tmp := &data_manage.EdbDataList{
+						DataTime:      v.DataTime,
+						DataTimestamp: v.DataTimestamp,
+						Value:         v.Value,
+					}
+					child[v.ExcelChartEdbId] = append(child[v.ExcelChartEdbId], tmp)
+					excelDataMap[v.ChartInfoId] = child
+				}
+			}
+		}
+		_, err, errMsg, isSendEmail = AddBalanceStaticExcel(staticExcelInfo, excelClassifyId, staticExcelInfo.VersionName, sysUser, 0, excelInfo.ExcelInfoId, 1, childList, false, excelDataMap)
+		if err != nil {
+			errMsg = "复制动态表失败"
+			err = fmt.Errorf("复制动态表失败 %s", err.Error())
+			return
+		}
+	}
+	return
+}
+
+func TransferChartEdbToEdbMappingFormat(chartInfoId, chartType int, mappingListTmp []*excelModel.ExcelChartEdb, dataListMap map[int][]*data_manage.EdbDataList) (mappingList []*data_manage.ChartEdbInfoMapping, err error) {
+	mappingList = make([]*data_manage.ChartEdbInfoMapping, 0)
+	//循环组装映射关系
+	for _, v := range mappingListTmp {
+		dataList := make([]*data_manage.EdbDataList, 0)
+		dataListTmp, ok := dataListMap[v.ExcelChartEdbId]
+		if ok {
+			dataList = dataListTmp
+		} else {
+			//err = errors.New(fmt.Sprint("获取失败,指标类型异常", v.ExcelChartEdbId))
+			utils.FileLog.Info(fmt.Sprintf("获取失败,指标数据异常 %d", v.ExcelChartEdbId))
+		}
+		startDateStr, endDateStr, _, endVal, maxValue, minValue := getBalanceDataListStartDateAndValue(dataList)
+		mapping := &data_manage.ChartEdbInfoMapping{
+			EdbInfoId:         v.ExcelChartEdbId,
+			SourceName:        "平衡表",
+			Source:            0,
+			SubSource:         0,
+			EdbCode:           v.EdbCode,
+			EdbName:           v.EdbName,
+			EdbAliasName:      v.EdbName,
+			EdbNameEn:         v.EdbNameEn,
+			EdbAliasNameEn:    "",
+			EdbType:           0,
+			Frequency:         "",
+			FrequencyEn:       "",
+			Unit:              v.Unit,
+			UnitEn:            v.UnitEn,
+			StartDate:         startDateStr,
+			EndDate:           endDateStr,
+			ModifyTime:        v.ModifyTime.Format(utils.FormatDateTime),
+			ChartEdbMappingId: v.ExcelChartEdbId,
+			ChartInfoId:       chartInfoId,
+			MaxData:           v.MaxData,
+			MinData:           v.MinData,
+			IsOrder:           v.IsOrder,
+			IsAxis:            v.IsAxis,
+			EdbInfoType:       v.EdbInfoType,
+			//EdbInfoCategoryType: 0,
+			LeadValue:         v.LeadValue,
+			LeadUnit:          v.LeadUnit,
+			LeadUnitEn:        "",
+			ChartStyle:        "",
+			ChartColor:        "",
+			PredictChartColor: "",
+			ChartWidth:        v.ChartWidth,
+			ChartType:         chartType,
+			LatestDate:        endDateStr,
+			LatestValue:       endVal,
+			MoveLatestDate:    "",
+			UniqueCode:        "",
+			MinValue:          minValue,
+			MaxValue:          maxValue,
+			DataList:          nil,
+			IsNullData:        false,
+			MappingSource:     0,
+			RegionType:        "",
+			ClassifyId:        0,
+			IsConvert:         0,
+			ConvertType:       0,
+			ConvertValue:      0,
+			ConvertUnit:       "",
+			ConvertEnUnit:     "",
+			IsJoinPermission:  0,
+			HaveOperaAuth:     true,
+		}
+		mappingList = append(mappingList, mapping)
+	}
+	return
+}
+
+// GetBalanceExcelInfoOpButtonByChartInfoId 获取图表权限
+func GetBalanceExcelInfoOpButtonByChartInfoId(sysUserId, chartInfoId int, chartHaveOperaAuth bool) (opButton bool, err error) {
+	// 如果没有数据权限,那么直接返回
+	if !chartHaveOperaAuth {
+		return
+	}
+	// 获取表格信息
+	excelInfo, e := excelModel.GetExcelInfoByChartInfoId(chartInfoId)
+	if e != nil {
+		err = fmt.Errorf("获取ETA表格失败 Err %s", e.Error())
+		return
+	}
+	checkExcelInfo := excelInfo
+	if excelInfo.Source == utils.BALANCE_TABLE {
+		checkExcelInfoId := excelInfo.ExcelInfoId
+		if excelInfo.BalanceType == 1 {
+			checkExcelInfoId = excelInfo.RelExcelInfoId
+		} else {
+			if excelInfo.ParentId > 0 {
+				checkExcelInfoId = excelInfo.ParentId
+			}
+		}
+		if checkExcelInfoId != excelInfo.ExcelInfoId {
+			checkExcelInfo, e = excelModel.GetExcelInfoById(checkExcelInfoId)
+			if e != nil {
+				err = fmt.Errorf("获取平衡表格信息失败,Err:" + e.Error())
+				return
+			}
+		}
+	} else {
+		return
+	}
+	// 数据权限
+	haveOperaAuth, e := data_manage_permission.CheckExcelPermissionByExcelInfoId(checkExcelInfo.ExcelInfoId, checkExcelInfo.ExcelClassifyId, checkExcelInfo.IsJoinPermission, sysUserId)
+	if e != nil {
+		err = fmt.Errorf("获取表格权限信息失败,Err" + e.Error())
+		return
+	}
+	// 如果没有数据权限,那么直接返回
+	if !haveOperaAuth {
+		return
+	}
+	//button := GetBalanceExcelInfoOpButton(sysUserId, checkExcelInfo.SysUserId, haveOperaAuth, checkExcelInfo.ExcelInfoId)
+
+	if sysUserId == checkExcelInfo.SysUserId {
+		opButton = true
+	} else {
+		obj := new(excelModel.ExcelWorker)
+		workerList, e := obj.GetByExcelInfoId(checkExcelInfo.ExcelInfoId)
+		if e == nil {
+			for _, v := range workerList {
+				if v.SysUserId == sysUserId {
+					opButton = true
+					break
+				}
+			}
+		}
+	}
+	return
+}

+ 46 - 0
services/data/chart_info_interface.go

@@ -0,0 +1,46 @@
+package data
+
+import (
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/data_manage/excel"
+)
+
+type ChartInfoDataShow interface {
+	GetEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping, seasonExtraConfig string) (edbDataListMap map[int][]*data_manage.EdbDataList, edbList []*data_manage.ChartEdbInfoMapping, err error)
+	GetChartEdbMappingListByEdbInfoIdList(edbIdList []int) (mappingList []*data_manage.ChartEdbInfoMapping, err error)
+}
+
+type BaseChartInfoDataShow struct {
+}
+
+// GetEdbDataMapList 获取指标最后的基础数据
+func (e *BaseChartInfoDataShow) GetEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping, seasonExtraConfig string) (edbDataListMap map[int][]*data_manage.EdbDataList, edbList []*data_manage.ChartEdbInfoMapping, err error) {
+	// 指标对应的所有数据
+	edbDataListMap, edbList, err = GetEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList, seasonExtraConfig)
+	return
+}
+
+// GetChartEdbMappingListByEdbInfoIdList 获取指标最后的基础数据
+func (e *BaseChartInfoDataShow) GetChartEdbMappingListByEdbInfoIdList(edbIdList []int) (mappingList []*data_manage.ChartEdbInfoMapping, err error) {
+	mappingList, err = data_manage.GetChartEdbMappingListByEdbInfoIdList(edbIdList)
+	return
+}
+
+type ExcelChartInfoDataShow struct {
+	DataListMap    map[int][]*data_manage.EdbDataList
+	MappingListTmp []*excel.ExcelChartEdb
+}
+
+// GetEdbDataMapList 获取指标最后的基础数据
+func (e *ExcelChartInfoDataShow) GetEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping, seasonExtraConfig string) (edbDataListMap map[int][]*data_manage.EdbDataList, edbList []*data_manage.ChartEdbInfoMapping, err error) {
+	// 指标对应的所有数据
+	edbDataListMap, edbList, err = GetBalanceExcelEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList, seasonExtraConfig, e.DataListMap)
+	return
+}
+
+// GetChartEdbMappingListByEdbInfoIdList 获取指标最后的基础数据
+func (e *ExcelChartInfoDataShow) GetChartEdbMappingListByEdbInfoIdList(edbIdList []int) (mappingList []*data_manage.ChartEdbInfoMapping, err error) {
+	// 指标对应的所有数据
+	mappingList, err = TransferChartEdbToEdbMappingFormat(0, 0, e.MappingListTmp, e.DataListMap)
+	return
+}

+ 4 - 0
services/data/data_manage_permission/data_move.go

@@ -429,6 +429,10 @@ func GetMoveEdbChartList(source, subSource, userId int, keyword, classify string
 		condition += " AND source = ? "
 		pars = append(pars, subSource)
 
+		if subSource == utils.BALANCE_TABLE {
+			condition += " AND parent_id = 0 AND balance_type = 0 "
+		}
+
 		total, err = excel.GetExcelInfoCountByCondition(condition, pars)
 		if err != nil {
 			return

+ 7 - 1
services/data/data_manage_permission/edb_permission.go

@@ -119,7 +119,13 @@ func SetEdbChartPermission(source, subSource, userId int, authUserList []int, is
 	case 6:
 		// ETA表格
 		content += `(ETA表格)`
-		tmpList, tmpErr := excel.GetNoContentExcelListByExcelInfoIdList(dataIdList)
+		tmpList := make([]*excel.MyExcelInfoList, 0)
+		var tmpErr error
+		if subSource == utils.BALANCE_TABLE {
+			tmpList, tmpErr = excel.GetNoContentExcelListByExcelInfoIdAndParentId(dataIdList)
+		} else {
+			tmpList, tmpErr = excel.GetNoContentExcelListByExcelInfoIdList(dataIdList)
+		}
 		if tmpErr != nil {
 			err = tmpErr
 			return

+ 27 - 0
services/data/data_manage_permission/excel.go

@@ -153,3 +153,30 @@ func CheckExcelPermission(excelIsJoinPermission, excelClassifyIsJoinPermission,
 
 	return
 }
+
+func CheckBalanceExcelPermissionByExcelInfoId(excelInfo *excel.ExcelInfo, excelIsJoinPermission, userId int) (hasAuth bool, err error) {
+	// 查询父级ID
+	if excelInfo.Source != utils.BALANCE_TABLE {
+		return
+	}
+	parentId := excelInfo.ParentId
+	if excelInfo.BalanceType == 1 { //静态表关联的动态表的权限
+		parentId = excelInfo.RelExcelInfoId
+	}
+	parentExcelInfo, err := excel.GetExcelInfoById(parentId)
+	if err != nil {
+		err = fmt.Errorf("查询表格信息出错 err: %v", err)
+		return
+	}
+	excelClassifyId := parentExcelInfo.ExcelClassifyId
+	excelInfoId := parentExcelInfo.ExcelInfoId
+	currClassify, err := excel.GetExcelClassifyById(excelClassifyId)
+	if err != nil {
+		return
+	}
+	if currClassify != nil {
+		return CheckExcelPermission(excelIsJoinPermission, currClassify.IsJoinPermission, userId, excelInfoId, excelClassifyId)
+	}
+
+	return
+}

+ 88 - 16
services/data/edb_classify.go

@@ -449,7 +449,7 @@ func EditEdbClassify(classifyId int, classifyName, lang string, sysUser *system.
 }
 
 // DeleteCheck 删除检测
-func DeleteCheck(classifyId, edbInfoId int, sysUser *system.Admin) (deleteStatus int, tipsMsg string, err error, errMsg string) {
+func DeleteCheck(classifyId, edbInfoId int, sysUser *system.Admin) (deleteStatus int, tipsMsg string, tableList []*data_manage.ExcelBaseInfo, err error, errMsg string) {
 	//删除分类
 	if classifyId > 0 && edbInfoId == 0 {
 		// 查找分类
@@ -557,7 +557,7 @@ func DeleteCheck(classifyId, edbInfoId int, sysUser *system.Admin) (deleteStatus
 			}
 			if calculateCount > 0 {
 				deleteStatus = 4
-				tipsMsg = "当前指标已用作指标运算,不可删除"
+				tipsMsg = "当前指标已用作指标运算,不可删除"
 				return
 			}
 		}
@@ -571,7 +571,7 @@ func DeleteCheck(classifyId, edbInfoId int, sysUser *system.Admin) (deleteStatus
 				return
 			}
 			if predictEdbInfoCount > 0 {
-				deleteStatus = 3
+				deleteStatus = 5
 				tipsMsg = "当前指标已用作预测指标,不可删除"
 				return
 			}
@@ -579,22 +579,66 @@ func DeleteCheck(classifyId, edbInfoId int, sysUser *system.Admin) (deleteStatus
 
 		// 判断指标是否用作表格引用
 		{
-			calculateCount, tmpErr := excel.GetNoCustomAnalysisExcelEdbMappingCount(edbInfoId)
+			tableItems, tmpErr := excel.GetNoCustomAnalysisExcelEdbMapping(edbInfoId)
 			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
 				errMsg = "删除失败"
 				err = errors.New("判断指标是否用作表格引用,GetNoCustomAnalysisExcelEdbMappingCount Err:" + tmpErr.Error())
 				return
 			}
-			if calculateCount > 0 {
-				deleteStatus = 3
-				tipsMsg = "当前指标已添加到表格,不可删除"
+			//英文翻译:
+			//1、当前指标已用作画图,不可删除:The current metric is in use for charting and cannot be deleted
+			//2、当前指标已被表格引用,不可删除:The current metric is referenced by a table and cannot be deleted
+			//3、删除失败:Deletion failed
+			//4、知道了:Understood
+			if len(tableItems) > 0 {
+				deleteStatus = 6
+				tipsMsg = "当前指标已被表格引用,不可删除"
+				var excelIds []int
+				for _, tableItem := range tableItems {
+					if tableItem.ParentId > 0 {
+						excelIds = append(excelIds, tableItem.ParentId)
+					} else {
+						excelIds = append(excelIds, tableItem.ExcelInfoId)
+					}
+				}
+				tableList, tmpErr = excel.GetExcelBaseInfoByExcelInfoIdList(excelIds)
+				if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+					errMsg = "删除失败"
+					err = errors.New("判断指标是否用作表格引用,GetExcelBaseInfoByExcelInfoIdList Err:" + tmpErr.Error())
+					return
+				}
 				return
 			}
 		}
 
 		// 判断指标是否用作跨品种图表使用
 		{
-			calculateCount, tmpErr := cross_variety.GetCountByEdbInfoId(edbInfoId)
+			// todo 如果绑定的标签未画图,则允许删除绑定
+			// 查询跨品种的图表
+			tagXList, tmpErr := cross_variety.GetChartInfoCrossVarietyByXEdbInfoId(edbInfoId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "删除失败"
+				err = errors.New("判断指标是否用作跨品种图表使用,GetChartInfoCrossVarietyByXEdbInfoId Err:" + tmpErr.Error())
+				return
+			}
+			if len(tagXList) > 0 {
+				deleteStatus = 7
+				tipsMsg = "当前指标已添加到跨品种分析,不可删除"
+				return
+			}
+			tagYList, tmpErr := cross_variety.GetChartInfoCrossVarietyByYEdbInfoId(edbInfoId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "删除失败"
+				err = errors.New("判断指标是否用作跨品种图表使用,GetChartInfoCrossVarietyByYEdbInfoId Err:" + tmpErr.Error())
+				return
+			}
+
+			if len(tagYList) > 0 {
+				deleteStatus = 7
+				tipsMsg = "当前指标已添加到跨品种分析,不可删除"
+				return
+			}
+			/*calculateCount, tmpErr := cross_variety.GetCountByEdbInfoId(edbInfoId)
 			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
 				errMsg = "删除失败"
 				err = errors.New("判断指标是否用作跨品种图表使用,GetCountByEdbInfoId Err:" + tmpErr.Error())
@@ -604,14 +648,14 @@ func DeleteCheck(classifyId, edbInfoId int, sysUser *system.Admin) (deleteStatus
 				deleteStatus = 3
 				tipsMsg = "当前指标已添加到跨品种分析,不可删除"
 				return
-			}
+			}*/
 		}
 	}
 	return
 }
 
 // Delete 删除分类/指标
-func Delete(classifyId, edbInfoId int, sysUser *system.Admin, requestBody, requestUrl string) (nextItem *data_manage.EdbInfo, err error, errMsg string) {
+func Delete(classifyId, edbInfoId int, sysUser *system.Admin, requestBody, requestUrl string) (nextItem *data_manage.EdbInfo, tableList []*data_manage.ExcelBaseInfo, err error, errMsg string) {
 	//删除分类
 	if classifyId > 0 && edbInfoId == 0 {
 		// 查找分类
@@ -733,21 +777,49 @@ func Delete(classifyId, edbInfoId int, sysUser *system.Admin, requestBody, reque
 
 		// 判断指标是否用作表格引用
 		{
-			calculateCount, tmpErr := excel.GetNoCustomAnalysisExcelEdbMappingCount(edbInfoId)
+			tableItems, tmpErr := excel.GetNoCustomAnalysisExcelEdbMapping(edbInfoId)
 			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
 				errMsg = "删除失败"
 				err = errors.New("判断指标是否用作表格引用,GetNoCustomAnalysisExcelEdbMappingCount Err:" + tmpErr.Error())
 				return
 			}
-			if calculateCount > 0 {
-				errMsg = "当前指标已添加到表格,不可删除"
+			//英文翻译:
+			//1、当前指标已用作画图,不可删除:The current metric is in use for charting and cannot be deleted
+			//2、当前指标已被表格引用,不可删除:The current metric is referenced by a table and cannot be deleted
+			//3、删除失败:Deletion failed
+			//4、知道了:Understood
+			if len(tableItems) > 0 {
+				errMsg = "当前指标已被表格引用,不可删除"
 				return
 			}
 		}
-
 		// 判断指标是否用作跨品种图表使用
 		{
-			calculateCount, tmpErr := cross_variety.GetCountByEdbInfoId(edbInfoId)
+			// todo 如果绑定的标签未画图,则允许删除绑定
+			// 查询跨品种的图表
+			tagXList, tmpErr := cross_variety.GetChartInfoCrossVarietyByXEdbInfoId(edbInfoId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "删除失败"
+				err = errors.New("判断指标是否用作跨品种图表使用,GetChartInfoCrossVarietyByXEdbInfoId Err:" + tmpErr.Error())
+				return
+			}
+			if len(tagXList) > 0 {
+				errMsg = "当前指标已添加到跨品种分析,不可删除"
+				return
+			}
+			tagYList, tmpErr := cross_variety.GetChartInfoCrossVarietyByYEdbInfoId(edbInfoId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "删除失败"
+				err = errors.New("判断指标是否用作跨品种图表使用,GetChartInfoCrossVarietyByYEdbInfoId Err:" + tmpErr.Error())
+				return
+			}
+
+			if len(tagYList) > 0 {
+				errMsg = "当前指标已添加到跨品种分析,不可删除"
+				return
+			}
+
+			/*calculateCount, tmpErr := cross_variety.GetCountByEdbInfoId(edbInfoId)
 			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
 				errMsg = "删除失败"
 				err = errors.New("判断指标是否用作跨品种图表使用,GetCountByEdbInfoId Err:" + tmpErr.Error())
@@ -756,7 +828,7 @@ func Delete(classifyId, edbInfoId int, sysUser *system.Admin, requestBody, reque
 			if calculateCount > 0 {
 				errMsg = "当前指标已添加到跨品种分析,不可删除"
 				return
-			}
+			}*/
 		}
 
 		//真实删除

+ 30 - 18
services/data/edb_info.go

@@ -2619,24 +2619,36 @@ func BatchRefreshEdbByEdbIds(edbIdList []int, redisKey string, refreshKeys []str
 	if totalEdbInfo > 20 {
 		syncing = true
 
-		go func() {
-			defer func() {
-				if err != nil {
-					alarm_msg.SendAlarmMsg("BatchRefreshEdbByEdbIds, ErrMsg: "+err.Error(), 3)
-				}
-			}()
-
-			err = edbInfoRefreshAll(false, newBaseEdbInfoArr, newBasePredictEdbInfoArr, newCalculateMap, newPredictCalculateMap, calculateArr, predictCalculateArr)
-
-			if redisKey != `` {
-				_ = utils.Rc.Delete(redisKey)
-			}
-			if len(refreshKeys) > 0 {
-				for _, v := range refreshKeys {
-					_ = utils.Rc.Delete(v)
-				}
-			}
-		}()
+		//go func() {
+		//	defer func() {
+		//		if err != nil {
+		//			alarm_msg.SendAlarmMsg("BatchRefreshEdbByEdbIds, ErrMsg: "+err.Error(), 3)
+		//		}
+		//	}()
+		//
+		//	err = edbInfoRefreshAll(false, newBaseEdbInfoArr, newBasePredictEdbInfoArr, newCalculateMap, newPredictCalculateMap, calculateArr, predictCalculateArr)
+		//
+		//	if redisKey != `` {
+		//		_ = utils.Rc.Delete(redisKey)
+		//	}
+		//	if len(refreshKeys) > 0 {
+		//		for _, v := range refreshKeys {
+		//			_ = utils.Rc.Delete(v)
+		//		}
+		//	}
+		//}()
+
+		// 加入队列刷新
+		var refreshItem EdbQueueRefreshReq
+		refreshItem.RefreshKey = redisKey
+		refreshItem.ItemRefreshKeys = refreshKeys
+		refreshItem.BaseEdbInfoArr = newBaseEdbInfoArr
+		refreshItem.BasePredictEdbInfoArr = newBasePredictEdbInfoArr
+		refreshItem.CalculateMap = newCalculateMap
+		refreshItem.PredictCalculateMap = newPredictCalculateMap
+		refreshItem.CalculateArr = calculateArr
+		refreshItem.PredictCalculateArr = predictCalculateArr
+		go PushEdb2Refresh(refreshItem)
 		return
 	}
 

+ 105 - 0
services/data/edb_refresh.go

@@ -0,0 +1,105 @@
+package data
+
+import (
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/services/alarm_msg"
+	"eta/eta_api/utils"
+	"fmt"
+	"sync"
+)
+
+var (
+	EdbRefreshLock     sync.Map                // 指标刷新锁, 避免同时间重复key的刷新
+	EdbRefreshHandling chan EdbQueueRefreshReq // 排队刷新的20+指标的图表/表格
+	EdbRefreshWorker   chan struct{}           // 同时允许N组刷新
+)
+
+func init() {
+	EdbRefreshHandling = make(chan EdbQueueRefreshReq, 50) // N为最大排队等候的goroutine数
+	EdbRefreshWorker = make(chan struct{}, 5)              // 控制刷新速率修改此N值
+}
+
+type EdbQueueRefreshReq struct {
+	RefreshKey            string   `description:"刷新请求的缓存key"`
+	ItemRefreshKeys       []string `description:"图表/表格刷新后要删除的缓存key"`
+	BaseEdbInfoArr        []*data_manage.EdbInfo
+	BasePredictEdbInfoArr []*data_manage.EdbInfo
+	CalculateMap          map[int]*data_manage.EdbInfo
+	PredictCalculateMap   map[int]*data_manage.EdbInfo
+	CalculateArr          []int
+	PredictCalculateArr   []int
+}
+
+// PushEdb2Refresh 写入指标刷新队列
+func PushEdb2Refresh(item EdbQueueRefreshReq) {
+	refreshKey := item.RefreshKey
+	if refreshKey == "" {
+		return
+	}
+
+	// 检查是否已在队列中
+	_, ok := EdbRefreshLock.Load(refreshKey)
+	if ok {
+		return
+	}
+
+	// 追加至刷新队列
+	EdbRefreshHandling <- item
+
+	EdbRefreshLock.Store(refreshKey, true)
+}
+
+// HandleEdbRefreshQueue 排队刷新指标
+func HandleEdbRefreshQueue() {
+	defer func() {
+		if err := recover(); err != nil {
+			tips := fmt.Sprintf("[HandleEdbRefreshQueue] panic: %v", err)
+			fmt.Println(tips)
+			utils.FileLog.Info(tips)
+		}
+	}()
+	fmt.Println("HandleEdbRefreshQueue start")
+
+	for {
+		select {
+		case item, ok := <-EdbRefreshHandling:
+			if !ok {
+				return
+			}
+			go EdbRefreshTask(item)
+		}
+	}
+}
+
+// EdbRefreshTask 指标刷新任务
+func EdbRefreshTask(item EdbQueueRefreshReq) {
+	var err error
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("[EdbRefreshTask] ErrMsg: %s", err.Error())
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+		<-EdbRefreshWorker
+	}()
+	EdbRefreshWorker <- struct{}{}
+
+	fmt.Printf("EdbRefreshTask开始刷新: %s\n", item.RefreshKey)
+	// 刷新指标
+	e := edbInfoRefreshAll(false, item.BaseEdbInfoArr, item.BasePredictEdbInfoArr, item.CalculateMap, item.PredictCalculateMap, item.CalculateArr, item.PredictCalculateArr)
+	if e != nil {
+		err = fmt.Errorf("edbInfoRefreshAll err: %s", e.Error())
+	}
+
+	// 清除缓存
+	_ = utils.Rc.Delete(item.RefreshKey)
+	if len(item.ItemRefreshKeys) > 0 {
+		for _, v := range item.ItemRefreshKeys {
+			_ = utils.Rc.Delete(v)
+		}
+	}
+
+	// 解除锁
+	EdbRefreshLock.Delete(item.RefreshKey)
+	fmt.Printf("EdbRefreshTask结束刷新: %s\n", item.RefreshKey)
+}

+ 583 - 0
services/data/excel/balance_table.go

@@ -0,0 +1,583 @@
+package excel
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_api/models/data_manage"
+	excelModel "eta/eta_api/models/data_manage/excel"
+	"eta/eta_api/models/data_manage/excel/request"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/xuri/excelize/v2"
+	"strconv"
+	"time"
+)
+
+// GetBalanceExcelData 将表格信息转化成指标数据
+func GetBalanceExcelData(excelDetail *excelModel.ExcelInfo, lang string) (newDataMap map[int]map[int]request.MixedTableCellDataReq, allRows, allCols int, err error, errMsg string) {
+	var result request.MixedTableReq
+	err = json.Unmarshal([]byte(excelDetail.Content), &result)
+	if err != nil {
+		err = errors.New("表格json转结构体失败,Err:" + err.Error())
+		return
+	}
+	newData, tmpErr, tmpErrMsg := GetMixedTableCellData(result, lang)
+	if tmpErr != nil {
+		errMsg = "获取失败"
+		if tmpErrMsg != `` {
+			errMsg = tmpErrMsg
+		}
+		err = errors.New("获取最新的数据失败,Err:" + tmpErr.Error())
+		return
+	}
+
+	allRows = len(newData)
+	allCols = 0
+	newDataMap = make(map[int]map[int]request.MixedTableCellDataReq)
+	for r, row := range newData {
+		tmp := len(row)
+		if tmp > allCols {
+			allCols = tmp
+		}
+		colMap := make(map[int]request.MixedTableCellDataReq)
+		for c, col := range row {
+			colMap[c] = col
+		}
+		newDataMap[r] = colMap
+	}
+	return
+}
+
+func GetBalanceExcelChartList(excelInfo *excelModel.ExcelInfo, lang string) (list []*data_manage.ChartInfoView, mappingListMap map[int][]*excelModel.ExcelChartEdb, dataListMap map[int][]*data_manage.EdbDataList, err error, errMsg string) {
+	dataListMap = make(map[int][]*data_manage.EdbDataList)
+	// 相关联指标
+	mappingListTmp, err := excelModel.GetExcelChartEdbMappingByExcelInfoId(excelInfo.ExcelInfoId)
+	if err != nil {
+		errMsg = "获取失败"
+		err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+		return
+	}
+	charInfoIds := make([]int, 0)
+	mappingListMap = make(map[int][]*excelModel.ExcelChartEdb, 0)
+	if excelInfo.BalanceType == 1 {
+		for _, mapping := range mappingListTmp {
+			mappingListMap[mapping.ChartInfoId] = append(mappingListMap[mapping.ChartInfoId], mapping)
+		}
+		//查询库里是否有值
+		chartDataList, e := excelModel.GetExcelChartDataByExcelInfoId(excelInfo.ExcelInfoId)
+		if e != nil {
+			err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", e.Error())
+			return
+		}
+		if len(chartDataList) > 0 {
+			for _, v := range chartDataList {
+				tmp := &data_manage.EdbDataList{
+					EdbDataId:     v.ExcelChartDataId,
+					EdbInfoId:     v.ExcelChartEdbId,
+					DataTime:      v.DataTime,
+					DataTimestamp: v.DataTimestamp,
+					Value:         v.Value,
+				}
+				dataListMap[v.ExcelChartEdbId] = append(dataListMap[v.ExcelChartEdbId], tmp)
+			}
+		}
+	} else {
+		newExcelDataMap, excelAllRows, excelAllCols, e, msg := GetBalanceExcelData(excelInfo, lang)
+		if e != nil {
+			err = e
+			errMsg = msg
+			return
+		}
+		for _, mapping := range mappingListTmp {
+			mappingListMap[mapping.ChartInfoId] = append(mappingListMap[mapping.ChartInfoId], mapping)
+			er, ms := GetBalanceExcelEdbData(mapping, newExcelDataMap, dataListMap, excelAllRows, excelAllCols)
+			if er != nil {
+				utils.FileLog.Info(fmt.Sprintf(" 获取图表,指标信息失败 Err:%s, %s", er.Error(), ms))
+				continue
+			}
+		}
+	}
+
+	for k, _ := range mappingListMap {
+		charInfoIds = append(charInfoIds, k)
+	}
+	list = make([]*data_manage.ChartInfoView, 0)
+	if len(charInfoIds) > 0 {
+		chartInfoList, e := data_manage.GetChartInfoViewByIdList(charInfoIds)
+		if e != nil {
+			errMsg = "获取失败"
+			err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", e.Error())
+			return
+		}
+		list = chartInfoList
+	}
+	return
+}
+
+// GetBalanceExcelEdbData 将表格信息转化成指标数据
+func GetBalanceExcelEdbData(excelEdbMappingItem *excelModel.ExcelChartEdb, newMixedTableCellDataListMap map[int]map[int]request.MixedTableCellDataReq, dataListMap map[int][]*data_manage.EdbDataList, allRows, allCols int) (err error, errMsg string) {
+	var dateList, dataList []string
+	// 日期序列
+	{
+		_, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr := GetSheetStr(excelEdbMappingItem.DateSequence)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		startNum = startNum - 1
+		endNum = endNum - 1
+		// 选择行的数据
+		if isRow {
+			// 因为是选择一行的数据,所以开始行和结束行时一样的
+			//endNum = startNum - 1
+
+			// 开始列名、结束列
+			var startColumn, endColumn int
+			if isAll {
+				// 结束列(其实也就是整列的个数)
+				endColumn = allCols - 1
+			} else {
+				tmpStartColumn, tmpErr := excelize.ColumnNameToNumber(startColumnName)
+				if tmpErr != nil {
+					errMsg = "列名异常:" + startColumnName
+					err = errors.New(errMsg)
+					return
+				}
+
+				tmpEndColumn, tmpErr := excelize.ColumnNameToNumber(endColumnName)
+				if tmpErr != nil {
+					errMsg = "列名异常:" + endColumnName
+					err = errors.New(errMsg)
+					return
+				}
+				startColumn = tmpStartColumn - 1
+				endColumn = tmpEndColumn - 1
+			}
+
+			// 最大列数,如果设置的超过了最大列数,那么结束列就是最大列数
+			maxCol := allCols
+			if endColumn > maxCol {
+				endColumn = maxCol - 1
+			}
+
+			// 长度固定,避免一直申请内存空间
+			dateList = make([]string, endColumn-startColumn+1)
+
+			i := 0
+			for currColumn := startColumn; currColumn <= endColumn; currColumn++ {
+				currCell, ok := newMixedTableCellDataListMap[startNum][currColumn]
+				if !ok {
+					errMsg = fmt.Sprintf("第%d列,第%d行数据异常", currColumn, startNum)
+					err = errors.New(errMsg)
+					return
+				}
+				dateList[i] = currCell.ShowValue
+				i++
+			}
+
+		} else if isColumn { // 选择列的数据
+			if isAll {
+				// 选择一整列的话,结束行得根据实际情况调整(其实也就是整个sheet有多少行)
+				endNum = allRows - 1
+			}
+
+			startColumn, tmpErr := excelize.ColumnNameToNumber(startColumnName)
+			if tmpErr != nil {
+				errMsg = "列名异常:" + startColumnName
+				err = errors.New(errMsg)
+				return
+			}
+			startColumn = startColumn - 1
+
+			// 最大行数,如果设置的超过了最大行数,那么结束行就是最大行数
+			maxRow := allRows
+			if endNum > maxRow {
+				endNum = maxRow - 1
+			}
+			// 长度固定,避免一直申请内存空间
+			dateList = make([]string, endNum-startNum+1)
+			i := 0
+			for currRow := startNum; currRow <= endNum; currRow++ {
+				currCell, ok := newMixedTableCellDataListMap[currRow][startColumn]
+				if !ok {
+					errMsg = fmt.Sprintf("第%d列,第%d行数据异常", currRow, startColumn)
+					err = errors.New(errMsg)
+					return
+				}
+				//dateList = append(dateList, currCell.Value)
+				dateList[i] = currCell.ShowValue
+				i++
+			}
+		}
+
+	}
+
+	// 数据序列
+	{
+		_, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr := GetSheetStr(excelEdbMappingItem.DataSequence)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		startNum = startNum - 1
+		endNum = endNum - 1
+		// 选择行的数据
+		if isRow {
+			// 开始列名、结束列
+			var startColumn, endColumn int
+			if isAll {
+				// 结束列(其实也就是整列的个数)
+				endColumn = allCols - 1
+			} else {
+
+				tmpStartColumn, tmpErr := excelize.ColumnNameToNumber(startColumnName)
+				if tmpErr != nil {
+					errMsg = "列名异常:" + startColumnName
+					err = errors.New(errMsg)
+					return
+				}
+
+				tmpEndColumn, tmpErr := excelize.ColumnNameToNumber(endColumnName)
+				if tmpErr != nil {
+					errMsg = "列名异常:" + endColumnName
+					err = errors.New(errMsg)
+					return
+				}
+				startColumn = tmpStartColumn - 1
+				endColumn = tmpEndColumn - 1
+			}
+
+			// 最大列数,如果设置的超过了最大列数,那么结束列就是最大列数
+			maxCol := allCols
+			if endColumn > maxCol {
+				endColumn = maxCol - 1
+			}
+			// 长度固定,避免一直申请内存空间
+			dataList = make([]string, endColumn-startColumn+1)
+			i := 0
+			for currColumn := startColumn; currColumn <= endColumn; currColumn++ {
+				currCell, ok := newMixedTableCellDataListMap[startNum][currColumn]
+				if !ok {
+					errMsg = fmt.Sprintf("第%d列,第%d行数据异常", startColumn, currColumn)
+					err = errors.New(errMsg)
+					return
+				}
+				//dataList = append(dataList, currCell.Value)
+				dataList[i] = currCell.ShowValue
+				i++
+			}
+
+		} else if isColumn { // 选择列的数据
+			if isAll {
+				// 选择一整列的话,结束行得根据实际情况调整(其实也就是整个sheet有多少行)
+				endNum = allRows - 1
+			}
+
+			startColumn, tmpErr := excelize.ColumnNameToNumber(startColumnName)
+			if tmpErr != nil {
+				errMsg = "列名异常:" + startColumnName
+				err = errors.New(errMsg)
+				return
+			}
+			startColumn = startColumn - 1
+
+			// 最大行数,如果设置的超过了最大行数,那么结束行就是最大行数
+			maxRow := allRows
+			if endNum > maxRow {
+				endNum = maxRow - 1
+			}
+
+			// 长度固定,避免一直申请内存空间
+			dataList = make([]string, endNum-startNum+1)
+			i := 0
+			for currRow := startNum; currRow <= endNum; currRow++ {
+				currCell, ok := newMixedTableCellDataListMap[currRow][startColumn]
+				if !ok {
+					errMsg = fmt.Sprintf("第%d列,第%d行数据异常", currRow, startColumn)
+					err = errors.New(errMsg)
+					return
+				}
+				//dataList = append(dataList, currCell.Value)
+				dataList[i] = currCell.ShowValue
+				i++
+			}
+		}
+	}
+
+	//fmt.Println(dateList, dataList)
+	//fmt.Println("日期序列结束")
+
+	// 将excel中的日期、数据系列处理
+	//newDateList, newDataList, err, errMsg := excel2.HandleEdbSequenceVal(dateList, dataList)
+	newDateList, newDataList := dateList, dataList
+	if err != nil {
+		err = fmt.Errorf(" 处理日期和数据系列失败 %s", err.Error())
+		return
+	}
+	newDataMap := make(map[int]float64, len(newDataList))
+	for i, v := range newDataList {
+		if v != "" {
+			val, e := strconv.ParseFloat(v, 64)
+			if e != nil {
+				err = fmt.Errorf(" 处理日期和数据系列失败 %s", e.Error())
+				return
+			}
+			newDataMap[i] = val
+		}
+	}
+	//组装成excelEdbData
+	list := make([]*data_manage.EdbDataList, 0)
+
+	for i, v := range newDateList {
+		val, ok := newDataMap[i]
+		if !ok {
+			continue
+		}
+		// todo 处理DataTimestamp
+		dataTime, e := time.ParseInLocation(utils.FormatDate, v, time.Local)
+		if e != nil {
+			err = errors.New("time.Parse Err:" + e.Error())
+			return
+		}
+		timestamp := dataTime.UnixNano() / 1e6
+		tmp := &data_manage.EdbDataList{
+			EdbDataId:     i,
+			EdbInfoId:     excelEdbMappingItem.ExcelChartEdbId,
+			DataTime:      v,
+			DataTimestamp: timestamp,
+			Value:         val,
+		}
+		list = append(list, tmp)
+	}
+	dataListMap[excelEdbMappingItem.ExcelChartEdbId] = list
+	return
+}
+
+// 根据chartInfoId获取单个图表信息
+func GetBalanceExcelChartSingle(chartInfoId, ChartEdbId int, lang string) (mappingListTmp []*excelModel.ExcelChartEdb, dataListMap map[int][]*data_manage.EdbDataList, err error, errMsg string) {
+	// 相关联指标
+	if chartInfoId == 0 && ChartEdbId == 0 {
+		err = fmt.Errorf(" 获取图表,指标信息失败 Err:chartInfoId和ChartEdbId不能同时为空")
+		return
+	}
+	if chartInfoId == 0 {
+		chartEdb, e := excelModel.GetExcelChartEdbById(ChartEdbId)
+		if e != nil {
+			err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", e.Error())
+			return
+		}
+		chartInfoId = chartEdb.ChartInfoId
+	}
+	mappingListTmp, err = excelModel.GetExcelChartEdbMappingByChartInfoId(chartInfoId)
+	if err != nil {
+		errMsg = "获取失败"
+		err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+		return
+	}
+	if len(mappingListTmp) <= 0 {
+		errMsg = "获取失败"
+		err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+		return
+	}
+	// 查询所有子表
+	excelInfoId := mappingListTmp[0].ExcelInfoId
+	excelInfo, err := excelModel.GetExcelInfoById(excelInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "表格不存在"
+			err = fmt.Errorf(errMsg)
+			return
+		}
+		errMsg = "查询子表失败"
+		err = fmt.Errorf(" 查询子表失败图表,指标信息失败 Err:%s", err.Error())
+		return
+	}
+	dataListMap = make(map[int][]*data_manage.EdbDataList)
+	if excelInfo.BalanceType == 1 {
+		//查询库里是否有值
+		chartDataList, e := excelModel.GetExcelChartDataByChartInfoId(chartInfoId)
+		if e != nil {
+			err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", e.Error())
+			return
+		}
+		if len(chartDataList) == 0 {
+			err = fmt.Errorf(" 获取图表,指标数据不存在")
+			return
+		}
+		for _, v := range chartDataList {
+			tmp := &data_manage.EdbDataList{
+				EdbDataId:     v.ExcelChartDataId,
+				EdbInfoId:     v.ExcelChartEdbId,
+				DataTime:      v.DataTime,
+				DataTimestamp: v.DataTimestamp,
+				Value:         v.Value,
+			}
+			dataListMap[v.ExcelChartEdbId] = append(dataListMap[v.ExcelChartEdbId], tmp)
+		}
+		return
+	}
+
+	// 获取图表详情
+	newExcelDataMap, excelAllRows, excelAllCols, err, errMsg := GetBalanceExcelData(excelInfo, lang)
+	if err != nil {
+		return
+	}
+
+	for _, mapping := range mappingListTmp {
+		er, msg := GetBalanceExcelEdbData(mapping, newExcelDataMap, dataListMap, excelAllRows, excelAllCols)
+		if er != nil {
+			utils.FileLog.Info(fmt.Sprintf(" 获取图表,指标信息失败 Err:%s, %s", er.Error(), msg))
+			continue
+		}
+	}
+	return
+}
+
+// TransferBalanceExcelContentToStatic 将平衡表动态表的内容转换为静态表的格式
+func TransferBalanceExcelContentToStatic(excelDetail *excelModel.ExcelInfo, lang string) (newContent string, err error) {
+	var mixedTableReq request.MixedTableReq
+	err = json.Unmarshal([]byte(excelDetail.Content), &mixedTableReq)
+	if err != nil {
+		err = errors.New("表格json转结构体失败,Err:" + err.Error())
+		return
+	}
+
+	cellRelationConf := mixedTableReq.CellRelation
+	//config := mixedTableReq.Data
+	// 单元格关系配置x信息
+	cellRelationConfMap := make(map[string]request.CellRelationConf)
+	cellRelationConfList := make([]request.CellRelationConf, 0)
+	cellRelationConfNewList := make([]request.CellRelationConf, 0)
+	if cellRelationConf != `` {
+		err = json.Unmarshal([]byte(cellRelationConf), &cellRelationConfList)
+		if err != nil {
+			return
+		}
+
+		for _, v := range cellRelationConfList {
+			if v.Type == request.EdbDT || v.Type == request.InsertDataDT || v.Type == request.PopInsertDataDT || v.Type == request.InsertEdbCalculateDataDT || v.Type == request.DateCalculateDataDT {
+			} else {
+				cellRelationConfNewList = append(cellRelationConfNewList, v)
+			}
+		}
+	}
+
+	cellRelationConfByte, err := json.Marshal(cellRelationConfNewList)
+	if err != nil {
+		err = fmt.Errorf("cellRelationConf json转结构体失败,Err:%s", err.Error())
+		return
+	}
+	cellRelationConf = string(cellRelationConfByte)
+
+	newData, tmpErr, tmpErrMsg := GetMixedTableCellData(mixedTableReq, lang)
+	if tmpErr != nil {
+		err = errors.New(tmpErrMsg + "获取最新的数据失败 ,Err:" + tmpErr.Error())
+		return
+	}
+	for r, row := range newData {
+		for c, cell := range row {
+			// todo 系统日期类型,原先是不可编辑,转成静态表之后是否变成可编辑
+			cell.EdbInfoId = 0
+			if cell.DataType != request.FormulateCalculateDataDT { //除了公式计算,其他类型都转成自定义类型
+				cell.DataType = request.CustomTextDT //除了计算公式的关联关系被保留,其余绑定关系都删除
+				if _, ok := cellRelationConfMap[cell.Uid]; ok {
+					delete(cellRelationConfMap, cell.Uid)
+				}
+			}
+			if cell.DataTime != "" {
+				cell.DataTimeType = request.CustomDateT
+			}
+			cell.Value = cell.ShowValue
+			row[c] = cell
+		}
+		newData[r] = row
+	}
+	var newMixedTableReq request.MixedTableReq
+	newMixedTableReq.CellRelation = cellRelationConf
+	newMixedTableReq.Data = newData
+	newMixedTableByte, err := json.Marshal(newMixedTableReq)
+	if err != nil {
+		err = fmt.Errorf("newData json转结构体失败,Err:%s", err.Error())
+		return
+	}
+	newContent = string(newMixedTableByte)
+	return
+}
+
+func SyncBalanceEdbData(excelInfo *excelModel.ExcelInfo, balanceTableData [][]request.MixedTableCellDataReq) (err error) {
+	tmpMappingList, e := excelModel.GetExcelChartEdbMappingByExcelInfoId(excelInfo.ExcelInfoId)
+	if e != nil {
+		err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+		return
+	}
+	if len(tmpMappingList) == 0 {
+		return
+	}
+	excelDataMap := make(map[int][]*data_manage.EdbDataList)
+	excelEdbMap := make(map[int]*excelModel.ExcelChartEdb)
+	excelAllRows := len(balanceTableData)
+	excelAllCols := 0
+	newExcelDataMap := make(map[int]map[int]request.MixedTableCellDataReq)
+	for r, row := range balanceTableData {
+		tmp := len(row)
+		if tmp > excelAllCols {
+			excelAllCols = tmp
+		}
+		colMap := make(map[int]request.MixedTableCellDataReq)
+		for c, col := range row {
+			colMap[c] = col
+		}
+		newExcelDataMap[r] = colMap
+	}
+
+	for _, mapping := range tmpMappingList {
+		excelEdbMap[mapping.ExcelChartEdbId] = mapping
+		err, _ = GetBalanceExcelEdbData(mapping, newExcelDataMap, excelDataMap, excelAllRows, excelAllCols)
+		if err != nil {
+			err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+			return
+		}
+	}
+	// 批量更新图表数据
+	err = excelModel.BatchUpdateChartEdbData(excelInfo.ExcelInfoId, excelEdbMap, excelDataMap)
+	if err != nil {
+		err = fmt.Errorf(" 批量更新图表数据失败 Err:%s", err.Error())
+		return
+	}
+	return
+}
+
+// GetBalanceExcelInfoOpButton 获取ETA平衡表格的操作权限
+func GetBalanceExcelInfoOpButton(sysUserId, parentSysUserId int, haveOperaAuth bool, parentExcelInfoId int) (button excelModel.ExcelInfoDetailButton) {
+	// 如果没有数据权限,那么直接返回
+	if !haveOperaAuth {
+		return
+	}
+	//非管理员角色查看其他用户创建的表格,可刷新、另存为、下载表格;
+	button.RefreshButton = true
+	button.CopyButton = true
+	button.DownloadButton = true
+
+	if sysUserId == parentSysUserId {
+		button.OpButton = true
+		button.RefreshEdbButton = true
+		button.OpWorkerButton = true
+		button.DeleteButton = true
+	} else {
+		obj := new(excelModel.ExcelWorker)
+		workerList, err := obj.GetByExcelInfoId(parentExcelInfoId)
+		if err == nil {
+			for _, v := range workerList {
+				if v.SysUserId == sysUserId {
+					button.OpButton = true
+					button.RefreshEdbButton = true
+					button.DeleteButton = true
+					break
+				}
+			}
+		}
+	}
+	return
+}

+ 50 - 22
services/data/excel/excel_info.go

@@ -51,31 +51,56 @@ func GetExcelDetailInfoByUnicode(unicode string, sysUserId int, lang string) (ex
 }
 
 func formatExcelInfo2Detail(excelInfo *excel.ExcelInfo, sysUserId int, lang string) (excelDetail response.ExcelInfoDetail, errMsg string, err error) {
+	checkExcelInfo := excelInfo
+	if excelInfo.Source == utils.BALANCE_TABLE {
+		checkExcelInfoId := excelInfo.ExcelInfoId
+		if excelInfo.BalanceType == 1 {
+			checkExcelInfoId = excelInfo.RelExcelInfoId
+		} else {
+			if excelInfo.ParentId > 0 {
+				checkExcelInfoId = excelInfo.ParentId
+			}
+		}
+		if checkExcelInfoId != excelInfo.ExcelInfoId {
+			checkExcelInfo, err = excel.GetExcelInfoById(checkExcelInfoId)
+			if err != nil {
+				errMsg = "获取平衡表格信息失败"
+				err = errors.New("获取平衡表格信息失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
 	// 数据权限
-	haveOperaAuth, err := data_manage_permission.CheckExcelPermissionByExcelInfoId(excelInfo.ExcelInfoId, excelInfo.ExcelClassifyId, excelInfo.IsJoinPermission, sysUserId)
+	haveOperaAuth, err := data_manage_permission.CheckExcelPermissionByExcelInfoId(checkExcelInfo.ExcelInfoId, checkExcelInfo.ExcelClassifyId, checkExcelInfo.IsJoinPermission, sysUserId)
 	if err != nil {
 		err = errors.New("获取表格权限信息失败,Err" + err.Error())
 		return
 	}
 
 	excelDetail = response.ExcelInfoDetail{
-		ExcelInfoId:     excelInfo.ExcelInfoId,
-		Source:          excelInfo.Source,
-		ExcelType:       excelInfo.ExcelType,
-		ExcelName:       excelInfo.ExcelName,
-		UniqueCode:      excelInfo.UniqueCode,
-		ExcelClassifyId: excelInfo.ExcelClassifyId,
-		SysUserId:       excelInfo.SysUserId,
-		SysUserRealName: excelInfo.SysUserRealName,
-		Content:         excelInfo.Content,
-		ExcelImage:      excelInfo.ExcelImage,
-		FileUrl:         excelInfo.FileUrl,
-		Sort:            excelInfo.Sort,
-		IsDelete:        excelInfo.IsDelete,
-		ModifyTime:      excelInfo.ModifyTime,
-		CreateTime:      excelInfo.CreateTime,
-		TableData:       nil,
-		HaveOperaAuth:   haveOperaAuth,
+		ExcelInfoId:        excelInfo.ExcelInfoId,
+		Source:             excelInfo.Source,
+		ExcelType:          excelInfo.ExcelType,
+		ExcelName:          excelInfo.ExcelName,
+		UniqueCode:         excelInfo.UniqueCode,
+		ExcelClassifyId:    excelInfo.ExcelClassifyId,
+		SysUserId:          excelInfo.SysUserId,
+		SysUserRealName:    excelInfo.SysUserRealName,
+		Content:            excelInfo.Content,
+		ExcelImage:         excelInfo.ExcelImage,
+		FileUrl:            excelInfo.FileUrl,
+		Sort:               excelInfo.Sort,
+		IsDelete:           excelInfo.IsDelete,
+		ModifyTime:         excelInfo.ModifyTime,
+		CreateTime:         excelInfo.CreateTime,
+		TableData:          nil,
+		HaveOperaAuth:      haveOperaAuth,
+		ParentId:           excelInfo.ParentId,
+		BalanceType:        excelInfo.BalanceType,
+		UpdateUserId:       excelInfo.UpdateUserId,
+		UpdateUserRealName: excelInfo.UpdateUserRealName,
+		RelExcelInfoId:     excelInfo.RelExcelInfoId,
 	}
 
 	// 无权限,不需要返回数据
@@ -130,7 +155,7 @@ func formatExcelInfo2Detail(excelInfo *excel.ExcelInfo, sysUserId int, lang stri
 		}
 
 		excelDetail.TableData = result
-	case utils.MIXED_TABLE: // 混合表格
+	case utils.MIXED_TABLE, utils.BALANCE_TABLE: // 混合表格 平衡表
 		var result request.MixedTableReq
 		err = json.Unmarshal([]byte(excelDetail.Content), &result)
 		if err != nil {
@@ -149,11 +174,14 @@ func formatExcelInfo2Detail(excelInfo *excel.ExcelInfo, sysUserId int, lang stri
 		result.Data = newData
 		excelDetail.TableData = result
 	}
+	if excelDetail.Source == utils.BALANCE_TABLE {
+		excelDetail.Button = GetBalanceExcelInfoOpButton(sysUserId, checkExcelInfo.SysUserId, excelDetail.HaveOperaAuth, checkExcelInfo.ExcelInfoId)
+	}
 	return
 }
 
 // GetExcelInfoOpButton 获取ETA表格的操作权限
-func GetExcelInfoOpButton(sysUser *system.Admin, belongUserId, source int, haveOperaAuth bool) (button response.ExcelInfoDetailButton) {
+func GetExcelInfoOpButton(sysUser *system.Admin, belongUserId, source int, haveOperaAuth bool) (button excel.ExcelInfoDetailButton) {
 	// 如果没有数据权限,那么直接返回
 	if !haveOperaAuth {
 		return
@@ -492,10 +520,10 @@ func GetEdbIdsFromExcelCodes(excelCodes []string, sysUserId int, lang string) (e
 }
 
 // GetExcelEdbBatchRefreshKey 获取批量刷新表格指标缓存key
-func GetExcelEdbBatchRefreshKey(source string, reportId, chapterId int) string {
+func GetExcelEdbBatchRefreshKey(source string, primaryId, subId int) string {
 	if source == `` {
 		return ``
 	}
 
-	return fmt.Sprint("batch_refresh_excel_edb:", source, ":", reportId, ":", chapterId)
+	return fmt.Sprint("batch_refresh_excel_edb:", source, ":", primaryId, ":", subId)
 }

+ 50 - 10
services/data/excel/excel_op.go

@@ -53,6 +53,53 @@ func Delete(excelInfo *excelModel.ExcelInfo, sysUser *system.Admin) (err error,
 			isSendEmail = false
 			return
 		}
+	} else if excelInfo.Source == utils.BALANCE_TABLE {
+		// 如果父级删除是否删除子表和静态表,同时删除图表和图表数据
+		excelIds := make([]int, 0)
+		//查询动态表所有的子表,并复制为静态表
+		condition := " AND source=?"
+		var pars []interface{}
+		pars = append(pars, utils.BALANCE_TABLE)
+		excelIds = append(excelIds, excelInfo.ExcelInfoId)
+		if excelInfo.ParentId == 0 {
+			if excelInfo.BalanceType == 0 {
+				condition += "  AND (parent_id = ? or rel_excel_info_id=?)"
+				pars = append(pars, excelInfo.ExcelInfoId, excelInfo.ExcelInfoId)
+			} else if excelInfo.BalanceType == 1 {
+				condition += " AND (parent_id = ?)"
+				pars = append(pars, excelInfo.ExcelInfoId)
+			}
+			excelList, e := excelModel.GetNoContentExcelInfoListByConditionNoPage(condition, pars)
+			if e != nil {
+				err = fmt.Errorf("获取子表失败 %s", err.Error())
+				errMsg = "获取子表失败"
+				return
+			}
+			for _, v := range excelList {
+				excelIds = append(excelIds, v.ExcelInfoId)
+			}
+		}
+
+		// 相关联指标
+		mappingListTmp, e := excelModel.GetExcelChartEdbMappingByExcelInfoIds(excelIds)
+		if e != nil {
+			errMsg = "获取失败"
+			err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", e.Error())
+			return
+		}
+
+		charInfoIds := make([]int, 0)
+		for _, v := range mappingListTmp {
+			charInfoIds = append(charInfoIds, v.ChartInfoId)
+		}
+		err = excelModel.DeleteExcelChartEdbAndData(excelIds, charInfoIds)
+		if err != nil {
+			errMsg = "删除表格失败"
+			err = fmt.Errorf("删除图表,指标信息失败 Err:%s", err.Error())
+			return
+		}
+
+		return
 	}
 
 	// 标记删除
@@ -64,7 +111,7 @@ func Delete(excelInfo *excelModel.ExcelInfo, sysUser *system.Admin) (err error,
 }
 
 // Copy 复制excel
-func Copy(oldExcelInfoId, excelClassifyId int, excelName string, sysUser *system.Admin) (excelInfo *excelModel.ExcelInfo, err error, errMsg string, isSendEmail bool) {
+func Copy(oldExcelInfo *excelModel.ExcelInfo, excelClassifyId int, excelName string, sysUser *system.Admin) (excelInfo *excelModel.ExcelInfo, err error, errMsg string, isSendEmail bool) {
 	isSendEmail = true
 
 	excelName = utils.TrimLRStr(excelName)
@@ -87,13 +134,6 @@ func Copy(oldExcelInfoId, excelClassifyId int, excelName string, sysUser *system
 		return
 	}
 
-	// 获取原ETA表格信息
-	oldExcelInfo, err := excelModel.GetExcelInfoById(oldExcelInfoId)
-	if err != nil {
-		errMsg = "获取ETA表格失败"
-		return
-	}
-
 	// 操作权限校验
 	{
 		// 数据权限
@@ -172,8 +212,8 @@ func Copy(oldExcelInfoId, excelClassifyId int, excelName string, sysUser *system
 			v.ExcelInfoId = 0
 			list[k] = v
 		}
-
-		err = excelModel.AddExcelInfo(excelInfo, list)
+		var childExcel *excelModel.ExcelInfo
+		err = excelModel.AddExcelInfo(excelInfo, list, childExcel)
 		if err != nil {
 			errMsg = "保存失败"
 		}

+ 254 - 0
services/edb_info_replace.go

@@ -0,0 +1,254 @@
+package services
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_api/models/data_manage"
+	excelModel "eta/eta_api/models/data_manage/excel"
+	"eta/eta_api/models/data_manage/excel/request"
+	"eta/eta_api/services/alarm_msg"
+	"eta/eta_api/services/sandbox"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+// 全局的指标替换
+func DealReplaceEdbCache() {
+	var err error
+	for {
+		utils.Rc.Brpop(utils.CACHE_KEY_REPLACE_EDB, func(b []byte) {
+			defer func() {
+				if err != nil {
+					utils.FileLog.Info("DealReplaceEdbCache err:" + err.Error())
+					go alarm_msg.SendAlarmMsg("替换表格中的指标失败提醒,errmsg:"+err.Error(), 3)
+				}
+			}()
+			record := new(data_manage.ReplaceEdbInfoItem)
+			if err = json.Unmarshal(b, &record); err != nil {
+				fmt.Println("json unmarshal wrong!")
+				return
+			}
+			oldEdbInfo := record.OldEdbInfo
+			newEdbInfo := record.NewEdbInfo
+			deleteCache := true
+			setNxKey := fmt.Sprintf("EDB_INFO_REPLACE:%d-%d", oldEdbInfo.EdbInfoId, newEdbInfo.EdbInfoId)
+			defer func() {
+				if deleteCache {
+					utils.Rc.Delete(setNxKey)
+				}
+			}()
+			if !utils.Rc.SetNX(setNxKey, 1, 30*time.Minute) {
+				deleteCache = false
+				err = fmt.Errorf("替换表格中的指标失败旧指标:%d为新指标%d:正在处理中", oldEdbInfo.EdbInfoId, newEdbInfo.EdbInfoId)
+				return
+			}
+
+			// 替换相关性图表配置
+			_, err = data_manage.ReplaceMultipleGraphConfigChartEdb(oldEdbInfo, newEdbInfo)
+			if err != nil {
+				err = fmt.Errorf("替换相关性图表配置失败,errmsg:%s", err.Error())
+				return
+			}
+			// 替换拟合方程指标
+			_, err = data_manage.ReplaceEdbInfoInLineEquationMultipleGraphConfig(oldEdbInfo, newEdbInfo)
+			if err != nil {
+				err = fmt.Errorf("替换拟合方程指标失败,errmsg:%s", err.Error())
+				return
+			}
+
+			// 替换表格中的指标
+			err = ReplaceEdbInExcel(oldEdbInfo, newEdbInfo)
+			if err != nil {
+				err = fmt.Errorf("替换表格中的指标失败,errmsg:%s", err.Error())
+				return
+			}
+
+			//替换逻辑图中的指标
+			err = sandbox.ReplaceEdbInSandbox(oldEdbInfo.EdbInfoId, newEdbInfo.EdbInfoId)
+			if err != nil {
+				err = fmt.Errorf("替换逻辑图中的指标失败,errmsg:%s", err.Error())
+				return
+			}
+		})
+	}
+}
+
+// ReplaceEdbInExcel 替换表格中的指标
+func ReplaceEdbInExcel(oldEdbInfo, newEdbInfo *data_manage.EdbInfo) (err error) {
+	defer func() {
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("替换表格中的指标失败提醒,errmsg:"+err.Error(), 3)
+		}
+	}()
+	//查询和指标相关的时间序列表格和混合表格
+	mappingList, err := excelModel.GetExcelEdbMappingByEdbInfoIdAndSource(oldEdbInfo.EdbInfoId, []int{utils.TIME_TABLE, utils.MIXED_TABLE})
+	if err != nil {
+		err = fmt.Errorf("查询和指标相关的表格失败,错误:%s", err.Error())
+		return
+	}
+	updateList := make([]*excelModel.ExcelInfo, 0)
+	// 循环列表,根据表格类型单独处理
+	for _, excelMapping := range mappingList {
+		//查询和指标相关的混合表格
+		excelInfo, tmpErr := excelModel.GetExcelInfoById(excelMapping.ExcelInfoId)
+		if tmpErr != nil {
+			err = fmt.Errorf("查询和指标相关的混合表格失败,错误:%s", tmpErr.Error())
+			return
+		}
+		// 清除缓存
+		key := utils.HZ_CHART_LIB_EXCEL_TABLE_DETAIL + ":" + excelInfo.UniqueCode
+		if utils.Re == nil {
+			_ = utils.Rc.Delete(key)
+		}
+		// 根据表格类型,调用不同的处理函数
+		switch excelMapping.Source {
+		case utils.TIME_TABLE: // 时间序列表格
+			// 替换余额表格中的指标
+			newExcelInfo, e := replaceEdbInTimeExcel(oldEdbInfo, newEdbInfo, excelInfo)
+			if e != nil {
+				err = fmt.Errorf("替换余额表格中的指标失败,错误:%s", e.Error())
+				return
+			}
+			updateList = append(updateList, newExcelInfo)
+		case utils.MIXED_TABLE, utils.BALANCE_TABLE:
+			// 替换余额表格中的指标
+			newExcelInfo, e := replaceEdbInBalanceExcel(oldEdbInfo, newEdbInfo, excelInfo)
+			if e != nil {
+				err = fmt.Errorf("替换余额表格中的指标失败,错误:%s", e.Error())
+				return
+			}
+			updateList = append(updateList, newExcelInfo)
+		default:
+			// 其他表格类型的处理逻辑
+		}
+	}
+
+	err = excelModel.ReplaceEdbInExcel(oldEdbInfo.EdbInfoId, newEdbInfo.EdbInfoId, updateList)
+	if err != nil {
+		err = fmt.Errorf("替换表格中的指标失败,错误:%s", err.Error())
+		return
+	}
+
+	//todo 是否需要刷新表格中的指标数据
+	return
+}
+
+func replaceEdbInBalanceExcel(oldEdbInfo, newEdbInfo *data_manage.EdbInfo, excelInfo *excelModel.ExcelInfo) (newExcelInfo *excelModel.ExcelInfo, err error) {
+	newExcelInfo = excelInfo
+	var mixedTableReq request.MixedTableReq
+	err = json.Unmarshal([]byte(excelInfo.Content), &mixedTableReq)
+	if err != nil {
+		err = fmt.Errorf("表格json转结构体失败,Err:" + err.Error())
+		return
+	}
+	// 处理data
+	configList := mixedTableReq.Data
+	for ck, rowList := range configList {
+		for rk, cell := range rowList {
+			switch cell.DataType {
+			case request.EdbDT: // 指标信息
+				if cell.EdbInfoId == oldEdbInfo.EdbInfoId {
+					//更换成新指标ID
+					configList[ck][rk].EdbInfoId = newEdbInfo.EdbInfoId
+				}
+			case request.InsertDataDT, request.PopInsertDataDT: // 插值、弹框插值
+				if cell.EdbInfoId == oldEdbInfo.EdbInfoId {
+					//更换成新指标ID
+					configList[ck][rk].EdbInfoId = newEdbInfo.EdbInfoId
+				}
+			case request.InsertEdbCalculateDataDT: // 插入指标计算公式生成的值
+				var config request.CalculateConf
+				err = json.Unmarshal([]byte(cell.Value), &config)
+				if err != nil {
+					return
+				}
+				if cell.EdbInfoId == oldEdbInfo.EdbInfoId {
+					//更换成新指标ID
+					configList[ck][rk].EdbInfoId = newEdbInfo.EdbInfoId
+				}
+				if config.EdbInfoId == oldEdbInfo.EdbInfoId {
+					config.EdbInfoId = newEdbInfo.EdbInfoId
+					var configStr []byte
+					configStr, err = json.Marshal(config)
+					if err != nil {
+						return
+					}
+					configList[ck][rk].Value = string(configStr)
+				}
+
+			case request.DateDT: // 日期类型
+				// 指标日期类型的单元格需要额外将指标id取出来
+				if cell.DataTimeType == request.EdbDateDT {
+					var config request.EdbDateConf
+					err = json.Unmarshal([]byte(cell.Value), &config)
+					if err != nil {
+						return
+					}
+					if config.EdbInfoId == oldEdbInfo.EdbInfoId {
+						config.EdbInfoId = newEdbInfo.EdbInfoId
+						var configStr []byte
+						configStr, err = json.Marshal(config)
+						if err != nil {
+							return
+						}
+						configList[ck][rk].Value = string(configStr)
+					}
+				}
+			}
+		}
+	}
+
+	mixedTableReq.Data = configList
+	var newContentByte []byte
+	newContentByte, err = json.Marshal(mixedTableReq)
+	if err != nil {
+		return
+	}
+	// 生成的新内容替换原先的旧内容
+	excelInfo.Content = string(newContentByte)
+	newExcelInfo = excelInfo
+	return
+}
+
+func replaceEdbInTimeExcel(oldEdbInfo, newEdbInfo *data_manage.EdbInfo, excelInfo *excelModel.ExcelInfo) (newExcelInfo *excelModel.ExcelInfo, err error) {
+	newExcelInfo = excelInfo
+
+	var tableDataConfig request.TimeTableDataConfig
+	err = json.Unmarshal([]byte(excelInfo.Content), &tableDataConfig)
+	if err != nil {
+		err = errors.New("表格json转结构体失败,Err:" + err.Error())
+		return
+	}
+	if len(tableDataConfig.EdbInfoIdList) <= 0 {
+		return
+	}
+	// 实际期数没有的情况下,直接返回吧
+	if tableDataConfig.Num <= 0 {
+		return
+	}
+
+	// 先处理edbInfoList
+	for k, id := range tableDataConfig.EdbInfoIdList {
+		if id == oldEdbInfo.EdbInfoId {
+			tableDataConfig.EdbInfoIdList[k] = newEdbInfo.EdbInfoId
+		}
+	}
+
+	// 先处理tableEdbInfoList
+	for k, tableEdbInfo := range tableDataConfig.TableEdbInfoList {
+		if tableEdbInfo.EdbInfoId == oldEdbInfo.EdbInfoId {
+			tableDataConfig.TableEdbInfoList[k].EdbInfoId = newEdbInfo.EdbInfoId
+		}
+	}
+
+	var newContentByte []byte
+	newContentByte, err = json.Marshal(tableDataConfig)
+	if err != nil {
+		return
+	}
+	// 生成的新内容替换原先的旧内容
+	excelInfo.Content = string(newContentByte)
+	newExcelInfo = excelInfo
+	return
+}

+ 18 - 1
services/ppt/ppt_group.go

@@ -5,6 +5,7 @@ import (
 	"errors"
 	"eta/eta_api/models"
 	"eta/eta_api/models/system"
+	"eta/eta_api/services"
 	"eta/eta_api/utils"
 	"fmt"
 	"sort"
@@ -591,6 +592,15 @@ func GetGroupPptList(groupId int64, adminId int) (ret models.RespGroupPptList, e
 	}
 
 	for _, v := range pptList {
+		if v.PptPage == 0 && v.Content != "" {
+			var pptContent []services.PPTContent
+			err = json.Unmarshal([]byte(v.Content), &pptContent)
+			if err != nil {
+				err = errors.New("解析ppt内容出错" + err.Error())
+				return
+			}
+			v.PptPage = len(pptContent)
+		}
 		pptMap[v.PptId] = v
 	}
 
@@ -1574,8 +1584,15 @@ func GetMyPptList(adminId int, keyword string) (ret models.RespGroupPptList, err
 	if len(pptList) <= 0 {
 		return
 	}
-
+	var pptContent []services.PPTContent
 	for _, v := range pptList {
+		if v.PptPage == 0 && v.Content != "" {
+			err = json.Unmarshal([]byte(v.Content), &pptContent)
+			if err != nil {
+				return
+			}
+			v.PptPage = len(pptContent)
+		}
 		tmpV := &models.RespGroupPptListItem{
 			GroupPptId:    int64(v.PptId),
 			PptId:         int64(v.PptId),

+ 65 - 0
services/sandbox/sandbox.go

@@ -7,9 +7,11 @@ import (
 	"eta/eta_api/models/sandbox"
 	"eta/eta_api/models/sandbox/request"
 	"eta/eta_api/models/system"
+	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
 	"fmt"
 	"strconv"
+	"strings"
 	"time"
 )
 
@@ -852,3 +854,66 @@ func sandboxClassifyHaveChildV2(allNode []*sandbox.SandboxClassifyItems, node *s
 	}
 	return
 }
+
+func ReplaceEdbInSandbox(oldEdbInfoId, newEdbInfoId int) (err error) {
+	updateTotal := 0
+	logMsg := ""
+	//分页处理沙盘表
+	defer func() {
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("替换沙盘中的指标记录失败提醒,errmsg:"+err.Error(), 3)
+		}
+		if logMsg != "" {
+			utils.FileLog.Info(fmt.Sprintf("替换ETA逻辑的指标记录,替换总数:%d,旧的指标id:%d,新的指标id:%d;%s", updateTotal, oldEdbInfoId, newEdbInfoId, logMsg))
+		}
+	}()
+	//查询沙盘总数
+	total, err := sandbox.GetSandboxListCountByCondition("", []interface{}{})
+	if err != nil {
+		err = fmt.Errorf("查询沙盘总数失败 Err:%s", err)
+		return
+	}
+
+	// 根据沙盘列表总数,分页查询
+	// 计算总页数
+	totalPage := (total + 99) / 100 // 使用整数除法,并添加一页以防有余数
+	updateSandBox := make([]sandbox.Sandbox, 0)
+
+	//查询沙盘列表
+	for i := 0; i < totalPage; i += 1 {
+		startSize := i * 100
+		list, e := sandbox.GetSandboxListByCondition("", []interface{}{}, startSize, 100)
+		if e != nil {
+			err = fmt.Errorf("查询沙盘列表失败 Err:%s", e)
+			return
+		}
+		for _, v := range list {
+			sandOldEdbId := fmt.Sprintf(`"RId":"1-%d","Id":%d,`, oldEdbInfoId, oldEdbInfoId)
+			if strings.Contains(v.Content, sandOldEdbId) {
+				sandNewEdbId := fmt.Sprintf(`"RId":"1-%d","Id":%d,`, newEdbInfoId, newEdbInfoId)
+				v.Sandbox.Content = strings.ReplaceAll(v.Content, sandOldEdbId, sandNewEdbId)
+				updateSandBox = append(updateSandBox, v.Sandbox)
+				logMsg += `涉及到的逻辑id:` + strconv.Itoa(v.Sandbox.SandboxId) + ";"
+				if len(updateSandBox) > 100 {
+					err = sandbox.UpdateSandboxContent(updateSandBox)
+					if err != nil {
+						err = fmt.Errorf("更新沙盘表失败 Err:%s", err)
+						return
+					}
+					updateTotal += len(updateSandBox)
+					updateSandBox = make([]sandbox.Sandbox, 0)
+				}
+			}
+		}
+	}
+	if len(updateSandBox) > 0 {
+		err = sandbox.UpdateSandboxContent(updateSandBox)
+		if err != nil {
+			err = fmt.Errorf("更新沙盘表失败 Err:%s", err)
+			return
+		}
+		updateTotal += len(updateSandBox)
+	}
+
+	return
+}

+ 5 - 2
services/task.go

@@ -40,8 +40,11 @@ func Task() {
 
 	go AutoInsertAdminOperateRecordToDB()
 
-	// 测试用-生成bloomberg数据
-	//go InsertBloombergIndex()
+	// 指标刷新
+	go data.HandleEdbRefreshQueue()
+
+	// 进行指标替换操作
+	go DealReplaceEdbCache()
 
 	// TODO:修复权限
 	//FixEnCompanyPermission()

+ 8 - 1
static/ErrMsgConfig.json

@@ -50,5 +50,12 @@
   "系统处理中,请稍后重试!": "The system is processing, please try again later!",
   "固定日期不可为空": "The fixed date cannot be empty",
   "日期数量已达上限!": "The number of dates has reached the limit!",
-  "该品种已在指标库生成指标,不可删除": "This variety has already generated indicators in the index library and cannot be deleted"
+  "该品种已在指标库生成指标,不可删除": "This variety has already generated indicators in the index library and cannot be deleted",
+  "当前指标已用作画图,不可删除": "The current metric is in use for charting and cannot be deleted",
+  "当前指标已被表格引用,不可删除":"The current metric is referenced by a table and cannot be deleted",
+  "当前指标已用作指标运算,不可删除": "The current metric is used in metric calculations and cannot be deleted.",
+  "当前指标已用作预测指标,不可删除": "The current metric is used as a forecasting indicator and cannot be deleted.",
+  "当前指标已添加到跨品种分析,不可删除": "The current metric has been added to cross-commodity analysis and cannot be deleted.",
+  "指标已删除,请刷新页面": "Metric deleted, please refresh the page.",
+  "删除失败": "Deletion failed."
 }

+ 4 - 0
utils/constants.go

@@ -232,6 +232,8 @@ const (
 
 	CACHE_CREATE_REPORT_IMGPDF_QUEUE = "eta_report:report_img_pdf_queue" // 生成报告长图PDF队列
 	CACHE_EDB_TERMINAL_CODE_URL      = "edb:terminal_code:edb_code:"     // 指标与终端关系的缓存
+
+	CACHE_KEY_REPLACE_EDB = "eta:replace_edb" //系统用户操作日志队列
 )
 
 // 模板消息推送类型
@@ -278,6 +280,7 @@ const (
 	CHART_SOURCE_LINE_FEATURE_PERCENTILE         = 8  // 统计特征-百分位图表
 	CHART_SOURCE_LINE_FEATURE_FREQUENCY          = 9  // 统计特征-频率分布图表
 	CHART_SOURCE_CROSS_HEDGING                   = 10 // 跨品种分析图表
+	CHART_SOURCE_BALANCE_EXCEL                   = 11 // 平衡表图表
 )
 
 // 批量配置图表的位置来源
@@ -300,6 +303,7 @@ const (
 	TIME_TABLE            = 2 // 时间序列表格
 	MIXED_TABLE           = 3 // 混合表格
 	CUSTOM_ANALYSIS_TABLE = 4 // 自定义分析表格
+	BALANCE_TABLE         = 5 // 平衡表
 )
 
 // 图表样式类型