Преглед на файлове

Merge branch 'feature/eta_2.3.8' into debug

hsun преди 1 месец
родител
ревизия
527c68a513

+ 200 - 32
controllers/data_manage/ai_predict_model/index.go

@@ -204,6 +204,7 @@ func (this *AiPredictModelIndexController) Import() {
 	}
 	imports := make(map[string]*aiPredictModel.AiPredictModelImportData)
 	importsData := make(map[string]map[time.Time]*aiPredictModel.AiPredictModelData)
+	importsDailyData := make(map[string]map[time.Time]*aiPredictModel.AiPredictModelData)
 	for sheetKey, sheet := range xlFile.Sheets {
 		maxRow := sheet.MaxRow
 
@@ -262,7 +263,12 @@ func (this *AiPredictModelIndexController) Import() {
 					}
 				}
 				imports[indexName].Index.PredictDate = predictDate
-				predictVal, _ := cells[5].Float()
+
+				strVal := strings.TrimSpace(cells[5].String())
+				if strVal == "" {
+					continue
+				}
+				predictVal, _ := strconv.ParseFloat(strVal, 64)
 				imports[indexName].Index.PredictValue = predictVal
 				imports[indexName].Index.PredictFrequency = strings.TrimSpace(cells[6].String())
 				imports[indexName].Index.DirectionAccuracy = strings.TrimSpace(cells[7].String())
@@ -270,7 +276,7 @@ func (this *AiPredictModelIndexController) Import() {
 			}
 		}
 
-		// 数据页
+		// 月度数据页
 		if sheetKey == 1 {
 			// 每五列为一个指标的数据
 			colKeys := make(map[int]*ImportDataColKey) // 每一列对应的指标名称以及对应的字段序号
@@ -362,6 +368,7 @@ func (this *AiPredictModelIndexController) Import() {
 						importsData[colKeys[ck].IndexName][dataDate].DataTime = dataDate
 						importsData[colKeys[ck].IndexName][dataDate].CreateTime = time.Now()
 						importsData[colKeys[ck].IndexName][dataDate].ModifyTime = time.Now()
+						importsData[colKeys[ck].IndexName][dataDate].Source = aiPredictModel.ModelDataSourceMonthly
 					case 2, 3:
 						// 实际值和预测值, 可能为空
 						dataDate := colKeys[ck].DataDate
@@ -401,6 +408,132 @@ func (this *AiPredictModelIndexController) Import() {
 				}
 			}
 		}
