|
@@ -8,12 +8,40 @@ import (
|
|
|
"eta/eta_api/services/data"
|
|
|
"eta/eta_api/utils"
|
|
|
"fmt"
|
|
|
+ "github.com/shopspring/decimal"
|
|
|
+ "github.com/yidane/formula"
|
|
|
+ "sort"
|
|
|
+ "strconv"
|
|
|
"strings"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
+// BaseCalculate
|
|
|
+// @Description: 指标数据计算请求
|
|
|
+type BaseCalculate struct {
|
|
|
+ DataList []*data_manage.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:"对应的单元格信息"`
|
|
|
+}
|
|
|
+
|
|
|
// GetMixedTableCellData 获取混合表格数据
|
|
|
-func GetMixedTableCellData(cellRelationConf string, config [][]request.MixedTableCellDataReq) (newMixedTableCellDataList [][]request.MixedTableCellDataReq, err error, errMsg string) {
|
|
|
+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)
|
|
@@ -98,20 +126,17 @@ func GetMixedTableCellData(cellRelationConf string, config [][]request.MixedTabl
|
|
|
config[k] = row
|
|
|
}
|
|
|
|
|
|
- type BaseCalculate struct {
|
|
|
- DataList []*data_manage.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:日均值"`
|
|
|
- }
|
|
|
-
|
|
|
// 指标计算的结果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 {
|
|
@@ -125,6 +150,7 @@ func GetMixedTableCellData(cellRelationConf string, config [][]request.MixedTabl
|
|
|
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)
|
|
|
}
|
|
|
}
|
|
@@ -145,11 +171,37 @@ func GetMixedTableCellData(cellRelationConf string, config [][]request.MixedTabl
|
|
|
}
|
|
|
if val, ok2 := tmpDateValMap[cell.DataTime]; ok2 {
|
|
|
//cell.ShowValue = fmt.Sprint(val)
|
|
|
+ cellKeyVal[cell.Uid] = val
|
|
|
cell.ShowValue = utils.FormatTableDataShowValue(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,
|
|
|
+ }
|
|
|
|
|
|
- case request.InsertEdbCalculateDataDT: // 指标类型
|
|
|
+ calculateChainList = append(calculateChainList, cell.Uid)
|
|
|
+
|
|
|
+ case request.InsertEdbCalculateDataDT: // 插入指标系统计算公式生成的值
|
|
|
// 日期
|
|
|
var cellDateTime string
|
|
|
// 日期关系配置不存在,则默认最新数据
|
|
@@ -228,6 +280,7 @@ func GetMixedTableCellData(cellRelationConf string, config [][]request.MixedTabl
|
|
|
}
|
|
|
|
|
|
val := tmpDataMap[cellDateTime]
|
|
|
+ cellKeyVal[cell.Uid] = val
|
|
|
cell.ShowValue = utils.FormatTableDataShowValue(val)
|
|
|
}
|
|
|
|
|
@@ -236,11 +289,109 @@ func GetMixedTableCellData(cellRelationConf string, config [][]request.MixedTabl
|
|
|
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)
|
|
|
+ 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
|
|
@@ -499,3 +650,82 @@ func handleSystemAppointDateT(appointDay, frequency string) (date string, err er
|
|
|
|
|
|
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
|
|
|
+}
|