Ver Fonte

预测超季节性

xiexiaoyuan há 2 anos atrás
pai
commit
78d2be3bff

+ 21 - 0
controllers/base_from_predict_calculate.go

@@ -747,6 +747,9 @@ func (this *PredictCalculateController) CalculateBatchSave() {
 			return
 			return
 		}
 		}
 		edbInfo, latestDateStr, latestValue, err = models.SavePredictCalculateZjpj(&req, fromEdbInfo, secondEdbInfo, edbCode, uniqueCode, adminId, adminName)
 		edbInfo, latestDateStr, latestValue, err = models.SavePredictCalculateZjpj(&req, fromEdbInfo, secondEdbInfo, edbCode, uniqueCode, adminId, adminName)
+	} else if req.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_CJJX {
+		sourName = "超季节性"
+		edbInfo, latestDateStr, latestValue, err = models.SavePredictCalculateCjjx(&req, fromEdbInfo, edbCode, uniqueCode, adminId, adminName, formulaInt)
 	} else {
 	} else {
 		br.Msg = "无效计算方式"
 		br.Msg = "无效计算方式"
 		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
 		br.ErrMsg = "无效计算方式,source:" + strconv.Itoa(req.Source)
@@ -1054,6 +1057,24 @@ func (this *PredictCalculateController) Refresh() {
 			errMsg = "RefreshAllPredictCalculateZjpj Err:" + err.Error()
 			errMsg = "RefreshAllPredictCalculateZjpj Err:" + err.Error()
 			break
 			break
 		}
 		}
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_CJJX: //超季节性
+		calculateCjjx, err := models.GetEdbInfoCalculateMappingDetail(edbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoCalculateNszydpjjsDetail Err:" + err.Error()
+			break
+		}
+		fromEdbInfo, err := models.GetEdbInfoById(calculateCjjx.FromEdbInfoId)
+		if err != nil {
+			errMsg = "GetEdbInfoById Err:" + err.Error()
+			break
+		}
+		formulaInt, _ := strconv.Atoi(edbInfo.CalculateFormula)
+		startDate = `` //只要填写日期,就会出现问题,还是把日期给去掉吧
+		latestDateStr, latestValue, err = models.RefreshAllPredictCalculateCjjx(edbInfoId, edbInfo.Source, fromEdbInfo, calculateCjjx.EdbCode, startDate, "", edbInfo.Calendar, formulaInt)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			errMsg = "RefreshAllPredictCalculateCjjx Err:" + err.Error()
+			break
+		}
 	default:
 	default:
 		br.Msg = "来源异常,请联系相关开发!"
 		br.Msg = "来源异常,请联系相关开发!"
 		br.ErrMsg = "来源异常,请联系相关开发"
 		br.ErrMsg = "来源异常,请联系相关开发"

+ 361 - 0
models/predict_edb_data_calculate_cjjx.go