+
+		// 日度数据页
+		if sheetKey == 2 {
+			// 每3列为一个指标的数据
+			colKeys := make(map[int]*ImportDataColKey) // 每一列对应的指标名称以及对应的字段序号
+			for i := 0; i < maxRow; i++ {
+				// 首行为指标名称
+				if i == 0 {
+					nameCol := 0
+					row := sheet.Row(i)
+					for ck, cell := range row.Cells {
+						nameCol += 1
+						if nameCol > 3 {
+							nameCol = 1
+						}
+						if nameCol == 1 {
+							// nameCol=1时为指标/数据行则为日期
+							indexName := strings.TrimSpace(cell.String())
+							if indexName == "" {
+								continue
+							}
+							importsDailyData[indexName] = make(map[time.Time]*aiPredictModel.AiPredictModelData)
+
+							colKeys[ck] = &ImportDataColKey{
+								ColKey:    1,
+								IndexName: indexName,
+							}
+
+							// 后面两列分别对应: 实际值|预测值, 这里直接加无须考虑是否会越界
+							colKeys[ck+1] = &ImportDataColKey{
+								ColKey:    2,
+								IndexName: indexName,
+							}
+							colKeys[ck+2] = &ImportDataColKey{
+								ColKey:    3,
+								IndexName: indexName,
+							}
+							continue
+						}
+					}
+					continue
+				}
+
+				// 第二行为标题,遇到"预测值"单元格,需要取出其中的值作为预测图例名称
+				if i == 1 {
+					row := sheet.Row(i)
+					for ck, cell := range row.Cells {
+						if colKeys[ck] == nil {
+							continue
+						}
+						if colKeys[ck].IndexName == "" {
+							continue
+						}
+						if colKeys[ck].ColKey != 3 {
+							continue
+						}
+						if imports[colKeys[ck].IndexName] != nil && imports[colKeys[ck].IndexName].Index != nil {
+							var extraConfig aiPredictModel.AiPredictModelIndexExtraConfig
+							extraConfig.DailyChart.PredictLegendName = strings.TrimSpace(cell.String())
+							b, _ := json.Marshal(extraConfig)
+							imports[colKeys[ck].IndexName].Index.ExtraConfig = string(b)
+						}
+					}
+					continue
+				}
+
+				// 剩余为数据行
+				row := sheet.Row(i)
+				for ck, cell := range row.Cells {
+					if colKeys[ck] == nil {
+						continue
+					}
+					if colKeys[ck].IndexName == "" {
+						continue
+					}
+					switch colKeys[ck].ColKey {
+					case 1:
+						// 日期列
+						strDate := strings.TrimSpace(cell.String())
+						dataDate, _ := time.Parse("2006/01/02", strDate)
+						if dataDate.IsZero() {
+							dataDate, _ = time.Parse("01-02-06", strDate)
+							if dataDate.IsZero() {
+								dataDate, _ = time.Parse("2006/1/2", strDate)
+								if dataDate.IsZero() {
+									continue
+								}
+							}
+						}
+						colKeys[ck].DataDate = dataDate
+						colKeys[ck+1].DataDate = dataDate
+						colKeys[ck+2].DataDate = dataDate
+						importRow := imports[colKeys[ck].IndexName]
+						if importRow == nil {
+							continue
+						}
+						// 新增当前日期数据
+						importsDailyData[colKeys[ck].IndexName][dataDate] = new(aiPredictModel.AiPredictModelData)
+						importsDailyData[colKeys[ck].IndexName][dataDate].DataTime = dataDate
+						importsDailyData[colKeys[ck].IndexName][dataDate].CreateTime = time.Now()
+						importsDailyData[colKeys[ck].IndexName][dataDate].ModifyTime = time.Now()
+						importsDailyData[colKeys[ck].IndexName][dataDate].Source = aiPredictModel.ModelDataSourceDaily
+					case 2, 3:
+						// 实际值和预测值, 可能为空
+						dataDate := colKeys[ck].DataDate
+						if importsDailyData[colKeys[ck].IndexName][dataDate] == nil {
+							continue
+						}
+						strVal := strings.TrimSpace(cell.String())
+						if strVal == "" {
+							continue
+						}
+						val, _ := strconv.ParseFloat(strVal, 64)
+						if colKeys[ck].ColKey == 2 {
+							importsDailyData[colKeys[ck].IndexName][dataDate].Value.Valid = true
+							importsDailyData[colKeys[ck].IndexName][dataDate].Value.Float64 = val
+						} else {
+							importsDailyData[colKeys[ck].IndexName][dataDate].PredictValue.Valid = true
+							importsDailyData[colKeys[ck].IndexName][dataDate].PredictValue.Float64 = val
+						}
+					default:
+						continue
+					}
+				}
+			}
+		}
 	}
 
 	for indexName, v := range importsData {
@@ -411,6 +544,14 @@ func (this *AiPredictModelIndexController) Import() {
 			imports[indexName].Data = append(imports[indexName].Data, dateData)
 		}
 	}
