Ver Fonte

Merge branch '13.1'

hsun há 2 anos atrás
pai
commit
0971ac1572

+ 169 - 1
controllers/chart_common.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"hongze/hongze_chart_lib/models"
 	"hongze/hongze_chart_lib/models/data_manage"
+	correlationServ "hongze/hongze_chart_lib/services/data/correlation"
 	future_goodServ "hongze/hongze_chart_lib/services/data/future_good"
 	"hongze/hongze_chart_lib/utils"
 	"strings"
@@ -85,12 +86,22 @@ func (this *ChartController) CommonChartInfoDetailFromUniqueCode() {
 		br.Success = true
 		br.Msg = "获取成功"
 		br.Data = resp
+	case utils.CHART_SOURCE_CORRELATION:
+		resp, isOk, msg, errMsg := GetCorrelationChartInfoDetailFromUniqueCode(chartInfo, key)
+		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
 	}
-
 }
 
 // GetFutureGoodChartInfoDetailFromUniqueCode 根据编码获取图表详情
@@ -265,3 +276,160 @@ func (this *ChartController) FutureGoodChartInfoRefresh() {
 	br.Success = true
 	br.Msg = "刷新成功"
 }
+
+// GetCorrelationChartInfoDetailFromUniqueCode 根据编码获取相关性图表详情
+func GetCorrelationChartInfoDetailFromUniqueCode(chartInfo *models.ChartInfo, key string) (resp *models.ChartInfoDetailResp, isOk bool, msg, errMsg string) {
+	resp = new(models.ChartInfoDetailResp)
+
+	chartInfoId := chartInfo.ChartInfoId
+
+	startDate := chartInfo.StartDate
+	endDate := chartInfo.EndDate
+
+	// 兼容日期错误
+	{
+		if strings.Count(startDate, "-") == 1 {
+			startDate = startDate + "-01"
+		}
+		if strings.Count(endDate, "-") == 1 {
+			endDate = endDate + "-01"
+		}
+	}
+
+	// 相关性图表信息
+	correlationChart := new(data_manage.ChartInfoCorrelation)
+	if e := correlationChart.GetItemById(chartInfoId); e != nil {
+		msg = "获取失败"
+		errMsg = "获取图表相关性信息失败, Err:" + e.Error()
+		return
+	}
+	if correlationChart.PeriodData == "" || correlationChart.CorrelationData == "" {
+		msg = "获取失败"
+		errMsg = "相关性图表数据有误"
+		return
+	}
+	//chartInfo.CorrelationLeadUnit = correlationChart.LeadUnit
+	xData := make([]int, 0)
+	yData := make([]float64, 0)
+	if e := json.Unmarshal([]byte(correlationChart.PeriodData), &xData); e != nil {
+		msg = "获取失败"
+		errMsg = "相关性图表X轴数据有误, Err:" + e.Error()
+		return
+	}
+	if e := json.Unmarshal([]byte(correlationChart.CorrelationData), &yData); e != nil {
+		msg = "获取失败"
+		errMsg = "相关性图表X轴数据有误, Err:" + e.Error()
+		return
+	}
+
+	// 获取指标信息
+	edbInfoMappingA, e := models.GetChartEdbMappingByEdbInfoId(correlationChart.EdbInfoIdFirst)
+	if e != nil {
+		msg = "获取失败"
+		errMsg = "获取相关性图表, A指标mapping信息失败, Err:" + e.Error()
+		return
+	}
+	edbInfoMappingB, e := models.GetChartEdbMappingByEdbInfoId(correlationChart.EdbInfoIdSecond)
+	if e != nil {
+		msg = "获取失败"
+		errMsg = "获取相关性图表, B指标mapping信息失败, Err:" + e.Error()
+		return
+	}
+
+	xEdbIdValue := xData
+	yDataList := make([]models.YData, 0)
+	yDate := "0000-00-00"
+	yDataList = append(yDataList, models.YData{
+		Date:  yDate,
+		Value: yData,
+	})
+
+	// 完善指标信息
+	edbList, e := correlationServ.GetChartEdbInfoFormat(chartInfo.ChartInfoId, edbInfoMappingA, edbInfoMappingB)
+	if e != nil {
+		msg = "获取失败"
+		errMsg = "获取相关性图表, 完善指标信息失败, Err:" + e.Error()
+		return
+	}
+	correlationInfo := new(models.CorrelationInfo)
+	correlationInfo.LeadValue = correlationChart.LeadValue
+	correlationInfo.LeadUnit = correlationChart.LeadUnit
+	correlationInfo.StartDate = correlationChart.StartDate.Format(utils.FormatDate)
+	correlationInfo.EndDate = correlationChart.EndDate.Format(utils.FormatDate)
+	correlationInfo.LeadValue = correlationChart.LeadValue
+	correlationInfo.EdbInfoIdFirst = correlationChart.EdbInfoIdFirst
+	correlationInfo.EdbInfoIdSecond = correlationChart.EdbInfoIdSecond
+
+	resp.ChartInfo = chartInfo
+	resp.EdbInfoList = edbList
+	resp.XEdbIdValue = xEdbIdValue
+	resp.YDataList = yDataList
+	resp.CorrelationChartInfo = correlationInfo
+
+	// 将数据加入缓存
+	if utils.Re == nil {
+		d, _ := json.Marshal(resp)
+		_ = utils.Rc.Put(key, d, 2*time.Hour)
+	}
+	isOk = true
+	return
+}
+
+// CorrelationChartInfoRefresh
+// @Title 商品价格图表刷新接口
+// @Description 商品价格图表刷新接口
+// @Param   UniqueCode   query   string  true       "图表唯一编码,如果是管理后台访问,传固定字符串:7c69b590249049942070ae9dcd5bf6dc"
+// @Success Ret=200 刷新成功
+// @router /correlation/refresh [get]
+func (this *ChartController) CorrelationChartInfoRefresh() {
+	br := new(models.BaseResponse).Init()
+	chartId := 0
+	defer func() {
+		// 添加日志
+		if chartId > 0 {
+			shareChartRefreshLogInfo := &models.ShareChartRefreshLog{
+				Ip:         this.Ctx.Input.IP(),
+				ChartId:    chartId,
+				CreateTime: time.Now(),
+			}
+			models.AddShareChartRefreshLog(shareChartRefreshLogInfo)
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	uniqueCode := this.GetString("UniqueCode")
+	if uniqueCode == "" {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,uniqueCode is empty"
+		return
+	}
+	chartInfo, err := models.GetChartInfoByUniqueCode(uniqueCode)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "该图已被删除,请刷新页面"
+			br.ErrMsg = "该图已被删除,请刷新页面,Err:" + err.Error()
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+	chartId = chartInfo.ChartInfoId
+
+	// 刷新相关性图表
+	if e := correlationServ.ChartInfoRefresh(chartInfo.ChartInfoId); e != nil {
+		br.Msg = "刷新失败"
+		br.ErrMsg = "刷新相关性图表失败, Err:" + e.Error()
+		return
+	}
+
+	//清除数据缓存
+	key := utils.HZ_CHART_LIB_DETAIL + uniqueCode
+	if utils.Re == nil {
+		_ = utils.Rc.Delete(key)
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "刷新成功"
+}

+ 17 - 5
models/chart.go

@@ -155,11 +155,12 @@ type QuarterData struct {
 }
 
 type ChartInfoDetailResp struct {
-	ChartInfo   *ChartInfo
-	EdbInfoList []*ChartEdbInfoMapping
-	XEdbIdValue []int   `description:"柱方图的x轴数据,指标id"`
-	YDataList   []YData `description:"柱方图的y轴数据"`
-	XDataList   []XData `description:"商品价格曲线的X轴数据"`
+	ChartInfo            *ChartInfo
+	EdbInfoList          []*ChartEdbInfoMapping
+	XEdbIdValue          []int            `description:"柱方图的x轴数据,指标id"`
+	YDataList            []YData          `description:"柱方图的y轴数据"`
+	XDataList            []XData          `description:"商品价格曲线的X轴数据"`
+	CorrelationChartInfo *CorrelationInfo `description:"相关性图表信息"`
 }
 
 // XData 商品价格曲线的的x轴数据
@@ -358,3 +359,14 @@ func AddCalculateQuarterV5(dataList []*EdbDataList) (result *EdbDataResult, err
 	}
 	return
 }
+
+type CorrelationInfo struct {
+	LeadValue       int    `description:"领先值"`
+	LeadUnit        string `description:"领先单位"`
+	StartDate       string `description:"开始日期"`
+	EndDate         string `description:"结束日期"`
+	EdbInfoIdFirst  int    `description:"A指标ID"`
+	EdbInfoIdSecond int    `description:"B指标ID"`
+	PeriodData      string `description:"X轴-期数数据"`
+	CorrelationData string `description:"Y轴-相关性系数"`
+}

+ 10 - 0
models/chart_edb_mapping.go

@@ -31,3 +31,13 @@ func GetFutureGoodEdbChartEdbMapping(chartInfoId int) (item *ChartEdbInfoMapping
 	err = o.Raw(sql, chartInfoId, utils.CHART_SOURCE_FUTURE_GOOD).QueryRow(&item)
 	return
 }
+
+// GetChartEdbMappingByEdbInfoId 根据指标id获取edb_mapping
+func GetChartEdbMappingByEdbInfoId(edbInfoId int) (item *ChartEdbInfoMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT edb_info_id,source_name,source,edb_code,edb_name,edb_name_en,frequency,unit,unit_en,start_date,end_date,modify_time,latest_date,latest_value,unique_code,edb_info_type AS edb_info_category_type,max_value,min_value
+             FROM edb_info
+			 WHERE edb_info_id = ? limit 1`
+	err = o.Raw(sql, edbInfoId).QueryRow(&item)
+	return
+}

+ 79 - 0
models/data_manage/chart_info_correlation.go

@@ -0,0 +1,79 @@
+package data_manage
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// ChartInfoCorrelation 相关性图表-扩展信息
+type ChartInfoCorrelation struct {
+	CorrelationChartInfoId int       `orm:"column(correlation_chart_info_id);pk" description:"相关性图表ID(chart_info表source=3的)"`
+	LeadValue              int       `description:"领先值"`
+	LeadUnit               string    `description:"领先单位"`
+	StartDate              time.Time `description:"开始日期"`
+	EndDate                time.Time `description:"结束日期"`
+	EdbInfoIdFirst         int       `description:"A指标ID"`
+	EdbInfoIdSecond        int       `description:"B指标ID"`
+	PeriodData             string    `description:"X轴-期数数据"`
+	CorrelationData        string    `description:"Y轴-相关性系数"`
+	CreateTime             time.Time `description:"创建时间"`
+	ModifyTime             time.Time `description:"更新时间"`
+}
+
+func (m *ChartInfoCorrelation) TableName() string {
+	return "chart_info_correlation"
+}
+
+func (m *ChartInfoCorrelation) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Insert(m)
+	if err != nil {
+		return
+	}
+	//m.CorrelationChartInfoId = int(id)
+	return
+}
+
+func (m *ChartInfoCorrelation) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ChartInfoCorrelation) Delete() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE correlation_chart_info_id = ? LIMIT 1`, m.TableName())
+	_, err = o.Raw(sql, m.CorrelationChartInfoId).Exec()
+	return
+}
+
+func (m *ChartInfoCorrelation) GetItemById(id int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE correlation_chart_info_id = ? LIMIT 1`, m.TableName())
+	err = o.Raw(sql, id).QueryRow(&m)
+	return
+}
+
+func (m *ChartInfoCorrelation) GetItemByCondition(condition string, pars []interface{}) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&m)
+	return
+}
+
+func (m *ChartInfoCorrelation) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ChartInfoCorrelation, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}

+ 1 - 1
models/data_manage/edb_data_base.go

@@ -11,7 +11,7 @@ func GetEdbDataTableName(source int) (tableName string) {
 		tableName = "edb_data_ths"
 	case utils.DATA_SOURCE_WIND:
 		tableName = "edb_data_wind"
-	case utils.DATA_SOURCE_PB, utils.DATA_SOURCE_PB_FINANCE:
+	case utils.DATA_SOURCE_PB, utils.DATA_SOURCE_PB_FINANCE: //彭博经济数据、彭博财务数据
 		tableName = "edb_data_pb"
 	case utils.DATA_SOURCE_CALCULATE:
 		tableName = "edb_data_calculate"

+ 30 - 0
models/data_manage/edb_info.go

@@ -103,3 +103,33 @@ func GetRefreshEdbInfoFromBase(edbInfoId, source int) (baseEdbInfoArr, calculate
 	}
 	return
 }
+
+type EdbInfoSearchData struct {
+	DataTime string  `description:"数据日期"`
+	Value    float64 `description:"数据"`
+}
+
+// GetEdbDataListAll
+// order:1升序,其余值为降序
+func GetEdbDataListAll(condition string, pars []interface{}, source, order int) (item []*EdbInfoSearchData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ``
+	tableName := GetEdbDataTableName(source)
+	if tableName == "" {
+		err = fmt.Errorf("指标来源有误, source: %d", source)
+		return
+	}
+	sql = ` SELECT * FROM %s WHERE 1=1 `
+	sql = fmt.Sprintf(sql, tableName)
+
+	if condition != "" {
+		sql += condition
+	}
+	if order == 1 {
+		sql += ` ORDER BY data_time ASC `
+	} else {
+		sql += ` ORDER BY data_time DESC `
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&item)
+	return
+}

+ 1 - 0
models/db.go

@@ -44,6 +44,7 @@ func init() {
 		new(data_manage.PredictEdbConf),                 //预测指标配置
 		new(data_manage.PredictEdbRuleData),             //预测指标配置生成的数据
 		new(data_manage.PredictEdbConfCalculateMapping), //预测指标关系表
+		new(data_manage.ChartInfoCorrelation),           // 图表相关性信息
 	)
 	// 期货数据库
 	initFutureGood()

+ 9 - 0
routers/commentsRouter.go

@@ -16,6 +16,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hongze_chart_lib/controllers:ChartController"] = append(beego.GlobalControllerRouter["hongze/hongze_chart_lib/controllers:ChartController"],
+        beego.ControllerComments{
+            Method: "CorrelationChartInfoRefresh",
+            Router: `/correlation/refresh`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["hongze/hongze_chart_lib/controllers:ChartController"] = append(beego.GlobalControllerRouter["hongze/hongze_chart_lib/controllers:ChartController"],
         beego.ControllerComments{
             Method: "ChartInfoDetail",

+ 378 - 0
services/data/correlation/chart_info.go

@@ -0,0 +1,378 @@
+package correlation
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/shopspring/decimal"
+	"hongze/hongze_chart_lib/models"
+	"hongze/hongze_chart_lib/models/data_manage"
+	"hongze/hongze_chart_lib/services/alarm_msg"
+	"hongze/hongze_chart_lib/services/data"
+	"hongze/hongze_chart_lib/utils"
+	"math"
+	"time"
+)
+
+// HandleDataByLinearRegression 线性方程插值法补全数据
+func HandleDataByLinearRegression(originList []*models.EdbDataList, handleDataMap map[string]float64) (newList []*models.EdbDataList, err error) {
+	if len(originList) < 2 {
+		return
+	}
+
+	var startEdbInfoData *models.EdbDataList
+	for _, v := range originList {
+		handleDataMap[v.DataTime] = v.Value
+
+		// 第一个数据就给过滤了,给后面的试用
+		if startEdbInfoData == nil {
+			startEdbInfoData = v
+			newList = append(newList, &models.EdbDataList{
+				DataTime: v.DataTime,
+				Value:    v.Value,
+			})
+			continue
+		}
+
+		// 获取两条数据之间相差的天数
+		startDataTime, _ := time.ParseInLocation(utils.FormatDate, startEdbInfoData.DataTime, time.Local)
+		currDataTime, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+		betweenHour := int(currDataTime.Sub(startDataTime).Hours())
+		betweenDay := betweenHour / 24
+
+		// 如果相差一天,那么过滤
+		if betweenDay <= 1 {
+			startEdbInfoData = v
+			newList = append(newList, &models.EdbDataList{
+				DataTime: v.DataTime,
+				Value:    v.Value,
+			})
+			continue
+		}
+
+		// 生成线性方程式
+		var a, b float64
+		{
+			coordinateData := make([]utils.Coordinate, 0)
+			tmpCoordinate1 := utils.Coordinate{
+				X: 1,
+				Y: startEdbInfoData.Value,
+			}
+			coordinateData = append(coordinateData, tmpCoordinate1)
+			tmpCoordinate2 := utils.Coordinate{
+				X: float64(betweenDay) + 1,
+				Y: v.Value,
+			}
+			coordinateData = append(coordinateData, tmpCoordinate2)
+
+			a, b = utils.GetLinearResult(coordinateData)
+			if math.IsNaN(a) || math.IsNaN(b) {
+				err = fmt.Errorf("线性方程公式生成失败")
+				return
+			}
+		}
+
+		// 生成对应的值
+		{
+			for i := 1; i < betweenDay; i++ {
+				tmpDataTime := startDataTime.AddDate(0, 0, i)
+				aDecimal := decimal.NewFromFloat(a)
+				xDecimal := decimal.NewFromInt(int64(i) + 1)
+				bDecimal := decimal.NewFromFloat(b)
+
+				val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
+				handleDataMap[tmpDataTime.Format(utils.FormatDate)] = val
+				newList = append(newList, &models.EdbDataList{
+					DataTime: tmpDataTime.Format(utils.FormatDate),
+					Value:    val,
+				})
+			}
+		}
+
+		startEdbInfoData = v
+	}
+	return
+}
+
+// MoveDataDaysToNewDataList 平移指标数据生成新的数据序列
+func MoveDataDaysToNewDataList(dataList []*models.EdbDataList, moveDay int) (newDataList []models.EdbDataList, dateDataMap map[string]float64) {
+	dateMap := make(map[time.Time]float64)
+	var minDate, maxDate time.Time
+	dateDataMap = make(map[string]float64)
+
+	for _, v := range dataList {
+		currDate, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+		if minDate.IsZero() || currDate.Before(minDate) {
+			minDate = currDate
+		}
+		if maxDate.IsZero() || currDate.After(maxDate) {
+			maxDate = currDate
+		}
+		dateMap[currDate] = v.Value
+	}
+
+	// 处理领先、滞后数据
+	newDateMap := make(map[time.Time]float64)
+	for currDate, value := range dateMap {
+		newDate := currDate.AddDate(0, 0, moveDay)
+		newDateMap[newDate] = value
+	}
+	minDate = minDate.AddDate(0, 0, moveDay)
+	maxDate = maxDate.AddDate(0, 0, moveDay)
+
+	// 获取日期相差日
+	dayNum := utils.GetTimeSubDay(minDate, maxDate)
+
+	for i := 0; i <= dayNum; i++ {
+		currDate := minDate.AddDate(0, 0, i)
+		tmpValue, ok := newDateMap[currDate]
+		if !ok {
+			//找不到数据,那么就用前面的数据吧
+			if len(newDataList)-1 < 0 {
+				tmpValue = 0
+			} else {
+				tmpValue = newDataList[len(newDataList)-1].Value
+			}
+		}
+		tmpData := models.EdbDataList{
+			DataTime: currDate.Format(utils.FormatDate),
+			Value:    tmpValue,
+		}
+		dateDataMap[tmpData.DataTime] = tmpData.Value
+		newDataList = append(newDataList, tmpData)
+	}
+	return
+}
+
+// GetChartEdbInfoFormat 相关性图表-获取指标信息
+func GetChartEdbInfoFormat(chartInfoId int, edbInfoMappingA, edbInfoMappingB *models.ChartEdbInfoMapping) (edbList []*models.ChartEdbInfoMapping, err error) {
+	edbList = make([]*models.ChartEdbInfoMapping, 0)
+	if edbInfoMappingA == nil || edbInfoMappingB == nil {
+		err = fmt.Errorf("指标信息有误")
+		return
+	}
+
+	edbInfoMappingA.FrequencyEn = data.GetFrequencyEn(edbInfoMappingA.Frequency)
+	if edbInfoMappingA.Unit == `无` {
+		edbInfoMappingA.Unit = ``
+	}
+	if edbInfoMappingB.Unit == `无` {
+		edbInfoMappingB.Unit = ``
+	}
+	if chartInfoId <= 0 {
+		edbInfoMappingA.IsAxis = 1
+		edbInfoMappingA.LeadValue = 0
+		edbInfoMappingA.LeadUnit = ""
+		edbInfoMappingA.ChartEdbMappingId = 0
+		edbInfoMappingA.ChartInfoId = 0
+		edbInfoMappingA.IsOrder = false
+		edbInfoMappingA.EdbInfoType = 1
+		edbInfoMappingA.ChartStyle = ""
+		edbInfoMappingA.ChartColor = ""
+		edbInfoMappingA.ChartWidth = 0
+
+		edbInfoMappingB.IsAxis = 1
+		edbInfoMappingB.LeadValue = 0
+		edbInfoMappingB.LeadUnit = ""
+		edbInfoMappingB.ChartEdbMappingId = 0
+		edbInfoMappingB.ChartInfoId = 0
+		edbInfoMappingB.IsOrder = false
+		edbInfoMappingB.EdbInfoType = 1
+		edbInfoMappingB.ChartStyle = ""
+		edbInfoMappingB.ChartColor = ""
+		edbInfoMappingB.ChartWidth = 0
+	} else {
+		edbInfoMappingA.LeadUnitEn = data.GetLeadUnitEn(edbInfoMappingA.LeadUnit)
+		edbInfoMappingB.LeadUnitEn = data.GetLeadUnitEn(edbInfoMappingB.LeadUnit)
+	}
+	edbList = append(edbList, edbInfoMappingA, edbInfoMappingB)
+	return
+}
+
+// GetChartDataByEdbInfo 相关性图表-根据指标信息获取x轴和y轴
+func GetChartDataByEdbInfo(edbInfoMappingA, edbInfoMappingB *models.ChartEdbInfoMapping, leadValue int, leadUnit, startDate, endDate string) (xEdbIdValue []int, yDataList []models.YData, err error) {
+	xData := make([]int, 0)
+	yData := make([]float64, 0)
+	if leadValue == 0 {
+		xData = append(xData, 0)
+	}
+	if leadValue > 0 {
+		leadMin := 0 - leadValue
+		xLen := 2*leadValue + 1
+		for i := 0; i < xLen; i++ {
+			n := leadMin + i
+			xData = append(xData, n)
+		}
+	}
+
+	// 若不同频, 则需要将低频指标变为高频
+	frequencyIntMap := map[string]int{
+		"日度": 1, "周度": 2, "旬度": 3, "月度": 4, "季度": 5, "年度": 6,
+	}
+	baseEdbInfo := edbInfoMappingA
+	changeEdbInfo := edbInfoMappingB
+	if edbInfoMappingA.Frequency != edbInfoMappingB.Frequency {
+		if frequencyIntMap[edbInfoMappingA.Frequency] >= frequencyIntMap[edbInfoMappingB.Frequency] {
+			baseEdbInfo = edbInfoMappingB
+			changeEdbInfo = edbInfoMappingA
+		}
+	}
+
+	// 获取时间基准指标在时间区间内的值
+	baseDataList := make([]*models.EdbDataList, 0)
+	switch baseEdbInfo.EdbInfoCategoryType {
+	case 0:
+		baseDataList, err = models.GetEdbDataList(baseEdbInfo.Source, baseEdbInfo.EdbInfoId, startDate, endDate)
+	case 1:
+		_, baseDataList, _, _, err, _ = data.GetPredictDataListByPredictEdbInfoId(baseEdbInfo.EdbInfoId, startDate, endDate, false)
+	default:
+		err = errors.New("指标base类型异常")
+		return
+	}
+
+	// 获取变频指标所有日期的值, 插值法完善数据
+	changeDataList := make([]*models.EdbDataList, 0)
+	switch changeEdbInfo.EdbInfoCategoryType {
+	case 0:
+		changeDataList, err = models.GetEdbDataList(changeEdbInfo.Source, changeEdbInfo.EdbInfoId, "", "")
+	case 1:
+		_, changeDataList, _, _, err, _ = data.GetPredictDataListByPredictEdbInfoId(changeEdbInfo.EdbInfoId, "", "", false)
+	default:
+		err = errors.New("指标change类型异常")
+		return
+	}
+	changeDataMap := make(map[string]float64)
+	newChangeDataList, e := HandleDataByLinearRegression(changeDataList, changeDataMap)
+	if e != nil {
+		err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
+		return
+	}
+
+	// 计算不领先也不滞后时的相关系数
+	baseCalculateData := make([]float64, 0)
+	baseDataTimeArr := make([]string, 0)
+	for i := range baseDataList {
+		baseDataTimeArr = append(baseDataTimeArr, baseDataList[i].DataTime)
+		baseCalculateData = append(baseCalculateData, baseDataList[i].Value)
+	}
+	zeroCalculateData := make([]float64, 0)
+	for i := range baseDataTimeArr {
+		zeroCalculateData = append(zeroCalculateData, changeDataMap[baseDataTimeArr[i]])
+	}
+	if len(baseCalculateData) != len(zeroCalculateData) {
+		err = fmt.Errorf("相关系数两组序列元素数不一致, %d-%d", len(baseCalculateData), len(zeroCalculateData))
+		return
+	}
+	zeroRatio := utils.CalculateCorrelationByIntArr(baseCalculateData, zeroCalculateData)
+	if leadValue == 0 {
+		yData = append(yData, zeroRatio)
+	}
+
+	// 计算领先/滞后N期
+	if leadValue > 0 {
+		// 平移变频指标领先/滞后的日期(单位天)
+		frequencyDaysMap := map[string]float64{
+			"天": 1, "周": 7, "月": 30, "季": 90, "年": 365,
+		}
+		moveUnitDays := frequencyDaysMap[leadUnit]
+
+		// 此处需注意, 图表代表的意思是A领先/滞后B指标的相关系数, 只是时间序列以高频的为基准
+		// 所以需要判断高频指标是A还是B
+		// 举例: 当高频指标为A时, 即B为变频, newChangeDataList为B的值, A领先B三天则mDays应为+3, 取B滞后三天的值与基准日期的时间相比较
+		for i := range xData {
+			if xData[i] == 0 {
+				yData = append(yData, zeroRatio)
+				continue
+			}
+			yCalculateData := make([]float64, 0)
+
+			// 判断是向前平移还是向后平移
+			mDays := 0
+			if baseEdbInfo.EdbInfoId == edbInfoMappingA.EdbInfoId {
+				mDays = int(moveUnitDays) * -xData[i]
+			} else {
+				mDays = int(moveUnitDays) * xData[i]
+			}
+			_, dMap := MoveDataDaysToNewDataList(newChangeDataList, mDays)
+
+			// 取出对应的基准日期的值
+			for i2 := range baseDataTimeArr {
+				yCalculateData = append(yCalculateData, dMap[baseDataTimeArr[i2]])
+			}
+			if len(baseCalculateData) != len(yCalculateData) {
+				err = fmt.Errorf("领先滞后相关系数两组序列元素数不一致, %d-%d", len(baseCalculateData), len(yCalculateData))
+				return
+			}
+
+			// 公式计算出领先/滞后频度对应点的相关性系数
+			ratio := utils.CalculateCorrelationByIntArr(baseCalculateData, yCalculateData)
+			yData = append(yData, ratio)
+		}
+	}
+
+	xEdbIdValue = xData
+	yDataList = make([]models.YData, 0)
+	yDate := "0000-00-00"
+	yDataList = append(yDataList, models.YData{
+		Date:  yDate,
+		Value: yData,
+	})
+	return
+}
+
+// ChartInfoRefresh 图表刷新
+func ChartInfoRefresh(chartInfoId int) (err error) {
+	var errMsg string
+	defer func() {
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("CorrelationChartInfoRefresh: "+errMsg, 3)
+		}
+	}()
+	correlationChart := new(data_manage.ChartInfoCorrelation)
+	if err = correlationChart.GetItemById(chartInfoId); err != nil {
+		errMsg = "获取相关性图表失败, Err: " + err.Error()
+		return
+	}
+
+	// 批量刷新ETA指标
+	err, errMsg = data.EdbInfoRefreshAllFromBase([]int{correlationChart.EdbInfoIdFirst, correlationChart.EdbInfoIdSecond}, false)
+	if err != nil {
+		return
+	}
+
+	// 重新生成数据并更新
+	edbInfoMappingA, err := models.GetChartEdbMappingByEdbInfoId(correlationChart.EdbInfoIdFirst)
+	if err != nil {
+		errMsg = "获取相关性图表, A指标mapping信息失败, Err:" + err.Error()
+		return
+	}
+	edbInfoMappingB, err := models.GetChartEdbMappingByEdbInfoId(correlationChart.EdbInfoIdSecond)
+	if err != nil {
+		errMsg = "获取相关性图表, B指标mapping信息失败, Err:" + err.Error()
+		return
+	}
+	periodData, correlationData, err := GetChartDataByEdbInfo(edbInfoMappingA, edbInfoMappingB, correlationChart.LeadValue, correlationChart.LeadUnit, correlationChart.StartDate.Format(utils.FormatDate), correlationChart.EndDate.Format(utils.FormatDate))
+	if err != nil {
+		errMsg = "获取相关性图表, 图表计算值失败, Err:" + err.Error()
+		return
+	}
+	periodDataByte, err := json.Marshal(periodData)
+	if err != nil {
+		errMsg = "相关性图表, X轴信息有误, Err:" + err.Error()
+		return
+	}
+	correlationDataByte, err := json.Marshal(correlationData[0].Value)
+	if err != nil {
+		errMsg = "相关性图表, Y轴信息有误, Err:" + err.Error()
+		return
+	}
+	correlationChart.PeriodData = string(periodDataByte)
+	correlationChart.CorrelationData = string(correlationDataByte)
+	correlationChart.ModifyTime = time.Now().Local()
+	correlationUpdateCols := []string{"PeriodData", "CorrelationData", "ModifyTime"}
+	if err = correlationChart.Update(correlationUpdateCols); err != nil {
+		errMsg = "更新相关性图表失败, Err:" + err.Error()
+		return
+	}
+	return
+}

+ 50 - 0
utils/calculate.go

@@ -1,5 +1,7 @@
 package utils
 
+import "math"
+
 // Series is a container for a series of data
 type Series []Coordinate
 
@@ -43,3 +45,51 @@ func GetLinearResult(s []Coordinate) (gradient, intercept float64) {
 
 	return
 }
+
+// CalculateCorrelationByIntArr 相关性计算
+// 计算步骤
+// 1.分别计算两个序列的平均值Mx和My
+// 2.分别计算两个序列的标准偏差SDx和SDy	=> √{1/(n-1)*SUM[(Xi-Mx)²]}
+// 3.计算相关系数	=> SUM[(Xi-Mx)*(Yi-My)]/[(N-1)(SDx*SDy)]
+func CalculateCorrelationByIntArr(xArr, yArr []float64) (ratio float64) {
+	// 序列元素数要一致
+	xLen := float64(len(xArr))
+	yLen := float64(len(yArr))
+	if xLen == 0 || xLen != yLen {
+		return
+	}
+
+	// 计算Mx和My
+	var Xa, Ya float64
+	for i := range xArr {
+		Xa += xArr[i]
+	}
+	Mx := Xa / xLen
+	for i := range yArr {
+		Ya += yArr[i]
+	}
+	My := Ya / yLen
+
+	// 计算标准偏差SDx和SDy
+	var Xb, Yb, SDx, SDy float64
+	for i := range xArr {
+		Xb += (xArr[i] - Mx) * (xArr[i] - Mx)
+	}
+	SDx = math.Sqrt(1 / (xLen - 1) * Xb)
+	for i := range yArr {
+		Yb += (yArr[i] - My) * (yArr[i] - My)
+	}
+	SDy = math.Sqrt(1 / (yLen - 1) * Yb)
+
+	// 计算相关系数
+	var Nume, Deno float64
+	for i := 0; i < int(xLen); i++ {
+		Nume += (xArr[i] - Mx) * (yArr[i] - My)
+	}
+	Deno = (xLen - 1) * (SDx * SDy)
+	ratio = Nume / Deno
+	if math.IsNaN(ratio) {
+		ratio = 0
+	}
+	return
+}

+ 52 - 51
utils/constants.go

@@ -32,57 +32,57 @@ const (
 
 // 数据来源渠道
 const (
-	DATA_SOURCE_THS                         = iota + 1 //同花顺
-	DATA_SOURCE_WIND                                   //wind
-	DATA_SOURCE_PB                                     //彭博
-	DATA_SOURCE_CALCULATE                              //指标运算
-	DATA_SOURCE_CALCULATE_LJZZY                        //累计值转月
-	DATA_SOURCE_CALCULATE_TBZ                          //同比值
-	DATA_SOURCE_CALCULATE_TCZ                          //同差值
-	DATA_SOURCE_CALCULATE_NSZYDPJJS                    //N数值移动平均计算
-	DATA_SOURCE_MANUAL                                 //手工指标
-	DATA_SOURCE_LZ                                     //隆众
-	DATA_SOURCE_YS                                     //有色
-	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_THS                          = iota + 1 //同花顺
+	DATA_SOURCE_WIND                                    //wind
+	DATA_SOURCE_PB                                      //彭博
+	DATA_SOURCE_CALCULATE                               //指标运算
+	DATA_SOURCE_CALCULATE_LJZZY                         //累计值转月
+	DATA_SOURCE_CALCULATE_TBZ                           //同比值
+	DATA_SOURCE_CALCULATE_TCZ                           //同差值
+	DATA_SOURCE_CALCULATE_NSZYDPJJS                     //N数值移动平均计算
+	DATA_SOURCE_MANUAL                                  //手工指标
+	DATA_SOURCE_LZ                                      //隆众
+	DATA_SOURCE_YS                                      //有色
+	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
@@ -133,4 +133,5 @@ var (
 const (
 	CHART_SOURCE_DEFAULT     = 1
 	CHART_SOURCE_FUTURE_GOOD = 2
+	CHART_SOURCE_CORRELATION = 3 // 相关性图表
 )