@@ -0,0 +1,361 @@
+package models
+
+import (
+	"errors"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_edb_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// SavePredictCalculateCjjx 超季节性
+func SavePredictCalculateCjjx(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo *EdbInfo, edbCode, uniqueCode string, sysUserId int, sysUserRealName string, formulaInt int) (edbInfo *EdbInfo, latestDateStr string, latestValue float64, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("SavePredictCalculateCjjx,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	fmt.Println("req.EdbInfoId:", req.EdbInfoId)
+	if req.EdbInfoId <= 0 {
+		edbInfo = new(EdbInfo)
+		edbInfo.EdbInfoType = 1
+		edbInfo.Source = utils.DATA_SOURCE_PREDICT_CALCULATE_CJJX
+		edbInfo.SourceName = "预测超季节性"
+		edbInfo.EdbCode = edbCode
+		edbInfo.EdbName = req.EdbName
+		edbInfo.EdbNameSource = req.EdbName
+		edbInfo.Frequency = req.Frequency
+		edbInfo.Unit = req.Unit
+		edbInfo.ClassifyId = req.ClassifyId
+		edbInfo.SysUserId = sysUserId
+		edbInfo.SysUserRealName = sysUserRealName
+		edbInfo.CreateTime = time.Now()
+		edbInfo.ModifyTime = time.Now()
+		edbInfo.UniqueCode = uniqueCode
+		edbInfo.CalculateFormula = req.Formula
+		edbInfo.Calendar = req.Calendar
+		edbInfo.EdbType = 2
+		newEdbInfoId, tmpErr := to.Insert(edbInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		edbInfo.EdbInfoId = int(newEdbInfoId)
+
+		//关联关系
+		{
+			calculateMappingItem := new(EdbInfoCalculateMapping)
+			calculateMappingItem.CreateTime = time.Now()
+			calculateMappingItem.ModifyTime = time.Now()
+			calculateMappingItem.Sort = 1
+			calculateMappingItem.EdbCode = edbCode
+			calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+			calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+			calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+			calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+			calculateMappingItem.FromSource = fromEdbInfo.Source
+			calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+			calculateMappingItem.FromTag = ""
+			calculateMappingItem.Source = edbInfo.Source
+			calculateMappingItem.SourceName = edbInfo.SourceName
+			_, err = to.Insert(calculateMappingItem)
+			if err != nil {
+				return
+			}
+		}
+	} else {
+		edbInfo, err = GetEdbInfoById(req.EdbInfoId)
+		if err != nil {
+			return
+		}
+		oldCalculateFormula := edbInfo.CalculateFormula //原先的n值
+		oldCalendar := edbInfo.Calendar                 //原先的公历、农历
+		edbInfo, err = GetEdbInfoById(req.EdbInfoId)
+		if err != nil {
+			return
+		}
+
+		//修改指标信息
+		edbInfo.EdbName = req.EdbName
+		edbInfo.EdbNameSource = req.EdbName
+		edbInfo.Frequency = req.Frequency
+		edbInfo.Unit = req.Unit
+		edbInfo.ClassifyId = req.ClassifyId
+		edbInfo.CalculateFormula = req.Formula
+		edbInfo.Calendar = req.Calendar
+		edbInfo.ModifyTime = time.Now()
+		_, err = to.Update(edbInfo, "EdbName", "EdbNameSource", "Frequency", "Unit", "ClassifyId", "CalculateFormula", "Calendar", "ModifyTime")
+		if err != nil {
+			return
+		}
+
+		//判断计算指标是否被更换
+		var existCondition string
+		var existPars []interface{}
+		existCondition += " AND edb_info_id=? AND from_edb_info_id=? "
+		existPars = append(existPars, edbInfo.EdbInfoId, req.FromEdbInfoId)
+		var count int
+		count, err = GetEdbInfoCalculateCountByCondition(existCondition, existPars)
+		if err != nil {
+			err = errors.New("判断指标是否改变失败,Err:" + err.Error())
+			return
+		}
+		if count > 0 && oldCalculateFormula == req.Formula && oldCalendar == req.Calendar { // 指标未被替换,同时N值未修改,同时公历/农历未变更,无需重新计算
+			return
+		}
+
+		// 指标被替换,或者N值未修改,那么需要重新计算数据
+		//基础指标被替换了,需要删除原先的 计算指标关联的,基础指标的关联关系
+		if count <= 0 {
+			// 需要删除原先的 计算指标关联的,基础指标的关联关系
+			sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id = ? `
+			_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+			if err != nil {
+				return
+			}
+
+			// 添加新的关联关系
+			{
+				calculateMappingItem := &EdbInfoCalculateMapping{
+					EdbInfoCalculateMappingId: 0,
+					EdbInfoId:                 edbInfo.EdbInfoId,
+					Source:                    utils.DATA_SOURCE_PREDICT_CALCULATE_CJJX,
+					SourceName:                "预测超季节性",
+					EdbCode:                   edbInfo.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
+				}
+			}
+		}
+
+		//清空原有数据
+		tableName := GetEdbDataTableName(edbInfo.Source)
+		sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? `, tableName)
+		_, err = to.Raw(sql, edbInfo.EdbInfoId).Exec()
+		if err != nil {
+			return
+		}
+
+	}
+
+	//计算数据
+	latestDateStr, latestValue, err = refreshAllPredictCalculateCjjx(to, edbInfo.EdbInfoId, edbInfo.Source, fromEdbInfo, edbInfo.EdbCode, "", "", edbInfo.Calendar, formulaInt)
+
+	return
+}
+
+// RefreshAllPredictCalculateCjjx 刷新全部超季节性数据
+func RefreshAllPredictCalculateCjjx(edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, startDate, endDate, calendar string, formulaInt int) (latestDateStr string, latestValue float64, err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RefreshAllPredictCalculateCjjx,Err:" + err.Error())
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 重新计算
+	latestDateStr, latestValue, err = refreshAllPredictCalculateCjjx(to, edbInfoId, source, fromEdbInfo, edbCode, startDate, endDate, calendar, formulaInt)
+
+	return
+}
+
+// refreshAllPredictCalculateCjjx 刷新全部超季节性数据
+func refreshAllPredictCalculateCjjx(to orm.TxOrmer, edbInfoId, source int, fromEdbInfo *EdbInfo, edbCode, startDate, endDate, calendar string, formulaInt int) (latestDateStr string, latestValue float64, err error) {
+	edbInfoIdStr := strconv.Itoa(edbInfoId)
+	// 获取关联指标数据
+	dataList, err := GetPredictEdbDataListAllByStartDate(fromEdbInfo, 0, "")
+	if err != nil {
+		return
+	}
+	latestDateStr = fromEdbInfo.LatestDate
+	var dateArr []string
+	dataMap := make(map[string]*EdbInfoSearchData)
+	for _, v := range dataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+	}
+	//获取指标所有数据
+	existDataList := make([]*EdbData, 0)
+	dataTableName := GetEdbDataTableName(source)
+	sql := `SELECT * FROM %s WHERE edb_info_id=? `
+	sql = fmt.Sprintf(sql, dataTableName)
+	_, err = to.Raw(sql, edbInfoId).QueryRows(&existDataList)
+	if err != nil {
+		return
+	}
+	existDataMap := make(map[string]string)
+	removeDataTimeMap := make(map[string]int) //需要移除的日期数据
+	for _, v := range existDataList {
+		existDataMap[edbCode+v.DataTime] = v.Value
+		removeDataTimeMap[v.DataTime] = 1
+	}
+
+	addSql := ` INSERT INTO edb_data_predict_calculate_cjjx(edb_info_id,edb_code,data_time,value,create_time,modify_time,data_timestamp) values `
+	var isAdd bool
+	//日度/周度/季度/月度
+	isCompatibility := false //是否向上下兼容35天
+	if utils.InArrayByStr([]string{"日度", "周度", "季度", "月度"}, fromEdbInfo.Frequency) {
+		isCompatibility = true
+	}
+
+	// 每个年份的日期数据需要平移的天数
+	moveDayMap := make(map[int]int, 0) // 每个年份的春节公历
+	var lastDataDay time.Time
+	if len(dataList) > 0 {
+		lastDataDay, _ = time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
+	}
+	for _, av := range dateArr {
+		currentItem := dataMap[av]
+		if currentItem != nil {
+			pastValueList := make([]float64, 0) // 过去几期的数据
+			//当前日期
+			currentDate, tmpErr := time.Parse(utils.FormatDate, av)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			pastValueList = append(pastValueList, currentItem.Value)
+
+			for i := 1; i < formulaInt; i++ {
+				//前几年当天公历的日期
+				hisoryPreDate := currentDate.AddDate(-i, 0, 0)
+				moveDay := 0
+				if calendar == "农历" {
+					if tmpMoveDay, ok := moveDayMap[hisoryPreDate.Year()]; !ok {
+						moveDay, err = getMoveDay(lastDataDay, hisoryPreDate)
+						if err != nil {
+							return
+						}
+					} else {
+						moveDay = tmpMoveDay
+					}
+
+					// 移动天数到对应农历 的 公历 日期
+					hisoryPreDate = hisoryPreDate.AddDate(0, 0, moveDay)
+				}
+
+				hisoryPreDateStr := hisoryPreDate.Format(utils.FormatDate)
+				if findItem, ok := dataMap[hisoryPreDateStr]; ok { //上一年同期找到
+					pastValueList = append(pastValueList, findItem.Value)
+				} else if isCompatibility { // 如果需要兼容上下35天
+					nextDateDay := hisoryPreDate
+					preDateDay := hisoryPreDate
+					for i := 0; i < 35; i++ {
+						nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+							pastValueList = append(pastValueList, findItem.Value)
+							break
+						} else {
+							preDateDayStr := preDateDay.Format(utils.FormatDate)
+							if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+								pastValueList = append(pastValueList, findItem.Value)
+								break
+							}
+						}
+						nextDateDay = nextDateDay.AddDate(0, 0, 1)
+						preDateDay = preDateDay.AddDate(0, 0, -1)
+					}
+				}
+				if av == "2022-11-28" {
+					fmt.Println(moveDay)
+				}
+			}
+			if av == "2022-11-28" {
+				fmt.Println(pastValueList)
+			}
+
+			if len(pastValueList) == formulaInt {
+				delete(removeDataTimeMap, av) //将待删除的日期给移除
+
+				val := CjjxSub(currentItem.Value, pastValueList)
+				if fromEdbInfo.LatestDate == av {
+					latestValueDecimal, tmpErr := decimal.NewFromString(val)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					latestValue, _ = latestValueDecimal.Truncate(4).Float64() //保留4位小数
+				}
+				if existVal, ok := existDataMap[edbCode+av]; !ok {
+					timestamp := currentDate.UnixNano() / 1e6
+					timestampStr := fmt.Sprintf("%d", timestamp)
+					addSql += GetAddSql(edbInfoIdStr, edbCode, av, timestampStr, val)
+					isAdd = true
+				} else {
+					var existValDecimal decimal.Decimal
+					existValDecimal, err = decimal.NewFromString(existVal)
+					if err != nil {
+						return
+					}
+					existStr := existValDecimal.String()
+					if existStr != val {
+						sql = ` UPDATE %s SET value=?,modify_time=NOW() WHERE edb_info_id=? AND data_time=? `
+						sql = fmt.Sprintf(sql, dataTableName)
+						_, err = to.Raw(sql, val, edbInfoId, av).Exec()
+						if err != nil {
+							return
+						}
+					}
+				}
+			}
+			existDataMap[edbCode+av] = av
+		}
+	}
+
+	//删除已经不存在的超季节性指标数据(由于该指标当日的数据删除了)
+	{
+		removeDateList := make([]string, 0)
+		for dateTime := range removeDataTimeMap {
+			removeDateList = append(removeDateList, dateTime)
+		}
+		removeNum := len(removeDateList)
+		if removeNum > 0 {
+			//如果拼接指标变更了,那么需要删除所有的指标数据
+			tableName := GetEdbDataTableName(source)
+			sql := fmt.Sprintf(` DELETE FROM %s WHERE edb_info_id = ? and data_time in (`+utils.GetOrmInReplace(removeNum)+`) `, tableName)
+			_, err = to.Raw(sql, edbInfoId, removeDateList).Exec()
+			if err != nil {
+				err = fmt.Errorf("删除不存在的超季节性指标数据失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+
+	if isAdd {
+		addSql = strings.TrimRight(addSql, ",")
+		_, err = to.Raw(addSql).Exec()
+		if err != nil {
+			return
+		}
+	}
+	return
+}