Browse Source

Merge remote-tracking branch 'origin/chart/13.0' into debug

# Conflicts:
#	controllers/base_from_predict_calculate.go
Roc 2 years ago
parent
commit
b00adbf3f4

+ 71 - 0
controllers/base_from_predict_calculate.go

@@ -517,6 +517,14 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 		return
 	}
 
+	// 基础指标id
+	fromEdbInfoId := req.FromEdbInfoId
+	notNeedFromEdbSourceList := []int{utils.DATA_SOURCE_PREDICT_CALCULATE_KSZS} // 不需要传入来源指标id的 指标类型
+	if fromEdbInfoId <= 0 && !utils.InArrayByInt(notNeedFromEdbSourceList, req.Source) {
+		br.Msg = "请选择指标"
+		return
+	}
+
 	//加入缓存机制,避免创建同一个名称的指标 start
 	redisKey := fmt.Sprint("edb_lib:predict_calculate:batch:save:", req.Source, ":", req.EdbName)
 	isExist := utils.Rc.IsExist(redisKey)
@@ -555,6 +563,22 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 		return
 	}
 
+	// 来源预测指标信息
+	var fromEdbInfo *models.EdbInfo
+	if req.FromEdbInfoId > 0 {
+		fromEdbInfo, err = models.GetEdbInfoById(req.FromEdbInfoId)
+		if err != nil {
+			br.Msg = "获取指标信息失败"
+			br.ErrMsg = "获取指标信息失败:Err:" + err.Error()
+			return
+		}
+		if fromEdbInfo.EdbInfoType != 1 {
+			br.Msg = "来源指标不是预测指标"
+			br.ErrMsg = "来源指标不是预测指标"
+			return
+		}
+	}
+
 	//生成指标编码
 	randStr := utils.GetRandDigit(4)
 	edbCode := `C` + time.Now().Format("060102") + randStr
