Browse Source

feat;混合表格优化

Roc 1 year ago
parent
commit
dbc005a8f6

+ 5 - 4
controllers/excel_info.go

@@ -6,6 +6,7 @@ import (
 	"eta/eta_chart_lib/models/request"
 	"eta/eta_chart_lib/models/request"
 	"eta/eta_chart_lib/models/response"
 	"eta/eta_chart_lib/models/response"
 	"eta/eta_chart_lib/services/data"
 	"eta/eta_chart_lib/services/data"
+	"eta/eta_chart_lib/services/data/table"
 	"eta/eta_chart_lib/services/excel"
 	"eta/eta_chart_lib/services/excel"
 	"eta/eta_chart_lib/utils"
 	"eta/eta_chart_lib/utils"
 	"fmt"
 	"fmt"
@@ -68,7 +69,7 @@ func (this *ExcelInfoController) GetTableDetail() {
 	}
 	}
 	var tableData excel.TableData
 	var tableData excel.TableData
 	switch excelInfo.Source {
 	switch excelInfo.Source {
-	case 1:
+	case utils.EXCEL_DEFAULT:
 		luckySheetData, err := excel.GetLuckySheetData(excelInfo.Content)
 		luckySheetData, err := excel.GetLuckySheetData(excelInfo.Content)
 		if err != nil {
 		if err != nil {
 			br.Msg = "获取失败"
 			br.Msg = "获取失败"
@@ -81,7 +82,7 @@ func (this *ExcelInfoController) GetTableDetail() {
 			br.ErrMsg = "转换成table失败,Err:" + err.Error()
 			br.ErrMsg = "转换成table失败,Err:" + err.Error()
 			return
 			return
 		}
 		}
-	case 2:
+	case utils.TIME_TABLE:
 		var tableDataConfig data.TableDataConfig
 		var tableDataConfig data.TableDataConfig
 		err = json.Unmarshal([]byte(excelInfo.Content), &tableDataConfig)
 		err = json.Unmarshal([]byte(excelInfo.Content), &tableDataConfig)
 		if err != nil {
 		if err != nil {
@@ -101,7 +102,7 @@ func (this *ExcelInfoController) GetTableDetail() {
 			br.ErrMsg = "转换成table失败,Err:" + err.Error()
 			br.ErrMsg = "转换成table失败,Err:" + err.Error()
 			return
 			return
 		}
 		}
-	case 3:
+	case utils.MIXED_TABLE:
 		var result request.MixedTableReq
 		var result request.MixedTableReq
 		err = json.Unmarshal([]byte(excelInfo.Content), &result)
 		err = json.Unmarshal([]byte(excelInfo.Content), &result)
 		if err != nil {
 		if err != nil {
@@ -109,7 +110,7 @@ func (this *ExcelInfoController) GetTableDetail() {
 			br.ErrMsg = "表格json转结构体失败,Err:" + err.Error()
 			br.ErrMsg = "表格json转结构体失败,Err:" + err.Error()
 			return
 			return
 		}
 		}
-		newResult, tmpErr := data.GetMixedTableCellData(result.Data)
+		newResult, tmpErr, _ := table.GetMixedTableCellData(result)
 		if tmpErr != nil {
 		if tmpErr != nil {
 			br.Msg = "获取失败"
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取最新的数据失败,Err:" + err.Error()
 			br.ErrMsg = "获取最新的数据失败,Err:" + err.Error()

+ 0 - 16
models/request/excel_info.go

@@ -30,19 +30,3 @@ type RelationEdbInfo struct {
 	Tag string `description:"指标标签"`
 	Tag string `description:"指标标签"`
 	Row string `description:"第几行"`
 	Row string `description:"第几行"`
 }
 }
-
-// MixedTableReq 混合表格保存请求参数
-type MixedTableReq struct {
-	CellRelation string                    `description:"单元格关系"`
-	Data         [][]MixedTableCellDataReq `description:"混合表格单元格参数"`
-}
-
-// MixedTableCellDataReq 混合表格单元格参数
-type MixedTableCellDataReq struct {
-	Uid       string `description:"单元格唯一标识"`
-	DataType  int    `description:"数据类型,1:日期,2:指标,3:自定义文本,4:插值"`
-	DataTime  string `description:"所属日期"`
-	EdbInfoId int    `description:"指标id"`
-	ShowValue string `description:"展示值"`
-	Value     string `description:"实际值"`
-}

+ 105 - 0
models/request/mixed_table.go

@@ -0,0 +1,105 @@
+package request
+
+// 单元格的数据类型
+const (
+	DateDT                   = iota + 1 //日期
+	EdbDT                               // 2 指标类型
+	CustomTextDT                        // 3 自定义文本
+	InsertDataDT                        // 4 插值(插入指标值,表格上,自动判断日期和指标的交集位置,插入值)
+	PopInsertDataDT                     // 5 弹框插值(在表格上选择日期,然后空白单元格选择弹框并选择指标,插入该指标与该日期的值)
+	FormulateCalculateDataDT            // 6 公式计算(A+B这种)
+	InsertEdbCalculateDataDT            // 7 插入指标系统计算公式生成的值
+)
+
+// 单元格的日期类型类型
+const (
+	CustomDateT = iota //手动输入日期
+	SystemDateT        // 系统日期
+	EdbDateDT          // 导入指标日期(指标库的最新日期)
+)
+
+// 单元格的日期类型类型
+const (
+	SystemCurrDateT      = iota + 1 //系统当前日期
+	SystemCalculateDateT            // 系统日期计算后的日期
+	SystemFrequencyDateT            // 导入系统日期相关的指定频率(所在周/旬/月/季/半年/年的最后/最早一天)
+)
+
+// MixedTableReq 混合表格保存请求参数
+type MixedTableReq struct {
+	CellRelation string                    `description:"单元格关系"`
+	Data         [][]MixedTableCellDataReq `description:"混合表格单元格参数"`
+}
+
+// MixedTableCellDataReq 混合表格单元格参数
+type MixedTableCellDataReq struct {
+	Uid          string `description:"单元格唯一标识"`
+	DataType     int    `description:"数据类型,1:日期,2:指标,3:自定义文本,4:插值"`
+	DataTime     string `description:"所属日期"`
+	DataTimeType int    `description:"日期类型:0:手动输入日期;1:导入系统日期;;3:导入指标日期(指标库的最新日期);"`
+	EdbInfoId    int    `description:"指标id"`
+	ShowValue    string `description:"展示值"`
+	Value        string `description:"实际值"`
+	Extra        string `description:"额外参数"`
+}
+
+// CellRelationConf
+// @Description: 单元格的关系配置结构体
+type CellRelationConf struct {
+	CellRelation
+	//Type         int          `json:"type" description:"数据类型,跟MixedTableCellDataReq的DataType保持一致"`
+	//Key          string       `json:"key" description:"单元格的唯一标识"`
+	RelationDate CellRelation `json:"relation_date"`
+	RelationEdb  CellRelation `json:"relation_edb"`
+}
+
+// CellRelation
+// @Description: 单元格的关系结构体
+type CellRelation struct {
+	Type int    `json:"type" description:"数据类型,跟MixedTableCellDataReq的DataType保持一致"`
+	Key  string `json:"key" description:"单元格的唯一标识"`
+}
+
+// SystemDateConf
+// @Description: 系统导入日期配置
+type SystemDateConf struct {
+	Source             int    `description:"类型,1:导入系统日期;2:导入系统日期计算后的日期;3:导入系统日期相关的指定频率"`
+	CalculateNum       int    `description:"计算频度的数量"`
+	CalculateFrequency string `description:"计算频度"`
+	Frequency          string `description:"指定频度"`
+	Day                string `description:"指定日期"`
+}
+
+// EdbDateConf
+// @Description: 导入指标日期配置
+type EdbDateConf struct {
+	EdbInfoId int `description:"指标id"`
+}
+
+// CalculateConf
+// @Description: 计算公式
+type CalculateConf struct {
+	EdbInfoId     int         `description:"指标id"`
+	DataTime      string      `description:"所属日期,这个日期有传递的话,那么就取上下两期+自己的数据;没有传就默认最近5期的数据"`
+	Frequency     string      `description:"需要转换的频度"`
+	Formula       interface{} `description:"计算公式,默认是string,实际上还需要转成其他样式"`
+	Calendar      string      `description:"公历/农历"`
+	MoveType      int         `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency string      `description:"移动频度"`
+	Source        int         `description:"1:累计值转月;2:累计值转季;3:同比值;4:同差值;5:N数值移动平均数计算;6:环比值;7:环差值;8:升频;9:降频;10:时间移位;11:超季节性;12:年化;13:累计值;14:累计值年初至今;15:指数修匀;16:日均值"`
+}
+
+// BaseCalculateConf
+// @Description: 基础计算公式(A+B)
+type BaseCalculateConf struct {
+	Formula             string         `description:"计算公式,默认是string,实际上还需要转成其他样式"`
+	RelationEdbInfoList []RelationCell `description:"关联单元格(计算公式中关联的单元格,用于计算的时候去匹配)"`
+}
+
+// RelationCell
+// @Description: 关联单元格的信息
+type RelationCell struct {
+	Tag string `description:"指标标签"`
+	Row string `description:"第几行"`
+	Key string `json:"key" description:"单元格的唯一标识"`
+}

+ 45 - 0
services/data/base_edb_lib.go

@@ -158,6 +158,36 @@ func RefreshPredictEdbCalculateData(edbInfoId int, edbCode, startDate string) (r
 	return
 	return
 }
 }
 
 
+// BaseCalculateResp 拟合残差计算相关性的值返回
+type BaseCalculateResp struct {
+	Ret         int
+	Msg         string
+	ErrMsg      string
+	ErrCode     string
+	Data        BaseCalculateDataResp
+	Success     bool `description:"true 执行成功,false 执行失败"`
+	IsSendEmail bool `json:"-" description:"true 发送邮件,false 不发送邮件"`
+	IsAddLog    bool `json:"-" description:"true 新增操作日志,false 不新增操作日志" `
+}
+
+// BaseCalculateDataResp
+// @Description: 基础计算的返回结果
+type BaseCalculateDataResp struct {
+	DataMap  map[string]float64
+	DateList []string
+}
+
+// BaseCalculate 基础计算
+func BaseCalculate(param string) (resp *BaseCalculateResp, err error) {
+	urlStr := "calculate/base"
+	_, resultByte, err := postAddEdbData(param, urlStr)
+	err = json.Unmarshal(resultByte, &resp)
+	if err != nil {
+		return
+	}
+	return
+}
+
 // postRefreshEdbData 刷新指标数据
 // postRefreshEdbData 刷新指标数据
 func postRefreshEdbData(param map[string]interface{}, urlStr string) (resp *models.BaseResponse, err error) {
 func postRefreshEdbData(param map[string]interface{}, urlStr string) (resp *models.BaseResponse, err error) {
 	postUrl := utils.EDB_LIB_URL + urlStr
 	postUrl := utils.EDB_LIB_URL + urlStr
@@ -176,6 +206,21 @@ func postRefreshEdbData(param map[string]interface{}, urlStr string) (resp *mode
 	return resp, nil
 	return resp, nil
 }
 }
 
 
+// postAddEdbData 新增指标数据
+func postAddEdbData(paramStr string, urlStr string) (resp *models.BaseResponse, result []byte, err error) {
+	postUrl := utils.EDB_LIB_URL + urlStr
+	result, err = HttpPost(postUrl, paramStr, "application/json")
+	if err != nil {
+		return
+	}
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
 func HttpPost(url, postData string, params ...string) ([]byte, error) {
 func HttpPost(url, postData string, params ...string) ([]byte, error) {
 	body := ioutil.NopCloser(strings.NewReader(postData))
 	body := ioutil.NopCloser(strings.NewReader(postData))
 	client := &http.Client{}
 	client := &http.Client{}

+ 0 - 89
services/data/excel_info.go

@@ -1118,92 +1118,3 @@ func calculate(calculateFormula string, TagMap map[string]float64) (calVal, errM
 
 
 	return
 	return
 }
 }
-
-// GetMixedTableCellData 获取混合表格数据
-func GetMixedTableCellData(config [][]request.MixedTableCellDataReq) (newMixedTableCellDataList [][]request.MixedTableCellDataReq, err error) {
-	newMixedTableCellDataList = config
-
-	edbInfoIdList := make([]int, 0)
-	dataEdbInfoIdList := make([]int, 0)
-	for _, row := range config {
-		for _, cell := range row {
-			if cell.DataType == 2 {
-				edbInfoIdList = append(edbInfoIdList, cell.EdbInfoId)
-			} else if utils.InArrayByInt([]int{4, 5}, cell.DataType) {
-				dataEdbInfoIdList = append(dataEdbInfoIdList, cell.EdbInfoId)
-			}
-		}
-	}
-
-	edbInfoList, err := data_manage.GetEdbInfoByIdList(edbInfoIdList)
-	if err != nil {
-		return
-	}
-
-	// 指标信息map
-	edbInfoMap := make(map[int]*data_manage.EdbInfo)
-	// 指标数据map
-	edbDataListMap := make(map[int]map[string]float64)
-	// 月度指标数据map
-	edbMonthDataListMap := make(map[int]map[string]float64)
-	for _, edbInfo := range edbInfoList {
-		edbInfoMap[edbInfo.EdbInfoId] = edbInfo
-
-		dataList := make([]*models.EdbDataList, 0)
-		switch edbInfo.EdbInfoType {
-		case 0:
-			dataList, _ = models.GetEdbDataList(edbInfo.Source, edbInfo.EdbInfoId, ``, ``)
-		case 1:
-			_, dataList, _, _, _, _ = GetPredictDataListByPredictEdbInfoId(edbInfo.EdbInfoId, ``, ``, false)
-		default:
-			err = errors.New(fmt.Sprint("获取失败,指标类型异常", edbInfo.EdbInfoType))
-		}
-
-		dateValMap := make(map[string]float64)
-		monthValMap := make(map[string]float64)
-		for _, data := range dataList {
-			// 日度数据
-			dateValMap[data.DataTime] = data.Value
-			// 月度数据(取该月份的第一个数据)
-			yearMonth := strings.Join(strings.Split(data.DataTime, "-")[0:2], "-")
-			if _, ok := monthValMap[yearMonth]; !ok {
-				monthValMap[yearMonth] = data.Value
-			}
-		}
-		edbDataListMap[edbInfo.EdbInfoId] = dateValMap
-		edbMonthDataListMap[edbInfo.EdbInfoId] = monthValMap
-	}
-
-	for k, row := range newMixedTableCellDataList {
-		for i, cell := range row {
-			if cell.DataType == 2 {
-				if edbInfo, ok := edbInfoMap[cell.EdbInfoId]; ok {
-					cell.ShowValue = edbInfo.EdbName
-				}
-			} else if utils.InArrayByInt([]int{4, 5}, cell.DataType) {
-				tmpDateList := strings.Split(cell.DataTime, "-")
-				tmpDateValMap := make(map[string]float64)
-				if len(tmpDateList) == 2 {
-					//月度数据
-					if dateValMap, ok := edbMonthDataListMap[cell.EdbInfoId]; ok {
-						tmpDateValMap = dateValMap
-					}
-				} else {
-					// 日度数据
-					if dateValMap, ok := edbDataListMap[cell.EdbInfoId]; ok {
-						tmpDateValMap = dateValMap
-					}
-
-				}
-				if val, ok2 := tmpDateValMap[cell.DataTime]; ok2 {
-					cell.ShowValue = fmt.Sprint(val)
-				}
-			}
-
-			row[i] = cell
-		}
-		newMixedTableCellDataList[k] = row
-	}
-
-	return
-}

+ 735 - 0
services/data/table/mixed_table.go

@@ -0,0 +1,735 @@
+package table
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_chart_lib/models"
+	"eta/eta_chart_lib/models/data_manage"
+	"eta/eta_chart_lib/models/request"
+	"eta/eta_chart_lib/services/data"
+	"eta/eta_chart_lib/utils"
+	"fmt"
+	"github.com/shopspring/decimal"
+	"github.com/yidane/formula"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// BaseCalculate
+// @Description: 指标数据计算请求
+type BaseCalculate struct {
+	DataList      []*models.EdbDataList
+	Frequency     string `description:"需要转换的频度"`
+	Formula       interface{}
+	Calendar      string `description:"公历/农历"`
+	MoveType      int    `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency string `description:"移动频度"`
+	FromFrequency string `description:"来源的频度"`
+	Source        int    `description:"1:累计值转月;2:累计值转季;3:同比值;4:同差值;5:N数值移动平均数计算;6:环比值;7:环差值;8:升频;9:降频;10:时间移位;11:超季节性;12:年化;13:累计值;14:累计值年初至今;15:指数修匀;16:日均值"`
+}
+
+// Cell
+// @Description: 单元格位置
+type Cell struct {
+	Column   int                           `description:"行"`
+	Row      int                           `description:"列"`
+	CellInfo request.MixedTableCellDataReq `description:"对应的单元格信息"`
+}
+
+func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCellDataList [][]request.MixedTableCellDataReq, err error, errMsg string) {
+	cellRelationConf := mixedTableReq.CellRelation
+	config := mixedTableReq.Data
+
+	// 单元格关系配置x信息
+	cellRelationConfMap := make(map[string]request.CellRelationConf)
+	cellRelationConfList := make([]request.CellRelationConf, 0)
+	if cellRelationConf != `` {
+		err = json.Unmarshal([]byte(cellRelationConf), &cellRelationConfList)
+		if err != nil {
+			return
+		}
+
+		for _, v := range cellRelationConfList {
+			cellRelationConfMap[v.Key] = v
+		}
+	}
+
+	// 找出所有的关联指标id
+	config, edbInfoIdList, _, err, errMsg := handleConfig(config)
+	if err != nil {
+		return
+	}
+
+	// 查询所有关联的指标信息
+	edbInfoList, err := data_manage.GetEdbInfoByIdList(edbInfoIdList)
+	if err != nil {
+		return
+	}
+
+	// 指标信息map
+	edbInfoMap := make(map[int]*data_manage.EdbInfo)
+	// 日度指标数据map
+	edbDayDataListMap := make(map[int]map[string]float64)
+	// 月度指标数据map
+	edbMonthDataListMap := make(map[int]map[string]float64)
+	// 日度指标数据map
+	edbDataListMap := make(map[int][]*models.EdbDataList)
+	for _, edbInfo := range edbInfoList {
+		edbInfoMap[edbInfo.EdbInfoId] = edbInfo
+
+		dataList := make([]*models.EdbDataList, 0)
+		switch edbInfo.EdbInfoType {
+		case 0:
+			dataList, _ = models.GetEdbDataList(edbInfo.Source, edbInfo.EdbInfoId, ``, ``)
+		case 1:
+			_, dataList, _, _, _, _ = data.GetPredictDataListByPredictEdbInfoId(edbInfo.EdbInfoId, ``, ``, false)
+		default:
+			err = errors.New(fmt.Sprint("获取失败,指标类型异常", edbInfo.EdbInfoType))
+		}
+
+		dateValMap := make(map[string]float64)
+		monthValMap := make(map[string]float64)
+		for _, tmpData := range dataList {
+			// 日度数据
+			dateValMap[tmpData.DataTime] = tmpData.Value
+			// 月度数据(取该月份的第一个数据)
+			yearMonth := strings.Join(strings.Split(tmpData.DataTime, "-")[0:2], "-")
+			if _, ok := monthValMap[yearMonth]; !ok {
+				monthValMap[yearMonth] = tmpData.Value
+			}
+		}
+		edbDayDataListMap[edbInfo.EdbInfoId] = dateValMap
+		edbMonthDataListMap[edbInfo.EdbInfoId] = monthValMap
+		edbDataListMap[edbInfo.EdbInfoId] = dataList
+	}
+
+	// 单元格实际绑定的信息map
+	cellDataRelationMap := make(map[string]request.MixedTableCellDataReq, 0)
+
+	// 处理指定指标的日期
+	for k, row := range config {
+		for i, cell := range row {
+			// 单元格是日期类型,且是日导入指标日期(指标库的最新日期)
+			if cell.DataType == request.DateDT && cell.DataTimeType == request.EdbDateDT {
+				if edbInfo, ok := edbInfoMap[cell.EdbInfoId]; ok {
+					cell.ShowValue = edbInfo.EndDate
+					cell.DataTime = edbInfo.EndDate
+					config[k][i] = cell
+				}
+			}
+			row[i] = cell
+
+			cellDataRelationMap[cell.Uid] = cell
+		}
+		config[k] = row
+	}
+
+	// 指标计算的结果map
+	edbSourceDataMap := make(map[string]map[string]float64)
+
+	// 单元格对应的key与他的值(只处理数据类型)
+	cellKeyVal := make(map[string]float64)
+
+	// 基础计算单元格的位置信息
+	calculateCellMap := make(map[string]Cell)
+	calculateChainList := make([]string, 0)
+
+	// 处理单元格中的数据类型(除去基础计算,因为这个是依赖于其他)
+	for k, row := range config {
+		for i, cell := range row {
+			switch cell.DataType {
+			case request.EdbDT: // 指标类型
+				if edbInfo, ok := edbInfoMap[cell.EdbInfoId]; ok {
+					cell.ShowValue = edbInfo.EdbName
+				}
+			case request.InsertDataDT, request.PopInsertDataDT: // 数据类型
+				if cell.DataTime == `` {
+					// 指标的最新日期
+					if dateValList, ok := edbDataListMap[cell.EdbInfoId]; ok {
+						tmpLenData := len(dateValList)
+						if tmpLenData > 0 {
+							cellKeyVal[cell.Uid] = dateValList[tmpLenData-1].Value
+							//cell.ShowValue = utils.FormatTableDataShowValue(dateValList[tmpLenData-1].Value)
+							cell.ShowValue = fmt.Sprint(dateValList[tmpLenData-1].Value)
+						}
+					}
+				} else {
+					tmpDateList := strings.Split(cell.DataTime, "-")
+					tmpDateValMap := make(map[string]float64)
+					if len(tmpDateList) == 2 {
+						//月度数据
+						if dateValMap, ok := edbMonthDataListMap[cell.EdbInfoId]; ok {
+							tmpDateValMap = dateValMap
+						}
+					} else {
+						// 日度数据
+						if dateValMap, ok := edbDayDataListMap[cell.EdbInfoId]; ok {
+							tmpDateValMap = dateValMap
+						}
+
+					}
+					if val, ok2 := tmpDateValMap[cell.DataTime]; ok2 {
+						//cell.ShowValue = fmt.Sprint(val)
+						cellKeyVal[cell.Uid] = val
+						//cell.ShowValue = utils.FormatTableDataShowValue(val)
+						cell.ShowValue = fmt.Sprint(val)
+					}
+				}
+			case request.CustomTextDT: //自定义文本
+				if cell.Value == `` {
+					continue
+				}
+				// 处理看下能否转成float,如果可以的话,说明这个也是可以参与计算的
+				tmpDeci, tmpErr := decimal.NewFromString(cell.Value)
+				if tmpErr == nil {
+					tmpVal, _ := tmpDeci.Float64()
+					cellKeyVal[cell.Uid] = tmpVal
+
+					calculateCellMap[cell.Uid] = Cell{
+						Column:   k,
+						Row:      i,
+						CellInfo: cell,
+					}
+				}
+
+			case request.FormulateCalculateDataDT: // 公式计算(A+B这种)
+				calculateCellMap[cell.Uid] = Cell{
+					Column:   k,
+					Row:      i,
+					CellInfo: cell,
+				}
+
+				calculateChainList = append(calculateChainList, cell.Uid)
+
+			case request.InsertEdbCalculateDataDT: // 插入指标系统计算公式生成的值
+				// 日期
+				var cellDateTime string
+				// 日期关系配置不存在,则默认最新数据
+				if relationConf, ok := cellRelationConfMap[cell.Uid]; ok {
+					if relationConf.RelationDate.Key == `` {
+						// 日期关系配置未绑定
+						continue
+					}
+					// 配置
+					relationCell, ok := cellDataRelationMap[relationConf.RelationDate.Key]
+					if !ok {
+						// 找不到对应日期的单元格
+						continue
+					}
+					cellDateTime = relationCell.DataTime
+				}
+
+				var tmpDataMap map[string]float64
+
+				key := utils.MD5(cell.Value)
+				tmpDataMap, ok := edbSourceDataMap[key]
+				if !ok {
+					// 对应的配置值
+					var tmpConfig request.CalculateConf
+					err = json.Unmarshal([]byte(cell.Value), &tmpConfig)
+					if err != nil {
+						return
+					}
+
+					tmpDataList, ok := edbDataListMap[tmpConfig.EdbInfoId]
+					if !ok {
+						continue
+					}
+					edbInfo, ok := edbInfoMap[tmpConfig.EdbInfoId]
+					if !ok {
+						continue
+					}
+
+					req2 := &BaseCalculate{
+						DataList:      tmpDataList,
+						Frequency:     tmpConfig.Frequency,
+						Formula:       tmpConfig.Formula,
+						Calendar:      tmpConfig.Calendar,
+						MoveType:      tmpConfig.MoveType,
+						MoveFrequency: tmpConfig.MoveFrequency,
+						FromFrequency: edbInfo.Frequency,
+						Source:        tmpConfig.Source,
+					}
+
+					// 调用指标库去更新
+					reqJson, tmpErr := json.Marshal(req2)
+					if tmpErr != nil {
+						utils.FileLog.Error(fmt.Sprintf("计算失败1,配置信息;%s;错误原因:%s", cell.Value, tmpErr.Error()))
+						err = tmpErr
+						return
+					}
+					respItem, tmpErr := data.BaseCalculate(string(reqJson))
+					if tmpErr != nil {
+						utils.FileLog.Error(fmt.Sprintf("计算失败2,配置信息;%s;错误原因:%s", cell.Value, tmpErr.Error()))
+						err = tmpErr
+						return
+					}
+					if respItem.Ret != 200 {
+						utils.FileLog.Error(fmt.Sprintf("计算失败3,配置信息;%s;原因:%s;错误原因:%s", cell.Value, respItem.Msg, respItem.ErrMsg))
+						continue
+					}
+
+					tmpDataMap = respItem.Data.DataMap
+					// 计算结果存一份,万一存在重复的计算方式,那么省的重新计算一下
+					edbSourceDataMap[key] = tmpDataMap
+
+					lenDataList := len(respItem.Data.DateList)
+					if cellDateTime == `` && lenDataList > 0 {
+						cellDateTime = respItem.Data.DateList[lenDataList-1]
+					}
+				}
+
+				val := tmpDataMap[cellDateTime]
+				cellKeyVal[cell.Uid] = val
+				//cell.ShowValue = utils.FormatTableDataShowValue(val)
+				cell.ShowValue = fmt.Sprint(val)
+			}
+
+			row[i] = cell
+		}
+		config[k] = row
+	}
+
+	// 公式链计算
+	if len(calculateChainList) > 0 {
+		for _, cellKey := range calculateChainList {
+			// 查找这个单元格的位置,直接map找了,而不是遍历整个单元格
+			cellPosition, ok := calculateCellMap[cellKey]
+			if !ok {
+				utils.FileLog.Error("找不到单元格位置:", cellKey)
+				continue
+			}
+
+			cell := config[cellPosition.Column][cellPosition.Row]
+			if cell.DataType != request.FormulateCalculateDataDT { // 判断公式计算(A+B这种)类型,不是的话也过滤了
+				continue
+			}
+
+			val, tmpErr, has := getCalculateValueByCell(calculateCellMap, cellKey, cellKeyVal)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			if !has {
+				continue
+			}
+
+			cellKeyVal[cell.Uid] = val
+			//cell.ShowValue = utils.FormatTableDataShowValue(val)
+			cell.ShowValue = fmt.Sprint(val)
+			config[cellPosition.Column][cellPosition.Row] = cell
+
+		}
+	}
+
+	newMixedTableCellDataList = config
+
+	return
+}
+
+// getCalculateValue 获取公式计算的结果
+func getCalculateValueByCell(calculateCellMap map[string]Cell, key string, cellKeyValMap map[string]float64) (val float64, err error, has bool) {
+	// 单元格的标签名
+	val, ok := cellKeyValMap[key]
+	if ok {
+		has = true
+		return
+	}
+
+	// 查找单元格数据
+	cell, ok := calculateCellMap[key]
+	if !ok {
+		err = errors.New("查找单元格" + key + "的数据失败")
+		return
+	}
+
+	colData := cell.CellInfo
+
+	// 如果不是基础计算单元格,直接返回
+	if colData.DataType != request.FormulateCalculateDataDT {
+		return
+	}
+
+	// 如果是计算单元格
+
+	tagList := make([]utils.CellPosition, 0)
+	// 计算单元格relationCellList
+	var relationCellList []request.RelationCell
+	err = json.Unmarshal([]byte(colData.Extra), &relationCellList)
+	if err != nil {
+		return
+	}
+
+	for _, relation := range relationCellList {
+		//relationCellTagName := strings.ToUpper(relation.Tag) + relation.Row
+		tmpVal, tmpErr, _ := getCalculateValueByCell(calculateCellMap, relation.Key, cellKeyValMap)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		cellKeyValMap[relation.Key] = tmpVal
+
+		rowInt, tmpErr := strconv.Atoi(relation.Row)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		tagList = append(tagList, utils.CellPosition{
+			Tag:   relation.Tag,
+			Row:   rowInt,
+			Value: tmpVal,
+		})
+	}
+
+	// 计算
+	val, _, err = calculateByCellList(strings.ToUpper(colData.Value), tagList)
+	if err != nil {
+		return
+	}
+	// 重新赋值
+	has = true
+	cellKeyValMap[key] = val
+
+	return
+}
+
+// handleConfig
+// @Description: 处理混合表格配置
+// @author: Roc
+// @datetime2023-10-27 13:24:53
+// @param configList [][]request.MixedTableCellDataReq
+// @return newConfig [][]request.MixedTableCellDataReq
+// @return edbInfoIdList []int
+// @return dataEdbInfoIdList []int
+// @return err error
+// @return errMsg string
+func handleConfig(configList [][]request.MixedTableCellDataReq) (newConfig [][]request.MixedTableCellDataReq, edbInfoIdList []int, dataEdbInfoIdList []int, err error, errMsg string) {
+	edbInfoIdList = make([]int, 0)
+	dataEdbInfoIdList = make([]int, 0)
+
+	for ck, rowList := range configList {
+		for rk, cell := range rowList {
+			switch cell.DataType {
+			case request.EdbDT: // 指标信息
+				edbInfoIdList = append(edbInfoIdList, cell.EdbInfoId)
+			case request.InsertDataDT, request.PopInsertDataDT: // 插值、弹框插值
+				dataEdbInfoIdList = append(dataEdbInfoIdList, cell.EdbInfoId)
+			case request.InsertEdbCalculateDataDT: // 插入指标计算公式生成的值
+				var config request.CalculateConf
+				err = json.Unmarshal([]byte(cell.Value), &config)
+				if err != nil {
+					return
+				}
+				edbInfoIdList = append(edbInfoIdList, config.EdbInfoId)
+				dataEdbInfoIdList = append(dataEdbInfoIdList, cell.EdbInfoId)
+
+			case request.DateDT: // 日期类型
+				if cell.DataTimeType == request.EdbDateDT {
+					var config request.EdbDateConf
+					err = json.Unmarshal([]byte(cell.Value), &config)
+					if err != nil {
+						return
+					}
+					edbInfoIdList = append(edbInfoIdList, config.EdbInfoId)
+				} else {
+					date, tmpErr, tmpErrMsg := handleDate(cell.DataTimeType, cell.Value)
+					if tmpErr != nil {
+						err = tmpErr
+						errMsg = tmpErrMsg
+						return
+					}
+					rowList[rk].DataTime = date
+					rowList[rk].ShowValue = date
+				}
+			}
+		}
+		configList[ck] = rowList
+	}
+
+	newConfig = configList
+
+	return
+}
+
+// HandleDate
+// @Description: 日期处理
+// @author: Roc
+// @datetime2023-10-27 09:37:02
+// @param dataTimeType int
+// @param val string
+// @return date string
+// @return err error
+// @return errMsg string
+func HandleDate(dataTimeType int, val string) (date string, err error, errMsg string) {
+	return handleDate(dataTimeType, val)
+}
+
+// handleDate
+// @Description: 日期处理
+// @author: Roc
+// @datetime2023-10-27 09:36:49
+// @param dataTimeType int
+// @param val string
+// @return date string
+// @return err error
+// @return errMsg string
+func handleDate(dataTimeType int, val string) (date string, err error, errMsg string) {
+	if val == `` {
+		errMsg = "错误的日期数据"
+		err = errors.New(errMsg)
+		return
+	}
+	switch dataTimeType {
+	case request.CustomDateT: //手动输入日期
+		date = val
+	case request.SystemDateT: // 系统日期
+		date, err, errMsg = handleSystemDateT(val)
+	case request.EdbDateDT: // 导入指标日期(指标库的最新日期)
+	default:
+		errMsg = "错误的日期类型"
+		err = errors.New(errMsg)
+		return
+	}
+
+	return
+}
+
+// handleSystemDateT
+// @Description: 处理导入系统日期
+// @author: Roc
+// @datetime2023-10-27 09:36:21
+// @param confStr string
+// @return date string
+// @return err error
+// @return errMsg string
+func handleSystemDateT(confStr string) (date string, err error, errMsg string) {
+	var config request.SystemDateConf
+	err = json.Unmarshal([]byte(confStr), &config)
+	if err != nil {
+		return
+	}
+	switch config.Source {
+	case request.SystemCurrDateT:
+		date = time.Now().Format(utils.FormatDate)
+	case request.SystemCalculateDateT:
+		date, err, errMsg = handleSystemCalculateDateT(config.CalculateNum, config.CalculateFrequency)
+	case request.SystemFrequencyDateT: // 处理系统日期相关的指定频率(所在周/旬/月/季/半年/年的最后/最早一天)
+		date, err, errMsg = handleSystemAppointDateT(config.Day, config.Frequency)
+	default:
+		errMsg = "错误的日期日期导入方式"
+		err = errors.New(fmt.Sprint("错误的日期日期导入方式:", config.Source))
+		return
+	}
+
+	return
+}
+
+// handleSystemCalculateDateT
+// @Description: 处理系统日期计算后的日期
+// @author: Roc
+// @datetime2023-10-27 09:31:22
+// @param num int
+// @param frequency string
+// @return date string
+// @return err error
+// @return errMsg string
+func handleSystemCalculateDateT(num int, frequency string) (date string, err error, errMsg string) {
+	if err != nil {
+		return
+	}
+	currDate := time.Now()
+	switch frequency {
+	case "", "日":
+		date = currDate.AddDate(0, 0, num).Format(utils.FormatDate)
+	default:
+		errMsg = "错误的日期频度:" + frequency
+		err = errors.New(errMsg)
+		return
+	}
+
+	return
+}
+
+// handleSystemAppointDateT
+// @Description: 处理系统日期相关的指定频率(所在周/旬/月/季/半年/年的最后/最早一天)
+// @author: Roc
+// @datetime2023-10-27 09:31:35
+// @param Frequency string
+// @param Day string
+// @return date string
+// @return err error
+// @return errMsg string
+func handleSystemAppointDateT(appointDay, frequency string) (date string, err error, errMsg string) {
+	currDate := time.Now()
+	switch frequency {
+	case "本周":
+		day := int(currDate.Weekday())
+		if day == 0 { // 周日
+			day = 7
+		}
+		num := 0
+		switch appointDay {
+		case "周一":
+			num = 1
+		case "周二":
+			num = 2
+		case "周三":
+			num = 3
+		case "周四":
+			num = 4
+		case "周五":
+			num = 5
+		case "周六":
+			num = 6
+		case "周日":
+			num = 7
+		}
+		day = num - day
+		date = currDate.AddDate(0, 0, day).Format(utils.FormatDate)
+	case "本旬":
+		day := currDate.Day()
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			if day <= 10 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, currDate.Location())
+			} else if day <= 20 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 11, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 21, 0, 0, 0, 0, currDate.Location())
+			}
+		case "最后一天":
+			if day <= 10 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 10, 0, 0, 0, 0, currDate.Location())
+			} else if day <= 20 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 20, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), currDate.Month()+1, 1, 0, 0, 0, 0, currDate.Location()).AddDate(0, 0, -1)
+			}
+		}
+		date = tmpDate.Format(utils.FormatDate)
+	case "本月":
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			tmpDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, currDate.Location())
+		case "最后一天":
+			tmpDate = time.Date(currDate.Year(), currDate.Month()+1, 1, 0, 0, 0, 0, currDate.Location()).AddDate(0, 0, -1)
+		}
+		date = tmpDate.Format(utils.FormatDate)
+	case "本季":
+		month := currDate.Month()
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			if month <= 3 {
+				tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 4, 1, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 9 {
+				tmpDate = time.Date(currDate.Year(), 7, 1, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 10, 1, 0, 0, 0, 0, currDate.Location())
+			}
+		case "最后一天":
+			if month <= 3 {
+				tmpDate = time.Date(currDate.Year(), 3, 31, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 9 {
+				tmpDate = time.Date(currDate.Year(), 9, 30, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location())
+			}
+		}
+		date = tmpDate.Format(utils.FormatDate)
+	default:
+		errMsg = "错误的日期频度:" + frequency
+		err = errors.New(errMsg)
+		return
+	}
+
+	return
+}
+
+// calculateByCellList
+// @Description: 根据单元格来进行公式计算
+// @author: Roc
+// @datetime2023-11-14 16:17:38
+// @param calculateFormula string
+// @param tagList []utils.CellPosition
+// @return calVal string
+// @return errMsg string
+// @return err error
+func calculateByCellList(calculateFormula string, tagList []utils.CellPosition) (calVal float64, errMsg string, err error) {
+	if calculateFormula == "" {
+		errMsg = "公式异常"
+		err = errors.New(errMsg)
+		return
+	}
+
+	calculateFormula = strings.TrimPrefix(calculateFormula, "=")
+	calculateFormula = strings.Replace(calculateFormula, "(", "(", -1)
+	calculateFormula = strings.Replace(calculateFormula, ")", ")", -1)
+	calculateFormula = strings.Replace(calculateFormula, ",", ",", -1)
+	calculateFormula = strings.Replace(calculateFormula, "。", ".", -1)
+	calculateFormula = strings.Replace(calculateFormula, "%", "*0.01", -1)
+
+	rowList := make([]int, 0)
+	rowListMap := make(map[int][]utils.CellPosition)
+	for _, v := range tagList {
+		tmpRowList, ok := rowListMap[v.Row]
+		if !ok {
+			rowList = append(rowList, v.Row)
+			tmpRowList = make([]utils.CellPosition, 0)
+		}
+		tmpRowList = append(tmpRowList, v)
+		rowListMap[v.Row] = tmpRowList
+	}
+
+	sort.Ints(rowList)
+
+	list := make([]utils.CellPosition, 0)
+	for _, row := range rowList {
+		list = append(list, rowListMap[row]...)
+	}
+
+	formulaFormStr := utils.ReplaceFormulaByCellList(list, calculateFormula)
+	//计算公式异常,那么就移除该指标
+	if formulaFormStr == `` {
+		errMsg = "公式异常"
+		err = errors.New(errMsg)
+		return
+	}
+
+	expression := formula.NewExpression(formulaFormStr)
+	calResult, err := expression.Evaluate()
+	if err != nil {
+		errMsg = "计算失败"
+		err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+		// 分母为0的报错
+		if strings.Contains(err.Error(), "divide by zero") {
+			errMsg = "分母不能为0"
+			err = errors.New("分母不能为空,计算公式:" + formulaFormStr)
+		}
+		return
+	}
+	// 如果计算结果是NAN,那么就提示报错
+	if calResult.IsNan() {
+		errMsg = "计算失败"
+		err = errors.New("计算失败:计算结果是:NAN;formulaStr:" + formulaFormStr)
+		return
+	}
+	calVal, err = calResult.Float64()
+	if err != nil {
+		return
+	}
+
+	// 转Decimal然后四舍五入
+	calVal, _ = decimal.NewFromFloat(calVal).Round(4).Float64()
+
+	return
+}

