Browse Source

feat:新增预测指标分段

Roc 2 years ago
parent
commit
2ac1b6de6c

+ 1 - 0
go.mod

@@ -21,6 +21,7 @@ require (
 	github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
 	github.com/rdlucklib/rdluck_tools v1.0.3
 	github.com/satori/go.uuid v1.2.0 // indirect
+	github.com/shopspring/decimal v1.3.1
 	github.com/silenceper/wechat/v2 v2.1.2
 	github.com/spf13/viper v1.9.0
 	github.com/swaggo/gin-swagger v1.3.3

+ 15 - 6
models/tables/predict_edb_conf/predict_edb_info.go

@@ -6,12 +6,15 @@ import (
 
 // PredictEdbConf 预测指标规则配置表
 type PredictEdbConf struct {
-	PredictEdbInfoID uint64    `gorm:"primaryKey;column:predict_edb_info_id;type:bigint(9) unsigned;not null;default:0" json:"-"` // 预测指标id
-	SourceEdbInfoID  uint64    `gorm:"column:source_edb_info_id;type:bigint(9) unsigned;default:0" json:"sourceEdbInfoId"`        // 来源指标id
-	RuleType         uint8     `gorm:"column:rule_type;type:tinyint(9) unsigned;default:1" json:"ruleType"`                       // 预测规则,1:最新,2:固定值
-	FixedValue       float64   `gorm:"column:fixed_value;type:decimal(38,4)" json:"fixedValue"`                                   // 固定值
-	ModifyTime       time.Time `gorm:"column:modify_time;type:datetime;default:CURRENT_TIMESTAMP" json:"modifyTime"`              // 修改时间
-	CreateTime       time.Time `gorm:"column:create_time;type:datetime" json:"createTime"`                                        // 添加时间
+	ConfigID         uint64    `gorm:"primaryKey;column:config_id;type:bigint(9) unsigned;not null" json:"-"`                         // 规则id
+	PredictEdbInfoID uint64    `gorm:"column:predict_edb_info_id;type:bigint(9) unsigned;not null;default:0" json:"predictEdbInfoId"` // 预测指标id
+	SourceEdbInfoID  uint64    `gorm:"column:source_edb_info_id;type:bigint(9) unsigned;default:0" json:"sourceEdbInfoId"`            // 来源指标id
+	RuleType         uint8     `gorm:"column:rule_type;type:tinyint(9) unsigned;default:1" json:"ruleType"`                           // 预测规则,1:最新,2:固定值
+	FixedValue       float64   `gorm:"column:fixed_value;type:decimal(38,4)" json:"fixedValue"`                                       // 固定值
+	Value            string    `gorm:"column:value;type:varchar(255);default:''" json:"value"`                                        // 配置的值
+	EndDate          time.Time `gorm:"column:end_date;type:date" json:"endDate"`                                                      // 截止日期
+	ModifyTime       time.Time `gorm:"column:modify_time;type:datetime;default:CURRENT_TIMESTAMP" json:"modifyTime"`                  // 修改时间
+	CreateTime       time.Time `gorm:"column:create_time;type:datetime" json:"createTime"`                                            // 添加时间
 }
 
 // TableName get sql table name.获取数据库表名
@@ -21,17 +24,23 @@ func (m *PredictEdbConf) TableName() string {
 
 // PredictEdbConfColumns get sql column name.获取数据库列名
 var PredictEdbConfColumns = struct {
+	ConfigID         string
 	PredictEdbInfoID string
 	SourceEdbInfoID  string
 	RuleType         string
 	FixedValue       string
+	Value            string
+	EndDate          string
 	ModifyTime       string
 	CreateTime       string
 }{
+	ConfigID:         "config_id",
 	PredictEdbInfoID: "predict_edb_info_id",
 	SourceEdbInfoID:  "source_edb_info_id",
 	RuleType:         "rule_type",
 	FixedValue:       "fixed_value",
+	Value:            "value",
+	EndDate:          "end_date",
 	ModifyTime:       "modify_time",
 	CreateTime:       "create_time",
 }

+ 6 - 0
models/tables/predict_edb_conf/query.go

@@ -7,3 +7,9 @@ func GetPredictEdbConfById(edbInfoId int) (item *PredictEdbConf, err error) {
 	err = global.MYSQL["data"].Where("predict_edb_info_id = ? ", edbInfoId).First(&item).Error
 	return
 }
+
+// GetPredictEdbConfListById 根据预测指标id获取预测指标配置信息列表
+func GetPredictEdbConfListById(edbInfoId int) (items []*PredictEdbConf, err error) {
+	err = global.MYSQL["data"].Where("predict_edb_info_id = ? ", edbInfoId).Order("config_id asc").Find(&items).Error
+	return
+}

+ 157 - 12
services/chart/predict_edb_info.go

@@ -2,10 +2,12 @@ package chart
 
 import (
 	"errors"
+	"github.com/shopspring/decimal"
 	edbDataModel "hongze/hongze_yb/models/tables/edb_data"
 	edbInfoModel "hongze/hongze_yb/models/tables/edb_info"
 	predictEdbConfModel "hongze/hongze_yb/models/tables/predict_edb_conf"
 	"hongze/hongze_yb/utils"
+	"strconv"
 	"time"
 )
 
@@ -56,6 +58,138 @@ func GetChartPredictEdbInfoDataList(predictEdbConf predictEdbConfModel.PredictEd
 	return
 }
 
+// GetChartPredictEdbInfoDataListByConfList 获取图表的预测指标的未来数据
+func GetChartPredictEdbInfoDataListByConfList(predictEdbConfList []*predictEdbConfModel.PredictEdbConf, filtrateStartDateStr, latestDateStr, endDateStr, frequency string, realPredictEdbInfoData []*edbDataModel.EdbDataList) (predictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64, err error) {
+	endDate, err := time.ParseInLocation(utils.FormatDate, endDateStr, time.Local)
+	if err != nil {
+		return
+	}
+
+	latestDate, err := time.ParseInLocation(utils.FormatDate, latestDateStr, time.Local)
+	if err != nil {
+		return
+	}
+
+	// 开始预测数据的时间
+	startDate := latestDate
+
+	// 如果有筛选时间的话
+	if filtrateStartDateStr != `` {
+		filtrateStartDate, tmpErr := time.ParseInLocation(utils.FormatDate, filtrateStartDateStr, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		//如果筛选时间晚于实际数据时间,那么就以筛选时间作为获取预测数据的时间
+		if filtrateStartDate.After(latestDate) {
+			startDate = filtrateStartDate.AddDate(0, 0, -1)
+		}
+	}
+
+	//var dateArr []string
+	// 对应日期的值
+	existMap := make(map[string]float64)
+	for _, v := range realPredictEdbInfoData {
+		//dateArr = append(dateArr, v.DataTime)
+		existMap[v.DataTime] = v.Value
+	}
+
+	predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
+	//dataValue := lastDataValue
+	//预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值
+
+	for _, predictEdbConf := range predictEdbConfList {
+		dataEndTime := endDate
+		if predictEdbConf.EndDate.Before(dataEndTime) {
+			dataEndTime = predictEdbConf.EndDate
+		}
+
+		var tmpMinValue, tmpMaxValue float64 // 当前预测结果中的最大/最小值
+
+		switch predictEdbConf.RuleType {
+		case 1: //1:最新
+			var lastDataValue float64 //最新值
+			tmpAllData := make([]*edbDataModel.EdbDataList, 0)
+			tmpAllData = append(tmpAllData, realPredictEdbInfoData...)
+			tmpAllData = append(tmpAllData, predictEdbInfoData...)
+			lenTmpAllData := len(tmpAllData)
+			if lenTmpAllData > 0 {
+				lastDataValue = tmpAllData[lenTmpAllData-1].Value
+			}
+			predictEdbInfoData = GetChartPredictEdbInfoDataListByRule1(int(predictEdbConf.PredictEdbInfoID), lastDataValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+			tmpMaxValue = lastDataValue
+			tmpMinValue = lastDataValue
+		case 2: //2:固定值
+			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			dataValue, _ := tmpValDecimal.Float64()
+			predictEdbInfoData = GetChartPredictEdbInfoDataListByRule1(int(predictEdbConf.PredictEdbInfoID), dataValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+
+			tmpMaxValue = dataValue
+			tmpMinValue = dataValue
+		case 3: //3:同比
+			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			tbValue, _ := tmpValDecimal.Float64()
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleTb(int(predictEdbConf.PredictEdbInfoID), tbValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+		case 4: //4:同差
+			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			tcValue, _ := tmpValDecimal.Float64()
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleTc(int(predictEdbConf.PredictEdbInfoID), tcValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+		case 5: //5:环比
+			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			hbValue, _ := tmpValDecimal.Float64()
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleHb(int(predictEdbConf.PredictEdbInfoID), hbValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+		case 6: //6:环差
+			tmpValDecimal, tmpErr := decimal.NewFromString(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			hcValue, _ := tmpValDecimal.Float64()
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleHc(int(predictEdbConf.PredictEdbInfoID), hcValue, startDate, dataEndTime, frequency, predictEdbInfoData, existMap)
+		case 7: //7:N期移动均值
+			nValue, tmpErr := strconv.Atoi(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleNMoveMeanValue(int(predictEdbConf.PredictEdbInfoID), nValue, startDate, dataEndTime, frequency, realPredictEdbInfoData, predictEdbInfoData, existMap)
+		case 8: //8:N期段线性外推值
+			nValue, tmpErr := strconv.Atoi(predictEdbConf.Value)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			predictEdbInfoData, tmpMinValue, tmpMaxValue = GetChartPredictEdbInfoDataListByRuleNLinearRegression(int(predictEdbConf.PredictEdbInfoID), nValue, startDate, dataEndTime, frequency, realPredictEdbInfoData, predictEdbInfoData, existMap)
+		}
+		//startDate = dataEndTime.AddDate(0, 0, 1)
+		startDate = dataEndTime
+		if tmpMinValue < minValue {
+			minValue = tmpMinValue
+		}
+		if tmpMaxValue < maxValue {
+			maxValue = tmpMaxValue
+		}
+	}
+
+	return
+}
+
 // GetPredictEdbDayList 获取预测指标日期列表
 func getPredictEdbDayList(startDate, endDate time.Time, frequency string) (dayList []time.Time) {
 	//if !utils.InArrayByStr([]string{"日度", "周度", "月度"}, frequency)
@@ -104,16 +238,17 @@ func GetPredictDataListByPredictEdbInfo(edbInfo *edbInfoModel.EdbInfo, startDate
 		return GetPredictCalculateDataListByPredictEdbInfo(edbInfo, startDate, endDate)
 	}
 	// 查找该预测指标配置
-	predictEdbConf, err = predictEdbConfModel.GetPredictEdbConfById(edbInfo.EdbInfoId)
-	if err != nil && err != utils.ErrNoRow {
+	predictEdbConfList, err := predictEdbConfModel.GetPredictEdbConfListById(edbInfo.EdbInfoId)
+	if err != nil {
 		errMsg = "获取预测指标配置信息失败"
 		return
 	}
-	if predictEdbConf == nil {
+	if len(predictEdbConfList) == 0 {
 		errMsg = "获取预测指标配置信息失败"
 		err = errors.New(errMsg)
 		return
 	}
+	predictEdbConf = predictEdbConfList[0]
 
 	// 来源指标
 	sourceEdbInfoItem, err = edbInfoModel.GetEdbInfoById(int(predictEdbConf.SourceEdbInfoID))
@@ -125,11 +260,21 @@ func GetPredictDataListByPredictEdbInfo(edbInfo *edbInfoModel.EdbInfo, startDate
 		return
 	}
 
+	allDataList := make([]*edbDataModel.EdbDataList, 0)
 	//获取指标数据(实际已生成)
 	dataList, err = edbDataModel.GetEdbDataList(sourceEdbInfoItem.Source, sourceEdbInfoItem.EdbInfoId, startDate, endDate)
 	if err != nil {
 		return
 	}
+	// 如果选择了日期,那么需要筛选所有的数据,用于未来指标的生成
+	if startDate != `` {
+		allDataList, err = edbDataModel.GetEdbDataList(sourceEdbInfoItem.Source, sourceEdbInfoItem.EdbInfoId, "", "")
+		if err != nil {
+			return
+		}
+	} else {
+		allDataList = dataList
+	}
 
 	// 获取预测指标未来的数据
 	predictDataList := make([]*edbDataModel.EdbDataList, 0)
@@ -143,22 +288,22 @@ func GetPredictDataListByPredictEdbInfo(edbInfo *edbInfoModel.EdbInfo, startDate
 			endDateStr = endDate
 		}
 	}
-	predictDataList, err = GetChartPredictEdbInfoDataList(*predictEdbConf, startDate, sourceEdbInfoItem.LatestDate.Format(utils.FormatDate), sourceEdbInfoItem.LatestValue, endDateStr, edbInfo.Frequency)
+	//predictDataList, err = GetChartPredictEdbInfoDataList(*predictEdbConf, startDate, sourceEdbInfoItem.LatestDate.Format(utils.FormatDate), sourceEdbInfoItem.LatestValue, endDateStr, edbInfo.Frequency)
+	var predictMinValue, predictMaxValue float64
+	predictDataList, predictMinValue, predictMaxValue, err = GetChartPredictEdbInfoDataListByConfList(predictEdbConfList, startDate, sourceEdbInfoItem.LatestDate.Format(utils.FormatDate), endDateStr, edbInfo.Frequency, allDataList)
 	if err != nil {
 		return
 	}
 	dataList = append(dataList, predictDataList...)
 	if len(predictDataList) > 0 {
-		tmpValue := predictDataList[0]
-
-		// 如果最大值 小于 预测值,那么将预测值作为最大值数据返回
-		if edbInfo.MaxValue < tmpValue.Value {
-			edbInfo.MaxValue = tmpValue.Value
+		// 如果最小值 大于 预测值,那么将预测值作为最小值数据返回
+		if edbInfo.MinValue > predictMinValue {
+			edbInfo.MinValue = predictMinValue
 		}
 
-		// 如果最小值 大于 预测值,那么将预测值作为最小值数据返回
-		if edbInfo.MinValue > tmpValue.Value {
-			edbInfo.MinValue = tmpValue.Value
+		// 如果最大值 小于 预测值,那么将预测值作为最大值数据返回
+		if edbInfo.MaxValue < predictMaxValue {
+			edbInfo.MaxValue = predictMaxValue
 		}
 	}
 	return

+ 561 - 0
services/chart/predict_edb_info_rule.go

@@ -0,0 +1,561 @@
+package chart
+
+import (
+	"github.com/shopspring/decimal"
+	edbDataModel "hongze/hongze_yb/models/tables/edb_data"
+	"hongze/hongze_yb/utils"
+	"time"
+)
+
+// GetChartPredictEdbInfoDataListByRule1 根据规则1获取预测数据
+func GetChartPredictEdbInfoDataListByRule1(edbInfoId int, dataValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList) {
+	newPredictEdbInfoData = predictEdbInfoData
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
+	for k, v := range dayList {
+		newPredictEdbInfoData = append(newPredictEdbInfoData, &edbDataModel.EdbDataList{
+			EdbDataId:     edbInfoId + 10000000000 + k,
+			EdbInfoId:     edbInfoId,
+			DataTime:      v.Format(utils.FormatDate),
+			Value:         dataValue,
+			DataTimestamp: (v.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
+		})
+		existMap[v.Format(utils.FormatDate)] = dataValue
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleTb 根据同比值规则获取预测数据
+//	2.1 同比: 在未来某一个时间段内,给定一个固定的同比增速a,用去年同期值X乘以同比增速(1+a),得到预测值Y=X(1+a)
+//	例: 今年1-3月值,100,100,120。给定同比增速a=0.1,则明年1-3月预测值为: 100*1.1=110,100*1.1=110,120*1.1=132。
+func GetChartPredictEdbInfoDataListByRuleTb(edbInfoId int, tbValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
+	newPredictEdbInfoData = predictEdbInfoData
+	index := len(predictEdbInfoData)
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
+	for k, currentDate := range dayList {
+
+		tmpData := &edbDataModel.EdbDataList{
+			EdbDataId: edbInfoId + 10000000000 + index + k,
+			EdbInfoId: edbInfoId,
+			DataTime:  currentDate.Format(utils.FormatDate),
+			//Value:         dataValue,
+			DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
+		}
+
+		var val float64
+		var calculateStatus bool //计算结果
+		//currentItem := existMap[av]
+		//上一年的日期
+		preDate := currentDate.AddDate(-1, 0, 0)
+		preDateStr := preDate.Format(utils.FormatDate)
+		if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到
+			val = TbzDiv(preValue, tbValue)
+			calculateStatus = true
+		} else {
+			switch frequency {
+			case "月度":
+				//向上和向下,各找一个月
+				nextDateDay := preDate
+				preDateDay := preDate
+				for i := 0; i <= 35; i++ {
+					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+					if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+						val = TbzDiv(preValue, tbValue)
+						calculateStatus = true
+						break
+					} else {
+						preDateDayStr := preDateDay.Format(utils.FormatDate)
+						if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+							val = TbzDiv(preValue, tbValue)
+							calculateStatus = true
+							break
+						}
+					}
+					nextDateDay = nextDateDay.AddDate(0, 0, 1)
+					preDateDay = preDateDay.AddDate(0, 0, -1)
+				}
+
+			case "季度", "年度":
+				if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到
+					val = TbzDiv(preValue, tbValue)
+					calculateStatus = true
+					break
+				}
+			default:
+				nextDateDay := preDate
+				preDateDay := preDate
+
+				for i := 0; i < 35; i++ {
+					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+					if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+						val = TbzDiv(preValue, tbValue)
+						calculateStatus = true
+						break
+					} else {
+						preDateDayStr := preDateDay.Format(utils.FormatDate)
+						if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+							val = TbzDiv(preValue, tbValue)
+							calculateStatus = true
+							break
+						} else {
+							//fmt.Println("pre not find:", preDateStr, "i:", i)
+						}
+					}
+					nextDateDay = nextDateDay.AddDate(0, 0, 1)
+					preDateDay = preDateDay.AddDate(0, 0, -1)
+				}
+			}
+		}
+
+		if calculateStatus {
+			tmpData.Value = val
+			newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
+
+			// 最大最小值
+			if val < minValue {
+				minValue = val
+			}
+			if val < maxValue {
+				maxValue = val
+			}
+		}
+	}
+	return
+}
+
+// TbzDiv 同比值计算
+// @params a float64 去年同期值
+// @params b float64 固定同比增速
+func TbzDiv(a, b float64) (result float64) {
+	if b != 0 {
+		// 去年同期值
+		af := decimal.NewFromFloat(a)
+
+		// 同比增速
+		bf := decimal.NewFromFloat(b)
+
+		// 默认1
+		cf := decimal.NewFromFloat(1)
+
+		// 总增速
+		val := bf.Add(cf)
+
+		// 计算
+		result, _ = val.Mul(af).RoundCeil(4).Float64()
+	} else {
+		result = 0
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleTc 根据同差值规则获取预测数据
+//	2.2 同差: 在未来某一个时间段内,给定一个固定的同比增加值a,用去年同期值X加上同比增加值A,得到预测值Y=X+a
+//	例: 今年1-3月值,100,100,120。给定同比增加值a=10,则明年1-3月预测值为: 100+10=110,100+10=110,120+10=130
+func GetChartPredictEdbInfoDataListByRuleTc(edbInfoId int, tcValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
+	newPredictEdbInfoData = predictEdbInfoData
+	index := len(predictEdbInfoData)
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
+	for k, currentDate := range dayList {
+
+		tmpData := &edbDataModel.EdbDataList{
+			EdbDataId: edbInfoId + 10000000000 + index + k,
+			EdbInfoId: edbInfoId,
+			DataTime:  currentDate.Format(utils.FormatDate),
+			//Value:         dataValue,
+			DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
+		}
+
+		var val float64
+		var calculateStatus bool //计算结果
+		//currentItem := existMap[av]
+		//上一年的日期
+		preDate := currentDate.AddDate(-1, 0, 0)
+		preDateStr := preDate.Format(utils.FormatDate)
+		if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到
+			val = TczDiv(preValue, tcValue)
+			calculateStatus = true
+		} else {
+			switch frequency {
+			case "月度":
+				//向上和向下,各找一个月
+				nextDateDay := preDate
+				preDateDay := preDate
+				for i := 0; i <= 35; i++ {
+					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+					if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+						val = TczDiv(preValue, tcValue)
+						calculateStatus = true
+						break
+					} else {
+						preDateDayStr := preDateDay.Format(utils.FormatDate)
+						if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+							val = TczDiv(preValue, tcValue)
+							calculateStatus = true
+							break
+						}
+					}
+					nextDateDay = nextDateDay.AddDate(0, 0, 1)
+					preDateDay = preDateDay.AddDate(0, 0, -1)
+				}
+
+			case "季度", "年度":
+				if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到
+					val = TczDiv(preValue, tcValue)
+					calculateStatus = true
+					break
+				}
+			default:
+				nextDateDay := preDate
+				preDateDay := preDate
+
+				for i := 0; i < 35; i++ {
+					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+					if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+						val = TczDiv(preValue, tcValue)
+						calculateStatus = true
+						break
+					} else {
+						preDateDayStr := preDateDay.Format(utils.FormatDate)
+						if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+							val = TczDiv(preValue, tcValue)
+							calculateStatus = true
+							break
+						} else {
+							//fmt.Println("pre not find:", preDateStr, "i:", i)
+						}
+					}
+					nextDateDay = nextDateDay.AddDate(0, 0, 1)
+					preDateDay = preDateDay.AddDate(0, 0, -1)
+				}
+			}
+		}
+
+		if calculateStatus {
+			tmpData.Value = val
+			newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
+
+			// 最大最小值
+			if val < minValue {
+				minValue = val
+			}
+			if val < maxValue {
+				maxValue = val
+			}
+		}
+	}
+	return
+}
+
+// TczDiv 环差值计算
+// @params a float64 上一期值
+// @params b float64 固定的环比增加值
+func TczDiv(a, b float64) (result float64) {
+	if b != 0 {
+		// 上一期值
+		af := decimal.NewFromFloat(a)
+
+		// 固定的环比增加值
+		bf := decimal.NewFromFloat(b)
+
+		// 计算
+		result, _ = af.Add(bf).RoundCeil(4).Float64()
+	} else {
+		result = 0
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleHb 根据环比值规则获取预测数据
+//	环比:在未来某一个时间段内,给定一个固定的环比增速a,用上一期值X乘以环比增速(1+a),得到预测值Y=X(1+a)
+//	例: 最近1期值为100,给定环比增速a=0.2,则未来3期预测值为: 100*1.2=120,120*1.2=144,144*1.2=172.8
+func GetChartPredictEdbInfoDataListByRuleHb(edbInfoId int, hbValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
+	newPredictEdbInfoData = predictEdbInfoData
+	index := len(predictEdbInfoData)
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	for k, currentDate := range dayList {
+		tmpK := index + k - 1 //上1期的值
+
+		// 环比值计算
+		val := HbzDiv(newPredictEdbInfoData[tmpK].Value, hbValue)
+
+		currentDateStr := currentDate.Format(utils.FormatDate)
+		newPredictEdbInfoData = append(newPredictEdbInfoData, &edbDataModel.EdbDataList{
+			EdbDataId:     edbInfoId + 10000000000 + index + k,
+			EdbInfoId:     edbInfoId,
+			DataTime:      currentDateStr,
+			Value:         val,
+			DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
+		})
+		existMap[currentDateStr] = val
+
+		// 最大最小值
+		if val < minValue {
+			minValue = val
+		}
+		if val < maxValue {
+			maxValue = val
+		}
+	}
+	return
+}
+
+// HbzDiv 环比值计算
+// @params a float64 上一期值
+// @params b float64 固定的环比增速
+func HbzDiv(a, b float64) (result float64) {
+	if b != 0 {
+		// 上一期值
+		af := decimal.NewFromFloat(a)
+
+		// 固定的环比增速
+		bf := decimal.NewFromFloat(b)
+
+		// 默认1
+		cf := decimal.NewFromFloat(1)
+
+		// 总增速
+		val := bf.Add(cf)
+
+		// 计算
+		result, _ = val.Mul(af).RoundCeil(4).Float64()
+	} else {
+		result = 0
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleHc 根据环差值规则获取预测数据
+//	2.4 环差:在未来某一个时间段内,给定一个固定的环比增加值a,用上一期值X加上环比增加值a,得到预测值Y=X+a
+//	例: 最近1期值为100,给定环比增加值a=10,则未来3期预测值为: 100+10=110,110+10=120,120+10=130
+func GetChartPredictEdbInfoDataListByRuleHc(edbInfoId int, hcValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
+	newPredictEdbInfoData = predictEdbInfoData
+	index := len(predictEdbInfoData)
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	for k, currentDate := range dayList {
+		tmpK := index + k - 1 //上1期的值
+
+		// 环差别值计算
+		val := HczDiv(newPredictEdbInfoData[tmpK].Value, hcValue)
+
+		currentDateStr := currentDate.Format(utils.FormatDate)
+		newPredictEdbInfoData = append(newPredictEdbInfoData, &edbDataModel.EdbDataList{
+			EdbDataId:     edbInfoId + 10000000000 + index + k,
+			EdbInfoId:     edbInfoId,
+			DataTime:      currentDateStr,
+			Value:         val,
+			DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
+		})
+		existMap[currentDateStr] = val
+
+		// 最大最小值
+		if val < minValue {
+			minValue = val
+		}
+		if val < maxValue {
+			maxValue = val
+		}
+	}
+	return
+}
+
+// HczDiv 环差值计算
+// @params a float64 上一期值
+// @params b float64 固定的环比增加值
+func HczDiv(a, b float64) (result float64) {
+	if b != 0 {
+		// 上一期值
+		af := decimal.NewFromFloat(a)
+
+		// 固定的环比增加值
+		bf := decimal.NewFromFloat(b)
+
+		// 计算
+		result, _ = af.Add(bf).RoundCeil(4).Float64()
+	} else {
+		result = 0
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleNMoveMeanValue 根据N期移动均值规则获取预测数据
+//	2.5 N期移动均值:在未来某一个时间段内,下一期值等于过去N期值得平均值。
+//	例:最近3期值(N=3),为95,98,105则未来第1期值为 1/3*(95+98+105)=99.33, 未来第2期值为 1/3*(98+105+99.33)=100.78依次类推。
+func GetChartPredictEdbInfoDataListByRuleNMoveMeanValue(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
+	allDataList := make([]*edbDataModel.EdbDataList, 0)
+	allDataList = append(allDataList, realPredictEdbInfoData...)
+	allDataList = append(allDataList, predictEdbInfoData...)
+
+	newPredictEdbInfoData = predictEdbInfoData
+
+	lenAllData := len(allDataList)
+	if lenAllData < nValue || lenAllData <= 0 {
+		return
+	}
+	if nValue <= 0 {
+		return
+	}
+	// 分母
+	decimalN := decimal.NewFromInt(int64(nValue))
+
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	for k, currentDate := range dayList {
+		tmpIndex := lenAllData + k - 1 //上1期的值
+
+		// 数据集合中的最后一个数据
+		tmpDecimalVal := decimal.NewFromFloat(allDataList[tmpIndex].Value)
+		for tmpK := 2; tmpK <= nValue; tmpK++ {
+			tmpIndex2 := tmpIndex - tmpK //上N期的值
+			tmpDecimalVal2 := decimal.NewFromFloat(allDataList[tmpIndex2].Value)
+			tmpDecimalVal = tmpDecimalVal.Add(tmpDecimalVal2)
+		}
+
+		// N期移动均值计算
+		val, _ := tmpDecimalVal.Div(decimalN).RoundCeil(4).Float64()
+
+		currentDateStr := currentDate.Format(utils.FormatDate)
+		tmpData := &edbDataModel.EdbDataList{
+			EdbDataId:     edbInfoId + 10000000000 + lenAllData + k,
+			EdbInfoId:     edbInfoId,
+			DataTime:      currentDateStr,
+			Value:         val,
+			DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
+		}
+		newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
+		allDataList = append(allDataList, tmpData)
+		existMap[currentDateStr] = val
+
+		// 最大最小值
+		if val < minValue {
+			minValue = val
+		}
+		if val < maxValue {
+			maxValue = val
+		}
+	}
+	return
+}
+
+//	GetChartPredictEdbInfoDataListByRuleNLinearRegression 根据N期移动均值规则获取预测数据
+//	2.6N期段线性外推值:给出过去N期值所确定的线性回归方程(Y=aX+b)在未来一段时间内的推算值。回归方程虽然比较复杂,但各种编程语言应该都有现成的模块或函数,应该无需自己编写。
+//	例1:过去5期值(N=5)分别为:3,5,7,9,11(每两期值之间的时间间隔相等)。那么按照线性回归方程推算,未来三期的预测值是:13,15,17。
+//
+//	例2:过去6期值(N=6)分别为:3,3,5,7,9,11(每两期值之间的时间间隔相等)。那么按照线性回归方程推算,未来三期的预测值是:12.33,14.05,15.76。例1和例2的区别在于,多加了一期数据,导致回归方程发生改变,从而预测值不同。
+func GetChartPredictEdbInfoDataListByRuleNLinearRegression(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
+	//var errMsg string
+	//defer func() {
+	//	if errMsg != `` {
+	//		go alarm_msg.SendAlarmMsg("更新上海的token失败;ERR:"+err.Error(), 3)
+	//	}
+	//}()
+	allDataList := make([]*edbDataModel.EdbDataList, 0)
+	allDataList = append(allDataList, realPredictEdbInfoData...)
+	allDataList = append(allDataList, predictEdbInfoData...)
+
+	newPredictEdbInfoData = predictEdbInfoData
+
+	lenAllData := len(allDataList)
+	if lenAllData < nValue || lenAllData <= 0 {
+		return
+	}
+
+	if nValue <= 0 {
+		return
+	}
+
+	//获取后面的预测数据
+	// 获取线性方程公式的a、b的值
+	coordinateData := make([]Coordinate, 0)
+	for tmpK := nValue; tmpK > 0; tmpK-- {
+		tmpIndex2 := lenAllData - tmpK //上N期的值
+		tmpCoordinate := Coordinate{
+			X: float64(nValue - tmpK + 1),
+			Y: allDataList[tmpIndex2].Value,
+		}
+		coordinateData = append(coordinateData, tmpCoordinate)
+	}
+	a, b := getLinearResult(coordinateData)
+	//fmt.Println("a:", a, ";======b:", b)
+
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	for k, currentDate := range dayList {
+		tmpK := nValue + k + 1
+
+		aDecimal := decimal.NewFromFloat(a)
+		xDecimal := decimal.NewFromInt(int64(tmpK))
+		bDecimal := decimal.NewFromFloat(b)
+
+		val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).RoundCeil(4).Float64()
+
+		currentDateStr := currentDate.Format(utils.FormatDate)
+		tmpData := &edbDataModel.EdbDataList{
+			EdbDataId:     edbInfoId + 10000000000 + lenAllData + k,
+			EdbInfoId:     edbInfoId,
+			DataTime:      currentDateStr,
+			Value:         val,
+			DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
+		}
+		newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
+		allDataList = append(allDataList, tmpData)
+		existMap[currentDateStr] = val
+
+		// 最大最小值
+		if val < minValue {
+			minValue = val
+		}
+		if val < maxValue {
+			maxValue = val
+		}
+	}
+	return
+}
+
+// Series is a container for a series of data
+type Series []Coordinate
+
+// Coordinate holds the data in a series
+type Coordinate struct {
+	X, Y float64
+}
+
+func getLinearResult(s []Coordinate) (gradient, intercept float64) {
+	if len(s) == 0 {
+		return
+	}
+
+	// Placeholder for the math to be done
+	var sum [5]float64
+
+	// Loop over data keeping index in place
+	i := 0
+	for ; i < len(s); i++ {
+		sum[0] += s[i].X
+		sum[1] += s[i].Y
+		sum[2] += s[i].X * s[i].X
+		sum[3] += s[i].X * s[i].Y
+		sum[4] += s[i].Y * s[i].Y
+	}
+
+	// Find gradient and intercept
+	f := float64(i)
+	gradient = (f*sum[3] - sum[0]*sum[1]) / (f*sum[2] - sum[0]*sum[0])
+	intercept = (sum[1] / f) - (gradient * sum[0] / f)
+
+	//fmt.Println("gradient:", gradient, ";intercept:", intercept)
+	// Create the new regression series
+	//for j := 0; j < len(s); j++ {
+	//	regressions = append(regressions, Coordinate{
+	//		X: s[j].X,
+	//		Y: s[j].X*gradient + intercept,
+	//	})
+	//}
+
+	return
+}