Browse Source

feat:新增时间截面图

Roc 2 years ago
parent
commit
e9a8e65a55
5 changed files with 554 additions and 49 deletions
  1. 18 38
      controllers/chart.go
  2. 2 0
      models/chart.go
  3. 96 0
      models/data_manage/chart_info.go
  4. 374 10
      services/data/chart_info.go
  5. 64 1
      utils/calculate.go

+ 18 - 38
controllers/chart.go

@@ -12,7 +12,7 @@ import (
 	"time"
 )
 
-//图表
+// 图表
 type ChartController struct {
 	BaseAuthController
 }
@@ -157,40 +157,30 @@ func (this *ChartController) ChartInfoDetail() {
 		return
 	}
 
-	var barConfig data_manage.BarChartInfoReq
-	barChartInfoDateList := make([]data_manage.BarChartInfoDateReq, 0)
-	barChartInfoSort := data_manage.BarChartInfoSortReq{}
+	extraConfigStr := chartInfo.ExtraConfig
 	// 柱方图的一些配置
+	var barConfig data_manage.BarChartInfoReq
 	if chartInfo != nil && chartInfo.ChartType == 7 {
 		if chartInfo.BarConfig == `` {
 			br.Msg = "柱方图未配置"
 			br.ErrMsg = "柱方图未配置"
 			return
 		}
-
 		err := json.Unmarshal([]byte(chartInfo.BarConfig), &barConfig)
 		if err != nil {
 			br.Msg = "柱方图配置异常"
 			br.ErrMsg = "柱方图配置异常"
 			return
 		}
-
-		barChartInfoDateList = barConfig.DateList
-		barChartInfoSort = barConfig.Sort
-
-		// 指标别名
-		for _, v := range mappingList {
-			for _, confEdb := range barConfig.EdbInfoIdList {
-				if v.EdbInfoId == confEdb.EdbInfoId {
-					v.EdbAliasName = confEdb.Name
-				}
-			}
-		}
+		extraConfigStr = chartInfo.BarConfig
 	}
 
-	edbList, xEdbIdValue, yDataList, err := data.GetChartEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList, barChartInfoDateList, barChartInfoSort)
+	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList, extraConfigStr)
 	if err != nil {
 		br.Msg = "获取失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
 		return
 	}
