Browse Source

Merge branch 'master' into ETA_1.4.5

zwxi 1 year ago
parent
commit
aaf5907ab3

+ 1 - 0
.gitignore

@@ -6,6 +6,7 @@
 .DS_Store
 binlog/
 /eta_index_lib.exe
+/eta_index_lib.tar.gz
 /eta_index_lib.exe~
 eta_index_lib
 /static/imgs/python/*

+ 44 - 19
controllers/base_from_calculate.go

@@ -139,11 +139,18 @@ func (this *CalculateController) Add() {
 		formulaStr += v.FromTag + ","
 		edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
 	}
-	formulaMap := utils.CheckFormula(req.CalculateFormula)
-	for _, v := range formulaMap {
-		if !strings.Contains(formulaStr, v) {
-			br.Msg = "公式错误,请重新填写"
-			return
+	formulaSlice, err := utils.CheckFormulaJson(req.CalculateFormula)
+	if err != nil {
+		br.Msg = "公式格式错误,请重新填写"
+		return
+	}
+	for _, formula := range formulaSlice {
+		formulaMap := utils.CheckFormula(formula)
+		for _, v := range formulaMap {
+			if !strings.Contains(formulaStr, v) {
+				br.Msg = "公式错误,请重新填写"
+				return
+			}
 		}
 	}
 
@@ -188,10 +195,15 @@ func (this *CalculateController) Add() {
 			calculateMappingList = append(calculateMappingList, calculateMappingItem)
 		}
 	}
-	ok, _ := models.CheckFormula2(edbInfoList, formulaMap, calculateFormula, edbInfoIdBytes)
-	if !ok {
-		br.Msg = "生成计算指标失败,请使用正确的计算公式"
-		return
+
+	for _, v := range formulaSlice {
+		formulaMap := utils.CheckFormula(v)
+		//预先计算,判断公式是否正常
+		ok, _ := models.CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
+		if !ok {
+			br.Msg = "生成计算指标失败,请使用正确的计算公式"
+			return
+		}
 	}
 
 	randStr := utils.GetRandDigit(4)
@@ -298,10 +310,12 @@ func (this *CalculateController) Edit() {
 	}
 
 	if req.CalculateFormula == "" {
-		br.Msg = "请填写指标"
+		br.Msg = "请填写指标公式"
 		return
 	}
 
+	//todo 校验空值类型,max、min空值类型
+
 	calculateFormula := req.CalculateFormula
 	calculateFormula = strings.Replace(calculateFormula, "(", "(", -1)
 	calculateFormula = strings.Replace(calculateFormula, ")", ")", -1)
@@ -371,7 +385,7 @@ func (this *CalculateController) Edit() {
 	}
 	var needCalculate bool
 
-	if edbInfoDetail.CalculateFormula != req.CalculateFormula {
+	if edbInfoDetail.CalculateFormula != req.CalculateFormula || edbInfoDetail.EmptyType != req.EmptyType || edbInfoDetail.MaxEmptyType != req.MaxEmptyType {
 		needCalculate = true
 	}
 
@@ -403,16 +417,23 @@ func (this *CalculateController) Edit() {
 		}
 	}
 
-	formulaMap := utils.CheckFormula(req.CalculateFormula)
-	for _, v := range formulaMap {
-		if !strings.Contains(formulaStr, v) {
-			br.Msg = "公式错误,请重新填写"
-			return
+	formulaSlice, err := utils.CheckFormulaJson(req.CalculateFormula)
+	if err != nil {
+		br.Msg = "公式格式错误,请重新填写"
+		return
+	}
+	for _, formula := range formulaSlice {
+		formulaMap := utils.CheckFormula(formula)
+		for _, v := range formulaMap {
+			if !strings.Contains(formulaStr, v) {
+				br.Msg = "公式错误,请重新填写"
+				return
+			}
 		}
 	}
 
 	//重新计算
-	err, errMsg := models.EditCalculateInfo(edbInfoDetail, req, formulaMap, edbInfoIdBytes, needCalculate)
+	err, errMsg := models.EditCalculateInfo(edbInfoDetail, req, formulaSlice, edbInfoIdBytes, needCalculate)
 	if err != nil {
 		br.Msg = "生成计算指标失败"
 		if errMsg != `` {
@@ -1409,7 +1430,7 @@ func (this *CalculateController) Refresh() {
 			edbInfo, _ := models.GetEdbInfoById(v.FromEdbInfoId)
 			edbInfoList = append(edbInfoList, edbInfo)
 		}
-		err = models.RefreshAllCalculate(edbInfoList, edbInfo.EdbInfoId, source, subSource, edbInfo.EdbCode, edbInfo.CalculateFormula, startDate, endDate, edbInfoIdBytes)
+		err = models.RefreshAllCalculate(edbInfoList, edbInfo.EdbInfoId, source, subSource, edbInfo.EdbCode, edbInfo.CalculateFormula, startDate, endDate, edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType)
 		if err != nil && err.Error() != utils.ErrNoRow() {
 			errMsg = "RefreshCalculate Err:" + err.Error()
 			break
@@ -1963,7 +1984,11 @@ func (this *CalculateController) CalculateComputeCorrelation() {
 		return
 	}
 
-	valStr := fmt.Sprintf("y = %sx + %s   R²=%f", utils.SubFloatToString(aVal, 4), utils.SubFloatToString(bVal, 4), rVal)
+	connStr := ``
+	if bVal >= 0 {
+		connStr = `+`
+	}
+	valStr := fmt.Sprintf("y = %sx %s %s   R²=%f", utils.SubFloatToString(aVal, 4), connStr, utils.SubFloatToString(bVal, 4), rVal)
 
 	br.Ret = 200
 	br.Success = true

+ 72 - 0
controllers/base_from_predict.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"eta/eta_index_lib/logic"
 	"eta/eta_index_lib/models"
+	"eta/eta_index_lib/services"
 	"eta/eta_index_lib/utils"
 	"fmt"
 	"strconv"
@@ -183,3 +184,74 @@ func (this *PredictController) Refresh() {
 	br.Success = true
 	br.Msg = "获取成功"
 }
+
+// CalculateByNinePreview
+// @Title 动态环差演算
+// @Description 指标运算演算
+// @Param request body models.RuleConfig true "type json string"
+// @Success Ret=200 返回指标id
+// @router /calculate_by_nine/preview [post]
+func (this *PredictController) CalculateByNinePreview() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req models.RuleConfig
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	var errMsg string
+	// 获取计算参数
+	formula, edbInfoList, edbInfoIdBytes, err, errMsg := services.GetCalculateByRuleByNineParams(req)
+	if err != nil {
+		br.Msg = "计算失败"
+		if errMsg != "" {
+			br.Msg = errMsg
+		}
+		br.Msg = err.Error()
+		br.IsSendEmail = false
+		return
+	}
+	// 获取计算数据
+	dataList, err := services.CalculateByRuleByNine(formula, edbInfoList, edbInfoIdBytes, req.EmptyType, req.MaxEmptyType)
+	if err != nil {
+		br.Msg = "数据计算失败"
+		br.ErrMsg = "数据计算失败:Err:" + err.Error()
+		br.IsSendEmail = false
+		return
+	}
+
+	latestDate := time.Now()
+	for _, v := range edbInfoList {
+		tmpLatestDate, err := time.ParseInLocation(utils.FormatDate, v.LatestDate, time.Local)
+		if err != nil {
+			continue
+		}
+		if tmpLatestDate.Before(latestDate) {
+			latestDate = tmpLatestDate
+		}
+	}
+
+	newDataList := make([]*models.EdbDataList, 0)
+	lenData := len(dataList)
+	if lenData > 0 {
+		for i := lenData - 1; i >= 0; i-- {
+			newDataList = append(newDataList, dataList[i])
+		}
+	}
+	resp := models.PredictRuleCalculateByNineResp{
+		LatestDate: latestDate.Format(utils.FormatDate),
+		DataList:   newDataList,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 46 - 23
controllers/base_from_predict_calculate.go

@@ -106,11 +106,18 @@ func addPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSav
 		formulaStr += v.FromTag + ","
 		edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
 	}
-	formulaMap := utils.CheckFormula(req.CalculateFormula)
-	for _, v := range formulaMap {
-		if !strings.Contains(formulaStr, v) {
-			br.Msg = "公式错误,请重新填写"
-			return
+	formulaSlice, err := utils.CheckFormulaJson(req.CalculateFormula)
+	if err != nil {
+		br.Msg = "公式格式错误,请重新填写"
+		return
+	}
+	for _, formula := range formulaSlice {
+		formulaMap := utils.CheckFormula(formula)
+		for _, v := range formulaMap {
+			if !strings.Contains(formulaStr, v) {
+				br.Msg = "公式错误,请重新填写"
+				return
+			}
 		}
 	}
 
@@ -173,11 +180,14 @@ func addPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSav
 		}
 	}
 
-	//预先计算,判断公式是否正常
-	ok, _ := models.CheckFormula2(edbInfoList, formulaMap, calculateFormula, edbInfoIdBytes)
-	if !ok {
-		br.Msg = "生成计算指标失败,请使用正确的计算公式"
-		return
+	for _, v := range formulaSlice {
+		formulaMap := utils.CheckFormula(v)
+		//预先计算,判断公式是否正常
+		ok, _ := models.CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
+		if !ok {
+			br.Msg = "生成计算指标失败,请使用正确的计算公式"
+			return
+		}
 	}
 
 	// 指标入库
@@ -202,6 +212,8 @@ func addPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSav
 		UniqueCode:       uniqueCode,
 		CalculateFormula: req.CalculateFormula,
 		EdbType:          2,
+		EmptyType:        req.EmptyType,
+		MaxEmptyType:     req.MaxEmptyType,
 	}
 	edbInfoId, err := models.AddEdbInfo(edbInfo)
 	if err != nil {
@@ -308,7 +320,7 @@ func editPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSa
 	isExist := utils.Rc.IsExist(redisKey)
 	if isExist {
 		br.Msg = "指标正在处理,请勿重复提交"
-		return
+		//	return
 	} else {
 		//设置3分钟缓存
 		utils.Rc.SetNX(redisKey, 1, time.Second*300)
@@ -334,11 +346,18 @@ func editPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSa
 		formulaStr += v.FromTag + ","
 		edbInfoIdBytes = append(edbInfoIdBytes, v.FromTag)
 	}
-	formulaMap := utils.CheckFormula(req.CalculateFormula)
-	for _, v := range formulaMap {
-		if !strings.Contains(formulaStr, v) {
-			br.Msg = "公式错误,请重新填写"
-			return
+	formulaSlice, err := utils.CheckFormulaJson(req.CalculateFormula)
+	if err != nil {
+		br.Msg = "公式格式错误,请重新填写"
+		return
+	}
+	for _, formula := range formulaSlice {
+		formulaMap := utils.CheckFormula(formula)
+		for _, v := range formulaMap {
+			if !strings.Contains(formulaStr, v) {
+				br.Msg = "公式错误,请重新填写"
+				return
+			}
 		}
 	}
 
@@ -407,12 +426,14 @@ func editPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSa
 			calculateMappingList = append(calculateMappingList, calculateMappingItem)
 		}
 	}
-
-	//预先计算,判断公式是否正常
-	ok, _ := models.CheckFormula2(edbInfoList, formulaMap, calculateFormula, edbInfoIdBytes)
-	if !ok {
-		br.Msg = "生成计算指标失败,请使用正确的计算公式"
-		return
+	for _, v := range formulaSlice {
+		formulaMap := utils.CheckFormula(v)
+		//预先计算,判断公式是否正常
+		ok, _ := models.CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
+		if !ok {
+			br.Msg = "生成计算指标失败,请使用正确的计算公式"
+			return
+		}
 	}
 
 	//处理同名指标
@@ -441,6 +462,8 @@ func editPredictCalculate(br *models.BaseResponse, req models.EdbInfoCalculateSa
 	edbInfo.Unit = req.Unit
 	edbInfo.ClassifyId = req.ClassifyId
 	edbInfo.CalculateFormula = req.CalculateFormula
+	edbInfo.EmptyType = req.EmptyType
+	edbInfo.MaxEmptyType = req.MaxEmptyType
 	err = models.EditPredictCalculate(edbInfo, calculateMappingList)
 	if err != nil {
 		br.Msg = "修改计算指标失败"
@@ -952,7 +975,7 @@ func (this *PredictCalculateController) Refresh() {
 			edbInfo, _ := models.GetEdbInfoById(v.FromEdbInfoId)
 			edbInfoList = append(edbInfoList, edbInfo)
 		}
-		latestDateStr, latestValue, err = models.RefreshAllPredictCalculate(edbInfoList, edbInfo.EdbInfoId, source, edbInfo.SubSource, edbInfo.EdbCode, edbInfo.CalculateFormula, startDate, edbInfoIdBytes)
+		latestDateStr, latestValue, err = models.RefreshAllPredictCalculate(edbInfoList, edbInfo.EdbInfoId, source, edbInfo.SubSource, edbInfo.EdbCode, edbInfo.CalculateFormula, startDate, edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType)
 		if err != nil && err.Error() != utils.ErrNoRow() {
 			errMsg = "RefreshCalculate Err:" + err.Error()
 			break

+ 174 - 0
controllers/base_from_yongyi.go

@@ -0,0 +1,174 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_index_lib/logic"
+	"eta/eta_index_lib/models"
+	"eta/eta_index_lib/services"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"strconv"
+	"time"
+)
+
+// YongyiController 涌益咨询
+type YongyiController struct {
+	BaseAuthController
+}
+
+// Add
+// @Title 新增涌益咨询指标接口
+// @Description 新增涌益咨询指标接口
+// @Success 200 {object} models.AddEdbInfoReq
+// @router /add [post]
+func (this *YongyiController) Add() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	yongyi := new(models.BaseFromYongyi)
+	source := yongyi.GetSource()
+	var req models.AddEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+	cacheKey = utils.CACHE_EDB_DATA_ADD + strconv.Itoa(source) + "_" + req.EdbCode
+	if utils.Rc.IsExist(cacheKey) {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+	}()
+	err = yongyi.Add(req.EdbCode)
+	if err != nil {
+		br.Msg = "获取指标信息失败!"
+		br.ErrMsg = "获取指标信息失败 AddEdbDataFromYongyi,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	return
+}
+
+// Refresh
+// @Title 刷新涌益咨询指标接口
+// @Description 刷新涌益咨询指标接口
+// @Success 200 {object} models.RefreshEdbInfoReq
+// @router /refresh [post]
+func (this *YongyiController) Refresh() {
+	br := new(models.BaseResponse).Init()
+	var cacheKey string
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	yongyi := new(models.BaseFromYongyi)
+	source := yongyi.GetSource()
+	var req models.RefreshEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EdbCode == "" {
+		br.Msg = "请输入指标编码!"
+		br.ErrMsg = "请输入指标编码,指标编码为空"
+		return
+	}
+	if req.EdbInfoId < 0 {
+		br.Msg = "请输入指标ID!"
+		br.ErrMsg = "请输入指标ID"
+		return
+	}
+
+	// 获取指标详情
+	edbInfo, err := models.GetEdbInfoByEdbCode(source, req.EdbCode)
+	if err != nil {
+		br.Msg = "指标不存在!"
+		br.ErrMsg = "指标不存在"
+		return
+	}
+	cacheKey = utils.CACHE_EDB_DATA_REFRESH + strconv.Itoa(source) + "_" + req.EdbCode
+	if utils.Rc.IsExist(cacheKey) {
+		br.Ret = 501
+		br.Success = true
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 1*time.Minute)
+	defer func() {
+		utils.Rc.Delete(cacheKey)
+	}()
+	if req.EdbInfoId <= 0 {
+		req.EdbInfoId = edbInfo.EdbInfoId
+	}
+	err = yongyi.Refresh(req.EdbInfoId, req.EdbCode, req.StartDate)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "刷新指标信息失败!"
+		br.ErrMsg = "刷新指标信息失败 RefreshEdbDataFromYongyi,Err:" + err.Error()
+		return
+	}
+	// 更新指标最大最小值
+	err, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	// 更新ES
+	go logic.UpdateEs(edbInfo.EdbInfoId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// HandleExcelData
+// @Title 处理涌益咨询指标的接口
+// @Description 处理涌益咨询指标的接口
+// @Success 200 {object} models.HandleYongyiExcelDataReq
+// @router /handle/excel_data [post]
+func (this *YongyiController) HandleExcelData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.HandleYongyiExcelDataReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	// 处理excel表数据
+	err = services.HandleYongyiIndex(&req)
+	if err != nil {
+		fmt.Println("HandleMysteelIndex Err:" + err.Error())
+		br.Msg = "处理失败"
+		br.ErrMsg = "处理失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "处理成功"
+}

+ 0 - 11
logic/edbinfo.go

@@ -4,7 +4,6 @@ import (
 	"eta/eta_index_lib/models"
 	"eta/eta_index_lib/services/elastic"
 	"eta/eta_index_lib/utils"
-	"fmt"
 	"strconv"
 )
 
@@ -14,13 +13,3 @@ func UpdateEs(edbInfoId int) {
 	itemInfo, _ := models.GetEdbInfoItemByCondition("AND edb_info_id=?", []interface{}{edbInfoId})
 	elastic.EsAddOrEditEdbInfoData(utils.DATA_INDEX_NAME, strconv.Itoa(itemInfo.EdbInfoId), itemInfo)
 }
-
-// UpdateAllEdbInfoEs 更新所有的指标ES的信息
-func UpdateAllEdbInfoEs() {
-	//添加es
-	list, _ := models.GetEdbInfoItemListByCondition()
-	for _, itemInfo := range list {
-		elastic.EsAddOrEditEdbInfoData(utils.DATA_INDEX_NAME, strconv.Itoa(itemInfo.EdbInfoId), itemInfo)
-	}
-	fmt.Println("修复完成")
-}

+ 70 - 33
logic/predict_edb.go

@@ -199,12 +199,20 @@ func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName, dataDateType st
 				formulaStr += tmpEdbInfoId.FromTag + ","
 				edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
 			}
-			formulaMap := utils.CheckFormula(formula)
-			for _, formula := range formulaMap {
-				if !strings.Contains(formulaStr, formula) {
-					errMsg = "公式错误,请重新填写"
-					err = errors.New(errMsg)
-					return
+			formulaSlice, tErr := utils.CheckFormulaJson(formula)
+			if tErr != nil {
+				errMsg = "公式格式错误,请重新填写"
+				err = errors.New(errMsg)
+				return
+			}
+			for _, fm := range formulaSlice {
+				formulaMap := utils.CheckFormula(fm)
+				for _, f := range formulaMap {
+					if !strings.Contains(formulaStr, f) {
+						errMsg = "公式错误,请重新填写"
+						err = errors.New(errMsg)
+						return
+					}
 				}
 			}
 
@@ -269,12 +277,17 @@ func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName, dataDateType st
 				}
 				trendsMappingList = append(trendsMappingList, tmpPredictEdbConfCalculateMapping)
 			}
-			ok, _ := models.CheckFormula2(edbInfoList, formulaMap, formula, edbInfoIdBytes)
-			if !ok {
-				errMsg = "生成计算指标失败,请使用正确的计算公式"
-				err = errors.New(errMsg)
-				return
+			for _, f := range formulaSlice {
+				formulaMap := utils.CheckFormula(f)
+				//预先计算,判断公式是否正常
+				ok, _ := models.CheckFormula2(edbInfoList, formulaMap, f, edbInfoIdBytes)
+				if !ok {
+					errMsg = "生成计算指标失败,请使用正确的计算公式"
+					err = errors.New(errMsg)
+					return
+				}
 			}
+
 			calculateRuleMap[ruleIndex] = models.CalculateRule{
 				TrendsCalculateMappingList: trendsMappingList,
 				EdbInfoList:                edbInfoList,
@@ -569,12 +582,21 @@ func EditPredictEdbInfo(edbInfoId, classifyId int, edbName, dataDateType string,
 				formulaStr += tmpEdbInfoId.FromTag + ","
 				edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
 			}
-			formulaMap := utils.CheckFormula(formula)
-			for _, formula := range formulaMap {
-				if !strings.Contains(formulaStr, formula) {
-					errMsg = "公式错误,请重新填写"
-					err = errors.New(errMsg)
-					return
+
+			formulaSlice, tErr := utils.CheckFormulaJson(formula)
+			if tErr != nil {
+				errMsg = "公式格式错误,请重新填写"
+				err = errors.New(errMsg)
+				return
+			}
+			for _, fm := range formulaSlice {
+				formulaMap := utils.CheckFormula(fm)
+				for _, f := range formulaMap {
+					if !strings.Contains(formulaStr, f) {
+						errMsg = "公式错误,请重新填写"
+						err = errors.New(errMsg)
+						return
+					}
 				}
 			}
 
@@ -639,13 +661,17 @@ func EditPredictEdbInfo(edbInfoId, classifyId int, edbName, dataDateType string,
 				}
 				trendsMappingList = append(trendsMappingList, tmpPredictEdbConfCalculateMapping)
 			}
-			ok, _ := models.CheckFormula2(edbInfoList, formulaMap, formula, edbInfoIdBytes)
-			if !ok {
-				errMsg = "生成计算指标失败,请使用正确的计算公式"
-				err = errors.New(errMsg)
-				return
-			}
 
+			for _, f := range formulaSlice {
+				formulaMap := utils.CheckFormula(f)
+				//预先计算,判断公式是否正常
+				ok, _ := models.CheckFormula2(edbInfoList, formulaMap, f, edbInfoIdBytes)
+				if !ok {
+					errMsg = "生成计算指标失败,请使用正确的计算公式"
+					err = errors.New(errMsg)
+					return
+				}
+			}
 			calculateRuleMap[ruleIndex] = models.CalculateRule{
 				TrendsCalculateMappingList: trendsMappingList,
 				EdbInfoList:                edbInfoList,
@@ -842,6 +868,7 @@ func RefreshPredictEdbInfo(edbInfoId int) (edbInfo *models.EdbInfo, err error, e
 				errMsg = "请填写计算规则"
 				return
 			}
+			// todo 动态环差的空值类型处理
 			formula := v.Value
 
 			// 动态环差规则 关系表
@@ -872,19 +899,29 @@ func RefreshPredictEdbInfo(edbInfoId int) (edbInfo *models.EdbInfo, err error, e
 				formulaStr += tmpEdbInfoId.FromTag + ","
 				edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
 			}
-			formulaMap := utils.CheckFormula(formula)
-			for _, formula := range formulaMap {
-				if !strings.Contains(formulaStr, formula) {
-					errMsg = "公式错误,请重新填写"
-					return
-				}
-			}
 
-			ok, _ := models.CheckFormula2(edbInfoList, formulaMap, formula, edbInfoIdBytes)
-			if !ok {
-				errMsg = "生成计算指标失败,请使用正确的计算公式"
+			formulaSlice, tErr := utils.CheckFormulaJson(formula)
+			if tErr != nil {
+				errMsg = "公式格式错误,请重新填写"
+				err = errors.New(errMsg)
 				return
 			}
+			for _, fm := range formulaSlice {
+				formulaMap := utils.CheckFormula(fm)
+				for _, f := range formulaMap {
+					if !strings.Contains(formulaStr, f) {
+						errMsg = "公式错误,请重新填写"
+						err = errors.New(errMsg)
+						return
+					}
+				}
+				//预先计算,判断公式是否正常
+				ok, _ := models.CheckFormula2(edbInfoList, formulaMap, fm, edbInfoIdBytes)
+				if !ok {
+					errMsg = "生成计算指标失败,请使用正确的计算公式"
+					return
+				}
+			}
 
 			rule := models.CalculateRule{
 				EdbInfoId:                  v.PredictEdbInfoId,

+ 243 - 130
models/base_from_calculate.go

@@ -22,6 +22,8 @@ type EdbInfoCalculateSaveReq struct {
 	Unit             string `description:"单位"`
 	ClassifyId       int    `description:"分类id"`
 	CalculateFormula string `description:"计算公式"`
+	EmptyType        int    `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int    `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
 	EdbInfoIdArr     []EdbInfoFromTag
 }
 
@@ -37,92 +39,6 @@ type CalculateItems struct {
 	DataMap   map[string]float64
 }
 
-// AddCalculate 新增计算(运算)指标
-func AddCalculate(edbInfoIdArr []*EdbInfo, edbInfoId int, edbCode, formulaStr string, edbInfoIdBytes []string) (err error) {
-	o := orm.NewOrm()
-	defer func() {
-		if err != nil {
-			utils.FileLog.Info(fmt.Sprintf("Calculate Err:%s" + err.Error()))
-		}
-	}()
-	saveDataMap := make(map[string]map[int]float64)
-	for _, v := range edbInfoIdArr {
-		var condition string
-		var pars []interface{}
-		condition += " AND edb_info_id=? "
-		pars = append(pars, v.EdbInfoId)
-		dataList, err := GetEdbDataListAll(condition, pars, v.Source, v.SubSource, 1)
-		if err != nil {
-			return err
-		}
-		dataMap := make(map[string]float64)
-		for _, dv := range dataList {
-			if val, ok := saveDataMap[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
-				saveDataMap[dv.DataTime] = temp
-			}
-		}
-		item := new(CalculateItems)
-		item.EdbInfoId = v.EdbInfoId
-		item.DataMap = dataMap
-	}
-	formulaMap := utils.CheckFormula(formulaStr)
-	addSql := ` INSERT INTO edb_data_calculate(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
-	nowStr := time.Now().Format(utils.FormatDateTime)
-	var isAdd bool
-	for sk, sv := range saveDataMap {
-		formulaStr = strings.ToUpper(formulaStr)
-		formulaFormStr := ReplaceFormula(edbInfoIdArr, sv, formulaMap, formulaStr, edbInfoIdBytes)
-		if formulaStr == "" {
-			return
-		}
-		if formulaFormStr != "" {
-			expression := formula.NewExpression(formulaFormStr)
-			calResult, err := expression.Evaluate()
-			if err != nil {
-				err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
-				fmt.Println(err)
-				return err
-			}
-			calVal, err := calResult.Float64()
-			if err != nil {
-				err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
-				fmt.Println(err)
-				return err
-			}
-
-			//需要存入的数据
-			{
-				dataTime, _ := time.ParseInLocation(utils.FormatDate, sk, time.Local)
-				timestamp := dataTime.UnixNano() / 1e6
-				timeStr := fmt.Sprintf("%d", timestamp)
-				addSql += "("
-				addSql += strconv.Itoa(edbInfoId) + "," + "'" + edbCode + "'" + "," + "'" + sk + "'" + "," + utils.SubFloatToString(calVal, 4) + "," + "'" + nowStr + "'" +
-					"," + "'" + nowStr + "'" + "," + "'" + timeStr + "'"
-				addSql += "),"
-				isAdd = true
-			}
-		} else {
-			fmt.Println("formulaFormStr is empty")
-		}
-	}
-	if isAdd {
-		addSql = strings.TrimRight(addSql, ",")
-		_, err = o.Raw(addSql).Exec()
-		if err != nil {
-			fmt.Println("AddEdbDataCalculate Err:" + err.Error())
-			//errMsg = " tx.Exec Err :" + err.Error()
-			return
-		}
-	}
-	return
-}
-
 // AddCalculateInfo 新增计算(运算)指标
 func AddCalculateInfo(req EdbInfoCalculateSaveReq, calculateMappingList []*EdbInfoCalculateMapping, edbInfoList []*EdbInfo, edbCode, uniqueCode string, edbInfoIdBytes []string) (edbInfo *EdbInfo, err error) {
 
@@ -172,6 +88,8 @@ func AddCalculateInfo(req EdbInfoCalculateSaveReq, calculateMappingList []*EdbIn
 		LatestDate:       "",
 		LatestValue:      0,
 		ChartImage:       "",
+		EmptyType:        req.EmptyType,
+		MaxEmptyType:     req.MaxEmptyType,
 	}
 	lastId, err := to.Insert(edbInfo)
 	if err != nil {
@@ -194,13 +112,12 @@ func AddCalculateInfo(req EdbInfoCalculateSaveReq, calculateMappingList []*EdbIn
 	}
 
 	//计算数据
-	err = refreshAllCalculate(to, edbInfoList, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, edbInfo.CalculateFormula, "", "", edbInfoIdBytes)
-
+	err = refreshAllCalculate(to, edbInfoList, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, edbInfo.CalculateFormula, "", "", edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType)
 	return
 }
 
 // EditCalculateInfo 编辑计算(运算)指标
-func EditCalculateInfo(edbInfo *EdbInfo, req EdbInfoCalculateSaveReq, formulaMap map[string]string, edbInfoIdBytes []string, needCalculate bool) (err error, errMsg string) {
+func EditCalculateInfo(edbInfo *EdbInfo, req EdbInfoCalculateSaveReq, formulaSlice []string, edbInfoIdBytes []string, needCalculate bool) (err error, errMsg string) {
 	o := orm.NewOrm()
 	to, err := o.Begin()
 	if err != nil {
@@ -223,7 +140,9 @@ func EditCalculateInfo(edbInfo *EdbInfo, req EdbInfoCalculateSaveReq, formulaMap
 	edbInfo.ClassifyId = req.ClassifyId
 	edbInfo.CalculateFormula = req.CalculateFormula
 	edbInfo.ModifyTime = time.Now()
-	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+	edbInfo.EmptyType = req.EmptyType
+	edbInfo.MaxEmptyType = req.MaxEmptyType
+	_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime", "EmptyType", "MaxEmptyType")
 	if err != nil {
 		return
 	}
@@ -274,12 +193,15 @@ func EditCalculateInfo(edbInfo *EdbInfo, req EdbInfoCalculateSaveReq, formulaMap
 			}
 		}
 
-		//预先计算,判断公式是否正常
-		ok, _ := CheckFormula2(edbInfoList, formulaMap, req.CalculateFormula, edbInfoIdBytes)
-		if !ok {
-			errMsg = "生成计算指标失败,请使用正确的计算公式"
-			err = errors.New(errMsg)
-			return
+		for _, v := range formulaSlice {
+			formulaMap := utils.CheckFormula(v)
+			//预先计算,判断公式是否正常
+			ok, _ := CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
+			if !ok {
+				errMsg = "生成计算指标失败,请使用正确的计算公式"
+				err = errors.New(errMsg)
+				return
+			}
 		}
 
 		//关联关系表
@@ -293,8 +215,7 @@ func EditCalculateInfo(edbInfo *EdbInfo, req EdbInfoCalculateSaveReq, formulaMap
 		}
 
 		//计算数据
-		err = refreshAllCalculate(to, edbInfoList, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, edbInfo.CalculateFormula, "", "", edbInfoIdBytes)
-
+		err = refreshAllCalculate(to, edbInfoList, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, edbInfo.CalculateFormula, "", "", edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType)
 	}
 
 	return
@@ -337,7 +258,7 @@ func DeleteCalculateEdbInfo(edbInfoId int) (err error) {
 }
 
 // RefreshAllCalculate 刷新全部数据
-func RefreshAllCalculate(edbInfoIdArr []*EdbInfo, edbInfoId, source, subSource int, edbCode, formulaStr, startDate, endDate string, edbInfoIdBytes []string) (err error) {
+func RefreshAllCalculate(edbInfoIdArr []*EdbInfo, edbInfoId, source, subSource int, edbCode, formulaStr, startDate, endDate string, edbInfoIdBytes []string, emptyType, maxEmptyType int) (err error) {
 	o := orm.NewOrm()
 	to, err := o.Begin()
 	if err != nil {
@@ -354,13 +275,12 @@ func RefreshAllCalculate(edbInfoIdArr []*EdbInfo, edbInfoId, source, subSource i
 	fmt.Println(startDate, endDate)
 
 	//计算数据
-	err = refreshAllCalculate(to, edbInfoIdArr, edbInfoId, source, subSource, edbCode, formulaStr, startDate, endDate, edbInfoIdBytes)
-
+	err = refreshAllCalculate(to, edbInfoIdArr, edbInfoId, source, subSource, edbCode, formulaStr, startDate, endDate, edbInfoIdBytes, emptyType, maxEmptyType)
 	return
 }
 
 // refreshAllCalculate 刷新全部数据
-func refreshAllCalculate(to orm.TxOrmer, edbInfoIdArr []*EdbInfo, edbInfoId, source, subSource int, edbCode, formulaStr, startDate, endDate string, edbInfoIdBytes []string) (err error) {
+func refreshAllCalculate(to orm.TxOrmer, edbInfoIdArr []*EdbInfo, edbInfoId, source, subSource int, edbCode, formulaStr, startDate, endDate string, edbInfoIdBytes []string, emptyType, maxEmptyType int) (err error) {
 	realSaveDataMap := make(map[string]map[int]float64)
 	saveDataMap := make(map[string]map[int]float64)
 
@@ -387,8 +307,6 @@ func refreshAllCalculate(to orm.TxOrmer, edbInfoIdArr []*EdbInfo, edbInfoId, sou
 			return err
 		}
 
-		lenData := len(dataList)
-
 		for _, dv := range dataList {
 			if val, ok := realSaveDataMap[dv.DataTime]; ok {
 				if _, ok := val[v.EdbInfoId]; !ok {
@@ -414,26 +332,24 @@ func refreshAllCalculate(to orm.TxOrmer, edbInfoIdArr []*EdbInfo, edbInfoId, sou
 			// 以第一个指标的日期作为基准日期
 			if edbInfoIndex == 0 {
 				dateList = append(dateList, dv.DataTime)
-			}
-		}
-
-		if lenData > 0 {
-			tmpLatestDate, _ := time.ParseInLocation(utils.FormatDate, dataList[lenData-1].DataTime, time.Local)
-			if minLatestDate.IsZero() || minLatestDate.After(tmpLatestDate) {
-				minLatestDate = tmpLatestDate
-			}
-
-			tmpStartDate, _ := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
-			if maxStartDate.IsZero() || maxStartDate.Before(tmpStartDate) {
-				maxStartDate = tmpStartDate
+				tmpDate, _ := time.ParseInLocation(utils.FormatDate, dv.DataTime, 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)
+	HandleDateSaveDataMap(dateList, maxStartDate, minLatestDate, realSaveDataMap, saveDataMap, edbInfoIdArr, emptyType)
 
-	formulaMap := utils.CheckFormula(formulaStr)
+	formulaDateSlice, formulaDateMap, err := utils.HandleFormulaJson(formulaStr, minLatestDate)
+	if err != nil {
+		return
+	}
 	addSql := ` INSERT INTO edb_data_calculate(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
 	var isAdd bool
 
@@ -455,10 +371,44 @@ func refreshAllCalculate(to orm.TxOrmer, edbInfoIdArr []*EdbInfo, edbInfoId, sou
 	}
 	edbInfoIdStr := strconv.Itoa(edbInfoId)
 	existDataMap := make(map[string]string)
+
+	// 判断是否特殊处理max和min函数
+	maxDealFlag := false
+	if emptyType == 4 && maxEmptyType == 2 {
+		maxDealFlag = true
+	}
 	for sk, sv := range saveDataMap {
+		// 当空值处理类型选择了不计算时,只要有一个指标在某个日期没有值(即空值),则计算指标在该日期没有值
+		if emptyType == 1 {
+			if len(sv) != len(edbInfoIdArr) {
+				continue
+			}
+		}
 		//fmt.Println(sk, sv)
+		// 根据时间范围,选择对应的公式
+		formulaMap := make(map[string]string)
+		formulaStr = ""
+		for _, fv := range formulaDateSlice {
+			if sk < fv {
+				if f, ok := formulaDateMap[fv]; ok {
+					formulaStr = f
+					formulaMap = utils.CheckFormula(formulaStr)
+				}
+				break
+			}
+		}
+		if formulaStr == "" {
+			continue
+		}
+		svMax := make(map[int]float64)
+		if maxDealFlag {
+			// 特殊处理max和min函数,如果原本的值为空,则选择空值参与运算
+			if svMaxData, ok := realSaveDataMap[sk]; ok {
+				svMax = svMaxData
+			}
+		}
 		formulaStr = strings.ToUpper(formulaStr)
-		formulaFormStr := ReplaceFormula(edbInfoIdArr, sv, formulaMap, formulaStr, edbInfoIdBytes)
+		formulaFormStr := ReplaceFormula(edbInfoIdArr, sv, svMax, formulaMap, formulaStr, edbInfoIdBytes, maxDealFlag)
 		//计算公式异常,那么就移除该指标
 		if formulaFormStr == `` {
 			//removeDateList = append(removeDateList, sk)
@@ -551,15 +501,20 @@ func refreshAllCalculate(to orm.TxOrmer, edbInfoIdArr []*EdbInfo, edbInfoId, sou
 }
 
 // ReplaceFormula 替换计算方式
-func ReplaceFormula(edbInfoIdArr []*EdbInfo, valArr map[int]float64, formulaMap map[string]string, formulaStr string, edbInfoIdBytes []string) string {
+func ReplaceFormula(edbInfoIdArr []*EdbInfo, valArr, valArrMax map[int]float64, formulaMap map[string]string, formulaStr string, edbInfoIdBytes []string, maxDealFlag bool) string {
+	// todo 先处理max和min函数的特殊情况
+	//if strings.Contains(formulaStr, "MAX") || strings.Contains(formulaStr, "MIN") {
 	funMap := GetFormulaMap()
 	for k, v := range funMap {
 		formulaStr = strings.Replace(formulaStr, k, v, -1)
 	}
+	if maxDealFlag {
+		formulaStr = GetMaxMinEdbInfo(formulaStr)
+	}
 	replaceCount := 0
 	for dk, dv := range edbInfoIdArr {
 		var isReplace bool
-		formulaStr, isReplace = GetFormulaReplace(dk, dv.EdbInfoId, formulaStr, edbInfoIdBytes, formulaMap, valArr)
+		formulaStr, isReplace = GetFormulaReplace(dk, dv.EdbInfoId, formulaStr, edbInfoIdBytes, formulaMap, valArr, valArrMax, maxDealFlag)
 		if isReplace {
 			replaceCount++
 		}
@@ -567,6 +522,9 @@ func ReplaceFormula(edbInfoIdArr []*EdbInfo, valArr map[int]float64, formulaMap
 	for k, v := range funMap {
 		formulaStr = strings.Replace(formulaStr, v, k, -1)
 	}
+	if strings.Contains(formulaStr, "MAX()") || strings.Contains(formulaStr, "MIN()") {
+		return ""
+	}
 	if replaceCount == len(formulaMap) {
 		return formulaStr
 	} else {
@@ -574,14 +532,23 @@ func ReplaceFormula(edbInfoIdArr []*EdbInfo, valArr map[int]float64, formulaMap
 	}
 }
 
-func GetFormulaReplace(index, edbInfoId int, formulaStr string, edbInfoIdBytes []string, formulaMap map[string]string, valArr map[int]float64) (formulaResult string, isReplace bool) {
+func GetFormulaReplace(index, edbInfoId int, formulaStr string, edbInfoIdBytes []string, formulaMap map[string]string, valArr, valArrMax map[int]float64, maxDealFlag bool) (formulaResult string, isReplace bool) {
 	formulaResult = formulaStr
 	dKey := edbInfoIdBytes[index]
 	if _, ok := formulaMap[dKey]; ok { //公式中存在
 		if val, valOk := valArr[edbInfoId]; valOk { //值存在
 			dvStr := fmt.Sprintf("%v", val)
-			formulaResult = strings.Replace(formulaStr, dKey, dvStr, -1)
+			formulaResult = strings.Replace(formulaResult, dKey, dvStr, -1)
 			isReplace = true
+			if maxDealFlag {
+				if valM, valMOk := valArrMax[edbInfoId]; valMOk { //值存在
+					dvMStr := fmt.Sprintf("%v", valM)
+					formulaResult = strings.Replace(formulaResult, strings.ToLower(dKey), dvMStr, -1)
+				} else {
+					formulaResult = strings.Replace(formulaResult, strings.ToLower(dKey)+",", "", -1)
+					formulaResult = strings.Replace(formulaResult, ","+strings.ToLower(dKey), "", -1)
+				}
+			}
 		}
 	}
 	return
@@ -598,7 +565,9 @@ func GetFormulaMap() map[string]string {
 	funMap["FLOOR"] = "[@^]"
 	funMap["MOD"] = "[@&]"
 	funMap["POW"] = "[@*]"
-	funMap["ROUND"] = "[@(]"
+	funMap["ROUND"] = "[@`]"
+	funMap["LN"] = "[@-]"
+	funMap["EXP"] = "[@+]"
 	return funMap
 }
 
@@ -656,8 +625,11 @@ func CheckFormula2(edbInfoArr []*EdbInfo, formulaMap map[string]string, formulaS
 	for _, v := range edbInfoArr {
 		valArr[v.EdbInfoId] = 100
 	}
+
 	formulaStr = strings.ToUpper(formulaStr)
-	formulaFormStr := ReplaceFormula(edbInfoArr, valArr, formulaMap, formulaStr, edbInfoIdBytes)
+	// 预设里的max和min无需特殊处理
+	valArrMax := make(map[int]float64)
+	formulaFormStr := ReplaceFormula(edbInfoArr, valArr, valArrMax, formulaMap, formulaStr, edbInfoIdBytes, false)
 	if formulaFormStr == "" {
 		return
 	}
@@ -671,14 +643,14 @@ func CheckFormula2(edbInfoArr []*EdbInfo, formulaMap map[string]string, formulaS
 }
 
 // 处理整个数据
-func handleDateSaveDataMap(dateList []string, maxStartDate, minLatestDate time.Time, realSaveDataMap, saveDataMap map[string]map[int]float64, edbInfoIdArr []*EdbInfo) {
+func HandleDateSaveDataMap(dateList []string, maxStartDate, minLatestDate time.Time, realSaveDataMap, saveDataMap map[string]map[int]float64, edbInfoIdArr []*EdbInfo, emptyType int) {
 	for _, date := range dateList {
 		dateTime, _ := time.ParseInLocation(utils.FormatDate, date, time.Local)
 		// 如果当前日期早于数据的最大开始日期,那么不处理,进入下一个循环
 		if dateTime.Before(maxStartDate) {
 			continue
 		}
-		// 如果当前日期晚于数据的最小结束日期,那么不处理,直接返回了
+		// todo 规则是否生效如果当前日期晚于数据的最小结束日期,那么不处理,直接返回了
 		if dateTime.After(minLatestDate) {
 			return
 		}
@@ -688,6 +660,7 @@ func handleDateSaveDataMap(dateList []string, maxStartDate, minLatestDate time.T
 			tmpEdbInfoId := edbInfo.EdbInfoId // 当前指标id
 			// 如果该日期不存在该指标数据,那么需要找寻前后日期的数据,进行填补
 			if _, ok := tmpDataMap[tmpEdbInfoId]; !ok {
+
 				//day := 0
 				//switch edbInfo.Frequency {
 				//case "周度":
@@ -702,14 +675,24 @@ func handleDateSaveDataMap(dateList []string, maxStartDate, minLatestDate time.T
 				//	day = 365
 				//}
 				// 需求池 255 指标运算文案修改,补数据遍历区间修改(2023-3-7 09:37:23修改)
-				day := 35
-				handleDateDataMap(realSaveDataMap, saveDataMap, date, tmpEdbInfoId, day)
+
+				switch emptyType {
+				case 0:
+					handleDateDataMap(realSaveDataMap, saveDataMap, date, tmpEdbInfoId, 35)
+				case 2:
+					handleDateDataMapBefore(realSaveDataMap, saveDataMap, date, tmpEdbInfoId, edbInfo.StartDate, edbInfo.EndDate)
+				case 3:
+					handleDateDataMapAfter(realSaveDataMap, saveDataMap, date, tmpEdbInfoId, edbInfo.StartDate, edbInfo.EndDate)
+				case 4:
+					handleDateDataMapZero(saveDataMap, date, tmpEdbInfoId)
+				}
+
 			}
 		}
 	}
 }
 
-// handleDateDataMap 处理单个日期的数据
+// handleDateDataMap 前后35天 处理单个日期的数据
 func handleDateDataMap(realSaveDataMap, saveDataMap map[string]map[int]float64, date string, edbInfoId, day int) {
 	currDate, _ := time.ParseInLocation(utils.FormatDate, date, time.Local)
 
@@ -728,6 +711,7 @@ func handleDateDataMap(realSaveDataMap, saveDataMap map[string]map[int]float64,
 			}
 			if findDataMap, hasFindDataMap := realSaveDataMap[nextDateDayStr]; hasFindDataMap { // 下一个日期有数据
 				if val, hasFindItem := findDataMap[edbInfoId]; hasFindItem {
+					fmt.Println(fmt.Sprintf("date:%s, 无值,取%s的值%.4f", date, nextDateDayStr, val))
 					saveDataMap[date][edbInfoId] = val
 					return
 				}
@@ -741,10 +725,139 @@ func handleDateDataMap(realSaveDataMap, saveDataMap map[string]map[int]float64,
 			}
 			if findDataMap, hasFindDataMap := realSaveDataMap[preDateDayStr]; hasFindDataMap { // 下一个日期有数据
 				if val, hasFindItem := findDataMap[edbInfoId]; hasFindItem {
+					fmt.Println(fmt.Sprintf("date:%s, 无值,取%s的值%.4f", date, preDateDayStr, val))
+					saveDataMap[date][edbInfoId] = val
+					return
+				}
+			}
+		}
+	}
+}
+
+// handleDateDataMapBefore 前值填充:空值优先以最近的前值填充,没有前值时,用后值填充
+func handleDateDataMapBefore(realSaveDataMap, saveDataMap map[string]map[int]float64, date string, edbInfoId int, startDate, endDate string) {
+	currDate, _ := time.ParseInLocation(utils.FormatDate, date, time.Local)
+
+	// 前一天
+	preDateDay := currDate
+
+	startDateT, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+	endDateT, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+
+	if currDate.Before(startDateT) {
+		if findDataMap, hasFindDataMap := realSaveDataMap[startDate]; hasFindDataMap { // 下一个日期有数据
+			if val, hasFindItem := findDataMap[edbInfoId]; hasFindItem {
+				//utils.FileLog.Info(fmt.Sprintf("date:%s, 无值,取%s的值%.4f", date, startDate, val))
+				saveDataMap[date][edbInfoId] = val
+				return
+			}
+		}
+		return
+	}
+	if currDate.After(endDateT) {
+		if findDataMap, hasFindDataMap := realSaveDataMap[endDate]; hasFindDataMap { // 下一个日期有数据
+			if val, hasFindItem := findDataMap[edbInfoId]; hasFindItem {
+				//utils.FileLog.Info(fmt.Sprintf("date:%s, 无值,取%s的值%.4f", date, endDate, val))
+				saveDataMap[date][edbInfoId] = val
+				return
+			}
+		}
+	}
+	for i := 1; preDateDay.After(startDateT) || preDateDay == startDateT; i++ {
+		// 上个日期的数据
+		{
+			preDateDay = currDate.AddDate(0, 0, -i)
+			preDateDayStr := preDateDay.Format(utils.FormatDate)
+			if findDataMap, hasFindDataMap := realSaveDataMap[preDateDayStr]; hasFindDataMap { // 下一个日期有数据
+				if val, hasFindItem := findDataMap[edbInfoId]; hasFindItem {
+					//utils.FileLog.Info(fmt.Sprintf("date:%s, 无值,取%s的值%.4f", date, preDateDayStr, val))
+					saveDataMap[date][edbInfoId] = val
+					return
+				}
+			}
+		}
+	}
+	return
+}
+
+// handleDateDataMapAfter 后值填充:空值优先以最近的后值填充,没有后值时,用前值填充
+func handleDateDataMapAfter(realSaveDataMap, saveDataMap map[string]map[int]float64, date string, edbInfoId int, startDate, endDate string) {
+	currDate, _ := time.ParseInLocation(utils.FormatDate, date, time.Local)
+
+	// 后一天
+	nextDateDay := currDate
+
+	startDateT, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+	endDateT, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+
+	if currDate.Before(startDateT) {
+		if findDataMap, hasFindDataMap := realSaveDataMap[startDate]; hasFindDataMap { // 下一个日期有数据
+			if val, hasFindItem := findDataMap[edbInfoId]; hasFindItem {
+				//utils.FileLog.Info(fmt.Sprintf("date:%s, 无值,取%s的值%.4f", date, startDate, val))
+				saveDataMap[date][edbInfoId] = val
+				return
+			}
+		}
+		return
+	}
+	if currDate.After(endDateT) {
+		if findDataMap, hasFindDataMap := realSaveDataMap[endDate]; hasFindDataMap { // 下一个日期有数据
+			if val, hasFindItem := findDataMap[edbInfoId]; hasFindItem {
+				//utils.FileLog.Info(fmt.Sprintf("date:%s, 无值,取%s的值%.4f", date, endDate, val))
+				saveDataMap[date][edbInfoId] = val
+				return
+			}
+		}
+	}
+
+	for i := 1; nextDateDay.Before(endDateT) || nextDateDay == endDateT; i++ {
+		// 下个日期的数据
+		{
+			nextDateDay = currDate.AddDate(0, 0, i)
+			nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+			if findDataMap, hasFindDataMap := realSaveDataMap[nextDateDayStr]; hasFindDataMap { // 下一个日期有数据
+				if val, hasFindItem := findDataMap[edbInfoId]; hasFindItem {
+					//utils.FileLog.Info(fmt.Sprintf("date:%s, 无值,取%s的值%.4f", date, nextDateDayStr, val))
 					saveDataMap[date][edbInfoId] = val
 					return
 				}
 			}
 		}
 	}
+	return
+}
+
+// handleDateDataMapZero 等于0
+func handleDateDataMapZero(saveDataMap map[string]map[int]float64, date string, edbInfoId int) {
+	saveDataMap[date][edbInfoId] = 0
+	return
+}
+
+func GetMaxMinEdbInfo(formula string) string {
+	flag := 0
+	for k, v := range formula {
+		if v >= 65 && v <= 122 {
+			// 从左边找到第一个左括号的位置,往前截取三个字符,判断是不是max或者min,如果是的话,把该入参改成小写
+			for i := k; i >= 4; i-- {
+				if formula[i:i+1] == "(" && flag >= 1 {
+					t := formula[i-4 : i]
+					if t == "[@@]" || t == "[@!]" {
+						formula = formula[:k] + strings.ToLower(formula[k:k+1]) + formula[k+1:]
+					}
+					break
+				}
+			}
+		} else if formula[k:k+1] == "(" {
+			flag += 1
+		} else if formula[k:k+1] == ")" {
+			flag -= 1
+		}
+	}
+	return formula
+}
+
+// PredictRuleCalculateByNineResp 获取预测指标规则9的绘图数据返回
+type PredictRuleCalculateByNineResp struct {
+	LatestDate string
+	DataList   interface{}
 }

+ 325 - 0
models/base_from_yongyi.go

@@ -0,0 +1,325 @@
+package models
+
+import (
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+// BaseFromYongyi 涌溢咨询
+type BaseFromYongyi struct{}
+
+type BaseFromYongyiData struct {
+	YongyiDataId  int `orm:"column(yongyi_data_id);pk"`
+	YongyiIndexId int
+	IndexCode     string
+	DataTime      string
+	Value         string
+	CreateTime    time.Time
+	ModifyTime    time.Time
+	DataTimestamp int64
+}
+
+func GetBaseFromYongyiDataByCondition(condition string, pars []interface{}) (list []*BaseFromYongyiData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM base_from_yongyi_data WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+	return
+}
+
+// Add 添加
+func (obj BaseFromYongyi) Add(edbCode string) (err error) {
+	o := orm.NewOrm()
+
+	var condition string
+	var pars []interface{}
+	if edbCode != "" {
+		condition += " AND index_code=? "
+		pars = append(pars, edbCode)
+	}
+	yongyiBaseDataAll, err := GetBaseFromYongyiDataByCondition(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		return
+	}
+	var isAdd bool
+	addSql := ` INSERT INTO edb_data_yongyi(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	existMap := make(map[string]string)
+	for _, sv := range yongyiBaseDataAll {
+		eDate := sv.DataTime
+		dataTime, err := time.Parse(utils.FormatDate, eDate)
+		if err != nil {
+			return err
+		}
+		timestamp := dataTime.UnixNano() / 1e6
+		timeStr := fmt.Sprintf("%d", timestamp)
+		if _, ok := existMap[eDate]; !ok {
+			addSql += GetAddSql("0", edbCode, eDate, timeStr, sv.Value)
+			isAdd = true
+		}
+		existMap[eDate] = sv.Value
+	}
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		utils.FileLog.Info("addSql:" + addSql)
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			return err
+		}
+	}
+	return
+}
+
+// Refresh 刷新涌益咨询指标数据
+func (obj BaseFromYongyi) Refresh(edbInfoId int, edbCode, startDate string) (err error) {
+	source := obj.GetSource()
+	o := orm.NewOrm()
+	if err != nil {
+		return
+	}
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	//计算数据
+	var condition string
+	var pars []interface{}
+
+	if edbCode != "" {
+		condition += " AND index_code=? "
+		pars = append(pars, edbCode)
+	}
+
+	if startDate != "" {
+		condition += " AND data_time>=? "
+		pars = append(pars, startDate)
+	}
+
+	yongyiDataList, err := GetBaseFromYongyiDataByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+
+	// 真实数据的最大日期  , 插入规则配置的日期
+	var realDataMaxDate, edbDataInsertConfigDate time.Time
+	var edbDataInsertConfig *EdbDataInsertConfig
+	var isFindConfigDateRealData bool //是否找到配置日期的实际数据的值
+	{
+		edbDataInsertConfig, err = GetEdbDataInsertConfigByEdbId(edbInfoId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			return
+		}
+		if edbDataInsertConfig != nil {
+			edbDataInsertConfigDate = edbDataInsertConfig.Date
+		}
+	}
+
+	var existCondition string
+	var existPars []interface{}
+
+	existCondition += " AND edb_info_id=? "
+	existPars = append(existPars, edbInfoId)
+	if startDate != "" {
+		existCondition += " AND data_time>=? "
+		existPars = append(existPars, startDate)
+	}
+
+	existList, err := GetEdbDataByCondition(source, 0, existCondition, existPars)
+	if err != nil {
+		return err
+	}
+	existMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range existList {
+		existMap[v.DataTime] = v
+	}
+	addSql := ` INSERT INTO edb_data_yongyi(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	for _, v := range yongyiDataList {
+		item := v
+		eDate := item.DataTime
+		dataTime, err := time.ParseInLocation(utils.FormatDate, eDate, time.Local)
+		if err != nil {
+			return err
+		}
+		if findItem, ok := existMap[v.DataTime]; !ok {
+			sValue := item.Value
+
+			timestamp := dataTime.UnixNano() / 1e6
+			timeStr := fmt.Sprintf("%d", timestamp)
+
+			addSql += GetAddSql(edbInfoIdStr, edbCode, eDate, timeStr, sValue)
+			isAdd = true
+		} else {
+			if findItem != nil && utils.SubFloatToString(findItem.Value, 30) != item.Value {
+				err = ModifyEdbDataById(source, 0, findItem.EdbDataId, item.Value)
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		// 下面代码主要目的是处理掉手动插入的数据判断
+		{
+			if realDataMaxDate.IsZero() || dataTime.After(realDataMaxDate) {
+				realDataMaxDate = dataTime
+			}
+			if edbDataInsertConfigDate.IsZero() || dataTime.Equal(edbDataInsertConfigDate) {
+				isFindConfigDateRealData = true
+			}
+		}
+	}
+
+	// 处理手工数据补充的配置
+	HandleConfigInsertEdbData(realDataMaxDate, edbDataInsertConfig, edbInfoId, source, 0, existMap, isFindConfigDateRealData)
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = o.Raw(addSql).Exec()
+		if err != nil {
+			fmt.Println("RefreshEdbDataFromBaiinfo add Err", err.Error())
+			return
+		}
+	}
+	return
+}
+
+// GetSource 获取来源编码id
+func (obj BaseFromYongyi) GetSource() int {
+	return utils.DATA_SOURCE_YONYI
+}
+
+// GetSourceName 获取来源名称
+func (obj BaseFromYongyi) GetSourceName() string {
+	return utils.DATA_SOURCE_NAME_YONYI
+}
+
+type BaseFromYongyiIndex struct {
+	YongyiIndexId int64 `orm:"column(yongyi_index_id);pk"`
+	IndexCode     string
+	IndexName     string
+	Frequency     string
+	Unit          string
+	ClassifyId    int64
+	StartDate     string
+	EndDate       string
+	Sort          int
+	TerminalCode  string
+	CreateTime    time.Time
+	ModifyTime    time.Time
+}
+
+type BaseFromYongyiIndexList struct {
+	YongyiIndexId int64 `orm:"column(yongyi_index_id);pk"`
+	IndexCode     string
+	IndexName     string
+	Frequency     string
+	Unit          string
+	ClassifyId    int64
+	StartDate     string
+	EndDate       string
+	TerminalCode  string
+	CreateTime    string
+	ModifyTime    string
+}
+
+func (y *BaseFromYongyiData) GetByIndexCode(indexCode string) (list []*BaseFromYongyiData, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM base_from_yongyi_data WHERE index_code=? `
+	_, err = o.Raw(sql, indexCode).QueryRows(&list)
+	return
+}
+
+func (y *BaseFromYongyiData) AddMulti(item []*BaseFromYongyiData) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(1, item)
+	return
+}
+
+// HandleYongyiExcelData 涌益咨询的excel数据
+type HandleYongyiExcelData struct {
+	ClassifyName string `description:"指标目录"`
+	ClassifySort int    `description:"指标目录排序号"`
+	IndexName    string `description:"指标名称"`
+	IndexCode    string `description:"指标编码"`
+	Unit         string `description:"单位"`
+	Sort         int    `description:"排序号"`
+	Frequency    string `description:"频度"`
+	ExcelDataMap map[string]string
+}
+
+type HandleYongyiExcelDataReq struct {
+	List         []*HandleYongyiExcelData
+	TerminalCode string `description:"编码"`
+}
+
+func (y *BaseFromYongyiData) GetMaxAndMinDateByIndexCode(indexCode string) (item *EdbInfoMaxAndMinInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT MIN(data_time) AS min_date,MAX(data_time) AS max_date,MIN(value) AS min_value,MAX(value) AS max_value FROM base_from_yongyi_data WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&item)
+	return
+}
+
+func (y *BaseFromYongyiIndex) ModifyIndexMaxAndMinDate(indexCode string, item *EdbInfoMaxAndMinInfo) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE base_from_yongyi_index SET start_date=?,end_date=?,modify_time=NOW() WHERE index_code=? `
+	_, err = o.Raw(sql, item.MinDate, item.MaxDate, indexCode).Exec()
+	return
+}
+
+func (y *BaseFromYongyiIndex) GetByIndexCode(indexCode string) (item *BaseFromYongyiIndex, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM base_from_yongyi_index WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&item)
+	return
+}
+
+func (y *BaseFromYongyiIndex) Add() (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(y)
+	return
+}
+
+// Update 修改
+func (y *BaseFromYongyiIndex) Update(updateCols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(y, updateCols...)
+
+	return
+}
+
+// BaseFromYongyiClassify Yongyi原始数据分类表
+type BaseFromYongyiClassify struct {
+	ClassifyId      int64     `orm:"column(classify_id);pk"`
+	ClassifyName    string    `description:"分类名称"`
+	ParentId        int       `description:"父级id"`
+	SysUserId       int       `description:"创建人id"`
+	SysUserRealName string    `description:"创建人姓名"`
+	Level           int       `description:"层级"`
+	Sort            int       `description:"排序字段,越小越靠前,默认值:10"`
+	ModifyTime      time.Time `description:"修改时间"`
+	CreateTime      time.Time `description:"创建时间"`
+}
+
+func (y *BaseFromYongyiClassify) Add() (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(y)
+	return
+}
+
+// Update 修改
+func (y *BaseFromYongyiClassify) Update(updateCols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(y, updateCols...)
+
+	return
+}
+
+func (y *BaseFromYongyiClassify) GetByClassifyName(classifyName string) (item *BaseFromYongyiClassify, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM base_from_yongyi_classify WHERE classify_name=? `
+	err = o.Raw(sql, classifyName).QueryRow(&item)
+	return
+}

+ 56 - 10
models/base_predict_from_calculate.go

@@ -59,7 +59,7 @@ func EditPredictCalculate(edbInfo *EdbInfo, calculateMappingList []*EdbInfoCalcu
 	}()
 
 	// 修改指标信息
-	_, err = to.Update(edbInfo, "EdbName", "Frequency", "Unit", "ClassifyId", "CalculateFormula")
+	_, err = to.Update(edbInfo, "EdbName", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "EmptyType", "MaxEmptyType")
 	if err != nil {
 		return
 	}
@@ -238,12 +238,12 @@ func AddPredictCalculateData(edbInfoIdList []*EdbInfo, edbInfo *EdbInfo, edbCode
 		}
 	}()
 
-	latestDateStr, latestValue, err = refreshAllPredictCalculate(to, edbInfoIdList, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbCode, formulaStr, "", edbInfoIdBytes)
+	latestDateStr, latestValue, err = refreshAllPredictCalculate(to, edbInfoIdList, edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbCode, formulaStr, "", edbInfoIdBytes, edbInfo.EmptyType, edbInfo.MaxEmptyType)
 	return
 }
 
 // RefreshAllPredictCalculate 刷新预测计算指标的全部数据
-func RefreshAllPredictCalculate(edbInfoIdList []*EdbInfo, edbInfoId, source, subSource int, edbCode, formulaStr, startDate string, edbInfoIdBytes []string) (latestDateStr string, latestValue float64, err error) {
+func RefreshAllPredictCalculate(edbInfoIdList []*EdbInfo, edbInfoId, source, subSource int, edbCode, formulaStr, startDate string, edbInfoIdBytes []string, emptyType, maxEmptyType int) (latestDateStr string, latestValue float64, err error) {
 	o := orm.NewOrm()
 	to, err := o.Begin()
 	if err != nil {
@@ -258,13 +258,13 @@ func RefreshAllPredictCalculate(edbInfoIdList []*EdbInfo, edbInfoId, source, sub
 		}
 	}()
 
-	latestDateStr, latestValue, err = refreshAllPredictCalculate(to, edbInfoIdList, edbInfoId, source, subSource, edbCode, formulaStr, startDate, edbInfoIdBytes)
+	latestDateStr, latestValue, err = refreshAllPredictCalculate(to, edbInfoIdList, edbInfoId, source, subSource, edbCode, formulaStr, startDate, edbInfoIdBytes, emptyType, maxEmptyType)
 
 	return
 }
 
 // refreshAllPredictCalculate 刷新预测计算指标的全部数据
-func refreshAllPredictCalculate(to orm.TxOrmer, edbInfoIdList []*EdbInfo, edbInfoId, source, subSource int, edbCode, formulaStr, startDate string, edbInfoIdBytes []string) (latestDateStr string, latestValue float64, err error) {
+func refreshAllPredictCalculate(to orm.TxOrmer, edbInfoIdList []*EdbInfo, edbInfoId, source, subSource int, edbCode, formulaStr, startDate string, edbInfoIdBytes []string, emptyType, maxEmptyType int) (latestDateStr string, latestValue float64, err error) {
 	fmt.Println("startDate:", startDate)
 
 	// 最小的结束日期 , 最晚的数据开始日期
@@ -275,7 +275,8 @@ func refreshAllPredictCalculate(to orm.TxOrmer, edbInfoIdList []*EdbInfo, edbInf
 	saveDataMap := make(map[string]map[int]float64)
 
 	for edbInfoIndex, v := range edbInfoIdList {
-		tmpLatestDate, tmpErr := time.ParseInLocation(utils.FormatDate, v.LatestDate, time.Local)
+		// 单独存储max、min函数里的指标的数据
+		/*tmpLatestDate, tmpErr := time.ParseInLocation(utils.FormatDate, v.LatestDate, time.Local)
 		if tmpErr != nil {
 			err = errors.New(`最近的日期格式化失败;日期:` + v.EndDate + `;err:` + tmpErr.Error())
 			return
@@ -290,7 +291,7 @@ func refreshAllPredictCalculate(to orm.TxOrmer, edbInfoIdList []*EdbInfo, edbInf
 		}
 		if maxStartDate.IsZero() || maxStartDate.Before(tmpStartDate) {
 			maxStartDate = tmpStartDate
-		}
+		}*/
 
 		// 获取关联指标数据
 		dataList, tmpErr := GetPredictEdbDataListAllByStartDate(v, 1, startDate)
