Browse Source

Merge branch 'master' into feature/eta1.5.7_edb_replace

# Conflicts:
#	models/data_manage/excel/excel_info.go
xyxie 9 months ago
parent
commit
0638b9bf7a
46 changed files with 6733 additions and 274 deletions
  1. 60 0
      controllers/data_manage/chart_common.go
  2. 23 2
      controllers/data_manage/chart_info.go
  3. 7 7
      controllers/data_manage/data_manage_permission/data_manage_permission.go
  4. 4 4
      controllers/data_manage/data_manage_permission/data_move.go
  5. 47 0
      controllers/data_manage/edb_info.go
  6. 1731 0
      controllers/data_manage/excel/balance_table.go
  7. 40 6
      controllers/data_manage/excel/excel_classify.go
  8. 350 80
      controllers/data_manage/excel/excel_info.go
  9. 2 0
      controllers/data_manage/my_chart.go
  10. 0 1
      controllers/data_source/guagnzhouqihuo.go
  11. 325 0
      controllers/data_source/sci99.go
  12. 35 33
      models/data_manage/chart_info.go
  13. 15 0
      models/data_manage/chart_theme/chart_theme_type.go
  14. 2 0
      models/data_manage/edb_data_base.go
  15. 39 0
      models/data_manage/edb_data_sci99.go
  16. 8 8
      models/data_manage/edb_info.go
  17. 126 0
      models/data_manage/excel/excel_chart_data.go
  18. 455 0
      models/data_manage/excel/excel_chart_edb.go
  19. 149 40
      models/data_manage/excel/excel_info.go
  20. 82 0
      models/data_manage/excel/excel_worker.go
  21. 77 0
      models/data_manage/excel/request/balance_table.go
  22. 13 1
      models/data_manage/excel/request/excel_info.go
  23. 44 30
      models/data_manage/excel/response/excel_info.go
  24. 17 17
      models/data_manage/excel/response/sheet.go
  25. 11 0
      models/data_manage/my_chart.go
  26. 135 0
      models/data_source/base_from_sci99.go
  27. 3 0
      models/db.go
  28. 9 0
      models/english_report.go
  29. 58 0
      models/mgo/common.go
  30. 8 0
      models/report.go
  31. 9 0
      models/smart_report/smart_report.go
  32. 144 0
      routers/commentsRouter.go
  33. 4 0
      services/data/base_edb_lib.go
  34. 111 0
      services/data/chart_info.go
  35. 1756 0
      services/data/chart_info_excel_balance.go
  36. 46 0
      services/data/chart_info_interface.go
  37. 4 0
      services/data/data_manage_permission/data_move.go
  38. 7 1
      services/data/data_manage_permission/edb_permission.go
  39. 27 0
      services/data/data_manage_permission/excel.go
  40. 7 7
      services/data/edb_data.go
  41. 583 0
      services/data/excel/balance_table.go
  42. 48 20
      services/data/excel/excel_info.go
  43. 50 10
      services/data/excel/excel_op.go
  44. 37 7
      services/smart_report.go
  45. 22 0
      utils/common.go
  46. 3 0
      utils/constants.go

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

