xyxie пре 1 година
родитељ
комит
439c542bd3
4 измењених фајлова са 203 додато и 18 уклоњено
  1. 16 16
      models/data_manage/excel/request/mixed_table.go
  2. 147 1
      services/data/excel/mixed_table.go
  3. 18 0
      utils/calculate.go
  4. 22 1
      utils/common.go

+ 16 - 16
models/data_manage/excel/request/mixed_table.go

@@ -2,14 +2,14 @@ package request
 
 // 单元格的数据类型
 const (
-	DateDT                       = iota + 1 //日期
-	EdbDT                                   // 2 指标类型
-	CustomTextDT                            // 3 自定义文本
-	InsertDataDT                            // 4 插值(插入指标值,表格上,自动判断日期和指标的交集位置,插入值)
-	PopInsertDataDT                         // 5 弹框插值(在表格上选择日期,然后空白单元格选择弹框并选择指标,插入该指标与该日期的值)
-	FormulateCalculateDataDT                // 6 公式计算(A+B这种)
-	InsertEdbCalculateDataDT                // 7 插入指标系统计算公式生成的值
-	FormulateCalculateDateDataDT            // 8 日期计算
+	DateDT                   = iota + 1 //日期
+	EdbDT                               // 2 指标类型
+	CustomTextDT                        // 3 自定义文本
+	InsertDataDT                        // 4 插值(插入指标值,表格上,自动判断日期和指标的交集位置,插入值)
+	PopInsertDataDT                     // 5 弹框插值(在表格上选择日期,然后空白单元格选择弹框并选择指标,插入该指标与该日期的值)
+	FormulateCalculateDataDT            // 6 公式计算(A+B这种)
+	InsertEdbCalculateDataDT            // 7 插入指标系统计算公式生成的值
+	DateCalculateDataDT                 // 8 日期计算
 )
 
 // 单元格的日期类型类型
@@ -102,16 +102,16 @@ type EdbDateConfFrequencyChange struct {
 	FrequencyDay string `description:"频度的固定日期"`
 }
 
-// DateCalculate 混合表格中的日期计算
-type DateCalculateExtra struct {
-	Formula string                          `description:"计算公式"`
-	CellArr []DateCalculateExtraCellFromTag `description:"表格中的单元格"`
+// MixDateCalculateConf 混合表格中的日期计算
+type MixDateCalculateConf struct {
+	Formula          string                `description:"计算公式"`
+	RelationCellList []MixDateCalculateTag `description:"表格中的单元格"`
 }
 
-// DateCalculateExtraCellFromTag 日期计算的关联单元格
-type DateCalculateExtraCellFromTag struct {
-	Uid     string `description:"单元格唯一值"`
-	FromTag string `description:"指标对应标签"`
+// MixDateCalculateTag 混合表格中的日期计算的关联单元格
+type MixDateCalculateTag struct {
+	Uid string `description:"单元格唯一值"`
+	Tag string `description:"指标对应标签"`
 }
 
 // CalculateConf

+ 147 - 1
services/data/excel/mixed_table.go

@@ -151,11 +151,17 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 	// 基础计算单元格的位置信息
 	calculateCellMap := make(map[string]Cell)
 	calculateChainList := make([]string, 0)
-
+	dateCalculateList := make([]string, 0)
 	// 处理单元格中的数据类型(除去基础计算,因为这个是依赖于其他)
 	for k, row := range config {
 		for i, cell := range row {
 			switch cell.DataType {
+			case request.DateDT: // 日期类型
+				calculateCellMap[cell.Uid] = Cell{
+					Column:   k,
+					Row:      i,
+					CellInfo: cell,
+				}
 			case request.EdbDT: // 指标类型
 				if edbInfo, ok := edbInfoMap[cell.EdbInfoId]; ok {
 					cell.ShowValue = edbInfo.EdbName
@@ -380,6 +386,15 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 				val := tmpDataMap[cellDateTime]
 				cellKeyVal[cell.Uid] = val
 				cell.ShowValue = utils.FormatTableDataShowValue(val)
+			case request.DateCalculateDataDT: //日期计算
+				// 把关联的单元格存在数组里
+				calculateCellMap[cell.Uid] = Cell{
+					Column:   k,
+					Row:      i,
+					CellInfo: cell,
+				}
+				dateCalculateList = append(dateCalculateList, cell.Uid)
+				// 遍历数组,根据公式进行计算,并将得到的结果放到对应的单元格中
 			}
 
 			row[i] = cell
@@ -419,6 +434,11 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 		}
 	}
 
+	// 日期计算
+	config, err, errMsg = handlerDateCalculate(dateCalculateList, calculateCellMap, config)
+	if err != nil {
+		return
+	}
 	newMixedTableCellDataList = config
 
 	return
@@ -1008,3 +1028,129 @@ func calculateByCellList(calculateFormula string, tagList []utils.CellPosition)
 
 	return
 }
+
+// handlerDateCalculate 处理日期计算
+func handlerDateCalculate(dateCalculateList []string, calculateCellMap map[string]Cell, oldConfig [][]request.MixedTableCellDataReq) (config [][]request.MixedTableCellDataReq, err error, errMsg string) {
+	config = oldConfig
+	if len(dateCalculateList) == 0 {
+		return
+	}
+	if len(dateCalculateList) > 0 {
+		for _, cellKey := range dateCalculateList {
+			// 查找这个单元格的位置,直接map找了,而不是遍历整个单元格
+			cellPosition, ok := calculateCellMap[cellKey]
+			if !ok {
+				utils.FileLog.Error("找不到单元格位置:", cellKey)
+				continue
+			}
+
+			cell := config[cellPosition.Column][cellPosition.Row]
+			if cell.DataType != request.DateCalculateDataDT { // 判断公式计算(A+B这种)类型,不是的话也过滤了
+				continue
+			}
+
+			val, tmpErr, tmpErrMsg := dateCalculate(calculateCellMap, cell.Value)
+			if tmpErr != nil {
+				errMsg = tmpErrMsg
+				err = tmpErr
+				return
+			}
+
+			cell.ShowValue = utils.FormatTableDataShowValue(val)
+			config[cellPosition.Column][cellPosition.Row] = cell
+		}
+	}
+	return
+}
+
+func dateCalculate(calculateCellMap map[string]Cell, config string) (val float64, err error, errMsg string) {
+	var edbDateConf request.MixDateCalculateConf
+	err = json.Unmarshal([]byte(config), &edbDateConf)
+	if err != nil {
+		err = fmt.Errorf("日期计算配置json解析失败失败: %s, Err:%s", config, err.Error())
+		return
+	}
+	if len(edbDateConf.RelationCellList) == 0 {
+		err = fmt.Errorf("日期计算 未配置日期单元格失败: %s", config)
+		return
+	}
+	valMap := make(map[string]int)
+	for _, v := range edbDateConf.RelationCellList {
+		// 查找单元格数据
+		cell, ok := calculateCellMap[v.Uid]
+		if !ok {
+			err = fmt.Errorf("查找单元格:%s 的数据失败", v.Uid)
+			return
+		}
+		colData := cell.CellInfo
+
+		// 如果不是基础计算单元格,直接返回
+		_, err = time.ParseInLocation(utils.FormatDate, colData.ShowValue, time.Local)
+		if err != nil {
+			err = fmt.Errorf("%s 的单元格非日期类型, Err: %s", colData.ShowValue, err.Error())
+			return
+		}
+		// todo 把日期转换成excel里的天数
+		realDiffDay := utils.GetDaysDiff1900(colData.ShowValue)
+
+		valMap[strings.ToUpper(v.Tag)] = realDiffDay
+	}
+
+	// 计算
+	val, errMsg, err = dateFormulaCalculate(valMap, edbDateConf.Formula)
+	if err != nil {
+		return
+	}
+	return
+}
+
+func dateFormulaCalculate(valTagMap map[string]int, calculateFormula string) (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)
+
+	formulaFormStr := utils.ReplaceFormulaByTagMap(valTagMap, calculateFormula)
+
+	if formulaFormStr == `` {
+		errMsg = "公式异常"
+		err = errors.New(errMsg)
+		return
+	}
+	fmt.Println("公式:" + formulaFormStr)
+	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
+}

+ 18 - 0
utils/calculate.go

@@ -221,6 +221,24 @@ func ReplaceFormulaByCellList(cellList []CellPosition, formulaStr string) string
 	return formulaStr
 }
 
