Przeglądaj źródła

Merge branch 'feature/eta1.6.5_edb_calculate' of eta_server/eta_index_lib into master

xyxie 1 rok temu
rodzic
commit
131369f0da

+ 260 - 144
controllers/base_from_calculate.go

@@ -10,6 +10,7 @@ import (
 	"fmt"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 )
 
@@ -68,13 +69,6 @@ func (this *CalculateController) Add() {
 		br.Msg = "请填写指标"
 		return
 	}
-	calculateFormula := req.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)
-	req.CalculateFormula = calculateFormula
 
 	//加入缓存机制,避免创建同一个名称的指标 start
 	redisKey := fmt.Sprint("edb_lib:edb_info:calculate:add:", utils.DATA_SOURCE_CALCULATE, ":", req.EdbName)
@@ -90,140 +84,20 @@ func (this *CalculateController) Add() {
 		}()
 	}
 
-	//判断是否重复指标
-	edbInfoMap := make(map[int]string)
-	//移除研究员选择指标中的未使用的指标
-	{
-		//转大写的计算公式
-		upperCalculateFormulaStr := strings.ToUpper(req.CalculateFormula)
-		//用到的指标
-		newEdbInfoIdArr := make([]models.EdbInfoFromTag, 0)
-		for _, tmpEdbInfo := range req.EdbInfoIdArr {
-			_, ok := edbInfoMap[tmpEdbInfo.EdbInfoId]
-			if ok {
-				br.Msg = "选择指标失败,请勿选择重复指标!"
-				return
-			}
-			edbInfoMap[tmpEdbInfo.EdbInfoId] = tmpEdbInfo.FromTag
-
-			upperFromTag := strings.ToUpper(tmpEdbInfo.FromTag)
-			if strings.Contains(upperCalculateFormulaStr, upperFromTag) {
-				newEdbInfoIdArr = append(newEdbInfoIdArr, tmpEdbInfo)
-			}
-		}
-		req.EdbInfoIdArr = newEdbInfoIdArr
-	}
-
-	var condition string
-	var pars []interface{}
-	condition += " AND edb_name=? "
-	pars = append(pars, req.EdbName)
-
-	count, err := models.GetEdbInfoCountByCondition(condition, pars)
-	if err != nil {
-		br.Msg = "判断指标名称是否存在失败"
-		br.ErrMsg = "判断指标名称是否存在失败,Err:" + err.Error()
-		return
-	}
-
-	if count > 0 {
-		br.Msg = "指标名称已存在,请重新填写"
-		br.ErrMsg = "指标名称已存在,请重新填写"
-		br.IsSendEmail = false
-		return
-	}
-	//检验公式
-	var formulaStr string
-	var edbInfoIdBytes []string
-	for _, v := range req.EdbInfoIdArr {
-		formulaStr += v.FromTag + ","
-		edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
-	}
-	formulaSlice, err := utils.CheckFormulaJson(req.CalculateFormula)
+	// 新增
+	edbInfo, uniqueCode, err, errMsg := services.EdbCalculateAdd(req)
 	if err != nil {
-		br.Msg = "公式格式错误,请重新填写"
+		br.Msg = err.Error()
+		br.ErrMsg = errMsg
 		return
 	}
-	for _, formula := range formulaSlice {
-		formulaMap := utils.CheckFormula(formula)
-		for _, v := range formulaMap {
-			if !strings.Contains(formulaStr, v) {
-				br.Msg = "公式错误,请重新填写"
-				return
-			}
-		}
-	}
 
