Browse Source

feat:统计特征图

Roc 1 year ago
parent
commit
4e2f346110

+ 3 - 1
.gitignore

@@ -2,4 +2,6 @@
 /conf/
 /hongze_ETA_mobile_api
 /binlog/
-/.idea
+/.idea
+/.idea/
+/.DS_Store

+ 0 - 8
.idea/.gitignore

@@ -1,8 +0,0 @@
-# 默认忽略的文件
-/shelf/
-/workspace.xml
-# 基于编辑器的 HTTP 客户端请求
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml

+ 0 - 1
.idea/.name

@@ -1 +0,0 @@
-hongze_ETA_mobile_api

+ 0 - 4
.idea/awesomeProject.iml

@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module version="4">
-  <component name="Go" enabled="true" />
-</module>

+ 1 - 5
.idea/hongze_ETA_mobile_api.iml

@@ -2,11 +2,7 @@
 <module type="WEB_MODULE" version="4">
   <component name="Go" enabled="true" />
   <component name="NewModuleRootManager">
-    <content url="file://$MODULE_DIR$">
-      <excludeFolder url="file://$MODULE_DIR$/binlog" />
-      <excludeFolder url="file://$MODULE_DIR$/conf" />
-      <excludeFolder url="file://$MODULE_DIR$/rdlucklog" />
-    </content>
+    <content url="file://$MODULE_DIR$" />
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
   </component>

+ 1 - 1
.idea/modules.xml

@@ -2,7 +2,7 @@
 <project version="4">
   <component name="ProjectModuleManager">
     <modules>
-      <module fileurl="file://$PROJECT_DIR$/../hongze_ETA_mobile_api/.idea/hongze_ETA_mobile_api.iml" filepath="$PROJECT_DIR$/../hongze_ETA_mobile_api/.idea/hongze_ETA_mobile_api.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/hongze_ETA_mobile_api.iml" filepath="$PROJECT_DIR$/.idea/hongze_ETA_mobile_api.iml" />
     </modules>
   </component>
 </project>

+ 1 - 1
.idea/vcs.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="VcsDirectoryMappings">
-    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+    <mapping directory="" vcs="Git" />
   </component>
 </project>

+ 180 - 0
controllers/data_manage/chart_common.go

@@ -1,11 +1,19 @@
 package data_manage
 
 import (
+	"encoding/json"
+	"fmt"
 	"hongze/hongze_ETA_mobile_api/controllers/data_manage/correlation"
 	"hongze/hongze_ETA_mobile_api/controllers/data_manage/future_good"
 	"hongze/hongze_ETA_mobile_api/models"
 	"hongze/hongze_ETA_mobile_api/models/data_manage"
+	"hongze/hongze_ETA_mobile_api/models/data_manage/line_feature/request"
+	"hongze/hongze_ETA_mobile_api/models/system"
+	"hongze/hongze_ETA_mobile_api/services/data"
+	lineFeatureServ "hongze/hongze_ETA_mobile_api/services/data/line_feature"
 	"hongze/hongze_ETA_mobile_api/utils"
+	"strconv"
+	"time"
 )
 
 // CommonChartInfoDetailFromUniqueCode
@@ -114,9 +122,181 @@ func (this *ChartInfoController) CommonChartInfoDetailFromUniqueCode() {
 		br.Success = true
 		br.Msg = "获取成功"
 		br.Data = resp
+	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, isCache, sysUser)
+		if !isOk {
+			br.Msg = msg
+			br.ErrMsg = errMsg
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
 	default:
 		br.Msg = "错误的图表"
 		br.ErrMsg = "错误的图表"
 		return
 	}
 }