+ 23 - 2
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 != `` {

+ 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

+ 47 - 0
controllers/data_manage/edb_info.go

@@ -1756,6 +1756,53 @@ func (this *EdbInfoController) EdbInfoSearch() {
 			searchItem.Frequency = indexInfo.Frequency
 			searchItem.Unit = indexInfo.Unit
 			searchItem.EdbName = indexInfo.IndexName
+		} else if source == utils.DATA_SOURCE_SCI99 { //卓创资讯
+			dataItems, err := data_manage.GetEdbDataAllByEdbCode(edbCode, source, subSource, utils.EDB_DATA_LIMIT)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取上期所已存在信息失败,Err:" + err.Error()
+				return
+			}
+
+			if len(dataItems) > 0 {
+				searchItem.EdbCode = edbCode
+				minDate, maxDate, err := data_manage.GetEdbDataSci99MaxOrMinDate(edbCode)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取上期所日期信息失败,Err:" + err.Error()
+					return
+				}
+				searchItem.DataList = dataItems
+				searchItem.StartDate = minDate
+				searchItem.EndDate = maxDate
+			} else {
+				respItem, err := data.AddEdbData(source, edbCode, frequency)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取失败,Err:" + err.Error()
+					return
+				}
+				if respItem.Ret != 200 {
+					br.Msg = "未搜索到该指标"
+					br.ErrMsg = respItem.ErrMsg + ";EdbCode:" + edbCode
+					return
+				}
+				isAdd = true
+			}
+
+			//获取指标信息
+			indexInfo, err := data_manage.GetBaseInfoFromSci99ByIndexCode(edbCode)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取上期所指标详情失败,Err:" + err.Error()
+				return
+			}
+
+			if indexInfo != nil {
+				searchItem.Frequency = "日度"
+				searchItem.Unit = "元/吨"
+				searchItem.EdbName = indexInfo.IndexName
+			}
 		} else {
 			// 代码中没有的来源那么从edb_source中找是否有对应的
 			sourceItem := data_manage.EdbSourceIdMap[source]

+ 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 = "删除失败"

+ 350 - 80
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 != `` {

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

+ 0 - 1
controllers/data_source/guagnzhouqihuo.go

@@ -13,7 +13,6 @@ import (
 	"time"
 )
 
-// 广州期货交易所
 type DataSourceController struct {
 	controllers.BaseAuthController
 }

+ 325 - 0
controllers/data_source/sci99.go

@@ -0,0 +1,325 @@
+package data_source
+
+import (
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_source"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+// 卓创资讯
+
+// ComTradeCountryList
+// @Title 获取居民消费价格指数分类
+// @Description 获取居民消费价格指数分类
+// @Success 200 {object} []data_manage.ComTradeCountryItem
+// @router /sci99/classify/list [get]
+func (this *DataSourceController) Sci99ClassifyList() {
+	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
+	}
+
+	classifyList, err := data_source.GetBaseFromSci99Classify()
+	if err != nil {
+		br.Msg = "获取分类失败"
+		br.ErrMsg = "获取分类失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = classifyList
+}
+
+// @Title 获取消费者价格指数数据
+// @Description 获取消费者价格指数数据接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   BaseFromSci99ClassifyId   query   int  true       "分类id"
+// @Param   KeyWord   query   string  true       "关键词"
+// @Success 200 {object} data_source.BaseFromSci99IndexView
+// @router /sci99/index/data [get]
+func (this *DataSourceController) Sci99Data() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	baseFromSci99ClassifyId, _ := this.GetInt("ClassifyId")
+	if baseFromSci99ClassifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+
+	keyword := this.GetString("KeyWord")
+
+	//获取指标
+	var condition string
+	var pars []interface{}
+
+	if baseFromSci99ClassifyId > 0 {
+		condition += ` AND classify_id=? `
+		pars = append(pars, baseFromSci99ClassifyId)
+	}
+
+	if keyword != "" {
+		condition += ` AND (index_code =? OR index_name LIKE ?)  `
+		pars = append(pars, keyword)
+		pars = append(pars, "%"+keyword+"%")
+	}
+
+
+	sci99List, err := data_source.GetSci99Index(condition, pars)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	resultList := make([]*data_source.BaseFromSci99IndexList, 0)
+	for _, v := range sci99List {
+		product := new(data_source.BaseFromSci99IndexList)
+		product.BaseFromSciIndexId = v.BaseFromSciIndexId
+		product.ClassifyId = v.ClassifyId
+		product.IndexCode = v.IndexCode
+		product.IndexName = v.IndexName
+		product.Frequency = v.Frequency
+		product.Unit = v.Unit
+		product.ModifyTime = v.ModifyTime
+
+		total, err := data_source.GetSci99IndexDataCount(v.IndexCode)
+		page := paging.GetPaging(currentIndex, pageSize, total)
+		dataList, err := data_source.GetSci99IndexData(v.IndexCode, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+		if dataList == nil {
+			dataList = make([]*data_source.BaseFromSci99DataItem, 0)
+		}
+		product.DataList = dataList
+		product.Paging = page
+		resultList = append(resultList, product)
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resultList
+}
+
+// ExportBaiinfoList
+// @Title 导出ICPI数据
+// @Description 导出ICPI数据
+// @Param   BaseFromSci99ClassifyId   query   int  true       "分类id"
+// @Param   KeyWord   query   string  true       "关键词"
+// @Success 200  导出成功
+// @router /sci99/export/sci99DataList [get]
+func (this *DataSourceController) ExportSci99DataList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	baseFromSci99ClassifyId, _ := this.GetInt("ClassifyId")
+	if baseFromSci99ClassifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+
+	keyword := this.GetString("KeyWord")
+
+	//获取指标
+	var condition string
+	var pars []interface{}
+
+	if baseFromSci99ClassifyId >= 0 {
+		condition += ` AND classify_id=? `
+		pars = append(pars, baseFromSci99ClassifyId)
+	}
+
+	if keyword != "" {
+		condition += ` AND (index_code =? OR index_name LIKE ?)  `
+		pars = append(pars, keyword)
+		pars = append(pars, "%"+keyword+"%")
+	}
+
+	sci99List, err := data_source.GetSci99Index(condition, pars)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+
+	downLoadnFilePath := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+
+	if err != nil {
+		fmt.Println("新增Sheet失败", err.Error())
+		return
+	}
+	//sheetNew.SetColWidth()
+	//获取指标数据
+	sheetNew, _ := xlsxFile.AddSheet("卓创资讯")
+	windRow := sheetNew.AddRow()
+	secNameRow := sheetNew.AddRow()
+	indexCodeRow := sheetNew.AddRow()
+	frequencyRow := sheetNew.AddRow()
+	unitRow := sheetNew.AddRow()
+	lastModifyDateRow := sheetNew.AddRow()
+	//获取分类下指标最大数据量
+	dataMax, err := data_source.GetSci99DataMaxCount(baseFromSci99ClassifyId)
+	if err != nil {
+		fmt.Println("获取指标最大数据量失败", err.Error())
+		return
+	}
+	fmt.Println("dataMax:", dataMax)
+	setRowIndex := 6
+	for k, sv := range sci99List {
+		//获取数据
+		dataList, err := data_source.GetSci99IndexDataByCode(sv.IndexCode)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		if len(dataList) > 0 {
+			windRow.AddCell().SetValue("卓创资讯")
+			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()
+			min := k * 3
+			sheetNew.SetColWidth(min, min, 15)
+
+			if len(dataList) <= 0 {
+				for n := 0; n < dataMax; n++ {
+					rowIndex := setRowIndex + n
+					row := sheetNew.Row(rowIndex)
+					row.AddCell()
+					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()
+						row.AddCell()
+					}
+				}
+			}
+		}
+	}
+
+	err = xlsxFile.Save(downLoadnFilePath)
+	if err != nil {
+		//有指标无数据时先导出一遍空表
+		sheet, err := xlsxFile.AddSheet("无数据")
+		if err != nil {
+			br.Msg = "新增Sheet失败"
+			br.ErrMsg = "新增Sheet失败,Err:" + err.Error()
+			return
+		}
+		rowSecName := sheet.AddRow()
+		celSecName := rowSecName.AddCell()
+		celSecName.SetValue("")
+		err = xlsxFile.Save(downLoadnFilePath)
+		if err != nil {
+			br.Msg = "保存文件失败"
+			br.ErrMsg = "保存文件失败"
+			return
+		}
+	}
+	fileName := `卓创资讯`
+	fileName += time.Now().Format(utils.FormatDateUnSpace) + `.xlsx` //文件名称
+	this.Ctx.Output.Download(downLoadnFilePath, fileName)
+	defer func() {
+		os.Remove(downLoadnFilePath)
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "success"
+
+}

+ 35 - 33
models/data_manage/chart_info.go

@@ -459,24 +459,15 @@ func getEdbDataListByMongo(source, subSource, edbInfoId int, startDate, endDate
 		"edb_info_id": edbInfoId,
 	}
 
-	// 数据开始日期
-	if startDate != `` {
-		startDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		queryConditions["data_time"] = bson.M{"$gte": startDateTime}
+	// 数据日期
+	dateCondition, err := mgo.BuildDateCondition(startDate, endDate)
+	if err != nil {
+		return
 	}
-	// 数据结束日期
-	if endDate != "" {
-		endDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		queryConditions["data_time"] = bson.M{"$lte": endDateTime}
+	if len(dateCondition) > 0 {
+		queryConditions["data_time"] = dateCondition
 	}
+
 	// 获取列表数据
 	tmpDataList, tmpErr := mogDataObj.GetAllDataList(queryConditions, []string{"data_time"})
 	if tmpErr != nil {
@@ -591,25 +582,15 @@ func getEdbDataListMinAndMaxByMongo(source, subSource, edbInfoId int, startDate,
 	queryConditions := bson.M{
 		"edb_info_id": edbInfoId,
 	}
-
-	// 数据开始日期
-	if startDate != `` {
-		startDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		queryConditions["data_time"] = bson.M{"$gte": startDateTime}
+	// 日期
+	dateCondition, err := mgo.BuildDateCondition(startDate, endDate)
+	if err != nil {
+		return
 	}
-	// 数据结束日期
-	if endDate != "" {
-		endDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		queryConditions["data_time"] = bson.M{"$lte": endDateTime}
+	if len(dateCondition) > 0 {
+		queryConditions["data_time"] = dateCondition
 	}
+
 	pipeline := []bson.M{
 		{"$match": queryConditions},
 		{"$group": bson.M{
@@ -720,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 {
@@ -1420,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 {
@@ -1453,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 (

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

+ 2 - 0
models/data_manage/edb_data_base.go

@@ -172,6 +172,8 @@ func GetEdbDataTableName(source, subSource int) (tableName string) {
 		tableName = "edb_data_gz"
 	case utils.DATA_SOURCE_ICPI: //ICPI消费价格指数->79
 		tableName = "edb_data_icpi"
+	case utils.DATA_SOURCE_SCI99: //ICPI消费价格指数->85
+		tableName = "edb_data_sci99"
 	default:
 		edbSource := EdbSourceIdMap[source]
 		if edbSource != nil {

+ 39 - 0
models/data_manage/edb_data_sci99.go

@@ -0,0 +1,39 @@
+package data_manage
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type Sci99Data struct {
+	InputValue string `orm:"column(DATA_VALUE)" description:"日期"`
+	DataTime   string `orm:"column(DATA_DATE)" description:"值"`
+}
+
+func GetEdbDataSci99MaxOrMinDate(edbCode string) (minDate, maxDate string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT MIN(data_time) AS minDate,MAX(data_time) AS maxDate FROM edb_data_icpi WHERE edb_code=? `
+	err = o.Raw(sql, edbCode).QueryRow(&minDate, &maxDate)
+	return
+}
+
+type BaseFromSci99IndexItem struct {
+	BaseFromSciIndexId int    `orm:"column(base_from_sci_index_id);pk"` // 主键,自动递增
+	IndexCode          string // 指标编码
+	IndexName          string // 指标名称
+	ClassifyId         int    // 分类Id
+	Unit               string // 单位
+	Frequency          string // 频度
+	Describe           string // 指标描述
+	CreateTime         string // 创建时间
+	ModifyTime         string // 修改时间
+}
+
+// GetBaseInfoFromSci99ByIndexCode 获取指标信息
+func GetBaseInfoFromSci99ByIndexCode(indexCode string) (item *BaseFromSci99IndexItem, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_sci99_index WHERE index_code=? `
+	sql = fmt.Sprintf(sql)
+	err = o.Raw(sql, indexCode).QueryRow(&item)
+	return
+}

+ 8 - 8
models/data_manage/edb_info.go

@@ -1635,14 +1635,14 @@ func getAllDataByMongo(edbInfoId, source, subSource int, startDataTime string) (
 	queryConditions := bson.M{
 		"edb_info_id": edbInfoId,
 	}
-	// 结束日期
-	if startDataTime != "" {
-		startDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, startDataTime, time.Local)
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		queryConditions["data_time"] = bson.M{"$gt": startDateTime}
+
+	// 开始日期
+	dateCondition, err := mgo.BuildDateCondition(startDataTime, "")
+	if err != nil {
+		return
+	}
+	if len(dateCondition) > 0 {
+		queryConditions["data_time"] = dateCondition
 	}
 
 	// 获取列表数据

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

+ 149 - 40
models/data_manage/excel/excel_info.go

@@ -11,22 +11,28 @@ import (
 
 // 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表格基础信息
@@ -37,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
@@ -73,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 {
@@ -106,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()
@@ -152,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")
@@ -206,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
 }
 
@@ -327,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
@@ -338,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 `
@@ -376,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)
 
@@ -386,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
@@ -395,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
@@ -571,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
@@ -685,3 +774,23 @@ func ReplaceEdbInExcel(oldEdbInfoId, newEdbInfoId int, updateExcelList []*ExcelI
 	}
 	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 用英文逗号拼接"`
+}

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

+ 11 - 0
models/data_manage/my_chart.go

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

+ 135 - 0
models/data_source/base_from_sci99.go

@@ -0,0 +1,135 @@
+package data_source
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+// BaseFromSci99Index 代表卓创资讯-原始指标表的结构
+type BaseFromSci99Index struct {
+	BaseFromSciIndexId int       `orm:"column(base_from_sci_index_id);pk"` // 主键,自动递增
+	IndexCode          string    // 指标编码
+	IndexName          string    // 指标名称
+	ClassifyId         int       // 分类Id
+	Unit               string    // 单位
+	Frequency          string    // 频度
+	Describe           string    // 指标描述
+	CreateTime         time.Time // 创建时间
+	ModifyTime         time.Time // 修改时间
+}
+
+// BaseFromSci99Data 代表卓创资讯-原始指标数据表的结构
+type BaseFromSci99Data struct {
+	BaseFromSciDataId  int       `orm:"column(base_from_sci_data_id);pk"` // 主键,自动递增
+	BaseFromSciIndexId int       // 指标id
+	IndexCode          string    // 指标编码
+	DataTime           string    // 数据日期
+	Value              float64   // 数据值
+	CreateTime         time.Time // 创建时间
+	ModifyTime         time.Time // 修改时间
+}
+
+// BaseFromSci99Classify 代表卓创资讯-原始指标分类表的结构
+type BaseFromSci99Classify struct {
+	BaseFromSciClassifyId int       `orm:"column(base_from_sci_classify_id);pk"` // 主键,自动递增
+	ClassifyName          string    // 分类名称
+	Sort                  int       // 排序
+	CreateTime            time.Time // 创建时间
+	ModifyTime            time.Time // 修改时间
+}
+
+type BaseFromSci99DataItem struct {
+	BaseFromSciDataId  int       `orm:"column(base_from_sci_data_id);pk"` // 主键,自动递增
+	BaseFromSciIndexId int       // 指标id
+	IndexCode          string    // 指标编码
+	DataTime           string    // 数据日期
+	Value              float64   // 数据值
+	CreateTime         string // 创建时间
+	ModifyTime         string // 修改时间
+}
+
+type BaseFromSci99IndexList struct {
+	BaseFromSciIndexId int       `orm:"column(base_from_sci_index_id);pk"` // 主键,自动递增
+	IndexCode          string    // 指标编码
+	IndexName          string    // 指标名称
+	ClassifyId         int       // 分类Id
+	Unit               string    // 单位
+	Frequency          string    // 频度
+	Describe           string    // 指标描述
+	CreateTime         string // 创建时间
+	ModifyTime         string // 修改时间
+	DataList           []*BaseFromSci99DataItem
+	Paging             *paging.PagingItem `description:"分页数据"`
+}
+
+// 添加数据
+func AddBaseFromSci99Index(item *BaseFromSci99Index) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	lastId, err = o.Insert(item)
+	return
+}
+
+func AddBaseFromSci99Classify(item *BaseFromSci99Classify) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	lastId, err = o.Insert(item)
+	return
+}
+
+func AddBaseFromSci99DataMulti(item []*BaseFromSci99Data) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(1000, item)
+	return
+}
+
+func GetBaseFromSci99Classify() (list []*BaseFromSci99Classify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_sci99_classify `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+func GetSci99Index(condition string, pars interface{}) (items []*BaseFromSci99IndexList, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_sci99_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += `ORDER BY base_from_sci_index_id ASC `
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+
+func GetSci99IndexDataCount(indexCode string) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count  FROM base_from_sci99_data WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&count)
+	return
+}
+
+func GetSci99IndexData(indexCode string, startSize, pageSize int) (items []*BaseFromSci99DataItem, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_sci99_data WHERE index_code=? ORDER BY data_time DESC LIMIT ?,? `
+	_, err = o.Raw(sql, indexCode, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetSci99DataMaxCount(classifyId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT MAX(t.num) AS count FROM (
+				SELECT COUNT(1) AS num  FROM base_from_icpi_index AS a
+				INNER JOIN base_from_icpi_data AS b ON a.base_from_icpi_index_id=b.base_from_icpi_index_id
+				WHERE a.base_from_icpi_classify_id=?
+				GROUP BY a.base_from_icpi_index_id
+			)AS t `
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+func GetSci99IndexDataByCode(indexCode string) (items []*BaseFromSci99DataItem, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_sci99_data WHERE index_code=? ORDER BY data_time DESC  `
+	_, err = o.Raw(sql, indexCode).QueryRows(&items)
+	return
+}

+ 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),  // 平衡表作图数据
 	)
 }
 

+ 9 - 0
models/english_report.go

@@ -986,3 +986,12 @@ func UpdateEnglishReportEmailHasFail(reportId int) (err error) {
 	_, err = o.Raw(sql, reportId).Exec()
 	return
 }
+
+
+// UpdatePdfUrlEnglishReportById 清空pdf相关字段
+func UpdatePdfUrlEnglishReportById(reportId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE english_report SET detail_img_url = '',detail_pdf_url='',modify_time=NOW() WHERE id = ? `
+	_, err = o.Raw(sql, reportId).Exec()
+	return
+}

+ 58 - 0
models/mgo/common.go

@@ -0,0 +1,58 @@
+package mgo
+
+import (
+	"eta/eta_api/utils"
+	"go.mongodb.org/mongo-driver/bson"
+	"time"
+)
+
+// BuildDateCondition
+// @Description:  构建日期查询条件
+// @author: Roc
+// @datetime 2024-06-03 09:41:19
+// @param start string
+// @param end string
+// @return condition bson.M
+// @return err error
+func BuildDateCondition(start, end string) (condition bson.M, err error) {
+	var startDateTime, endDateTime time.Time
+	if start != "" {
+		// 使用开始日期条件
+		startDateTime, err = time.ParseInLocation(utils.FormatDate, start, time.Local)
+		if err != nil {
+			return
+		}
+	}
+	if end != "" {
+		// 使用结束日期条件
+		endDateTime, err = time.ParseInLocation(utils.FormatDate, end, time.Local)
+		if err != nil {
+			return
+		}
+	}
+
+	return BuildDateTimeCondition(startDateTime, endDateTime)
+
+}
+
+// BuildDateTimeCondition
+// @Description: 构建日期查询条件
+// @author: Roc
+// @datetime 2024-06-03 09:47:32
+// @param startDateTime time.Time
+// @param endDateTime time.Time
+// @return condition bson.M
+// @return err error
+func BuildDateTimeCondition(startDateTime, endDateTime time.Time) (condition bson.M, err error) {
+	if !startDateTime.IsZero() && !endDateTime.IsZero() {
+		condition = bson.M{utils.DateConvMysqlConvMongo(">="): startDateTime, utils.DateConvMysqlConvMongo("<="): endDateTime}
+	} else if !startDateTime.IsZero() {
+		cond := utils.DateConvMysqlConvMongo(">=")
+		condition = bson.M{cond: startDateTime}
+	} else if !endDateTime.IsZero() {
+		cond := utils.DateConvMysqlConvMongo("<=")
+		condition = bson.M{cond: endDateTime}
+	}
+
+	return
+}

+ 8 - 0
models/report.go

@@ -1166,3 +1166,11 @@ func ModifyReportImgUrl(reportId int, detailImgUrl string) (err error) {
 	_, err = o.Raw(sql, detailImgUrl, reportId).Exec()
 	return
 }
+
+// UpdatePdfUrlReportById 清空pdf相关字段
+func UpdatePdfUrlReportById(reportId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE report SET detail_img_url = '',detail_pdf_url='',modify_time=NOW() WHERE id = ? `
+	_, err = o.Raw(sql, reportId).Exec()
+	return
+}

+ 9 - 0
models/smart_report/smart_report.go

@@ -418,3 +418,12 @@ func UpdateSmartReportsStateBySecondIds(oldState, newState int, secondIds []int)
 	_, err = o.Raw(sql, oldState, newState, secondIds).Exec()
 	return
 }
+
+
+// UpdatePdfUrlSmartReportById 清空pdf相关字段
+func UpdatePdfUrlSmartReportById(reportId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE smart_report SET detail_img_url = '',detail_pdf_url='',modify_time=NOW() WHERE smart_report_id = ? `
+	_, err = o.Raw(sql, reportId).Exec()
+	return
+}

+ 144 - 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",
@@ -4759,6 +4876,33 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
+        beego.ControllerComments{
+            Method: "Sci99ClassifyList",
+            Router: `/sci99/classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
+        beego.ControllerComments{
+            Method: "ExportSci99DataList",
+            Router: `/sci99/export/sci99DataList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
+        beego.ControllerComments{
+            Method: "Sci99Data",
+            Router: `/sci99/index/data`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_stat:EdbSourceStatController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_stat:EdbSourceStatController"],
         beego.ControllerComments{
             Method: "EdbDeleteLog",

+ 4 - 0
services/data/base_edb_lib.go

@@ -72,6 +72,8 @@ func AddEdbData(source int, edbCode, frequency string) (resp *models.BaseRespons
 		urlStr = "gz/add"
 	case utils.DATA_SOURCE_ICPI:
 		urlStr = "icpi/add"
+	case utils.DATA_SOURCE_SCI99:
+		urlStr = "sci99/add"
 	default:
 		edbSource := data_manage.EdbSourceIdMap[source]
 		if edbSource != nil {
@@ -267,6 +269,8 @@ func RefreshEdbData(edbInfoId, source, subSource int, edbCode, startDate string)
 		urlStr = "gz/refresh"
 	case utils.DATA_SOURCE_ICPI:
 		urlStr = "icpi/refresh"
+	case utils.DATA_SOURCE_SCI99:
+		urlStr = "sci99/refresh"
 	default:
 		edbSource := data_manage.EdbSourceIdMap[source]
 		if edbSource != nil {

+ 111 - 0
services/data/chart_info.go

@@ -3081,3 +3081,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
+}

+ 7 - 7
services/data/edb_data.go

@@ -480,14 +480,14 @@ func getPageDataByMongo(edbInfoId, source, subSource int, endDataTime string, st
 	queryConditions := bson.M{
 		"edb_info_id": edbInfoId,
 	}
+
 	// 结束日期
-	if endDataTime != "" {
-		endDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, endDataTime, time.Local)
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		queryConditions["data_time"] = bson.M{"$lte": endDateTime}
+	dateCondition, err := mgo.BuildDateCondition("", endDataTime)
+	if err != nil {
+		return
+	}
+	if len(dateCondition) > 0 {
+		queryConditions["data_time"] = dateCondition
 	}
 
 	// 获取数据总量

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

+ 48 - 20
services/data/excel/excel_info.go

@@ -52,31 +52,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,
 	}
 
 	// 无权限,不需要返回数据
@@ -131,7 +156,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 {
@@ -150,11 +175,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

+ 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 = "保存失败"
 		}

+ 37 - 7
services/smart_report.go

@@ -199,7 +199,7 @@ finally:
 	return
 }
 
-func ReportToJpeg(reportUrl, filePath string) (err error) {
+func ReportToJpeg(width int, reportUrl, filePath string) (err error) {
 	pyCode := `
 import asyncio
 from pyppeteer import launch, errors
@@ -218,7 +218,7 @@ async def main():
         
         # 设置视口大小
         await page.setViewport({
-            'width': 1920,
+            'width': %d,
             'height': 1080
         })
         
@@ -227,11 +227,15 @@ async def main():
             'waitUntil': 'networkidle0',
             'timeout': 1000000  # 设置超时时间为 100 秒
         })
-        
-        # 截取全页面的屏幕截图
+        # Customizing footer for page numbers starting from page 2
+
+        # 在这里添加两秒的等待
+        await asyncio.sleep(2)
+
         await page.screenshot({
             'path': "%s",
             'fullPage': True,
+			'quality':100
         })
         
     except errors.BrowserError as e:
@@ -256,7 +260,7 @@ finally:
     loop.close()
 `
 
-	pyCode = fmt.Sprintf(pyCode, utils.ChromePath, reportUrl, filePath)
+	pyCode = fmt.Sprintf(pyCode, utils.ChromePath, width, reportUrl, filePath)
 	utils.FileLog.Info("jpeg pyCode: \n" + pyCode)
 	cmd := exec.Command("python3", "-c", pyCode)
 
@@ -274,7 +278,7 @@ finally:
 	return
 }
 
-func Report2pdfAndJpeg(reportUrl string, reportId,reportType int) {
+func Report2pdfAndJpeg(reportUrl string, reportId, reportType int) {
 	var err error
 
 	defer func() {
@@ -283,6 +287,28 @@ func Report2pdfAndJpeg(reportUrl string, reportId,reportType int) {
 			utils.FileLog.Info("Report2pdfAndJpeg failed: , error: \n" + err.Error())
 		}
 	}()
+
+	// 先清空字段
+	if reportType == 1 {
+		err = models.UpdatePdfUrlReportById(reportId)
+		if err != nil {
+			utils.FileLog.Info("清空pdf长图字段失败, Err: \n" + err.Error())
+			return
+		}
+	} else if reportType == 2 {
+		err = models.UpdatePdfUrlEnglishReportById(reportId)
+		if err != nil {
+			utils.FileLog.Info("清空pdf长图字段失败, Err: \n" + err.Error())
+			return
+		}
+	} else if reportType == 3 {
+		err = smart_report.UpdatePdfUrlSmartReportById(reportId)
+		if err != nil {
+			utils.FileLog.Info("清空pdf长图字段失败, Err: \n" + err.Error())
+			return
+		}
+	}
+
 	reportCode := utils.MD5(strconv.Itoa(reportId))
 
 	pdfPath := `./static/` + reportCode + ".pdf"
@@ -350,7 +376,11 @@ func Report2pdfAndJpeg(reportUrl string, reportId,reportType int) {
 	}()
 
 	go func() {
-		err := ReportToJpeg(reportUrl, jpegPath)
+		width := 1200
+		if reportType == 3 {
+			width = 800
+		}
+		err := ReportToJpeg(width, reportUrl, jpegPath)
 		if err != nil {
 			utils.FileLog.Info("ReportToJpeg failed: , error: \n" + err.Error())
 		}

+ 22 - 0
utils/common.go

@@ -2446,3 +2446,25 @@ func MillisecondsToHHMMSS(ms int) string {
 func ByteToMB(byteCount int) float64 {
 	return float64(byteCount) / (1024 * 1024)
 }
+
+// DateConvMysqlConvMongo
+// @Description: 将mysql中的日期比较符转换成mongo中的日期比较符
+// @author: Roc
+// @datetime 2024-05-08 11:03:26
+// @param dateCon string
+func DateConvMysqlConvMongo(dateCon string) string {
+	cond := ""
+	switch dateCon {
+	case "=":
+		cond = "$eq"
+	case "<":
+		cond = "$lt"
+	case "<=":
+		cond = "$lte"
+	case ">":
+		cond = "$gt"
+	case ">=":
+		cond = "$gte"
+	}
+	return cond
+}

+ 3 - 0
utils/constants.go

@@ -172,6 +172,7 @@ const (
 	DATA_SOURCE_ICPI                                 = 79       // ICPI消费价格指数->79
 	DATA_SOURCE_BLOOMBERG                            = 83       // bloomberg彭博数据
 	DATA_SOURCE_BUSINESS                             = 84       // 来源于自有数据
+	DATA_SOURCE_SCI99                                = 85       // 卓创资讯 -> 85
 )
 
 // 数据刷新频率
@@ -277,6 +278,7 @@ const (
 	CHART_SOURCE_LINE_FEATURE_PERCENTILE         = 8  // 统计特征-百分位图表
 	CHART_SOURCE_LINE_FEATURE_FREQUENCY          = 9  // 统计特征-频率分布图表
 	CHART_SOURCE_CROSS_HEDGING                   = 10 // 跨品种分析图表
+	CHART_SOURCE_BALANCE_EXCEL                   = 11 // 平衡表图表
 )
 
 // 批量配置图表的位置来源
@@ -299,6 +301,7 @@ const (
 	TIME_TABLE            = 2 // 时间序列表格
 	MIXED_TABLE           = 3 // 混合表格
 	CUSTOM_ANALYSIS_TABLE = 4 // 自定义分析表格
+	BALANCE_TABLE         = 5 // 平衡表
 )
 
 // 图表样式类型