-	//关联的指标信息
-	edbInfoList := make([]*models.EdbInfo, 0)
-	//关联的指标数据表
-	calculateMappingList := make([]*models.EdbInfoCalculateMapping, 0)
-	for k, v := range req.EdbInfoIdArr {
-		fromEdbInfo, err := models.GetEdbInfoById(v.EdbInfoId)
-		if err != nil {
-			if err.Error() == utils.ErrNoRow() {
-				br.Msg = "生成计算指标失败"
-				br.Msg = "指标 " + strconv.Itoa(v.EdbInfoId) + " 不存在"
-				return
-			}
-			br.Msg = "生成计算指标失败"
-			br.Msg = "获取指标失败:Err:" + err.Error()
-			return
-		}
-
-		edbInfoList = append(edbInfoList, fromEdbInfo)
-		//关联关系表
-		{
-			calculateMappingItem := &models.EdbInfoCalculateMapping{
-				EdbInfoCalculateMappingId: 0,
-				EdbInfoId:                 0,
-				Source:                    utils.DATA_SOURCE_CALCULATE,
-				SourceName:                "指标运算",
-				EdbCode:                   "",
-				FromEdbInfoId:             fromEdbInfo.EdbInfoId,
-				FromEdbCode:               fromEdbInfo.EdbCode,
-				FromEdbName:               fromEdbInfo.EdbName,
-				FromSource:                fromEdbInfo.Source,
-				FromSourceName:            fromEdbInfo.SourceName,
-				FromTag:                   v.FromTag,
-				Sort:                      k + 1,
-				CreateTime:                time.Now(),
-				ModifyTime:                time.Now(),
-			}
-			//calculateMappingItem.EdbCode = edbCode
-			//calculateMappingItem.EdbInfoId = int(edbInfoId)
-			calculateMappingList = append(calculateMappingList, calculateMappingItem)
-		}
-	}
-
-	for _, v := range formulaSlice {
-		formulaMap := utils.CheckFormula(v)
-		//预先计算,判断公式是否正常
-		ok, _ := models.CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
-		if !ok {
-			br.Msg = "生成计算指标失败,请使用正确的计算公式"
-			return
-		}
-	}
-
-	randStr := utils.GetRandDigit(4)
-	edbCode := `C` + time.Now().Format("060102") + randStr
-	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
-	uniqueCode := utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
-
-	// 新增计算指标
-	edbInfo, err := models.AddCalculateInfo(req, calculateMappingList, edbInfoList, edbCode, uniqueCode, edbInfoIdBytes)
-	if err != nil {
-		br.Msg = "生成计算指标失败"
-		br.Msg = "生成计算指标失败,AddEdbInfo Err:" + err.Error()
-		return
-	}
 	//处理同名指标
 	{
-		edbNameList, err := models.GetEdbInfoByName(req.EdbName)
-		if err != nil {
-			br.Msg = "保存失败"
-			br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+		edbNameList, e := models.GetEdbInfoByName(req.EdbName)
+		if e != nil {
+			err = fmt.Errorf("保存失败")
+			errMsg = "获取指标信息失败,Err:" + e.Error()
 			return
 		}
 		if len(edbNameList) >= 2 {
@@ -231,8 +105,8 @@ func (this *CalculateController) Add() {
 				edbName := v.EdbName + "(" + v.SourceName + ")"
 				err = models.ModifyEdbInfoNameSource(edbName, v.EdbInfoId)
 				if err != nil {
-					br.Msg = "保存失败"
-					br.ErrMsg = "修改指标名称失败,Err:" + err.Error()
+					errMsg = "修改指标名称失败,Err:" + err.Error()
+					err = fmt.Errorf("保存失败")
 					return
 				}
 			}
@@ -240,10 +114,8 @@ func (this *CalculateController) Add() {
 	}
 
 	// 更新指标最大最小值
-	err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	err, errMsg = models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
 	if err != nil {
-		br.Msg = errMsg
-		br.ErrMsg = err.Error()
 		return
 	}
 	resp := models.AddEdbInfoResp{
@@ -315,6 +187,7 @@ func (this *CalculateController) Edit() {
 	}
 
 	//todo 校验空值类型,max、min空值类型
+	//todo req.EdbInfoIdArr根据tag排序,排成A,B,AB,这种格式
 
 	calculateFormula := req.CalculateFormula
 	calculateFormula = strings.Replace(calculateFormula, "(", "(", -1)
@@ -385,7 +258,7 @@ func (this *CalculateController) Edit() {
 	}
 	var needCalculate bool
 
-	if edbInfoDetail.CalculateFormula != req.CalculateFormula || edbInfoDetail.EmptyType != req.EmptyType || edbInfoDetail.MaxEmptyType != req.MaxEmptyType {
+	if edbInfoDetail.CalculateFormula != req.CalculateFormula || edbInfoDetail.EmptyType != req.EmptyType || edbInfoDetail.MaxEmptyType != req.MaxEmptyType || edbInfoDetail.Extra != req.Extra {
 		needCalculate = true
 	}
 
@@ -423,7 +296,11 @@ func (this *CalculateController) Edit() {
 		return
 	}
 	for _, formula := range formulaSlice {
-		formulaMap := utils.CheckFormula(formula)
+		formulaMap, e := utils.CheckFormula(formula)
+		if e != nil {
+			br.Msg = "公式错误,请重新填写"
+			return
+		}
 		for _, v := range formulaMap {
 			if !strings.Contains(formulaStr, v) {
 				br.Msg = "公式错误,请重新填写"
@@ -1423,14 +1300,15 @@ func (this *CalculateController) Refresh() {
 		}
 		var formulaStr string
 		edbInfoList := make([]*models.EdbInfo, 0)
-
+		edbInfoTag := make(map[string]int)
 		for _, v := range calculateMap {
+			edbInfoTag[v.FromTag] = v.FromEdbInfoId
 			formulaStr += v.FromTag + ","
 			edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
 			edbInfo, _ := models.GetEdbInfoById(v.FromEdbInfoId)
 			edbInfoList = append(edbInfoList, edbInfo)
 		}
-		err = models.RefreshAllCalculate(edbInfoList, edbInfo.EdbInfoId, source, subSource, edbInfo.EdbCode, edbInfo.CalculateFormula, startDate, endDate, edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType)
+		err = models.RefreshAllCalculate(edbInfoList, edbInfoTag, edbInfo.EdbInfoId, source, subSource, edbInfo.EdbCode, edbInfo.CalculateFormula, startDate, endDate, edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType, edbInfo.Extra)
 		if err != nil && err.Error() != utils.ErrNoRow() {
 			errMsg = "RefreshCalculate Err:" + err.Error()
 			break
@@ -2185,3 +2063,241 @@ func (this *CalculateController) Calculate() {
 	}
 	br.IsAddLog = true
 }
+
+// BatchSaveMulti
+// @Title 批量计算 累计值转月-同比值-同差等计算新增
+// @Description 批量计算 累计值转月-同比值-同差等计算新增接口
+// @Param request body models.EdbInfoCalculateBatchSaveReq true "type json string"
+// @Success Ret=200 返回指标id
+// @router /batch/save/multi [post]
+func (this *CalculateController) BatchSaveMulti() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req models.CalculateBatchSaveReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	resp := models.BatchEdbInfoCalculateBatchSaveResp{
+		Fail:    make([]models.BatchEdbInfoCalculateBatchSaveFailResp, 0),
+		Success: make([]models.BatchEdbInfoCalculateBatchSaveSuccessResp, 0),
+	}
+
+	redisKeyList := make([]string, 0) //需要清理的缓存key列表
+	defer func() {
+		for _, redisKey := range redisKeyList {
+			utils.Rc.Delete(redisKey)
+		}
+	}()
+	var wg sync.WaitGroup
+	// 校验参数
+	for _, item := range req.EdbList {
+
+		calculateId := item.CalculateId
+		//加入缓存机制,避免创建同一个名称的指标 start
+		redisKey := fmt.Sprint("edb_info:calculate:batch:save:", req.AdminId, ":", req.Source, ":", calculateId)
+		isExist := utils.Rc.IsExist(redisKey)
+		if isExist {
+			resp.Fail = append(resp.Fail, models.BatchEdbInfoCalculateBatchSaveFailResp{
+				CalculateId: calculateId,
+				Msg:         "指标正在处理,请勿重复提交",
+			})
+			continue
+		} else {
+			//设置3分钟缓存
+			utils.Rc.SetNX(redisKey, 1, time.Second*300)
+			redisKeyList = append(redisKeyList, redisKey)
+		}
+		wg.Add(1)
+		reqItem := models.EdbInfoCalculateBatchSaveReq{
+			AdminId:          req.AdminId,
+			AdminName:        req.AdminName,
+			EdbInfoId:        item.EdbInfoId,
+			EdbName:          item.EdbName,
+			Frequency:        item.Frequency,
+			Unit:             item.Unit,
+			ClassifyId:       item.ClassifyId,
+			Formula:          req.Formula, //N数值移动平均计算、环比值、环差值
+			FromEdbInfoId:    item.FromEdbInfoId,
+			CalculateFormula: req.CalculateFormula,
+			Source:           req.Source,
+			MoveType:         req.MoveType,
+			MoveFrequency:    req.MoveFrequency,
+			EdbInfoIdArr:     req.EdbInfoIdArr,
+			Calendar:         req.Calendar,
+			EmptyType:        req.EmptyType,
+			MaxEmptyType:     req.MaxEmptyType,
+			Extra:            req.Extra,
+		}
+
+		var edbInfo *models.EdbInfo
+		uniqueCode := ""
+		errMsg := ""
+		go func() {
+			defer func() {
+				// todo 批量报错处理
+				if err != nil {
+					// 整理报错的指标
+					utils.FileLog.Info(fmt.Sprintf("批量指标计算,保存失败 Err:%v, ErrMsg:%s", err, errMsg))
+					resp.Fail = append(resp.Fail, models.BatchEdbInfoCalculateBatchSaveFailResp{
+						CalculateId: calculateId,
+						Msg:         err.Error(),
+						ErrMsg:      errMsg,
+					})
+				} else {
+					// 整理成功的指标
+					//todo 整理执行成功指标信息
+					resp.Success = append(resp.Success, models.BatchEdbInfoCalculateBatchSaveSuccessResp{
+						ClassifyId:  reqItem.ClassifyId,
+						CalculateId: calculateId,
+						EdbInfoId:   edbInfo.EdbInfoId,
+						UniqueCode:  uniqueCode,
+					})
+				}
+				wg.Done()
+			}()
+
+			edbInfo, uniqueCode, err, errMsg = services.EdbCalculateBatchSave(reqItem)
+			return
+		}()
+	}
+	wg.Wait()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// BatchEditMulti
+// @Title 批量计算 累计值转月-同比值-同差等计算编辑
+// @Description 批量计算 累计值转月-同比值-同差等计算编辑接口
+// @Param request body models.EdbInfoCalculateBatchSaveReq true "type json string"
+// @Success Ret=200 返回指标id
+// @router /batch/edit/multi [post]
+func (this *CalculateController) BatchEditMulti() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req models.CalculateBatchSaveReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	resp := models.BatchEdbInfoCalculateBatchSaveResp{
+		Fail:    make([]models.BatchEdbInfoCalculateBatchSaveFailResp, 0),
+		Success: make([]models.BatchEdbInfoCalculateBatchSaveSuccessResp, 0),
+	}
+
+	redisKeyList := make([]string, 0) //需要清理的缓存key列表
+	defer func() {
+		for _, redisKey := range redisKeyList {
+			utils.Rc.Delete(redisKey)
+		}
+	}()
+	var wg sync.WaitGroup
+	// 校验参数
+	for _, item := range req.EdbList {
+		calculateId := item.CalculateId
+		if item.EdbInfoId <= 0 {
+			resp.Fail = append(resp.Fail, models.BatchEdbInfoCalculateBatchSaveFailResp{
+				CalculateId: calculateId,
+				Msg:         "请选择要编辑的指标",
+			})
+			continue
+		}
+		//加入缓存机制,避免创建同一个名称的指标 start
+		redisKey := fmt.Sprint("edb_info:calculate:batch:edit:", req.AdminId, ":", req.Source, ":", item.EdbInfoId)
+		isExist := utils.Rc.IsExist(redisKey)
+		if isExist {
+			resp.Fail = append(resp.Fail, models.BatchEdbInfoCalculateBatchSaveFailResp{
+				CalculateId: calculateId,
+				Msg:         "指标正在处理,请勿重复提交",
+			})
+			continue
+		} else {
+			//设置3分钟缓存
+			utils.Rc.SetNX(redisKey, 1, time.Second*300)
+			redisKeyList = append(redisKeyList, redisKey)
+		}
+		wg.Add(1)
+		reqItem := models.EdbInfoCalculateBatchEditReq{
+			EdbInfoId:        item.EdbInfoId,
+			EdbName:          item.EdbName,
+			Frequency:        item.Frequency,
+			Unit:             item.Unit,
+			ClassifyId:       item.ClassifyId,
+			Formula:          req.Formula, //N数值移动平均计算、环比值、环差值
+			FromEdbInfoId:    item.FromEdbInfoId,
+			CalculateFormula: req.CalculateFormula,
+			Source:           req.Source,
+			MoveType:         req.MoveType,
+			MoveFrequency:    req.MoveFrequency,
+			EdbInfoIdArr:     req.EdbInfoIdArr,
+			Calendar:         req.Calendar,
+			EmptyType:        req.EmptyType,
+			MaxEmptyType:     req.MaxEmptyType,
+			Extra:            req.Extra,
+		}
+
+		var errMsg string
+		var edbInfo *models.EdbInfo
+		uniqueCode := ""
+		go func() {
+			defer func() {
+				// todo 批量报错处理
+				if err != nil {
+					// 整理报错的指标
+					utils.FileLog.Info(fmt.Sprintf("批量指标计算,保存失败 Err:%v, ErrMsg:%s", err, errMsg))
+					resp.Fail = append(resp.Fail, models.BatchEdbInfoCalculateBatchSaveFailResp{
+						CalculateId: calculateId,
+						Msg:         err.Error(),
+						ErrMsg:      errMsg,
+					})
+				} else {
+					// 整理成功的指标
+					//todo 整理执行成功指标信息
+					resp.Success = append(resp.Success, models.BatchEdbInfoCalculateBatchSaveSuccessResp{
+						ClassifyId:  reqItem.ClassifyId,
+						CalculateId: calculateId,
+						EdbInfoId:   edbInfo.EdbInfoId,
+						UniqueCode:  uniqueCode,
+					})
+				}
+				wg.Done()
+			}()
+			edbInfo, uniqueCode, err, errMsg = services.EdbCalculateBatchEdit(reqItem)
+			return
+		}()
+	}
+	wg.Wait()
+	if err != nil {
+		br.Msg = "批量指标计算,保存失败"
+		br.ErrMsg = "批量指标计算,保存失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}

+ 20 - 4
controllers/base_from_predict_calculate.go

@@ -112,7 +112,11 @@ func addPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSav
 		return
 	}
 	for _, formula := range formulaSlice {
-		formulaMap := utils.CheckFormula(formula)
+		formulaMap, e := utils.CheckFormula(formula)
+		if e != nil {
+			br.Msg = "公式错误,请重新填写"
+			return
+		}
 		for _, v := range formulaMap {
 			if !strings.Contains(formulaStr, v) {
 				br.Msg = "公式错误,请重新填写"
@@ -181,7 +185,11 @@ func addPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSav
 	}
 
 	for _, v := range formulaSlice {
-		formulaMap := utils.CheckFormula(v)
+		formulaMap, e := utils.CheckFormula(v)
+		if e != nil {
+			br.Msg = "公式错误,请重新填写"
+			return
+		}
 		//预先计算,判断公式是否正常
 		ok, _ := models.CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
 		if !ok {
@@ -352,7 +360,11 @@ func editPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSa
 		return
 	}
 	for _, formula := range formulaSlice {
-		formulaMap := utils.CheckFormula(formula)
+		formulaMap, e := utils.CheckFormula(formula)
+		if e != nil {
+			br.Msg = "公式错误,请重新填写"
+			return
+		}
 		for _, v := range formulaMap {
 			if !strings.Contains(formulaStr, v) {
 				br.Msg = "公式错误,请重新填写"
@@ -427,7 +439,11 @@ func editPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSa
 		}
 	}
 	for _, v := range formulaSlice {
-		formulaMap := utils.CheckFormula(v)
+		formulaMap, e := utils.CheckFormula(v)
+		if e != nil {
+			br.Msg = "公式错误,请重新填写"
+			return
+		}
 		//预先计算,判断公式是否正常
 		ok, _ := models.CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
 		if !ok {

+ 26 - 5
logic/predict_edb.go

@@ -4,6 +4,7 @@ import (
 	"errors"
 	"eta/eta_index_lib/models"
 	"eta/eta_index_lib/utils"
+	"fmt"
 	"strconv"
 	"strings"
 	"time"
@@ -206,7 +207,11 @@ func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName, dataDateType st
 				return
 			}
 			for _, fm := range formulaSlice {
-				formulaMap := utils.CheckFormula(fm)
+				formulaMap, e := utils.CheckFormula(fm)
+				if e != nil {
+					err = fmt.Errorf("公式错误,请重新填写")
+					return
+				}
 				for _, f := range formulaMap {
 					if !strings.Contains(formulaStr, f) {
 						errMsg = "公式错误,请重新填写"
@@ -278,7 +283,11 @@ func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName, dataDateType st
 				trendsMappingList = append(trendsMappingList, tmpPredictEdbConfCalculateMapping)
 			}
 			for _, f := range formulaSlice {
-				formulaMap := utils.CheckFormula(f)
+				formulaMap, e := utils.CheckFormula(f)
+				if e != nil {
+					err = fmt.Errorf("公式错误,请重新填写")
+					return
+				}
 				//预先计算,判断公式是否正常
 				ok, _ := models.CheckFormula2(edbInfoList, formulaMap, f, edbInfoIdBytes)
 				if !ok {
@@ -590,7 +599,11 @@ func EditPredictEdbInfo(edbInfoId, classifyId int, edbName, dataDateType string,
 				return
 			}
 			for _, fm := range formulaSlice {
-				formulaMap := utils.CheckFormula(fm)
+				formulaMap, e := utils.CheckFormula(fm)
+				if e != nil {
+					err = fmt.Errorf("公式错误,请重新填写")
+					return
+				}
 				for _, f := range formulaMap {
 					if !strings.Contains(formulaStr, f) {
 						errMsg = "公式错误,请重新填写"
@@ -663,7 +676,11 @@ func EditPredictEdbInfo(edbInfoId, classifyId int, edbName, dataDateType string,
 			}
 
 			for _, f := range formulaSlice {
-				formulaMap := utils.CheckFormula(f)
+				formulaMap, e := utils.CheckFormula(f)
+				if e != nil {
+					err = fmt.Errorf("公式错误,请重新填写")
+					return
+				}
 				//预先计算,判断公式是否正常
 				ok, _ := models.CheckFormula2(edbInfoList, formulaMap, f, edbInfoIdBytes)
 				if !ok {
@@ -907,7 +924,11 @@ func RefreshPredictEdbInfo(edbInfoId int) (edbInfo *models.EdbInfo, err error, e
 				return
 			}
 			for _, fm := range formulaSlice {
-				formulaMap := utils.CheckFormula(fm)
+				formulaMap, e := utils.CheckFormula(fm)
+				if e != nil {
+					err = fmt.Errorf("公式错误,请重新填写")
+					return
+				}
 				for _, f := range formulaMap {
 					if !strings.Contains(formulaStr, f) {
 						errMsg = "公式错误,请重新填写"

+ 7 - 4
models/base_calculate.go

@@ -1374,20 +1374,23 @@ func (obj BaseCalculate) Ljz() (dateDataMap map[time.Time]float64, err error, er
 			itemDate := item.DataTime
 			dayInt := itemDate.Year()*100 + int(itemDate.Month())
 			var currTime time.Time
-			if itemDate.Month() <= 10 {
-				tmpK := fmt.Sprint(dayInt*100, "10")
+			if itemDate.Day() <= 10 {
+				//本月上旬
+				tmpK := fmt.Sprint(dayInt, "10")
 				currTime, err = time.ParseInLocation(utils.FormatDateUnSpace, tmpK, time.Local)
 				if err != nil {
 					return
 				}
 
-			} else if itemDate.Month() <= 20 {
-				tmpK := fmt.Sprint(dayInt*100, "20")
+			} else if itemDate.Day() <= 20 {
+				// 本月中旬
+				tmpK := fmt.Sprint(dayInt, "20")
 				currTime, err = time.ParseInLocation(utils.FormatDateUnSpace, tmpK, time.Local)
 				if err != nil {
 					return
 				}
 			} else {
+				// 本月下旬
 				currTime, err = time.ParseInLocation(utils.FormatYearMonthUnSpace, fmt.Sprint(dayInt), time.Local)
 				if err != nil {
 					return

+ 138 - 35
models/base_from_calculate.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"encoding/json"
 	"errors"
 	"eta/eta_index_lib/utils"
 	"fmt"
@@ -24,6 +25,7 @@ type EdbInfoCalculateSaveReq struct {
 	CalculateFormula string `description:"计算公式"`
 	EmptyType        int    `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
 	MaxEmptyType     int    `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
+	Extra            string `description:"指标额外配置"`
 	EdbInfoIdArr     []EdbInfoFromTag
 }
 
@@ -90,16 +92,19 @@ func AddCalculateInfo(req EdbInfoCalculateSaveReq, calculateMappingList []*EdbIn
 		ChartImage:       "",
 		EmptyType:        req.EmptyType,
 		MaxEmptyType:     req.MaxEmptyType,
+		Extra:            req.Extra,
 	}
 	lastId, err := to.Insert(edbInfo)
 	if err != nil {
 		return
 	}
 	edbInfo.EdbInfoId = int(lastId)
+	edbInfoTag := make(map[string]int)
 	//关联关系表
 	{
 		//处理关联指标
 		for _, v := range calculateMappingList {
+			edbInfoTag[v.FromTag] = v.FromEdbInfoId
 			v.EdbCode = edbCode
 			v.EdbInfoId = edbInfo.EdbInfoId
 		}
@@ -112,7 +117,7 @@ func AddCalculateInfo(req EdbInfoCalculateSaveReq, calculateMappingList []*EdbIn
 	}
 
 	//计算数据
-	err = refreshAllCalculate(to, edbInfoList, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, edbInfo.CalculateFormula, "", "", edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType)
+	err = refreshAllCalculate(to, edbInfoList, edbInfoTag, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, edbInfo.CalculateFormula, "", "", edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType, edbInfo.Extra)
 	return
 }
 
@@ -142,7 +147,8 @@ func EditCalculateInfo(edbInfo *EdbInfo, req EdbInfoCalculateSaveReq, formulaSli
 	edbInfo.ModifyTime = time.Now()
 	edbInfo.EmptyType = req.EmptyType
 	edbInfo.MaxEmptyType = req.MaxEmptyType
-	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime", "EmptyType", "MaxEmptyType")
+	edbInfo.Extra = req.Extra
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime", "EmptyType", "MaxEmptyType", "Extra")
 	if err != nil {
 		return
 	}
@@ -163,7 +169,9 @@ func EditCalculateInfo(edbInfo *EdbInfo, req EdbInfoCalculateSaveReq, formulaSli
 		edbInfoList := make([]*EdbInfo, 0)
 
 		calculateMappingList := make([]*EdbInfoCalculateMapping, 0)
+		edbInfoTag := make(map[string]int)
 		for k, v := range req.EdbInfoIdArr {
+			edbInfoTag[v.FromTag] = v.EdbInfoId
 			fromEdbInfo, tmpErr := GetEdbInfoById(v.EdbInfoId)
 			if tmpErr != nil {
 				err = tmpErr
@@ -194,7 +202,11 @@ func EditCalculateInfo(edbInfo *EdbInfo, req EdbInfoCalculateSaveReq, formulaSli
 		}
 
 		for _, v := range formulaSlice {
-			formulaMap := utils.CheckFormula(v)
+			formulaMap, e := utils.CheckFormula(v)
+			if e != nil {
+				err = fmt.Errorf("公式错误,请重新填写")
+				return
+			}
 			//预先计算,判断公式是否正常
 			ok, _ := CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
 			if !ok {
@@ -215,7 +227,7 @@ func EditCalculateInfo(edbInfo *EdbInfo, req EdbInfoCalculateSaveReq, formulaSli
 		}
 
 		//计算数据
-		err = refreshAllCalculate(to, edbInfoList, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, edbInfo.CalculateFormula, "", "", edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType)
+		err = refreshAllCalculate(to, edbInfoList, edbInfoTag, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, edbInfo.CalculateFormula, "", "", edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType, edbInfo.Extra)
 	}
 
 	return
@@ -258,7 +270,7 @@ func DeleteCalculateEdbInfo(edbInfoId int) (err error) {
 }
 
 // RefreshAllCalculate 刷新全部数据
-func RefreshAllCalculate(edbInfoIdArr []*EdbInfo, edbInfoId, source, subSource int, edbCode, formulaStr, startDate, endDate string, edbInfoIdBytes []string, emptyType, maxEmptyType int) (err error) {
+func RefreshAllCalculate(edbInfoIdArr []*EdbInfo, edbInfoTag map[string]int, edbInfoId, source, subSource int, edbCode, formulaStr, startDate, endDate string, edbInfoIdBytes []string, emptyType, maxEmptyType int, extra string) (err error) {
 	o := orm.NewOrm()
 	to, err := o.Begin()
 	if err != nil {
@@ -275,18 +287,30 @@ func RefreshAllCalculate(edbInfoIdArr []*EdbInfo, edbInfoId, source, subSource i
 	fmt.Println(startDate, endDate)
 
 	//计算数据
-	err = refreshAllCalculate(to, edbInfoIdArr, edbInfoId, source, subSource, edbCode, formulaStr, startDate, endDate, edbInfoIdBytes, emptyType, maxEmptyType)
+	err = refreshAllCalculate(to, edbInfoIdArr, edbInfoTag, edbInfoId, source, subSource, edbCode, formulaStr, startDate, endDate, edbInfoIdBytes, emptyType, maxEmptyType, extra)
 	return
 }
 
 // refreshAllCalculate 刷新全部数据
-func refreshAllCalculate(to orm.TxOrmer, edbInfoIdArr []*EdbInfo, edbInfoId, source, subSource int, edbCode, formulaStr, startDate, endDate string, edbInfoIdBytes []string, emptyType, maxEmptyType int) (err error) {
+func refreshAllCalculate(to orm.TxOrmer, edbInfoIdArr []*EdbInfo, edbInfoTag map[string]int, edbInfoId, source, subSource int, edbCode, formulaStr, startDate, endDate string, edbInfoIdBytes []string, emptyType, maxEmptyType int, extra string) (err error) {
 	realSaveDataMap := make(map[string]map[int]float64)
 	saveDataMap := make(map[string]map[int]float64)
 
 	// 最小的结束日期 , 最晚的数据开始日期
 	var minLatestDate, maxStartDate time.Time
-	dateList := make([]string, 0) // 第一个指标的日期数据
+	dateList := make([]string, 0)        // 最终的日期数据
+	dateMap := make(map[string]struct{}) // 最终的日期数据
+
+	dateTagConfig := ""
+	if extra != "" {
+		var dateConfig CalculateEdbExtra
+		err = json.Unmarshal([]byte(extra), &dateConfig)
+		if err != nil {
+			err = fmt.Errorf("refreshAllCalculate,extra解析失败,Err:%s", err.Error())
+			return
+		}
+		dateTagConfig = dateConfig.DateTag
+	}
 
 	for edbInfoIndex, v := range edbInfoIdArr {
 		var condition string
@@ -329,19 +353,40 @@ func refreshAllCalculate(to orm.TxOrmer, edbInfoIdArr []*EdbInfo, edbInfoId, sou
 				saveDataMap[dv.DataTime] = temp2
 			}
 
-			// 以第一个指标的日期作为基准日期
-			if edbInfoIndex == 0 {
-				dateList = append(dateList, dv.DataTime)
-				tmpDate, _ := time.ParseInLocation(utils.FormatDate, dv.DataTime, time.Local)
-				if minLatestDate.IsZero() || tmpDate.After(minLatestDate) {
-					minLatestDate = tmpDate
+			if dateTagConfig == "all" {
+				if _, ok := dateMap[dv.DataTime]; !ok {
+					dateList = append(dateList, dv.DataTime)
+					dateMap[dv.DataTime] = struct{}{}
 				}
-				if maxStartDate.IsZero() || tmpDate.Before(maxStartDate) {
-					maxStartDate = tmpDate
+			} else if dateTagConfig == "" { // 默认取第一个指标的时间序列
+				if edbInfoIndex == 0 {
+					if _, ok := dateMap[dv.DataTime]; !ok {
+						dateList = append(dateList, dv.DataTime)
+						dateMap[dv.DataTime] = struct{}{}
+					}
+				}
+			} else {
+				if eId, ok := edbInfoTag[dateTagConfig]; ok {
+					if v.EdbInfoId == eId {
+						if _, ok1 := dateMap[dv.DataTime]; !ok1 {
+							dateList = append(dateList, dv.DataTime)
+							dateMap[dv.DataTime] = struct{}{}
+						}
+					}
 				}
 			}
 		}
 	}
+	// 处理最大日期和最小日期
+	for _, v := range dateList {
+		tmpDate, _ := time.ParseInLocation(utils.FormatDate, v, time.Local)
+		if minLatestDate.IsZero() || tmpDate.After(minLatestDate) {
+			minLatestDate = tmpDate
+		}
+		if maxStartDate.IsZero() || tmpDate.Before(maxStartDate) {
+			maxStartDate = tmpDate
+		}
+	}
 
 	//数据处理,将日期内不全的数据做补全
 	HandleDateSaveDataMap(dateList, maxStartDate, minLatestDate, realSaveDataMap, saveDataMap, edbInfoIdArr, emptyType)
@@ -392,7 +437,11 @@ func refreshAllCalculate(to orm.TxOrmer, edbInfoIdArr []*EdbInfo, edbInfoId, sou
 			if sk < fv {
 				if f, ok := formulaDateMap[fv]; ok {
 					formulaStr = f
-					formulaMap = utils.CheckFormula(formulaStr)
+					formulaMap, err = utils.CheckFormula(formulaStr)
+					if err != nil {
+						err = fmt.Errorf("公式错误,请重新填写")
+						return
+					}
 				}
 				break
 			}
@@ -512,7 +561,9 @@ func ReplaceFormula(edbInfoIdArr []*EdbInfo, valArr, valArrMax map[int]float64,
 		formulaStr = GetMaxMinEdbInfo(formulaStr)
 	}
 	replaceCount := 0
-	for dk, dv := range edbInfoIdArr {
+	for dk := len(edbInfoIdArr) - 1; dk >= 0; dk-- {
+		dv := edbInfoIdArr[dk]
+		//for dk, dv := range edbInfoIdArr {
 		var isReplace bool
 		formulaStr, isReplace = GetFormulaReplace(dk, dv.EdbInfoId, formulaStr, edbInfoIdBytes, formulaMap, valArr, valArrMax, maxDealFlag)
 		if isReplace {
@@ -571,6 +622,34 @@ func GetFormulaMap() map[string]string {
 	return funMap
 }
 
+// CalculateBatchSaveReq 添加计算指标的请求参数
+type CalculateBatchSaveReq struct {
+	EdbList          []*CalculateEdbInfoItem        //需要批量计算的指标列表
+	AdminId          int                            `description:"添加人id"`
+	AdminName        string                         `description:"添加人名称"`
+	Formula          string                         `description:"N值/移动天数"`
+	Source           int                            `description:"来源:1:同花顺,2:wind,3:彭博,4:指标运算,5:累计值转月,6:同比值,7:同差值,8:N数值移动平均计算,12:环比值,13:环差值,14:升频"`
+	CalculateFormula string                         `description:"计算公式"`
+	EdbInfoIdArr     []EdbInfoCalculateEdbInfoIdReq `description:"关联指标列表"`
+	MoveType         int                            `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency    string                         `description:"移动频度:天/周/月/季/年"`
+	Calendar         string                         `description:"公历/农历"`
+	Data             interface{}                    `description:"数据"`
+	EmptyType        int                            `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int                            `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
+	Extra            string                         `description:"指标额外配置"`
+}
+
+type CalculateEdbInfoItem struct {
+	EdbInfoId     int    `description:"指标id"`
+	EdbName       string `description:"指标名称"`
+	Frequency     string `description:"频度"`
+	Unit          string `description:"单位"`
+	ClassifyId    int    `description:"分类id"`
+	CalculateId   string `description:"当前请求时,单个计算的唯一标识"`
+	FromEdbInfoId int    `description:"计算来源指标id"`
+}
+
 // EdbInfoCalculateBatchSaveReq 添加计算指标的请求参数
 type EdbInfoCalculateBatchSaveReq struct {
 	AdminId          int                            `description:"添加人id"`
@@ -589,6 +668,9 @@ type EdbInfoCalculateBatchSaveReq struct {
 	MoveFrequency    string                         `description:"移动频度:天/周/月/季/年"`
 	Calendar         string                         `description:"公历/农历"`
 	Data             interface{}                    `description:"数据"`
+	EmptyType        int                            `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int                            `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
+	Extra            string                         `description:"指标额外配置"`
 }
 
 // EdbInfoCalculateEdbInfoIdReq 新增/编辑请求 关联的指标列表
@@ -600,23 +682,23 @@ type EdbInfoCalculateEdbInfoIdReq struct {
 
 // EdbInfoCalculateBatchEditReq 编辑计算指标的请求参数
 type EdbInfoCalculateBatchEditReq struct {
-	EdbName       string `description:"指标名称"`
-	Frequency     string `description:"频度"`
-	Unit          string `description:"单位"`
-	ClassifyId    int    `description:"分类id"`
-	Formula       string `description:"N值"`
-	EdbInfoId     int    `description:"编辑指标id"`
-	FromEdbInfoId int    `description:"计算来源指标id"`
-	Source        int    `description:"来源:1:同花顺,2:wind,3:彭博,4:指标运算,5:累计值转月,6:同比值,7:同差值,8:N数值移动平均计算,12:环比值,13:环差值,14:升频"`
-	MoveType      int    `description:"移动方式:1:领先(默认),2:滞后"`
-	MoveFrequency string `description:"移动频度:天/周/月/季/年"`
-	EdbInfoIdArr  []struct {
-		EdbInfoId int    `description:"指标id"`
-		FromTag   string `description:"指标对应标签"`
-		MoveValue int    `description:"移动的值"`
-	}
-	Calendar string      `description:"公历/农历" orm:"default(公历)"`
-	Data     interface{} `description:"数据"`
+	EdbName          string                         `description:"指标名称"`
+	Frequency        string                         `description:"频度"`
+	Unit             string                         `description:"单位"`
+	ClassifyId       int                            `description:"分类id"`
+	Formula          string                         `description:"N值"`
+	EdbInfoId        int                            `description:"编辑指标id"`
+	FromEdbInfoId    int                            `description:"计算来源指标id"`
+	Source           int                            `description:"来源:1:同花顺,2:wind,3:彭博,4:指标运算,5:累计值转月,6:同比值,7:同差值,8:N数值移动平均计算,12:环比值,13:环差值,14:升频"`
+	MoveType         int                            `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency    string                         `description:"移动频度:天/周/月/季/年"`
+	EdbInfoIdArr     []EdbInfoCalculateEdbInfoIdReq `description:"关联指标列表"`
+	Calendar         string                         `description:"公历/农历" orm:"default(公历)"`
+	Extra            string                         `description:"指标的额外配置"`
+	Data             interface{}                    `description:"数据"`
+	EmptyType        int                            `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int                            `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
+	CalculateFormula string                         `description:"计算公式"`
 }
 
 // CheckFormula2 校验公式是否正常(比如说除法的分母不能为0之类的,实际上就是用预设的字段数据做一次计算)
@@ -861,3 +943,24 @@ type PredictRuleCalculateByNineResp struct {
 	LatestDate string
 	DataList   interface{}
 }
+
+// BatchEdbInfoCalculateBatchSaveResp 批量添加 计算指标 返回数据
+type BatchEdbInfoCalculateBatchSaveResp struct {
+	Fail    []BatchEdbInfoCalculateBatchSaveFailResp    `description:"添加失败的指标"`
+	Success []BatchEdbInfoCalculateBatchSaveSuccessResp `description:"添加成功的指标"`
+}
+
+// BatchEdbInfoCalculateBatchSaveFailResp 添加失败的指标信息
+type BatchEdbInfoCalculateBatchSaveFailResp struct {
+	CalculateId string `description:"当前请求时,单个计算的唯一标识"`
+	Msg         string `description:"用户提示信息"`
+	ErrMsg      string `description:"错误信息,内部查看"`
+}
+
+// BatchEdbInfoCalculateBatchSaveSuccessResp 添加成功的指标信息
+type BatchEdbInfoCalculateBatchSaveSuccessResp struct {
+	ClassifyId  int    `description:"分类id"`
+	CalculateId string `description:"当前请求时,单个计算的唯一标识"`
+	EdbInfoId   int    `description:"指标ID"`
+	UniqueCode  string `description:"指标唯一编码"`
+}

+ 5 - 1
models/base_predict_from_calculate.go

@@ -385,7 +385,11 @@ func refreshAllPredictCalculate(to orm.TxOrmer, edbInfoIdList []*EdbInfo, edbInf
 			if sk < fv {
 				if f, ok := formulaDateMap[fv]; ok {
 					formulaStr = f
-					formulaMap = utils.CheckFormula(formulaStr)
+					formulaMap, err = utils.CheckFormula(formulaStr)
+					if err != nil {
+						err = fmt.Errorf("公式错误,请重新填写")
+						return
+					}
 				}
 				break
 			}

+ 4 - 0
models/common.go

@@ -54,6 +54,10 @@ func GetBaseEdbInfoModel(source int) (baseEdbInfoModel BaseEdbInfoInterface) {
 		baseEdbInfoModel = ExponentialSmoothing{}
 	case utils.DATA_SOURCE_CALCULATE_ZDYFX:
 		baseEdbInfoModel = CustomAnalysis{}
+	case utils.DATA_SOURCE_CALCULATE_SUM:
+		baseEdbInfoModel = CalculateSum{}
+	case utils.DATA_SOURCE_CALCULATE_AVG:
+		baseEdbInfoModel = CalculateAvg{}
 	default:
 
 	}

+ 468 - 0
models/edb_data_calculate_avg.go

@@ -0,0 +1,468 @@
+package models
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type CalculateAvg struct {
+}
+
+// Add 新增
+func (obj CalculateAvg) Add(params AddCalculateBatchParams) (edbInfo *EdbInfo, err error, errMsg string) {
+	req := params.Req
+	edbCode := params.EdbCode
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("CalculateAvg.Add,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId > 0 {
+		err = errors.New("无法新增")
+		return
+	}
+
+	edbInfo = new(EdbInfo)
+	edbInfo.Source = obj.GetSource()
+	edbInfo.SourceName = obj.GetSourceName()
+	edbInfo.EdbCode = edbCode
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.SysUserId = params.SysUserId
+	edbInfo.SysUserRealName = params.SysUserRealName
+	edbInfo.CreateTime = time.Now()
+	edbInfo.ModifyTime = time.Now()
+	edbInfo.UniqueCode = params.UniqueCode
+	edbInfo.EdbType = obj.GetEdbType()
+	edbInfo.EmptyType = req.EmptyType
+	edbInfo.MaxEmptyType = req.MaxEmptyType
+	edbInfo.Extra = req.Extra
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	tagMap := make(map[string]int)
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+	for k, v := range req.EdbInfoIdArr {
+		tmpEdbInfo, e := GetEdbInfoById(v.EdbInfoId)
+		if e != nil {
+			err = e
+			return
+		}
+		relationEdbInfoList = append(relationEdbInfoList, tmpEdbInfo)
+
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = k
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = tmpEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = tmpEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = tmpEdbInfo.EdbName
+		calculateMappingItem.FromSource = tmpEdbInfo.Source
+		calculateMappingItem.FromSourceName = tmpEdbInfo.SourceName
+		calculateMappingItem.FromTag = v.FromTag
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		calculateMappingItem.FromSubSource = edbInfo.SubSource
+		calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+
+		tagMap[v.FromTag] = v.EdbInfoId
+	}
+	_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, relationEdbInfoList, edbInfo.EdbCode, edbInfo.Extra, tagMap, edbInfo.EmptyType)
+
+	return
+}
+
+func (obj CalculateAvg) Edit(params EditCalculateBatchParams) (err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("CalculateAvg.Edit,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	if len(req.EdbInfoIdArr) <= 0 {
+		err = fmt.Errorf("请选择要关联的指标")
+		return
+	}
+	needCalculate := false
+	if edbInfo.EmptyType != req.EmptyType || edbInfo.Extra != req.Extra {
+		needCalculate = true
+	}
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.Extra = req.Extra
+	edbInfo.EmptyType = req.EmptyType
+	edbInfo.MaxEmptyType = req.MaxEmptyType
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "Extra", "EmptyType", "MaxEmptyType", "ModifyTime")
+	if err != nil {
+		return
+	}
+	//判断是否重复指标
+	//edbInfoMap := make(map[int]string)
+	if !needCalculate {
+		//用到的指标
+		newEdbInfoIdArr := make([]string, 0)
+		for _, tmpEdbInfo := range req.EdbInfoIdArr {
+			/*_, ok := edbInfoMap[tmpEdbInfo.EdbInfoId]
+			if ok {
+				br.Msg = "选择指标失败,请勿选择重复指标!"
+				return
+			}
+			edbInfoMap[tmpEdbInfo.EdbInfoId] = tmpEdbInfo.FromTag*/
+			newEdbInfoIdArr = append(newEdbInfoIdArr, strconv.Itoa(tmpEdbInfo.EdbInfoId))
+		}
+
+		edbInfoIdStr := strings.Join(newEdbInfoIdArr, ",")
+		oldEdbInfoIdStr, e := GetCalculateEdbInfo(req.EdbInfoId)
+		if e != nil {
+			errMsg = "获取数据失败,GetCalculateEdbInfo:" + e.Error()
+			err = fmt.Errorf("查询关联指标失败")
+			return
+		}
+
+		if edbInfoIdStr != oldEdbInfoIdStr { // 不需要重复计算
+			needCalculate = true
+		}
+	}
+	fmt.Printf("needCalculate:%v\n", needCalculate)
+	if !needCalculate {
+		return
+	}
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		err = errors.New("删除计算指标关联关系失败,Err:" + err.Error())
+		return
+	}
+	//清空原有数据
+	tableName := GetEdbDataTableName(edbInfo.Source, edbInfo.SubSource)
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	tagMap := make(map[string]int)
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+	for k, v := range req.EdbInfoIdArr {
+		tmpEdbInfo, tmpErr := GetEdbInfoById(v.EdbInfoId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		relationEdbInfoList = append(relationEdbInfoList, tmpEdbInfo)
+
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = k
+		calculateMappingItem.EdbCode = edbInfo.EdbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = tmpEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = tmpEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = tmpEdbInfo.EdbName
+		calculateMappingItem.FromSource = tmpEdbInfo.Source
+		calculateMappingItem.FromSourceName = tmpEdbInfo.SourceName
+		calculateMappingItem.FromTag = v.FromTag
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		calculateMappingItem.FromSubSource = edbInfo.SubSource
+		calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+
+		tagMap[v.FromTag] = v.EdbInfoId
+	}
+	_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, relationEdbInfoList, edbInfo.EdbCode, edbInfo.Extra, tagMap, edbInfo.EmptyType)
+
+	return
+}
+
+func (obj CalculateAvg) Refresh(params RefreshParams) (err error, errMsg string) {
+	edbInfo := params.EdbInfo
+	edbInfoCalculateDetailList, err := GetEdbInfoCalculateDetailList(edbInfo.EdbInfoId)
+	if err != nil {
+		return
+	}
+	tagMap := make(map[string]int)
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	for _, v := range edbInfoCalculateDetailList {
+		tagMap[v.FromTag] = v.FromEdbInfoId
+		fromEdbInfo, _ := GetEdbInfoById(v.FromEdbInfoId)
+		relationEdbInfoList = append(relationEdbInfoList, fromEdbInfo)
+	}
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name() + ",Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, relationEdbInfoList, edbInfo.EdbCode, edbInfo.Extra, tagMap, edbInfo.EmptyType)
+
+	return
+}
+
+func (obj CalculateAvg) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfoIdArr []*EdbInfo, edbCode, extra string, edbInfoTag map[string]int, emptyType int) (err error) {
+
+	//to orm.TxOrmer, edbInfoId, source, subSource int, edbInfoIdArr []*EdbInfo, edbCode, extra string, edbInfoTag map[string]int, emptyType int
+	realSaveDataMap := make(map[string]map[int]float64)
+	saveDataMap := make(map[string]map[int]float64)
+
+	// 最小的结束日期 , 最晚的数据开始日期
+	var minLatestDate, maxStartDate time.Time
+	dateList := make([]string, 0)        // 最终的日期数据
+	dateMap := make(map[string]struct{}) // 最终的日期数据
+
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	tableName := GetEdbDataTableName(source, subSource)
+
+	// 获取多指标求和关联的指标id
+	dateTagConfig := ""
+	if extra != "" {
+		var dateConfig CalculateEdbExtra
+		err = json.Unmarshal([]byte(extra), &dateConfig)
+		if err != nil {
+			err = fmt.Errorf("refreshAllCalculate,extra解析失败,Err:%s", err.Error())
+			return
+		}
+		dateTagConfig = dateConfig.DateTag
+	}
+
+	for edbInfoIndex, v := range edbInfoIdArr {
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, v.EdbInfoId)
+		dataList, err := GetEdbDataListAllByTo(to, condition, pars, v.Source, v.SubSource, 1)
+		if err != nil {
+			return err
+		}
+
+		for _, dv := range dataList {
+			if val, ok := realSaveDataMap[dv.DataTime]; ok {
+				if _, ok := val[v.EdbInfoId]; !ok {
+					val[v.EdbInfoId] = dv.Value
+				}
+			} else {
+				temp := make(map[int]float64)
+				temp[v.EdbInfoId] = dv.Value
+				realSaveDataMap[dv.DataTime] = temp
+			}
+
+			// saveDataMap
+			if val, ok := saveDataMap[dv.DataTime]; ok {
+				if _, ok := val[v.EdbInfoId]; !ok {
+					val[v.EdbInfoId] = dv.Value
+				}
+			} else {
+				temp2 := make(map[int]float64)
+				temp2[v.EdbInfoId] = dv.Value
+				saveDataMap[dv.DataTime] = temp2
+			}
+
+			if dateTagConfig == "all" {
+				if _, ok := dateMap[dv.DataTime]; !ok {
+					dateList = append(dateList, dv.DataTime)
+					dateMap[dv.DataTime] = struct{}{}
+				}
+			} else if dateTagConfig == "" { // 默认取第一个指标的时间序列
+				if edbInfoIndex == 0 {
+					if _, ok := dateMap[dv.DataTime]; !ok {
+						dateList = append(dateList, dv.DataTime)
+						dateMap[dv.DataTime] = struct{}{}
+					}
+				}
+			} else {
+				if eId, ok := edbInfoTag[dateTagConfig]; ok {
+					if v.EdbInfoId == eId {
+						if _, ok1 := dateMap[dv.DataTime]; !ok1 {
+							dateList = append(dateList, dv.DataTime)
+							dateMap[dv.DataTime] = struct{}{}
+						}
+					}
+				}
+			}
+		}
+	}
+	// 处理最大日期和最小日期
+	for _, v := range dateList {
+		tmpDate, _ := time.ParseInLocation(utils.FormatDate, v, time.Local)
+		if minLatestDate.IsZero() || tmpDate.After(minLatestDate) {
+			minLatestDate = tmpDate
+		}
+		if maxStartDate.IsZero() || tmpDate.Before(maxStartDate) {
+			maxStartDate = tmpDate
+		}
+	}
+
+	//数据处理,将日期内不全的数据做补全
+	HandleDateSaveDataMap(dateList, maxStartDate, minLatestDate, realSaveDataMap, saveDataMap, edbInfoIdArr, emptyType)
+
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	//获取指标所有数据
+	dataList := make([]*EdbData, 0)
+	dataTableName := GetEdbDataTableName(source, subSource)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&dataList)
+	if err != nil {
+		return err
+	}
+	dataMap := make(map[string]string)
+
+	removeDateMap := make(map[string]int) //需要移除的日期
+	for _, v := range dataList {
+		dataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = 1
+	}
+	existDataMap := make(map[string]string)
+
+	for sk, sv := range saveDataMap {
+		if _, ok := dateMap[sk]; !ok {
+			continue
+		}
+		// 当空值处理类型选择了不计算时,只要有一个指标在某个日期没有值(即空值),则计算指标在该日期没有值
+		if len(sv) != len(edbInfoIdArr) {
+			continue
+		}
+		var calVal float64
+		for _, value := range sv {
+			calVal += value
+		}
+		calVal = calVal / float64(len(sv))
+		// 有计算出来值,那么就从待删除指标中移除
+		delete(removeDateMap, sk)
+		saveValue := decimal.NewFromFloat(calVal).RoundCeil(4).String()
+		if existVal, ok := dataMap[sk]; !ok {
+			dataTime, _ := time.ParseInLocation(utils.FormatDate, sk, time.Local)
+			timestamp := dataTime.UnixNano() / 1e6
+			timeStr := fmt.Sprintf("%d", timestamp)
+
+			if _, existOk := existDataMap[sk]; !existOk {
+				addSql += GetAddSql(edbInfoIdStr, edbCode, sk, timeStr, saveValue)
+				isAdd = true
+			}
+			existDataMap[sk] = sk
+		} else {
+			existValDecimal, err := decimal.NewFromString(existVal)
+			existStr := existValDecimal.String()
+			if existStr != saveValue {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, saveValue, edbInfoId, sk).Exec()
+				if err != nil {
+					return err
+				}
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("RefreshAllCalculate add Err", err.Error())
+			return
+		}
+	}
+
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (%s) `, tableName, removeDateStr)
+
+		_, err = to.Raw(sql, edbInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除计算失败的计算指标数据失败,Err:" + err.Error())
+			return
+		}
+	}
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj CalculateAvg) GetSource() int {
+	return utils.DATA_SOURCE_CALCULATE_AVG
+}
+
+// GetSourceName 获取来源名称
+func (obj CalculateAvg) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_CALCULATE_AVG
+}
+
+// GetEdbType 获取指标类型
+func (obj CalculateAvg) GetEdbType() int {
+	return utils.CALCULATE_EDB_TYPE
+}

+ 136 - 12
models/edb_data_calculate_ljz.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"encoding/json"
 	"errors"
 	"eta/eta_index_lib/utils"
 	"fmt"
@@ -80,6 +81,7 @@ func (obj Ljz) Add(params AddCalculateBatchParams) (edbInfo *EdbInfo, err error,
 		LatestValue:      0,
 		ChartImage:       "",
 		Calendar:         "",
+		Extra:            req.Extra,
 	}
 
 	newEdbInfoId, tmpErr := to.Insert(edbInfo)
@@ -113,7 +115,7 @@ func (obj Ljz) Add(params AddCalculateBatchParams) (edbInfo *EdbInfo, err error,
 	}
 
 	//计算数据
-	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo, fromEdbInfo, edbInfo.EdbCode, "", edbInfo.Extra)
 
 	return
 }
@@ -152,7 +154,7 @@ func (obj Ljz) Edit(params EditCalculateBatchParams) (err error, errMsg string)
 	tableName := GetEdbDataTableName(edbInfo.Source, edbInfo.SubSource)
 
 	var isRecalculate bool
-	if edbInfo.Frequency != req.Frequency {
+	if edbInfo.Frequency != req.Frequency || edbInfo.Extra != req.Extra {
 		isRecalculate = true
 	}
 	//修改指标信息
@@ -161,8 +163,9 @@ func (obj Ljz) Edit(params EditCalculateBatchParams) (err error, errMsg string)
 	edbInfo.Frequency = req.Frequency
 	edbInfo.Unit = req.Unit
 	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.Extra = req.Extra
 	edbInfo.ModifyTime = time.Now()
-	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "ModifyTime")
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "ModifyTime", "Extra")
 	if err != nil {
 		return
 	}
@@ -180,9 +183,9 @@ func (obj Ljz) Edit(params EditCalculateBatchParams) (err error, errMsg string)
 	}
 	if count > 0 { // 指标未被替换,无需删除关联数据
 
-		// 频度被换了,需要重新计算
+		// todo 频度被换了,需要重新计算
 		if isRecalculate {
-			err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+			err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo, fromEdbInfo, edbInfo.EdbCode, "", req.Extra)
 		}
 
 		return
@@ -228,7 +231,7 @@ func (obj Ljz) Edit(params EditCalculateBatchParams) (err error, errMsg string)
 	}
 
 	//计算数据
-	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo, fromEdbInfo, edbInfo.EdbCode, "")
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo, fromEdbInfo, edbInfo.EdbCode, "", edbInfo.Extra)
 
 	return
 }
@@ -261,7 +264,7 @@ func (obj Ljz) Refresh(params RefreshParams) (err error, errMsg string) {
 	}()
 
 	// 计算数据
-	err = obj.refresh(to, params.EdbInfo.EdbInfoId, params.EdbInfo.Source, params.EdbInfo.SubSource, params.EdbInfo, fromEdbInfo, params.EdbInfo.EdbCode, params.StartDate)
+	err = obj.refresh(to, params.EdbInfo.EdbInfoId, params.EdbInfo.Source, params.EdbInfo.SubSource, params.EdbInfo, fromEdbInfo, params.EdbInfo.EdbCode, params.StartDate, params.EdbInfo.Extra)
 
 	return
 }
@@ -281,7 +284,7 @@ func (obj Ljz) GetEdbType() int {
 	return utils.CALCULATE_EDB_TYPE
 }
 
-func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo, fromEdbInfo *EdbInfo, edbCode, startDate string) (err error) {
+func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo, fromEdbInfo *EdbInfo, edbCode, startDate string, extra string) (err error) {
 	dataTableName := GetEdbDataTableName(source, subSource)
 	edbInfoIdStr := strconv.Itoa(edbInfoId)
 	//计算数据
@@ -313,8 +316,19 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 			return
 		}
 	}
+	var lastValType int
+	if extra != "" {
+		var lastValConfig CalculateLjzEdbExtra
+		err = json.Unmarshal([]byte(extra), &lastValConfig)
+		if err != nil {
+			err = fmt.Errorf("refreshAllCalculate,extra解析失败,Err:%s", err.Error())
+			return
+		}
+		lastValType = lastValConfig.LastValType
+	}
 
 	//日度转周度:日期选周五,计算上周六到本周五的日度值的加总,最新日期为最新值对应的周五。
+	//日度转旬度:日期选每个旬的最后一天,计算当旬所有日度值的加总,最新日期为最新值对应当旬的最后一天。
 	//日度转月度:日期选每个月最后一天,计算当月所有日度值的加总,最新日期为最新值对应当月最后一天。
 	//日度转季度、年度:方法类似转月度。
 	//周度转月度/季度/年度:将周度值转成日度,空值用插值法插值,计算当月/当季/当年所有值的加总,然后除以7。
@@ -326,6 +340,7 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 	case "年度":
 		yearMap := make(map[int]float64)
 		yearList := make([]int, 0)
+		var lastNewDate time.Time
 		for _, item := range dataList {
 			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
 			if tmpErr != nil {
@@ -345,10 +360,37 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 			currTime := time.Date(v, 12, 31, 0, 0, 0, 0, time.Local)
 			dateList = append(dateList, currTime)
 			valueMap[currTime] = yearMap[v]
+			lastNewDate = currTime
+		}
+
+		// 根据配置处理最新值, 1 表示均值填充
+		if lastValType == 1 {
+			lastItem := dataList[len(dataList)-1]
+			// 最后一天的累计值
+			initVal := valueMap[lastNewDate]
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, lastItem.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+
+			//获取当季的总天数, 当季最后一天减去当季第一天
+			// 获取当年的总天数
+			startDateT := time.Date(itemDate.Year(), 1, 1, 0, 0, 0, 0, time.Local)
+			allDays := int(lastNewDate.Sub(startDateT).Hours() / 24)
+			//获取距离当年第一天的天数
+			days := int(itemDate.Sub(startDateT).Hours() / 24)
+
+			daysT := decimal.NewFromInt(int64(days))
+			initValAndMonthT := decimal.NewFromFloat(initVal * float64(allDays))
+			val, _ := initValAndMonthT.Div(daysT).RoundCeil(4).Float64()
+			fmt.Printf("最新值 计算公式:%d*%f/(%d) = %f\n", allDays, initVal, days, val)
+			valueMap[lastNewDate] = val
 		}
 	case "半年度":
 		yearMonthMap := make(map[string]float64)
 		yearMonthList := make([]string, 0)
+		var lastNewDate time.Time
 		for _, item := range dataList {
 			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
 			if tmpErr != nil {
@@ -380,10 +422,37 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 			currTime = currTime.AddDate(0, 1, -1)
 			dateList = append(dateList, currTime)
 			valueMap[currTime] = yearMonthMap[v]
+			lastNewDate = currTime
+		}
+
+		// 根据配置处理最新值, 1 表示均值填充
+		if lastValType == 1 {
+			lastItem := dataList[len(dataList)-1]
+			// 最后一天的累计值
+			initVal := valueMap[lastNewDate]
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, lastItem.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+
+			//获取上半年或者下半年的总天数, 半年度最后一天减去半年度第一天
+			startDateT := lastNewDate.AddDate(0, -6, 0)
+			allDays := int(lastNewDate.Sub(startDateT).Hours() / 24)
+
+			//获取距离半年度第一天的天数
+			days := int(itemDate.Sub(startDateT).Hours() / 24)
+
+			daysT := decimal.NewFromInt(int64(days))
+			initValAndMonthT := decimal.NewFromFloat(initVal * float64(allDays))
+			val, _ := initValAndMonthT.Div(daysT).RoundCeil(4).Float64()
+			fmt.Printf("最新值 计算公式:%d*%f/(%d) = %f\n", allDays, initVal, days, val)
+			valueMap[lastNewDate] = val
 		}
 	case "季度":
 		yearMonthMap := make(map[string]float64)
 		yearMonthList := make([]string, 0)
+		var lastNewDate time.Time
 		for _, item := range dataList {
 			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
 			if tmpErr != nil {
@@ -419,10 +488,37 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 			currTime = currTime.AddDate(0, 1, -1)
 			dateList = append(dateList, currTime)
 			valueMap[currTime] = yearMonthMap[v]
+			lastNewDate = currTime
+		}
+
+		// 根据配置处理最新值, 1 表示均值填充
+		if lastValType == 1 {
+			lastItem := dataList[len(dataList)-1]
+			// 最后一天的累计值
+			initVal := valueMap[lastNewDate]
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, lastItem.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+
+			//获取当季的总天数, 当季最后一天减去当季第一天
+			startDateT := lastNewDate.AddDate(0, -3, 0)
+			allDays := int(lastNewDate.Sub(startDateT).Hours() / 24)
+			//获取距离当季第一天的天数
+			days := int(itemDate.Sub(startDateT).Hours() / 24)
+
+			daysT := decimal.NewFromInt(int64(days))
+			initValAndMonthT := decimal.NewFromFloat(initVal * float64(allDays))
+			val, _ := initValAndMonthT.Div(daysT).RoundCeil(4).Float64()
+			fmt.Printf("最新值 计算公式:%d*%f/(%d) = %f\n", allDays, initVal, days, val)
+
+			valueMap[lastNewDate] = val
 		}
 	case "月度":
 		yearMonthMap := make(map[string]float64)
 		yearMonthList := make([]string, 0)
+		var lastNewDate time.Time
 		for _, item := range dataList {
 			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, item.DataTime, time.Local)
 			if tmpErr != nil {
@@ -436,6 +532,7 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 			yearVal, ok := yearMonthMap[tmpK]
 			if ok {
 				yearMonthMap[tmpK] = item.Value + yearVal
+				fmt.Printf("%s 累加值:%f = %.6f + %.6f\n", tmpK, yearMonthMap[tmpK], item.Value, yearVal)
 			} else {
 				yearMonthList = append(yearMonthList, tmpK)
 				yearMonthMap[tmpK] = item.Value
@@ -450,6 +547,30 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 			currTime = currTime.AddDate(0, 1, -1)
 			dateList = append(dateList, currTime)
 			valueMap[currTime] = yearMonthMap[v]
+			lastNewDate = currTime
+		}
+
+		// 根据配置处理最新值, 1 表示均值填充
+		if lastValType == 1 {
+			lastItem := dataList[len(dataList)-1]
+			// 最后一天的累计值
+			initVal := valueMap[lastNewDate]
+			itemDate, tmpErr := time.ParseInLocation(utils.FormatDate, lastItem.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+
+			//获取当月的天数
+			monthDays := lastNewDate.Day()
+			//获取自然日的天数
+			days := itemDate.Day()
+
+			daysT := decimal.NewFromInt(int64(days))
+			initValAndMonthT := decimal.NewFromFloat(initVal * float64(monthDays))
+			val, _ := initValAndMonthT.Div(daysT).RoundCeil(4).Float64()
+			fmt.Printf("最新值 计算公式:%d*%f/(%d) = %f\n", monthDays, initVal, days, val)
+			valueMap[lastNewDate] = val
 		}
 	case "旬度":
 		tmpDateDataMap := make(map[time.Time]float64)
@@ -462,20 +583,23 @@ func (obj Ljz) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfo
 			}
 			dayInt := itemDate.Year()*100 + int(itemDate.Month())
 			var currTime time.Time
-			if itemDate.Month() <= 10 {
-				tmpK := fmt.Sprint(dayInt*100, "10")
+			if itemDate.Day() <= 10 {
+				//本月上旬
+				tmpK := fmt.Sprint(dayInt, "10")
 				currTime, err = time.ParseInLocation(utils.FormatDateUnSpace, tmpK, time.Local)
 				if err != nil {
 					return
 				}
 
-			} else if itemDate.Month() <= 20 {
-				tmpK := fmt.Sprint(dayInt*100, "20")
+			} else if itemDate.Day() <= 20 {
+				// 本月中旬
+				tmpK := fmt.Sprint(dayInt, "20")
 				currTime, err = time.ParseInLocation(utils.FormatDateUnSpace, tmpK, time.Local)
 				if err != nil {
 					return
 				}
 			} else {
+				// 本月下旬
 				currTime, err = time.ParseInLocation(utils.FormatYearMonthUnSpace, fmt.Sprint(dayInt), time.Local)
 				if err != nil {
 					return

+ 475 - 0
models/edb_data_calculate_sum.go

@@ -0,0 +1,475 @@
+package models
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type CalculateSum struct {
+}
+
+// Add 新增
+func (obj CalculateSum) Add(params AddCalculateBatchParams) (edbInfo *EdbInfo, err error, errMsg string) {
+	req := params.Req
+	edbCode := params.EdbCode
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("CalculateSum.Add,Err:" + err.Error())
+			fmt.Println("CalculateSum.Add,ErrMsg:" + errMsg)
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	if req.EdbInfoId > 0 {
+		err = errors.New("无法新增")
+		return
+	}
+
+	edbInfo = new(EdbInfo)
+	edbInfo.Source = obj.GetSource()
+	edbInfo.SourceName = obj.GetSourceName()
+	edbInfo.EdbCode = edbCode
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.SysUserId = params.SysUserId
+	edbInfo.SysUserRealName = params.SysUserRealName
+	edbInfo.CreateTime = time.Now()
+	edbInfo.ModifyTime = time.Now()
+	edbInfo.UniqueCode = params.UniqueCode
+	edbInfo.EdbType = obj.GetEdbType()
+	edbInfo.EmptyType = req.EmptyType
+	edbInfo.MaxEmptyType = req.MaxEmptyType
+	edbInfo.Extra = req.Extra
+	newEdbInfoId, tmpErr := to.Insert(edbInfo)
+	if tmpErr != nil {
+		errMsg = "新增失败"
+		err = tmpErr
+		return
+	}
+	edbInfo.EdbInfoId = int(newEdbInfoId)
+
+	//关联关系
+	tagMap := make(map[string]int)
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+	for k, v := range req.EdbInfoIdArr {
+		tmpEdbInfo, e := GetEdbInfoById(v.EdbInfoId)
+		if e != nil {
+			errMsg = "查不到原指标"
+			err = e
+			return
+		}
+		relationEdbInfoList = append(relationEdbInfoList, tmpEdbInfo)
+
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = k
+		calculateMappingItem.EdbCode = edbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = tmpEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = tmpEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = tmpEdbInfo.EdbName
+		calculateMappingItem.FromSource = tmpEdbInfo.Source
+		calculateMappingItem.FromSourceName = tmpEdbInfo.SourceName
+		calculateMappingItem.FromTag = v.FromTag
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		calculateMappingItem.FromSubSource = edbInfo.SubSource
+		calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+
+		tagMap[v.FromTag] = v.EdbInfoId
+	}
+	_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+	if err != nil {
+		errMsg = "保存失败"
+		return
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, relationEdbInfoList, edbInfo.EdbCode, edbInfo.Extra, tagMap, edbInfo.EmptyType)
+
+	return
+}
+
+func (obj CalculateSum) Edit(params EditCalculateBatchParams) (err error, errMsg string) {
+	req := params.Req
+	edbInfo := params.EdbInfo
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("EditCalculateKszs,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	if len(req.EdbInfoIdArr) <= 0 {
+		err = fmt.Errorf("请选择要关联的指标")
+		return
+	}
+	needCalculate := false
+	if edbInfo.EmptyType != req.EmptyType || edbInfo.Extra != req.Extra {
+		needCalculate = true
+	}
+
+	//修改指标信息
+	edbInfo.EdbName = req.EdbName
+	edbInfo.EdbNameSource = req.EdbName
+	edbInfo.Frequency = req.Frequency
+	edbInfo.Unit = req.Unit
+	edbInfo.ClassifyId = req.ClassifyId
+	edbInfo.CalculateFormula = req.Formula
+	edbInfo.Extra = req.Extra
+	edbInfo.EmptyType = req.EmptyType
+	edbInfo.MaxEmptyType = req.MaxEmptyType
+	edbInfo.ModifyTime = time.Now()
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "Extra", "EmptyType", "MaxEmptyType", "ModifyTime")
+	if err != nil {
+		return
+	}
+	//判断是否重复指标
+	//edbInfoMap := make(map[int]string)
+	if !needCalculate {
+		//用到的指标
+		newEdbInfoIdArr := make([]string, 0)
+		for _, tmpEdbInfo := range req.EdbInfoIdArr {
+			/*_, ok := edbInfoMap[tmpEdbInfo.EdbInfoId]
+			if ok {
+				br.Msg = "选择指标失败,请勿选择重复指标!"
+				return
+			}
+			edbInfoMap[tmpEdbInfo.EdbInfoId] = tmpEdbInfo.FromTag*/
+			newEdbInfoIdArr = append(newEdbInfoIdArr, strconv.Itoa(tmpEdbInfo.EdbInfoId))
+		}
+
+		edbInfoIdStr := strings.Join(newEdbInfoIdArr, ",")
+		oldEdbInfoIdStr, e := GetCalculateEdbInfo(req.EdbInfoId)
+		if e != nil {
+			errMsg = "获取数据失败,GetCalculateEdbInfo:" + e.Error()
+			err = fmt.Errorf("查询关联指标失败")
+			return
+		}
+
+		if edbInfoIdStr != oldEdbInfoIdStr { // 不需要重复计算
+			fmt.Printf("edbInfoIdStr:%s,oldEdbInfoIdStr:%s\n", edbInfoIdStr, oldEdbInfoIdStr)
+			needCalculate = true
+		}
+	}
+	fmt.Printf("needCalculate:%v\n", needCalculate)
+	if !needCalculate {
+		return
+	}
+	//删除,计算指标关联的,基础指标的关联关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		err = errors.New("删除计算指标关联关系失败,Err:" + err.Error())
+		return
+	}
+	//清空原有数据
+	tableName := GetEdbDataTableName(edbInfo.Source, edbInfo.SubSource)
+	sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+	_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	//关联关系
+	tagMap := make(map[string]int)
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+	for k, v := range req.EdbInfoIdArr {
+		tmpEdbInfo, tmpErr := GetEdbInfoById(v.EdbInfoId)
+		if tmpErr != nil {
+			errMsg = "获取数据失败,GetEdbInfoById:" + tmpErr.Error()
+			err = tmpErr
+			return
+		}
+		relationEdbInfoList = append(relationEdbInfoList, tmpEdbInfo)
+
+		calculateMappingItem := new(EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = k
+		calculateMappingItem.EdbCode = edbInfo.EdbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = tmpEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = tmpEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = tmpEdbInfo.EdbName
+		calculateMappingItem.FromSource = tmpEdbInfo.Source
+		calculateMappingItem.FromSourceName = tmpEdbInfo.SourceName
+		calculateMappingItem.FromTag = v.FromTag
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		calculateMappingItem.FromSubSource = edbInfo.SubSource
+		calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+
+		tagMap[v.FromTag] = v.EdbInfoId
+	}
+	_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+	if err != nil {
+		return
+	}
+
+	//计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, relationEdbInfoList, edbInfo.EdbCode, edbInfo.Extra, tagMap, edbInfo.EmptyType)
+
+	return
+}
+
+func (obj CalculateSum) Refresh(params RefreshParams) (err error, errMsg string) {
+	edbInfo := params.EdbInfo
+	edbInfoCalculateDetailList, err := GetEdbInfoCalculateDetailList(edbInfo.EdbInfoId)
+	if err != nil {
+		return
+	}
+	tagMap := make(map[string]int)
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	for _, v := range edbInfoCalculateDetailList {
+		tagMap[v.FromTag] = v.FromEdbInfoId
+		fromEdbInfo, _ := GetEdbInfoById(v.FromEdbInfoId)
+		relationEdbInfoList = append(relationEdbInfoList, fromEdbInfo)
+	}
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println(reflect.TypeOf(obj).Name() + ",Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	err = obj.refresh(to, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, relationEdbInfoList, edbInfo.EdbCode, edbInfo.Extra, tagMap, edbInfo.EmptyType)
+
+	return
+}
+
+func (obj CalculateSum) refresh(to orm.TxOrmer, edbInfoId, source, subSource int, edbInfoIdArr []*EdbInfo, edbCode, extra string, edbInfoTag map[string]int, emptyType int) (err error) {
+
+	//to orm.TxOrmer, edbInfoId, source, subSource int, edbInfoIdArr []*EdbInfo, edbCode, extra string, edbInfoTag map[string]int, emptyType int
+	realSaveDataMap := make(map[string]map[int]float64)
+	saveDataMap := make(map[string]map[int]float64)
+
+	// 最小的结束日期 , 最晚的数据开始日期
+	var minLatestDate, maxStartDate time.Time
+	dateList := make([]string, 0)        // 最终的日期数据
+	dateMap := make(map[string]struct{}) // 最终的日期数据
+
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	tableName := GetEdbDataTableName(source, subSource)
+
+	// 获取多指标求和关联的指标id
+	dateTagConfig := ""
+	if extra != "" {
+		var dateConfig CalculateEdbExtra
+		err = json.Unmarshal([]byte(extra), &dateConfig)
+		if err != nil {
+			err = fmt.Errorf("refreshAllCalculate,extra解析失败,Err:%s", err.Error())
+			return
+		}
+		dateTagConfig = dateConfig.DateTag
+	}
+
+	for edbInfoIndex, v := range edbInfoIdArr {
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id=? "
+		pars = append(pars, v.EdbInfoId)
+		dataList, err := GetEdbDataListAllByTo(to, condition, pars, v.Source, v.SubSource, 1)
+		if err != nil {
+			return err
+		}
+
+		for _, dv := range dataList {
+			if val, ok := realSaveDataMap[dv.DataTime]; ok {
+				if _, ok := val[v.EdbInfoId]; !ok {
+					val[v.EdbInfoId] = dv.Value
+				}
+			} else {
+				temp := make(map[int]float64)
+				temp[v.EdbInfoId] = dv.Value
+				realSaveDataMap[dv.DataTime] = temp
+			}
+
+			// saveDataMap
+			if val, ok := saveDataMap[dv.DataTime]; ok {
+				if _, ok := val[v.EdbInfoId]; !ok {
+					val[v.EdbInfoId] = dv.Value
+				}
+			} else {
+				temp2 := make(map[int]float64)
+				temp2[v.EdbInfoId] = dv.Value
+				saveDataMap[dv.DataTime] = temp2
+			}
+
+			if dateTagConfig == "all" {
+				if _, ok := dateMap[dv.DataTime]; !ok {
+					dateList = append(dateList, dv.DataTime)
+					dateMap[dv.DataTime] = struct{}{}
+				}
+			} else if dateTagConfig == "" { // 默认取第一个指标的时间序列
+				if edbInfoIndex == 0 {
+					if _, ok := dateMap[dv.DataTime]; !ok {
+						dateList = append(dateList, dv.DataTime)
+						dateMap[dv.DataTime] = struct{}{}
+					}
+				}
+			} else {
+				if eId, ok := edbInfoTag[dateTagConfig]; ok {
+					if v.EdbInfoId == eId {
+						if _, ok1 := dateMap[dv.DataTime]; !ok1 {
+							dateList = append(dateList, dv.DataTime)
+							dateMap[dv.DataTime] = struct{}{}
+						}
+					}
+				}
+			}
+		}
+	}
+	// 处理最大日期和最小日期
+	for _, v := range dateList {
+		tmpDate, _ := time.ParseInLocation(utils.FormatDate, v, time.Local)
+		if minLatestDate.IsZero() || tmpDate.After(minLatestDate) {
+			minLatestDate = tmpDate
+		}
+		if maxStartDate.IsZero() || tmpDate.Before(maxStartDate) {
+			maxStartDate = tmpDate
+		}
+	}
+
+	//数据处理,将日期内不全的数据做补全
+	HandleDateSaveDataMap(dateList, maxStartDate, minLatestDate, realSaveDataMap, saveDataMap, edbInfoIdArr, emptyType)
+
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	//获取指标所有数据
+	dataList := make([]*EdbData, 0)
+	dataTableName := GetEdbDataTableName(source, subSource)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&dataList)
+	if err != nil {
+		return err
+	}
+	dataMap := make(map[string]string)
+
+	removeDateMap := make(map[string]int) //需要移除的日期
+	for _, v := range dataList {
+		dataMap[v.DataTime] = v.Value
+		removeDateMap[v.DataTime] = 1
+	}
+	existDataMap := make(map[string]string)
+
+	for sk, sv := range saveDataMap {
+		if _, ok := dateMap[sk]; !ok {
+			continue
+		}
+		// 当空值处理类型选择了不计算时,只要有一个指标在某个日期没有值(即空值),则计算指标在该日期没有值
+		if len(sv) != len(edbInfoIdArr) {
+			continue
+		}
+
+		var calVal float64
+		for _, value := range sv {
+			calVal += value
+		}
+
+		// 有计算出来值,那么就从待删除指标中移除
+		delete(removeDateMap, sk)
+		saveValue := decimal.NewFromFloat(calVal).RoundCeil(4).String()
+		if existVal, ok := dataMap[sk]; !ok {
+			dataTime, _ := time.ParseInLocation(utils.FormatDate, sk, time.Local)
+			timestamp := dataTime.UnixNano() / 1e6
+			timeStr := fmt.Sprintf("%d", timestamp)
+
+			if _, existOk := existDataMap[sk]; !existOk {
+				addSql += GetAddSql(edbInfoIdStr, edbCode, sk, timeStr, saveValue)
+				isAdd = true
+			}
+			existDataMap[sk] = sk
+		} else {
+			existValDecimal, err := decimal.NewFromString(existVal)
+			existStr := existValDecimal.String()
+			if existStr != saveValue {
+				sql := ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+				sql = fmt.Sprintf(sql, dataTableName)
+				_, err = to.Raw(sql, saveValue, edbInfoId, sk).Exec()
+				if err != nil {
+					return err
+				}
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("RefreshAllCalculate add Err", err.Error())
+			return
+		}
+	}
+
+	if len(removeDateMap) > 0 {
+		removeDateList := make([]string, 0) //需要移除的日期
+		for k := range removeDateMap {
+			removeDateList = append(removeDateList, k)
+		}
+		removeDateStr := strings.Join(removeDateList, `","`)
+		removeDateStr = `"` + removeDateStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (%s) `, tableName, removeDateStr)
+
+		_, err = to.Raw(sql, edbInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除计算失败的计算指标数据失败,Err:" + err.Error())
+			return
+		}
+	}
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj CalculateSum) GetSource() int {
+	return utils.DATA_SOURCE_CALCULATE_SUM
+}
+
+// GetSourceName 获取来源名称
+func (obj CalculateSum) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_CALCULATE_SUM
+}
+
+// GetEdbType 获取指标类型
+func (obj CalculateSum) GetEdbType() int {
+	return utils.CALCULATE_EDB_TYPE
+}

+ 11 - 0
models/edb_info.go

@@ -58,6 +58,7 @@ type EdbInfo struct {
 	SubSourceName    string  `description:"子数据来源名称"`
 	IndicatorCode    string  `description:"指标代码"`
 	StockCode        string  `description:"证券代码"`
+	Extra            string  `description:"指标的额外配置"`
 }
 
 func (e *EdbInfo) Add() (err error) {
@@ -1263,3 +1264,13 @@ func GetEdbAndClassifyMaxSort(parentId int, classifyType uint8) (maxSort int, er
 	}
 	return
 }
+
+// CalculateEdbExtra 指标运算额外配置
+type CalculateEdbExtra struct {
+	DateTag string `description:"时间序列的生成方式,all 表示所选指标的时间序列并集"`
+}
+
+// CalculateLjzEdbExtra 累计值额外配置
+type CalculateLjzEdbExtra struct {
+	LastValType int `description:"最新值处理:0默认、均值填充"`
+}

+ 5 - 1
models/predict_edb.go

@@ -160,7 +160,11 @@ func CalculateByRuleBy9(to orm.TxOrmer, rule CalculateRule) (resultDataList []*E
 			if sk < fv {
 				if f, ok := formulaDateMap[fv]; ok {
 					formulaStr = f
-					formulaMap = utils.CheckFormula(formulaStr)
+					formulaMap, err = utils.CheckFormula(formulaStr)
+					if err != nil {
+						err = fmt.Errorf("公式错误,请重新填写")
+						return
+					}
 				}
 				break
 			}

+ 18 - 0
routers/commentsRouter.go

@@ -115,6 +115,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:CalculateController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:CalculateController"],
+        beego.ControllerComments{
+            Method: "BatchEditMulti",
+            Router: `/batch/edit/multi`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_index_lib/controllers:CalculateController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:CalculateController"],
         beego.ControllerComments{
             Method: "BatchSave",
@@ -124,6 +133,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:CalculateController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:CalculateController"],
+        beego.ControllerComments{
+            Method: "BatchSaveMulti",
+            Router: `/batch/save/multi`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_index_lib/controllers:CalculateController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:CalculateController"],
         beego.ControllerComments{
             Method: "CalculateComputeCorrelation",

+ 1015 - 0
services/base_from_calculate.go

@@ -0,0 +1,1015 @@
+package services
+
+import (
+	"eta/eta_index_lib/logic"
+	"eta/eta_index_lib/models"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// EdbCalculateBatchSave 批量指标运算
+func EdbCalculateBatchSave(req models.EdbInfoCalculateBatchSaveReq) (edbInfo *models.EdbInfo, uniqueCode string, err error, errMsg string) {
+	// 校验参数
+	req.EdbName = strings.Trim(req.EdbName, " ")
+	if req.EdbName == "" {
+		err = fmt.Errorf("指标名称不能为空")
+		return
+	}
+
+	if req.Frequency == "" {
+		err = fmt.Errorf("频率不能为空")
+		return
+	}
+
+	if req.Unit == "" {
+		err = fmt.Errorf("单位不能为空")
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		err = fmt.Errorf("请选择分类")
+		return
+	}
+
+	// 基础指标id
+	fromEdbInfoId := req.FromEdbInfoId
+
+	var formulaInt int
+	var nhccDate models.NhccDate // 拟合残差的日期
+	switch req.Source {
+	case utils.DATA_SOURCE_CALCULATE_NSZYDPJJS, utils.DATA_SOURCE_CALCULATE_HBZ, utils.DATA_SOURCE_CALCULATE_HCZ, utils.DATA_SOURCE_CALCULATE_TIME_SHIFT, utils.DATA_SOURCE_CALCULATE_CJJX:
+		if req.Formula == "" {
+			err = fmt.Errorf("请填写N值")
+			return
+		}
+		formulaInt, _ = strconv.Atoi(req.Formula)
+		if formulaInt <= 0 {
+			err = fmt.Errorf(" N值输入错误,请重新输入")
+			return
+		}
+	case utils.DATA_SOURCE_CALCULATE_ZJPJ:
+		//直接拼接指标
+		//校验时间格式
+		_, err = time.ParseInLocation(utils.FormatDate, req.Formula, time.Local)
+		if err != nil {
+			err = fmt.Errorf("拼接日期有误,请重新输入")
+			return
+		}
+	case utils.DATA_SOURCE_CALCULATE_NHCC: //拟合残差指标
+		//指标校验
+		if len(req.EdbInfoIdArr) != 2 {
+			err = fmt.Errorf("选择的指标异常,请重新选择")
+			return
+		}
+		fromEdbInfoId = req.EdbInfoIdArr[0].EdbInfoId
+
+		//校验时间格式
+		//数据格式:2022-11-01,2022-11-10
+		timeList := strings.Split(req.Formula, ",")
+		if len(timeList) != 2 {
+			err = fmt.Errorf("选择时间有误,请重新输入")
+			return
+		}
+		startDate, e := time.ParseInLocation(utils.FormatDate, timeList[0], time.Local)
+		if e != nil {
+			err = fmt.Errorf("开始日期有误,请重新输入")
+			return
+		}
+		endDate, e := time.ParseInLocation(utils.FormatDate, timeList[1], time.Local)
+		if e != nil {
+			err = fmt.Errorf("结束日期有误,请重新输入")
+			return
+		}
+		if utils.GetTimeSubDay(startDate, endDate) < 2 {
+			err = fmt.Errorf("日期间隔不得少于两天")
+			return
+		}
+		nhccDate.StartDate = startDate
+		nhccDate.EndDate = endDate
+	case utils.DATA_SOURCE_CALCULATE_JP:
+		if req.Formula != "期末值" && req.Formula != "平均值" {
+			err = fmt.Errorf("数据取值类型错误:" + req.Formula)
+			return
+		}
+	case utils.DATA_SOURCE_CALCULATE_ZSXY: // 指数修匀
+		if req.Formula == "" {
+			err = fmt.Errorf("请填写alpha值")
+			return
+		}
+		a, e := strconv.ParseFloat(req.Formula, 64)
+		if e != nil {
+			err = fmt.Errorf("alpha值输入错误, 请重新输入")
+			return
+		}
+		if a <= 0 || a >= 1 {
+			err = fmt.Errorf("alpha值输入错误, 请重新输入")
+			return
+		}
+	}
+
+	notNeedFromEdbSourceList := []int{utils.DATA_SOURCE_CALCULATE_KSZS, utils.DATA_SOURCE_CALCULATE_CORRELATION, utils.DATA_SOURCE_CALCULATE_ZDYFX, utils.DATA_SOURCE_CALCULATE_SUM, utils.DATA_SOURCE_CALCULATE_AVG} // 不需要传入来源指标id的 指标类型
+	if fromEdbInfoId <= 0 && !utils.InArrayByInt(notNeedFromEdbSourceList, req.Source) {
+		err = fmt.Errorf("请选择指标")
+		return
+	}
+	//加入缓存机制,避免创建同一个名称的指标 start
+	redisKey := fmt.Sprint("edb_lib:edb_info:calculate:batch:save:", req.Source, ":", req.EdbName)
+	isExist := utils.Rc.IsExist(redisKey)
+	if isExist {
+		err = fmt.Errorf("指标正在处理,请勿重复提交")
+		return
+	} else {
+		//设置3分钟缓存
+		utils.Rc.SetNX(redisKey, 1, time.Second*300)
+		defer func() {
+			_ = utils.Rc.Delete(redisKey)
+		}()
+	}
+	//加入缓存机制,避免创建同一个名称的指标 end
+
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_name=? "
+	pars = append(pars, req.EdbName)
+
+	count, err := models.GetEdbInfoCountByCondition(condition, pars)
+	if err != nil {
+		err = fmt.Errorf("判断指标名称是否存在失败")
+		errMsg = "判断指标名称是否存在失败,Err:" + err.Error()
+		return
+	}
+
+	if count > 0 {
+		err = fmt.Errorf("指标名称已存在,请重新填写")
+		errMsg = "指标名称已存在,请重新填写"
+		return
+	}
+
+	// 来源指标
+	var fromEdbInfo *models.EdbInfo
+	if fromEdbInfoId > 0 {
+		fromEdbInfo, err = models.GetEdbInfoById(fromEdbInfoId)
+		if err != nil {
+			errMsg = "获取指标信息失败:Err:" + err.Error()
+			err = fmt.Errorf("获取指标信息失败")
+			return
+		}
+	}
+
+	//生成指标编码
+	randStr := utils.GetRandDigit(4)
+	edbCode := `C` + time.Now().Format("060102") + randStr
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	uniqueCode = utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
+
+	sysUserId := req.AdminId
+	sysUserName := req.AdminName
+
+	var sourName string
+	var edbInfoId int
+
+	var baseEdbInfoModel models.BaseEdbInfoInterface
+	addParam := models.AddCalculateBatchParams{
+		Req:             &req,
+		FromEdbInfo:     fromEdbInfo,
+		EdbCode:         edbCode,
+		UniqueCode:      uniqueCode,
+		SysUserId:       sysUserId,
+		SysUserRealName: sysUserName,
+	}
+
+	switch req.Source {
+	case utils.DATA_SOURCE_CALCULATE:
+		sourName = "计算指标"
+		req2 := models.EdbInfoCalculateSaveReq{
+			AdminId:          sysUserId,
+			AdminName:        sysUserName,
+			EdbName:          req.EdbName,
+			Frequency:        req.Frequency,
+			Unit:             req.Unit,
+			ClassifyId:       req.ClassifyId,
+			CalculateFormula: req.CalculateFormula,
+			//EdbInfoIdArr:     req.EdbInfoIdArr,
+			EmptyType:    req.EmptyType,
+			MaxEmptyType: req.MaxEmptyType,
+			Extra:        req.Extra,
+		}
+
+		// 放入A指标
+		t := models.EdbInfoFromTag{
+			EdbInfoId: req.FromEdbInfoId,
+			FromTag:   "A",
+		}
+		req2.EdbInfoIdArr = append(req2.EdbInfoIdArr, t)
+		// 放入B指标
+		if len(req.EdbInfoIdArr) > 0 {
+			t = models.EdbInfoFromTag{
+				EdbInfoId: req.EdbInfoIdArr[0].EdbInfoId,
+				FromTag:   "B",
+			}
+			req2.EdbInfoIdArr = append(req2.EdbInfoIdArr, t)
+		}
+
+		edbInfo, _, err, errMsg = EdbCalculateAdd(req2)
+	case utils.DATA_SOURCE_CALCULATE_LJZZY:
+		sourName = "累计值转月值"
+		if fromEdbInfo.Frequency != "月度" {
+			err = fmt.Errorf("请选择月度指标")
+			return
+		}
+		edbInfo, err = models.AddCalculateLjzzy(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_TBZ:
+		if req.Frequency != fromEdbInfo.Frequency {
+			err = fmt.Errorf("当前频度和原指标频度不一致")
+			return
+		}
+		if req.Unit != "无" {
+			err = fmt.Errorf("单位只允许为无,禁止选择其他单位")
+			return
+		}
+		sourName = "同比值"
+		edbInfo, err = models.AddCalculateTbz(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_TCZ:
+		if req.Frequency != fromEdbInfo.Frequency {
+			err = fmt.Errorf("当前频度和原指标频度不一致")
+			return
+		}
+		if req.Unit != fromEdbInfo.Unit {
+			err = fmt.Errorf("当前单位和原指标单位不一致")
+			return
+		}
+		sourName = "同差值"
+		edbInfo, err = models.AddCalculateTcz(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_NSZYDPJJS:
+		sourName = "N数值移动平均计算"
+		edbInfo, err = models.AddCalculateNszydpjjs(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName, formulaInt)
+	case utils.DATA_SOURCE_CALCULATE_HBZ:
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id =? "
+		pars = append(pars, fromEdbInfoId)
+		condition += " AND value <=0 "
+		checkCount, tmpErr := models.GetEdbDataCount(condition, pars, fromEdbInfo.Source, fromEdbInfo.SubSource)
+		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			err = fmt.Errorf("判断环比值是否可计算失败")
+			errMsg = "判断环比值是否可计算失败,Err:" + tmpErr.Error()
+			return
+		}
+		if checkCount > 0 {
+			err = fmt.Errorf("原始数据中存在0或负数,该指标不能进行环比运算")
+			errMsg = "原始数据中出现0和负值时,提示该指标不能进行环比运算"
+			return
+		}
+		sourName = "环比值"
+		edbInfo, err = models.AddCalculateHbz(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName, formulaInt)
+	case utils.DATA_SOURCE_CALCULATE_HCZ:
+		sourName = "环差值"
+		edbInfo, err = models.AddCalculateHcz(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName, formulaInt)
+	case utils.DATA_SOURCE_CALCULATE_BP:
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_BP
+		edbInfo, err = models.AddCalculateBp(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_TIME_SHIFT:
+		sourName = "时间移位"
+		edbInfo, err = models.AddCalculateTimeShift(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_ZJPJ:
+		sourName = "直接拼接"
+
+		if len(req.EdbInfoIdArr) != 1 {
+			err = fmt.Errorf("请传入拼接日期之后的指标")
+			errMsg = "请传入拼接日期之后的指标"
+			return
+		}
+
+		secondEdbInfoReq := req.EdbInfoIdArr[0]
+		secondEdbInfo, tmpErr := models.GetEdbInfoById(secondEdbInfoReq.EdbInfoId)
+		if tmpErr != nil {
+			err = fmt.Errorf("获取拼接日期之后的指标信息失败")
+			errMsg = "获取拼接日期之后的指标信息失败:Err:" + tmpErr.Error()
+			return
+		}
+
+		if fromEdbInfo.EdbInfoId == secondEdbInfo.EdbInfoId {
+			err = fmt.Errorf("两个指标不允许为同一个")
+			errMsg = "两个指标不允许为同一个"
+			return
+		}
+		edbInfo, err = models.AddCalculateZjpj(&req, fromEdbInfo, secondEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_LJZTBPJ:
+		sourName = "累计值同比拼接"
+
+		if fromEdbInfo.Frequency != "月度" {
+			err = fmt.Errorf("待拼接指标只能筛选月度指标")
+			errMsg = "待拼接指标只能筛选月度指标"
+			return
+		}
+		if len(req.EdbInfoIdArr) != 1 {
+			err = fmt.Errorf("请传入同比值指标")
+			errMsg = "请传入同比值指标"
+			return
+		}
+
+		secondEdbInfoReq := req.EdbInfoIdArr[0]
+		tbzEdbInfo, tmpErr := models.GetEdbInfoById(secondEdbInfoReq.EdbInfoId)
+		if tmpErr != nil {
+			err = fmt.Errorf("获取同比值指标信息失败")
+			errMsg = "获取同比值指标信息失败:Err:" + tmpErr.Error()
+			return
+		}
+		if tbzEdbInfo.Source != utils.DATA_SOURCE_CALCULATE_TBZ {
+			err = fmt.Errorf("指标必须是传入同比值指标类型")
+			errMsg = "指标必须是传入同比值指标类型"
+			return
+		}
+		if tbzEdbInfo.Frequency != "月度" {
+			err = fmt.Errorf("同比值指标只能筛选月度指标")
+			errMsg = "同比值指标只能筛选月度指标"
+			return
+		}
+
+		if fromEdbInfo.EdbInfoId == tbzEdbInfo.EdbInfoId {
+			err = fmt.Errorf("两个指标不允许为同一个")
+			errMsg = "两个指标不允许为同一个"
+			return
+		}
+		edbInfo, err = models.AddCalculateLjztbpj(&req, fromEdbInfo, tbzEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_CJJX:
+		sourName = "超季节性"
+		edbInfo, err = models.AddCalculateCjjx(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName, formulaInt)
+	case utils.DATA_SOURCE_CALCULATE_NHCC:
+		sourName = "拟合残差"
+
+		secondEdbInfoReq := req.EdbInfoIdArr[1]
+		secondEdbInfo, tmpErr := models.GetEdbInfoById(secondEdbInfoReq.EdbInfoId)
+		if tmpErr != nil {
+			err = fmt.Errorf("获取因变量的指标信息失败")
+			errMsg = "获取因变量的指标信息失败:Err:" + tmpErr.Error()
+			return
+		}
+
+		if fromEdbInfo.EdbInfoId == secondEdbInfo.EdbInfoId {
+			err = fmt.Errorf("两个指标不允许为同一个")
+			errMsg = "两个指标不允许为同一个"
+			return
+		}
+		edbInfo, err, errMsg = models.AddCalculateNhcc(&req, fromEdbInfo, secondEdbInfo, edbCode, uniqueCode, nhccDate, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_JP:
+		if !models.CheckFrequency(fromEdbInfo.Frequency, req.Frequency) {
+			err = fmt.Errorf("频度异常,不允许低频降频到高频")
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_JP
+		edbInfo, err = models.AddCalculateJp(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_NH:
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_NH
+		edbInfo, err = models.AddCalculateNh(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_KSZS:
+		//关联的指标信息
+		if len(req.EdbInfoIdArr) < 2 {
+			err = fmt.Errorf("指标数量不能小于2个,请重新选择")
+			errMsg = "指标数量不能小于2个,请重新选择"
+			//br.IsSendEmail = false
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_KSZS
+		edbInfo, err = models.AddCalculateKszs(&req, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_CORRELATION:
+		//关联的指标信息
+		if len(req.EdbInfoIdArr) < 2 {
+			err = fmt.Errorf("指标数量不能小于2个,请重新选择")
+			errMsg = "指标数量不能小于2个,请重新选择"
+			//br.IsSendEmail = false
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_CORRELATION
+		edbInfo, err, errMsg = models.AddCalculateCorrelation(&req, edbCode, uniqueCode, sysUserId, sysUserName)
+	case utils.DATA_SOURCE_CALCULATE_RJZ:
+		if req.Frequency == "日度" {
+			err = fmt.Errorf("日度指标无需进行日均值计算")
+			return
+		}
+		if req.Frequency != fromEdbInfo.Frequency {
+			err = fmt.Errorf("当前频度和原指标频度不一致")
+			return
+		}
+		if req.Unit != fromEdbInfo.Unit {
+			err = fmt.Errorf("单位只允许为和原指标频度保持一致,禁止选择其他单位")
+			return
+		}
+		sourName = "日均值"
+		edbInfo, err = models.AddCalculateRjz(&req, fromEdbInfo, edbCode, uniqueCode, sysUserId, sysUserName)
+	default:
+		// 获取通用的数据源处理服务
+		baseEdbInfoModel = models.GetBaseEdbInfoModel(req.Source)
+		// 没有找到的话,那么就直接返回报错吧
+		if baseEdbInfoModel == nil {
+			err = fmt.Errorf("无效计算方式")
+			errMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
+			return
+		}
+		sourName = baseEdbInfoModel.GetSourceName()
+		edbInfo, err, errMsg = baseEdbInfoModel.Add(addParam)
+	}
+
+	if err != nil {
+		errMsg = "生成" + sourName + "失败 Err:" + err.Error() + errMsg
+		err = fmt.Errorf("生成" + sourName + "失败")
+		return
+	}
+
+	if edbInfo == nil {
+		err = fmt.Errorf("生成" + sourName + "失败")
+		errMsg = "生成" + sourName + "失败,指标ID错误:" + strconv.Itoa(edbInfoId)
+		return
+	}
+	edbInfoId = edbInfo.EdbInfoId
+
+	//处理同名指标
+	{
+		edbNameList, tErr := models.GetEdbInfoByName(req.EdbName)
+		if tErr != nil {
+			err = fmt.Errorf("保存失败")
+			errMsg = "获取指标信息失败,Err:" + tErr.Error()
+			return
+		}
+		if len(edbNameList) >= 2 {
+			for _, v := range edbNameList {
+				edbName := v.EdbName + "(" + v.SourceName + ")"
+				err = models.ModifyEdbInfoNameSource(edbName, v.EdbInfoId)
+				if err != nil {
+					errMsg = "修改指标名称失败,Err:" + err.Error()
+					err = fmt.Errorf("保存失败")
+					return
+				}
+			}
+		}
+	}
+
+	// 更新指标最大最小值
+	err, errMsg = models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	if err != nil {
+		return
+	}
+
+	// 更新ES
+	go logic.UpdateEs(edbInfo.EdbInfoId)
+	return
+}
+
+// EdbCalculateBatchEdit 批量指标编辑
+func EdbCalculateBatchEdit(req models.EdbInfoCalculateBatchEditReq) (edbInfo *models.EdbInfo, uniqueCode string, err error, errMsg string) {
+	// 校验参数
+	req.EdbName = strings.Trim(req.EdbName, " ")
+	if req.EdbName == "" {
+		err = fmt.Errorf("指标名称不能为空")
+		return
+	}
+
+	if req.Frequency == "" {
+		err = fmt.Errorf("频率不能为空")
+		return
+	}
+
+	if req.Unit == "" {
+		err = fmt.Errorf("单位不能为空")
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		err = fmt.Errorf("请选择分类")
+		return
+	}
+
+	if req.EdbInfoId <= 0 {
+		err = fmt.Errorf("请选择指标")
+		return
+	}
+
+	//加入缓存机制,避免创建同一个名称的指标 start
+	redisKey := fmt.Sprint("edb_lib:edb_info:calculate:batch:save:", req.Source, ":", req.EdbName)
+	isExist := utils.Rc.IsExist(redisKey)
+	if isExist {
+		err = fmt.Errorf("指标正在处理,请勿重复提交")
+		return
+	} else {
+		//设置3分钟缓存
+		utils.Rc.SetNX(redisKey, 1, time.Second*300)
+		defer func() {
+			_ = utils.Rc.Delete(redisKey)
+		}()
+	}
+	//加入缓存机制,避免创建同一个名称的指标 end
+
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_name=?  AND edb_info_id<>? "
+	pars = append(pars, req.EdbName, req.EdbInfoId)
+
+	count, err := models.GetEdbInfoCountByCondition(condition, pars)
+	if err != nil {
+		err = fmt.Errorf("判断指标名称是否存在失败")
+		errMsg = "判断指标名称是否存在失败,Err:" + err.Error()
+		return
+	}
+
+	if count > 0 {
+		err = fmt.Errorf("指标名称已存在,请重新填写")
+		errMsg = "指标名称已存在,请重新填写"
+		return
+	}
+
+	edbInfo, err = models.GetEdbInfoById(req.EdbInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "指标已被删除,请刷新页面:Err:" + err.Error()
+			err = fmt.Errorf("指标已被删除,请刷新页面")
+
+			return
+		}
+		errMsg = "获取指标信息失败:Err:" + err.Error()
+		err = fmt.Errorf("获取指标信息失败")
+
+		return
+	}
+	// 基础指标id
+	fromEdbInfoId := req.FromEdbInfoId
+
+	if req.Source <= 0 {
+		req.Source = edbInfo.Source
+	}
+	var formulaInt int
+	var nhccDate models.NhccDate // 拟合残差的日期
+	// 初期的参数校验
+	switch req.Source {
+	case utils.DATA_SOURCE_CALCULATE_NSZYDPJJS, utils.DATA_SOURCE_CALCULATE_HBZ, utils.DATA_SOURCE_CALCULATE_HCZ, utils.DATA_SOURCE_CALCULATE_TIME_SHIFT, utils.DATA_SOURCE_CALCULATE_CJJX:
+		if req.Formula == "" {
+			err = fmt.Errorf("请填写N值")
+			return
+		}
+		formulaInt, _ = strconv.Atoi(req.Formula)
+		if formulaInt <= 0 {
+			err = fmt.Errorf("N值输入错误,请重新输入")
+			return
+		}
+	case utils.DATA_SOURCE_CALCULATE_ZJPJ:
+		//直接拼接指标
+
+		//校验时间格式
+		_, err = time.ParseInLocation(utils.FormatDate, req.Formula, time.Local)
+		if err != nil {
+			err = fmt.Errorf("拼接日期有误,请重新输入")
+			return
+		}
+	case utils.DATA_SOURCE_CALCULATE_NHCC: //拟合残差指标
+		//指标校验
+		if len(req.EdbInfoIdArr) != 2 {
+			err = fmt.Errorf("选择的指标异常,请重新选择")
+			return
+		}
+		fromEdbInfoId = req.EdbInfoIdArr[0].EdbInfoId
+
+		//校验时间格式
+		//数据格式:2022-11-01,2022-11-10
+		timeList := strings.Split(req.Formula, ",")
+		if len(timeList) != 2 {
+			err = fmt.Errorf("选择时间有误,请重新输入")
+			return
+		}
+		startDate, e := time.ParseInLocation(utils.FormatDate, timeList[0], time.Local)
+		if e != nil {
+			err = fmt.Errorf("开始日期有误,请重新输入")
+			return
+		}
+		endDate, e := time.ParseInLocation(utils.FormatDate, timeList[1], time.Local)
+		if e != nil {
+			err = fmt.Errorf("结束日期有误,请重新输入")
+			return
+		}
+		if utils.GetTimeSubDay(startDate, endDate) < 2 {
+			err = fmt.Errorf("日期间隔不得少于两天")
+			return
+		}
+		nhccDate.StartDate = startDate
+		nhccDate.EndDate = endDate
+	case utils.DATA_SOURCE_CALCULATE_JP:
+		if req.Formula != "期末值" && req.Formula != "平均值" {
+			err = fmt.Errorf("数据取值类型错误:" + req.Formula)
+			return
+		}
+	case utils.DATA_SOURCE_CALCULATE_ZSXY: // 指数修匀
+		if req.Formula == "" {
+			err = fmt.Errorf("请填写alpha值")
+			return
+		}
+		a, e := strconv.ParseFloat(req.Formula, 64)
+		if e != nil {
+			err = fmt.Errorf("alpha值输入错误, 请重新输入")
+			return
+		}
+		if a <= 0 || a >= 1 {
+			err = fmt.Errorf("alpha值输入错误, 请重新输入")
+			return
+		}
+	}
+	// 来源指标
+	var fromEdbInfo *models.EdbInfo
+	// 基础指标id
+	if fromEdbInfoId > 0 {
+		fromEdbInfo, err = models.GetEdbInfoById(fromEdbInfoId)
+		if err != nil {
+			errMsg = "获取指标信息失败:Err:" + err.Error()
+			err = fmt.Errorf("获取指标信息失败")
+			return
+		}
+	}
+
+	var sourName string
+	var edbInfoId int
+	var baseEdbInfoModel models.BaseEdbInfoInterface
+	editParams := models.EditCalculateBatchParams{
+		Req:         &req,
+		EdbInfo:     edbInfo,
+		FromEdbInfo: fromEdbInfo,
+	}
+
+	switch req.Source {
+	case utils.DATA_SOURCE_CALCULATE_LJZZY:
+		sourName = "累计值转月值"
+		if fromEdbInfo.Frequency != "月度" {
+			err = fmt.Errorf("请选择月度指标")
+			return
+		}
+		err = models.EditCalculateLjzzy(edbInfo, &req, fromEdbInfo)
+	case utils.DATA_SOURCE_CALCULATE_TBZ:
+		sourName = "同比值"
+		err = models.EditCalculateTbz(edbInfo, &req, fromEdbInfo)
+	case utils.DATA_SOURCE_CALCULATE_TCZ:
+		fmt.Println("start edit", time.Now())
+		sourName = "同差值"
+		err = models.EditCalculateTcz(edbInfo, &req, fromEdbInfo)
+		fmt.Println("end edit", time.Now())
+	case utils.DATA_SOURCE_CALCULATE_NSZYDPJJS:
+		sourName = "N数值移动平均计算"
+		err = models.EditCalculateNszydpjjs(edbInfo, &req, fromEdbInfo, formulaInt, edbInfo.CalculateFormula)
+	case utils.DATA_SOURCE_CALCULATE_HBZ:
+		var condition string
+		var pars []interface{}
+		condition += " AND edb_info_id =? "
+		pars = append(pars, req.FromEdbInfoId)
+		condition += " AND value <=0 "
+		checkCount, tmpErr := models.GetEdbDataCount(condition, pars, fromEdbInfo.Source, fromEdbInfo.SubSource)
+		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			errMsg = "判断环比值是否可计算失败,Err:" + tmpErr.Error()
+			err = fmt.Errorf("判断环比值是否可计算失败")
+			return
+		}
+		if checkCount > 0 {
+			err = fmt.Errorf("原始数据中存在0或负数,该指标不能进行环比运算")
+			errMsg = "原始数据中出现0和负值时,提示该指标不能进行环比运算"
+			return
+		}
+		sourName = "环比值"
+		err = models.EditCalculateHbz(edbInfo, &req, fromEdbInfo, formulaInt, edbInfo.CalculateFormula)
+	case utils.DATA_SOURCE_CALCULATE_HCZ:
+		sourName = "环差值"
+		err = models.EditCalculateHcz(edbInfo, &req, fromEdbInfo, formulaInt, edbInfo.CalculateFormula)
+	case utils.DATA_SOURCE_CALCULATE_BP:
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_BP
+		err = models.EditCalculateBp(edbInfo, &req, fromEdbInfo)
+	case utils.DATA_SOURCE_CALCULATE_TIME_SHIFT:
+		sourName = "时间移位"
+		err = models.EditCalculateTimeShift(edbInfo, &req, fromEdbInfo)
+	case utils.DATA_SOURCE_CALCULATE_ZJPJ:
+		sourName = "直接拼接"
+
+		if len(req.EdbInfoIdArr) != 1 {
+			err = fmt.Errorf("请传入拼接日期之后的指标")
+			errMsg = "请传入拼接日期之后的指标"
+			return
+		}
+
+		secondEdbInfoReq := req.EdbInfoIdArr[0]
+		secondEdbInfo, tmpErr := models.GetEdbInfoById(secondEdbInfoReq.EdbInfoId)
+		if tmpErr != nil {
+			errMsg = "获取拼接日期之后的指标信息失败:Err:" + tmpErr.Error()
+			err = fmt.Errorf("获取拼接日期之后的指标信息失败")
+			return
+		}
+
+		if fromEdbInfo.EdbInfoId == secondEdbInfo.EdbInfoId {
+			err = fmt.Errorf("两个指标不允许为同一个")
+			errMsg = "两个指标不允许为同一个"
+			return
+		}
+		err = models.EditCalculateZjpj(&req, edbInfo, fromEdbInfo, secondEdbInfo)
+	case utils.DATA_SOURCE_CALCULATE_LJZTBPJ: //累计值同比拼接
+		sourName = "累计值同比拼接"
+
+		if fromEdbInfo.Frequency != "月度" {
+			errMsg = "待拼接指标只能筛选月度指标"
+			err = fmt.Errorf("待拼接指标只能筛选月度指标")
+			return
+		}
+		if len(req.EdbInfoIdArr) != 1 {
+			errMsg = "请传入同比值指标"
+			err = fmt.Errorf("请传入同比值指标")
+			return
+		}
+
+		secondEdbInfoReq := req.EdbInfoIdArr[0]
+		tbzEdbInfo, tmpErr := models.GetEdbInfoById(secondEdbInfoReq.EdbInfoId)
+		if tmpErr != nil {
+			errMsg = "获取同比值指标信息失败:Err:" + tmpErr.Error()
+			err = fmt.Errorf("获取同比值指标信息失败")
+
+			return
+		}
+		if tbzEdbInfo.Source != utils.DATA_SOURCE_CALCULATE_TBZ {
+			err = fmt.Errorf("指标必须是传入同比值指标类型")
+			errMsg = "指标必须是传入同比值指标类型"
+			return
+		}
+		if tbzEdbInfo.Frequency != "月度" {
+			err = fmt.Errorf("同比值指标只能筛选月度指标")
+			errMsg = "同比值指标只能筛选月度指标"
+			return
+		}
+
+		if fromEdbInfo.EdbInfoId == tbzEdbInfo.EdbInfoId {
+			err = fmt.Errorf("两个指标不允许为同一个")
+			errMsg = "两个指标不允许为同一个"
+			return
+		}
+		err = models.EditCalculateLjztbpj(&req, edbInfo, fromEdbInfo, tbzEdbInfo)
+	case utils.DATA_SOURCE_CALCULATE_CJJX:
+		sourName = "超季节性"
+		err = models.EditCalculateCjjx(&req, edbInfo, fromEdbInfo, formulaInt)
+	case utils.DATA_SOURCE_CALCULATE_NHCC:
+		sourName = "拟合残差"
+		secondEdbInfoReq := req.EdbInfoIdArr[1]
+		secondEdbInfo, tmpErr := models.GetEdbInfoById(secondEdbInfoReq.EdbInfoId)
+		if tmpErr != nil {
+			errMsg = "获取因变量的指标信息失败:Err:" + tmpErr.Error()
+			err = fmt.Errorf("获取因变量的指标信息失败")
+
+			return
+		}
+
+		if fromEdbInfo.EdbInfoId == secondEdbInfo.EdbInfoId {
+			err = fmt.Errorf("两个指标不允许为同一个")
+			errMsg = "两个指标不允许为同一个"
+			return
+		}
+		err, errMsg = models.EditCalculateNhcc(&req, edbInfo, fromEdbInfo, secondEdbInfo, nhccDate)
+	case utils.DATA_SOURCE_CALCULATE_JP:
+		if !models.CheckFrequency(fromEdbInfo.Frequency, req.Frequency) {
+			err = fmt.Errorf("频度异常,不允许低频降频到高频")
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_JP
+		err = models.EditCalculateJp(edbInfo, &req, fromEdbInfo)
+	case utils.DATA_SOURCE_CALCULATE_NH:
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_NH
+		err = models.EditCalculateNh(edbInfo, &req, fromEdbInfo)
+	case utils.DATA_SOURCE_CALCULATE_KSZS:
+		//关联的指标信息
+		if len(req.EdbInfoIdArr) < 2 {
+			err = fmt.Errorf("指标数量不能小于2个,请重新选择")
+			errMsg = "指标数量不能小于2个,请重新选择"
+			//br.IsSendEmail = false
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_KSZS
+		err = models.EditCalculateKszs(edbInfo, &req)
+	case utils.DATA_SOURCE_CALCULATE_CORRELATION:
+		//关联的指标信息
+		if len(req.EdbInfoIdArr) < 2 {
+			err = fmt.Errorf("指标数量不能小于2个,请重新选择")
+			errMsg = "指标数量不能小于2个,请重新选择"
+			//br.IsSendEmail = false
+			return
+		}
+		sourName = utils.DATA_SOURCE_NAME_CALCULATE_CORRELATION
+		err, errMsg = models.EditCalculateCorrelation(edbInfo, &req)
+	case utils.DATA_SOURCE_CALCULATE_RJZ:
+		if req.Frequency == "日度" {
+			err = fmt.Errorf("日度指标无需进行日均值计算")
+			return
+		}
+		if req.Frequency != fromEdbInfo.Frequency {
+			err = fmt.Errorf("当前频度和原指标频度不一致")
+			return
+		}
+		if req.Unit != fromEdbInfo.Unit {
+			err = fmt.Errorf("单位只允许为和原指标频度保持一致,禁止选择其他单位")
+			return
+		}
+		sourName = "日均值"
+		err = models.EditCalculateRjz(edbInfo, &req, fromEdbInfo)
+	default:
+		// 获取通用的数据源处理服务
+		baseEdbInfoModel = models.GetBaseEdbInfoModel(req.Source)
+		// 没有找到的话,那么就直接返回报错吧
+		if baseEdbInfoModel == nil {
+			err = fmt.Errorf("无效计算方式")
+			errMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
+			return
+		}
+		sourName = baseEdbInfoModel.GetSourceName()
+		err, errMsg = baseEdbInfoModel.Edit(editParams)
+	}
+	if err != nil {
+		errMsg = "生成" + sourName + "失败 Err:" + err.Error() + errMsg
+		err = fmt.Errorf("生成" + sourName + "失败")
+		return
+	}
+	if edbInfo == nil {
+		err = fmt.Errorf("生成" + sourName + "失败")
+		errMsg = "生成" + sourName + "失败,指标ID错误:" + strconv.Itoa(edbInfoId)
+		return
+	}
+	edbInfoId = edbInfo.EdbInfoId
+	uniqueCode = edbInfo.UniqueCode
+	//处理同名指标
+	{
+		edbNameList, tErr := models.GetEdbInfoByName(req.EdbName)
+		if tErr != nil {
+			err = fmt.Errorf("保存失败")
+			errMsg = "获取指标信息失败,Err:" + tErr.Error()
+			return
+		}
+		if len(edbNameList) >= 2 {
+			for _, v := range edbNameList {
+				edbName := v.EdbName + "(" + v.SourceName + ")"
+				err = models.ModifyEdbInfoNameSource(edbName, v.EdbInfoId)
+				if err != nil {
+					errMsg = "修改指标名称失败,Err:" + err.Error()
+					err = fmt.Errorf("保存失败")
+					return
+				}
+			}
+		}
+	}
+
+	// 更新指标最大最小值
+	err, errMsg = models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	if err != nil {
+		return
+	}
+
+	// 更新ES
+	go logic.UpdateEs(edbInfo.EdbInfoId)
+	return
+}
+
+// EdbCalculateAdd 新增指标运算
+func EdbCalculateAdd(req models.EdbInfoCalculateSaveReq) (edbInfo *models.EdbInfo, uniqueCode string, err error, errMsg string) {
+	calculateFormula := req.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)
+	req.CalculateFormula = calculateFormula
+
+	//判断是否重复指标
+	edbInfoMap := make(map[int]string)
+	//移除研究员选择指标中的未使用的指标
+	{
+		//转大写的计算公式
+		upperCalculateFormulaStr := strings.ToUpper(req.CalculateFormula)
+		//用到的指标
+		newEdbInfoIdArr := make([]models.EdbInfoFromTag, 0)
+		for _, tmpEdbInfo := range req.EdbInfoIdArr {
+			_, ok := edbInfoMap[tmpEdbInfo.EdbInfoId]
+			if ok {
+				err = fmt.Errorf("选择指标失败,请勿选择重复指标!")
+				return
+			}
+			edbInfoMap[tmpEdbInfo.EdbInfoId] = tmpEdbInfo.FromTag
+
+			upperFromTag := strings.ToUpper(tmpEdbInfo.FromTag)
+			if strings.Contains(upperCalculateFormulaStr, upperFromTag) {
+				newEdbInfoIdArr = append(newEdbInfoIdArr, tmpEdbInfo)
+			}
+		}
+		req.EdbInfoIdArr = newEdbInfoIdArr
+	}
+
+	var condition string
+	var pars []interface{}
+	condition += " AND edb_name=? "
+	pars = append(pars, req.EdbName)
+
+	count, err := models.GetEdbInfoCountByCondition(condition, pars)
+	if err != nil {
+		errMsg = "判断指标名称是否存在失败,Err:" + err.Error()
+		err = fmt.Errorf("判断指标名称是否存在失败")
+		return
+	}
+
+	if count > 0 {
+		err = fmt.Errorf("指标名称已存在,请重新填写")
+		errMsg = "指标名称已存在,请重新填写"
+		//br.IsSendEmail = false
+		return
+	}
+	//检验公式
+	var formulaStr string
+	var edbInfoIdBytes []string
+	for _, v := range req.EdbInfoIdArr {
+		formulaStr += v.FromTag + ","
+		edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
+	}
+	formulaSlice, err := utils.CheckFormulaJson(req.CalculateFormula)
+	if err != nil {
+		err = fmt.Errorf("公式格式错误,请重新填写")
+		return
+	}
+	for _, formula := range formulaSlice {
+		formulaMap, e := utils.CheckFormula(formula)
+		if e != nil {
+			err = fmt.Errorf("公式错误,请重新填写")
+			return
+		}
+		for _, v := range formulaMap {
+			if !strings.Contains(formulaStr, v) {
+				err = fmt.Errorf("公式错误,请重新填写")
+				return
+			}
+		}
+	}
+
+	//关联的指标信息
+	edbInfoList := make([]*models.EdbInfo, 0)
+	//关联的指标数据表
+	calculateMappingList := make([]*models.EdbInfoCalculateMapping, 0)
+	for k, v := range req.EdbInfoIdArr {
+		fromEdbInfo, e := models.GetEdbInfoById(v.EdbInfoId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				err = fmt.Errorf("生成计算指标失败")
+				errMsg = "指标 " + strconv.Itoa(v.EdbInfoId) + " 不存在"
+				return
+			}
+			err = fmt.Errorf("生成计算指标失败")
+			errMsg = "获取指标失败:Err:" + e.Error()
+			return
+		}
+
+		edbInfoList = append(edbInfoList, fromEdbInfo)
+		//关联关系表
+		{
+			calculateMappingItem := &models.EdbInfoCalculateMapping{
+				EdbInfoCalculateMappingId: 0,
+				EdbInfoId:                 0,
+				Source:                    utils.DATA_SOURCE_CALCULATE,
+				SourceName:                "指标运算",
+				EdbCode:                   "",
+				FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+				FromEdbCode:               fromEdbInfo.EdbCode,
+				FromEdbName:               fromEdbInfo.EdbName,
+				FromSource:                fromEdbInfo.Source,
+				FromSourceName:            fromEdbInfo.SourceName,
+				FromTag:                   v.FromTag,
+				Sort:                      k + 1,
+				CreateTime:                time.Now(),
+				ModifyTime:                time.Now(),
+			}
+			//calculateMappingItem.EdbCode = edbCode
+			//calculateMappingItem.EdbInfoId = int(edbInfoId)
+			calculateMappingList = append(calculateMappingList, calculateMappingItem)
+		}
+	}
+
+	for _, v := range formulaSlice {
+		formulaMap, e := utils.CheckFormula(v)
+		if e != nil {
+			err = fmt.Errorf("公式错误,请重新填写")
+			return
+		}
+		//预先计算,判断公式是否正常
+		ok, _ := models.CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
+		if !ok {
+			err = fmt.Errorf("生成计算指标失败,请使用正确的计算公式")
+			return
+		}
+	}
+
+	randStr := utils.GetRandDigit(4)
+	edbCode := `C` + time.Now().Format("060102") + randStr
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	uniqueCode = utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
+
+	// 新增计算指标
+	edbInfo, err = models.AddCalculateInfo(req, calculateMappingList, edbInfoList, edbCode, uniqueCode, edbInfoIdBytes)
+	if err != nil {
+		errMsg = "生成计算指标失败,AddEdbInfo Err:" + err.Error()
+		err = fmt.Errorf("生成计算指标失败")
+		return
+	}
+
+	return
+}

+ 18 - 3
services/base_from_predict.go

@@ -35,7 +35,12 @@ func GetCalculateByRuleByNineParams(req models.RuleConfig) (formula string, edbI
 		return
 	}
 	for _, f := range formulaSlice {
-		formulaMap := utils.CheckFormula(f)
+		formulaMap, e := utils.CheckFormula(f)
+		if e != nil {
+			err = fmt.Errorf("公式错误,请重新填写")
+			return
+		}
+
 		for _, v := range formulaMap {
 			if !strings.Contains(checkFormulaStr, v) {
 				errMsg = "公式错误,请重新填写"
@@ -63,7 +68,12 @@ func GetCalculateByRuleByNineParams(req models.RuleConfig) (formula string, edbI
 	}
 
 	for _, v := range formulaSlice {
-		formulaMap := utils.CheckFormula(v)
+		formulaMap, e := utils.CheckFormula(v)
+		if e != nil {
+			err = fmt.Errorf("公式错误,请重新填写")
+			return
+		}
+
 		//预先计算,判断公式是否正常
 		ok, _ := models.CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
 		if !ok {
@@ -178,7 +188,12 @@ func CalculateByRuleByNine(formulaStr string, edbInfoList []*models.EdbInfo, edb
 			if date < fv {
 				if f, ok := formulaDateMap[fv]; ok {
 					formulaStr = f
-					formulaMap = utils.CheckFormula(formulaStr)
+					formulaMap, err = utils.CheckFormula(formulaStr)
+					if err != nil {
+						err = fmt.Errorf("公式错误,请重新填写")
+						return
+					}
+
 				}
 				break
 			}

+ 18 - 4
utils/base_from_calculate.go

@@ -9,7 +9,7 @@ import (
 )
 
 // CheckFormula 检测计算公式是否异常
-func CheckFormula(formula string) map[string]string {
+func CheckFormula(formula string) (byteMap map[string]string, err error) {
 	mathFormula := []string{"MAX", "MIN", "ABS", "ACOS", "ASIN", "CEIL", "MOD", "POW", "ROUND", "SIGN", "SIN", "TAN", "LOG10", "LOG2", "LOG", "LN", "EXP"}
 
 	str := strings.ToUpper(formula)
@@ -19,17 +19,31 @@ func CheckFormula(formula string) map[string]string {
 	str = strings.Replace(str, "(", "", -1)
 	str = strings.Replace(str, ")", "", -1)
 
-	byteMap := make(map[string]string)
+	byteMap = make(map[string]string)
 	for i := 0; i < len(str); i++ {
 		byteInt := str[i]
 		if byteInt >= 65 && byteInt <= 90 {
-			byteStr := string(byteInt)
+			// 判断前一位是否是数字,如果是数字就报错
+			if i > 0 {
+				if str[i-1] >= '0' && str[i-1] <= '9' {
+					err = fmt.Errorf("计算公式异常,请检查公式:%s", formula)
+					return
+				}
+			}
+			byteStr := string(byteInt) //获取计算公式中的占位符A,B, AA,AB
+			if i+1 < len(str) {
+				next := str[i+1]
+				if next >= 65 && next <= 90 {
+					byteStr += string(next)
+					i++
+				}
+			}
 			if _, ok := byteMap[byteStr]; !ok {
 				byteMap[byteStr] = byteStr
 			}
 		}
 	}
-	return byteMap
+	return
 }
 
 type FormulaListItem struct {

+ 12 - 6
utils/constants.go

@@ -102,9 +102,12 @@ const (
 	DATA_SOURCE_PREDICT_CALCULATE_ZSXY                          // 预测指数修匀->73
 	DATA_SOURCE_CALCULATE_ZDYFX                                 // 自定义分析->74
 	DATA_SOURCE_CALCULATE_RJZ                                   // 日均值计算->75
-	DATA_SOURCE_GFEX                                 = 78       // 广州期货交易所->78
-	DATA_SOURCE_ICPI                                 = 79       // ICPI消费价格指数->79
-	DATA_SOURCE_MTJH                                 = 80       // 煤炭江湖->80
+
+	DATA_SOURCE_GFEX          = 78 // 广州期货交易所->78
+	DATA_SOURCE_ICPI          = 79 // ICPI消费价格指数->79
+	DATA_SOURCE_MTJH          = 80 // 煤炭江湖->80
+	DATA_SOURCE_CALCULATE_SUM = 81
+	DATA_SOURCE_CALCULATE_AVG = 82
 )
 
 // 指标来源的中文展示
@@ -184,9 +187,12 @@ const (
 	DATA_SOURCE_NAME_PREDICT_CALCULATE_ZSXY               = `预测指数修匀`            //预测指数修匀->73
 	DATA_SOURCE_NAME_CALCULATE_ZDYFX                      = `自定义分析`             //自定义分析->74
 	DATA_SOURCE_NAME_YONYI                                = `涌益咨询`              // 涌益咨询
-	DATA_SOURCE_NAME_FENWEI                               = `汾渭数据`              // 汾渭煤炭
-	DATA_SOURCE_NAME_MTJH                                 = `煤炭江湖`              // 煤炭江湖->80
-	DATA_SOURCE_NAME_ICPI                                 = "ICPI消费价格指数"
+
+	DATA_SOURCE_NAME_FENWEI        = `汾渭数据` // 汾渭煤炭
+	DATA_SOURCE_NAME_MTJH          = `煤炭江湖` // 煤炭江湖->80
+	DATA_SOURCE_NAME_ICPI          = "ICPI消费价格指数"
+	DATA_SOURCE_NAME_CALCULATE_SUM = `多指标求和`
+	DATA_SOURCE_NAME_CALCULATE_AVG = `多指标求平均`
 )
 
 // 基础数据初始化日期