+
+// GetLineFeatureChartInfoDetailFromUniqueCode 根据编码获取统计特征图表详情
+func GetLineFeatureChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCache bool, sysUser *system.Admin) (resp *data_manage.ChartInfoDetailFromUniqueCodeResp, isOk bool, msg, errMsg string) {
+	resp = new(data_manage.ChartInfoDetailFromUniqueCodeResp)
+
+	adminId := sysUser.AdminId
+
+	//判断是否存在缓存,如果存在缓存,那么直接从缓存中获取
+	key := data.GetChartInfoDataKey(chartInfo.ChartInfoId)
+	if utils.Re == nil && isCache {
+		if utils.Re == nil && utils.Rc.IsExist(key) {
+			if chartData, err1 := utils.Rc.RedisBytes(key); err1 == nil {
+				err := json.Unmarshal(chartData, &resp)
+				if err == nil && resp != nil {
+					// 这里跟当前用户相关的信息重新查询写入resp, 不使用缓存中的
+					var myCond string
+					var myPars []interface{}
+					myCond += ` AND a.admin_id=? `
+					myPars = append(myPars, adminId)
+					myCond += ` AND a.chart_info_id=? `
+					myPars = append(myPars, chartInfo.ChartInfoId)
+					myList, err := data_manage.GetMyChartByCondition(myCond, myPars)
+					if err != nil && err.Error() != utils.ErrNoRow() {
+						msg = "获取失败"
+						errMsg = "获取我的图表信息失败,GetMyChartByCondition,Err:" + err.Error()
+						return
+					}
+					resp.ChartInfo.IsAdd = false
+					resp.ChartInfo.MyChartId = 0
+					resp.ChartInfo.MyChartClassifyId = ""
+					if myList != nil && len(myList) > 0 {
+						resp.ChartInfo.IsAdd = true
+						resp.ChartInfo.MyChartId = myList[0].MyChartId
+						resp.ChartInfo.MyChartClassifyId = myList[0].MyChartClassifyId
+					}
+
+					isOk = true
+					fmt.Println("source redis")
+					return
+				}
+			}
+		}
+	}
+
+	if chartInfo.ExtraConfig == `` {
+		msg = "获取失败"
+		errMsg = "获取配置信息失败,图表的额外配置信息为空"
+		return
+	}
+
+	// 获取图表关联指标
+	edbMappingList, err := data_manage.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 []*data_manage.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 request.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 request.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
+	}
+
+	if chartInfo.ChartInfoId > 0 && chartInfo != nil {
+		//判断是否加入我的图库
+		{
+			var myChartCondition string
+			var myChartPars []interface{}
+			myChartCondition += ` AND a.admin_id=? `
+			myChartPars = append(myChartPars, sysUser.AdminId)
+			myChartCondition += ` AND a.chart_info_id=? `
+			myChartPars = append(myChartPars, chartInfo.ChartInfoId)
+
+			myChartList, err := data_manage.GetMyChartByCondition(myChartCondition, myChartPars)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				msg = "获取失败"
+				errMsg = "获取我的图表信息失败,GetMyChartByCondition,Err:" + err.Error()
+				return
+			}
+			if myChartList != nil && len(myChartList) > 0 {
+				chartInfo.IsAdd = true
+				chartInfo.MyChartId = myChartList[0].MyChartId
+				chartInfo.MyChartClassifyId = myChartList[0].MyChartClassifyId
+			}
+		}
+	}
+
+	//图表操作权限
+	chartInfo.IsEdit = data.CheckOpChartPermission(sysUser, chartInfo.SysUserId)
+	//判断是否需要展示英文标识
+	chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, []*data_manage.ChartEdbInfoMapping{}, chartInfo.Source, chartInfo.ChartType)
+	//chartInfo.UnitEn = edbInfoMappingA.UnitEn
+
+	isSaveAs := true
+	// 另存为
+	chartInfo.Button = data_manage.ChartViewButton{
+		IsEdit:    chartInfo.IsEdit,
+		IsEnChart: chartInfo.IsEnChart,
+		IsAdd:     chartInfo.IsAdd,
+		IsCopy:    isSaveAs,
+		IsSetName: chartInfo.IsSetName,
+	}
+
+	resp.ChartInfo = chartInfo
+	resp.EdbInfoList = edbList
+	resp.DataResp = resultResp
+	resp.Status = true
+
+	// 将数据加入缓存
+	if utils.Re == nil {
+		d, _ := json.Marshal(resp)
+		_ = utils.Rc.Put(key, d, 2*time.Hour)
+	}
+	isOk = true
+	return
+}

+ 194 - 0
models/data_manage/line_feature/line_feature.go

