|
@@ -1,8 +1,10 @@
|
|
|
package chart
|
|
|
|
|
|
import (
|
|
|
+ "encoding/json"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
+ "github.com/shopspring/decimal"
|
|
|
"hongze/hongze_yb/global"
|
|
|
"hongze/hongze_yb/models/request"
|
|
|
"hongze/hongze_yb/models/response/chart_info"
|
|
@@ -18,6 +20,7 @@ import (
|
|
|
edbDataService "hongze/hongze_yb/services/edb_data"
|
|
|
"hongze/hongze_yb/services/user"
|
|
|
"hongze/hongze_yb/utils"
|
|
|
+ "math"
|
|
|
"sort"
|
|
|
"strconv"
|
|
|
"strings"
|
|
@@ -402,12 +405,84 @@ func RefreshChart(chartInfoId int) (err error) {
|
|
|
}
|
|
|
|
|
|
// GetChartEdbData 获取图表的指标数据
|
|
|
-func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*chartEdbMappingModel.ChartEdbInfoMapping, barChartInfoDateList []request.BarChartInfoDateReq, barChartInfoSort request.BarChartInfoSortReq) (edbList []*chartEdbMappingModel.ChartEdbInfoMappingList, xEdbIdValue []int, yDataList []chart_info.YData, sourceArr []string, err error) {
|
|
|
+func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*chartEdbMappingModel.ChartEdbInfoMapping, extraConfigStr string) (edbList []*chartEdbMappingModel.ChartEdbInfoMappingList, xEdbIdValue []int, yDataList []chart_info.YData, sourceArr []string, dataResp interface{}, err error, errMsg string) {
|
|
|
edbList = make([]*chartEdbMappingModel.ChartEdbInfoMappingList, 0)
|
|
|
+ xEdbIdValue = make([]int, 0)
|
|
|
+ yDataList = make([]chart_info.YData, 0)
|
|
|
+
|
|
|
+ var extraConfig interface{}
|
|
|
+ switch chartType {
|
|
|
+ case 7: // 柱形图
|
|
|
+ var barConfig request.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 request.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
|
|
|
+ }
|
|
|
+
|
|
|
// 指标对应的所有数据
|
|
|
- edbDataListMap := make(map[int][]*edbDataModel.EdbDataList)
|
|
|
- sourceArr = make([]string, 0)
|
|
|
+ edbDataListMap, edbList, sourceArr, err := getEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 特殊图形数据处理
|
|
|
+ switch chartType {
|
|
|
+ case 7: // 柱形图
|
|
|
+ barChartConf := extraConfig.(request.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.(request.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 []*chartEdbMappingModel.ChartEdbInfoMapping) (edbDataListMap map[int][]*edbDataModel.EdbDataList, edbList []*chartEdbMappingModel.ChartEdbInfoMappingList, sourceArr []string, err error) {
|
|
|
+ // 关联指标来源
|
|
|
+ sourceArr = make([]string, 0)
|
|
|
+ // 指标对应的所有数据
|
|
|
+ edbDataListMap = make(map[int][]*edbDataModel.EdbDataList)
|
|
|
for _, v := range mappingList {
|
|
|
//fmt.Println("v:", v.EdbInfoId)
|
|
|
item := new(chartEdbMappingModel.ChartEdbInfoMappingList)
|
|
@@ -652,14 +727,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([]chart_info.YData, 0)
|
|
|
- }
|
|
|
-
|
|
|
return
|
|
|
}
|
|
|
|
|
@@ -950,3 +1017,297 @@ func ChartInfoRefreshV2(chartInfoId int) (err error) {
|
|
|
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
+// GetTimeSectionChartData 柱方图的数据处理
|
|
|
+func GetTimeSectionChartData(mappingList []*chartEdbMappingModel.ChartEdbInfoMapping, edbDataListMap map[int][]*edbDataModel.EdbDataList, extraConfig request.TimeSectionReq) (edbIdList []int, chartDataResp request.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]*chartEdbMappingModel.ChartEdbInfoMapping)
|
|
|
+ for _, v := range mappingList {
|
|
|
+ edbIdList = append(edbIdList, v.EdbInfoId)
|
|
|
+ edbMappingMap[v.EdbInfoId] = v
|
|
|
+ }
|
|
|
+ //TimeSectionSeriesInfoResp
|
|
|
+
|
|
|
+ dataListResp := make([]request.TimeSectionSeriesItemResp, 0) //y轴的数据列表
|
|
|
+
|
|
|
+ for _, seriesItem := range extraConfig.SeriesList {
|
|
|
+ var maxDate time.Time
|
|
|
+ // 系列中的指标数据
|
|
|
+ tmpSeriesEdbInfoList := make([]request.TimeSectionEdbItemResp, 0)
|
|
|
+
|
|
|
+ var minXVal, maxXVal, minYVal, maxYVal float64
|
|
|
+ for _, edbConf := range seriesItem.EdbInfoList {
|
|
|
+ tmpItem := request.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([]request.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, request.CoordinatePoint{
|
|
|
+ X: minXVal,
|
|
|
+ Y: minYVal,
|
|
|
+ }, request.CoordinatePoint{
|
|
|
+ X: maxXVal,
|
|
|
+ Y: maxYVal,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ dataListResp = append(dataListResp, request.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 = request.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
|
|
|
+}
|