+func ReplaceFormulaByTagMap(valTagMap map[string]int, formulaStr string) string {
+	funMap := getFormulaMap()
+	for k, v := range funMap {
+		formulaStr = strings.Replace(formulaStr, k, v, -1)
+	}
+
+	replaceCount := 0
+	for tag, val := range valTagMap {
+		dvStr := fmt.Sprintf("%v", val)
+		formulaStr = strings.Replace(formulaStr, tag, dvStr, -1)
+		replaceCount++
+	}
+	for k, v := range funMap {
+		formulaStr = strings.Replace(formulaStr, v, k, -1)
+	}
+	return formulaStr
+}
+
 func getFormulaMap() map[string]string {
 	funMap := make(map[string]string)
 	funMap["MAX"] = "[@@]"

+ 22 - 1
utils/common.go

@@ -637,6 +637,27 @@ func ConvertToFormatDay(excelDaysString string) string {
 	return resultTime
 }
 
+// GetDaysDiff1900 计算日期距离1900-01-01的天数
+func GetDaysDiff1900(date string) int {
+	// 将字符串转换为时间类型
+	//从1899年12月30日开始是因为Excel的日期计算是基于1900年1月1日的,而1900年并不是闰年,因此Excel日期计算存在一个错误。为了修正这个错误,
+	//我们将时间回溯到1899年12月30日,这样在进行日期计算时可以正确地处理Excel日期。
+	tStart, err := time.ParseInLocation(FormatDate, "1899-12-30", time.Local)
+	if err != nil {
+		return 0
+	}
+	tEnd, err := time.ParseInLocation(FormatDate, date, time.Local)
+	if err != nil {
+		return 0
+	}
+
+	// 计算两个日期之间的天数差值
+	duration := tEnd.Sub(tStart).Hours() / 24
+	days := int(duration)
+
+	return days
+}
+
 func CheckPwd(pwd string) bool {
 	compile := `([0-9a-z]+){6,12}|(a-z0-9]+){6,12}`
 	reg := regexp.MustCompile(compile)
@@ -2255,4 +2276,4 @@ func ArrContainsStr(arr []string, str string) bool {
 		}
 	}
 	return false
-}
+}