Răsfoiți Sursa

Merge branch 'eta_2.2.9_residual_analysis_1104@guomengyuan' into debug

# Conflicts:
#	utils/constants.go
gmy 5 luni în urmă
părinte
comite
20e1f86f55

+ 6 - 0
controllers/residual_analysis/residual_analysis.go

@@ -128,6 +128,12 @@ func (this *ResidualAnalysisController) SaveResidualAnalysis() {
 		return
 	}
 
+	if req.Source == 0 {
+		br.Msg = "来源不能为空"
+		br.ErrMsg = "来源不能为空"
+		return
+	}
+
 	err = residual_analysis_service.SaveResidualAnalysis(req, sysUser)
 	if err != nil {
 		br.Ret = 408

+ 29 - 23
models/residual_analysis_model/calculate_residual_analysis_config.go

@@ -3,14 +3,15 @@ package residual_analysis_model
 import (
 	"eta/eta_api/models/data_manage"
 	"github.com/beego/beego/v2/client/orm"
+	"time"
 )
 
 type CalculateResidualAnalysisConfig struct {
-	CalculateResidualAnalysisConfigId int    `orm:"column(calculate_residual_analysis_config_id);pk;auto" description:"自增id"`
-	Config                            string `orm:"column(config)" description:"计算参数配置"`
-	SysUserId                         int    `orm:"column(sys_user_id)" description:"操作人id"`
-	CreateTime                        string `orm:"column(create_time)" description:"创建时间"`
-	ModifyTime                        string `orm:"column(modify_time)" description:"修改时间"`
+	CalculateResidualAnalysisConfigId int       `orm:"column(calculate_residual_analysis_config_id);pk;auto" description:"自增id"`
+	Config                            string    `orm:"column(config)" description:"计算参数配置"`
+	SysUserId                         int       `orm:"column(sys_user_id)" description:"操作人id"`
+	CreateTime                        time.Time `orm:"column(create_time)" description:"创建时间"`
+	ModifyTime                        time.Time `orm:"column(modify_time)" description:"修改时间"`
 }
 
 func init() {
@@ -45,10 +46,14 @@ type ResidualAnalysisResp struct {
 	OriginalChartData ChartResp `description:"原始图数据"`
 	MappingChartData  ChartResp `description:"映射图数据"`
 	ResidualChartData ChartResp `description:"残差图数据"`
+	A                 float64   `description:"斜率"`
+	B                 float64   `description:"截距"`
+	R                 float64   `description:"相关系数"`
+	R2                float64   `description:"决定系数"`
 }
 
 type ChartResp struct {
-	ChartInfo   *ResidualAnalysisChartInfo
+	ChartInfo   ResidualAnalysisChartInfo
 	EdbInfoList []ResidualAnalysisChartEdbInfoMapping
 }
 
@@ -109,18 +114,19 @@ type ResidualAnalysisChartEdbInfoMapping struct {
 }
 
 type ResidualAnalysisIndexSaveReq struct {
-	EdbCode      string                 `description:"指标编码"`
-	EdbName      string                 `description:"指标名称"`
-	EdbNameEn    string                 `description:"英文指标名称"`
-	EdbType      int                    `description:"指标类型:1:基础指标,2:计算指标"`
-	Frequency    string                 `description:"频率"`
-	FrequencyEn  string                 `description:"英文频率"`
-	Unit         string                 `description:"单位"`
-	UnitEn       string                 `description:"英文单位"`
-	ClassifyId   int                    `description:"分类id"`
-	ConfigId     int                    `description:"残差配置id"`
-	ResidualType int                    `orm:"column(residual_type)" description:"残差类型: 1-映射残差 2-拟合残差"`
-	DataList     []ResidualAnalysisData `description:"指标数据"`
+	Source       int                       `description:"99-映射残差 100-拟合残差"`
+	EdbCode      string                    `description:"指标编码"`
+	EdbName      string                    `description:"指标名称"`
+	EdbNameEn    string                    `description:"英文指标名称"`
+	EdbType      int                       `description:"指标类型:1:基础指标,2:计算指标"`
+	Frequency    string                    `description:"频率"`
+	FrequencyEn  string                    `description:"英文频率"`
+	Unit         string                    `description:"单位"`
+	UnitEn       string                    `description:"英文单位"`
+	ClassifyId   int                       `description:"分类id"`
+	ConfigId     int                       `description:"残差配置id"`
+	ResidualType int                       `orm:"column(residual_type)" description:"残差类型: 1-映射残差 2-拟合残差"`
+	DataList     []edbDataResidualAnalysis `description:"指标数据"`
 }
 
 // ResidualAnalysisConfigVo 残差分析配置vo
@@ -150,15 +156,15 @@ type ResidualAnalysisDetailResp struct {
 
 // GetResidualAnalysisConfigById 根据配置id查询配置信息
 func GetResidualAnalysisConfigById(configId int) (residualAnalysisConfig CalculateResidualAnalysisConfig, err error) {
-	o := orm.NewOrm()
-	sql := "SELECT * FROM residual_analysis_config WHERE calculate_residual_analysis_config_id=?"
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM calculate_residual_analysis_config WHERE calculate_residual_analysis_config_id=?"
 	err = o.Raw(sql, configId).QueryRow(&residualAnalysisConfig)
 	return residualAnalysisConfig, nil
 }
 
 // UpdateResidualAnalysisConfig 更新配置信息
 func UpdateResidualAnalysisConfig(config CalculateResidualAnalysisConfig) (err error) {
-	o := orm.NewOrm()
+	o := orm.NewOrmUsingDB("data")
 	_, err = o.Update(config)
 	if err != nil {
 		return err
@@ -168,8 +174,8 @@ func UpdateResidualAnalysisConfig(config CalculateResidualAnalysisConfig) (err e
 
 // SaveResidualAnalysisConfig 保存配置信息
 func SaveResidualAnalysisConfig(config CalculateResidualAnalysisConfig) (id int64, err error) {
-	o := orm.NewOrm()
-	id, err = o.Insert(config)
+	o := orm.NewOrmUsingDB("data")
+	id, err = o.Insert(&config)
 	if err != nil {
 		return 0, err
 	}

+ 12 - 9
models/residual_analysis_model/calculate_residual_analysis_config_mapping.go

@@ -1,14 +1,17 @@
 package residual_analysis_model
 
-import "github.com/beego/beego/v2/client/orm"
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
 
 type CalculateResidualAnalysisConfigMapping struct {
-	CalculateResidualAnalysisConfigMappingId int    `orm:"column(calculate_residual_analysis_config_mapping_id);pk;auto" description:"自增id"`
-	CalculateResidualAnalysisConfigId        int    `orm:"column(calculate_residual_analysis_config_id)" description:"残差分析配置id"`
-	EdbInfoId                                int64  `orm:"column(edb_info_id)" description:"指标id"`
-	ResidualType                             int    `orm:"column(residual_type)" description:"残差类型: 1-映射残差 2-拟合残差"`
-	CreateTime                               string `orm:"column(create_time)" description:"创建时间"`
-	ModifyTime                               string `orm:"column(modify_time)" description:"修改时间"`
+	CalculateResidualAnalysisConfigMappingId int       `orm:"column(calculate_residual_analysis_config_mapping_id);pk;auto" description:"自增id"`
+	CalculateResidualAnalysisConfigId        int       `orm:"column(calculate_residual_analysis_config_id)" description:"残差分析配置id"`
+	EdbInfoId                                int64     `orm:"column(edb_info_id)" description:"指标id"`
+	ResidualType                             int       `orm:"column(residual_type)" description:"残差类型: 1-映射残差 2-拟合残差"`
+	CreateTime                               time.Time `orm:"column(create_time)" description:"创建时间"`
+	ModifyTime                               time.Time `orm:"column(modify_time)" description:"修改时间"`
 }
 
 func init() {
@@ -37,8 +40,8 @@ func GetConfigMappingListByCondition(condition string, pars []interface{}) (item
 
 // SaveConfigMapping 保存指标和配置的映射关系
 func SaveConfigMapping(mapping CalculateResidualAnalysisConfigMapping) (id int64, err error) {
-	o := orm.NewOrm()
-	id, err = o.Insert(mapping)
+	o := orm.NewOrmUsingDB("data")
+	id, err = o.Insert(&mapping)
 	if err != nil {
 		return 0, err
 	}

+ 15 - 10
models/residual_analysis_model/edb_data_residual_analysis.go

@@ -1,18 +1,23 @@
 package residual_analysis_model
 
-import "github.com/beego/beego/v2/client/orm"
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
 
-type ResidualAnalysisData struct {
-	EdbDataId     int     `orm:"column(edb_data_id);pk;auto" description:"自增id"`
-	EdbInfoId     int     `orm:"column(edb_info_id)" description:"指标id"`
-	EdbCode       string  `orm:"column(edb_code)" description:"指标编码"`
-	DataTime      string  `orm:"column(data_time)" description:"数据日期"`
-	Value         float64 `orm:"column(value)" description:"数据值"`
-	DataTimeStamp int64   `orm:"column(data_timestamp)"`
+type edbDataResidualAnalysis struct {
+	EdbDataId     int       `orm:"column(edb_data_id);pk;auto" description:"自增id"`
+	EdbInfoId     int       `orm:"column(edb_info_id)" description:"指标id"`
+	EdbCode       string    `orm:"column(edb_code)" description:"指标编码"`
+	DataTime      string    `orm:"column(data_time)" description:"数据日期"`
+	Value         float64   `orm:"column(value)" description:"数据值"`
+	CreateTime    time.Time `orm:"column(create_time)" description:"创建时间"`
+	ModifyTime    time.Time `orm:"column(modify_time)" description:"修改时间"`
+	DataTimeStamp int64     `orm:"column(data_timestamp)"`
 }
 
 func init() {
-	orm.RegisterModel(new(ResidualAnalysisData))
+	orm.RegisterModel(new(edbDataResidualAnalysis))
 }
 
 // DeleteResidualAnalysisDataByEdbCode 根据指标编码删除数据
@@ -24,7 +29,7 @@ func DeleteResidualAnalysisDataByEdbCode(edbCode string) error {
 }
 
 // AddResidualAnalysisData 新增指标数据
-func AddResidualAnalysisData(dataList []ResidualAnalysisData) (num int64, err error) {
+func AddResidualAnalysisData(dataList []edbDataResidualAnalysis) (num int64, err error) {
 	o := orm.NewOrmUsingDB("data")
 	num, err = o.InsertMulti(len(dataList), dataList)
 	if err != nil {

+ 194 - 66
services/residual_analysis_service/residual_analysis_service.go

@@ -7,6 +7,8 @@ import (
 	"eta/eta_api/models/system"
 	"eta/eta_api/utils"
 	"fmt"
+	"math"
+	"strconv"
 	"time"
 )
 
@@ -54,13 +56,13 @@ func ResidualAnalysisPreview(req residual_analysis_model.ResidualAnalysisReq) (r
 	resp := residual_analysis_model.ResidualAnalysisResp{}
 
 	// 图表基础信息
-	baseChartInfo := new(residual_analysis_model.ResidualAnalysisChartInfo)
+	/*baseChartInfo := new(residual_analysis_model.ResidualAnalysisChartInfo)
 	baseChartInfo.Calendar = `公历`
 	baseChartInfo.Source = utils.CHART_SOURCE_DEFAULT
 	baseChartInfo.DateType = req.DateType
 	baseChartInfo.StartDate = startDate
 	baseChartInfo.EndDate = endDate
-	baseChartInfo.ChartType = utils.CHART_TYPE_CURVE
+	baseChartInfo.ChartType = utils.CHART_TYPE_CURVE*/
 
 	// 原始图表信息
 	originalEdbList := make([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, 0)
@@ -69,10 +71,11 @@ func ResidualAnalysisPreview(req residual_analysis_model.ResidualAnalysisReq) (r
 	if err != nil {
 		return residual_analysis_model.ResidualAnalysisResp{}, err
 	}
-	baseChartInfo.ChartName = edbInfoMappingA.EdbName + "与" + edbInfoMappingB.EdbName
+
+	originalChartInfo := createChartInfoResp(req, startDate, endDate, edbInfoMappingA.EdbName+"与"+edbInfoMappingB.EdbName)
 
 	resp.OriginalChartData = residual_analysis_model.ChartResp{
-		ChartInfo:   baseChartInfo,
+		ChartInfo:   originalChartInfo,
 		EdbInfoList: originalEdbList,
 	}
 
@@ -86,34 +89,54 @@ func ResidualAnalysisPreview(req residual_analysis_model.ResidualAnalysisReq) (r
 	}
 
 	// 映射图表信息
-	mappingEdbList, err := fillMappingChartInfo(req, edbInfoMappingA, edbInfoMappingB, originalEdbList, indexADataMap)
+	mappingEdbList, a, b, r, err := fillMappingChartInfo(req, edbInfoMappingA, edbInfoMappingB, originalEdbList, indexADataMap)
 	if err != nil {
 		return residual_analysis_model.ResidualAnalysisResp{}, err
 	}
-	baseChartInfo.ChartName = edbInfoMappingA.EdbName + "与" + edbInfoMappingB.EdbName + "映射" + edbInfoMappingA.EdbName
+
+	mappingChartInfo := createChartInfoResp(req, startDate, endDate, edbInfoMappingA.EdbName+"与"+edbInfoMappingB.EdbName+"映射"+edbInfoMappingA.EdbName)
 
 	resp.MappingChartData = residual_analysis_model.ChartResp{
-		ChartInfo:   baseChartInfo,
+		ChartInfo:   mappingChartInfo,
 		EdbInfoList: mappingEdbList,
 	}
 
 	// 残差图表信息
-	ResidualEdbList, err := fillResidualChartInfo(edbInfoMappingA, edbInfoMappingB, mappingEdbList)
+	residualEdbList, R2, err := fillResidualChartInfo(edbInfoMappingA, edbInfoMappingB, mappingEdbList)
 	if err != nil {
 		return residual_analysis_model.ResidualAnalysisResp{}, err
 	}
 
-	baseChartInfo.ChartName = edbInfoMappingA.EdbName + "与" + edbInfoMappingA.EdbName + "映射残差/" + edbInfoMappingB.EdbName
+	residualChartInfo := createChartInfoResp(req, startDate, endDate, edbInfoMappingA.EdbName+"与"+edbInfoMappingA.EdbName+"映射残差/"+edbInfoMappingB.EdbName)
 
-	resp.MappingChartData = residual_analysis_model.ChartResp{
-		ChartInfo:   baseChartInfo,
-		EdbInfoList: ResidualEdbList,
+	resp.ResidualChartData = residual_analysis_model.ChartResp{
+		ChartInfo:   residualChartInfo,
+		EdbInfoList: residualEdbList,
+	}
+
+	if req.ResidualType == 2 {
+		resp.A = a
+		resp.B = b
+		resp.R = r
+		resp.R2 = R2
 	}
 
 	return resp, nil
 }
 
-func fillResidualChartInfo(edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, mappingEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, error) {
+func createChartInfoResp(req residual_analysis_model.ResidualAnalysisReq, startDate, endDate, chartName string) residual_analysis_model.ResidualAnalysisChartInfo {
+	return residual_analysis_model.ResidualAnalysisChartInfo{
+		Calendar:  `公历`,
+		Source:    utils.CHART_SOURCE_DEFAULT,
+		DateType:  req.DateType,
+		StartDate: startDate,
+		EndDate:   endDate,
+		ChartType: utils.CHART_TYPE_CURVE,
+		ChartName: chartName,
+	}
+}
+
+func fillResidualChartInfo(edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, mappingEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, float64, error) {
 	// 计算公式 映射残差 = 因变量指标 - 映射指标
 	var edbInfoA, edbInfoB residual_analysis_model.ResidualAnalysisChartEdbInfoMapping
 	if mappingEdbList[0].EdbInfoId == edbInfoMappingA.EdbInfoId {
@@ -125,26 +148,46 @@ func fillResidualChartInfo(edbInfoMappingA *data_manage.ChartEdbInfoMapping, edb
 	}
 	dataAList, ok := edbInfoA.DataList.([]*data_manage.EdbDataList)
 	if !ok {
-		return nil, fmt.Errorf("数据类型转换失败", nil)
+		return nil, 0, fmt.Errorf("数据类型转换失败", nil)
 	}
 	edbData := dataAList
 	dataBList, ok := edbInfoB.DataList.([]*data_manage.EdbDataList)
 	if !ok {
-		return nil, fmt.Errorf("数据类型转换失败", nil)
+		return nil, 0, fmt.Errorf("数据类型转换失败", nil)
 	}
 	var indexDataBMap = make(map[string]*data_manage.EdbDataList)
 	for _, data := range dataBList {
 		indexDataBMap[data.DataTime] = data
 	}
-	var valueB float64
+	// 求R2
+	var valueB, sumValueA, averageValueA, residualQuadraticSum, totalQuadraticSum, R2 float64
+
+	for _, indexData := range edbData {
+		// 因变量的值总和
+		sumValueA += indexData.Value
+	}
+	// 因变量平均值
+	averageValueA = sumValueA / float64(len(edbData))
+
 	for _, indexData := range edbData {
 		if dataB, ok := indexDataBMap[indexData.DataTime]; ok {
 			valueB = dataB.Value
 		} else {
 			valueB = 0
 		}
+
+		// 总因变量平方和
+		totalQuadraticSum += math.Pow(indexData.Value-averageValueA, 2)
+
+		// 补全残差值
 		indexData.Value = indexData.Value - valueB
+
+		// 残差平方和
+		residualQuadraticSum += math.Pow(indexData.Value, 2)
 	}
+	// 计算R2 公式:R2=1-SSE/SST R2越大,越符合线性  R2 = 1 - 残差平方和/总平方和
+	R2 = 1 - residualQuadraticSum/totalQuadraticSum
+
 	for _, mapping := range mappingEdbList {
 		if mapping.EdbInfoId != edbInfoMappingA.EdbInfoId {
 			mapping.DataList = edbData
@@ -152,32 +195,36 @@ func fillResidualChartInfo(edbInfoMappingA *data_manage.ChartEdbInfoMapping, edb
 		}
 	}
 
-	return mappingEdbList, nil
+	return mappingEdbList, R2, nil
 }
 
-func fillMappingChartInfo(req residual_analysis_model.ResidualAnalysisReq, edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, originalEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, indexADataMap map[string]*data_manage.EdbDataList) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, error) {
+func fillMappingChartInfo(req residual_analysis_model.ResidualAnalysisReq, edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, originalEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, indexADataMap map[string]*data_manage.EdbDataList) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, float64, float64, float64, error) {
 	// 计算公式:Y=aX+b,Y为映射后的指标,X为自变量指标
 	// 正序:a=(L2-L1)/(R2-R1)	b=L2-R2*a
 	// 逆序:a=(L2-L1)/(R1-R2)	b=L2-R1*a
 	// L2:左轴下限 R2:右轴上限 L1:左轴上限 R1:右轴下限
-	var a, b float64
-	if req.IsOrder {
-		a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMax - req.RightIndexMin)
-		b = req.LeftIndexMax - req.RightIndexMax*a
-	} else {
-		a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMin - req.RightIndexMax)
-		b = req.LeftIndexMax - req.RightIndexMin*a
+	var a, b, r float64
+
+	// 映射残差 计算a,b
+	if req.ResidualType == 1 {
+		if req.IsOrder {
+			a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMax - req.RightIndexMin)
+			b = req.LeftIndexMax - req.RightIndexMax*a
+		} else {
+			a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMin - req.RightIndexMax)
+			b = req.LeftIndexMax - req.RightIndexMin*a
+		}
 	}
 
 	dataList, ok := edbInfoMappingB.DataList.([]*data_manage.EdbDataList)
 	if !ok {
-		return nil, fmt.Errorf("数据类型转换失败", nil)
+		return nil, a, b, r, fmt.Errorf("数据类型转换失败", nil)
 	}
 
 	// 领先指标 dataList进行数据处理
 	if req.IndexType == 2 {
 		if req.LeadValue < 0 {
-			return nil, fmt.Errorf("领先值不能小于0", nil)
+			return nil, a, b, r, fmt.Errorf("领先值不能小于0", nil)
 		} else if req.LeadValue > 0 {
 			for _, indexData := range dataList {
 				switch req.LeadFrequency {
@@ -196,14 +243,12 @@ func fillMappingChartInfo(req residual_analysis_model.ResidualAnalysisReq, edbIn
 		}
 	}
 
+	// 指标B数据补充
 	for index := 0; index < len(dataList)-1; index++ {
 		// 获取当前数据和下一个数据
 		beforeIndexData := dataList[index]
 		afterIndexData := dataList[index+1]
 
-		// 计算映射值
-		beforeIndexData.Value = a*beforeIndexData.Value + b
-
 		// 从最早时间开始,补充时间为自然日
 		if utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
 			// 创建补充数据
@@ -217,27 +262,51 @@ func fillMappingChartInfo(req residual_analysis_model.ResidualAnalysisReq, edbIn
 			dataList = append(dataList, &replenishIndexData)
 		}
 	}
+
+	// 拟合残差 计算a,b
+	var coordinateList []utils.Coordinate
+	if req.ResidualType == 2 {
+		for _, indexData := range dataList {
+			if _, ok := indexADataMap[indexData.DataTime]; ok {
+
+				coordinate := utils.Coordinate{
+					X: indexData.Value,
+					Y: indexADataMap[indexData.DataTime].Value,
+				}
+				coordinateList = append(coordinateList, coordinate)
+			}
+		}
+		a, b = utils.GetLinearResult(coordinateList)
+		r = utils.CalculationDecisive(coordinateList)
+	}
+
 	// 根据指标A的时间key,在B的映射指标中筛选出对应的值
 	var dataBList []*data_manage.EdbDataList
+
 	for _, indexData := range dataList {
 		if _, ok := indexADataMap[indexData.DataTime]; ok {
+			// 计算指标B映射值
+			indexData.Value = math.Round(a*indexData.Value+b) / 10000
+
 			dataBList = append(dataBList, indexData)
 		}
 	}
 
-	mappingEdbList := originalEdbList
-	for _, mapping := range mappingEdbList {
-		if mapping.EdbInfoId == req.EdbInfoIdB {
-			mapping.EdbInfoId = 0
-			mapping.EdbCode = ""
-			mapping.EdbName = edbInfoMappingB.EdbName + "映射" + edbInfoMappingA.EdbName
-			mapping.DataList = dataList
+	mappingEdbList := make([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, len(originalEdbList))
+	copy(mappingEdbList, originalEdbList)
+
+	for i, mapping := range mappingEdbList {
+		if mapping.EdbInfoId != req.EdbInfoIdA {
+			mappingEdbList[i].EdbInfoId = 0
+			mappingEdbList[i].EdbCode = ""
+			mappingEdbList[i].EdbName = edbInfoMappingB.EdbName + "映射" + edbInfoMappingA.EdbName
+			mappingEdbList[i].DataList = dataBList
 		}
 	}
-	return mappingEdbList, nil
+	return mappingEdbList, a, b, r, nil
 }
 
-func fillOriginalChart(req residual_analysis_model.ResidualAnalysisReq, mappingList []*data_manage.ChartEdbInfoMapping, startDate string, endDate string, edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, OriginalEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, error) {
+func fillOriginalChart(req residual_analysis_model.ResidualAnalysisReq, mappingList []*data_manage.ChartEdbInfoMapping, startDate string, endDate string, edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, originalEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, error) {
 	for _, v := range mappingList {
 		var edbInfoMapping residual_analysis_model.ResidualAnalysisChartEdbInfoMapping
 		edbInfoMapping.EdbInfoType = 1
@@ -272,11 +341,15 @@ func fillOriginalChart(req residual_analysis_model.ResidualAnalysisReq, mappingL
 		edbInfoMapping.Frequency = v.Frequency
 		edbInfoMapping.Source = v.Source
 		edbInfoMapping.SourceName = v.SourceName
+		edbInfoMapping.MinValue = v.MinData
+		edbInfoMapping.MaxValue = v.MaxData
+		edbInfoMapping.LatestDate = v.LatestDate
+		edbInfoMapping.LatestValue = v.LatestValue
 
 		edbInfoMapping.DataList = dataList
-		OriginalEdbList = append(OriginalEdbList, edbInfoMapping)
+		originalEdbList = append(originalEdbList, edbInfoMapping)
 	}
-	return OriginalEdbList, nil
+	return originalEdbList, nil
 }
 
 func ContrastPreview(indexCode string) (residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, error) {
@@ -300,6 +373,7 @@ func ContrastPreview(indexCode string) (residual_analysis_model.ResidualAnalysis
 
 	var resp residual_analysis_model.ResidualAnalysisChartEdbInfoMapping
 	resp.EdbInfoId = edbInfo.EdbInfoId
+	resp.EdbCode = edbInfo.EdbCode
 	resp.SourceName = edbInfo.SourceName
 	resp.EdbName = edbInfo.EdbName
 	resp.Unit = edbInfo.Unit
@@ -319,17 +393,56 @@ func SaveResidualAnalysis(req residual_analysis_model.ResidualAnalysisIndexSaveR
 		return fmt.Errorf("分类不存在", nil)
 	}
 
+	// 获取指标数据最大值 最小值 最后更新时间 最后更新时间对应的值
+	var indexMax, indexMin, indexLatestValue float64
+	var indexLatestDate string
+	latestTime, _ := time.Parse(utils.YearMonthDay, req.DataList[0].DataTime)
+	for _, data := range req.DataList {
+		// 比较最大值
+		if data.Value > indexMax {
+			indexMax = data.Value
+		}
+
+		// 比较最小值
+		if data.Value < indexMin {
+			indexMin = data.Value
+		}
+
+		// 比较最新时间和对应值
+		currentTime, err := time.Parse(utils.YearMonthDay, data.DataTime)
+		if err != nil {
+			// 时间解析失败,跳过此项
+			continue
+		}
+
+		// 如果当前时间更晚
+		if currentTime.After(latestTime) {
+			latestTime = currentTime
+			indexLatestDate = data.DataTime
+			indexLatestValue = data.Value
+		}
+	}
+
+	var edbInfoId int64
+	var edbCode string
 	// 更新or新增
 	if req.EdbCode != "" {
 		// 查询指标库指标
-		edbInfo, err := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_RESIDUAL_ANALYSIS, req.EdbCode)
+		edbInfo, err := data_manage.GetEdbInfoByEdbCode(req.Source, req.EdbCode)
 		if err != nil {
 			return err
 		}
 		if edbInfo == nil {
 			return fmt.Errorf("指标不存在", nil)
 		}
-		// todo 须补充更新指标最大值,最小值,数据最新时间,
+		edbInfoId = int64(edbInfo.EdbInfoId)
+		edbCode = edbInfo.EdbCode
+
+		// 须补充更新指标最大值,最小值,数据最新时间,数据最新值
+		edbInfo.MaxValue = indexMax
+		edbInfo.MinValue = indexMin
+		edbInfo.LatestDate = indexLatestDate
+		edbInfo.LatestValue = indexLatestValue
 		err = edbInfo.Update([]string{"min_value", "max_value", "latest_date", "latest_value"})
 		if err != nil {
 			return err
@@ -340,30 +453,40 @@ func SaveResidualAnalysis(req residual_analysis_model.ResidualAnalysisIndexSaveR
 		if err != nil {
 			return fmt.Errorf("删除指标数据失败", nil)
 		}
-	}
-	// 新增指标
-	edbCode, err := utils.GenerateEdbCode(1, "")
-	if err != nil {
-		return err
-	}
+	} else {
+		// 新增指标
+		edbCode, err = utils.GenerateEdbCode(1, "")
+		if err != nil {
+			return err
+		}
 
-	_, err = data_manage.AddEdbInfo(&data_manage.EdbInfo{
-		EdbCode:    edbCode,
-		EdbName:    req.EdbName,
-		EdbNameEn:  req.EdbNameEn,
-		EdbType:    req.EdbType,
-		Unit:       req.Unit,
-		UnitEn:     req.UnitEn,
-		Frequency:  req.Frequency,
-		Source:     utils.DATA_SOURCE_RESIDUAL_ANALYSIS,
-		SourceName: "残差分析",
-	})
-	if err != nil {
-		return err
+		timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+
+		edbInfoId, err = data_manage.AddEdbInfo(&data_manage.EdbInfo{
+			EdbCode:    edbCode,
+			UniqueCode: utils.MD5(utils.CHART_PREFIX + "_" + timestamp),
+			EdbName:    req.EdbName,
+			EdbNameEn:  req.EdbNameEn,
+			ClassifyId: req.ClassifyId,
+			EdbType:    req.EdbType,
+			Unit:       req.Unit,
+			UnitEn:     req.UnitEn,
+			Frequency:  req.Frequency,
+			Source:     req.Source,
+			SourceName: "残差分析",
+		})
+		if err != nil {
+			return err
+		}
 	}
 
 	// 新增数据
-	edbInfoId, err := residual_analysis_model.AddResidualAnalysisData(req.DataList)
+	for i := range req.DataList {
+		req.DataList[i].EdbDataId = 0
+		req.DataList[i].EdbInfoId = int(edbInfoId)
+		req.DataList[i].EdbCode = edbCode
+	}
+	_, err = residual_analysis_model.AddResidualAnalysisData(req.DataList)
 	if err != nil {
 		return err
 	}
@@ -375,8 +498,9 @@ func SaveResidualAnalysis(req residual_analysis_model.ResidualAnalysisIndexSaveR
 	}
 	flag := true
 	for _, mapping := range mappingList {
-		if mapping.CalculateResidualAnalysisConfigId == req.ConfigId {
+		if mapping.EdbInfoId == edbInfoId {
 			flag = false
+			break
 		}
 	}
 
@@ -385,6 +509,8 @@ func SaveResidualAnalysis(req residual_analysis_model.ResidualAnalysisIndexSaveR
 			CalculateResidualAnalysisConfigId: req.ConfigId,
 			EdbInfoId:                         edbInfoId,
 			ResidualType:                      req.ResidualType,
+			CreateTime:                        time.Now(),
+			ModifyTime:                        time.Now(),
 		})
 		if err != nil {
 			return err
@@ -501,8 +627,10 @@ func SaveResidualAnalysisConfig(req residual_analysis_model.ResidualAnalysisReq,
 	}
 
 	analysisConfig := residual_analysis_model.CalculateResidualAnalysisConfig{
-		Config:    string(configJson),
-		SysUserId: sysUser.AdminId,
+		Config:     string(configJson),
+		SysUserId:  sysUser.AdminId,
+		CreateTime: time.Now(),
+		ModifyTime: time.Now(),
 	}
 
 	_, err = residual_analysis_model.SaveResidualAnalysisConfig(analysisConfig)

+ 3 - 2
utils/constants.go

@@ -187,8 +187,9 @@ const (
 	DATA_SOURCE_TRADE_ANALYSIS                       = 92       // 持仓分析
 	DATA_SOURCE_USDA_FAS                             = 96       //美国农业部->96
 	DATA_SOURCE_RZD                                  = 97       // 睿姿得数据
-	DATA_SOURCE_CALCULATE_STL                                  = 93       // 泛糖科技 -> 93
-	DATA_SOURCE_RESIDUAL_ANALYSIS                    = 99       // 残差分析
+	DATA_SOURCE_CALCULATE_STL                        = 98       // 泛糖科技 -> 93
+	DATA_SOURCE_MAPPING_RESIDUAL                     = 99       // 映射残差
+	DATA_SOURCE_FIT_RESIDUAL                         = 100      // 拟合残差
 )
 
 // 数据刷新频率