@@ -0,0 +1,194 @@
+package line_feature
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_admin/models/data_manage"
+	"hongze/hongze_admin/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type BatchAddChart struct {
+	ChartInfo                       *data_manage.ChartInfo
+	EdbMappingList                  []*data_manage.ChartEdbMapping
+	MultipleGraphConfigChartMapping *data_manage.MultipleGraphConfigChartMapping
+}
+
+func CreateLineFeatureChartAndEdb(chartInfo *data_manage.ChartInfo, edbMappingList []*data_manage.ChartEdbMapping) (chartInfoId int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+		} else {
+			_ = tx.Commit()
+		}
+	}()
+
+	// 新增图表信息
+	newId, err := tx.Insert(chartInfo)
+	if err != nil {
+		return
+	}
+	// 指标mapping
+	chartInfo.ChartInfoId = int(newId)
+	chartInfoId = int(newId)
+	if len(edbMappingList) > 0 {
+		for i := range edbMappingList {
+			edbMappingList[i].ChartInfoId = chartInfoId
+		}
+		_, err = tx.InsertMulti(len(edbMappingList), edbMappingList)
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// EditLineFeatureChartAndMapping 批量新增/编辑拟合方程图表
+func EditLineFeatureChartAndMapping(req *data_manage.EditChartInfoReq, edbInfoIdStr string, calendar string, dateType, disabled int, extraConfig string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	var pars []interface{}
+	pars = append(pars, req.ChartName)
+	pars = append(pars, edbInfoIdStr)
+	pars = append(pars, req.ChartClassifyId)
+	pars = append(pars, disabled)
+	pars = append(pars, extraConfig)
+
+	sql := ` UPDATE  chart_info
+			SET
+			  chart_name =?,
+              edb_info_ids=?,
+			  chart_classify_id = ?,
+			  modify_time = NOW(),
+              disabled = ?,
+              extra_config = ?
+			`
+	if calendar != "" {
+		sql += `,calendar = ? `
+		pars = append(pars, calendar)
+	}
+	if dateType > 0 {
+		sql += `,date_type = ? `
+		pars = append(pars, dateType)
+	}
+
+	sql += `,start_date = ? `
+	pars = append(pars, req.StartDate)
+
+	sql += `,end_date = ? `
+	pars = append(pars, req.EndDate)
+
+	sql += `,season_start_date = ? `
+	pars = append(pars, req.SeasonStartDate)
+
+	sql += `,season_end_date = ? `
+	pars = append(pars, req.SeasonEndDate)
+
+	sql += `,left_min = ? `
+	pars = append(pars, req.LeftMin)
+
+	sql += `,left_max = ? `
+	pars = append(pars, req.LeftMax)
+
+	sql += `,right_min = ? `
+	pars = append(pars, req.RightMin)
+
+	sql += `,right_max = ? `
+	pars = append(pars, req.RightMax)
+
+	sql += `WHERE chart_info_id = ?`
+
+	pars = append(pars, req.ChartInfoId)
+	_, err = to.Raw(sql, pars).Exec()
+	if err != nil {
+		fmt.Println("UPDATE  chart_info Err:", err.Error())
+		return err
+	}
+	chartEdbMappingIdList := make([]string, 0)
+	for _, v := range req.CorrelationChartInfo.EdbInfoIdList {
+		// 查询该指标是否存在,如果存在的话,那么就去修改,否则新增
+		var tmpChartEdbMapping *data_manage.ChartEdbMapping
+		csql := `SELECT *  FROM chart_edb_mapping WHERE chart_info_id=? AND edb_info_id=? AND source = ? `
+		err = to.Raw(csql, req.ChartInfoId, v.EdbInfoId, utils.CHART_SOURCE_CORRELATION).QueryRow(&tmpChartEdbMapping)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			fmt.Println("QueryRow Err:", err.Error())
+			return err
+		}
+		if tmpChartEdbMapping != nil {
+			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(tmpChartEdbMapping.ChartEdbMappingId))
+			//tmpChartEdbMapping.ModifyTime = time.Now()
+			//tmpChartEdbMapping.MaxData = v.MaxData
+			//tmpChartEdbMapping.MinData = v.MinData
+			//tmpChartEdbMapping.IsOrder = v.IsOrder
+			//tmpChartEdbMapping.IsAxis = v.IsAxis
+			//tmpChartEdbMapping.EdbInfoType = v.EdbInfoType
+			//tmpChartEdbMapping.LeadValue = v.LeadValue
+			//tmpChartEdbMapping.LeadUnit = v.LeadUnit
+			//tmpChartEdbMapping.ChartStyle = v.ChartStyle
+			//tmpChartEdbMapping.ChartColor = v.ChartColor
+			//tmpChartEdbMapping.PredictChartColor = v.PredictChartColor
+			//tmpChartEdbMapping.ChartWidth = v.ChartWidth
+			//_, err = to.Update(tmpChartEdbMapping, "ModifyTime", "MaxData", "MinData", "IsOrder", "IsAxis", "EdbInfoType", "LeadValue", "LeadUnit", "ChartStyle", "ChartColor", "PredictChartColor", "ChartWidth")
+			//if err != nil {
+			//	fmt.Println("chart_edb_mapping Err:" + err.Error())
+			//	return err
+			//}
+		} else {
+			mapItem := new(data_manage.ChartEdbMapping)
+			mapItem.ChartInfoId = req.ChartInfoId
+			mapItem.EdbInfoId = v.EdbInfoId
+			mapItem.CreateTime = time.Now()
+			mapItem.ModifyTime = time.Now()
+			timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+			mapItem.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp + "_" + strconv.Itoa(v.EdbInfoId))
+			//mapItem.MaxData = v.MaxData
+			//mapItem.MinData = v.MinData
+			mapItem.IsOrder = true
+			mapItem.IsAxis = 1
+			mapItem.EdbInfoType = 1
+			//mapItem.LeadValue = v.LeadValue
+			//mapItem.LeadUnit = v.LeadUnit
+			//mapItem.ChartStyle = v.ChartStyle
+			//mapItem.ChartColor = v.ChartColor
+			//mapItem.PredictChartColor = v.PredictChartColor
+			//mapItem.ChartWidth = v.ChartWidth
+			mapItem.Source = utils.CHART_SOURCE_CORRELATION
+			tmpId, err := to.Insert(mapItem)
+			if err != nil {
+				fmt.Println("AddChartEdbMapping Err:" + err.Error())
+				return err
+			}
+			mapItem.ChartEdbMappingId = int(tmpId)
+			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(mapItem.ChartEdbMappingId))
+		}
+	}
+	if len(chartEdbMappingIdList) > 0 {
+		chartEdbMappingIdStr := strings.Join(chartEdbMappingIdList, ",")
+		dsql := `DELETE FROM chart_edb_mapping WHERE chart_info_id=? AND chart_edb_mapping_id NOT IN(` + chartEdbMappingIdStr + `)`
+		_, err = to.Raw(dsql, req.ChartInfoId).Exec()
+		if err != nil {
+			fmt.Println("delete err:" + err.Error())
+			return err
+		}
+	}
+
+	return
+}

+ 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的话,就是另存为,不会建立与配置的关系"`
+}

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

