浏览代码

feat:预测指标绘图

Roc 2 年之前
父节点
当前提交
6e3f659da7

+ 164 - 157
controllers/chart.go

@@ -145,157 +145,164 @@ func (this *ChartController) ChartInfoDetail() {
 		return
 	}
 
-	edbList := make([]*models.ChartEdbInfoMapping, 0)
-	for _, v := range mappingList {
-		//minData, maxData, err := data_manage.GetEdbDataListMinAndMax(v.Source, v.EdbInfoId, startDate, endDate)
-		item := new(models.ChartEdbInfoMapping)
-		item.EdbInfoId = v.EdbInfoId
-		item.SourceName = v.SourceName
-		item.Source = v.Source
-		item.EdbCode = v.EdbCode
-		item.EdbName = v.EdbName
-		item.EdbNameEn = v.EdbNameEn
-		item.Frequency = v.Frequency
-		item.FrequencyEn = GetFrequencyEn(v.Frequency)
-		//无就是不展示
-		if v.Unit != `无` {
-			item.Unit = v.Unit
-		}
-		item.UnitEn = v.UnitEn
-		item.StartDate = v.StartDate
-		item.EndDate = v.EndDate
-		//item.ModifyTime = v.ModifyTime
-		//item.ChartEdbMappingId = v.ChartEdbMappingId
-		item.ChartInfoId = v.ChartInfoId
-		item.MaxData = v.MaxData
-		item.MinData = v.MinData
-		item.IsOrder = v.IsOrder
-		item.IsAxis = v.IsAxis
-		item.EdbInfoType = v.EdbInfoType
-		item.EdbType = v.EdbType
-		item.LeadValue = v.LeadValue
-		item.LeadUnit = v.LeadUnit
-		item.ChartStyle = v.ChartStyle
-		item.ChartColor = v.ChartColor
-		item.ChartWidth = v.ChartWidth
-
-		var startDateReal string
-		var diffSeconds int64
-		if chartType == 2 { //季节性图表
-			startDateReal = startDate
-		} else {
-			if v.EdbInfoType == 0 && v.LeadUnit != "" && v.LeadValue > 0 { //领先指标
-				var startTimeRealTemp time.Time
-				startDateParse, _ := time.Parse(utils.FormatDate, startDate)
-				switch v.LeadUnit {
-				case "天":
-					startTimeRealTemp = startDateParse.AddDate(0, 0, -v.LeadValue)
-				case "月":
-					startTimeRealTemp = startDateParse.AddDate(0, -v.LeadValue, 0)
-				case "季":
-					startTimeRealTemp = startDateParse.AddDate(0, -3*v.LeadValue, 0)
-				case "周":
-					startTimeRealTemp = startDateParse.AddDate(0, 0, -7*v.LeadValue)
-				case "年":
-					startTimeRealTemp = startDateParse.AddDate(-v.LeadValue, 0, 0)
-				}
-				if startTimeRealTemp.Before(startDateParse) {
-					startDateReal = startTimeRealTemp.Format(utils.FormatDate)
-					diffSeconds = (int64(startTimeRealTemp.UnixNano()) - int64(startDateParse.UnixNano())) / 1e6
-				} else {
-					startDateReal = startDate
-					diffSeconds = 0
-				}
-			} else {
-				startDateReal = startDate
-			}
-		}
-		calendarPreYear := 0
-		if calendar == "农历" {
-			newStartDateReal, err := time.Parse(utils.FormatDate, startDateReal)
-			if err != nil {
-				fmt.Println("time.Parse:" + err.Error())
-				utils.FileLog.Info("startDateReal parse err:" + err.Error())
-			}
-			calendarPreYear = newStartDateReal.Year() - 1
-			newStartDateReal = newStartDateReal.AddDate(-1, 0, 0)
-			startDateReal = newStartDateReal.Format(utils.FormatDate)
-		}
-		dataList := make([]*models.EdbDataList, 0)
-		fmt.Println("chart:", v.Source, v.EdbInfoId, startDateReal, endDate)
-		dataList, err = models.GetEdbDataList(v.Source, v.EdbInfoId, startDateReal, endDate)
-		if err != nil {
-			br.Msg = "获取失败"
-			br.Msg = "获取失败,Err:" + err.Error()
-			return
-		}
-		if diffSeconds != 0 && v.EdbInfoType == 0 {
-			dataListLen := len(dataList)
-			for i := 0; i < dataListLen; i++ {
-				dataList[i].DataTimestamp = dataList[i].DataTimestamp - diffSeconds
-			}
-		}
-
-		if chartType == 2 {
-			if calendar == "农历" {
-				result, err := models.AddCalculateQuarterV5(dataList)
-				if err != nil {
-					br.Msg = "获取失败"
-					br.Msg = "获取农历数据失败,Err:" + err.Error()
-					return
-				}
-				if result.List[0].Year != calendarPreYear {
-					itemList := make([]*models.EdbDataList, 0)
-					items := new(models.EdbDataItems)
-					//items.Year = calendarPreYear
-					items.Items = itemList
-
-					newResult := new(models.EdbDataResult)
-					newResult.List = append(newResult.List, items)
-					newResult.List = append(newResult.List, result.List...)
-					item.DataList = newResult
-				} else {
-					item.DataList = result
-				}
-			} else {
-				currentYear := time.Now().Year()
-				quarterDataList := make([]*models.QuarterData, 0)
-				quarterMap := make(map[int][]*models.EdbDataList)
-				var quarterArr []int
-				for _, v := range dataList {
-					//v.DataTime
-					itemDate, err := time.Parse(utils.FormatDate, v.DataTime)
-					if err != nil {
-						br.Msg = "获取失败"
-						br.Msg = "季度指标日期转换,Err:" + err.Error() + ";DataTime:" + v.DataTime
-						return
-					}
-					year := itemDate.Year()
-					newItemDate := itemDate.AddDate(currentYear-year, 0, 0)
-					timestamp := newItemDate.UnixNano() / 1e6
-					v.DataTimestamp = timestamp
-					if findVal, ok := quarterMap[year]; !ok {
-						quarterArr = append(quarterArr, year)
-						findVal = append(findVal, v)
-						quarterMap[year] = findVal
-					} else {
-						findVal = append(findVal, v)
-						quarterMap[year] = findVal
-					}
-				}
-				for _, v := range quarterArr {
-					itemList := quarterMap[v]
-					quarterItem := new(models.QuarterData)
-					quarterItem.Year = v
-					quarterItem.DataList = itemList
-					quarterDataList = append(quarterDataList, quarterItem)
-				}
-				item.DataList = quarterDataList
-			}
-		} else {
-			item.DataList = dataList
-		}
-		edbList = append(edbList, item)
+	//edbList := make([]*models.ChartEdbInfoMapping, 0)
+	//for _, v := range mappingList {
+	//	//minData, maxData, err := data_manage.GetEdbDataListMinAndMax(v.Source, v.EdbInfoId, startDate, endDate)
+	//	item := new(models.ChartEdbInfoMapping)
+	//	item.EdbInfoId = v.EdbInfoId
+	//	item.SourceName = v.SourceName
+	//	item.Source = v.Source
+	//	item.EdbCode = v.EdbCode
+	//	item.EdbName = v.EdbName
+	//	item.EdbNameEn = v.EdbNameEn
+	//	item.Frequency = v.Frequency
+	//	item.FrequencyEn = GetFrequencyEn(v.Frequency)
+	//	//无就是不展示
+	//	if v.Unit != `无` {
+	//		item.Unit = v.Unit
+	//	}
+	//	item.UnitEn = v.UnitEn
+	//	item.StartDate = v.StartDate
+	//	item.EndDate = v.EndDate
+	//	//item.ModifyTime = v.ModifyTime
+	//	//item.ChartEdbMappingId = v.ChartEdbMappingId
+	//	item.ChartInfoId = v.ChartInfoId
+	//	item.MaxData = v.MaxData
+	//	item.MinData = v.MinData
+	//	item.IsOrder = v.IsOrder
+	//	item.IsAxis = v.IsAxis
+	//	item.EdbInfoType = v.EdbInfoType
+	//	item.EdbType = v.EdbType
+	//	item.LeadValue = v.LeadValue
+	//	item.LeadUnit = v.LeadUnit
+	//	item.ChartStyle = v.ChartStyle
+	//	item.ChartColor = v.ChartColor
+	//	item.ChartWidth = v.ChartWidth
+	//
+	//	var startDateReal string
+	//	var diffSeconds int64
+	//	if chartType == 2 { //季节性图表
+	//		startDateReal = startDate
+	//	} else {
+	//		if v.EdbInfoType == 0 && v.LeadUnit != "" && v.LeadValue > 0 { //领先指标
+	//			var startTimeRealTemp time.Time
+	//			startDateParse, _ := time.Parse(utils.FormatDate, startDate)
+	//			switch v.LeadUnit {
+	//			case "天":
+	//				startTimeRealTemp = startDateParse.AddDate(0, 0, -v.LeadValue)
+	//			case "月":
+	//				startTimeRealTemp = startDateParse.AddDate(0, -v.LeadValue, 0)
+	//			case "季":
+	//				startTimeRealTemp = startDateParse.AddDate(0, -3*v.LeadValue, 0)
+	//			case "周":
+	//				startTimeRealTemp = startDateParse.AddDate(0, 0, -7*v.LeadValue)
+	//			case "年":
+	//				startTimeRealTemp = startDateParse.AddDate(-v.LeadValue, 0, 0)
+	//			}
+	//			if startTimeRealTemp.Before(startDateParse) {
+	//				startDateReal = startTimeRealTemp.Format(utils.FormatDate)
+	//				diffSeconds = (int64(startTimeRealTemp.UnixNano()) - int64(startDateParse.UnixNano())) / 1e6
+	//			} else {
+	//				startDateReal = startDate
+	//				diffSeconds = 0
+	//			}
+	//		} else {
+	//			startDateReal = startDate
+	//		}
+	//	}
+	//	calendarPreYear := 0
+	//	if calendar == "农历" {
+	//		newStartDateReal, err := time.Parse(utils.FormatDate, startDateReal)
+	//		if err != nil {
+	//			fmt.Println("time.Parse:" + err.Error())
+	//			utils.FileLog.Info("startDateReal parse err:" + err.Error())
+	//		}
+	//		calendarPreYear = newStartDateReal.Year() - 1
+	//		newStartDateReal = newStartDateReal.AddDate(-1, 0, 0)
+	//		startDateReal = newStartDateReal.Format(utils.FormatDate)
+	//	}
+	//	dataList := make([]*models.EdbDataList, 0)
+	//	fmt.Println("chart:", v.Source, v.EdbInfoId, startDateReal, endDate)
+	//	dataList, err = models.GetEdbDataList(v.Source, v.EdbInfoId, startDateReal, endDate)
+	//	if err != nil {
+	//		br.Msg = "获取失败"
+	//		br.Msg = "获取失败,Err:" + err.Error()
+	//		return
+	//	}
+	//	if diffSeconds != 0 && v.EdbInfoType == 0 {
+	//		dataListLen := len(dataList)
+	//		for i := 0; i < dataListLen; i++ {
+	//			dataList[i].DataTimestamp = dataList[i].DataTimestamp - diffSeconds
+	//		}
+	//	}
+	//
+	//	if chartType == 2 {
+	//		if calendar == "农历" {
+	//			result, err := models.AddCalculateQuarterV5(dataList)
+	//			if err != nil {
+	//				br.Msg = "获取失败"
+	//				br.Msg = "获取农历数据失败,Err:" + err.Error()
+	//				return
+	//			}
+	//			if result.List[0].Year != calendarPreYear {
+	//				itemList := make([]*models.EdbDataList, 0)
+	//				items := new(models.EdbDataItems)
+	//				//items.Year = calendarPreYear
+	//				items.Items = itemList
+	//
+	//				newResult := new(models.EdbDataResult)
+	//				newResult.List = append(newResult.List, items)
+	//				newResult.List = append(newResult.List, result.List...)
+	//				item.DataList = newResult
+	//			} else {
+	//				item.DataList = result
+	//			}
+	//		} else {
+	//			currentYear := time.Now().Year()
+	//			quarterDataList := make([]*models.QuarterData, 0)
+	//			quarterMap := make(map[int][]*models.EdbDataList)
+	//			var quarterArr []int
+	//			for _, v := range dataList {
+	//				//v.DataTime
+	//				itemDate, err := time.Parse(utils.FormatDate, v.DataTime)
+	//				if err != nil {
+	//					br.Msg = "获取失败"
+	//					br.Msg = "季度指标日期转换,Err:" + err.Error() + ";DataTime:" + v.DataTime
+	//					return
+	//				}
+	//				year := itemDate.Year()
+	//				newItemDate := itemDate.AddDate(currentYear-year, 0, 0)
+	//				timestamp := newItemDate.UnixNano() / 1e6
+	//				v.DataTimestamp = timestamp
+	//				if findVal, ok := quarterMap[year]; !ok {
+	//					quarterArr = append(quarterArr, year)
+	//					findVal = append(findVal, v)
+	//					quarterMap[year] = findVal
+	//				} else {
+	//					findVal = append(findVal, v)
+	//					quarterMap[year] = findVal
+	//				}
+	//			}
+	//			for _, v := range quarterArr {
+	//				itemList := quarterMap[v]
+	//				quarterItem := new(models.QuarterData)
+	//				quarterItem.Year = v
+	//				quarterItem.DataList = itemList
+	//				quarterDataList = append(quarterDataList, quarterItem)
+	//			}
+	//			item.DataList = quarterDataList
+	//		}
+	//	} else {
+	//		item.DataList = dataList
+	//	}
+	//	edbList = append(edbList, item)
+	//}
+	// 获取图表中的指标数据
+	edbList, err := data.GetChartEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+		return
 	}
 
 	resp.ChartInfo = chartInfo