+ 3 - 4
services/excel/lucky_sheet.go

@@ -106,10 +106,9 @@ type LuckySheetData struct {
 	ChWidth  int64                `json:"ch_width" description:"工作表区域的宽度"`
 	ChWidth  int64                `json:"ch_width" description:"工作表区域的宽度"`
 	Config   LuckySheetDataConfig `json:"config" description:""`
 	Config   LuckySheetDataConfig `json:"config" description:""`
 	//Index             int                  `json:"index" description:"工作表索引"`
 	//Index             int                  `json:"index" description:"工作表索引"`
-	RhHeight   float64 `json:"rh_height" description:"工作表区域的高度"`
-	ScrollLeft float64 `json:"scrollLeft" description:"左右滚动条位置"`
-	ScrollTop  float64 `json:"scrol
-lTop" description:"上下滚动条位置"`
+	RhHeight          float64     `json:"rh_height" description:"工作表区域的高度"`
+	ScrollLeft        float64     `json:"scrollLeft" description:"左右滚动条位置"`
+	ScrollTop         float64     `json:"scrollTop" description:"上下滚动条位置"`
 	Status            interface{} `json:"status" description:"激活状态"`
 	Status            interface{} `json:"status" description:"激活状态"`
 	VisibleDataColumn []int64     `json:"visibledatacolumn" description:"所有列的位置信息,递增的列位置数据,初始化无需设置"`
 	VisibleDataColumn []int64     `json:"visibledatacolumn" description:"所有列的位置信息,递增的列位置数据,初始化无需设置"`
 	VisibleDataRow    []int64     `json:"visibledatarow" description:"所有行的位置信息,递增的行位置数据,初始化无需设置"`
 	VisibleDataRow    []int64     `json:"visibledatarow" description:"所有行的位置信息,递增的行位置数据,初始化无需设置"`

+ 33 - 0
utils/calculate.go

@@ -188,6 +188,39 @@ func ReplaceFormula(valArr map[string]float64, formulaStr string) string {
 	return formulaStr
 	return formulaStr
 }
 }
 
 
