Browse Source

Merge remote-tracking branch 'origin/15.0'

# Conflicts:
#	models/edb_data_base.go
#	utils/constants.go
Roc 1 year ago
parent
commit
0c758a689b

+ 86 - 0
controllers/chart_common.go

@@ -9,11 +9,14 @@ import (
 	"hongze/hongze_chart_lib/models/data_manage/future_good/request"
 	"hongze/hongze_chart_lib/models/data_manage/future_good/response"
 	line_equationReq "hongze/hongze_chart_lib/models/data_manage/line_equation/request"
+	line_featureReq "hongze/hongze_chart_lib/models/data_manage/line_feature/request"
 	"hongze/hongze_chart_lib/services/data"
 	correlationServ "hongze/hongze_chart_lib/services/data/correlation"
 	future_goodServ "hongze/hongze_chart_lib/services/data/future_good"
 	"hongze/hongze_chart_lib/services/data/line_equation"
+	lineFeatureServ "hongze/hongze_chart_lib/services/data/line_feature"
 	"hongze/hongze_chart_lib/utils"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -83,6 +86,8 @@ func (this *ChartController) CommonChartInfoDetailFromUniqueCode() {
 		resp, isOk, msg, errMsg = GetCorrelationChartInfoDetailFromUniqueCode(chartInfo, key)
 	case utils.CHART_SOURCE_LINE_EQUATION:
 		resp, isOk, msg, errMsg = GetLineEquationChartInfoDetailFromUniqueCode(chartInfo, key)
+	case utils.CHART_SOURCE_LINE_FEATURE_STANDARD_DEVIATION, utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE, utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY:
+		resp, isOk, msg, errMsg = GetLineFeatureChartInfoDetailFromUniqueCode(chartInfo, key)
 	default:
 		br.Msg = "错误的图表"
 		br.ErrMsg = "错误的图表"
@@ -363,6 +368,87 @@ func GetLineEquationChartInfoDetailFromUniqueCode(chartInfo *models.ChartInfo, k
 	return
 }
 
+// GetLineFeatureChartInfoDetailFromUniqueCode 根据编码获取统计特征图表详情
+func GetLineFeatureChartInfoDetailFromUniqueCode(chartInfo *models.ChartInfo, key string) (resp *models.ChartInfoDetailResp, isOk bool, msg, errMsg string) {
+	resp = new(models.ChartInfoDetailResp)
+	// 获取图表关联指标
+	edbMappingList, err := models.GetChartEdbMappingList(chartInfo.ChartInfoId)
+	if err != nil {
+		msg = "获取失败"
+		errMsg = "获取图表关联指标信息失败,Err:" + err.Error()
+		return
+	}
+	if len(edbMappingList) != 1 {
+		msg = "获取失败"
+		errMsg = fmt.Sprint("获取图表关联指标信息异常,数量:", len(edbMappingList))
+		return
+	}
+	edbMapping := edbMappingList[0]
+
+	var edbList []*models.ChartEdbInfoMapping
+	var resultResp interface{}
+
+	switch chartInfo.Source {
+	case utils.CHART_SOURCE_LINE_FEATURE_STANDARD_DEVIATION:
+		calculateValue, tmpErr := strconv.Atoi(chartInfo.ExtraConfig)
+		if tmpErr != nil {
+			msg = "获取失败"
+			errMsg = "格式化配置项失败,Err:" + tmpErr.Error()
+			return
+		}
+		edbList, resultResp, err, msg = lineFeatureServ.GetStandardDeviationData(0, edbMapping, calculateValue)
+	case utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE:
+		var percentileConfig line_featureReq.Percentile
+		err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &percentileConfig)
+		if err != nil {
+			msg = "获取失败"
+			errMsg = "格式化配置项失败,Err:" + err.Error()
+			return
+		}
+		edbList, resultResp, err, msg = lineFeatureServ.GetPercentileData(0, edbMapping, percentileConfig.CalculateValue, percentileConfig.CalculateUnit)
+	case utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY:
+		var frequencyDistributionConfig line_featureReq.FrequencyDistribution
+		err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &frequencyDistributionConfig)
+		if err != nil {
+			msg = "获取失败"
+			errMsg = "格式化配置项失败,Err:" + err.Error()
+			return
+		}
+		// 获取图表中的指标数据
+		edbList, resultResp, err, errMsg = lineFeatureServ.GetFrequencyDistributionData(0, edbMapping, frequencyDistributionConfig.DateType, frequencyDistributionConfig.FrequencyValue, frequencyDistributionConfig.StartDate, frequencyDistributionConfig.EndDate)
+	default:
+		msg = `错误的图表`
+		errMsg = fmt.Sprint("错误的图表来源,source", chartInfo.Source)
+		return
+	}
+	if err != nil {
+		if msg == `` {
+			msg = "获取失败"
+		}
+		errMsg = "获取图表,指标信息失败,Err:" + err.Error()
+		return
+	}
+
+	//chartInfo.UnitEn = baseEdbInfo.UnitEn
+	// 图表的指标来源
+	sourceNameList, sourceNameEnList := data.GetEdbSourceByEdbInfoIdList(edbList)
+	chartInfo.ChartSource = strings.Join(sourceNameList, ",")
+	chartInfo.ChartSourceEn = strings.Join(sourceNameEnList, ",")
+
+	resp.ChartInfo = chartInfo
+	resp.DataResp = resultResp
+	resp.EdbInfoList = edbList
+
+	// 将数据加入缓存
+	if utils.Re == nil {
+		data, _ := json.Marshal(resp)
+		utils.Rc.Put(key, data, 2*time.Hour)
+	}
+	isOk = true
+
+	return
+}
+
 // FutureGoodChartInfoRefresh
 // @Title 商品价格图表刷新接口
 // @Description 商品价格图表刷新接口

