Parcourir la source

Merge branch 'eta_2.4.2_area_graph_1216@guomengyuan' of eta_server/eta_api into master

chenhan il y a 1 mois
Parent
commit
3487cd53de

+ 79 - 0
controllers/data_manage/chart_info.go

@@ -2,6 +2,7 @@ package data_manage
 
 import (
 	"encoding/json"
+	"errors"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
@@ -10,6 +11,7 @@ import (
 	"eta/eta_api/services"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/data"
+	"eta/eta_api/services/data/area_graph"
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/services/data/excel"
 	"eta/eta_api/services/eta_forum"
@@ -1374,6 +1376,8 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 		extraConfigStr = chartInfo.BarConfig
 	} else if chartInfo != nil && chartInfo.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
 		extraConfigStr = req.ExtraConfig
+	} else if chartInfo != nil && chartInfo.ChartType == utils.CHART_TYPE_AREA {
+		extraConfigStr = req.ExtraConfig
 	}
 
 	// 获取图表中的指标数据
@@ -1401,6 +1405,16 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 		chartInfo.WarnMsg = `图表引用指标异常,异常指标:` + strings.Join(warnEdbList, ",")
 	}
 
+	// 面积图 面积堆积 数据处理
+	if req.ChartType == utils.CHART_TYPE_AREA {
+		err, errMsg = fillAreaGraphData(extraConfigStr, edbList)
+		if err != nil {
+			br.Msg = "面积图处理失败"
+			br.ErrMsg = errMsg
+			return
+		}
+	}
+
 	//图表操作权限
 	chartInfo.IsEdit = data.CheckOpChartPermission(sysUser, chartInfo.SysUserId, true)
 	//判断是否需要展示英文标识
@@ -1493,6 +1507,60 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 	br.Data = resp
 }
 
+func fillAreaGraphData(extraConfigStr string, edbDataList []*data_manage.ChartEdbInfoMapping) (err error, errMsg string) {
+
+	var tmpConfig data_manage.AreaExtraConf
+	if extraConfigStr != `` {
+		err = json.Unmarshal([]byte(extraConfigStr), &tmpConfig)
+		if err != nil {
+			errMsg = "面积图配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+		if tmpConfig.StandardEdbInfoId <= 0 {
+			utils.FileLog.Info("面积图未开启面积堆积")
+			return
+		}
+	}
+	if tmpConfig.IsHeap == 1 {
+		standardIndexMap := make(map[string]*data_manage.EdbDataList)
+		var startDate, endDate string
+		for _, v := range edbDataList {
+			// 判断是否为基准指标
+			if v.EdbInfoId == tmpConfig.StandardEdbInfoId {
+				if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+					startDate = dataList[0].DataTime
+					endDate = dataList[len(dataList)-1].DataTime
+					for _, dataObject := range dataList {
+						standardIndexMap[dataObject.DataTime] = dataObject
+					}
+				}
+				break
+			}
+		}
+		strategy, err := area_graph.CreateStrategy(tmpConfig.NullDealWay)
+		if err != nil {
+			return err, "创建空值处理器失败"
+		}
+		err = strategy.Deal(tmpConfig, edbDataList, standardIndexMap, startDate, endDate)
+		if err != nil {
+			return err, err.Error()
+		}
+
+		// 时间戳处理
+		for _, mapping := range edbDataList {
+			if dataList, ok := mapping.DataList.([]*data_manage.EdbDataList); ok {
+				for _, dataInfo := range dataList {
+					toFormatTime := utils.StringToFormatTime(dataInfo.DataTime, utils.FormatDate)
+					dataInfo.DataTimestamp = toFormatTime.UnixMilli()
+				}
+			}
+		}
+	}
+
+	return nil, ""
+}
+
 // ChartInfoDetailV2
 // @Title 获取图表详情
 // @Description 获取图表详情接口
@@ -2825,6 +2893,17 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 		chartInfo.ChartClassify = append(chartInfo.ChartClassify, chartClassifyParent)
 	}
 	chartInfo.ChartClassify = append(chartInfo.ChartClassify, chartViewClassify)
