|
@@ -1,6 +1,7 @@
|
|
|
package chart
|
|
|
|
|
|
import (
|
|
|
+ "encoding/json"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"github.com/nosixtools/solarlunar"
|
|
@@ -1498,3 +1499,325 @@ func handleNhccData(dataList []*edbDataModel.EdbDataList, moveDay int) (newDataL
|
|
|
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func GetChartPredictEdbInfoDataListByRuleNAnnualAverage(edbInfoId int, configValue string, dayList []time.Time, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64, err error) {
|
|
|
+
|
|
|
+ yearList, _, err := getYearListBySeasonConf(configValue)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ allDataList := make([]*edbDataModel.EdbDataList, 0)
|
|
|
+ allDataList = append(allDataList, realPredictEdbInfoData...)
|
|
|
+ allDataList = append(allDataList, predictEdbInfoData...)
|
|
|
+ newPredictEdbInfoData = predictEdbInfoData
|
|
|
+
|
|
|
+
|
|
|
+ handleDataMap := make(map[string]float64)
|
|
|
+ err = HandleDataByLinearRegression(allDataList, handleDataMap)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ index := len(allDataList)
|
|
|
+
|
|
|
+ predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
|
|
|
+ for k, currentDate := range dayList {
|
|
|
+
|
|
|
+ if strings.Contains(currentDate.Format(utils.FormatDate), "02-29") {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ tmpK := len(allDataList) - 1
|
|
|
+ lastDayData := allDataList[tmpK]
|
|
|
+
|
|
|
+ tmpHistoryVal := decimal.NewFromFloat(0)
|
|
|
+ tmpHistoryValNum := 0
|
|
|
+ for _, year := range yearList {
|
|
|
+
|
|
|
+ tmpHistoryCurrentDate := currentDate.AddDate(year-currentDate.Year(), 0, 0)
|
|
|
+ if val, ok := handleDataMap[tmpHistoryCurrentDate.Format(utils.FormatDate)]; ok {
|
|
|
+ tmpHistoryVal = tmpHistoryVal.Add(decimal.NewFromFloat(val))
|
|
|
+ tmpHistoryValNum++
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if tmpHistoryValNum != len(yearList) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ val, _ := tmpHistoryVal.Div(decimal.NewFromInt(int64(tmpHistoryValNum))).RoundCeil(4).Float64()
|
|
|
+
|
|
|
+ currentDateStr := currentDate.Format(utils.FormatDate)
|
|
|
+ tmpData := &edbDataModel.EdbDataList{
|
|
|
+ EdbDataId: edbInfoId + 100000 + index + k,
|
|
|
+ EdbInfoId: edbInfoId,
|
|
|
+ DataTime: currentDateStr,
|
|
|
+ Value: val,
|
|
|
+ DataTimestamp: currentDate.UnixNano() / 1e6,
|
|
|
+ }
|
|
|
+ newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
|
|
|
+ allDataList = append(allDataList, tmpData)
|
|
|
+ existMap[currentDateStr] = val
|
|
|
+
|
|
|
+
|
|
|
+ err = HandleDataByLinearRegression([]*edbDataModel.EdbDataList{
|
|
|
+ lastDayData, tmpData,
|
|
|
+ }, handleDataMap)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if val < minValue {
|
|
|
+ minValue = val
|
|
|
+ }
|
|
|
+ if val > maxValue {
|
|
|
+ maxValue = val
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type AnnualValueInversionConf struct {
|
|
|
+ Value float64 `description:"年度值"`
|
|
|
+ Type int `description:"分配方式,1:均值法;2:同比法"`
|
|
|
+ Year int `description:"同比年份"`
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func GetChartPredictEdbInfoDataListByRuleAnnualValueInversion(edbInfoId int, configValue string, dayList []time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64, err error) {
|
|
|
+ if frequency == "年度" {
|
|
|
+ err = errors.New("当前指标频度是年度,不允许配置年度值倒推")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ var annualValueInversionConf AnnualValueInversionConf
|
|
|
+ err = json.Unmarshal([]byte(configValue), &annualValueInversionConf)
|
|
|
+ if err != nil {
|
|
|
+ err = errors.New("年度值倒推配置信息异常:" + err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ allDataList := make([]*edbDataModel.EdbDataList, 0)
|
|
|
+ allDataList = append(allDataList, realPredictEdbInfoData...)
|
|
|
+ allDataList = append(allDataList, predictEdbInfoData...)
|
|
|
+ newPredictEdbInfoData = predictEdbInfoData
|
|
|
+ index := len(allDataList)
|
|
|
+
|
|
|
+
|
|
|
+ yearValueConfig := annualValueInversionConf.Value
|
|
|
+
|
|
|
+
|
|
|
+ currDayTime := dayList[0]
|
|
|
+ lastDayTime := dayList[len(dayList)-1]
|
|
|
+ if currDayTime.Year() != lastDayTime.Year() {
|
|
|
+ err = errors.New("年度值倒推不支持跨年预测")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if annualValueInversionConf.Type == 1 {
|
|
|
+
|
|
|
+ currYearN := 0
|
|
|
+ var currYearVal float64
|
|
|
+ for _, v := range allDataList {
|
|
|
+ currTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if currTime.Year() != currDayTime.Year() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ currYearN++
|
|
|
+ currYearVal = currYearVal + v.Value
|
|
|
+ }
|
|
|
+
|
|
|
+ var averageVal float64
|
|
|
+ switch frequency {
|
|
|
+ case "半年度":
|
|
|
+ averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(2 - currYearN))).Float64()
|
|
|
+ case "季度":
|
|
|
+ averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(4 - currYearN))).Float64()
|
|
|
+ case "月度":
|
|
|
+ averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(12 - currYearN))).Float64()
|
|
|
+ case "旬度":
|
|
|
+ averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(36 - currYearN))).Float64()
|
|
|
+ case "周度", "日度":
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ yearFirstDay := time.Date(currDayTime.Year(), 1, 1, 0, 0, 0, 0, time.Local)
|
|
|
+ subDay := utils.GetTimeSubDay(yearFirstDay, currDayTime) + 1
|
|
|
+
|
|
|
+
|
|
|
+ yearLastDay := time.Date(currDayTime.Year(), 12, 31, 0, 0, 0, 0, time.Local)
|
|
|
+ subDay2 := utils.GetTimeSubDay(yearFirstDay, yearLastDay) + 1
|
|
|
+
|
|
|
+ surplusN := decimal.NewFromInt(int64(subDay2 - subDay)).Div(decimal.NewFromInt(int64(subDay))).Mul(decimal.NewFromInt(int64(currYearN)))
|
|
|
+ averageVal, _ = (decimal.NewFromFloat(annualValueInversionConf.Value).Sub(decimal.NewFromFloat(currYearVal))).Div(surplusN).Round(4).Float64()
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ averageVal, _ = decimal.NewFromFloat(averageVal).Round(4).Float64()
|
|
|
+
|
|
|
+ for k, currentDate := range dayList {
|
|
|
+ currentDateStr := currentDate.Format(utils.FormatDate)
|
|
|
+ tmpData := &edbDataModel.EdbDataList{
|
|
|
+ EdbDataId: edbInfoId + 100000 + index + k,
|
|
|
+ EdbInfoId: edbInfoId,
|
|
|
+ DataTime: currentDateStr,
|
|
|
+ Value: averageVal,
|
|
|
+ DataTimestamp: currentDate.UnixNano() / 1e6,
|
|
|
+ }
|
|
|
+ newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
|
|
|
+ allDataList = append(allDataList, tmpData)
|
|
|
+ existMap[currentDateStr] = averageVal
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if averageVal < minValue {
|
|
|
+ minValue = averageVal
|
|
|
+ }
|
|
|
+ if averageVal > maxValue {
|
|
|
+ maxValue = averageVal
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ dateTotalMap := make(map[time.Time]float64)
|
|
|
+
|
|
|
+ yearTotalMap := make(map[int]float64)
|
|
|
+ for _, v := range allDataList {
|
|
|
+ currTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ yearVal := yearTotalMap[currTime.Year()]
|
|
|
+ yearVal = yearVal + v.Value
|
|
|
+ yearTotalMap[currTime.Year()] = yearVal
|
|
|
+ dateTotalMap[currTime] = yearVal
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ for k, currentDate := range dayList {
|
|
|
+ currYearBalance := yearValueConfig - yearTotalMap[currentDate.Year()]
|
|
|
+
|
|
|
+ lastYear := annualValueInversionConf.Year + (currentDate.Year() - currDayTime.Year())
|
|
|
+
|
|
|
+ var lastDateTime time.Time
|
|
|
+
|
|
|
+ switch frequency {
|
|
|
+ case "半年度", "季度":
|
|
|
+ lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
|
|
|
+ case "月度":
|
|
|
+ lastDateTime = time.Date(lastYear, currentDate.Month()+1, 1, 0, 0, 0, 0, currentDate.Location()).AddDate(0, 0, -1)
|
|
|
+ case "旬度":
|
|
|
+ if currentDate.Day() == 10 || currentDate.Day() == 20 {
|
|
|
+ lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
|
|
|
+ } else {
|
|
|
+ lastDateTime = time.Date(lastYear, currentDate.Month()+1, 1, 0, 0, 0, 0, currentDate.Location()).AddDate(0, 0, -1)
|
|
|
+ }
|
|
|
+ case "周度", "日度":
|
|
|
+ lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ var dateTotal float64
|
|
|
+ dateTotal, ok := dateTotalMap[lastDateTime]
|
|
|
+ if !ok {
|
|
|
+ yearFirstDayTime := time.Date(lastDateTime.Year(), 1, 1, 0, 0, 0, 0, lastDateTime.Location())
|
|
|
+ for tmpDateTime := lastDateTime.AddDate(0, 0, -1); tmpDateTime.After(yearFirstDayTime) || tmpDateTime.Equal(yearFirstDayTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
|
|
|
+ dateTotal, ok = dateTotalMap[tmpDateTime]
|
|
|
+ if ok {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ lastYearDateBalance := yearValueConfig - dateTotal
|
|
|
+
|
|
|
+
|
|
|
+ tbVal := decimal.NewFromFloat(currYearBalance).Div(decimal.NewFromFloat(lastYearDateBalance))
|
|
|
+
|
|
|
+
|
|
|
+ if lastDateVal, ok := existMap[lastDateTime.Format(utils.FormatDate)]; ok {
|
|
|
+
|
|
|
+ tmpVal, _ := decimal.NewFromFloat(lastDateVal).Mul(tbVal).Round(4).Float64()
|
|
|
+ currentDateStr := currentDate.Format(utils.FormatDate)
|
|
|
+ tmpData := &edbDataModel.EdbDataList{
|
|
|
+ EdbDataId: edbInfoId + 100000 + index + k,
|
|
|
+ EdbInfoId: edbInfoId,
|
|
|
+ DataTime: currentDateStr,
|
|
|
+ Value: tmpVal,
|
|
|
+ DataTimestamp: currentDate.UnixNano() / 1e6,
|
|
|
+ }
|
|
|
+ newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
|
|
|
+ allDataList = append(allDataList, tmpData)
|
|
|
+ existMap[currentDateStr] = tmpVal
|
|
|
+
|
|
|
+ yearVal := yearTotalMap[currentDate.Year()]
|
|
|
+ yearVal = yearVal + tmpVal
|
|
|
+ yearTotalMap[currentDate.Year()] = yearVal
|
|
|
+ dateTotalMap[currentDate] = yearVal
|
|
|
+
|
|
|
+
|
|
|
+ if tmpVal < minValue {
|
|
|
+ minValue = tmpVal
|
|
|
+ }
|
|
|
+ if tmpVal > maxValue {
|
|
|
+ maxValue = tmpVal
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func getYearListBySeasonConf(configValue string) (yearList []int, seasonConf SeasonConf, err error) {
|
|
|
+ tmpErr := json.Unmarshal([]byte(configValue), &seasonConf)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = errors.New("年份配置信息异常:" + tmpErr.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if seasonConf.YearType == 1 {
|
|
|
+ if seasonConf.NValue < 1 {
|
|
|
+ err = errors.New("连续N年不允许小于1")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ currYear := time.Now().Year()
|
|
|
+ for i := 0; i < seasonConf.NValue; i++ {
|
|
|
+ yearList = append(yearList, currYear-i-1)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ yearList = seasonConf.YearList
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+}
|