+	for indexName, v := range importsDailyData {
+		if imports[indexName] == nil {
+			continue
+		}
+		for _, dateData := range v {
+			imports[indexName].Data = append(imports[indexName].Data, dateData)
+		}
+	}
 	importIndexes := make([]*aiPredictModel.AiPredictModelImportData, 0)
 	for _, v := range imports {
 		importIndexes = append(importIndexes, v)
@@ -471,40 +612,60 @@ func (this *AiPredictModelIndexController) Detail() {
 	}
 
 	// 获取标的数据
-	indexData := make([]*aiPredictModel.AiPredictModelData, 0)
+	monthData, dailyData := make([]*aiPredictModel.AiPredictModelData, 0), make([]*aiPredictModel.AiPredictModelData, 0)
 	{
 		tableData := make([]*aiPredictModel.AiPredictModelDataItem, 0)
 		dataOb := new(aiPredictModel.AiPredictModelData)
-		dataCond := fmt.Sprintf(` AND %s = ?`, dataOb.Cols().AiPredictModelIndexId)
+		dataCond := fmt.Sprintf(` AND %s = ?`, dataOb.Cols().IndexCode)
 		dataPars := make([]interface{}, 0)
-		dataPars = append(dataPars, indexId)
+		dataPars = append(dataPars, indexItem.IndexCode)
 		list, e := dataOb.GetItemsByCondition(dataCond, dataPars, []string{}, fmt.Sprintf("%s DESC", dataOb.Cols().DataTime))
 		if e != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = fmt.Sprintf("获取标的数据失败, %v", e)
 			return
 		}
-		indexData = list
-		// tableData最多显示10条
+
+		// tableData取月度数据,最多显示10条
 		count, limit := 0, 10
 		for _, v := range list {
-			if count >= limit {
-				break
+			// 日度数据
+			if v.Source == aiPredictModel.ModelDataSourceDaily {
+				dailyData = append(dailyData, v)
+				continue
+			}
+
+			// 月度数据
+			if count < limit {
+				tableData = append(tableData, v.Format2Item())
+				count += 1
 			}
-			tableData = append(tableData, v.Format2Item())
-			count += 1
+			monthData = append(monthData, v)
 		}
 		resp.TableData = tableData
 	}
 
-	// 获取图表数据
-	chartDetail, e := services.GetAiPredictChartDetailByData(indexItem, indexData)
-	if e != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = fmt.Sprintf("获取图表数据失败, %v", e)
-		return
+	// 月度图表
+	if len(monthData) > 0 {
+		chartDetail, e := services.GetAiPredictChartDetailByData(indexItem, monthData, aiPredictModel.ModelDataSourceMonthly)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取月度图表失败, %v", e)
+			return
+		}
+		resp.ChartView = chartDetail
+	}
+
+	// 日度图表
+	if len(dailyData) > 0 {
+		dailyChartDetail, e := services.GetAiPredictChartDetailByData(indexItem, dailyData, aiPredictModel.ModelDataSourceDaily)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取日度图表失败, %v", e)
+			return
+		}
+		resp.DailyChartView = dailyChartDetail
 	}
-	resp.ChartView = chartDetail
 
 	br.Data = resp
 	br.Ret = 200
@@ -553,22 +714,34 @@ func (this *AiPredictModelIndexController) Save() {
 			br.Msg = "标的已被删除,请刷新页面"
 			return
 		}
-		br.Msg = "获取失败"
+		br.Msg = "操作失败"
 		br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
 		return
 	}
 
-	var updateCols []string
-	if req.LeftMin != "" {
-		indexItem.LeftMin = req.LeftMin
-		updateCols = append(updateCols, indexOb.Cols().LeftMin)
+	var extraConfig aiPredictModel.AiPredictModelIndexExtraConfig
+	if indexItem.ExtraConfig != "" {
+		if e = json.Unmarshal([]byte(indexItem.ExtraConfig), &extraConfig); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("标的配置解析失败, %v", e)
+			return
+		}
 	}