+
+	// 面积图 面积堆积 数据处理
+	if chartType == utils.CHART_TYPE_AREA {
+		err, errMsg = fillAreaGraphData(extraConfigStr, edbList)
+		if err != nil {
+			msg = "获取失败"
+			errMsg = "获取面积图数据失败,Err:" + err.Error()
+			return
+		}
+	}
+
 	resp.EdbInfoList = edbList
 	//判断是否需要展示英文标识
 	chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, edbList, chartInfo.Source, chartInfo.ChartType)

+ 3 - 0
controllers/data_manage/chart_theme.go

@@ -207,6 +207,9 @@ func (c *ChartThemeController) GetThemePreviewData() {
 		chartInfo.LeftMax = "4000"
 		extraConfigStr = `{"DateConfList":[],"IsHeap":0,"XDataList":[{"Name":"内销"},{"Name":"出口"},{"Name":"销量"},{"Name":"产量"}],"UnitList":{"LeftName":"万台","RightName":"%","RightTwoName":""},"BaseChartSeriesName":"增量","SortType":0,"SeriesList":[{"ChartSeriesId":0,"SeriesName":"增量","ChartStyle":"column","ChartColor":"rgba(0, 0, 255, 1)","ChartWidth":1,"IsPoint":0,"IsNumber":0,"IsAxis":1,"EdbInfoList":[{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":19,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":20,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":21,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":22,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}}]},{"ChartSeriesId":0,"SeriesName":"增速","ChartStyle":"spline","ChartColor":"rgba(255, 0, 200, 1)","ChartWidth":1,"IsPoint":0,"IsNumber":0,"IsAxis":0,"EdbInfoList":[{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":23,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":24,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":25,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":26,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}}]}]}`
 		chartInfo.ChartName = "图表标题"
+	case utils.CHART_TYPE_AREA: // 曲线图
+		edbInfoIdList = []int{25, 26}
+		chartInfo.ChartName = "面积图"
 	default:
 		br.Msg = "暂不支持该类型"
 		br.IsSendEmail = false

+ 1 - 0
models/data_manage/chart_edb_mapping.go

@@ -28,6 +28,7 @@ type ChartEdbMapping struct {
 	ChartColor        string    `description:"颜色"`
 	PredictChartColor string    `description:"预测数据的颜色"`
 	ChartWidth        float64   `description:"线条大小"`
+	ChartScale        float64   `description:"参考刻度线"`
 	Source            int       `description:"1:ETA图库;2:商品价格曲线"`
 	EdbAliasName      string    `description:"中文别名"`
 	IsConvert         int       `description:"是否数据转换 0不转 1转"`

+ 10 - 0
models/data_manage/chart_info.go

@@ -63,6 +63,14 @@ type ChartInfo struct {
 	DateTypeNum       int    `description:"date_type=25(N月前)时的N值,其他N值可复用此字段"`
 }
 
+// AreaExtraConf 面积图配置
+type AreaExtraConf struct {
+	IsHeap            int `description:"是否堆积 1-堆积 2-不堆积"`
+	HeapWay           int `description:"堆积方式 1-普通 2-百分比"`
+	StandardEdbInfoId int `description:"基准指标id"`
+	NullDealWay       int `description:"空值处理方式,1-插值填充 2-前值填充 3-后值填充 4-等于0 5-删除日期"`
+}
+
 type ChartInfoMore struct {
 	ChartInfo
 	IsEnChart     bool   `description:"是否展示英文标识"`
@@ -197,6 +205,7 @@ type ChartSaveItem struct {
 	ChartColor        string  `description:"颜色"`
 	PredictChartColor string  `description:"预测数据的颜色"`
 	ChartWidth        float64 `description:"线条大小"`
+	ChartScale        float64 `description:"参考刻度线"`
 	Source            int     `description:"1:ETA图库;2:商品价格曲线"`
 	EdbAliasName      string  `description:"中文别名"`
 	IsConvert         int     `description:"是否数据转换 0不转 1转"`
@@ -717,6 +726,7 @@ type ChartEdbInfoMapping struct {
 	ChartColor          string  `description:"颜色"`
 	PredictChartColor   string  `description:"预测数据的颜色"`
 	ChartWidth          float64 `description:"线条大小"`
+	ChartScale          float64 `description:"参考刻度线"`
 	ChartType           int     `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图,8:商品价格曲线图,9:相关性图"`
 	LatestDate          string  `description:"数据最新日期"`
 	LatestValue         float64 `description:"数据最新值"`

+ 592 - 0
services/data/area_graph/processor_business_logic.go

@@ -0,0 +1,592 @@
+package area_graph
+
+import (
+	"errors"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/utils"
+	"github.com/shopspring/decimal"
+	"math"
+	"sort"
+	"time"
+)
+
+type InterpolateStrategy struct{}
+
+// Deal 空值填充:插值法填充
+func (i *InterpolateStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
+	for _, v := range edbDataList {
+		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
+			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+
+				// 存放补充数据
+				var replenishDataList []*data_manage.EdbDataList
+
+				// 处理从 startDate 到第一个数据的日期补充
+				if len(dataList) > 0 {
+					firstData := dataList[0]
+					// 将 startDate 到第一个数据日期之间的自然日填充补充数据,值为 0
+					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)
+
+					// 计算两个日期之间的天数差
+					if !startDataTime.Equal(firstDataTime) {
+						for startDataTime.Before(firstDataTime) {
+							// 补充数据
+							nextDay := startDataTime.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: startDataTime.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 startDataTime 到下一个日期
+							startDataTime = startDataTime.AddDate(0, 0, 1)
+						}
+					}
+				}
+
+				// 插值法补充数据
+				var startEdbInfoData *data_manage.EdbDataList
+				for index := 0; index < len(dataList); index++ {
+					// 获取当前数据和下一个数据
+					currentIndexData := dataList[index]
+					//afterIndexData := dataList[index+1]
+
+					if startEdbInfoData == nil {
+						startEdbInfoData = currentIndexData
+						continue
+					}
+
+					// 获取两条数据之间相差的天数
+					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startEdbInfoData.DataTime, time.Local)
+					currDataTime, _ := time.ParseInLocation(utils.FormatDate, currentIndexData.DataTime, time.Local)
+					betweenHour := int(currDataTime.Sub(startDataTime).Hours())
+					betweenDay := betweenHour / 24
+
+					// 如果相差一天,那么过滤
+					if betweenDay <= 1 {
+						startEdbInfoData = currentIndexData
+						continue
+					}
+
+					// 生成线性方程式
+					var a, b float64
+					{
+						coordinateData := make([]utils.Coordinate, 0)
+						tmpCoordinate1 := utils.Coordinate{
+							X: 1,
+							Y: startEdbInfoData.Value,
+						}
+						coordinateData = append(coordinateData, tmpCoordinate1)
+						tmpCoordinate2 := utils.Coordinate{
+							X: float64(betweenDay) + 1,
+							Y: currentIndexData.Value,
+						}
+						coordinateData = append(coordinateData, tmpCoordinate2)
+
+						a, b = utils.GetLinearResult(coordinateData)
+						if math.IsNaN(a) || math.IsNaN(b) {
+							err = errors.New("线性方程公式生成失败")
+							return
+						}
+					}
+
+					// 插值补充数据
+					for i := 1; i < betweenDay; i++ {
+						tmpDataTime := startDataTime.AddDate(0, 0, i)
+						aDecimal := decimal.NewFromFloat(a)
+						xDecimal := decimal.NewFromInt(int64(i) + 1)
+						bDecimal := decimal.NewFromFloat(b)
+
+						val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
+						nextDay := tmpDataTime.Format(utils.FormatDate)
+
+						replenishIndexData := data_manage.EdbDataList{
+							EdbDataId:     currentIndexData.EdbDataId,
+							DataTime:      nextDay,
+							DataTimestamp: tmpDataTime.UnixMilli(),
+							Value:         val,
+						}
+
+						// 将补充数据加入补充数据列表
+						replenishDataList = append(replenishDataList, &replenishIndexData)
+					}
+					startEdbInfoData = currentIndexData
+				}
+
+				// 处理从最后一个数据到 endDate 的日期补充
+				if len(dataList) > 0 {
+					lastData := dataList[len(dataList)-1]
+					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据,值为 0
+					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
+					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+
+					// 如果 lastDataTime 不等于 endDate,进行补充
+					if !lastDataTime.Equal(endDataTime) {
+						// 补充数据直到 endDate
+						for lastDataTime.Before(endDataTime) {
+							// 补充数据
+							addDate := lastDataTime.AddDate(0, 0, 1)
+							nextDay := addDate.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: addDate.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 lastDataTime 到下一个日期
+							lastDataTime = addDate
+						}
+					}
+				}
+
+				dataList = append(dataList, replenishDataList...)
+
+				// 根据基准指标筛选出符合数据
+				var resultDataList []*data_manage.EdbDataList
+				for _, dataObject := range dataList {
+					if _, ok := standardIndexMap[dataObject.DataTime]; ok {
+						// 存在才保留
+						resultDataList = append(resultDataList, dataObject)
+					}
+				}
+
+				// 排序
+				sort.Slice(resultDataList, func(i, j int) bool {
+					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
+				})
+
+				v.DataList = resultDataList
+			}
+		}
+	}
+	return nil
+}
+
+type FillWithPreviousStrategy struct{}
+
+// Deal 空值填充:前值填充
+func (f *FillWithPreviousStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
+	// 按自然日补充,再根据基准指标取对应数据
+	for _, v := range edbDataList {
+		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
+			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+				// 存放补充数据
+				var replenishDataList []*data_manage.EdbDataList
+
+				// 处理从 startDate 到第一个数据的日期补充
+				if len(dataList) > 0 {
+					firstData := dataList[0]
+					// 将 startDate 到第一个数据日期之间的自然日填充补充数据,值为 0
+					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)
+
+					// 计算两个日期之间的天数差
+					if !startDataTime.Equal(firstDataTime) {
+						for startDataTime.Before(firstDataTime) {
+							// 补充数据
+							nextDay := startDataTime.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: startDataTime.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 startDataTime 到下一个日期
+							startDataTime = startDataTime.AddDate(0, 0, 1)
+						}
+					}
+				}
+
+				// 处理指标中空值数据
+				for index := 0; index < len(dataList)-1; index++ {
+					// 获取当前数据和下一个数据
+					beforeIndexData := dataList[index]
+					afterIndexData := dataList[index+1]
+
+					for utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
+						// 创建补充数据
+						nextDay := utils.GetNextDay(beforeIndexData.DataTime)
+
+						toTime := utils.StringToTime(nextDay)
+						replenishIndexData := data_manage.EdbDataList{
+							EdbInfoId:     v.EdbInfoId,
+							DataTime:      nextDay,
+							DataTimestamp: toTime.UnixMilli(),
+							Value:         beforeIndexData.Value,
+						}
+
+						// 将补充数据加入补充数据列表
+						replenishDataList = append(replenishDataList, &replenishIndexData)
+
+						// 更新 beforeIndexData 为新创建的补充数据
+						beforeIndexData = &replenishIndexData
+					}
+				}
+
+				// 处理从最后一个数据到 endDate 的日期补充
+				if len(dataList) > 0 {
+					lastData := dataList[len(dataList)-1]
+					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据,值为 0
+					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
+					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+
+					// 如果 lastDataTime 不等于 endDate,进行补充
+					if !lastDataTime.Equal(endDataTime) {
+						// 补充数据直到 endDate
+						for lastDataTime.Before(endDataTime) {
+							// 补充数据
+							addDate := lastDataTime.AddDate(0, 0, 1)
+							nextDay := addDate.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: addDate.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 lastDataTime 到下一个日期
+							lastDataTime = addDate
+						}
+					}
+				}
+
+				dataList = append(dataList, replenishDataList...)
+
+				// 根据基准指标筛选出符合数据
+				var resultDataList []*data_manage.EdbDataList
+				for _, dataObject := range dataList {
+					_, ok = standardIndexMap[dataObject.DataTime]
+					if ok {
+						// 存在才保留
+						resultDataList = append(resultDataList, dataObject)
+					}
+				}
+
+				// 排序
+				sort.Slice(resultDataList, func(i, j int) bool {
+					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
+				})
+
+				v.DataList = resultDataList
+			}
+		}
+	}
+	return nil
+}
+
+type FillWithNextStrategy struct{}
+
+// Deal 空值填充:后值填充
+func (f *FillWithNextStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
+	// 按自然日补充,再根据基准指标取对应数据
+	for _, v := range edbDataList {
+		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
+			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+
+				// 存放补充数据
+				var replenishDataList []*data_manage.EdbDataList
+
+				// 处理从 startDate 到第一个数据的日期补充
+				if len(dataList) > 0 {
+					firstData := dataList[0]
+					// 将 startDate 到第一个数据日期之间的自然日填充补充数据,值为 0
+					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)
+
+					// 计算两个日期之间的天数差
+					if !startDataTime.Equal(firstDataTime) {
+						for !startDataTime.After(firstDataTime) {
+							// 补充数据
+							nextDay := startDataTime.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: startDataTime.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 startDataTime 到下一个日期
+							startDataTime = startDataTime.AddDate(0, 0, 1)
+						}
+					}
+				}
+
+				// 处理从最后一个数据到 endDate 的日期补充
+				if len(dataList) > 0 {
+					lastData := dataList[len(dataList)-1]
+					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据,值为 0
+					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
+					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+
+					// 如果 lastDataTime 不等于 endDate,进行补充
+					if !lastDataTime.Equal(endDataTime) {
+						// 补充数据直到 endDate
+						for lastDataTime.Before(endDataTime) {
+							// 补充数据
+							addDate := lastDataTime.AddDate(0, 0, 1)
+							nextDay := addDate.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: addDate.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 lastDataTime 到下一个日期
+							lastDataTime = addDate
+						}
+					}
+				}
+
+				// 将切片数据倒序
+				reverseSlice(dataList)
+
+				// 处理指标中空值数据
+				for index := 0; index < len(dataList)-1; index++ {
+					// 获取当前数据和下一个数据
+					beforeIndexData := dataList[index]
+					afterIndexData := dataList[index+1]
+
+					for utils.IsMoreThanOneDay(afterIndexData.DataTime, beforeIndexData.DataTime) {
+						// 创建补充数据
+						nextDay := utils.GetNextDay(afterIndexData.DataTime)
+
+						toTime := utils.StringToTime(nextDay)
+						replenishIndexData := data_manage.EdbDataList{
+							EdbInfoId:     v.EdbInfoId,
+							DataTime:      nextDay,
+							DataTimestamp: toTime.UnixMilli(),
+							Value:         beforeIndexData.Value,
+						}
+
+						// 将补充数据加入补充数据列表
+						replenishDataList = append(replenishDataList, &replenishIndexData)
+
+						// 更新 beforeIndexData 为新创建的补充数据
+						afterIndexData = &replenishIndexData
+					}
+				}
+
+				dataList = append(dataList, replenishDataList...)
+
+				// 根据基准指标筛选出符合数据
+				var resultDataList []*data_manage.EdbDataList
+				for _, dataObject := range dataList {
+					_, ok = standardIndexMap[dataObject.DataTime]
+					if ok {
+						// 存在才保留
+						resultDataList = append(resultDataList, dataObject)
+					}
+				}
+
+				// 排序
+				sort.Slice(resultDataList, func(i, j int) bool {
+					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
+				})
+
+				v.DataList = resultDataList
+			}
+		}
+	}
+	return nil
+}
+
+type SetToZeroStrategy struct{}
+
+// Deal 空值填充:设为0
+func (s *SetToZeroStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
+	// 按自然日补充,再根据基准指标取对应数据
+	for _, v := range edbDataList {
+		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
+			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+				// 存放补充数据
+				var replenishDataList []*data_manage.EdbDataList
+
+				// 处理从 startDate 到第一个数据的日期补充
+				if len(dataList) > 0 {
+					firstData := dataList[0]
+					// 将 startDate 到第一个数据日期之间的自然日填充补充数据,值为 0
+					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)
+
+					// 计算两个日期之间的天数差
+					if !startDataTime.Equal(firstDataTime) {
+						for startDataTime.Before(firstDataTime) {
+							// 补充数据
+							nextDay := startDataTime.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: startDataTime.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 startDataTime 到下一个日期
+							startDataTime = startDataTime.AddDate(0, 0, 1)
+						}
+					}
+				}
+
+				// 处理指标中空值数据
+				for index := 0; index < len(dataList)-1; index++ {
+					// 获取当前数据和下一个数据
+					beforeIndexData := dataList[index]
+					afterIndexData := dataList[index+1]
+
+					for utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
+						// 创建补充数据
+						nextDay := utils.GetNextDay(beforeIndexData.DataTime)
+
+						toTime := utils.StringToTime(nextDay)
+						replenishIndexData := data_manage.EdbDataList{
+							EdbInfoId:     v.EdbInfoId,
+							DataTime:      nextDay,
+							DataTimestamp: toTime.UnixMilli(),
+							Value:         0,
+						}
+
+						// 将补充数据加入补充数据列表
+						replenishDataList = append(replenishDataList, &replenishIndexData)
+
+						// 更新 beforeIndexData 为新创建的补充数据
+						beforeIndexData = &replenishIndexData
+					}
+				}
+
+				// 处理从最后一个数据到 endDate 的日期补充
+				if len(dataList) > 0 {
+					lastData := dataList[len(dataList)-1]
+					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据,值为 0
+					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
+					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+
+					// 如果 lastDataTime 不等于 endDate,进行补充
+					if !lastDataTime.Equal(endDataTime) {
+						// 补充数据直到 endDate
+						for lastDataTime.Before(endDataTime) {
+							// 补充数据
+							addDate := lastDataTime.AddDate(0, 0, 1)
+							nextDay := addDate.Format(utils.FormatDate)
+
+							// 生成补充数据,值为 0
+							replenishIndexData := data_manage.EdbDataList{
+								EdbInfoId:     v.EdbInfoId,
+								DataTime:      nextDay,
+								DataTimestamp: addDate.UnixMilli(),
+								Value:         0,
+							}
+
+							// 将补充数据加入补充数据列表
+							replenishDataList = append(replenishDataList, &replenishIndexData)
+
+							// 更新 lastDataTime 到下一个日期
+							lastDataTime = addDate
+						}
+					}
+				}
+
+				dataList = append(dataList, replenishDataList...)
+
+				// 根据基准指标筛选出符合数据
+				var resultDataList []*data_manage.EdbDataList
+				for _, dataObject := range dataList {
+					_, ok = standardIndexMap[dataObject.DataTime]
+					if ok {
+						// 存在才保留
+						resultDataList = append(resultDataList, dataObject)
+					}
+				}
+
+				// 排序
+				sort.Slice(resultDataList, func(i, j int) bool {
+					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
+				})
+
+				v.DataList = resultDataList
+			}
+		}
+	}
+	return nil
+}
+
+type DeleteDateStrategy struct{}
+
+// Deal 删除日期
+func (d *DeleteDateStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) error {
+	// 取所有指标的时间交集
+	// 创建一个 map 来保存每个时间点的出现次数
+	timeMap := make(map[string]int)
+	for _, v := range edbDataList {
+		if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+			// 遍历所有的 dataList,为每个 DataTime 增加一个计数
+			for _, dataObject := range dataList {
+				timeMap[dataObject.DataTime]++
+			}
+		}
+	}
+
+	for _, v := range edbDataList {
+		if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+			// 遍历所有的 dataList,保留所有时间点在所有指标中都存在的数据
+			var resultDataList []*data_manage.EdbDataList
+			for _, dataObject := range dataList {
+				if timeMap[dataObject.DataTime] == len(edbDataList) {
+					// 如果该时间点在所有指标中都存在,加入到结果列表
+					resultDataList = append(resultDataList, dataObject)
+				}
+			}
+
+			// 将符合条件的数据重新赋值回 v.DataList
+			v.DataList = resultDataList
+		}
+	}
+	return nil
+}
+
+// 将列表颠倒
+func reverseSlice(dataList []*data_manage.EdbDataList) {
+	// 使用双指针法,前后两个指针向中间逼近
+	for i, j := 0, len(dataList)-1; i < j; i, j = i+1, j-1 {
+		// 交换位置
+		dataList[i], dataList[j] = dataList[j], dataList[i]
+	}
+}