+// CellPosition
+// @Description: 单元格位置
+type CellPosition struct {
+	Tag   string
+	Row   int
+	Value float64
+}
+
+// ReplaceFormulaByCellList
+// @Description: 根据单元格列表替换
+// @author: Roc
+// @datetime2023-11-14 16:16:12
+// @param cellList []CellPosition
+// @param formulaStr string
+// @return string
+func ReplaceFormulaByCellList(cellList []CellPosition, formulaStr string) string {
+	funMap := getFormulaMap()
+	for k, v := range funMap {
+		formulaStr = strings.Replace(formulaStr, k, v, -1)
+	}
+
+	replaceCount := 0
+	for _, cell := range cellList {
+		dvStr := fmt.Sprintf("%v", cell.Value)
+		formulaStr = strings.Replace(formulaStr, fmt.Sprint(cell.Tag, cell.Row), dvStr, -1)
+		replaceCount++
+	}
+	for k, v := range funMap {
+		formulaStr = strings.Replace(formulaStr, v, k, -1)
+	}
+	return formulaStr
+}
+
 func getFormulaMap() map[string]string {
 func getFormulaMap() map[string]string {
 	funMap := make(map[string]string)
 	funMap := make(map[string]string)
 	funMap["MAX"] = "[@@]"
 	funMap["MAX"] = "[@@]"

+ 8 - 0
utils/constants.go

@@ -135,6 +135,14 @@ const (
 	CHART_SOURCE_LINE_FEATURE_FREQUENCY          = 9 // 统计特征-频率分布图表
 	CHART_SOURCE_LINE_FEATURE_FREQUENCY          = 9 // 统计特征-频率分布图表
 )
 )
 
 
+// ETA表格
+const (
+	EXCEL_DEFAULT         = 1 // 自定义excel
+	TIME_TABLE            = 2 // 时间序列表格
+	MIXED_TABLE           = 3 // 混合表格
+	CUSTOM_ANALYSIS_TABLE = 4 // 自定义分析表格
+)
+
 // 图表样式类型
 // 图表样式类型
 const (
 const (
 	CHART_TYPE_CURVE           = 1  //曲线图
 	CHART_TYPE_CURVE           = 1  //曲线图