+ 6 - 0
go.mod

@@ -6,6 +6,12 @@ require (
 	github.com/beego/bee/v2 v2.0.2
 	github.com/beego/beego/v2 v2.0.2
 	github.com/go-sql-driver/mysql v1.6.0
+	github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac // indirect
+	github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect
+	github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // indirect
+	github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 // indirect
+	github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 // indirect
+	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b // indirect
 	github.com/google/go-cmp v0.5.6 // indirect
 	github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19
 	github.com/rdlucklib/rdluck_tools v1.0.2

+ 12 - 0
go.sum

@@ -176,6 +176,18 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
 github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac h1:Q0Jsdxl5jbxouNs1TQYt0gxesYMU4VXRbsTlgDloZ50=
+github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
+github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 h1:EvokxLQsaaQjcWVWSV38221VAK7qc2zhaO17bKys/18=
+github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=
+github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 h1:8jtTdc+Nfj9AR+0soOeia9UZSvYBvETVHZrugUowJ7M=
+github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=
+github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 h1:7qnwS9+oeSiOIsiUMajT+0R7HR6hw5NegnKPmn/94oI=
+github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=
+github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 h1:V2IgdyerlBa/MxaEFRbV5juy/C3MGdj4ePi+g6ePIp4=
+github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
+github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b h1:fbskpz/cPqWH8VqkQ7LJghFkl2KPAiIFUHrTJ2O3RGk=
+github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=

+ 8 - 0
models/data_manage/edb_data_base.go

@@ -135,6 +135,14 @@ func GetEdbDataTableName(source int) (tableName string) {
 		tableName = "edb_data_predict_calculate_ljz"
 	case utils.DATA_SOURCE_PREDICT_CALCULATE_LJZNCZJ: //预测指标 - 累计值(年初至今) -> 66
 		tableName = "edb_data_predict_calculate_ljznczj"
+	case utils.DATA_SOURCE_CALCULATE_STANDARD_DEVIATION: //标准差->67
+		tableName = "edb_data_calculate_standard_deviation"
+	case utils.DATA_SOURCE_CALCULATE_PERCENTILE: //百分位->68
+		tableName = "edb_data_calculate_percentile"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_STANDARD_DEVIATION: //预测标准差->69
+		tableName = "edb_data_predict_ccalculate_standard_deviation"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_PERCENTILE: //预测百分位->70
+		tableName = "edb_data_predict_ccalculate_percentile"
 	default:
 		tableName = ""
 	}

+ 107 - 0
models/data_manage/line_feature/request/line_feature.go

@@ -0,0 +1,107 @@
+package request
+
+// SaveMultipleGraphConfigReq 多图配置请求
+type SaveMultipleGraphConfigReq struct {
+	MultipleGraphConfigId int                   `description:"配置id"`
+	EdbInfoId             int                   `description:"指标"`
+	Curve                 CurveConfig           `description:"曲线图配置"`
+	StandardDeviation     StandardDeviation     `description:"标准差配置"`
+	Percentile            Percentile            `description:"百分位配置"`
+	FrequencyDistribution FrequencyDistribution `description:"频率分布配置"`
+}
+
+// ConfigSave 数据库保存
+type ConfigSave struct {
+	Curve                 CurveConfig           `description:"曲线图配置"`
+	StandardDeviation     StandardDeviation     `description:"标准差配置"`
+	Percentile            Percentile            `description:"百分位配置"`
+	FrequencyDistribution FrequencyDistribution `description:"频率分布配置"`
+}
+
+// CurveConfig 曲线图配置
+type CurveConfig struct {
+	DateType  int     `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间"`
+	StartDate string  `description:"自定义开始日期"`
+	EndDate   string  `description:"自定义结束日期"`
+	LeftMin   float64 `description:"图表左侧最小值"`
+	LeftMax   float64 `description:"图表左侧最大值"`
+}
+
+type StandardDeviation struct {
+	CalculateValue int `description:"滚动期数"`
+}
+
+type Percentile struct {
+	CalculateValue int    `description:"时间长度期数"`
+	CalculateUnit  string `description:"时间长度频度"`
+}
+
+type FrequencyDistribution struct {
+	//最近3月 最近6月 最近1年 最近2年 最近3年 最近5年 最近10年
+	DateType       int    `description:"日期类型:1:最近3月;2:最近6月;3:最近1年;4:最近2年;5:最近3年;6:最近5年;7:最近10年,8:自定义时间"`
+	StartDate      string `description:"自定义开始日期"`
+	EndDate        string `description:"自定义结束日期"`
+	FrequencyValue int    `description:"频段数,10/20"`
+}
+
+// LineChartInfoReq 线性拟合图表预览请求数据
+type LineChartInfoReq struct {
+	DateType       int    `description:"日期类型"`
+	StartDate      string `description:"开始日期"`
+	EndDate        string `description:"结束日期"`
+	XEdbInfoIdList []int  `description:"X轴的指标id列表"`
+	YEdbInfoIdList []int  `description:"Y轴的指标id列表"`
+	Source         int    `description:"来源,1:曲线图,8:标准差图表;9:百分位图表;10:频率分布图表;"`
+}
+
+type AddChartInfoReq struct {
+	MultipleGraphConfigId int              `description:"统一配置id,有的话就是编辑,没有则是新增"`
+	BatchAddChart         []AddChart       `description:"批量创建图的信息"`
+	ExtraConfig           LineChartInfoReq `description:"图表额外配置信息"`
+}
+
+type AddChart struct {
+	Source          int    `description:"来源,1:曲线图,8:标准差图表;9:百分位图表;10:频率分布图表;"`
+	ChartClassifyId int    `description:"分类id"`
+	ChartName       string `description:"图表名称"`
+	LeftMin         string `description:"图表左侧最小值"`
+	LeftMax         string `description:"图表左侧最大值"`
+	ChartImage      string `description:"图表截图,复制的时候才用到" json:"-"`
+}
+
+// EditChartEnInfoReq 编辑图表英文信息
+type EditChartEnInfoReq struct {
+	ChartInfoId int    `description:"图表ID"`
+	ChartNameEn string `description:"英文图表名称"`
+}
+
+// SaveMultipleGraphChartReq 多图配置的单图保存请求
+type SaveMultipleGraphChartReq struct {
+	Source                int                   `description:"来源,1:曲线图,8:标准差图表;9:百分位图表;10:频率分布图表;"`
+	ChartName             string                `description:"图表名称"`
+	ClassifyId            int                   `description:"分类id"`
+	MultipleGraphConfigId int                   `description:"配置id"`
+	EdbInfoId             int                   `description:"指标"`
+	Curve                 CurveConfig           `description:"曲线图配置"`
+	StandardDeviation     StandardDeviation     `description:"标准差配置"`
+	Percentile            Percentile            `description:"百分位配置"`
+	FrequencyDistribution FrequencyDistribution `description:"频率分布配置"`
+	IsSaveAs              bool                  `description:"是否另存为,true的话,就是另存为,不会建立与配置的关系"`
+}
+
+// SaveMultipleGraphEdbReq 多图配置的单指标保存请求
+type SaveMultipleGraphEdbReq struct {
+	EdbName    string `description:"指标名称"`
+	Frequency  string `description:"频度"`
+	Unit       string `description:"单位"`
+	ClassifyId int    `description:"分类id"`
+
+	MultipleGraphConfigId int                   `description:"配置id"`
+	Source                int                   `description:"来源,1:曲线图,8:标准差图表;9:百分位图表;10:频率分布图表;"`
+	EdbInfoId             int                   `description:"指标"`
+	Curve                 CurveConfig           `description:"曲线图配置"`
+	StandardDeviation     StandardDeviation     `description:"标准差配置"`
+	Percentile            Percentile            `description:"百分位配置"`
+	FrequencyDistribution FrequencyDistribution `description:"频率分布配置"`
+	IsSaveAs              bool                  `description:"是否另存为,true的话,就是另存为,不会建立与配置的关系"`
+}

+ 48 - 0
models/data_manage/line_feature/response/line_feature.go

@@ -0,0 +1,48 @@
+package response
+
+import "hongze/hongze_chart_lib/models"
+
+// LineFeatureDataResp 曲线图的一些数据返回
+type LineFeatureDataResp struct {
+	MaxData             float64
+	MinData             float64
+	LatestDate          string `description:"真实数据的最后日期"`
+	EdbInfoCategoryType int
+	ChartColor          string
+	ChartStyle          string
+	PredictChartColor   string
+	ChartType           int
+	ChartWidth          float64
+	EdbName             string
+	EdbNameEn           string
+	Unit                string
+	UnitEn              string
+	IsAxis              int
+	DataList            []models.EdbDataList
+}
+
+// FrequencyDistributionResp 频率分布图数据
+type FrequencyDistributionResp struct {
+	LeftMinValue  float64
+	LeftMaxValue  float64
+	RightMinValue float64
+	RightMaxValue float64
+	DataList      []FrequencyDistributionData
+}
+
+// FrequencyDistributionData 频率分布的值
+type FrequencyDistributionData struct {
+	Name   string      `description:"别名"`
+	NameEn string      `description:"英文别名"`
+	Unit   string      `description:"单位"`
+	UnitEn string      `description:"单位别名"`
+	Value  interface{} `description:"每个指标的值"`
+	Color  string      `description:"数据颜色"`
+	IsAxis int         `description:"1:左轴,0:右轴"`
+}
+
+// FrequencyDistributionYData 频率分布的实际数据
+type FrequencyDistributionYData struct {
+	X float64
+	Y float64
+}

+ 8 - 0
models/edb_data_base.go

@@ -134,6 +134,14 @@ func GetEdbDataTableName(source int) (tableName string) {
 		tableName = "edb_data_predict_calculate_ljz"
 	case utils.DATA_SOURCE_PREDICT_CALCULATE_LJZNCZJ: //预测指标 - 累计值(年初至今) -> 66
 		tableName = "edb_data_predict_calculate_ljznczj"
+	case utils.DATA_SOURCE_CALCULATE_STANDARD_DEVIATION: //标准差->67
+		tableName = "edb_data_calculate_standard_deviation"
+	case utils.DATA_SOURCE_CALCULATE_PERCENTILE: //百分位->68
+		tableName = "edb_data_calculate_percentile"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_STANDARD_DEVIATION: //预测标准差->69
+		tableName = "edb_data_predict_ccalculate_standard_deviation"
+	case utils.DATA_SOURCE_PREDICT_CALCULATE_PERCENTILE: //预测百分位->70
+		tableName = "edb_data_predict_ccalculate_percentile"
 	case utils.DATA_SOURCE_FUBAO: //富宝 -> 71
 		tableName = "edb_data_fubao"
 	default:

+ 349 - 0
services/data/line_feature/chart_info.go

@@ -0,0 +1,349 @@
+package line_feature
+
+import (
+	"errors"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_chart_lib/models"
+	"hongze/hongze_chart_lib/models/data_manage/line_feature/response"
+	"hongze/hongze_chart_lib/services/data"
+	"hongze/hongze_chart_lib/utils"
+	"time"
+)
+
+// GetStandardDeviationData 获取标准差图表的指标数据
+func GetStandardDeviationData(chartInfoId int, mappingInfo *models.ChartEdbInfoMapping, calculateValue int) (edbList []*models.ChartEdbInfoMapping, dataResp response.LineFeatureDataResp, err error, errMsg string) {
+	edbList = make([]*models.ChartEdbInfoMapping, 0)
+
+	// 指标对应的所有数据
+	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, ``, ``, []*models.ChartEdbInfoMapping{mappingInfo})
+	if err != nil {
+		return
+	}
+
+	if len(edbList) != 1 {
+		errMsg = `指标异常`
+		err = errors.New(errMsg)
+		return
+	}
+
+	edb := edbList[0]
+	dataList := edb.DataList.([]*models.EdbDataList)
+	newDataList := make([]models.EdbDataList, 0)
+	lenData := len(dataList)
+
+	var minVal, maxVal float64
+	if lenData >= calculateValue {
+		tmpDataList := make([]float64, 0)
+		for _, tmpData := range dataList {
+			tmpDataList = append(tmpDataList, tmpData.Value)
+		}
+		for i := calculateValue; i <= lenData; i++ {
+			tmpV := utils.CalculateStandardDeviation(tmpDataList[i-calculateValue : i])
+			tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64()
+			newDataList = append(newDataList, models.EdbDataList{
+				EdbDataId:     i,
+				EdbInfoId:     edb.EdbInfoId,
+				DataTime:      dataList[i-1].DataTime,
+				DataTimestamp: dataList[i-1].DataTimestamp,
+				Value:         tmpV,
+			})
+			if tmpV > maxVal {
+				maxVal = tmpV
+			}
+			if tmpV < minVal {
+				minVal = tmpV
+			}
+		}
+	}
+
+	dataResp = response.LineFeatureDataResp{
+		MaxData:             maxVal,
+		MinData:             minVal,
+		LatestDate:          edb.LatestDate,
+		EdbInfoCategoryType: edb.EdbInfoCategoryType,
+		ChartColor:          `#00F`,
+		ChartStyle:          `spline`,
+		PredictChartColor:   `#00F`,
+		ChartType:           0,
+		ChartWidth:          3,
+		EdbName:             "标准差",
+		EdbNameEn:           "standard deviation",
+		Unit:                edb.Unit,
+		UnitEn:              edb.UnitEn,
+		IsAxis:              1,
+		DataList:            newDataList,
+	}
+
+	return
+}
+
+// GetPercentileData 获取百分位图表的指标数据
+func GetPercentileData(chartInfoId int, mappingInfo *models.ChartEdbInfoMapping, calculateValue int, calculateUnit string) (edbList []*models.ChartEdbInfoMapping, dataResp response.LineFeatureDataResp, err error, errMsg string) {
+	edbList = make([]*models.ChartEdbInfoMapping, 0)
+	moveUnitDays, ok := utils.FrequencyDaysMap[calculateUnit]
+	if !ok {
+		errMsg = `错误的周期`
+		err = errors.New(errMsg)
+		return
+	}
+	calculateDay := calculateValue * moveUnitDays
+	// 指标对应的所有数据
+	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, ``, ``, []*models.ChartEdbInfoMapping{mappingInfo})
+	if err != nil {
+		return
+	}
+
+	if len(edbList) != 1 {
+		errMsg = `指标异常`
+		err = errors.New(errMsg)
+		return
+	}
+
+	edb := edbList[0]
+	dataList := edb.DataList.([]*models.EdbDataList)
+	newDataList := make([]models.EdbDataList, 0)
+
+	var edbMinVal, edbMaxVal float64
+	dataMap := make(map[time.Time]float64, 0)
+	for _, tmpData := range dataList {
+		currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local)
+		dataMap[currDateTime] = tmpData.Value
+	}
+
+	//百分位:对所选指标滚动地取对应时间长度的数据值,取最大值Max,最小值Min,计算Max-Min,百分位=(现值-Min)/(Max-Min),Max=Min时不予计算。
+	for i, tmpData := range dataList {
+		currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local)
+		maxVal := tmpData.Value
+		minVal := tmpData.Value
+		for k := 0; k < calculateDay; k++ {
+			preVal, ok2 := dataMap[currDateTime.AddDate(0, 0, -k)]
+			if ok2 {
+				if preVal > maxVal {
+					maxVal = preVal
+				}
+				if preVal < minVal {
+					minVal = preVal
+				}
+			}
+		}
+
+		if maxVal == minVal {
+			continue
+		}
+		//百分位=(现值-Min)/(Max-Min)
+		tmpV := (tmpData.Value - minVal) / (maxVal - minVal) * 100
+		tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64()
+		newDataList = append(newDataList, models.EdbDataList{
+			EdbDataId:     i,
+			EdbInfoId:     edb.EdbInfoId,
+			DataTime:      dataList[i].DataTime,
+			DataTimestamp: dataList[i].DataTimestamp,
+			Value:         tmpV,
+		})
+
+		if tmpV < edbMinVal {
+			edbMinVal = tmpV
+		}
+		if tmpV > edbMaxVal {
+			edbMaxVal = tmpV
+		}
+	}
+
+	dataResp = response.LineFeatureDataResp{
+		MaxData:             edbMaxVal,
+		MinData:             edbMinVal,
+		LatestDate:          edb.LatestDate,
+		EdbInfoCategoryType: edb.EdbInfoCategoryType,
+		ChartColor:          `#00F`,
+		ChartStyle:          `spline`,
+		PredictChartColor:   `#00F`,
+		ChartType:           0,
+		ChartWidth:          3,
+		EdbName:             "百分位",
+		EdbNameEn:           "percentile",
+		Unit:                "%",
+		UnitEn:              "%",
+		IsAxis:              1,
+		DataList:            newDataList,
+	}
+
+	return
+}
+
+// GetFrequencyDistributionData 获取频率分布的图表数据
+func GetFrequencyDistributionData(chartInfoId int, mappingInfo *models.ChartEdbInfoMapping, dateType, stepVal int, startDate, endDate string) (edbList []*models.ChartEdbInfoMapping, dataResp response.FrequencyDistributionResp, err error, errMsg string) {
+	XDataList := make([]float64, 0)
+	// 频度
+	Y1DataList := make([]response.FrequencyDistributionYData, 0)
+	// 累计频率
+	Y2DataList := make([]response.FrequencyDistributionYData, 0)
+	edbList = make([]*models.ChartEdbInfoMapping, 0)
+
+	// 指标对应的所有数据
+	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*models.ChartEdbInfoMapping{mappingInfo})
+	if err != nil {
+		return
+	}
+	if len(edbList) != 1 {
+		err = errors.New("指标异常")
+		return
+	}
+	startDateTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+	var endDateTime time.Time
+	if endDate != `` {
+		endDateTime, _ = time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+	}
+
+	edb := edbList[0]
+	dataList := edb.DataList.([]*models.EdbDataList)
+	if len(dataList) <= 0 {
+		return
+	}
+
+	// 非自定义
+	if dateType != 8 {
+		endDate = dataList[len(dataList)-1].DataTime
+		endDateTime, err = time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+		if err != nil {
+			return
+		}
+		//日期类型:1:最近3月;2:最近6月;3:最近1年;4:最近2年;5:最近3年;6:最近5年;7:最近10年,8:自定义时间
+		startDateTime = utils.GetDateByDateType2(dateType, endDateTime)
+		startDate = startDateTime.Format(utils.FormatDate)
+
+		newDataList := make([]*models.EdbDataList, 0)
+		for _, v := range dataList {
+			tmpDataTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			if tmpDataTime.Equal(startDateTime) || tmpDataTime.After(startDateTime) {
+				newDataList = append(newDataList, v)
+			}
+
+		}
+		dataList = newDataList
+	}
+
+	maxVal := dataList[0].Value
+	minVal := dataList[0].Value
+	dataValMap := make(map[float64]int)
+	total := 0 // 数据总量
+	for _, tmpData := range dataList {
+		currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local)
+		if (currDateTime.Equal(startDateTime) || currDateTime.After(startDateTime)) && (endDateTime.IsZero() || currDateTime.Before(endDateTime)) {
+			if maxVal < tmpData.Value {
+				maxVal = tmpData.Value
+			}
+			if minVal > tmpData.Value {
+				minVal = tmpData.Value
+			}
+
+			num, ok := dataValMap[tmpData.Value]
+			if ok {
+				dataValMap[tmpData.Value] = num + 1
+			} else {
+				dataValMap[tmpData.Value] = 1
+			}
+			total++
+		}
+	}
+
+	if total <= 0 {
+		errMsg = `没有数据`
+		err = errors.New(errMsg)
+		return
+	}
+
+	// 最大最小值 向上/下取整
+	minVal = utils.GetFloorNewNum(minVal, 2)
+	maxVal = utils.GetCeilNewNum(maxVal, 2)
+	//间距
+	spacing, _ := (decimal.NewFromFloat(maxVal).Sub(decimal.NewFromFloat(minVal))).Div(decimal.NewFromInt(int64(stepVal))).Float64()
+
+	distributionDataNumMap := make(map[float64]int)
+	for i := 1; i <= stepVal; i++ {
+		tmpMinVal, _ := decimal.NewFromFloat(minVal).Add((decimal.NewFromFloat(spacing)).Mul(decimal.NewFromInt(int64(i - 1)))).Float64()
+		tmpMaxVal, _ := decimal.NewFromFloat(minVal).Add((decimal.NewFromFloat(spacing)).Mul(decimal.NewFromInt(int64(i)))).Float64()
+		XDataList = append(XDataList, tmpMinVal)
+		distributionDataNumMap[tmpMinVal] = 0
+		for tmpVal, num := range dataValMap {
+			if tmpMinVal <= tmpVal {
+				// 最后一期数据是要小于等于
+				if i == stepVal {
+					if tmpVal <= tmpMaxVal {
+						distributionDataNumMap[tmpMinVal] += num
+					}
+				} else {
+					if tmpVal < tmpMaxVal {
+						distributionDataNumMap[tmpMinVal] += num
+					}
+				}
+			}
+		}
+	}
+
+	var minFrequency, maxFrequency float64
+	tmpNum := 0
+	for k, tmpMinVal := range XDataList {
+		// 数量
+		frequencyYNum := distributionDataNumMap[tmpMinVal]
+
+		// 频率
+		tmpFrequency, _ := decimal.NewFromInt(int64(frequencyYNum)).Div(decimal.NewFromInt(int64(total))).Mul(decimal.NewFromInt(100)).Round(4).Float64()
+		Y1DataList = append(Y1DataList, response.FrequencyDistributionYData{
+			X: tmpMinVal,
+			Y: tmpFrequency,
+		})
+		if k == 0 {
+			minFrequency = tmpFrequency
+			maxFrequency = tmpFrequency
+		} else {
+			if tmpFrequency < minFrequency {
+				minFrequency = tmpFrequency
+			}
+			if tmpFrequency > maxFrequency {
+				maxFrequency = tmpFrequency
+			}
+		}
+
+		// 累计数
+		tmpNum += frequencyYNum
+		// 累计频率
+		tmpTotalFrequency, _ := decimal.NewFromInt(int64(tmpNum)).Div(decimal.NewFromInt(int64(total))).Mul(decimal.NewFromInt(100)).Round(4).Float64()
+		Y2DataList = append(Y2DataList, response.FrequencyDistributionYData{
+			X: tmpMinVal,
+			Y: tmpTotalFrequency,
+		})
+	}
+
+	newDataList := []response.FrequencyDistributionData{
+		{
+			Name:   "频率",
+			NameEn: "Frequency",
+			Unit:   "%",
+			UnitEn: "%",
+			Value:  Y1DataList,
+			Color:  "#00F",
+			IsAxis: 1,
+		}, {
+			Name:   "累计频率",
+			NameEn: "Total Frequency",
+			Unit:   "%",
+			UnitEn: "%",
+			Value:  Y2DataList,
+			Color:  "#F00",
+			IsAxis: 0,
+		},
+	}
+	edbList[0].DataList = nil
+	dataResp = response.FrequencyDistributionResp{
+		LeftMinValue:  minFrequency,
+		LeftMaxValue:  maxFrequency,
+		RightMinValue: 0,
+		RightMaxValue: 100,
+		DataList:      newDataList,
+	}
+	return
+}