+ 27 - 0
services/data/area_graph/processor_factory.go

@@ -0,0 +1,27 @@
+package area_graph
+
+import (
+	"eta/eta_api/models/data_manage"
+	"fmt"
+)
+
+type NullDealStrategy interface {
+	Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error)
+}
+
+func CreateStrategy(dealWay int) (NullDealStrategy, error) {
+	switch dealWay {
+	case 1:
+		return &InterpolateStrategy{}, nil
+	case 2:
+		return &FillWithPreviousStrategy{}, nil
+	case 3:
+		return &FillWithNextStrategy{}, nil
+	case 4:
+		return &SetToZeroStrategy{}, nil
+	case 5:
+		return &DeleteDateStrategy{}, nil
+	default:
+		return nil, fmt.Errorf("未知的空值处理类型: %d", dealWay)
+	}
+}

+ 4 - 0
services/data/chart_info.go

@@ -653,6 +653,7 @@ func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate
 			item.ChartStyle = ""
 			item.ChartColor = ""
 			item.ChartWidth = 0
+			item.ChartScale = 0
 			item.MaxData = v.MaxValue
 			item.MinData = v.MinValue
 		} else {
@@ -666,6 +667,7 @@ func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate
 			item.ChartStyle = v.ChartStyle
 			item.ChartColor = v.ChartColor
 			item.ChartWidth = v.ChartWidth
+			item.ChartScale = v.ChartScale
 			item.IsOrder = v.IsOrder
 			item.MaxData = v.MaxData
 			item.MinData = v.MinData
@@ -2433,6 +2435,7 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 					ChartColor:        "",
 					PredictChartColor: "",
 					ChartWidth:        0,
+					ChartScale:        0,
 					Source:            utils.CHART_SOURCE_DEFAULT,
 				})
 			}