@@ -723,6 +747,15 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 			return
 		}
 		edbInfo, latestDateStr, latestValue, err = models.SavePredictCalculateNhcc(&req, fromEdbInfo, secondEdbInfo, edbCode, uniqueCode, nhccDate, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_JP {
+		sourName = utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_JP
+		edbInfo, latestDateStr, latestValue, err, errMsg = models.SavePredictCalculateJp(req.EdbInfoId, req.ClassifyId, req.EdbName, req.Frequency, req.Unit, req.Formula, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_NH {
+		sourName = utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_NH
+		edbInfo, latestDateStr, latestValue, err, errMsg = models.SavePredictCalculateNh(req.EdbInfoId, req.ClassifyId, req.EdbName, req.Frequency, req.Unit, req.Formula, fromEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_KSZS {
+		sourName = utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_KSZS
+		edbInfo, latestDateStr, latestValue, err, errMsg = models.SavePredictCalculateKszs(req.EdbInfoId, req.ClassifyId, req.EdbName, req.Frequency, req.Unit, req.Formula, req.EdbInfoIdArr, edbCode, uniqueCode, adminId, adminName)
 	} else {
 		br.Msg = "无效计算方式"
 		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
@@ -1054,6 +1087,44 @@ func (this *PredictCalculateController) Refresh() {
 			errMsg = "RefreshAllPredictCalculateNhcc Err:" + err.Error()
 			break
 		}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_JP: //降频
+		calculateInfo, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoCalculateTczDetail Err:" + err.Error()
+			break
+		}
+		fromEdbInfo, err := models.GetEdbInfoById(calculateInfo.FromEdbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoById Err:" + err.Error()
+			break
+		}
+		latestDateStr, latestValue, err = models.RefreshAllPredictCalculateJp(edbInfoId, source, fromEdbInfo, calculateInfo.EdbCode, edbInfo.Frequency, edbInfo.CalculateFormula)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllPredictCalculateJp Err:" + err.Error()
+			break
+		}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_NH: //年化
+		calculateInfo, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoCalculateTczDetail Err:" + err.Error()
+			break
+		}
+		fromEdbInfo, err := models.GetEdbInfoById(calculateInfo.FromEdbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoById Err:" + err.Error()
+			break
+		}
+		latestDateStr, latestValue, err = models.RefreshAllPredictCalculateNh(edbInfoId, source, fromEdbInfo, calculateInfo.EdbCode, edbInfo.Frequency, edbInfo.CalculateFormula)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllPredictCalculateNh Err:" + err.Error()
+			break
+		}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_KSZS: //扩散指数
+		latestDateStr, latestValue, err = models.RefreshAllPredictCalculateKszs(edbInfo)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllPredictCalculateKszs Err:" + err.Error()
+			break
+		}
 	default:
 		br.Msg = "来源异常,请联系相关开发!"
 		br.ErrMsg = "来源异常,请联系相关开发"

+ 22 - 19
models/base_from_calculate.go

@@ -581,25 +581,28 @@ func GetFormulaMap() map[string]string {
 
 // EdbInfoCalculateBatchSaveReq 添加计算指标的请求参数
 type EdbInfoCalculateBatchSaveReq struct {
-	AdminId          int    `description:"添加人id"`
-	AdminName        string `description:"添加人名称"`
-	EdbInfoId        int    `description:"指标id"`
-	EdbName          string `description:"指标名称"`
-	Frequency        string `description:"频度"`
-	Unit             string `description:"单位"`
-	ClassifyId       int    `description:"分类id"`
-	Formula          string `description:"N值/移动天数"`
-	FromEdbInfoId    int    `description:"计算来源指标id"`
-	Source           int    `description:"来源:1:同花顺,2:wind,3:彭博,4:指标运算,5:累计值转月,6:同比值,7:同差值,8:N数值移动平均计算,12:环比值,13:环差值,14:升频"`
-	CalculateFormula string `description:"计算公式"`
-	EdbInfoIdArr     []struct {
-		EdbInfoId int    `description:"指标id"`
-		FromTag   string `description:"指标对应标签"`
-		MoveValue int    `description:"移动的值"`
-	}
-	MoveType      int    `description:"移动方式:1:领先(默认),2:滞后"`
-	MoveFrequency string `description:"移动频度:天/周/月/季/年"`
-	Calendar      string `description:"公历/农历"`
+	AdminId          int                            `description:"添加人id"`
+	AdminName        string                         `description:"添加人名称"`
+	EdbInfoId        int                            `description:"指标id"`
+	EdbName          string                         `description:"指标名称"`
+	Frequency        string                         `description:"频度"`
+	Unit             string                         `description:"单位"`
+	ClassifyId       int                            `description:"分类id"`
+	Formula          string                         `description:"N值/移动天数"`
+	FromEdbInfoId    int                            `description:"计算来源指标id"`
+	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:"公历/农历"`
+}
+
+// EdbInfoCalculateEdbInfoIdReq 新增/编辑请求 关联的指标列表
+type EdbInfoCalculateEdbInfoIdReq struct {
+	EdbInfoId int    `description:"指标id"`
+	FromTag   string `description:"指标对应标签"`
+	MoveValue int    `description:"移动的值"`
 }
 
 // EdbInfoCalculateBatchEditReq 编辑计算指标的请求参数

+ 6 - 6
models/edb_data_calculate_jp.go

@@ -113,7 +113,7 @@ func AddCalculateJp(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edb
 	}
 
 	//计算数据
-	err = refreshAllCalculateJp(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode, edbInfo.Frequency, fromEdbInfo.Frequency, edbInfo.CalculateFormula)
+	err = refreshAllCalculateJp(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode, edbInfo.Frequency, edbInfo.CalculateFormula)
 
 	return
 }
@@ -181,8 +181,8 @@ func EditCalculateJp(edbInfo *EdbInfo, req *EdbInfoCalculateBatchEditReq, fromEd
 		calculateMappingItem := &EdbInfoCalculateMapping{
 			EdbInfoCalculateMappingId: 0,
 			EdbInfoId:                 edbInfo.EdbInfoId,
-			Source:                    utils.DATA_SOURCE_CALCULATE_JP,
-			SourceName:                utils.DATA_SOURCE_NAME_CALCULATE_JP,
+			Source:                    edbInfo.Source,
+			SourceName:                edbInfo.SourceName,
 			EdbCode:                   edbInfo.EdbCode,
 			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
 			FromEdbCode:               fromEdbInfo.EdbCode,
@@ -201,7 +201,7 @@ func EditCalculateJp(edbInfo *EdbInfo, req *EdbInfoCalculateBatchEditReq, fromEd
 	}
 
 	//计算数据
-	err = refreshAllCalculateJp(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode, edbInfo.Frequency, fromEdbInfo.Frequency, edbInfo.CalculateFormula)
+	err = refreshAllCalculateJp(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode, edbInfo.Frequency, edbInfo.CalculateFormula)
 
 	return
 }
@@ -222,13 +222,13 @@ func RefreshAllCalculateJp(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode,
 	}()
 
 	// 计算数据
-	err = refreshAllCalculateJp(to, edbInfoId, source, fromEdbInfo, edbCode, edbFrequency, fromEdbInfo.Frequency, formula)
+	err = refreshAllCalculateJp(to, edbInfoId, source, fromEdbInfo, edbCode, edbFrequency, formula)
 
 	return
 }
 
 // refreshAllCalculateJp 刷新降频数据
-func refreshAllCalculateJp(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, edbFrequency, fromEdbFrequency, formula string) (err error) {
+func refreshAllCalculateJp(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, edbFrequency, formula string) (err error) {
 	edbInfoIdStr := strconv.Itoa(edbInfoId)
 
 	//计算数据

+ 2 - 2
models/edb_data_calculate_nh.go

@@ -154,8 +154,8 @@ func EditCalculateNh(edbInfo *EdbInfo, req *EdbInfoCalculateBatchEditReq, fromEd
 		calculateMappingItem := &EdbInfoCalculateMapping{
 			EdbInfoCalculateMappingId: 0,
 			EdbInfoId:                 edbInfo.EdbInfoId,
-			Source:                    utils.DATA_SOURCE_CALCULATE_NH,
-			SourceName:                utils.DATA_SOURCE_NAME_CALCULATE_NH,
+			Source:                    edbInfo.Source,
+			SourceName:                edbInfo.SourceName,
 			EdbCode:                   edbInfo.EdbCode,
 			FromEdbInfoId:             fromEdbInfo.EdbInfoId,
 			FromEdbCode:               fromEdbInfo.EdbCode,

+ 6 - 0
models/edb_data_table.go

@@ -109,6 +109,12 @@ func GetEdbDataTableName(source int) (tableName string) {
 		tableName = "edb_data_calculate_nh"
 	case utils.DATA_SOURCE_CALCULATE_KSZS:
 		tableName = "edb_data_calculate_kszs"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_JP:
+		tableName = "edb_data_predict_calculate_jp"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_NH:
+		tableName = "edb_data_predict_calculate_nh"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_KSZS:
+		tableName = "edb_data_predict_calculate_kszs"
 	default:
 		tableName = ""
 	}

+ 419 - 0
models/predict_edb_data_calculate_jp.go

@@ -0,0 +1,419 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// SavePredictCalculateJp 预测降频值
+func SavePredictCalculateJp(reqEdbInfoId, classifyId int, edbName, frequency, unit, formula string, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("SavePredictCalculateJp,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	fmt.Println("reqEdbInfoId:", reqEdbInfoId)
+
+	if reqEdbInfoId <= 0 {
+		edbInfo = &EdbInfo{
+			//EdbInfoId:        0,
+			EdbInfoType:   1,
+			SourceName:    utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_JP,
+			Source:        utils.DATA_SOURCE_PREDICT_CALCULATE_JP,
+			EdbCode:       edbCode,
+			EdbName:       edbName,
+			EdbNameSource: edbName,
+			Frequency:     frequency,
+			Unit:          unit,
+			//StartDate:        "",
+			//EndDate:          "",
+			ClassifyId:       classifyId,
+			SysUserId:        sysUserId,
+			SysUserRealName:  sysUserRealName,
+			UniqueCode:       uniqueCode,
+			CreateTime:       time.Now(),
+			ModifyTime:       time.Now(),
+			MinValue:         0,
+			MaxValue:         0,
+			CalculateFormula: formula,
+			EdbType:          2,
+			Sort:             0,
+			MoveType:         0,
+			MoveFrequency:    "",
+			NoUpdate:         0,
+			ServerUrl:        "",
+			EdbNameEn:        "",
+			UnitEn:           "",
+			LatestDate:       "",
+			LatestValue:      0,
+			ChartImage:       "",
+		}
+		newEdbInfoId, tmpErr := to.Insert(edbInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		edbInfo.EdbInfoId = int(newEdbInfoId)
+
+		// 添加关联关系
+		{
+			calculateMappingItem := &EdbInfoCalculateMapping{
+				EdbInfoCalculateMappingId: 0,
+				EdbInfoId:                 edbInfo.EdbInfoId,
+				Source:                    edbInfo.Source,
+				SourceName:                edbInfo.SourceName,
+				EdbCode:                   edbCode,
+				FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+				FromEdbCode:               fromEdbInfo.EdbCode,
+				FromEdbName:               fromEdbInfo.EdbName,
+				FromSource:                fromEdbInfo.Source,
+				FromSourceName:            fromEdbInfo.SourceName,
+				FromTag:                   "",
+				Sort:                      1,
+				CreateTime:                time.Now(),
+				ModifyTime:                time.Now(),
+			}
+			_, err = to.Insert(calculateMappingItem)
+			if err != nil {
+				return
+			}
+		}
+	} else {
+		edbInfo, err = GetEdbInfoById(reqEdbInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = `获取指标信息失败`
+			}
+			return
+		}
+		if edbInfo.EdbInfoType != 1 {
+			errMsg = `该指标不是预测指标`
+			err = errors.New(errMsg)
+			return
+		}
+
+		//判断计算指标是否被更换
+		var existCondition string
+		var existPars []interface{}
+		existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+		existPars = append(existPars, edbInfo.EdbInfoId, fromEdbInfo.EdbInfoId)
+
+		count, tmpErr := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+		if tmpErr != nil {
+			err = errors.New("判断指标是否改变失败,Err:" + tmpErr.Error())
+			return
+		}
+		if count > 0 { // 指标未被替换,无需重新计算
+			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)
+		sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+		_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+
+		//关联关系
+		{
+			calculateMappingItem := &EdbInfoCalculateMapping{
+				EdbInfoCalculateMappingId: 0,
+				EdbInfoId:                 edbInfo.EdbInfoId,
+				Source:                    edbInfo.Source,
+				SourceName:                edbInfo.SourceName,
+				EdbCode:                   edbCode,
+				FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+				FromEdbCode:               fromEdbInfo.EdbCode,
+				FromEdbName:               fromEdbInfo.EdbName,
+				FromSource:                fromEdbInfo.Source,
+				FromSourceName:            fromEdbInfo.SourceName,
+				FromTag:                   "",
+				Sort:                      1,
+				CreateTime:                time.Now(),
+				ModifyTime:                time.Now(),
+			}
+			_, err = to.Insert(calculateMappingItem)
+			if err != nil {
+				return
+			}
+		}
+
+	}
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateJp(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbCode, edbInfo.Frequency, formula)
+
+	return
+}
+
+// RefreshAllPredictCalculateJp 刷新全部预测降频值数据
+func RefreshAllPredictCalculateJp(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, edbFrequency, formula string) (latestDateStr string, latestValue float64, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculateJp,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateJp(to, edbInfoId, source, fromEdbInfo, edbCode, edbFrequency, formula)
+	return
+}
+
+// refreshAllPredictCalculateJp 刷新预测降频数据
+func refreshAllPredictCalculateJp(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, edbFrequency, formula string) (latestDateStr string, latestValue float64, err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	//获取源指标数据
+	fmt.Println("EdbInfoId:", fromEdbInfo.EdbInfoId)
+	dataList, err := GetPredictEdbDataListAll(fromEdbInfo, 1)
+	if err != nil {
+		return
+	}
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	fromDataMap := make(map[string]float64)
+	//来源指指标数据
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+		fromDataMap[v.DataTime] = v.Value
+	}
+	fmt.Println("source:", source)
+
+	//获取降频指标所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source)
+	if err != nil {
+		return
+	}
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+	}
+
+	latestDateStr = fromEdbInfo.LatestDate
+
+	tableName := GetEdbDataTableName(utils.DATA_SOURCE_PREDICT_CALCULATE_JP)
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	//existMap := make(map[string]string)
+	dataLen := len(dataList)
+	if dataLen <= 0 {
+		return
+	}
+	startDataTime, _ := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
+	endDataTime, _ := time.ParseInLocation(utils.FormatDate, dataList[dataLen-1].DataTime, time.Local)
+
+	var lastValue float64     // 最近的值
+	var nextEndDate time.Time // 下一个节点的日期
+	weekDayDataList := make([]float64, 0)
+	for currStartDataTime := startDataTime; !currStartDataTime.After(endDataTime); currStartDataTime = currStartDataTime.AddDate(0, 0, 1) {
+		// 将当前数据加入到 weekDayDataList
+		if tmpData, ok := dataMap[currStartDataTime.Format(utils.FormatDate)]; ok {
+			tmpValue := decimal.NewFromFloat(tmpData.Value)
+			tmpValueFloat, _ := tmpValue.Round(4).Float64()
+			weekDayDataList = append(weekDayDataList, tmpValueFloat)
+		}
+		// 如果下个节点的日期不存在,那么就先给赋值(兼容时间区间内只有一组数据的情况)
+		if nextEndDate.IsZero() {
+			nextEndDate = utils.GetFrequencyEndDay(currStartDataTime, edbFrequency)
+		}
+
+		// 日期处理过滤
+		switch edbFrequency {
+		case "周度":
+			if currStartDataTime.Weekday() != 0 {
+				//不是周日,代表需要进入下一个循环获取数据并计算
+				continue
+			} else {
+				//记录下一个结束节点的日期
+				nextEndDate = currStartDataTime.AddDate(0, 0, 7)
+			}
+		case "旬度":
+			nextDay := currStartDataTime.AddDate(0, 0, 1)
+			if nextDay.Day() != 1 && nextDay.Day() != 11 && nextDay.Day() != 21 {
+				//不是每月10、20、最后一天,代表需要进入下一个循环获取数据并计算
+				continue
+			} else {
+				//记录下一个结束节点的日期
+				if nextDay.Day() == 1 || nextDay.Day() == 11 {
+					//月初或者月末的时候,加10天就好了
+					nextEndDate = nextDay.AddDate(0, 0, 9)
+				} else {
+					tmpNextMonth := nextDay.AddDate(0, 1, 0)
+					nextEndDate = time.Date(tmpNextMonth.Year(), tmpNextMonth.Month(), 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, -1)
+				}
+			}
+		case "月度":
+			nextDay := currStartDataTime.AddDate(0, 0, 1)
+			if nextDay.Day() != 1 {
+				//不是每月最后一天,代表需要进入下一个循环获取数据并计算
+				continue
+			} else {
+				//记录下一个结束节点的日期
+				nextEndDate = nextDay.AddDate(0, 1, -1)
+			}
+		case "季度":
+			nextDay := currStartDataTime.AddDate(0, 0, 1)
+			if (nextDay.Month() == 1 || nextDay.Month() == 4 || nextDay.Month() == 7 || nextDay.Month() == 10) && nextDay.Day() == 1 {
+				//记录下一个结束节点的日期
+				nextEndDate = nextDay.AddDate(0, 3, -1)
+			} else {
+				//不是3,6,9,12 月份的最后一天,代表需要进入下一个循环获取数据并计算
+				continue
+			}
+		case "年度":
+			if currStartDataTime.Month() == 12 && currStartDataTime.Day() == 31 {
+				//记录下一个结束节点的日期
+				nextEndDate = currStartDataTime.AddDate(1, 0, 0)
+			} else {
+				//不是每年的12-31日,代表需要进入下一个循环获取数据并计算
+				continue
+			}
+		default:
+			err = errors.New("错误的频度:" + edbFrequency)
+			return
+		}
+
+		// 当前时间段内的数据计算,得出实际值
+		var currVal float64
+		lenWeekDayDataList := len(weekDayDataList)
+		// 如果这个时间区间内没有数据,那么就采用上一个时间区间的值
+		if len(weekDayDataList) <= 0 {
+			currVal = lastValue
+		} else {
+			if formula == "期末值" {
+				currVal = weekDayDataList[lenWeekDayDataList-1]
+			} else {
+				// 平均值
+				sumValDeci := decimal.NewFromFloat(0)
+				for _, v := range weekDayDataList {
+					tmpValDeci := decimal.NewFromFloat(v)
+					sumValDeci = sumValDeci.Add(tmpValDeci)
+				}
+				lenDeci := decimal.NewFromInt(int64(lenWeekDayDataList))
+				currVal, _ = sumValDeci.Div(lenDeci).Round(4).Float64()
+			}
+		}
+
+		// 给实际日期数据的值赋值
+		if fromEdbInfo.LatestDate == currStartDataTime.Format(utils.FormatDate) {
+			latestValue = currVal
+		}
+
+		// 判断降频指标是否存在数据
+		if existData, ok := existDataMap[currStartDataTime.Format(utils.FormatDate)]; ok {
+			// 处理降频数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断降频数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := currStartDataTime.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currStartDataTime.Format(utils.FormatDate), timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		// 一轮结束后,数据清空
+		weekDayDataList = make([]float64, 0)
+	}
+
+	// 最后已有的日期处理完成后,需要对剩余不在时间段内的数据做处理
+	if len(weekDayDataList) > 0 {
+		// 当前时间段内的数据计算,得出实际值
+		var currVal float64
+		lenWeekDayDataList := len(weekDayDataList)
+		// 如果这个时间区间内没有数据,那么就采用上一个时间区间的值
+		if len(weekDayDataList) < 0 {
+			currVal = lastValue
+		} else {
+			if formula == "期末值" {
+				currVal = weekDayDataList[lenWeekDayDataList-1]
+			} else {
+				// 平均值
+				sumValDeci := decimal.NewFromFloat(0)
+				for _, v := range weekDayDataList {
+					tmpValDeci := decimal.NewFromFloat(v)
+					sumValDeci = sumValDeci.Add(tmpValDeci)
+				}
+				lenDeci := decimal.NewFromInt(int64(lenWeekDayDataList))
+				currVal, _ = sumValDeci.Div(lenDeci).Round(4).Float64()
+			}
+		}
+
+		// 判断降频指标是否存在数据
+		if existData, ok := existDataMap[nextEndDate.Format(utils.FormatDate)]; ok {
+			// 处理降频数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断降频数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := nextEndDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, nextEndDate.Format(utils.FormatDate), timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+	}
+
+	return
+}

+ 405 - 0
models/predict_edb_data_calculate_kszs.go

@@ -0,0 +1,405 @@
+package models
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// SavePredictCalculateKszs 预测扩散指数
+func SavePredictCalculateKszs(reqEdbInfoId, classifyId int, edbName, frequency, unit, formula string, relationCalculateEdbInfoIdList []EdbInfoCalculateEdbInfoIdReq, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("SavePredictCalculateKszs,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	fmt.Println("reqEdbInfoId:", reqEdbInfoId)
+
+	tagMap := make(map[string]int) //指标的标签map
+	relationEdbInfoList := make([]*EdbInfo, 0)
+	if reqEdbInfoId <= 0 {
+		edbInfo = &EdbInfo{
+			//EdbInfoId:        0,
+			EdbInfoType:   1,
+			SourceName:    utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_KSZS,
+			Source:        utils.DATA_SOURCE_PREDICT_CALCULATE_KSZS,
+			EdbCode:       edbCode,
+			EdbName:       edbName,
+			EdbNameSource: edbName,
+			Frequency:     frequency,
+			Unit:          unit,
+			//StartDate:        "",
+			//EndDate:          "",
+			ClassifyId:       classifyId,
+			SysUserId:        sysUserId,
+			SysUserRealName:  sysUserRealName,
+			UniqueCode:       uniqueCode,
+			CreateTime:       time.Now(),
+			ModifyTime:       time.Now(),
+			MinValue:         0,
+			MaxValue:         0,
+			CalculateFormula: formula,
+			EdbType:          2,
+			Sort:             0,
+			MoveType:         0,
+			MoveFrequency:    "",
+			NoUpdate:         0,
+			ServerUrl:        "",
+			EdbNameEn:        "",
+			UnitEn:           "",
+			LatestDate:       "",
+			LatestValue:      0,
+			ChartImage:       "",
+		}
+		newEdbInfoId, tmpErr := to.Insert(edbInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		edbInfo.EdbInfoId = int(newEdbInfoId)
+
+		//关联关系
+		calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+		for _, v := range relationCalculateEdbInfoIdList {
+			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 = 1
+			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
+			calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+
+			tagMap[v.FromTag] = v.EdbInfoId
+		}
+		_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+		if err != nil {
+			return
+		}
+	} else {
+		edbInfo, err = GetEdbInfoById(reqEdbInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = `获取指标信息失败`
+			}
+			return
+		}
+		if edbInfo.EdbInfoType != 1 {
+			errMsg = `该指标不是预测指标`
+			err = errors.New(errMsg)
+			return
+		}
+		//修改指标信息
+		edbInfo.EdbName = edbName
+		edbInfo.EdbNameSource = edbName
+		edbInfo.Frequency = frequency
+		edbInfo.Unit = unit
+		edbInfo.ClassifyId = classifyId
+		edbInfo.CalculateFormula = formula
+		edbInfo.ModifyTime = time.Now()
+		_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "ModifyTime")
+		if err != nil {
+			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)
+		sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+		_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+
+		//关联关系
+		calculateMappingItemList := make([]*EdbInfoCalculateMapping, 0)
+		for _, v := range relationCalculateEdbInfoIdList {
+			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 = 1
+			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
+			calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+
+			tagMap[v.FromTag] = v.EdbInfoId
+		}
+		_, err = to.InsertMulti(len(calculateMappingItemList), calculateMappingItemList)
+		if err != nil {
+			return
+		}
+
+	}
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateKszs(to, edbInfo.EdbInfoId, edbInfo.Source, relationEdbInfoList, edbCode, formula, tagMap)
+
+	return
+}
+
+// RefreshAllPredictCalculateKszs 刷新全部预测扩散指数数据
+func RefreshAllPredictCalculateKszs(edbInfo *EdbInfo) (latestDateStr string, latestValue float64, err error) {
+	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("RefreshAllCalculateKszs,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateKszs(to, edbInfo.EdbInfoId, edbInfo.Source, relationEdbInfoList, edbInfo.EdbCode, edbInfo.CalculateFormula, tagMap)
+	return
+}
+
+// refreshAllPredictCalculateKszs 刷新预测年化数据
+func refreshAllPredictCalculateKszs(to orm.TxOrmer, edbInfoId, source int, relationEdbInfoList []*EdbInfo, edbCode, calculateFormula string, tagMap map[string]int) (latestDateStr string, latestValue float64, err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	tableName := GetEdbDataTableName(utils.DATA_SOURCE_PREDICT_CALCULATE_KSZS)
+
+	// 获取扩散指标关联的指标id
+	checkEdbInfoIdMap := make(map[int]int)
+	{
+		var config KszsConfig
+		err = json.Unmarshal([]byte(calculateFormula), &config)
+		if err != nil {
+			return
+		}
+		if config.DateType == 1 {
+			for _, tmpEdbInfoId := range tagMap {
+				checkEdbInfoIdMap[tmpEdbInfoId] = tmpEdbInfoId
+			}
+		} else {
+			for _, v := range config.CheckList {
+				if tmpEdbInfoId, ok := tagMap[v]; ok {
+					checkEdbInfoIdMap[tmpEdbInfoId] = tmpEdbInfoId
+				}
+			}
+		}
+	}
+
+	//获取当前指标的所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source)
+	if err != nil {
+		return
+	}
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	removeDateMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+		removeDateMap[v.DataTime] = ``
+	}
+
+	var latestDateTime time.Time       //真实数据的最后日期
+	var hasValLatestDateTime time.Time // 存在数据的真实日期(正常情况下,这两个值是相等的,除非出现一种情况:真实数据的最后日期当天 计算不出结果,那么 hasValLatestDateTime 不等于 latestDateTime)
+	//获取来源指标的数据
+	relationEdbDataMap := make(map[int]map[string]float64)
+	// 获取选择指标的 需要数据的 开始日期和结束日期
+	var startDate, endDate time.Time
+	for _, v := range relationEdbInfoList {
+		tmpLatestDate, _ := time.ParseInLocation(utils.FormatDate, v.LatestDate, time.Local)
+		if latestDateTime.IsZero() || latestDateTime.After(tmpLatestDate) {
+			// 真实数据的最后日期
+			latestDateTime = tmpLatestDate
+		}
+
+		tmpDataList, tmpErr := GetPredictEdbDataListAll(v, 1)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		if tmpDataList != nil {
+			if _, ok2 := checkEdbInfoIdMap[v.EdbInfoId]; ok2 {
+				lenTmpDataList := len(tmpDataList)
+				if lenTmpDataList > 0 {
+					tmpStartTime, _ := time.ParseInLocation(utils.FormatDate, tmpDataList[0].DataTime, time.Local)
+					tmpEndTime, _ := time.ParseInLocation(utils.FormatDate, tmpDataList[lenTmpDataList-1].DataTime, time.Local)
+
+					if startDate.IsZero() || tmpStartTime.Before(startDate) {
+						startDate = tmpStartTime
+					}
+
+					if tmpEndTime.IsZero() || tmpEndTime.After(endDate) {
+						endDate = tmpEndTime
+					}
+				}
+			}
+			// 用上期的数据补充当期的数据处理
+			handleDataMap := make(map[string]float64)
+			err = HandleDataByPreviousData(tmpDataList, handleDataMap)
+			if err != nil {
+				return
+			}
+			relationEdbDataMap[v.EdbInfoId] = handleDataMap
+		}
+	}
+
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+
+	for currDate := startDate.AddDate(0, 0, 1); !currDate.After(endDate); currDate = currDate.AddDate(0, 0, 1) {
+		currDateStr := currDate.Format(utils.FormatDate)
+
+		//环差指数列表
+		tmpValList := make([]float64, 0)
+		for _, dataMap := range relationEdbDataMap {
+			currVal, ok := dataMap[currDateStr]
+			if !ok {
+				continue
+			}
+
+			perVal, ok := dataMap[currDate.AddDate(0, 0, -1).Format(utils.FormatDate)]
+			if !ok {
+				continue
+			}
+
+			var tmpVal float64
+			if currVal > perVal {
+				tmpVal = 1
+			} else if currVal == perVal {
+				tmpVal = 0.5
+			} else {
+				tmpVal = 0
+			}
+			tmpValList = append(tmpValList, tmpVal)
+		}
+
+		lenTmpValList := len(tmpValList)
+		if lenTmpValList <= 0 {
+			continue
+		}
+
+		currValDeci := decimal.NewFromFloat(0)
+		for _, tmpVal := range tmpValList {
+			currValDeci = currValDeci.Add(decimal.NewFromFloat(tmpVal))
+		}
+		currVal, _ := currValDeci.Div(decimal.NewFromInt(int64(lenTmpValList))).Round(4).Float64()
+
+		// 如果存在数据的真实日期为空,或者 当前日期 早于或等于 实际数据的日期,那么就给 存在数据的真实日期 赋值
+		if hasValLatestDateTime.IsZero() || currDate.Before(latestDateTime) || currDate.Equal(latestDateTime) {
+			latestValue = currVal
+			hasValLatestDateTime = currDate
+		}
+
+		// 判断扩散指数指标是否存在数据
+		if existData, ok := existDataMap[currDateStr]; ok {
+			// 处理扩散指数数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断扩散指数数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := currDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		delete(removeDateMap, currDateStr)
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+	}
+
+	// 移除不存在的日期数据
+	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
+		}
+	}
+
+	// 真实数据的最后日期
+	latestDateStr = hasValLatestDateTime.Format(utils.FormatDate)
+
+	return
+}

+ 363 - 0
models/predict_edb_data_calculate_nh.go

@@ -0,0 +1,363 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// SavePredictCalculateNh 预测年化值
+func SavePredictCalculateNh(reqEdbInfoId, classifyId int, edbName, frequency, unit, formula string, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error, errMsg string) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("SavePredictCalculateNh,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	fmt.Println("reqEdbInfoId:", reqEdbInfoId)
+
+	if reqEdbInfoId <= 0 {
+		edbInfo = &EdbInfo{
+			//EdbInfoId:        0,
+			EdbInfoType:   1,
+			SourceName:    utils.DATA_SOURCE_NAME_PREDICT_CALCULATE_NH,
+			Source:        utils.DATA_SOURCE_PREDICT_CALCULATE_NH,
+			EdbCode:       edbCode,
+			EdbName:       edbName,
+			EdbNameSource: edbName,
+			Frequency:     frequency,
+			Unit:          unit,
+			//StartDate:        "",
+			//EndDate:          "",
+			ClassifyId:       classifyId,
+			SysUserId:        sysUserId,
+			SysUserRealName:  sysUserRealName,
+			UniqueCode:       uniqueCode,
+			CreateTime:       time.Now(),
+			ModifyTime:       time.Now(),
+			MinValue:         0,
+			MaxValue:         0,
+			CalculateFormula: formula,
+			EdbType:          2,
+			Sort:             0,
+			MoveType:         0,
+			MoveFrequency:    "",
+			NoUpdate:         0,
+			ServerUrl:        "",
+			EdbNameEn:        "",
+			UnitEn:           "",
+			LatestDate:       "",
+			LatestValue:      0,
+			ChartImage:       "",
+		}
+		newEdbInfoId, tmpErr := to.Insert(edbInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		edbInfo.EdbInfoId = int(newEdbInfoId)
+
+		// 添加关联关系
+		{
+			calculateMappingItem := &EdbInfoCalculateMapping{
+				EdbInfoCalculateMappingId: 0,
+				EdbInfoId:                 edbInfo.EdbInfoId,
+				Source:                    edbInfo.Source,
+				SourceName:                edbInfo.SourceName,
+				EdbCode:                   edbCode,
+				FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+				FromEdbCode:               fromEdbInfo.EdbCode,
+				FromEdbName:               fromEdbInfo.EdbName,
+				FromSource:                fromEdbInfo.Source,
+				FromSourceName:            fromEdbInfo.SourceName,
+				FromTag:                   "",
+				Sort:                      1,
+				CreateTime:                time.Now(),
+				ModifyTime:                time.Now(),
+			}
+			_, err = to.Insert(calculateMappingItem)
+			if err != nil {
+				return
+			}
+		}
+	} else {
+		edbInfo, err = GetEdbInfoById(reqEdbInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = `获取指标信息失败`
+			}
+			return
+		}
+		if edbInfo.EdbInfoType != 1 {
+			errMsg = `该指标不是预测指标`
+			err = errors.New(errMsg)
+			return
+		}
+
+		//判断计算指标是否被更换
+		var existCondition string
+		var existPars []interface{}
+		existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+		existPars = append(existPars, edbInfo.EdbInfoId, fromEdbInfo.EdbInfoId)
+
+		count, tmpErr := GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+		if tmpErr != nil {
+			err = errors.New("判断指标是否改变失败,Err:" + tmpErr.Error())
+			return
+		}
+		if count > 0 { // 指标未被替换,无需重新计算
+			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)
+		sql = ` DELETE FROM ` + tableName + ` WHERE edb_info_id = ? `
+		_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+
+		//关联关系
+		{
+			calculateMappingItem := &EdbInfoCalculateMapping{
+				EdbInfoCalculateMappingId: 0,
+				EdbInfoId:                 edbInfo.EdbInfoId,
+				Source:                    edbInfo.Source,
+				SourceName:                edbInfo.SourceName,
+				EdbCode:                   edbCode,
+				FromEdbInfoId:             fromEdbInfo.EdbInfoId,
+				FromEdbCode:               fromEdbInfo.EdbCode,
+				FromEdbName:               fromEdbInfo.EdbName,
+				FromSource:                fromEdbInfo.Source,
+				FromSourceName:            fromEdbInfo.SourceName,
+				FromTag:                   "",
+				Sort:                      1,
+				CreateTime:                time.Now(),
+				ModifyTime:                time.Now(),
+			}
+			_, err = to.Insert(calculateMappingItem)
+			if err != nil {
+				return
+			}
+		}
+
+	}
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateNh(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbCode)
+
+	return
+}
+
+// RefreshAllPredictCalculateNh 刷新全部预测年化值数据
+func RefreshAllPredictCalculateNh(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, edbFrequency, formula string) (latestDateStr string, latestValue float64, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllCalculateNh,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateNh(to, edbInfoId, source, fromEdbInfo, edbCode)
+	return
+}
+
+// refreshAllPredictCalculateNh 刷新预测年化数据
+func refreshAllPredictCalculateNh(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode string) (latestDateStr string, latestValue float64, err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	tableName := GetEdbDataTableName(utils.DATA_SOURCE_PREDICT_CALCULATE_NH)
+
+	//获取年化指标所有数据
+	existDataList, err := GetAllEdbDataListByTo(to, edbInfoId, source)
+	if err != nil {
+		return
+	}
+	//计算指标的map
+	existDataMap := make(map[string]*EdbData, 0)
+	removeDateMap := make(map[string]string)
+	for _, v := range existDataList {
+		existDataMap[v.DataTime] = v
+		removeDateMap[v.DataTime] = ``
+	}
+
+	//获取源指标数据
+	fmt.Println("EdbInfoId:", fromEdbInfo.EdbInfoId)
+	fromDataList, err := GetPredictEdbDataListAll(fromEdbInfo, 1)
+	if err != nil {
+		return
+	}
+	// 插值法数据处理
+	handleDataMap := make(map[string]float64)
+	err = HandleDataByLinearRegression(fromDataList, handleDataMap)
+	if err != nil {
+		return
+	}
+
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	fromDataMap := make(map[string]float64)
+	//来源指指标数据
+	for _, v := range fromDataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+		fromDataMap[v.DataTime] = v.Value
+	}
+	lenFromDataList := len(fromDataList)
+	// 如果来源指标没有数据,那么就直接返回得了
+	if lenFromDataList <= 0 {
+		sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? `, tableName)
+
+		_, err = to.Raw(sql, edbInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除年化指标数据失败,Err:" + err.Error())
+			return
+		}
+		return
+	}
+	fmt.Println("source:", source)
+
+	// 真实数据的最后日期
+	latestDateStr = fromEdbInfo.LatestDate
+	// 每年的最后一天的数据值
+	yearLastValMap := make(map[int]float64)
+	startDataTime, _ := time.ParseInLocation(utils.FormatDate, fromDataList[0].DataTime, time.Local)
+	endDataTime, _ := time.ParseInLocation(utils.FormatDate, fromDataList[lenFromDataList-1].DataTime, time.Local)
+	for i := startDataTime.Year(); i <= endDataTime.Year(); i++ {
+		tmpDateStr := fmt.Sprintf("%d-12-31", i)
+		if tmpVal, ok := handleDataMap[tmpDateStr]; ok {
+			yearLastValMap[i] = tmpVal
+		}
+	}
+
+	addSql := ` INSERT INTO ` + tableName + ` (edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	//来源指指标数据
+	for _, v := range fromDataList {
+		currDateStr := v.DataTime
+		currDate, _ := time.ParseInLocation(utils.FormatDate, currDateStr, time.Local)
+
+		perValMap := make(map[time.Time]float64)
+		//前3年当日的数据
+		for i := 1; i <= 3; i++ {
+			tmpDateTime := currDate.AddDate(-i, 0, 0)
+			if tmpVal, ok := handleDataMap[tmpDateTime.Format(utils.FormatDate)]; ok {
+				perValMap[tmpDateTime] = tmpVal
+			}
+		}
+		lenPerValMap := len(perValMap)
+		// 如果数据少于2年,那么就不参与计算,结束当前循环,进入下一个循环
+		if lenPerValMap < 2 {
+			continue
+		}
+
+		// N年 当前值占全年比重 的值列表
+		divValList := make([]decimal.Decimal, 0)
+		for tmpDateTime, tmpVal := range perValMap {
+			yearLastVal, ok2 := yearLastValMap[tmpDateTime.Year()]
+			// 如果当年最后一天没有数据
+			if !ok2 {
+				continue
+			}
+
+			// 当前值占全年比重
+			divVal := decimal.NewFromFloat(tmpVal).Div(decimal.NewFromFloat(yearLastVal))
+			divValList = append(divValList, divVal)
+		}
+
+		lenDivValList := len(divValList)
+		// 如果 N年 当前值占全年比重 的值 小于 2个,那么就不参与计算,结束当前循环,进入下一个循环
+		if lenDivValList < 2 {
+			continue
+		}
+
+		divValSum := decimal.NewFromFloat(0)
+		for _, divVal := range divValList {
+			divValSum = divValSum.Add(divVal)
+		}
+
+		// 当前计算出来的结果
+		currVal, _ := decimal.NewFromFloat(v.Value).Div(divValSum.Div(decimal.NewFromInt(int64(lenDivValList)))).Round(4).Float64()
+
+		// 给实际日期数据的值赋值
+		if fromEdbInfo.LatestDate == currDateStr {
+			latestValue = currVal
+		}
+		// 判断年化指标是否存在数据
+		if existData, ok := existDataMap[currDateStr]; ok {
+			// 处理年化数据的值
+			existValStr := existData.Value
+			existValDeci, tmpErr := decimal.NewFromString(existValStr)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			existVal, _ := existValDeci.Round(4).Float64()
+			// 判断年化数据的值 与 当前计算出来的结果, 如果两个数据结果不相等的话,那么就修改咯
+			if existVal != currVal {
+				err = ModifyEdbDataById(source, existData.EdbDataId, fmt.Sprint(currVal))
+				if err != nil {
+					return
+				}
+			}
+		} else {
+			// 直接入库
+			timestamp := currDate.UnixNano() / 1e6
+			timestampStr := fmt.Sprintf("%d", timestamp)
+			addSql += GetAddSql(edbInfoIdStr, edbCode, currDateStr, timestampStr, fmt.Sprint(currVal))
+			isAdd = true
+		}
+
+		delete(removeDateMap, currDateStr)
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+	}
+
+	// 移除不存在的日期数据
+	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
+}

+ 6 - 0
utils/constants.go

@@ -82,6 +82,9 @@ const (
 	DATA_SOURCE_CALCULATE_JP                            //降频->51
 	DATA_SOURCE_CALCULATE_NH                            //年化->52
 	DATA_SOURCE_CALCULATE_KSZS                          //扩散指数->53
+	DATA_SOURCE_PREDICT_CALCULATE_JP                    //预测指标 - 计算指标(降频)->54
+	DATA_SOURCE_PREDICT_CALCULATE_NH                    //预测指标 - 计算指标(年化)->55
+	DATA_SOURCE_PREDICT_CALCULATE_KSZS                  //预测指标 - 计算指标(扩散指数)->56
 )
 
 // 指标来源的中文展示
@@ -139,6 +142,9 @@ const (
 	DATA_SOURCE_NAME_CALCULATE_JP                 = `降频`                //降频->51
 	DATA_SOURCE_NAME_CALCULATE_NH                 = `年化`                //年化->52
 	DATA_SOURCE_NAME_CALCULATE_KSZS               = `扩散指数`              //扩散指数->53
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_JP         = `预测降频`              //预测指标 - 计算指标(降频)->54
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_NH         = `预测年化`              //预测指标 - 计算指标(年化)->55
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_KSZS       = `预测扩散指数`            //预测指标 - 计算指标(扩散指数)->56
 )
 
 // 基础数据初始化日期