@@ -0,0 +1,104 @@
+package response
+
+import (
+	"hongze/hongze_ETA_mobile_api/models/data_manage"
+	"hongze/hongze_ETA_mobile_api/models/data_manage/line_feature/request"
+)
+
+// PreviewMultipleGraphConfigResp 预览
+type PreviewMultipleGraphConfigResp struct {
+	CurveData                 interface{} `description:"曲线图数据"`
+	StandardDeviationData     interface{} `description:"标准差数据"`
+	PercentileData            interface{} `description:"百分位数据"`
+	FrequencyDistributionData interface{} `description:"频率分布数据"`
+}
+
+type CurveResp struct {
+	ChartInfo   data_manage.ChartInfoView
+	EdbInfoList []data_manage.ChartEdbInfoMapping
+	DataResp    interface{}
+}
+
+// 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            []data_manage.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
+}
+
+type ChartInfoResp struct {
+	List []ChartDataResp `description:"数据列表"`
+}
+
+// ChartDataResp 图表数据
+type ChartDataResp struct {
+	LatestDate          string  `description:"真实数据的日期"`
+	Name                string  `description:"名称"`
+	NameEn              string  `description:"英文名称"`
+	EdbInfoCategoryType int     `description:"普通数据还是预测数据,0:普通,1:预测"`
+	MaxData             float64 `description:"最大值"`
+	MinData             float64 `description:"最小值"`
+	DataList            []data_manage.EdbDataList
+}
+
+// MultipleGraphConfigDetailResp 配置详情接口返回
+type MultipleGraphConfigDetailResp struct {
+	MultipleGraphConfigId int
+	Curve                 request.CurveConfig           `description:"曲线图配置"`
+	StandardDeviation     request.StandardDeviation     `description:"标准差配置"`
+	Percentile            request.Percentile            `description:"百分位配置"`
+	FrequencyDistribution request.FrequencyDistribution `description:"频率分布配置"`
+	EdbInfoList           []*data_manage.ChartEdbInfoMapping
+	ChartMappingList      []MultipleGraphConfigChartMapping `description:"关联图表"`
+	EdbMappingList        []MultipleGraphConfigEdbMapping   `description:"关联指标"`
+}
+type MultipleGraphConfigChartMapping struct {
+	ChartInfoId            int    `description:"图表id"`
+	Source                 int    `description:"来源,1:曲线图,8:标准差图表;9:百分位图表;10:频率分布图表;"`
+	MultipleLocationSource int    `description:"来源,1:曲线图,8:标准差图表;9:百分位图表;10:频率分布图表;"`
+	ChartName              string `description:"图表名称"`
+	ChartClassifyId        int    `description:"图表分类id"`
+}
+type MultipleGraphConfigEdbMapping struct {
+	EdbInfoId              int `description:"图表id"`
+	Source                 int `description:"来源,1:曲线图,8:标准差图表;9:百分位图表;10:频率分布图表;"`
+	MultipleLocationSource int `description:"来源,1:曲线图,8:标准差图表;9:百分位图表;10:频率分布图表;"`
+}

+ 6 - 2
services/data/chart_info.go

@@ -16,7 +16,6 @@ import (
 	"time"
 )
 
-
 type ChartInfoReq struct {
 	ChartInfoId int `description:"图表id,新增时传0"`
 }
@@ -138,6 +137,12 @@ func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate st
 	return
 }
 