@@ -210,6 +200,7 @@ func (this *ChartController) ChartInfoDetail() {
 	resp.EdbInfoList = edbList
 	resp.XEdbIdValue = xEdbIdValue
 	resp.YDataList = yDataList
+	resp.DataResp = dataResp
 
 	if utils.Re == nil {
 		data, _ := json.Marshal(resp)
@@ -334,10 +325,9 @@ func GetLeadUnitEn(unit string) (unitEn string) {
 }
 
 // GetChartInfoDetailFromUniqueCode 根据编码获取图表详情
-func GetChartInfoDetailFromUniqueCode(chartInfo *models.ChartInfo,key string) (resp *models.ChartInfoDetailResp, isOk bool, msg, errMsg string) {
+func GetChartInfoDetailFromUniqueCode(chartInfo *models.ChartInfo, key string) (resp *models.ChartInfoDetailResp, isOk bool, msg, errMsg string) {
 	resp = new(models.ChartInfoDetailResp)
 
-
 	chartInfoId := chartInfo.ChartInfoId
 
 	dateType := chartInfo.DateType
@@ -429,40 +419,30 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *models.ChartInfo,key string) (r
 		return
 	}
 
-	var barConfig data_manage.BarChartInfoReq
-	barChartInfoDateList := make([]data_manage.BarChartInfoDateReq, 0)
-	barChartInfoSort := data_manage.BarChartInfoSortReq{}
+	extraConfigStr := chartInfo.ExtraConfig
 	// 柱方图的一些配置
+	var barConfig data_manage.BarChartInfoReq
 	if chartInfo != nil && chartInfo.ChartType == 7 {
 		if chartInfo.BarConfig == `` {
 			msg = "柱方图未配置"
 			errMsg = "柱方图未配置"
 			return
 		}
-
 		err := json.Unmarshal([]byte(chartInfo.BarConfig), &barConfig)
 		if err != nil {
 			msg = "柱方图配置异常"
 			errMsg = "柱方图配置异常"
 			return
 		}
-
-		barChartInfoDateList = barConfig.DateList
-		barChartInfoSort = barConfig.Sort
-
-		// 指标别名
-		for _, v := range mappingList {
-			for _, confEdb := range barConfig.EdbInfoIdList {
-				if v.EdbInfoId == confEdb.EdbInfoId {
-					v.EdbAliasName = confEdb.Name
-				}
-			}
-		}
+		extraConfigStr = chartInfo.BarConfig
 	}
 
-	edbList, xEdbIdValue, yDataList, err := data.GetChartEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList, barChartInfoDateList, barChartInfoSort)
+	edbList, xEdbIdValue, yDataList, dataResp, err, tmpErrMsg := data.GetChartEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList, extraConfigStr)
 	if err != nil {
 		msg = "获取失败"
+		if tmpErrMsg != `` {
+			msg = tmpErrMsg
+		}
 		errMsg = "获取图表,指标信息失败,Err:" + err.Error()
 		return
 	}
@@ -482,6 +462,7 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *models.ChartInfo,key string) (r
 	resp.EdbInfoList = edbList
 	resp.XEdbIdValue = xEdbIdValue
 	resp.YDataList = yDataList
+	resp.DataResp = dataResp
 
 	if utils.Re == nil {
 		jsonData, _ := json.Marshal(resp)
@@ -491,4 +472,3 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *models.ChartInfo,key string) (r
 	isOk = true
 	return
 }
-

+ 2 - 0
models/chart.go

@@ -36,6 +36,7 @@ type ChartInfo struct {
 	LeftMax         string    `description:"图表左侧最大值"`
 	Source          int       `description:"1:ETA图库;2:商品价格曲线"`
 	UnitEn          string    `description:"英文图表名称"`
+	ExtraConfig     string    `description:"图表额外配置,json数据" json:"-"`
 }
 
 func GetChartInfoByUniqueCode(uniqueCode string) (item *ChartInfo, err error) {
@@ -161,6 +162,7 @@ type ChartInfoDetailResp struct {
 	YDataList            []YData          `description:"柱方图的y轴数据"`
 	XDataList            []XData          `description:"商品价格曲线的X轴数据"`
 	CorrelationChartInfo *CorrelationInfo `description:"相关性图表信息"`
+	DataResp             interface{}      `description:"图表数据,根据图的类型而定的,没有确定的数据格式"`
 }
 
 // XData 商品价格曲线的的x轴数据

+ 96 - 0
models/data_manage/chart_info.go

@@ -141,3 +141,99 @@ type BarChartInfoSortReq struct {
 	Sort      int `description:"排序类型,0:默认,1:升序,2:降序"`
 	DateIndex int `description:"日期数据的下标,从0开始"`
 }
+
+// TimeSectionReq 时间截面请求
+type TimeSectionReq struct {
+	XName       string `description:"x轴名称"`
+	XNameEn     string `description:"x轴名称(英文)"`
+	XUnitName   string `description:"x轴单位名称"`
+	XUnitNameEn string `description:"x轴单位名称(英文)"`
+	YName       string `description:"y轴名称"`
+	YNameEn     string `description:"y轴名称(英文)"`
+	YUnitName   string `description:"y轴单位名称"`
+	YUnitNameEn string `description:"y轴单位名称(英文)"`
+	XMinValue   string `description:"X轴的最小值"`
+	XMaxValue   string `description:"X轴的最大值"`
+	YMinValue   string `description:"Y轴的最小值"`
+	YMaxValue   string `description:"Y轴的最大值"`
+	//EdbList     []TimeSectionEdbItemReq    `description:"指标数据"`
+	SeriesList []TimeSectionSeriesItemReq `description:"系列数据"`
+}
+
+// TimeSectionSeriesItemReq 系列的请求
+type TimeSectionSeriesItemReq struct {
+	Name            string `description:"系列名"`
+	Color           string `description:"颜色"`
+	EdbInfoList     []TimeSectionEdbItemReq
+	ShowTrendLine   bool `description:"是否展示趋势线"`
+	ShowFitEquation bool `description:"是否展示方程式"`
+	ShowRSquare     bool `description:"是否展示R平方"`
+}
+
+// TimeSectionEdbItemReq 时间截面请求的指标
+type TimeSectionEdbItemReq struct {
+	XEdbInfoId int    `description:"X轴的指标ID"`
+	YEdbInfoId int    `description:"Y轴的指标ID"`
+	Name       string `description:"别名"`
+	NameEn     string `description:"英文别名"`
+	XDateType  int    `description:"X轴的日期配置类型"`
+	XDate      string `description:"X轴的日期固定日期"`
+	XDateValue int    `description:"X轴的日期N天的值"`
+	YDateType  int    `description:"Y轴的日期配置类型"`
+	YDate      string `description:"Y轴的日期固定日期"`
+	YDateValue int    `description:"Y轴的日期N天的值"`
+	IsShow     bool   `description:"是否展示"`
+}
+
+// TimeSectionInfoResp 时间截面图数据
+type TimeSectionInfoResp struct {
+	XName       string                      `description:"x轴名称"`
+	XNameEn     string                      `description:"x轴名称(英文)"`
+	XUnitName   string                      `description:"x轴单位名称"`
+	XUnitNameEn string                      `description:"x轴单位名称(英文)"`
+	YName       string                      `description:"y轴名称"`
+	YNameEn     string                      `description:"y轴名称(英文)"`
+	YUnitName   string                      `description:"y轴单位名称"`
+	YUnitNameEn string                      `description:"y轴单位名称(英文)"`
+	XMinValue   string                      `description:"X轴的最小值"`
+	XMaxValue   string                      `description:"X轴的最大值"`
+	YMinValue   string                      `description:"Y轴的最小值"`
+	YMaxValue   string                      `description:"Y轴的最大值"`
+	DataList    []TimeSectionSeriesItemResp `description:"数据列"`
+}
+
+// TimeSectionSeriesItemResp 系列的返回
+type TimeSectionSeriesItemResp struct {
+	Name            string `description:"系列名"`
+	Color           string `description:"颜色"`
+	EdbInfoList     []TimeSectionEdbItemResp
+	ShowTrendLine   bool              `description:"是否展示趋势线"`
+	ShowFitEquation bool              `description:"是否展示方程式"`
+	ShowRSquare     bool              `description:"是否展示R平方"`
+	TrendLine       string            `description:"方程式"`
+	RSquare         string            `description:"R平方的值(决定系数R2)"`
+	TrendLimitData  []CoordinatePoint `description:"趋势线的前后坐标点"`
+}
+
+// TimeSectionEdbItemResp 时间截面的返回参数
+type TimeSectionEdbItemResp struct {
+	XEdbInfoId int     `description:"X轴指标id"`
+	XDate      string  `description:"X轴指标实际日期"`
+	XName      string  `description:"X轴指标名称"`
+	XNameEn    string  `description:"X轴指标英文名称"`
+	XValue     float64 `description:"X轴实际值"`
+	YEdbInfoId int     `description:"Y轴指标id"`
+	YDate      string  `description:"Y轴指标实际日期"`
+	YName      string  `description:"Y轴指标名称"`
+	YNameEn    string  `description:"Y轴指标英文名称"`
+	YValue     float64 `description:"Y轴实际值"`
+	IsShow     bool    `description:"是否展示"`
+	Name       string  `description:"标签名称"`
+	NameEn     string  `description:"英文标签名称"`
+}
+
+// CoordinatePoint 坐标点
+type CoordinatePoint struct {
+	X float64
+	Y float64
+}

+ 374 - 10
services/data/chart_info.go

@@ -1,12 +1,15 @@
 package data
 
 import (
+	"encoding/json"
 	"errors"
 	"fmt"
+	"github.com/shopspring/decimal"
 	"hongze/hongze_chart_lib/models"
 	"hongze/hongze_chart_lib/models/data_manage"
 	"hongze/hongze_chart_lib/services/alarm_msg"
 	"hongze/hongze_chart_lib/utils"
+	"math"
 	"sort"
 	"time"
 )
@@ -190,10 +193,85 @@ func GetLeadUnitEn(unit string) (unitEn string) {
 }
 
 // GetChartEdbData 获取图表的指标数据
-func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*models.ChartEdbInfoMapping, barChartInfoDateList []data_manage.BarChartInfoDateReq, barChartInfoSort data_manage.BarChartInfoSortReq) (edbList []*models.ChartEdbInfoMapping, xEdbIdValue []int, yDataList []models.YData, err error) {
+func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*models.ChartEdbInfoMapping, extraConfigStr string) (edbList []*models.ChartEdbInfoMapping, xEdbIdValue []int, yDataList []models.YData, dataResp interface{}, err error, errMsg string) {
 	edbList = make([]*models.ChartEdbInfoMapping, 0)
 	// 指标对应的所有数据
-	edbDataListMap := make(map[int][]*models.EdbDataList)
+	xEdbIdValue = make([]int, 0)
+	yDataList = make([]models.YData, 0)
+	var extraConfig interface{}
+	switch chartType {
+	case 7: // 柱形图
+		var barConfig data_manage.BarChartInfoReq
+		if extraConfigStr == `` {
+			errMsg = "柱方图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &barConfig)
+		if err != nil {
+			errMsg = "柱方图配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+		extraConfig = barConfig
+	case 10: // 时间截面图
+		var tmpExtraConfig data_manage.TimeSectionReq
+		if extraConfigStr == `` {
+			errMsg = "时间截面图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &tmpExtraConfig)
+		if err != nil {
+			errMsg = "时间截面配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+		extraConfig = tmpExtraConfig
+	default:
+		xEdbIdValue = make([]int, 0)
+		yDataList = make([]models.YData, 0)
+	}
+
+	// 指标对应的所有数据
+	edbDataListMap, edbList, err := getEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList)
+	if err != nil {
+		return
+	}
+
+	// 特殊图形数据处理
+	switch chartType {
+	case 7: // 柱形图
+		barChartConf := extraConfig.(data_manage.BarChartInfoReq)
+		xEdbIdValue, yDataList, err = BarChartData(mappingList, edbDataListMap, barChartConf.DateList, barChartConf.Sort)
+
+		for _, v := range edbList {
+			// 指标别名
+			if barChartConf.EdbInfoIdList != nil && len(barChartConf.EdbInfoIdList) > 0 {
+				for _, reqEdb := range barChartConf.EdbInfoIdList {
+					if v.EdbInfoId == reqEdb.EdbInfoId {
+						v.EdbAliasName = reqEdb.Name
+					}
+				}
+			}
+		}
+	case 10: // 时间截面图
+		timeSectionConf := extraConfig.(data_manage.TimeSectionReq)
+		xEdbIdValue, dataResp, err = GetTimeSectionChartData(mappingList, edbDataListMap, timeSectionConf)
+
+		// 这个数据没有必要返回给前端
+		for _, v := range edbList {
+			v.DataList = nil
+		}
+	}
+
+	return
+}
+
+// getEdbDataMapList 获取指标最后的基础数据
+func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*models.ChartEdbInfoMapping) (edbDataListMap map[int][]*models.EdbDataList, edbList []*models.ChartEdbInfoMapping, err error) {
+	// 指标对应的所有数据
+	edbDataListMap = make(map[int][]*models.EdbDataList)
 
 	for _, v := range mappingList {
 		//fmt.Println("v:", v.EdbInfoId)
@@ -427,14 +505,6 @@ func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate st
 		edbList = append(edbList, item)
 	}
 
-	// 柱方图
-	if chartType == 7 {
-		xEdbIdValue, yDataList, err = BarChartData(mappingList, edbDataListMap, barChartInfoDateList, barChartInfoSort)
-	} else {
-		xEdbIdValue = make([]int, 0)
-		yDataList = make([]models.YData, 0)
-	}
-
 	return
 }
 
@@ -678,3 +748,297 @@ func ChartInfoRefreshV2(chartInfoId int) (err error) {
 
 	return
 }
+
+// GetTimeSectionChartData 柱方图的数据处理
+func GetTimeSectionChartData(mappingList []*models.ChartEdbInfoMapping, edbDataListMap map[int][]*models.EdbDataList, extraConfig data_manage.TimeSectionReq) (edbIdList []int, chartDataResp data_manage.TimeSectionInfoResp, err error) {
+	// 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3})
+	edbDataMap := make(map[int]map[string]float64)
+	for edbInfoId, edbDataList := range edbDataListMap {
+		edbDateData := make(map[string]float64)
+		for _, edbData := range edbDataList {
+			edbDateData[edbData.DataTime] = edbData.Value
+		}
+		edbDataMap[edbInfoId] = edbDateData
+	}
+
+	// edbIdList 指标展示顺序;x轴的指标顺序
+	edbIdList = make([]int, 0)
+	edbMappingMap := make(map[int]*models.ChartEdbInfoMapping)
+	for _, v := range mappingList {
+		edbIdList = append(edbIdList, v.EdbInfoId)
+		edbMappingMap[v.EdbInfoId] = v
+	}
+	//TimeSectionSeriesInfoResp
+
+	dataListResp := make([]data_manage.TimeSectionSeriesItemResp, 0) //y轴的数据列表
+
+	for _, seriesItem := range extraConfig.SeriesList {
+		var maxDate time.Time
+		// 系列中的指标数据
+		tmpSeriesEdbInfoList := make([]data_manage.TimeSectionEdbItemResp, 0)
+
+		var minXVal, maxXVal, minYVal, maxYVal float64
+		for _, edbConf := range seriesItem.EdbInfoList {
+			tmpItem := data_manage.TimeSectionEdbItemResp{
+				IsShow: edbConf.IsShow,
+				Name:   edbConf.Name,
+				NameEn: edbConf.NameEn,
+			} //单个坐标点的数据
+
+			//X轴的数据
+			{
+				edbInfoId := edbConf.XEdbInfoId //X轴的指标
+				edbMappingInfo, ok := edbMappingMap[edbInfoId]
+				if !ok {
+					continue
+				}
+				findDate := edbConf.XDate             //需要的日期值
+				dataList := edbDataListMap[edbInfoId] //指标的所有数据值
+				if len(dataList) <= 0 {
+					// 没有数据的指标id
+					//findDataList = append(findDataList, 0)
+					continue
+				}
+
+				tmpItem.XEdbInfoId = edbInfoId
+				tmpItem.XName = edbMappingInfo.EdbName
+				tmpItem.XNameEn = edbMappingInfo.EdbNameEn
+
+				switch edbConf.XDateType {
+				case 1: //最新值
+					dataList := edbDataListMap[edbInfoId]
+					findDate = dataList[len(dataList)-1].DataTime
+				case 2: //近期几天
+					findDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, dataList[len(dataList)-1].DataTime, time.Local)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					findDateTime = findDateTime.AddDate(0, 0, -edbConf.XDateValue)
+
+					lenData := len(dataList) - 1
+					for i := lenData; i >= 0; i-- {
+						currDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, dataList[i].DataTime, time.Local)
+						if tmpErr != nil {
+							err = tmpErr
+							return
+						}
+						if currDateTime.Equal(findDateTime) || currDateTime.Before(findDateTime) {
+							findDate = dataList[i].DataTime
+							break
+						}
+					}
+				case 3: // 固定日期
+					//最早的日期
+					minDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					//寻找固定日期的数据
+					findDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, edbConf.XDate, time.Local)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					for tmpDateTime := findDateTime; tmpDateTime.After(minDateTime) || tmpDateTime.Equal(minDateTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
+						tmpDate := tmpDateTime.Format(utils.FormatDate)
+						if _, ok := edbDataMap[edbInfoId][tmpDate]; ok { //如果能找到数据,那么就返回
+							findDate = tmpDate
+							break
+						}
+					}
+				default:
+					err = errors.New(fmt.Sprint("日期类型异常,Type:", edbConf.XDate))
+					return
+				}
+				findDateTime, _ := time.ParseInLocation(utils.FormatDate, findDate, time.Local)
+				if maxDate.IsZero() {
+					maxDate = findDateTime
+				} else {
+					if findDateTime.After(maxDate) {
+						maxDate = findDateTime
+					}
+				}
+				if tmpValue, ok := edbDataMap[edbInfoId][findDate]; ok {
+					tmpItem.XDate = findDate
+					tmpItem.XValue = tmpValue
+				} else {
+					continue
+				}
+			}
+
+			//Y轴的数据
+			{
+				edbInfoId := edbConf.YEdbInfoId //Y轴的指标
+				edbMappingInfo, ok := edbMappingMap[edbInfoId]
+				if !ok {
+					continue
+				}
+				findDate := edbConf.YDate             //需要的日期值
+				dataList := edbDataListMap[edbInfoId] //指标的所有数据值
+				if len(dataList) <= 0 {
+					// 没有数据的指标id
+					//findDataList = append(findDataList, 0)
+					continue
+				}
+
+				tmpItem.YEdbInfoId = edbInfoId
+				tmpItem.YName = edbMappingInfo.EdbName
+				tmpItem.YNameEn = edbMappingInfo.EdbNameEn
+
+				switch edbConf.YDateType {
+				case 1: //最新值
+					dataList := edbDataListMap[edbInfoId]
+					findDate = dataList[len(dataList)-1].DataTime
+				case 2: //近期几天
+					findDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, dataList[len(dataList)-1].DataTime, time.Local)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					findDateTime = findDateTime.AddDate(0, 0, -edbConf.YDateValue)
+
+					lenData := len(dataList) - 1
+					for i := lenData; i >= 0; i-- {
+						currDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, dataList[i].DataTime, time.Local)
+						if tmpErr != nil {
+							err = tmpErr
+							return
+						}
+						if currDateTime.Equal(findDateTime) || currDateTime.Before(findDateTime) {
+							findDate = dataList[i].DataTime
+							break
+						}
+					}
+				case 3: // 固定日期
+					//最早的日期
+					minDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					//寻找固定日期的数据
+					findDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, edbConf.YDate, time.Local)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					for tmpDateTime := findDateTime; tmpDateTime.After(minDateTime) || tmpDateTime.Equal(minDateTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
+						tmpDate := tmpDateTime.Format(utils.FormatDate)
+						if _, ok := edbDataMap[edbInfoId][tmpDate]; ok { //如果能找到数据,那么就返回
+							findDate = tmpDate
+							break
+						}
+					}
+				default:
+					err = errors.New(fmt.Sprint("日期类型异常,Type:", edbConf.YDate))
+					return
+				}
+				findDateTime, _ := time.ParseInLocation(utils.FormatDate, findDate, time.Local)
+				if maxDate.IsZero() {
+					maxDate = findDateTime
+				} else {
+					if findDateTime.After(maxDate) {
+						maxDate = findDateTime
+					}
+				}
+				if tmpValue, ok := edbDataMap[edbInfoId][findDate]; ok {
+					tmpItem.YDate = findDate
+					tmpItem.YValue = tmpValue
+				} else {
+					continue
+				}
+			}
+
+			// 获取当前系列的X轴的最大最小值
+			{
+				if tmpItem.XValue < minXVal {
+					minXVal = tmpItem.XValue
+				}
+				if tmpItem.XValue > maxXVal {
+					maxXVal = tmpItem.XValue
+				}
+				if tmpItem.YValue < minYVal {
+					minYVal = tmpItem.YValue
+				}
+				if tmpItem.YValue > maxYVal {
+					maxYVal = tmpItem.YValue
+				}
+			}
+			tmpSeriesEdbInfoList = append(tmpSeriesEdbInfoList, tmpItem)
+		}
+
+		trendLimitData := make([]data_manage.CoordinatePoint, 0) //趋势线的前后坐标点
+		var trendLine, rSquare string
+		// 生成线性方程式
+		var a, b float64
+		{
+			coordinateData := make([]utils.Coordinate, 0)
+			for _, tmpSeriesEdbInfo := range tmpSeriesEdbInfoList {
+				tmpCoordinate1 := utils.Coordinate{
+					X: tmpSeriesEdbInfo.XValue,
+					Y: tmpSeriesEdbInfo.YValue,
+				}
+				coordinateData = append(coordinateData, tmpCoordinate1)
+			}
+
+			// 只有存在两个坐标点的时候,才能去计算线性方程和R平方
+			if len(coordinateData) >= 2 {
+				a, b = utils.GetLinearResult(coordinateData)
+				if math.IsNaN(a) || math.IsNaN(b) {
+					err = errors.New("线性方程公式生成失败")
+					return
+				}
+				if b > 0 {
+					trendLine = fmt.Sprintf("y=%sx+%s", utils.SubFloatToString(a, 4), utils.SubFloatToString(b, 4))
+				} else {
+					trendLine = fmt.Sprintf("y=%sx%s", utils.SubFloatToString(a, 4), utils.SubFloatToString(b, 4))
+				}
+
+				// 计算R平方
+				rSquare = fmt.Sprint(utils.CalculationDecisive(coordinateData))
+
+				minYVal, _ = decimal.NewFromFloat(a).Mul(decimal.NewFromFloat(minXVal)).Add(decimal.NewFromFloat(b)).Round(4).Float64()
+				maxYVal, _ = decimal.NewFromFloat(a).Mul(decimal.NewFromFloat(maxXVal)).Add(decimal.NewFromFloat(b)).Round(4).Float64()
+			}
+
+			trendLimitData = append(trendLimitData, data_manage.CoordinatePoint{
+				X: minXVal,
+				Y: minYVal,
+			}, data_manage.CoordinatePoint{
+				X: maxXVal,
+				Y: maxYVal,
+			})
+		}
+
+		dataListResp = append(dataListResp, data_manage.TimeSectionSeriesItemResp{
+			Name:            seriesItem.Name,
+			Color:           seriesItem.Color,
+			EdbInfoList:     tmpSeriesEdbInfoList,
+			ShowTrendLine:   seriesItem.ShowTrendLine,
+			ShowFitEquation: seriesItem.ShowFitEquation,
+			ShowRSquare:     seriesItem.ShowRSquare,
+			TrendLine:       trendLine,
+			RSquare:         rSquare,
+			TrendLimitData:  trendLimitData,
+		})
+	}
+
+	chartDataResp = data_manage.TimeSectionInfoResp{
+		XName:       extraConfig.XName,
+		XNameEn:     extraConfig.XNameEn,
+		XUnitName:   extraConfig.XUnitName,
+		XUnitNameEn: extraConfig.XUnitNameEn,
+		YName:       extraConfig.YName,
+		YNameEn:     extraConfig.YNameEn,
+		YUnitName:   extraConfig.YUnitName,
+		YUnitNameEn: extraConfig.YUnitNameEn,
+		XMinValue:   extraConfig.XMinValue,
+		XMaxValue:   extraConfig.XMaxValue,
+		YMinValue:   extraConfig.YMinValue,
+		YMaxValue:   extraConfig.YMaxValue,
+		DataList:    dataListResp,
+	}
+	return
+}