+ 6 - 0
utils/calculate.go

@@ -1,6 +1,7 @@
 package utils
 
 import (
+	"github.com/gonum/stat"
 	"github.com/shopspring/decimal"
 	"math"
 )
@@ -161,3 +162,8 @@ func CalculationDecisive(sList []Coordinate) (r2 float64) {
 
 	return
 }
+
+// CalculateStandardDeviation 计算标准差
+func CalculateStandardDeviation(data []float64) float64 {
+	return stat.StdDev(data, nil)
+}

+ 208 - 0
utils/common.go

@@ -8,6 +8,7 @@ import (
 	"encoding/hex"
 	"encoding/json"
 	"fmt"
+	"github.com/shopspring/decimal"
 	"image"
 	"image/png"
 	"io"
@@ -781,3 +782,210 @@ func GetDateByDateType(dateType int, tmpStartDate, tmpEndDate string) (startDate
 
 	return
 }
+
+// GetDateByDateType2 通过dateType获取需要的开始/结束日期(日期类型:1:最近3月;2:最近6月;3:最近1年;4:最近2年;5:最近3年;6:最近5年;7:最近10年,8:自定义时间)
+func GetDateByDateType2(dateType int, currDate time.Time) (startDate time.Time) {
+	switch dateType {
+	case 1:
+		startDate = currDate.AddDate(0, -3, 0)
+	case 2:
+		startDate = currDate.AddDate(0, -6, 0)
+	case 3:
+		startDate = currDate.AddDate(-1, 0, 0)
+	case 4:
+		startDate = currDate.AddDate(-2, 0, 0)
+	case 5:
+		startDate = currDate.AddDate(-3, 0, 0)
+	case 6:
+		startDate = currDate.AddDate(-5, 0, 0)
+	case 7:
+		startDate = currDate.AddDate(-10, 0, 0)
+	}
+
+	return
+}
+
+// GetCeilNewNum 保留n位有效数字的向上取整
+// @params num 实际数据
+// @params baseLen 需要保留的有效位数
+func GetCeilNewNum(num float64, baseLen int) (newNum float64) {
+	if num >= 1 {
+		tmpNum := int(math.Ceil(num)) // 向上取整
+		str := strconv.Itoa(tmpNum)
+		lenStr := len(str)
+
+		if lenStr > baseLen {
+			newNumStr := str[0:baseLen]
+			newNumInt, _ := strconv.Atoi(newNumStr)
+			newNum = float64(newNumInt) * math.Pow(10, float64(lenStr-baseLen))
+			if newNum < num {
+				newNumInt += 1
+				newNum = float64(newNumInt) * math.Pow(10, float64(lenStr-baseLen))
+			}
+		} else {
+			newNum = float64(tmpNum)
+		}
+		return
+	} else if num > 0 {
+		// 这是小数
+		str := strconv.FormatFloat(num, 'f', -1, 64)
+		// 去除小数点和负号
+		str = removeDecimalPoint(str)
+		// 计算字符串长度
+		lenStr := len(str)
+
+		if lenStr > baseLen {
+			newNumStr := str[0:baseLen]
+			newNumInt, _ := strconv.Atoi(newNumStr)
+			newNum, _ = decimal.NewFromInt(int64(newNumInt)).Div(decimal.NewFromFloat(math.Pow(10, float64(baseLen)))).Float64()
+			if newNum < num {
+				newNumInt += 1
+				newNum, _ = decimal.NewFromInt(int64(newNumInt)).Div(decimal.NewFromFloat(math.Pow(10, float64(baseLen)))).Float64()
+			}
+		} else {
+			newNum = num
+		}
+	} else if num > -1 {
+		// 这是小数
+		str := strconv.FormatFloat(num, 'f', -1, 64)
+		// 去除小数点和负号
+		str = removeDecimalPoint(str)
+		// 计算字符串长度
+		lenStr := len(str)
+
+		if lenStr > baseLen {
+			newNumStr := str[0:baseLen]
+			newNumInt, _ := strconv.Atoi(newNumStr)
+			newNum, _ = decimal.NewFromInt(int64(newNumInt)).Div(decimal.NewFromFloat(math.Pow(10, float64(baseLen)))).Float64()
+			newNum = -newNum
+			if newNum < num {
+				newNumInt -= 1
+				newNum, _ = decimal.NewFromInt(int64(newNumInt)).Div(decimal.NewFromFloat(math.Pow(10, float64(baseLen)))).Float64()
+				newNum = -newNum
+			}
+		} else {
+			newNum = num
+		}
+		if newNum == -0 {
+			newNum = 0
+		}
+	} else { // 小于等于-1
+		tmpNumFloat := math.Abs(num)
+		tmpNum := int(math.Floor(tmpNumFloat)) // 向上取整
+		str := strconv.Itoa(tmpNum)
+		lenStr := len(str)
+
+		if lenStr > baseLen {
+			newNumStr := str[0:baseLen]
+			//fmt.Println("newNumStr:", newNumStr)
+			newNumInt, _ := strconv.Atoi(newNumStr)
+			newNum = float64(newNumInt) * math.Pow(10, float64(lenStr-baseLen))
+			newNum = -newNum
+			if newNum < num {
+				newNumInt -= 1
+				newNum = float64(newNumInt) * math.Pow(10, float64(lenStr-baseLen))
+				newNum = -newNum
+			}
+		} else {
+			newNum = float64(-tmpNum)
+		}
+	}
+
+	return
+}
+
+// GetFloorNewNum 保留n位有效数字的向下取整
+// @params num 实际数据
+// @params baseLen 需要保留的有效位数
+func GetFloorNewNum(num float64, baseLen int) (newNum float64) {
+	if num >= 1 {
+		tmpNum := int(math.Floor(num)) // 向上取整
+		str := strconv.Itoa(tmpNum)
+		lenStr := len(str)
+
+		if lenStr > baseLen {
+			newNumStr := str[0:baseLen]
+			newNumInt, _ := strconv.Atoi(newNumStr)
+			newNum = float64(newNumInt) * math.Pow(10, float64(lenStr-baseLen))
+			if newNum < num {
+				newNumInt -= 1
+				newNum = float64(newNumInt) * math.Pow(10, float64(lenStr-baseLen))
+			}
+		} else {
+			newNum = float64(tmpNum)
+		}
+		return
+	} else if num > 0 {
+		// 这是小数
+		str := strconv.FormatFloat(num, 'f', -1, 64)
+		// 去除小数点和负号
+		str = removeDecimalPoint(str)
+		// 计算字符串长度
+		lenStr := len(str)
+
+		if lenStr > baseLen {
+			newNumStr := str[0:baseLen]
+			newNumInt, _ := strconv.Atoi(newNumStr)
+			newNum, _ = decimal.NewFromInt(int64(newNumInt)).Div(decimal.NewFromFloat(math.Pow(10, float64(baseLen)))).Float64()
+			if newNum > num {
+				newNumInt -= 1
+				newNum, _ = decimal.NewFromInt(int64(newNumInt)).Div(decimal.NewFromFloat(math.Pow(10, float64(baseLen)))).Float64()
+			}
+		} else {
+			newNum = num
+		}
+	} else if num > -1 {
+		// 这是小数
+		str := strconv.FormatFloat(num, 'f', -1, 64)
+		// 去除小数点和负号
+		str = removeDecimalPoint(str)
+		// 计算字符串长度
+		lenStr := len(str)
+
+		if lenStr > baseLen {
+			newNumStr := str[0:baseLen]
+			newNumInt, _ := strconv.Atoi(newNumStr)
+			newNum, _ = decimal.NewFromInt(int64(newNumInt)).Div(decimal.NewFromFloat(math.Pow(10, float64(baseLen)))).Float64()
+			newNum = -newNum
+			if newNum > num {
+				newNumInt += 1
+				newNum, _ = decimal.NewFromInt(int64(newNumInt)).Div(decimal.NewFromFloat(math.Pow(10, float64(baseLen)))).Float64()
+				newNum = -newNum
+			}
+		} else {
+			newNum = num
+		}
+		if newNum == -0 {
+			newNum = 0
+		}
+	} else { // 小于等于-1
+		tmpNumFloat := math.Abs(num)
+		tmpNum := int(math.Ceil(tmpNumFloat)) // 向上取整
+		str := strconv.Itoa(tmpNum)
+		lenStr := len(str)
+
+		if lenStr > baseLen {
+			newNumStr := str[0:baseLen]
+			//fmt.Println("newNumStr:", newNumStr)
+			newNumInt, _ := strconv.Atoi(newNumStr)
+			newNum = float64(newNumInt) * math.Pow(10, float64(lenStr-baseLen))
+			newNum = -newNum
+			if newNum > num {
+				newNumInt += 1
+				newNum = float64(newNumInt) * math.Pow(10, float64(lenStr-baseLen))
+				newNum = -newNum
+			}
+		} else {
+			newNum = float64(-tmpNum)
+		}
+	}
+
+	return
+}
+
+// 去除小数点和负号
+func removeDecimalPoint(str string) string {
+	// 去除小数点
+	str = str[strings.Index(str, ".")+1:]
+	return str
+}