-	if req.LeftMax != "" {
-		indexItem.LeftMax = req.LeftMax
-		updateCols = append(updateCols, indexOb.Cols().LeftMax)
+	if req.MonthlyChart != nil {
+		extraConfig.MonthlyChart.LeftMin = req.MonthlyChart.LeftMin
+		extraConfig.MonthlyChart.LeftMax = req.MonthlyChart.LeftMax
+		extraConfig.MonthlyChart.Unit = req.MonthlyChart.Unit
 	}
+	if req.DailyChart != nil {
+		extraConfig.DailyChart.LeftMin = req.DailyChart.LeftMin
+		extraConfig.DailyChart.LeftMax = req.DailyChart.LeftMax
+		extraConfig.DailyChart.Unit = req.DailyChart.Unit
+	}
+
+	configByte, _ := json.Marshal(extraConfig)
+	indexItem.ExtraConfig = string(configByte)
 	indexItem.ModifyTime = time.Now()
-	updateCols = append(updateCols, indexOb.Cols().ModifyTime)
+	updateCols := []string{indexOb.Cols().ExtraConfig, indexOb.Cols().ModifyTime}
 	if e = indexItem.Update(updateCols); e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = fmt.Sprintf("保存标的失败, %v", e)
@@ -626,11 +799,6 @@ func (this *AiPredictModelIndexController) DashboardSave() {
 		br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
 		return
 	}
-	// 仅标的创建人有权限操作看板
-	//if sysUser.AdminId != indexItem.SysUserId {
-	//	br.Msg = "仅创建人可操作"
-	//	return
-	//}
 
 	// 获取看板
 	var isUpdate bool

+ 8 - 0
models/ai_predict_model/ai_predict_model_data.go

@@ -9,6 +9,11 @@ import (
 	"time"
 )
 
+const (
+	ModelDataSourceMonthly = 1 // 月度预测数据
+	ModelDataSourceDaily   = 2 // 日度预测数据
+)
+
 // AiPredictModelData AI预测模型标的数据
 type AiPredictModelData struct {
 	AiPredictModelDataId  int             `orm:"column(ai_predict_model_data_id);pk"`
@@ -22,6 +27,7 @@ type AiPredictModelData struct {
 	CreateTime            time.Time       `description:"创建时间"`
 	ModifyTime            time.Time       `description:"修改时间"`
 	DataTimestamp         int64           `description:"数据日期时间戳"`
+	Source                int             `description:"来源:1-月度预测(默认);2-日度预测"`
 }
 
 func (m *AiPredictModelData) TableName() string {
@@ -40,6 +46,7 @@ type AiPredictModelDataCols struct {
 	CreateTime            string
 	ModifyTime            string
 	DataTimestamp         string
+	Source                string
 }
 
 func (m *AiPredictModelData) Cols() AiPredictModelDataCols {
@@ -55,6 +62,7 @@ func (m *AiPredictModelData) Cols() AiPredictModelDataCols {
 		CreateTime:            "create_time",
 		ModifyTime:            "modify_time",
 		DataTimestamp:         "data_timestamp",
+		Source:                "source",
 	}
 }
 

+ 24 - 3
models/ai_predict_model/ai_predict_model_index.go

@@ -358,12 +358,33 @@ func (m *AiPredictModelIndex) ImportIndexAndData(createIndexes, updateIndexes []
 }
 
 type AiPredictModelDetailResp struct {
-	TableData []*AiPredictModelDataItem        `description:"表格数据"`
-	ChartView *data_manage.ChartInfoDetailResp `description:"图表信息"`
+	TableData      []*AiPredictModelDataItem        `description:"表格数据"`
+	ChartView      *data_manage.ChartInfoDetailResp `description:"月度预测数据图表"`
+	DailyChartView *data_manage.ChartInfoDetailResp `description:"日度预测数据图表"`
 }
 
 type AiPredictModelIndexSaveReq struct {
-	IndexId int    `description:"指标ID"`
+	IndexId      int                           `description:"指标ID"`
+	MonthlyChart *AiPredictModelIndexSaveChart `description:"月度图表信息"`
+	DailyChart   *AiPredictModelIndexSaveChart `description:"日度图表信息"`
+}
+
+type AiPredictModelIndexSaveChart struct {
 	LeftMin string `description:"图表左侧最小值"`
 	LeftMax string `description:"图表左侧最大值"`
+	Unit    string `description:"单位"`
+}
+
+type AiPredictModelIndexExtraConfig struct {
+	MonthlyChart struct {
+		LeftMin string `description:"图表左侧最小值"`
+		LeftMax string `description:"图表左侧最大值"`
+		Unit    string `description:"单位"`
+	}
+	DailyChart struct {
+		LeftMin           string `description:"图表左侧最小值"`
+		LeftMax           string `description:"图表左侧最大值"`
+		Unit              string `description:"单位"`
+		PredictLegendName string `description:"预测图例的名称(通常为Predicted)"`
+	}
 }

+ 75 - 8
services/ai_predict_model_index.go

@@ -1,6 +1,7 @@
 package services
 
 import (
+	"encoding/json"
 	aiPredictModel "eta/eta_api/models/ai_predict_model"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/services/data"
@@ -28,13 +29,29 @@ func ImportAiPredictModelIndexAndData(imports []*aiPredictModel.AiPredictModelIm
 		}
 	}
 
-	updateCols := []string{indexOb.Cols().ClassifyId, indexOb.Cols().ModelFramework, indexOb.Cols().PredictDate, indexOb.Cols().PredictValue, indexOb.Cols().DirectionAccuracy, indexOb.Cols().AbsoluteDeviation, indexOb.Cols().SysUserId, indexOb.Cols().SysUserRealName, indexOb.Cols().ModifyTime}
+	updateCols := []string{indexOb.Cols().ClassifyId, indexOb.Cols().ModelFramework, indexOb.Cols().PredictDate, indexOb.Cols().PredictValue, indexOb.Cols().DirectionAccuracy, indexOb.Cols().AbsoluteDeviation, indexOb.Cols().ExtraConfig, indexOb.Cols().SysUserId, indexOb.Cols().SysUserRealName, indexOb.Cols().ModifyTime}
 	updateIndexes := make([]*aiPredictModel.AiPredictModelImportData, 0)
 	createIndexes := make([]*aiPredictModel.AiPredictModelImportData, 0)
 	for _, v := range imports {
 		exist := indexNameItem[v.Index.IndexName]
 		// 编辑
 		if exist != nil {
+			// 图例信息
+			if exist.ExtraConfig != "" && v.Index.ExtraConfig != "" {
+				var oldConfig, newConfig aiPredictModel.AiPredictModelIndexExtraConfig
+				if e := json.Unmarshal([]byte(exist.ExtraConfig), &oldConfig); e != nil {
+					err = fmt.Errorf("标的原配置解析失败, Config: %s, Err: %v", exist.ExtraConfig, e)
+					return
+				}
+				if e := json.Unmarshal([]byte(v.Index.ExtraConfig), &newConfig); e != nil {
+					err = fmt.Errorf("标的新配置解析失败, Config: %s, Err: %v", v.Index.ExtraConfig, e)
+					return
+				}
+				oldConfig.DailyChart.PredictLegendName = newConfig.DailyChart.PredictLegendName
+				b, _ := json.Marshal(oldConfig)
+				v.Index.ExtraConfig = string(b)
+			}
+
 			v.Index.AiPredictModelIndexId = exist.AiPredictModelIndexId
 			v.Index.IndexCode = exist.IndexCode
 			updateIndexes = append(updateIndexes, v)
@@ -59,9 +76,43 @@ func ImportAiPredictModelIndexAndData(imports []*aiPredictModel.AiPredictModelIm
 	return
 }
 
-func GetAiPredictChartDetailByData(indexItem *aiPredictModel.AiPredictModelIndex, indexData []*aiPredictModel.AiPredictModelData) (resp *data_manage.ChartInfoDetailResp, err error) {
+func GetAiPredictChartDetailByData(indexItem *aiPredictModel.AiPredictModelIndex, indexData []*aiPredictModel.AiPredictModelData, source int) (resp *data_manage.ChartInfoDetailResp, err error) {
 	resp = new(data_manage.ChartInfoDetailResp)
 
+	// 标的配置
+	var extraConfig aiPredictModel.AiPredictModelIndexExtraConfig
+	if indexItem.ExtraConfig != "" {
+		if e := json.Unmarshal([]byte(indexItem.ExtraConfig), &extraConfig); e != nil {
+			err = fmt.Errorf("标的额外配置解析失败, Config: %s, Err: %v", indexItem.ExtraConfig, e)
+			return
+		}
+	}
+
+	// 图表信息
+	var predictLegendName, confLeftMin, confLeftMax, unit string
+	if source == aiPredictModel.ModelDataSourceDaily {
+		predictLegendName = extraConfig.DailyChart.PredictLegendName
+		if predictLegendName == "" {
+			predictLegendName = "Predicted"
+		}
+		unit = extraConfig.DailyChart.Unit
+		confLeftMin = extraConfig.DailyChart.LeftMin
+		confLeftMax = extraConfig.DailyChart.LeftMax
+	}
+	if source == aiPredictModel.ModelDataSourceMonthly {
+		predictLegendName = "预测值"
+		unit = extraConfig.MonthlyChart.Unit
+		confLeftMin = extraConfig.MonthlyChart.LeftMin
+		confLeftMax = extraConfig.MonthlyChart.LeftMax
+	}
+	// 这里简单兼容下吧,暂时就不修数据了
+	if confLeftMin == "" {
+		confLeftMin = indexItem.LeftMin
+	}
+	if confLeftMax == "" {
+		confLeftMax = indexItem.LeftMax
+	}
+
 	// 获取曲线图主题样式
 	chartView := new(data_manage.ChartInfoView)
 	chartView.ChartType = utils.CHART_SOURCE_DEFAULT
@@ -86,14 +137,20 @@ func GetAiPredictChartDetailByData(indexItem *aiPredictModel.AiPredictModelIndex
 	edbActual.EdbName = indexItem.IndexName
 	edbActual.EdbNameEn = indexItem.IndexName
 	edbActual.IsAxis = 1
-	edbPredict.EdbName = "预测值"
-	edbPredict.EdbNameEn = "预测值"
+	edbActual.Unit = unit
+	edbActual.UnitEn = unit
+
+	edbPredict.EdbName = predictLegendName
+	edbPredict.EdbNameEn = predictLegendName
 	edbPredict.IsAxis = 1
+	edbPredict.Unit = unit
+	edbPredict.UnitEn = unit
 	actualData, predictData := make([]*data_manage.EdbDataList, 0), make([]*data_manage.EdbDataList, 0)
 
 	var startDate, endDate time.Time
 	var actualValues, predictValues []float64
 	var actualNewest, predictNewest bool
+	var actualLatestTimestamp int64 // 实际值最后一天的时间戳,作为日度图表的分割线
 	for k, v := range indexData {
 		// 如果实际值和预测值都是null那么该日期无效直接忽略
 		if !v.Value.Valid && !v.PredictValue.Valid {
@@ -125,6 +182,7 @@ func GetAiPredictChartDetailByData(indexItem *aiPredictModel.AiPredictModelIndex
 			if !actualNewest {
 				edbActual.LatestDate = v.DataTime.Format(utils.FormatDate)
 				edbActual.LatestValue = v.Value.Float64
+				actualLatestTimestamp = v.DataTime.UnixNano() / 1e6
 				actualNewest = true
 			}
 			actualData = append(actualData, &data_manage.EdbDataList{
@@ -160,8 +218,8 @@ func GetAiPredictChartDetailByData(indexItem *aiPredictModel.AiPredictModelIndex
 	edbList = append(edbList, edbActual, edbPredict)
 
 	// 上下限
-	if indexItem.LeftMin != "" {
-		chartView.LeftMin = indexItem.LeftMin
+	if confLeftMin != "" {
+		chartView.LeftMin = confLeftMin
 	} else {
 		leftMin := actualMin
 		if leftMin > predictMin {
@@ -169,8 +227,8 @@ func GetAiPredictChartDetailByData(indexItem *aiPredictModel.AiPredictModelIndex
 		}
 		chartView.LeftMin = fmt.Sprint(leftMin)
 	}
-	if indexItem.LeftMax != "" {
-		chartView.LeftMax = indexItem.LeftMax
+	if confLeftMax != "" {
+		chartView.LeftMax = confLeftMax
 	} else {
 		leftMax := actualMax
 		if leftMax < predictMax {
@@ -182,6 +240,15 @@ func GetAiPredictChartDetailByData(indexItem *aiPredictModel.AiPredictModelIndex
 	chartView.StartDate = startDate.Format(utils.FormatDate)
 	chartView.EndDate = endDate.Format(utils.FormatDate)
 
+	// 日度图表的分割线日期
+	if source == aiPredictModel.ModelDataSourceDaily {
+		var dataResp struct {
+			ActualLatestTimestamp int64
+		}
+		dataResp.ActualLatestTimestamp = actualLatestTimestamp
+		resp.DataResp = dataResp
+	}
+
 	resp.ChartInfo = chartView
 	resp.EdbInfoList = edbList
 	return