@@ -380,22 +387,22 @@ func (this *ChartController) ChartInfoRefresh() {
 func GetFrequencyEn(frequency string) (frequencyEn string) {
 	switch frequency {
 	case "日度":
-		frequencyEn="day"
+		frequencyEn = "day"
 		return
 	case "周度":
-		frequencyEn="week"
+		frequencyEn = "week"
 		return
 	case "旬度":
-		frequencyEn="ten days"
+		frequencyEn = "ten days"
 		return
 	case "月度":
-		frequencyEn="month"
+		frequencyEn = "month"
 		return
 	case "季度":
-		frequencyEn="quarter"
+		frequencyEn = "quarter"
 		return
 	case "年度":
-		frequencyEn="year"
+		frequencyEn = "year"
 		return
 	}
 	return

+ 21 - 6
models/chart.go

@@ -53,7 +53,7 @@ type ChartEdbInfoMapping struct {
 	UnitEn      string `description:"英文单位"`
 	StartDate   string `description:"起始日期"`
 	EndDate     string `description:"终止日期"`
-	//ModifyTime        string  `description:"指标最后更新时间"`
+	ModifyTime  string `description:"指标最后更新时间"`
 	//ChartEdbMappingId int     `description:"图表指标id"`
 	ChartInfoId int     `description:"图表id"`
 	MaxData     float64 `description:"上限"`
@@ -68,11 +68,21 @@ type ChartEdbInfoMapping struct {
 	ChartColor  string  `description:"颜色"`
 	ChartWidth  float64 `description:"线条大小"`
 	DataList    interface{}
+
+	EdbInfoCategoryType int     `description:"0:普通指标,1:预测指标"`
+	LeadUnitEn          string  `description:"领先英文单位"`
+	PredictChartColor   string  `description:"预测数据的颜色"`
+	ChartType           int     `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图"`
+	LatestDate          string  `description:"数据最新日期"`
+	LatestValue         float64 `description:"数据最新值"`
+	UniqueCode          string  `description:"指标唯一编码"`
+	MinValue            float64 `json:"-" description:"最小值"`
+	MaxValue            float64 `json:"-" description:"最大值"`
 }
 
 func GetChartEdbMappingList(chartInfoId int) (list []*ChartEdbInfoMapping, err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := ` SELECT a.*,b.source_name,b.source,b.edb_code,b.edb_name,b.edb_name_en,b.frequency,b.unit,b.unit_en,b.start_date,b.end_date,b.modify_time,b.edb_type
+	sql := ` SELECT a.*,b.source_name,b.source,b.edb_code,b.edb_name,b.edb_name_en,b.frequency,b.unit,b.unit_en,b.start_date,b.end_date,b.modify_time,b.edb_type,b.latest_date,b.latest_value,b.unique_code,b.edb_info_type AS edb_info_category_type
              FROM chart_edb_mapping AS a
 			 INNER JOIN edb_info AS b ON a.edb_info_id=b.edb_info_id
 			 WHERE chart_info_id=? 
@@ -118,8 +128,10 @@ func GetEdbDataList(source, endInfoId int, startDate, endDate string) (list []*E
 }
 
 type EdbDataItems struct {
-	Items []*EdbDataList
-	Year  int
+	Items                []*EdbDataList
+	Year                 int
+	BetweenDay           int   `json:"-" description:"公历与农历之间相差的天数"`
+	CuttingDataTimestamp int64 `description:"切割的时间戳"`
 }
 
 type EdbDataResult struct {
@@ -127,8 +139,9 @@ type EdbDataResult struct {
 }
 
 type QuarterData struct {
-	Year     int
-	DataList []*EdbDataList
+	Year                 int
+	DataList             []*EdbDataList
+	CuttingDataTimestamp int64 `description:"切割的时间戳"`
 }
 
 type ChartInfoDetailResp struct {
@@ -221,6 +234,7 @@ func AddCalculateQuarterV5(dataList []*EdbDataList) (result *EdbDataResult, err
 			day := currentYearCjglDate.Sub(preYearCjglDate).Hours() / float64(24)
 
 			items := new(EdbDataItems)
+			items.BetweenDay = int(day) //公历日期换算成农历,需要减除的天数
 			items.Year = preYear
 			for _, v := range dataList {
 				dateTime, err := time.Parse(utils.FormatDate, v.DataTime)
@@ -266,6 +280,7 @@ func AddCalculateQuarterV5(dataList []*EdbDataList) (result *EdbDataResult, err
 			fmt.Println("day:", day, nextYearCjglDate, preYearCjglDate)
 
 			items := new(EdbDataItems)
+			items.BetweenDay = int(day) //公历日期换算成农历,需要减除的天数
 			items.Year = preYear - 1
 			fmt.Println("preYear:", preYear, "ky:", ky, "yearArrLen:", len(yearArr))
 			if ky+1 < len(yearArr) {

+ 10 - 0
models/data_manage/edb_info.go

@@ -8,6 +8,7 @@ import (
 
 type EdbInfo struct {
 	EdbInfoId        int    `orm:"column(edb_info_id);pk"`
+	EdbInfoType      int    `description:"指标类型,0:普通指标,1:预测指标"`
 	SourceName       string `description:"来源名称"`
 	Source           int    `description:"来源id"`
 	EdbCode          string `description:"指标编码"`
@@ -28,6 +29,8 @@ type EdbInfo struct {
 	CalculateFormula string  `description:"计算公式"`
 	EdbType          int     `description:"指标类型:1:基础指标,2:计算指标"`
 	Sort             int     `description:"排序字段"`
+	LatestDate       string  `description:"数据最新日期"`
+	LatestValue      float64 `description:"数据最新值"`
 	MoveType         int     `description:"移动方式:1:领先(默认),2:滞后"`
 	MoveFrequency    string  `description:"移动频度"`
 }
@@ -62,3 +65,10 @@ func ModifyEdbInfoMaxAndMinInfo(edbInfoId int, item *EdbInfoMaxAndMinInfo) (err
 	_, err = o.Raw(sql, item.MinDate, item.MaxDate, item.MinValue, item.MaxValue, item.MaxDate, item.LatestValue, edbInfoId).Exec()
 	return
 }
+
+func GetEdbInfoById(edbInfoId int) (item *EdbInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM edb_info WHERE edb_info_id=? `
+	err = o.Raw(sql, edbInfoId).QueryRow(&item)
+	return
+}

+ 110 - 0
models/data_manage/predict_edb_conf.go

@@ -0,0 +1,110 @@
+package data_manage
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_chart_lib/services/alarm_msg"
+	"time"
+)
+
+type PredictEdbConf struct {
+	PredictEdbInfoId int       `orm:"column(predict_edb_info_id);pk" description:"预测指标id"`
+	SourceEdbInfoId  int       `description:"来源指标id"`
+	RuleType         int       `description:"预测规则,1:最新,2:固定值"`
+	FixedValue       float64   `description:"固定值"`
+	ModifyTime       time.Time `description:"修改时间"`
+	CreateTime       time.Time `description:"添加时间"`
+}
+
+// GetPredictEdbConfById 根据预测指标id获取预测指标配置信息
+func GetPredictEdbConfById(edbInfoId int) (item *PredictEdbConf, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM predict_edb_conf WHERE predict_edb_info_id=? `
+	err = o.Raw(sql, edbInfoId).QueryRow(&item)
+	return
+}
+
+// GetPredictEdbConfBySourceEdbInfoId 根据来源指标id获取配置
+func GetPredictEdbConfBySourceEdbInfoId(sourceEdbInfoId int) (item *PredictEdbConf, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM predict_edb_conf WHERE source_edb_info_id=? `
+	err = o.Raw(sql, sourceEdbInfoId).QueryRow(&item)
+	return
+}
+
+// GetPredictEdbConfCount 根据来源指标id获取被引用的次数
+func GetPredictEdbConfCount(sourceEdbInfoId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count FROM predict_edb_conf WHERE source_edb_info_id=? `
+	err = o.Raw(sql, sourceEdbInfoId).QueryRow(&count)
+	return
+}
+
+// AddPredictEdbConf 添加预测指标规则
+func AddPredictEdbConf(item *PredictEdbConf) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	lastId, err = o.Insert(item)
+	return
+}
+
+// AddPredictEdb 添加预测指标
+func AddPredictEdb(item *EdbInfo, predictEdbConf *PredictEdbConf) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tmpErr := tx.Rollback()
+			if tmpErr != nil {
+				go alarm_msg.SendAlarmMsg("AddPredictEdb 事务回滚失败,Err:"+tmpErr.Error(), 3)
+			}
+		} else {
+			err = tx.Commit()
+		}
+	}()
+	// 新增预测指标
+	edbInfoId, err := o.Insert(item)
+	if err != nil {
+		return
+	}
+	item.EdbInfoId = int(edbInfoId)
+
+	// 新增预测指标配置
+	predictEdbConf.PredictEdbInfoId = item.EdbInfoId
+	_, err = o.Insert(predictEdbConf)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// EditPredictEdb 修改预测指标
+func EditPredictEdb(edbInfo *EdbInfo, predictEdbConf *PredictEdbConf, updateEdbInfoCol, updatePredictEdbConfCol []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tmpErr := tx.Rollback()
+			if tmpErr != nil {
+				go alarm_msg.SendAlarmMsg("AddPredictEdb 事务回滚失败,Err:"+tmpErr.Error(), 3)
+			}
+		} else {
+			err = tx.Commit()
+		}
+	}()
+	// 修改预测指标
+	_, err = o.Update(edbInfo, updateEdbInfoCol...)
+	if err != nil {
+		return
+	}
+	// 修改预测指标配置
+	_, err = o.Update(predictEdbConf, updatePredictEdbConfCol...)
+	if err != nil {
+		return
+	}
+	return
+}

+ 3 - 2
models/db.go

@@ -38,7 +38,8 @@ func init() {
 	//注册对象
 	orm.RegisterModel(
 		new(data_manage.EdbInfo),
-		new(ShareChartRefreshLog), //分享图表刷新日志表
-		new(ExcelInfo),            //excel表格
+		new(ShareChartRefreshLog),       //分享图表刷新日志表
+		new(ExcelInfo),                  //excel表格
+		new(data_manage.PredictEdbConf), //预测指标配置
 	)
 }

+ 281 - 0
services/data/chart_info.go

@@ -3,6 +3,7 @@ package data
 import (
 	"errors"
 	"fmt"
+	"hongze/hongze_chart_lib/models"
 	"hongze/hongze_chart_lib/models/data_manage"
 	"hongze/hongze_chart_lib/utils"
 	"sort"
@@ -136,3 +137,283 @@ func ChartInfoRefreshV1(chartInfoId int) (err error) {
 
 	return err
 }
+
+// GetFrequencyEn 获取频度的英文版
+func GetFrequencyEn(frequency string) (frequencyEn string) {
+	switch frequency {
+	case "日度":
+		frequencyEn = "day"
+		return
+	case "周度":
+		frequencyEn = "week"
+		return
+	case "旬度":
+		frequencyEn = "ten days"
+		return
+	case "月度":
+		frequencyEn = "month"
+		return
+	case "季度":
+		frequencyEn = "quarter"
+		return
+	case "年度":
+		frequencyEn = "year"
+		return
+	}
+	return
+}
+
+func GetLeadUnitEn(unit string) (unitEn string) {
+	switch unit {
+	case "天":
+		unitEn = "day"
+		return
+	case "周":
+		unitEn = "week"
+		return
+	case "月":
+		unitEn = "month"
+		return
+	case "季":
+		unitEn = "quarter"
+		return
+	case "年":
+		unitEn = "year"
+		return
+	}
+	return
+}
+
+// GetChartEdbData 获取图表的指标数据
+func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*models.ChartEdbInfoMapping) (edbList []*models.ChartEdbInfoMapping, err error) {
+	edbList = make([]*models.ChartEdbInfoMapping, 0)
+
+	for _, v := range mappingList {
+		//fmt.Println("v:", v.EdbInfoId)
+		item := new(models.ChartEdbInfoMapping)
+		item.EdbInfoId = v.EdbInfoId
+		item.SourceName = v.SourceName
+		item.Source = v.Source
+		item.EdbCode = v.EdbCode
+		item.EdbName = v.EdbName
+		item.EdbNameEn = v.EdbNameEn
+		item.Frequency = v.Frequency
+		item.FrequencyEn = GetFrequencyEn(v.Frequency)
+		if v.Unit != `无` {
+			item.Unit = v.Unit
+		}
+		item.UnitEn = v.UnitEn
+		item.StartDate = v.StartDate
+		item.EndDate = v.EndDate
+		item.ModifyTime = v.ModifyTime
+		item.EdbInfoCategoryType = v.EdbInfoCategoryType
+		item.PredictChartColor = v.PredictChartColor
+
+		if chartInfoId <= 0 {
+			item.IsAxis = 1
+			item.LeadValue = 0
+			item.LeadUnit = ""
+			//item.ChartEdbMappingId = 0
+			item.ChartInfoId = 0
+			item.IsOrder = false
+			item.EdbInfoType = 1
+			item.ChartStyle = ""
+			item.ChartColor = ""
+			item.ChartWidth = 0
+			item.MaxData = v.MaxValue
+			item.MinData = v.MinValue
+		} else {
+			item.IsAxis = v.IsAxis
+			item.EdbInfoType = v.EdbInfoType
+			item.LeadValue = v.LeadValue
+			item.LeadUnit = v.LeadUnit
+			item.LeadUnitEn = GetLeadUnitEn(v.LeadUnit)
+			//item.ChartEdbMappingId = v.ChartEdbMappingId
+			item.ChartInfoId = v.ChartInfoId
+			item.ChartStyle = v.ChartStyle
+			item.ChartColor = v.ChartColor
+			item.ChartWidth = v.ChartWidth
+			item.IsOrder = v.IsOrder
+			item.MaxData = v.MaxData
+			item.MinData = v.MinData
+		}
+		item.LatestValue = v.LatestValue
+		item.LatestDate = v.LatestDate
+		item.UniqueCode = v.UniqueCode
+
+		var startDateReal string
+		var diffSeconds int64
+		if chartType == 2 { //季节性图
+			startDateReal = startDate
+		} else {
+			if v.EdbInfoType == 0 && v.LeadUnit != "" && v.LeadValue > 0 { //领先指标
+				var startTimeRealTemp time.Time
+				startDateParse, _ := time.Parse(utils.FormatDate, startDate)
+				switch v.LeadUnit {
+				case "天":
+					startTimeRealTemp = startDateParse.AddDate(0, 0, -v.LeadValue)
+				case "月":
+					startTimeRealTemp = startDateParse.AddDate(0, -v.LeadValue, 0)
+				case "季":
+					startTimeRealTemp = startDateParse.AddDate(0, -3*v.LeadValue, 0)
+				case "周":
+					startTimeRealTemp = startDateParse.AddDate(0, 0, -7*v.LeadValue)
+				case "年":
+					startTimeRealTemp = startDateParse.AddDate(-v.LeadValue, 0, 0)
+				}
+				if startTimeRealTemp.Before(startDateParse) {
+					startDateReal = startTimeRealTemp.Format(utils.FormatDate)
+					diffSeconds = (int64(startTimeRealTemp.UnixNano()) - int64(startDateParse.UnixNano())) / 1e6
+				} else {
+					startDateReal = startDate
+					diffSeconds = 0
+				}
+			} else {
+				startDateReal = startDate
+			}
+		}
+		//fmt.Println("line 1011 chart:", v.Source, v.EdbInfoId, startDateReal, endDate)
+		calendarPreYear := 0
+		if calendar == "农历" {
+			newStartDateReal, err := time.Parse(utils.FormatDate, startDateReal)
+			if err != nil {
+				fmt.Println("time.Parse:" + err.Error())
+			}
+			calendarPreYear = newStartDateReal.Year() - 1
+			newStartDateReal = newStartDateReal.AddDate(-1, 0, 0)
+			startDateReal = newStartDateReal.Format(utils.FormatDate)
+		}
+		dataList := make([]*models.EdbDataList, 0)
+		//fmt.Println("chart:", v.Source, v.EdbInfoId, startDateReal, endDate)
+
+		switch v.EdbInfoCategoryType {
+		case 0:
+			dataList, err = models.GetEdbDataList(v.Source, v.EdbInfoId, startDateReal, endDate)
+		case 1:
+			dataList, _, _, err, _ = GetPredictDataListByPredictEdbInfoId(v.EdbInfoId, startDateReal, endDate, false)
+		default:
+			err = errors.New(fmt.Sprint("获取失败,指标类型异常", v.EdbInfoCategoryType))
+		}
+		if err != nil {
+			return
+		}
+		if diffSeconds != 0 && v.EdbInfoType == 0 {
+			dataListLen := len(dataList)
+			for i := 0; i < dataListLen; i++ {
+				dataList[i].DataTimestamp = dataList[i].DataTimestamp - diffSeconds
+			}
+		}
+
+		if chartType == 2 {
+			latestDateStr := v.LatestDate //实际数据的截止日期
+			latestDate, tmpErr := time.Parse(utils.FormatDate, v.LatestDate)
+			if tmpErr != nil {
+				err = errors.New(fmt.Sprint("获取最后实际数据的日期失败,Err:" + tmpErr.Error() + ";LatestDate:" + v.LatestDate))
+				return
+			}
+			latestDateYear := latestDate.Year() //实际数据截止年份
+
+			if calendar == "农历" {
+				if len(dataList) <= 0 {
+					result := new(models.EdbDataResult)
+					item.DataList = result
+				} else {
+					result, tmpErr := models.AddCalculateQuarterV5(dataList)
+					if tmpErr != nil {
+						err = errors.New("获取农历数据失败,Err:" + tmpErr.Error())
+						return
+					}
+
+					// 处理季节图的截止日期
+					for k, edbDataItems := range result.List {
+						var cuttingDataTimestamp int64
+
+						// 切割的日期时间字符串
+						cuttingDataTimeStr := latestDate.AddDate(0, 0, edbDataItems.BetweenDay).Format(utils.FormatDate)
+						//如果等于最后的实际日期,那么遍历找到该日期对应的时间戳,并将其赋值为 切割时间戳
+						if edbDataItems.Year >= latestDateYear {
+							for _, tmpData := range edbDataItems.Items {
+								if tmpData.DataTime == cuttingDataTimeStr {
+									cuttingDataTimestamp = tmpData.DataTimestamp
+									break
+								}
+							}
+						}
+						edbDataItems.CuttingDataTimestamp = cuttingDataTimestamp
+						result.List[k] = edbDataItems
+					}
+
+					if result.List[0].Year != calendarPreYear {
+						itemList := make([]*models.EdbDataList, 0)
+						items := new(models.EdbDataItems)
+						//items.Year = calendarPreYear
+						items.Items = itemList
+
+						newResult := new(models.EdbDataResult)
+						newResult.List = append(newResult.List, items)
+						newResult.List = append(newResult.List, result.List...)
+						item.DataList = newResult
+					} else {
+						item.DataList = result
+					}
+				}
+
+			} else {
+				currentYear := time.Now().Year()
+
+				quarterDataList := make([]*models.QuarterData, 0)
+				quarterMap := make(map[int][]*models.EdbDataList)
+				var quarterArr []int
+
+				for _, v := range dataList {
+					itemDate, tmpErr := time.Parse(utils.FormatDate, v.DataTime)
+					if tmpErr != nil {
+						err = errors.New("季度指标日期转换,Err:" + tmpErr.Error() + ";DataTime:" + v.DataTime)
+						return
+					}
+					year := itemDate.Year()
+					newItemDate := itemDate.AddDate(currentYear-year, 0, 0)
+					timestamp := newItemDate.UnixNano() / 1e6
+					v.DataTimestamp = timestamp
+					if findVal, ok := quarterMap[year]; !ok {
+						quarterArr = append(quarterArr, year)
+						findVal = append(findVal, v)
+						quarterMap[year] = findVal
+					} else {
+						findVal = append(findVal, v)
+						quarterMap[year] = findVal
+					}
+				}
+				for _, v := range quarterArr {
+					itemList := quarterMap[v]
+					quarterItem := new(models.QuarterData)
+					quarterItem.Year = v
+					quarterItem.DataList = itemList
+
+					//如果等于最后的实际日期,那么将切割时间戳记录
+					if v == latestDateYear {
+						var cuttingDataTimestamp int64
+						for _, tmpData := range itemList {
+							if tmpData.DataTime == latestDateStr {
+								cuttingDataTimestamp = tmpData.DataTimestamp
+								break
+							}
+						}
+						quarterItem.CuttingDataTimestamp = cuttingDataTimestamp
+					} else if v > latestDateYear {
+						//如果大于最后的实际日期,那么第一个点就是切割的时间戳
+						if len(itemList) > 0 {
+							quarterItem.CuttingDataTimestamp = itemList[0].DataTimestamp - 100
+						}
+					}
+					quarterDataList = append(quarterDataList, quarterItem)
+				}
+				item.DataList = quarterDataList
+			}
+		} else {
+			item.DataList = dataList
+		}
+		edbList = append(edbList, item)
+	}
+	return
+}

+ 158 - 0
services/data/predict_edb_info.go

@@ -0,0 +1,158 @@
+package data
+
+import (
+	"errors"
+	"hongze/hongze_chart_lib/models"
+	"hongze/hongze_chart_lib/models/data_manage"
+	"hongze/hongze_chart_lib/utils"
+	"time"
+)
+
+// GetChartPredictEdbInfoDataList 获取图表的预测指标的未来数据
+func GetChartPredictEdbInfoDataList(predictEdbConf data_manage.PredictEdbConf, filtrateStartDateStr, latestDateStr string, lastDataValue float64, endDateStr, frequency string) (predictEdbInfoData []*models.EdbDataList, 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)
+		}
+	}
+
+	dataValue := lastDataValue
+	if predictEdbConf.RuleType == 2 {
+		dataValue = predictEdbConf.FixedValue
+	}
+	//获取后面的预测数据
+	dayList := getPredictEdbDayList(startDate, endDate, frequency)
+	predictEdbInfoData = make([]*models.EdbDataList, 0)
+	for k, v := range dayList {
+		predictEdbInfoData = append(predictEdbInfoData, &models.EdbDataList{
+			EdbDataId:     predictEdbConf.PredictEdbInfoId + 10000000000 + k,
+			EdbInfoId:     predictEdbConf.PredictEdbInfoId,
+			DataTime:      v.Format(utils.FormatDate),
+			Value:         dataValue,
+			DataTimestamp: (v.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适
+		})
+	}
+	return
+}
+
+// GetPredictEdbDayList 获取预测指标日期列表
+func getPredictEdbDayList(startDate, endDate time.Time, frequency string) (dayList []time.Time) {
+	//if !utils.InArrayByStr([]string{"日度", "周度", "月度"}, frequency)
+	switch frequency {
+	case "日度":
+		for currDate := startDate.AddDate(0, 0, 1); currDate.Before(endDate) || currDate.Equal(endDate); currDate = currDate.AddDate(0, 0, 1) {
+			//周六、日排除
+			if currDate.Weekday() == time.Sunday || currDate.Weekday() == time.Saturday {
+				continue
+			}
+			dayList = append(dayList, currDate)
+		}
+	case "周度":
+		//nextDate := startDate.AddDate(0, 0, 7)
+		for currDate := startDate.AddDate(0, 0, 7); currDate.Before(endDate) || currDate.Equal(endDate); currDate = currDate.AddDate(0, 0, 7) {
+			dayList = append(dayList, currDate)
+		}
+	case "月度":
+		for currDate := startDate; currDate.Before(endDate) || currDate.Equal(endDate); {
+			currDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 2, -1)
+			if !currDate.After(endDate) {
+				dayList = append(dayList, currDate)
+			}
+		}
+	}
+	return
+}
+
+// GetPredictDataListByPredictEdbInfoId 根据预测指标id获取预测指标的数据
+func GetPredictDataListByPredictEdbInfoId(edbInfoId int, startDate, endDate string, isTimeBetween bool) (dataList []*models.EdbDataList, sourceEdbInfoItem *data_manage.EdbInfo, predictEdbConf *data_manage.PredictEdbConf, err error, errMsg string) {
+	edbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
+	if err != nil {
+		errMsg = `获取预测指标信息失败`
+		return
+	}
+	return GetPredictDataListByPredictEdbInfo(edbInfo, startDate, endDate, isTimeBetween)
+}
+
+// GetPredictDataListByPredictEdbInfo 根据预测指标信息获取预测指标的数据
+func GetPredictDataListByPredictEdbInfo(edbInfo *data_manage.EdbInfo, startDate, endDate string, isTimeBetween bool) (dataList []*models.EdbDataList, sourceEdbInfoItem *data_manage.EdbInfo, predictEdbConf *data_manage.PredictEdbConf, err error, errMsg string) {
+	// 查找该预测指标配置
+	predictEdbConf, err = data_manage.GetPredictEdbConfById(edbInfo.EdbInfoId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取预测指标配置信息失败"
+		return
+	}
+	if predictEdbConf == nil {
+		errMsg = "获取预测指标配置信息失败"
+		err = errors.New(errMsg)
+		return
+	}
+
+	// 来源指标
+	sourceEdbInfoItem, err = data_manage.GetEdbInfoById(predictEdbConf.SourceEdbInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "找不到来源指标信息"
+			err = errors.New(errMsg)
+		}
+		return
+	}
+
+	//获取指标数据(实际已生成)
+	dataList, err = models.GetEdbDataList(sourceEdbInfoItem.Source, sourceEdbInfoItem.EdbInfoId, startDate, endDate)
+	if err != nil {
+		return
+	}
+
+	// 获取预测指标未来的数据
+	predictDataList := make([]*models.EdbDataList, 0)
+
+	endDateStr := edbInfo.EndDate //预测指标的结束日期
+
+	if isTimeBetween { //如果是时间区间,那么
+		reqEndDateTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+		endDateTime, _ := time.ParseInLocation(utils.FormatDate, edbInfo.EndDate, time.Local)
+		// 如果选择的时间区间结束日期 晚于 当天,那么预测数据截止到当天
+		if reqEndDateTime.Before(endDateTime) {
+			endDateStr = endDate
+		}
+	}
+	predictDataList, err = GetChartPredictEdbInfoDataList(*predictEdbConf, startDate, sourceEdbInfoItem.LatestDate, sourceEdbInfoItem.LatestValue, endDateStr, edbInfo.Frequency)
+	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 > tmpValue.Value {
+			edbInfo.MinValue = tmpValue.Value
+		}
+	}
+	return
+}

+ 3 - 2
utils/constants.go

@@ -61,6 +61,7 @@ const (
 	DATA_SOURCE_PYTHON                          //python代码->27
 	DATA_SOURCE_PB_FINANCE                      //彭博财务数据->28
 	DATA_SOURCE_GOOGLE_TRAVEL                   //谷歌出行->29
+	DATA_SOURCE_PREDICT                         //普通预测指标->30
 )
 
 //数据刷新频率
@@ -87,8 +88,8 @@ const (
 )
 
 const (
-	HZ_CHART_LIB_DETAIL = "HZ_CHART_LIB_DETAIL_"	//图表
-	HZ_TABLE_LIB_DETAIL = "HZ_TABLE_LIB_DETAIL_"	//表格
+	HZ_CHART_LIB_DETAIL             = "HZ_CHART_LIB_DETAIL_" //图表
+	HZ_TABLE_LIB_DETAIL             = "HZ_TABLE_LIB_DETAIL_" //表格
 	HZ_CHART_LIB_EXCEL_TABLE_DETAIL = "HZ_CHART_LIB_EXCEL_TABLE_DETAIL"
 )