+// GetEdbDataMapList 获取指标最后的基础数据
+func GetEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping) (edbDataListMap map[int][]*data_manage.EdbDataList, edbList []*data_manage.ChartEdbInfoMapping, err error) {
+	edbDataListMap, edbList, err = getEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList)
+	return
+}
+
 // getEdbDataMapList 获取指标最后的基础数据
 func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping) (edbDataListMap map[int][]*data_manage.EdbDataList, edbList []*data_manage.ChartEdbInfoMapping, err error) {
 	// 指标对应的所有数据
@@ -592,7 +597,6 @@ func BarChartData(mappingList []*data_manage.ChartEdbInfoMapping, edbDataListMap
 	return
 }
 
-
 func CheckIsEnChart(chartNameEn string, edbList []*data_manage.ChartEdbInfoMapping, source, chartType int) bool {
 	// 相关性图表不判断指标
 	if source == utils.CHART_SOURCE_CORRELATION && chartNameEn != "" {

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

@@ -0,0 +1,305 @@
+package line_feature
+
+import (
+	"errors"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_ETA_mobile_api/models/data_manage"
+	"hongze/hongze_ETA_mobile_api/models/data_manage/line_feature/response"
+	"hongze/hongze_ETA_mobile_api/services/data"
+	"hongze/hongze_ETA_mobile_api/utils"
+	"time"
+)
+
+// GetStandardDeviationData 获取标准差图表的指标数据
+func GetStandardDeviationData(chartInfoId int, mappingInfo *data_manage.ChartEdbInfoMapping, calculateValue int) (edbList []*data_manage.ChartEdbInfoMapping, dataResp response.LineFeatureDataResp, err error, errMsg string) {
+	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
+
+	// 指标对应的所有数据
+	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, ``, ``, []*data_manage.ChartEdbInfoMapping{mappingInfo})
+	if err != nil {
+		return
+	}
+
+	if len(edbList) != 1 {
+		errMsg = `指标异常`
+		err = errors.New(errMsg)
+		return
+	}
+
+	edb := edbList[0]
+	dataList := edb.DataList.([]*data_manage.EdbDataList)
+	newDataList := make([]data_manage.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, data_manage.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 *data_manage.ChartEdbInfoMapping, calculateValue int, calculateUnit string) (edbList []*data_manage.ChartEdbInfoMapping, dataResp response.LineFeatureDataResp, err error, errMsg string) {
+	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
+	moveUnitDays, ok := utils.FrequencyDaysMap[calculateUnit]
+	if !ok {
+		errMsg = `错误的周期`
+		err = errors.New(errMsg)
+		return
+	}
+	calculateDay := calculateValue * moveUnitDays
+	// 指标对应的所有数据
+	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, ``, ``, []*data_manage.ChartEdbInfoMapping{mappingInfo})
+	if err != nil {
+		return
+	}
+
+	if len(edbList) != 1 {
+		errMsg = `指标异常`
+		err = errors.New(errMsg)
+		return
+	}
+
+	edb := edbList[0]
+	dataList := edb.DataList.([]*data_manage.EdbDataList)
+	newDataList := make([]data_manage.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 i := 0; i < calculateDay; i++ {
+			preVal, ok := dataMap[currDateTime.AddDate(0, 0, -i)]
+			if ok {
+				if preVal > maxVal {
+					maxVal = preVal
+				}
+				if preVal < minVal {
+					minVal = preVal
+				}
+			}
+		}
+
+		if maxVal == minVal {
+			continue
+		}
+		tmpV := (tmpData.Value) / (maxVal - minVal) * 100
+		tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64()
+		//百分位=(现值-Min)/(Max-Min)
+		newDataList = append(newDataList, data_manage.EdbDataList{
+			EdbDataId:     i,
+			EdbInfoId:     edb.EdbInfoId,
+			DataTime:      dataList[i-1].DataTime,
+			DataTimestamp: dataList[i-1].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 *data_manage.ChartEdbInfoMapping, dateType, stepVal int, startDate, endDate string) (edbList []*data_manage.ChartEdbInfoMapping, dataResp response.FrequencyDistributionResp, err error, errMsg string) {
+	//日期类型:1:最近3月;2:最近6月;3:最近1年;4:最近2年;5:最近3年;6:最近5年;7:最近10年,8:自定义时间
+	startDate, endDate = utils.GetDateByDateType2(dateType, startDate, endDate)
+	if startDate == `` {
+		errMsg = "错误的日期"
+		err = errors.New(errMsg)
+		return
+	}
+	XDataList := make([]float64, 0)
+	// 频度
+	Y1DataList := make([]response.FrequencyDistributionYData, 0)
+	// 累计频率
+	Y2DataList := make([]response.FrequencyDistributionYData, 0)
+	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
+
+	// 指标对应的所有数据
+	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*data_manage.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.([]*data_manage.EdbDataList)
+
+	if len(dataList) <= 0 {
+		return
+	}
+	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 && tmpVal < tmpMaxVal {
+				distributionDataNumMap[tmpMinVal] += num
+			}
+		}
+	}
+
+	tmpNum := 0
+	for _, tmpMinVal := range XDataList {
+		// 频率
+		frequencyYNum := distributionDataNumMap[tmpMinVal]
+		Y1DataList = append(Y1DataList, response.FrequencyDistributionYData{
+			X: tmpMinVal,
+			Y: float64(frequencyYNum),
+		})
+		// 累计数
+		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:  minVal,
+		LeftMaxValue:  maxVal,
+		RightMinValue: 0,
+		RightMaxValue: 100,
+		DataList:      newDataList,
+	}
+	return
+}

+ 28 - 0
utils/calculate.go

@@ -161,3 +161,31 @@ func CalculationDecisive(sList []Coordinate) (r2 float64) {
 
 	return
 }
+
+// CalculateStandardDeviation 计算标准差
+func CalculateStandardDeviation(data []float64) float64 {
+	// 计算平均值
+	mean := calculateMean(data)
+	// 计算方差
+	variance := calculateVariance(data, mean)
+	return math.Sqrt(variance)
+}
+
+// 计算平均值
+func calculateMean(data []float64) float64 {
+	sum := 0.0
+	for _, value := range data {
+		sum += value
+	}
+	return sum / float64(len(data))
+}
+
+// 计算方差
+func calculateVariance(data []float64, mean float64) float64 {
+	sumSquaredDiff := 0.0
+	for _, value := range data {
+		diff := value - mean
+		sumSquaredDiff += diff * diff
+	}
+	return sumSquaredDiff / float64(len(data))
+}

+ 217 - 0
utils/common.go

@@ -1475,3 +1475,220 @@ func GetDaysBetween2Date(format, date1Str, date2Str string) (int, error) {
 	//计算相差天数
 	return int(date1.Sub(date2).Hours() / 24), nil
 }
+
+// GetDateByDateType2 通过dateType获取需要的开始/结束日期(日期类型:1:最近3月;2:最近6月;3:最近1年;4:最近2年;5:最近3年;6:最近5年;7:最近10年,8:自定义时间)
+func GetDateByDateType2(dateType int, tmpStartDate, tmpEndDate string) (startDate, endDate string) {
+	startDate = tmpStartDate
+	endDate = tmpEndDate
+	currDate := time.Now()
+	switch dateType {
+	case 1:
+		startDate = currDate.AddDate(0, -3, 0).Format(FormatDate)
+		endDate = ""
+	case 2:
+		startDate = currDate.AddDate(0, -6, 0).Format(FormatDate)
+		endDate = ""
+	case 3:
+		startDate = currDate.AddDate(-1, 0, 0).Format(FormatDate)
+		endDate = ""
+	case 4:
+		startDate = currDate.AddDate(-2, 0, 0).Format(FormatDate)
+		endDate = ""
+	case 5:
+		startDate = currDate.AddDate(-3, 0, 0).Format(FormatDate)
+		endDate = ""
+	case 6:
+		startDate = currDate.AddDate(-5, 0, 0).Format(FormatDate)
+		endDate = ""
+	case 7:
+		startDate = currDate.AddDate(-10, 0, 0).Format(FormatDate)
+		endDate = ""
+	}
+
+	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
+}

+ 93 - 71
utils/constants.go

@@ -157,72 +157,76 @@ var PermissionAllClassifyArr = [...]string{"宏观经济", "化工产业", "建
 
 // 数据来源渠道
 const (
-	DATA_SOURCE_THS                          = iota + 1 //同花顺
-	DATA_SOURCE_WIND                                    //wind->2
-	DATA_SOURCE_PB                                      //彭博->3
-	DATA_SOURCE_CALCULATE                               //指标运算->4
-	DATA_SOURCE_CALCULATE_LJZZY                         //累计值转月->5
-	DATA_SOURCE_CALCULATE_TBZ                           //同比值->6
-	DATA_SOURCE_CALCULATE_TCZ                           //同差值->7
-	DATA_SOURCE_CALCULATE_NSZYDPJJS                     //N数值移动平均计算->8
-	DATA_SOURCE_MANUAL                                  //手工指标->9
-	DATA_SOURCE_LZ                                      //隆众->10
-	DATA_SOURCE_YS                                      //有色->11
-	DATA_SOURCE_CALCULATE_HBZ                           //环比值->12
-	DATA_SOURCE_CALCULATE_HCZ                           //环差值->13
-	DATA_SOURCE_CALCULATE_BP                            //变频->14
-	DATA_SOURCE_GL                                      //钢联->15
-	DATA_SOURCE_ZZ                                      //郑商所->16
-	DATA_SOURCE_DL                                      //大商所->17
-	DATA_SOURCE_SH                                      //上期所->18
-	DATA_SOURCE_CFFEX                                   //中金所->19
-	DATA_SOURCE_SHFE                                    //上期能源->20
-	DATA_SOURCE_GIE                                     //欧洲天然气->21
-	DATA_SOURCE_CALCULATE_TIME_SHIFT                    //时间移位->22
-	DATA_SOURCE_CALCULATE_ZJPJ                          //直接拼接->23
-	DATA_SOURCE_CALCULATE_LJZTBPJ                       //累计值同比拼接->24
-	DATA_SOURCE_LT                                      //路透->25
-	DATA_SOURCE_COAL                                    //中国煤炭网->26
-	DATA_SOURCE_PYTHON                                  //python代码->27
-	DATA_SOURCE_PB_FINANCE                              //彭博财务数据->28
-	DATA_SOURCE_GOOGLE_TRAVEL                           //谷歌出行->29
-	DATA_SOURCE_PREDICT                                 //普通预测指标->30
-	DATA_SOURCE_PREDICT_CALCULATE                       //预测指标运算->31
-	DATA_SOURCE_PREDICT_CALCULATE_TBZ                   //预测同比值->32
-	DATA_SOURCE_PREDICT_CALCULATE_TCZ                   //预测同差值->33
-	DATA_SOURCE_MYSTEEL_CHEMICAL                        //钢联化工->34
-	DATA_SOURCE_CALCULATE_CJJX                          //超季节性->35
-	DATA_SOURCE_EIA_STEO                                //eia steo报告->36
-	DATA_SOURCE_CALCULATE_NHCC                          //计算指标(拟合残差)->37
-	DATA_SOURCE_COM_TRADE                               //联合国商品贸易数据->38
-	DATA_SOURCE_PREDICT_CALCULATE_NSZYDPJJS             //预测指标 - N数值移动平均计算 -> 39
-	DATA_SOURCE_CALCULATE_ADJUST                        //数据调整->40
-	DATA_SOURCE_SCI                                     //卓创数据(红桃三)->41
-	DATA_SOURCE_PREDICT_CALCULATE_LJZZY                 //预测指标 - 累计值转月->42
-	DATA_SOURCE_PREDICT_CALCULATE_HBZ                   //预测指标 - 环比值->43
-	DATA_SOURCE_PREDICT_CALCULATE_HCZ                   //预测指标 - 环差值->44
-	DATA_SOURCE_PREDICT_CALCULATE_BP                    //预测指标 - 变频->45
-	DATA_SOURCE_PREDICT_CALCULATE_TIME_SHIFT            //预测指标 - 时间移位->46
-	DATA_SOURCE_PREDICT_CALCULATE_ZJPJ                  //预测指标 - 直接拼接->47
-	DATA_SOURCE_PREDICT_CALCULATE_LJZTBPJ               //预测指标 - 累计值同比拼接->48
-	DATA_SOURCE_PREDICT_CALCULATE_CJJX                  //预测指标 - 超季节性->49
-	DATA_SOURCE_PREDICT_CALCULATE_NHCC                  //预测指标 - 计算指标(拟合残差)->50
-	DATA_SOURCE_CALCULATE_JP                            //变频->51
-	DATA_SOURCE_CALCULATE_NH                            //年化->52
-	DATA_SOURCE_CALCULATE_KSZS                          //扩散指数->53
-	DATA_SOURCE_PREDICT_CALCULATE_JP                    //预测指标 - 计算指标(降频)->54
-	DATA_SOURCE_PREDICT_CALCULATE_NH                    //预测指标 - 计算指标(年化)->55
-	DATA_SOURCE_PREDICT_CALCULATE_KSZS                  //预测指标 - 计算指标(扩散指数)->56
-	DATA_SOURCE_BAIINFO                                 //百川盈孚 ->57
-	DATA_SOURCE_STOCK_PLANT                             //存量装置 ->58
-	DATA_SOURCE_CALCULATE_CORRELATION                   //滚动相关性->59
-	DATA_SOURCE_NATIONAL_STATISTICS                     //国家统计局->60
-	DATA_SOURCE_CALCULATE_LJZZJ                         //累计值转季 -> 61
-	DATA_SOURCE_CALCULATE_LJZ                           //累计值 -> 62
-	DATA_SOURCE_CALCULATE_LJZNCZJ                       //累计值(年初至今) -> 63
-	DATA_SOURCE_PREDICT_CALCULATE_LJZZJ                 //预测指标 - 累计值转季->64
-	DATA_SOURCE_PREDICT_CALCULATE_LJZ                   //预测指标 - 累计值 -> 65
-	DATA_SOURCE_PREDICT_CALCULATE_LJZNCZJ               //预测指标 - 累计值(年初至今) -> 66
+	DATA_SOURCE_THS                                  = iota + 1 //同花顺
+	DATA_SOURCE_WIND                                            //wind->2
+	DATA_SOURCE_PB                                              //彭博->3
+	DATA_SOURCE_CALCULATE                                       //指标运算->4
+	DATA_SOURCE_CALCULATE_LJZZY                                 //累计值转月->5
+	DATA_SOURCE_CALCULATE_TBZ                                   //同比值->6
+	DATA_SOURCE_CALCULATE_TCZ                                   //同差值->7
+	DATA_SOURCE_CALCULATE_NSZYDPJJS                             //N数值移动平均计算->8
+	DATA_SOURCE_MANUAL                                          //手工指标->9
+	DATA_SOURCE_LZ                                              //隆众->10
+	DATA_SOURCE_YS                                              //有色->11
+	DATA_SOURCE_CALCULATE_HBZ                                   //环比值->12
+	DATA_SOURCE_CALCULATE_HCZ                                   //环差值->13
+	DATA_SOURCE_CALCULATE_BP                                    //变频->14
+	DATA_SOURCE_GL                                              //钢联->15
+	DATA_SOURCE_ZZ                                              //郑商所->16
+	DATA_SOURCE_DL                                              //大商所->17
+	DATA_SOURCE_SH                                              //上期所->18
+	DATA_SOURCE_CFFEX                                           //中金所->19
+	DATA_SOURCE_SHFE                                            //上期能源->20
+	DATA_SOURCE_GIE                                             //欧洲天然气->21
+	DATA_SOURCE_CALCULATE_TIME_SHIFT                            //时间移位->22
+	DATA_SOURCE_CALCULATE_ZJPJ                                  //直接拼接->23
+	DATA_SOURCE_CALCULATE_LJZTBPJ                               //累计值同比拼接->24
+	DATA_SOURCE_LT                                              //路透->25
+	DATA_SOURCE_COAL                                            //中国煤炭网->26
+	DATA_SOURCE_PYTHON                                          //python代码->27
+	DATA_SOURCE_PB_FINANCE                                      //彭博财务数据->28
+	DATA_SOURCE_GOOGLE_TRAVEL                                   //谷歌出行->29
+	DATA_SOURCE_PREDICT                                         //普通预测指标->30
+	DATA_SOURCE_PREDICT_CALCULATE                               //预测指标运算->31
+	DATA_SOURCE_PREDICT_CALCULATE_TBZ                           //预测同比值->32
+	DATA_SOURCE_PREDICT_CALCULATE_TCZ                           //预测同差值->33
+	DATA_SOURCE_MYSTEEL_CHEMICAL                                //钢联化工->34
+	DATA_SOURCE_CALCULATE_CJJX                                  //超季节性->35
+	DATA_SOURCE_EIA_STEO                                        //eia steo报告->36
+	DATA_SOURCE_CALCULATE_NHCC                                  //计算指标(拟合残差)->37
+	DATA_SOURCE_COM_TRADE                                       //联合国商品贸易数据->38
+	DATA_SOURCE_PREDICT_CALCULATE_NSZYDPJJS                     //预测指标 - N数值移动平均计算 -> 39
+	DATA_SOURCE_CALCULATE_ADJUST                                //数据调整->40
+	DATA_SOURCE_SCI                                             //卓创数据(红桃三)->41
+	DATA_SOURCE_PREDICT_CALCULATE_LJZZY                         //预测指标 - 累计值转月->42
+	DATA_SOURCE_PREDICT_CALCULATE_HBZ                           //预测指标 - 环比值->43
+	DATA_SOURCE_PREDICT_CALCULATE_HCZ                           //预测指标 - 环差值->44
+	DATA_SOURCE_PREDICT_CALCULATE_BP                            //预测指标 - 变频->45
+	DATA_SOURCE_PREDICT_CALCULATE_TIME_SHIFT                    //预测指标 - 时间移位->46
+	DATA_SOURCE_PREDICT_CALCULATE_ZJPJ                          //预测指标 - 直接拼接->47
+	DATA_SOURCE_PREDICT_CALCULATE_LJZTBPJ                       //预测指标 - 累计值同比拼接->48
+	DATA_SOURCE_PREDICT_CALCULATE_CJJX                          //预测指标 - 超季节性->49
+	DATA_SOURCE_PREDICT_CALCULATE_NHCC                          //预测指标 - 计算指标(拟合残差)->50
+	DATA_SOURCE_CALCULATE_JP                                    //变频->51
+	DATA_SOURCE_CALCULATE_NH                                    //年化->52
+	DATA_SOURCE_CALCULATE_KSZS                                  //扩散指数->53
+	DATA_SOURCE_PREDICT_CALCULATE_JP                            //预测指标 - 计算指标(降频)->54
+	DATA_SOURCE_PREDICT_CALCULATE_NH                            //预测指标 - 计算指标(年化)->55
+	DATA_SOURCE_PREDICT_CALCULATE_KSZS                          //预测指标 - 计算指标(扩散指数)->56
+	DATA_SOURCE_BAIINFO                                         //百川盈孚 ->57
+	DATA_SOURCE_STOCK_PLANT                                     //存量装置 ->58
+	DATA_SOURCE_CALCULATE_CORRELATION                           //滚动相关性->59
+	DATA_SOURCE_NATIONAL_STATISTICS                             //国家统计局->60
+	DATA_SOURCE_CALCULATE_LJZZJ                                 //累计值转季 -> 61
+	DATA_SOURCE_CALCULATE_LJZ                                   //累计值 -> 62
+	DATA_SOURCE_CALCULATE_LJZNCZJ                               //累计值(年初至今) -> 63
+	DATA_SOURCE_PREDICT_CALCULATE_LJZZJ                         //预测指标 - 累计值转季->64
+	DATA_SOURCE_PREDICT_CALCULATE_LJZ                           //预测指标 - 累计值 -> 65
+	DATA_SOURCE_PREDICT_CALCULATE_LJZNCZJ                       //预测指标 - 累计值(年初至今) -> 66
+	DATA_SOURCE_CALCULATE_STANDARD_DEVIATION                    //标准差->67
+	DATA_SOURCE_CALCULATE_PERCENTILE                            //百分位图表->68
+	DATA_SOURCE_PREDICT_CALCULATE_STANDARD_DEVIATION            //预测标准差->69
+	DATA_SOURCE_PREDICT_CALCULATE_PERCENTILE                    //预测百分位->70
 )
 
 // 数据刷新频率
@@ -407,11 +411,29 @@ const (
 
 // 图表类型
 const (
-	CHART_SOURCE_DEFAULT             = 1
-	CHART_SOURCE_FUTURE_GOOD         = 2
-	CHART_SOURCE_CORRELATION         = 3 // 相关性图表
-	CHART_SOURCE_ROLLING_CORRELATION = 4 // 滚动相关性图表
-	CHART_SOURCE_FUTURE_GOOD_PROFIT  = 5 // 商品利润曲线
+	CHART_SOURCE_DEFAULT                         = 1
+	CHART_SOURCE_FUTURE_GOOD                     = 2
+	CHART_SOURCE_CORRELATION                     = 3 // 相关性图表
+	CHART_SOURCE_ROLLING_CORRELATION             = 4 // 滚动相关性图表
+	CHART_SOURCE_FUTURE_GOOD_PROFIT              = 5 // 商品利润曲线
+	CHART_SOURCE_LINE_EQUATION                   = 6 // 拟合方程图表
+	CHART_SOURCE_LINE_FEATURE_STANDARD_DEVIATION = 7 // 统计特征-标准差图表
+	CHART_SOURCE_LINE_FEATURE_PERCENTILE         = 8 // 统计特征-百分位图表
+	CHART_SOURCE_LINE_FEATURE_FREQUENCY          = 9 // 统计特征-频率分布图表
+)
+
+// 批量配置图表的位置来源
+const (
+	CHART_MULTIPLE_GRAPH_CURVE                           = 1  // 曲线图
+	CHART_MULTIPLE_GRAPH_CORRELATION                     = 2  // 相关性图
+	CHART_MULTIPLE_GRAPH_ROLLING_CORRELATION_ONE         = 3  // 滚动相关性图1
+	CHART_MULTIPLE_GRAPH_ROLLING_CORRELATION_TWO         = 4  // 滚动相关性图2
+	CHART_MULTIPLE_GRAPH_LINE_EQUATION_ONE               = 5  // 拟合方程-斜率图
+	CHART_MULTIPLE_GRAPH_LINE_EQUATION_TWO               = 6  // 拟合方程-截距图
+	CHART_MULTIPLE_GRAPH_LINE_EQUATION_THREE             = 7  // 拟合方程-相关性图
+	CHART_MULTIPLE_GRAPH_LINE_FEATURE_STANDARD_DEVIATION = 8  // 统计特征-标准差图表
+	CHART_MULTIPLE_GRAPH_LINE_FEATURE_PERCENTILE         = 9  // 统计特征-百分位图表
+	CHART_MULTIPLE_GRAPH_LINE_FEATURE_FREQUENCY          = 10 // 统计特征-频率分布图表
 )
 
 // 图表样式类型