@@ -2615,6 +2618,7 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 		mapItem.ChartColor = v.ChartColor
 		mapItem.PredictChartColor = v.PredictChartColor
 		mapItem.ChartWidth = v.ChartWidth
+		mapItem.ChartScale = v.ChartScale
 		mapItem.Source = utils.CHART_SOURCE_DEFAULT
 		mapItem.EdbAliasName = v.EdbAliasName
 		mapItem.IsConvert = v.IsConvert

+ 1 - 0
utils/constants.go

@@ -340,6 +340,7 @@ const (
 const (
 	CHART_TYPE_CURVE           = 1  //曲线图
 	CHART_TYPE_SEASON          = 2  //季节性图
+	CHART_TYPE_AREA            = 3  // 面积图
 	CHART_TYPE_BAR             = 7  //柱形图
 	CHART_TYPE_SECTION_SCATTER = 10 //截面散点图样式
 	CHART_TYPE_RADAR           = 11 //雷达图

+ 8 - 1
utils/date_util.go

@@ -42,7 +42,8 @@ func GetNextDay(date string) string {
 
 // GetNextDayN 获取 yyyy-MM-dd 类型的时间的下n天
 func GetNextDayN(date string, n int) string {
-	t, _ := time.Parse("2006-01-02", date)
+	t, _ := time.ParseInLocation("2006-01-02", date, time.Local)
+	//t, _ := time.Parse("2006-01-02", date)
 	nextDay := t.AddDate(0, 0, n)
 	return nextDay.Format("2006-01-02")
 }
@@ -115,6 +116,12 @@ func StringToTime(date string) time.Time {
 	return t
 }
 
+// StringToFormatTime string 类型时间 转换为指定格式的 time.Time类型
+func StringToFormatTime(data string, format string) time.Time {
+	t, _ := time.ParseInLocation(format, data, time.Local)
+	return t
+}
+
 // TimeToString time.Time 类型时间 转换为 string 类型
 func TimeToString(t time.Time, format string) string {
 	formattedTime := t.Format(format)