+ 64 - 1
utils/calculate.go

@@ -1,6 +1,9 @@
 package utils
 
-import "math"
+import (
+	"github.com/shopspring/decimal"
+	"math"
+)
 
 // Series is a container for a series of data
 type Series []Coordinate
@@ -93,3 +96,63 @@ func CalculateCorrelationByIntArr(xArr, yArr []float64) (ratio float64) {
 	}
 	return
 }
+
+// ComputeCorrelation 通过一组数据获取相关系数R
+// 计算步骤
+// 1.分别计算两个序列的平均值Mx和My
+// 2.分别计算两个序列的标准偏差SDx和SDy	=> √{1/(n-1)*SUM[(Xi-Mx)²]}
+// 3.计算相关系数	=> SUM[(Xi-Mx)*(Yi-My)]/[(N-1)(SDx*SDy)]
+func ComputeCorrelation(sList []Coordinate) (r float64) {
+	var xBar, yBar float64
+	lenSList := len(sList)
+	// 必须两组数据及两组以上的数据才能计算
+	if lenSList < 2 {
+		return
+	}
+	decimalX := decimal.NewFromFloat(0)
+	decimalY := decimal.NewFromFloat(0)
+
+	// 计算两组数据X、Y的平均值
+	for _, coordinate := range sList {
+		decimalX = decimalX.Add(decimal.NewFromFloat(coordinate.X))
+		decimalY = decimalY.Add(decimal.NewFromFloat(coordinate.Y))
+	}
+	xBar, _ = decimalX.Div(decimal.NewFromInt(int64(lenSList))).Round(4).Float64()
+	yBar, _ = decimalY.Div(decimal.NewFromInt(int64(lenSList))).Round(4).Float64()
+	//fmt.Println(xBar)
+	//fmt.Println(yBar)
+
+	varXDeci := decimal.NewFromFloat(0)
+	varYDeci := decimal.NewFromFloat(0)
+	ssrDeci := decimal.NewFromFloat(0)
+
+	for _, coordinate := range sList {
+		// 分别计算X、Y的实际数据与平均值的差值
+		diffXXbarDeci := decimal.NewFromFloat(coordinate.X).Sub(decimal.NewFromFloat(xBar))
+		diffYYbarDeci := decimal.NewFromFloat(coordinate.Y).Sub(decimal.NewFromFloat(yBar))
+		ssrDeci = ssrDeci.Add(diffXXbarDeci.Mul(diffYYbarDeci))
+		//fmt.Println("i:", i, ";diffXXbar:", diffXXbarDeci.String(), ";diffYYbar:", diffYYbarDeci.String(), ";ssr:", ssrDeci.String())
+		varXDeci = varXDeci.Add(diffXXbarDeci.Mul(diffXXbarDeci))
+		varYDeci = varYDeci.Add(diffYYbarDeci.Mul(diffYYbarDeci))
+		//varY += diffYYbar ** 2
+	}
+	sqrtVal, _ := varXDeci.Mul(varYDeci).Round(4).Float64()
+	//fmt.Println("sqrtVal:", sqrtVal)
+	sst := math.Sqrt(sqrtVal) // 平方根
+	//fmt.Println("sst:", sst)
+	// 如果计算出来的平方根是0,那么就直接返回,因为0不能作为除数
+	if sst == 0 {
+		return
+	}
+	r, _ = ssrDeci.Div(decimal.NewFromFloat(sst)).Round(4).Float64()
+
+	return
+}
+
+// CalculationDecisive 通过一组数据获取决定系数R2
+func CalculationDecisive(sList []Coordinate) (r2 float64) {
+	r := ComputeCorrelation(sList)
+	r2, _ = decimal.NewFromFloat(r).Mul(decimal.NewFromFloat(r)).Round(4).Float64()
+
+	return
+}