@@ -322,14 +323,24 @@ func refreshAllPredictCalculate(to orm.TxOrmer, edbInfoIdList []*EdbInfo, edbInf
 			}
 			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 maxStartDate.IsZero() || tmpDate.Before(maxStartDate) {
+					maxStartDate = tmpDate
+				}
 			}
 		}
 	}
 
 	//数据处理,将日期内不全的数据做填补
-	handleDateSaveDataMap(dateList, maxStartDate, minLatestDate, realSaveDataMap, saveDataMap, edbInfoIdList)
+	HandleDateSaveDataMap(dateList, maxStartDate, minLatestDate, realSaveDataMap, saveDataMap, edbInfoIdList, emptyType)
 
-	formulaMap := utils.CheckFormula(formulaStr)
+	formulaDateSlice, formulaDateMap, err := utils.HandleFormulaJson(formulaStr, minLatestDate)
+	if err != nil {
+		return
+	}
 	addSql := ` INSERT INTO edb_data_predict_calculate (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
 	var isAdd bool
 	if !minLatestDate.IsZero() {
@@ -353,12 +364,47 @@ func refreshAllPredictCalculate(to orm.TxOrmer, edbInfoIdList []*EdbInfo, edbInf
 	existDataMap := make(map[string]string)
 
 	removeDateList := make([]string, 0) //需要移除的日期
+	// 判断是否特殊处理max和min函数
+	maxDealFlag := false
+	if emptyType == 4 && maxEmptyType == 2 {
+		maxDealFlag = true
+	}
+
 	for sk, sv := range saveDataMap {
+		// 当空值处理类型选择了不计算时,只要有一个指标在某个日期没有值(即空值),则计算指标在该日期没有值
+		if emptyType == 1 {
+			if len(sv) != len(edbInfoIdList) {
+				continue
+			}
+		}
 		//fmt.Println(sk, sv)
+		// 根据时间范围,选择对应的公式
+		formulaMap := make(map[string]string)
+		formulaStr = ""
+		for _, fv := range formulaDateSlice {
+			if sk < fv {
+				if f, ok := formulaDateMap[fv]; ok {
+					formulaStr = f
+					formulaMap = utils.CheckFormula(formulaStr)
+				}
+				break
+			}
+		}
+		if formulaStr == "" {
+			continue
+		}
+		svMax := make(map[int]float64)
+		if maxDealFlag {
+			// 特殊处理max和min函数,如果原本的值为空,则选择空值参与运算
+			if svMaxData, ok := realSaveDataMap[sk]; ok {
+				svMax = svMaxData
+			}
+		}
 		formulaStr = strings.ToUpper(formulaStr)
-		formulaFormStr := ReplaceFormula(edbInfoIdList, sv, formulaMap, formulaStr, edbInfoIdBytes)
+		formulaFormStr := ReplaceFormula(edbInfoIdList, sv, svMax, formulaMap, formulaStr, edbInfoIdBytes, maxDealFlag)
 		if formulaFormStr != "" {
 			//utils.FileLog.Info(fmt.Sprintf("formulaFormStr:%s", formulaFormStr))
+			fmt.Println(fmt.Sprintf("date %s:formulaFormStr:%s", sk, formulaFormStr))
 			expression := formula.NewExpression(formulaFormStr)
 			calResult, tmpErr := expression.Evaluate()
 			if tmpErr != nil {

+ 3 - 0
models/db.go

@@ -111,6 +111,9 @@ func initBaseIndex() {
 		new(BaseFromTradeShanghaiIndex),
 		new(BaseFromTradeIneIndex),
 		new(BaseFromSmmClassify),
+		new(BaseFromYongyiIndex),
+		new(BaseFromYongyiData),
+		new(BaseFromYongyiClassify),
 	)
 }
 

+ 18 - 10
models/edb_info.go

@@ -37,14 +37,17 @@ type EdbInfo struct {
 	EdbType          int     `description:"指标类型:1:基础指标,2:计算指标"`
 	IsUpdate         int     `description:"当天是否已更新,1:未更新,2:已更新"`
 	Sort             int     `description:"排序字段"`
-	LatestDate       string  `description:"数据最新日期"`
-	LatestValue      float64 `description:"数据最新值"`
+	LatestDate       string  `description:"数据最新日期(实际日期)"`
+	LatestValue      float64 `description:"数据最新值(实际值)"`
+	EndValue         float64 `description:"数据的最新值(预测日期的最新值)"`
 	MoveType         int     `description:"移动方式:1:领先(默认),2:滞后"`
 	MoveFrequency    string  `description:"移动频度"`
 	NoUpdate         int8    `description:"是否停止更新,0:继续更新;1:停止更新"`
 	ServerUrl        string  `description:"服务器地址"`
 	ChartImage       string  `description:"图表图片"`
 	Calendar         string  `description:"公历/农历" orm:"default(公历);"`
+	EmptyType        int     `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int     `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
 	DataDateType     string  `orm:"column(data_date_type);size(255);null;default(交易日)"`
 	ManualSave       int     `description:"是否有手动保存过上下限: 0-否; 1-是"`
 	TerminalCode     string  `description:"终端编码,用于配置在机器上"`
@@ -85,8 +88,9 @@ type EdbInfoList struct {
 	UnitEn          string                 `description:"英文单位"`
 	StartDate       string                 `description:"起始日期"`
 	EndDate         string                 `description:"终止日期"`
-	LatestDate      string                 `description:"数据最新日期"`
-	LatestValue     float64                `description:"数据最新值"`
+	LatestDate      string                 `description:"数据最新日期(实际日期)"`
+	LatestValue     float64                `description:"数据最新值(实际值)"`
+	EndValue        float64                `description:"数据的最新值(预测日期的最新值)"`
 	ClassifyId      int                    `description:"分类id"`
 	UniqueCode      string                 `description:"指标唯一编码"`
 	SysUserId       int                    `description:"创建人id"`
@@ -220,6 +224,7 @@ type EdbInfoMaxAndMinInfo struct {
 	MaxValue    float64 `description:"最大值"`
 	LatestValue float64 `description:"最新值"`
 	LatestDate  string  `description:"实际数据最新日期"`
+	EndValue    float64 `description:"最新值"`
 }
 
 // GetEdbInfoMaxAndMinInfo 获取指标的最新数据记录信息
@@ -257,16 +262,16 @@ func GetEdbDataUpdateTimeByModify(source, subSource int, edbCode string, dataUpd
 // ModifyEdbInfoMaxAndMinInfo 修改指标的最新数据信息
 func ModifyEdbInfoMaxAndMinInfo(edbInfoId int, item *EdbInfoMaxAndMinInfo) (err error) {
 	o := orm.NewOrm()
-	sql := ` UPDATE edb_info SET start_date=?,end_date=?,min_value=?,max_value=?,is_update=2,latest_date=?,latest_value=?,modify_time=NOW() WHERE edb_info_id=? `
-	_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.MinValue, item.MaxValue, item.MaxDate, item.LatestValue, edbInfoId).Exec()
+	sql := ` UPDATE edb_info SET start_date=?,end_date=?,min_value=?,max_value=?,is_update=2,latest_date=?,latest_value=?, end_value = ?,modify_time=NOW() WHERE edb_info_id=? `
+	_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.MinValue, item.MaxValue, item.MaxDate, item.LatestValue, item.LatestValue, edbInfoId).Exec()
 	return
 }
 
 // ModifyEdbTimeAndLastInfo 修改指标的最新数据信息(除上下限)
 func ModifyEdbTimeAndLastInfo(edbInfoId int, item *EdbInfoMaxAndMinInfo) (err error) {
 	o := orm.NewOrm()
-	sql := `UPDATE edb_info SET start_date = ?, end_date = ?, is_update = 2,latest_date = ?, latest_value = ?, modify_time = NOW() WHERE edb_info_id = ?`
-	_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.MaxDate, item.LatestValue, edbInfoId).Exec()
+	sql := `UPDATE edb_info SET start_date = ?, end_date = ?, is_update = 2,latest_date = ?, latest_value = ?, end_value = ?, modify_time = NOW() WHERE edb_info_id = ?`
+	_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.MaxDate, item.LatestValue, item.LatestValue, edbInfoId).Exec()
 	return
 }
 
@@ -490,6 +495,9 @@ func UnifiedModifyPredictEdbInfoMaxAndMinInfo(edbInfo *EdbInfo, latestDateStr st
 		return
 	}
 
+	// 最晚的日期的值
+	maxAndMinItem.EndValue = maxAndMinItem.LatestValue
+
 	if maxAndMinItem != nil {
 		maxAndMinItem.LatestDate = latestDateStr
 		maxAndMinItem.LatestValue = latestValue
@@ -990,8 +998,8 @@ func GetPredictEdbDataListAllByStartDate(edbInfo *EdbInfo, order int, startDate
 // ModifyPredictEdbInfoMaxAndMinInfo 修改预测指标的最新数据信息
 func ModifyPredictEdbInfoMaxAndMinInfo(edbInfoId int, item *EdbInfoMaxAndMinInfo) (err error) {
 	o := orm.NewOrm()
-	sql := ` UPDATE edb_info SET start_date=?,end_date=?,min_value=?,max_value=?,is_update=2,latest_date=?,latest_value=?,modify_time=NOW() WHERE edb_info_id=? `
-	_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.MinValue, item.MaxValue, item.LatestDate, item.LatestValue, edbInfoId).Exec()
+	sql := ` UPDATE edb_info SET start_date=?,end_date=?,min_value=?,max_value=?,is_update=2,latest_date=?,latest_value=?,end_value=?,modify_time=NOW() WHERE edb_info_id=? `
+	_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.MinValue, item.MaxValue, item.LatestDate, item.LatestValue, item.EndValue, edbInfoId).Exec()
 	return
 }
 

+ 56 - 10
models/predict_edb.go

@@ -23,6 +23,8 @@ type CalculateRule struct {
 	RuleType                   int              `description:"预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值,9:动态环差"`
 	EndDate                    string           `description:"截止日期"`
 	EdbInfoIdArr               []EdbInfoFromTag `description:"指标信息"`
+	EmptyType                  int              `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType               int              `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
 }
 
 // RefreshCalculateByRuleBy9 刷新计算
@@ -56,15 +58,15 @@ func CalculateByRuleBy9(to orm.TxOrmer, rule CalculateRule) (resultDataList []*E
 
 	formulaStr := strings.ToUpper(rule.Formula)
 	// 获取关联指标数据
-	for _, v := range rule.EdbInfoList {
+	for edbInfoIndex, v := range rule.EdbInfoList {
 		dataList, tmpErr := GetPredictEdbDataListAll(v, 1)
 		if tmpErr != nil {
 			err = tmpErr
 			return
 		}
 
-		lenData := len(dataList)
-		for edbInfoIndex, dv := range dataList {
+		//lenData := len(dataList)
+		for _, dv := range dataList {
 			// 现有实际数据
 			if val, ok := realSaveDataMap[dv.DataTime]; ok {
 				if _, ok := val[v.EdbInfoId]; !ok {
@@ -90,10 +92,17 @@ func CalculateByRuleBy9(to orm.TxOrmer, rule CalculateRule) (resultDataList []*E
 			// 以第一个指标的日期作为基准日期
 			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 maxStartDate.IsZero() || tmpDate.Before(maxStartDate) {
+					maxStartDate = tmpDate
+				}
 			}
 		}
 
-		if lenData > 0 {
+		/*if lenData > 0 {
 			tmpLatestDate, _ := time.ParseInLocation(utils.FormatDate, dataList[lenData-1].DataTime, time.Local)
 			if minLatestDate.IsZero() || minLatestDate.After(tmpLatestDate) {
 				minLatestDate = tmpLatestDate
@@ -103,18 +112,20 @@ func CalculateByRuleBy9(to orm.TxOrmer, rule CalculateRule) (resultDataList []*E
 			if maxStartDate.IsZero() || maxStartDate.Before(tmpStartDate) {
 				maxStartDate = tmpStartDate
 			}
-		}
+		}*/
 	}
 
-	//数据处理,将日期内不全的数据做填补
-	handleDateSaveDataMap(dateList, maxStartDate, minLatestDate, realSaveDataMap, saveDataMap, rule.EdbInfoList)
+	// todo 数据处理,将日期内不全的数据做填补
+	HandleDateSaveDataMap(dateList, maxStartDate, minLatestDate, realSaveDataMap, saveDataMap, rule.EdbInfoList, rule.EmptyType)
 
 	// 添加数据
 	addDataList := make([]*PredictEdbRuleData, 0)
 
 	// 计算规则
-	formulaMap := utils.CheckFormula(formulaStr)
-
+	formulaDateSlice, formulaDateMap, err := utils.HandleFormulaJson(formulaStr, minLatestDate)
+	if err != nil {
+		return
+	}
 	//获取指标所有数据
 	dataList := make([]*PredictEdbRuleData, 0)
 	sql := `SELECT * FROM predict_edb_rule_data WHERE config_id = ?`
@@ -129,9 +140,44 @@ func CalculateByRuleBy9(to orm.TxOrmer, rule CalculateRule) (resultDataList []*E
 	existDataMap := make(map[string]string)
 
 	removeDateList := make([]string, 0) //需要移除的日期
+	// 判断是否特殊处理max和min函数
+	maxDealFlag := false
+	if rule.EmptyType == 4 && rule.MaxEmptyType == 2 {
+		maxDealFlag = true
+	}
 	for sk, sv := range saveDataMap {
+		// 当空值处理类型选择了不计算时,只要有一个指标在某个日期没有值(即空值),则计算指标在该日期没有值
+		if rule.EmptyType == 1 {
+			if len(sv) != len(rule.EdbInfoList) {
+				continue
+			}
+		}
+		//fmt.Println(sk, sv)
+		// 根据时间范围,选择对应的公式
+		formulaMap := make(map[string]string)
+		formulaStr = ""
+		for _, fv := range formulaDateSlice {
+			if sk < fv {
+				if f, ok := formulaDateMap[fv]; ok {
+					formulaStr = f
+					formulaMap = utils.CheckFormula(formulaStr)
+				}
+				break
+			}
+		}
+		if formulaStr == "" {
+			continue
+		}
+		svMax := make(map[int]float64)
+		if maxDealFlag {
+			// 特殊处理max和min函数,如果原本的值为空,则选择空值参与运算
+			if svMaxData, ok := realSaveDataMap[sk]; ok {
+				svMax = svMaxData
+			}
+		}
+		formulaStr = strings.ToUpper(formulaStr)
 		//fmt.Println(sk, sv)
-		formulaFormStr := ReplaceFormula(rule.EdbInfoList, sv, formulaMap, formulaStr, rule.EdbInfoIdBytes)
+		formulaFormStr := ReplaceFormula(rule.EdbInfoList, sv, svMax, formulaMap, formulaStr, rule.EdbInfoIdBytes, maxDealFlag)
 		//计算公式异常,那么就移除该指标
 		if formulaFormStr == "" {
 			removeDateList = append(removeDateList, sk)

+ 8 - 2
models/predict_edb_conf.go

@@ -24,6 +24,8 @@ type AddPredictEdbInfoReq struct {
 type RuleConfig struct {
 	RuleType     int              `description:"预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值,9:动态环差"`
 	Value        string           `description:"值/计算公式"`
+	EmptyType    int              `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType int              `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
 	EndDate      string           `description:"截止日期"`
 	EdbInfoIdArr []EdbInfoFromTag `description:"指标信息"`
 }
@@ -43,6 +45,8 @@ type PredictEdbConf struct {
 	RuleType         int       `description:"预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值"`
 	FixedValue       float64   `description:"固定值"`
 	Value            string    `description:"配置的值"`
+	EmptyType        int       `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int       `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
 	EndDate          time.Time `description:"截止日期"`
 	ModifyTime       time.Time `description:"修改时间"`
 	CreateTime       time.Time `description:"添加时间"`
@@ -56,6 +60,8 @@ type PredictEdbConfAndData struct {
 	RuleType         int                  `description:"预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值,9:动态环差"`
 	FixedValue       float64              `description:"固定值"`
 	Value            string               `description:"配置的值"`
+	EmptyType        int                  `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int                  `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
 	EndDate          time.Time            `description:"截止日期"`
 	ModifyTime       time.Time            `description:"修改时间"`
 	CreateTime       time.Time            `description:"添加时间"`
@@ -125,8 +131,8 @@ func ModifyPredictEdbInfoMaxAndMinInfoBySourceEdbInfoId(sourceEdbInfoId int, ite
 
 		//sql := ` UPDATE edb_info SET start_date=?,min_value=?,max_value=?,is_update=2,latest_date=?,latest_value=?,modify_time=NOW() WHERE edb_info_id in (` + utils.GetOrmInReplace(int(total)) + `) `
 		//_, err = o.Raw(sql, item.MinDate, item.MinValue, item.MaxValue, item.MaxDate, item.LatestValue, idList).Exec()
-		sql := ` UPDATE edb_info SET start_date=?,is_update=2,latest_date=?,latest_value=?,modify_time=NOW() WHERE edb_info_id in (` + utils.GetOrmInReplace(int(total)) + `) `
-		_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.LatestValue, idList).Exec()
+		sql := ` UPDATE edb_info SET start_date=?,is_update=2,latest_date=?,latest_value=?,end_value=?,modify_time=NOW() WHERE edb_info_id in (` + utils.GetOrmInReplace(int(total)) + `) `
+		_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.LatestValue, item.LatestValue, idList).Exec()
 	}
 	return
 }

+ 36 - 0
routers/commentsRouter.go

@@ -763,6 +763,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:PredictController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:PredictController"],
+        beego.ControllerComments{
+            Method: "CalculateByNinePreview",
+            Router: `/calculate_by_nine/preview`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_index_lib/controllers:PredictController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:PredictController"],
         beego.ControllerComments{
             Method: "Refresh",
@@ -1024,6 +1033,33 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:YongyiController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:YongyiController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:YongyiController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:YongyiController"],
+        beego.ControllerComments{
+            Method: "HandleExcelData",
+            Router: `/handle/excel_data`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_index_lib/controllers:YongyiController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:YongyiController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_index_lib/controllers:ZzController"] = append(beego.GlobalControllerRouter["eta/eta_index_lib/controllers:ZzController"],
         beego.ControllerComments{
             Method: "Add",

+ 5 - 0
routers/router.go

@@ -154,6 +154,11 @@ func init() {
 				&controllers.BaiinfoController{},
 			),
 		),
+		beego.NSNamespace("/yongyi",
+			beego.NSInclude(
+				&controllers.YongyiController{},
+			),
+		),
 		beego.NSNamespace("/stock_plant",
 			beego.NSInclude(
 				&controllers.StockPlantController{},

+ 241 - 0
services/base_from_predict.go

@@ -0,0 +1,241 @@
+package services
+
+import (
+	"errors"
+	"eta/eta_index_lib/models"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"github.com/shopspring/decimal"
+	"github.com/yidane/formula"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// GetCalculateByRuleByNineParams 获取预测规则9的计算参数
+func GetCalculateByRuleByNineParams(req models.RuleConfig) (formula string, edbInfoList []*models.EdbInfo, edbInfoIdBytes []string, err error, errMsg string) {
+	formula = req.Value
+	formula = strings.Replace(formula, "(", "(", -1)
+	formula = strings.Replace(formula, ")", ")", -1)
+	formula = strings.Replace(formula, ",", ",", -1)
+	formula = strings.Replace(formula, "。", ".", -1)
+	formula = strings.Replace(formula, "%", "*0.01", -1)
+
+	//检验公式
+	var checkFormulaStr string
+	for _, tmpEdbInfoId := range req.EdbInfoIdArr {
+		checkFormulaStr += tmpEdbInfoId.FromTag + ","
+		edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
+	}
+
+	formulaSlice, err := utils.CheckFormulaJson(formula)
+	if err != nil {
+		errMsg = "公式格式错误,请重新填写"
+		err = errors.New(errMsg)
+		return
+	}
+	for _, f := range formulaSlice {
+		formulaMap := utils.CheckFormula(f)
+		for _, v := range formulaMap {
+			if !strings.Contains(checkFormulaStr, v) {
+				errMsg = "公式错误,请重新填写"
+				err = errors.New(errMsg)
+				return
+			}
+		}
+	}
+
+	//关联的指标信息
+	edbInfoList = make([]*models.EdbInfo, 0)
+
+	for _, tmpEdbInfoId := range req.EdbInfoIdArr {
+		fromEdbInfo, tmpErr := models.GetEdbInfoById(tmpEdbInfoId.EdbInfoId)
+		if tmpErr != nil {
+			if tmpErr.Error() == utils.ErrNoRow() {
+				err = errors.New("指标 " + strconv.Itoa(tmpEdbInfoId.EdbInfoId) + " 不存在")
+			} else {
+				err = errors.New("获取指标失败:Err:" + tmpErr.Error())
+			}
+			errMsg = "数据计算失败"
+			return
+		}
+		edbInfoList = append(edbInfoList, fromEdbInfo)
+	}
+
+	for _, v := range formulaSlice {
+		formulaMap := utils.CheckFormula(v)
+		//预先计算,判断公式是否正常
+		ok, _ := models.CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
+		if !ok {
+			errMsg = "生成计算指标失败,请使用正确的计算公式"
+			err = errors.New(errMsg)
+			return
+		}
+	}
+
+	return
+}
+
+// CalculateByRuleByNine 动态环差规则计算入库
+func CalculateByRuleByNine(formulaStr string, edbInfoList []*models.EdbInfo, edbInfoIdBytes []string, emptyType, maxEmptyType int) (dataList []*models.EdbDataList, err error) {
+	realSaveDataMap := make(map[string]map[int]float64)
+	saveDataMap := make(map[string]map[int]float64)
+	dateList := make([]string, 0) //日期
+
+	// 最小的结束日期 , 最晚的数据开始日期
+	var minLatestDate, maxStartDate time.Time
+	formulaStr = strings.ToUpper(formulaStr)
+	// 获取关联指标数据
+	for edbInfoIndex, v := range edbInfoList {
+		// todo eta_api和eta_index_lib两个函数的逻辑不一致
+		sourceDataList, tmpErr := models.GetPredictEdbDataListAll(v, 1)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		dataMap := make(map[string]float64)
+		//lenData := len(sourceDataList)
+		for _, dv := range sourceDataList {
+			// 实际数据
+			if val, ok := realSaveDataMap[dv.DataTime]; ok {
+				if _, ok1 := val[v.EdbInfoId]; !ok1 {
+					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 _, ok1 := val[v.EdbInfoId]; !ok1 {
+					val[v.EdbInfoId] = dv.Value
+				}
+			} else {
+				temp2 := make(map[int]float64)
+				temp2[v.EdbInfoId] = dv.Value
+				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 maxStartDate.IsZero() || tmpDate.Before(maxStartDate) {
+					maxStartDate = tmpDate
+				}
+			}
+
+			/*			if lenData > 0 {
+						tmpLatestDate, _ := time.ParseInLocation(utils.FormatDate, sourceDataList[lenData-1].DataTime, time.Local)
+						if minLatestDate.IsZero() || minLatestDate.After(tmpLatestDate) {
+							minLatestDate = tmpLatestDate
+						}
+
+						tmpStartDate, _ := time.ParseInLocation(utils.FormatDate, sourceDataList[0].DataTime, time.Local)
+						if maxStartDate.IsZero() || maxStartDate.Before(tmpStartDate) {
+							maxStartDate = tmpStartDate
+						}
+					}*/
+		}
+		item := new(models.CalculateItems)
+		item.EdbInfoId = v.EdbInfoId
+		item.DataMap = dataMap
+	}
+
+	models.HandleDateSaveDataMap(dateList, maxStartDate, minLatestDate, realSaveDataMap, saveDataMap, edbInfoList, emptyType)
+	// 添加数据
+	dataList = make([]*models.EdbDataList, 0)
+
+	// 计算规则
+	formulaDateSlice, formulaDateMap, err := utils.HandleFormulaJson(formulaStr, minLatestDate)
+	if err != nil {
+		return
+	}
+	existDataMap := make(map[string]string)
+	// 判断是否特殊处理max和min函数
+	maxDealFlag := false
+	if emptyType == 4 && maxEmptyType == 2 {
+		maxDealFlag = true
+	}
+	for k, date := range dateList {
+		sv := saveDataMap[date]
+		// 当空值处理类型选择了不计算时,只要有一个指标在某个日期没有值(即空值),则计算指标在该日期没有值
+		if emptyType == 1 {
+			if len(sv) != len(edbInfoList) {
+				continue
+			}
+		}
+		//fmt.Println(sk, sv)
+		// 根据时间范围,选择对应的公式
+		formulaMap := make(map[string]string)
+		formulaStr = ""
+		for _, fv := range formulaDateSlice {
+			if date < fv {
+				if f, ok := formulaDateMap[fv]; ok {
+					formulaStr = f
+					formulaMap = utils.CheckFormula(formulaStr)
+				}
+				break
+			}
+		}
+		if formulaStr == "" {
+			continue
+		}
+		svMax := make(map[int]float64)
+		if maxDealFlag {
+			// 特殊处理max和min函数,如果原本的值为空,则选择空值参与运算
+			if svMaxData, ok := realSaveDataMap[date]; ok {
+				svMax = svMaxData
+			}
+		}
+
+		//fmt.Println(date, sv)
+
+		formulaFormStr := models.ReplaceFormula(edbInfoList, sv, svMax, formulaMap, formulaStr, edbInfoIdBytes, maxDealFlag)
+		if formulaFormStr == `` {
+			//计算公式异常,那么就移除该指标
+			continue
+		}
+
+		//fmt.Println(fmt.Sprintf("formulaFormStr:%s", formulaFormStr))
+		fmt.Println(fmt.Sprintf("%s:formulaFormStr:%s", date, formulaFormStr))
+		expression := formula.NewExpression(formulaFormStr)
+		calResult, tmpErr := expression.Evaluate()
+		if tmpErr != nil {
+			// 分母为0的报错
+			if strings.Contains(tmpErr.Error(), "divide by zero") {
+				continue
+			}
+			err = errors.New("计算失败:Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
+			return
+		}
+		calVal, tmpErr := calResult.Float64()
+		if tmpErr != nil {
+			err = errors.New("计算失败:获取计算值失败 Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
+			fmt.Println(err)
+			return
+		}
+
+		saveValue, _ := decimal.NewFromFloat(calVal).RoundCeil(4).Float64() //utils.SubFloatToString(calVal, 4)
+		dataTime, _ := time.Parse(utils.FormatDate, date)
+		timestamp := dataTime.UnixNano() / 1e6
+
+		if _, existOk := existDataMap[date]; !existOk {
+			tmpPredictEdbRuleData := &models.EdbDataList{
+				EdbDataId:     k,
+				EdbInfoId:     0,
+				DataTime:      date,
+				DataTimestamp: timestamp,
+				Value:         saveValue,
+			}
+			dataList = append(dataList, tmpPredictEdbRuleData)
+		}
+		existDataMap[date] = date
+	}
+	return
+}

+ 236 - 0
services/base_from_yongyi.go

@@ -0,0 +1,236 @@
+package services
+
+import (
+	"eta/eta_index_lib/logic"
+	"eta/eta_index_lib/models"
+	"eta/eta_index_lib/services/alarm_msg"
+	"eta/eta_index_lib/utils"
+	"fmt"
+	"strings"
+	"time"
+)
+
+// HandleYongyiIndex 处理涌益咨询的excel数据
+func HandleYongyiIndex(req *models.HandleYongyiExcelDataReq) (err error) {
+	errMsgList := make([]string, 0)
+	defer func() {
+		if len(errMsgList) > 0 {
+			msg := fmt.Sprint("数据源-涌益咨询数据处理失败,err:", strings.Join(errMsgList, "\n"))
+			utils.FileLog.Info(msg)
+			go alarm_msg.SendAlarmMsg(msg, 3)
+		}
+	}()
+	for _, v := range req.List {
+		if v.IndexName == "" || v.IndexCode == "" {
+			errMsgList = append(errMsgList, fmt.Sprintf("新增指标异常,指标编码%s或者指标ID%s为空:", v.IndexCode, v.IndexName))
+			continue
+		}
+		err = handleYongyiIndex(v, req.TerminalCode)
+		if err != nil {
+			errMsgList = append(errMsgList, fmt.Sprintf("新增指标异常,指标编码:%s, Err: %s", v.IndexCode, err))
+			return
+		}
+	}
+	return
+}
+
+func handleYongyiIndex(req *models.HandleYongyiExcelData, terminalCode string) (err error) {
+	indexName := req.IndexName
+	indexCode := req.IndexCode
+	excelDataMap := req.ExcelDataMap
+	errMsgList := make([]string, 0)
+	defer func() {
+		if len(errMsgList) > 0 {
+			msg := fmt.Sprint("数据源-涌益咨询数据处理失败,err:", strings.Join(errMsgList, "\n"))
+			utils.FileLog.Info(msg)
+			go alarm_msg.SendAlarmMsg(msg, 3)
+		}
+	}()
+	indexObj := new(models.BaseFromYongyiIndex)
+	dataObj := new(models.BaseFromYongyiData)
+	classifyObj := new(models.BaseFromYongyiClassify)
+
+	var indexId int64
+
+	addDataList := make([]*models.BaseFromYongyiData, 0)
+
+	exitDataMap := make(map[string]*models.BaseFromYongyiData)
+
+	// 修改指标信息
+	if indexName == "" {
+		utils.FileLog.Info("未刷新到指标数据:indexName:" + indexName)
+		return
+	}
+	// 判断目录是否存在
+	var classifyId int64
+	now := time.Now()
+	if req.ClassifyName != "" {
+		classifyObj, err = classifyObj.GetByClassifyName(req.ClassifyName)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				//新增分类
+				classifyObj = &models.BaseFromYongyiClassify{
+					ClassifyName:    req.ClassifyName,
+					ParentId:        0,
+					SysUserId:       0,
+					SysUserRealName: "",
+					Level:           1,
+					Sort:            req.ClassifySort,
+					ModifyTime:      now,
+					CreateTime:      now,
+				}
+
+				classifyId, err = classifyObj.Add()
+				if err != nil {
+					err = fmt.Errorf("新增分类失败 Err:%s", err)
+					return
+				}
+				classifyObj.ClassifyId = classifyId
+			} else {
+				return
+			}
+		} else {
+			classifyId = classifyObj.ClassifyId
+		}
+	}
+
+	//判断指标是否存在
+	var isAdd int
+	item, err := indexObj.GetByIndexCode(indexCode)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			isAdd = 1
+			err = nil
+		} else {
+			isAdd = -1
+			err = fmt.Errorf("查询数据源指标库失败 GetByIndexCode Err:%s", err)
+			return
+		}
+	}
+	if item != nil && item.YongyiIndexId > 0 {
+		fmt.Println("item:", item)
+		isAdd = 2
+	} else {
+		isAdd = 1
+	}
+
+	if isAdd == 1 {
+		indexObj.IndexCode = indexCode
+		indexObj.IndexName = indexName
+		indexObj.Frequency = req.Frequency
+		indexObj.ClassifyId = classifyId
+		indexObj.Unit = req.Unit
+		indexObj.Sort = req.Sort
+		indexObj.ModifyTime = time.Now()
+		indexObj.CreateTime = time.Now()
+		indexObj.TerminalCode = terminalCode
+		indexId, err = indexObj.Add()
+		if err != nil {
+			err = fmt.Errorf("数据源新增涌溢指标失败 Err:%s", err)
+			return
+		}
+		indexObj.YongyiIndexId = indexId
+	} else if isAdd == 2 {
+		if item.TerminalCode == `` && terminalCode != `` {
+			item.TerminalCode = terminalCode
+			err = item.Update([]string{"TerminalCode"})
+			if err != nil {
+				err = fmt.Errorf("数据源更新涌溢指标失败 Err:%s", err)
+				return
+			}
+		}
+
+		//获取已存在的所有数据
+		var exitDataList []*models.BaseFromYongyiData
+		exitDataList, err = dataObj.GetByIndexCode(indexCode)
+		if err != nil {
+			err = fmt.Errorf("数据源查询涌溢指标数据失败 Err:%s", err)
+			return
+		}
+		fmt.Println("exitDataListLen:", len(exitDataList))
+		for _, v := range exitDataList {
+			dateStr := v.DataTime
+			exitDataMap[dateStr] = v
+		}
+		indexId = item.YongyiIndexId
+	}
+
+	// 遍历excel数据,然后跟现有的数据做校验,不存在则入库
+	for date, value := range excelDataMap {
+		if findData, ok := exitDataMap[date]; !ok {
+			_, err = time.ParseInLocation(utils.FormatDate, date, time.Local)
+			if err != nil {
+				err = fmt.Errorf("%s 转换日期格式失败 Err:%s", date, err)
+				return
+			}
+			if !strings.Contains(value, "#N/A") {
+				var saveDataTime time.Time
+				if strings.Contains(date, "00:00:00") {
+					saveDataTime, err = time.Parse(utils.FormatDateTime, date)
+				} else {
+					saveDataTime, err = time.Parse(utils.FormatDate, date)
+				}
+				if err != nil {
+					err = fmt.Errorf("%s 转换日期格式失败 Err:%s", date, err)
+					continue
+				}
+				timestamp := saveDataTime.UnixNano() / 1e6
+
+				dataItem := new(models.BaseFromYongyiData)
+				dataItem.YongyiIndexId = int(indexId)
+				dataItem.IndexCode = indexCode
+				dataItem.DataTime = date
+				dataItem.Value = value
+				dataItem.CreateTime = time.Now()
+				dataItem.ModifyTime = time.Now()
+				dataItem.DataTimestamp = timestamp
+				addDataList = append(addDataList, dataItem)
+			}
+		} else {
+			if findData != nil && findData.Value != value && !strings.Contains(value, "#N/A") { //修改数据
+
+			}
+		}
+	}
+
+	if len(addDataList) > 0 {
+		err = dataObj.AddMulti(addDataList)
+		if err != nil {
+			err = fmt.Errorf("批量新增指标失败 Err:%s", err)
+			return
+		}
+
+		var dateItem *models.EdbInfoMaxAndMinInfo
+		dateItem, err = dataObj.GetMaxAndMinDateByIndexCode(indexCode)
+		if err != nil {
+			err = fmt.Errorf("查询指标最新日期失败 Err:%s", err)
+			return
+		}
+
+		go func() {
+			indexObj.ModifyIndexMaxAndMinDate(indexCode, dateItem)
+		}()
+	}
+
+	// 同步刷新ETA指标库的指标
+	{
+		// 获取指标详情
+		baseObj := new(models.BaseFromYongyi)
+		var edbInfo *models.EdbInfo
+		edbInfo, err = models.GetEdbInfoByEdbCode(baseObj.GetSource(), indexCode)
+		if err != nil {
+			if err.Error() != utils.ErrNoRow() {
+				errMsgList = append(errMsgList, fmt.Sprint("刷新ETA指标异常,指标编码:", indexCode, err.Error()))
+				return
+			} else {
+				err = nil
+			}
+		}
+
+		// 已经加入到指标库的话,那么就去更新ETA指标库吧
+		if edbInfo != nil {
+			go logic.RefreshBaseEdbInfo(edbInfo, ``)
+		}
+	}
+	return
+}

+ 53 - 1
utils/base_from_calculate.go

@@ -1,12 +1,16 @@
 package utils
 
 import (
+	"encoding/json"
+	"fmt"
+	"sort"
 	"strings"
+	"time"
 )
 
 // CheckFormula 检测计算公式是否异常
 func CheckFormula(formula string) map[string]string {
-	mathFormula := []string{"MAX", "MIN", "ABS", "ACOS", "ASIN", "CEIL", "MOD", "POW", "ROUND", "SIGN", "SIN", "TAN", "LOG10", "LOG2", "LOG", "LN"}
+	mathFormula := []string{"MAX", "MIN", "ABS", "ACOS", "ASIN", "CEIL", "MOD", "POW", "ROUND", "SIGN", "SIN", "TAN", "LOG10", "LOG2", "LOG", "LN", "EXP"}
 
 	str := strings.ToUpper(formula)
 	for _, v := range mathFormula {
@@ -27,3 +31,51 @@ func CheckFormula(formula string) map[string]string {
 	}
 	return byteMap
 }
+
+type FormulaListItem struct {
+	Formula string `json:"f"`
+	Date    string `json:"d"`
+}
+
+// HandleFormulaJson 处理计算公式json串是否异常
+func HandleFormulaJson(formula string, endDate time.Time) (dateSlice []string, formulaMap map[string]string, err error) {
+	list := make([]FormulaListItem, 0)
+	err = json.Unmarshal([]byte(formula), &list)
+	if err != nil {
+		err = fmt.Errorf("公式串解析失败: json.Unmarshal Err: %v", err)
+		return
+	}
+	formulaMap = make(map[string]string)
+	dateSlice = make([]string, 0)
+	// 查找最后的一天作为首个公式的起始日期
+	maxDate := endDate.AddDate(0, 0, 1).Format(FormatDate)
+	// 日期排序
+	for k, v := range list {
+		if k == 0 { // 首个日期均为起始日
+			v.Date = maxDate
+		}
+		formulaMap[v.Date] = v.Formula
+		dateSlice = append(dateSlice, v.Date)
+	}
+	sort.Slice(dateSlice, func(i, j int) bool {
+		return dateSlice[i] < dateSlice[j]
+	})
+
+	return
+}
+
+// CheckFormulaJson 检测计算公式json串是否异常
+func CheckFormulaJson(formula string) (formulaSlice []string, err error) {
+	list := make([]FormulaListItem, 0)
+	err = json.Unmarshal([]byte(formula), &list)
+	if err != nil {
+		err = fmt.Errorf("公式串解析失败: json.Unmarshal Err: %v", err)
+		return
+	}
+	formulaSlice = make([]string, 0)
+	// 日期排序
+	for _, v := range list {
+		formulaSlice = append(formulaSlice, v.Formula)
+	}
+	return
+}

+ 3 - 0
utils/constants.go

@@ -20,6 +20,8 @@ const (
 	PageSize30                 = 30
 )
 
+const DATA_SOURCE_YONYI = 76 //涌益咨询 =
+
 // 数据来源渠道
 const (
 	DATA_SOURCE_THS                                  = iota + 1 //同花顺
@@ -175,6 +177,7 @@ const (
 	DATA_SOURCE_NAME_CALCULATE_ZSXY                       = `指数修匀`              //指数修匀->72
 	DATA_SOURCE_NAME_PREDICT_CALCULATE_ZSXY               = `预测指数修匀`            //预测指数修匀->73
 	DATA_SOURCE_NAME_CALCULATE_ZDYFX                      = `自定义分析`             //自定义分析->74
+	DATA_SOURCE_NAME_YONYI                                = `涌益咨询`              // 涌益咨询
 )
 
 // 基础数据初始化日期