package residual_analysis_service import ( "encoding/json" "eta/eta_api/models/data_manage" "eta/eta_api/models/residual_analysis_model" "eta/eta_api/models/system" "eta/eta_api/utils" "fmt" "math" "sort" "strconv" "time" ) func ResidualAnalysisPreview(req residual_analysis_model.ResidualAnalysisReq) (residual_analysis_model.ResidualAnalysisResp, error) { mappingList, err := data_manage.GetChartEdbMappingListByEdbInfoIdList([]int{req.EdbInfoIdA, req.EdbInfoIdB}) if err != nil { return residual_analysis_model.ResidualAnalysisResp{}, fmt.Errorf("获取图表,指标信息失败,Err:%s", err.Error()) } var edbInfoMappingA, edbInfoMappingB *data_manage.ChartEdbInfoMapping for _, v := range mappingList { if v.Unit == "无" { v.Unit = "" } if v.EdbInfoId == req.EdbInfoIdA { edbInfoMappingA = v } if v.EdbInfoId == req.EdbInfoIdB { edbInfoMappingB = v } } if edbInfoMappingA == nil { return residual_analysis_model.ResidualAnalysisResp{}, fmt.Errorf("指标A不存在") } if edbInfoMappingB == nil { return residual_analysis_model.ResidualAnalysisResp{}, fmt.Errorf("指标B不存在") } // 时间处理 var startDate, endDate string switch req.DateType { case 0: startDate = req.StartDate endDate = req.EndDate case 1: startDate = req.StartDate endDate = "" default: startDate = utils.GetPreYearTime(req.DateType) endDate = "" } resp := residual_analysis_model.ResidualAnalysisResp{} // 原始图表信息 originalEdbList := make([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, 0) originalEdbList, fullADataList, fullBDataList, err := fillOriginalChart(req, mappingList, startDate, endDate, edbInfoMappingA, edbInfoMappingB, originalEdbList) if err != nil { return residual_analysis_model.ResidualAnalysisResp{}, err } originalChartInfo := createChartInfoResp(req, startDate, endDate, edbInfoMappingA.EdbName+"与"+edbInfoMappingB.EdbName) resp.OriginalChartData = residual_analysis_model.ChartResp{ ChartInfo: originalChartInfo, EdbInfoList: originalEdbList, } // 如果只需要第一张图表的数据 直接返回,避免继续处理 if req.QueryType == 1 { return resp, nil } dataAList, ok := edbInfoMappingA.DataList.([]*data_manage.EdbDataList) if !ok { return residual_analysis_model.ResidualAnalysisResp{}, fmt.Errorf("数据类型转换失败") } indexADataMap := map[string]*data_manage.EdbDataList{} for _, indexData := range dataAList { indexADataMap[indexData.DataTime] = indexData } // 映射图表信息 mappingEdbList, a, b, r, err := fillMappingChartInfo(req, edbInfoMappingA, edbInfoMappingB, originalEdbList, indexADataMap, startDate, endDate, fullADataList, fullBDataList) if err != nil { return residual_analysis_model.ResidualAnalysisResp{}, err } mappingChartInfo := createChartInfoResp(req, startDate, endDate, edbInfoMappingA.EdbName+"与"+edbInfoMappingB.EdbName+"映射"+edbInfoMappingA.EdbName) resp.MappingChartData = residual_analysis_model.ChartResp{ ChartInfo: mappingChartInfo, EdbInfoList: mappingEdbList, } // 残差图表信息 residualEdbList, R2, err := fillResidualChartInfo(req, edbInfoMappingA, edbInfoMappingB, mappingEdbList) if err != nil { return residual_analysis_model.ResidualAnalysisResp{}, err } residualChartInfo := createChartInfoResp(req, startDate, endDate, edbInfoMappingA.EdbName+"与"+edbInfoMappingA.EdbName+"映射残差/"+edbInfoMappingB.EdbName) resp.ResidualChartData = residual_analysis_model.ChartResp{ ChartInfo: residualChartInfo, EdbInfoList: residualEdbList, } if req.ResidualType == 2 { resp.A = math.Round(a*10000) / 10000 resp.B = math.Round(b*10000) / 10000 resp.R = math.Round(r*10000) / 10000 resp.R2 = math.Round(R2*10000) / 10000 } return resp, nil } 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(req residual_analysis_model.ResidualAnalysisReq, 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 { edbInfoA = mappingEdbList[0] edbInfoB = mappingEdbList[1] } else { edbInfoA = mappingEdbList[1] edbInfoB = mappingEdbList[0] } dataAList, ok := edbInfoA.DataList.([]*data_manage.EdbDataList) if !ok { return nil, 0, fmt.Errorf("数据类型转换失败") } edbData := make([]*data_manage.EdbDataList, len(dataAList)) for i, data := range dataAList { edbData[i] = &data_manage.EdbDataList{ Value: data.Value, // 确保为每个元素创建一个新的对象 DataTimestamp: data.DataTimestamp, DataTime: data.DataTime, EdbInfoId: data.EdbInfoId, EdbDataId: data.EdbDataId, } } dataBList, ok := edbInfoB.DataList.([]*data_manage.EdbDataList) if !ok { return nil, 0, fmt.Errorf("数据类型转换失败") } // 映射指标开始时间 var startTime string if len(dataBList) > 0 { startTime = dataBList[0].DataTime } var indexDataBMap = make(map[string]*data_manage.EdbDataList) for _, data := range dataBList { indexDataBMap[data.DataTime] = data } // 求R2 var valueB, sumValueA, averageValueA, residualQuadraticSum, totalQuadraticSum, R2 float64 for _, indexData := range edbData { // 因变量的值总和 sumValueA += indexData.Value } // 因变量平均值 averageValueA = sumValueA / float64(len(edbData)) var indexMax, indexMin float64 var edbDataResp []*data_manage.EdbDataList if len(edbData) > 0 { indexMax = edbData[0].Value indexMin = edbData[0].Value for _, indexData := range edbData { if dataB, ok := indexDataBMap[indexData.DataTime]; ok { valueB = dataB.Value } else { continue } // 总因变量平方和 totalQuadraticSum += math.Pow(indexData.Value-averageValueA, 2) // 补全残差值 indexData.Value = math.Round((indexData.Value-valueB)*10000) / 10000 // 残差平方和 residualQuadraticSum += math.Pow(indexData.Value, 2) if indexData.Value > indexMax { indexMax = indexData.Value } if indexData.Value < indexMin { indexMin = indexData.Value } // 获取映射指标之后的数据 if startTime != "" && utils.CompareDate(startTime, indexData.DataTime) { edbDataResp = append(edbDataResp, indexData) } } } // 计算R2 公式:R2=1-SSE/SST R2越大,越符合线性 R2 = 1 - 残差平方和/总平方和 R2 = 1 - residualQuadraticSum/totalQuadraticSum mappingEdb := make([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, len(mappingEdbList)) copy(mappingEdb, mappingEdbList) for i, mapping := range mappingEdb { if mapping.EdbInfoId != edbInfoMappingA.EdbInfoId { mappingEdb[i].DataList = edbDataResp mappingEdb[i].EdbName = edbInfoMappingA.EdbName + "映射残差/" + edbInfoMappingB.EdbName if req.IndexType == 2 { if req.LeadValue > 0 { mappingEdb[i].EdbName = edbInfoMappingA.EdbName + "映射残差/" + edbInfoMappingB.EdbName + "(领先" + strconv.Itoa(req.LeadValue) + req.LeadFrequency + ")" } } mappingEdb[i].IsAxis = 1 mappingEdb[i].ChartColor = `#00F` mappingEdb[i].IsOrder = false mappingEdb[i].MinValue = indexMin mappingEdb[i].MaxValue = indexMax } else { mappingEdb[i].IsAxis = 0 mappingEdb[i].ChartColor = `#F00` } } return mappingEdb, 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, startDate string, endDate string, fullADataList []*data_manage.EdbDataList, fullBDataList []*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, r float64 // 映射残差 计算a,b if req.ResidualType == 1 { if req.IsOrder { a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMin - req.RightIndexMax) b = req.LeftIndexMax - req.RightIndexMin*a } else { a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMax - req.RightIndexMin) b = req.LeftIndexMax - req.RightIndexMax*a } } //dataAList := edbInfoMappingA.DataList.([]*data_manage.EdbDataList) dataList, ok := edbInfoMappingB.DataList.([]*data_manage.EdbDataList) if !ok { return nil, a, b, r, fmt.Errorf("数据类型转换失败") } // 指标B数据补充 // 新建一个切片来保存补充的数据 var replenishDataList []*data_manage.EdbDataList for index := 0; index < len(dataList)-1; index++ { // 获取当前数据和下一个数据 beforeIndexData := dataList[index] afterIndexData := dataList[index+1] // 从最早时间开始,补充时间为自然日 for utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) { // 创建补充数据 nextDay := utils.GetNextDay(beforeIndexData.DataTime) toTime := utils.StringToTime(nextDay) replenishIndexData := data_manage.EdbDataList{ DataTime: nextDay, // 计算下一个自然日 DataTimestamp: toTime.UnixMilli(), Value: beforeIndexData.Value, // 可以选择使用前一天的值,或者其他逻辑来计算值 } // 将补充数据加入补充数据列表 replenishDataList = append(replenishDataList, &replenishIndexData) // 更新 beforeIndexData 为新创建的补充数据 beforeIndexData = &replenishIndexData } } // 将补充数据插入原始数据列表 dataList = append(dataList, replenishDataList...) // 排序 sort.Sort(ByDataTime(dataList)) // 拟合残差 计算a,b var coordinateList []utils.Coordinate var replenishADataList []*data_manage.EdbDataList var replenishBDataList []*data_manage.EdbDataList if req.ResidualType == 2 { // // 因变量指标也转换为日度 for index := 0; index < len(fullADataList)-1; index++ { // 获取当前数据和下一个数据 beforeIndexData := fullADataList[index] afterIndexData := fullADataList[index+1] replenishADataList = append(replenishADataList, beforeIndexData) // 从最早时间开始,补充时间为自然日 if utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) { for { // 创建补充数据 nextDay := utils.GetNextDay(beforeIndexData.DataTime) toTime := utils.StringToTime(nextDay) replenishIndexData := data_manage.EdbDataList{ DataTime: nextDay, // 计算下一个自然日 DataTimestamp: toTime.UnixMilli(), Value: beforeIndexData.Value, // 可以选择使用前一天的值,或者其他逻辑来计算值 } // 将补充数据加入补充数据列表 replenishADataList = append(replenishADataList, &replenishIndexData) // 更新 beforeIndexData 为新创建的补充数据 beforeIndexData = &replenishIndexData // 检查是否还需要继续补充数据 if !utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) { break } } } } replenishADataList = append(replenishADataList, fullADataList[len(fullADataList)-1]) // 自变量指标也转换为日度 for index := 0; index < len(fullBDataList)-1; index++ { // 获取当前数据和下一个数据 beforeIndexData := fullBDataList[index] afterIndexData := fullBDataList[index+1] replenishBDataList = append(replenishBDataList, beforeIndexData) // 从最早时间开始,补充时间为自然日 if utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) { for { // 创建补充数据 nextDay := utils.GetNextDay(beforeIndexData.DataTime) toTime := utils.StringToTime(nextDay) replenishIndexData := data_manage.EdbDataList{ DataTime: nextDay, // 计算下一个自然日 DataTimestamp: toTime.UnixMilli(), Value: beforeIndexData.Value, // 可以选择使用前一天的值,或者其他逻辑来计算值 } // 将补充数据加入补充数据列表 replenishBDataList = append(replenishBDataList, &replenishIndexData) // 更新 beforeIndexData 为新创建的补充数据 beforeIndexData = &replenishIndexData // 检查是否还需要继续补充数据 if !utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) { break } } } } replenishBDataList = append(replenishBDataList, fullBDataList[len(fullBDataList)-1]) // replenishADataList --> map replenishADataMap := make(map[string]*data_manage.EdbDataList) for _, indexData := range replenishADataList { if (utils.StringToTime(indexData.DataTime).After(utils.StringToTime(startDate)) || utils.StringToTime(indexData.DataTime).Equal(utils.StringToTime(startDate))) && (endDate == "" || utils.StringToTime(indexData.DataTime).Before(utils.StringToTime(endDate))) { replenishADataMap[indexData.DataTime] = indexData } } for _, indexData := range replenishBDataList { if _, ok = replenishADataMap[indexData.DataTime]; ok { coordinate := utils.Coordinate{ X: indexData.Value, Y: replenishADataMap[indexData.DataTime].Value, } coordinateList = append(coordinateList, coordinate) } } a, b = utils.GetLinearResult(coordinateList) r = utils.ComputeCorrelation(coordinateList) } // 填充映射指标值 使得时间长度一致 dataList = FillDataBList(dataList, edbInfoMappingA) // 根据指标A的时间key,在B的映射指标中筛选出对应的值 var dataBList []*data_manage.EdbDataList var indexMax, indexMin float64 if len(dataList) > 0 { indexMax = dataList[0].Value indexMin = dataList[0].Value for _, indexData := range dataList { if _, ok := indexADataMap[indexData.DataTime]; ok { indexDataCopy := *indexData // 计算指标B映射值 indexDataCopy.Value = math.Round((a*indexData.Value+b)*10000) / 10000 // 比较最大值 if indexData.Value > indexMax { indexMax = indexData.Value } // 比较最小值 if indexData.Value < indexMin { indexMin = indexData.Value } // 将副本添加到 dataBList dataBList = append(dataBList, &indexDataCopy) } } } 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].IsAxis = 1 mappingEdbList[i].EdbName = edbInfoMappingB.EdbName + "映射" + edbInfoMappingA.EdbName if req.IndexType == 2 { if req.LeadValue > 0 { mappingEdbList[i].EdbName = edbInfoMappingB.EdbName + "映射" + edbInfoMappingA.EdbName + "(领先" + strconv.Itoa(req.LeadValue) + req.LeadFrequency + ")" } } mappingEdbList[i].DataList = dataBList mappingEdbList[i].MinValue = indexMin mappingEdbList[i].MaxValue = indexMax } } return mappingEdbList, a, b, r, nil } // FillDataBList 填充B的数据 使得与A的时间保持一致 func FillDataBList(dataList []*data_manage.EdbDataList, edbInfoMappingA *data_manage.ChartEdbInfoMapping) []*data_manage.EdbDataList { dataAList, ok := edbInfoMappingA.DataList.([]*data_manage.EdbDataList) if !ok { return nil } for utils.StringToTime(dataList[len(dataList)-1].DataTime).Before(utils.StringToTime(dataAList[len(dataAList)-1].DataTime)) { // 使用A的时间填充时间差 timeDiff := utils.GetNextDayN(dataList[len(dataList)-1].DataTime, 1) // 创建新的数据点并填充 前值填充 newDataPoint := &data_manage.EdbDataList{ DataTime: timeDiff, Value: dataList[len(dataList)-1].Value, DataTimestamp: utils.StringToTime(timeDiff).UnixMilli(), } // 将新数据点添加到dataList末尾 dataList = append(dataList, newDataPoint) } return dataList } type ByDataTime []*data_manage.EdbDataList func (a ByDataTime) Len() int { return len(a) } func (a ByDataTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByDataTime) Less(i, j int) bool { t1 := utils.StringToTime(a[i].DataTime) t2 := utils.StringToTime(a[j].DataTime) return t1.Before(t2) } 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, []*data_manage.EdbDataList, []*data_manage.EdbDataList, error) { var fullADataList, fullBDataList []*data_manage.EdbDataList for _, v := range mappingList { var edbInfoMapping residual_analysis_model.ResidualAnalysisChartEdbInfoMapping edbInfoMapping.EdbInfoType = 1 edbInfoMapping.IsOrder = false edbInfoMapping.IsAxis = 1 edbInfoMapping.ChartColor = `#00F` edbInfoMapping.ChartWidth = 3 edbInfoMapping.EdbName = v.EdbName // 获取图表中的指标数据 dataList, err := data_manage.GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, startDate, endDate) if err != nil { return nil, nil, nil, fmt.Errorf("获取指标数据失败,Err:%s", err.Error()) } // 重新获取指标数据 产品要求需要和计算指标-拟合残差逻辑保持一致 fullDataList, err := data_manage.GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, "", "") if err != nil { return nil, nil, nil, fmt.Errorf("获取指标数据失败,Err:%s", err.Error()) } if v.EdbInfoId == req.EdbInfoIdB { edbInfoMapping.LeadValue = req.LeadValue edbInfoMapping.LeadUnit = req.LeadFrequency edbInfoMapping.EdbInfoType = req.IndexType edbInfoMapping.IsOrder = req.IsOrder edbInfoMapping.IsAxis = 0 edbInfoMapping.ChartColor = `#F00` edbInfoMapping.ChartWidth = 1 // 领先指标 dataList进行数据处理 if req.IndexType == 2 { if req.LeadValue < 0 { return nil, nil, nil, fmt.Errorf("领先值不能小于0") } else if req.LeadValue > 0 { edbInfoMapping.EdbName = v.EdbName + "(领先" + strconv.Itoa(req.LeadValue) + req.LeadFrequency + ")" for _, indexData := range dataList { switch req.LeadFrequency { case "天": indexData.DataTime = utils.GetNextDayN(indexData.DataTime, req.LeadValue) case "周": indexData.DataTime = utils.GetNextDayN(indexData.DataTime, req.LeadValue*7) case "月": indexData.DataTime = utils.TimeToString(utils.AddDate(utils.StringToTime(indexData.DataTime), 0, req.LeadValue), utils.YearMonthDay) case "季": indexData.DataTime = utils.TimeToString(utils.AddDate(utils.StringToTime(indexData.DataTime), 0, req.LeadValue*3), utils.YearMonthDay) case "年": indexData.DataTime = utils.TimeToString(utils.AddDate(utils.StringToTime(indexData.DataTime), req.LeadValue, 0), utils.YearMonthDay) } indexData.DataTimestamp = utils.StringToTime(indexData.DataTime).UnixMilli() } } } edbInfoMappingB.DataList = dataList fullBDataList = fullDataList } else { edbInfoMappingA.DataList = dataList fullADataList = fullDataList } edbInfoMapping.EdbInfoId = v.EdbInfoId edbInfoMapping.EdbCode = v.EdbCode edbInfoMapping.Unit = v.Unit edbInfoMapping.Frequency = v.Frequency edbInfoMapping.Source = v.Source edbInfoMapping.SourceName = v.SourceName edbInfoMapping.MinValue = v.MinValue edbInfoMapping.MaxValue = v.MaxValue edbInfoMapping.LatestDate = v.LatestDate edbInfoMapping.LatestValue = v.LatestValue edbInfoMapping.DataList = dataList originalEdbList = append(originalEdbList, edbInfoMapping) } return originalEdbList, fullADataList, fullBDataList, nil } func ContrastPreview(indexCode string) (residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, error) { var condition string var pars []interface{} if indexCode != "" { condition += " and edb_code=?" pars = append(pars, indexCode) } edbInfo, err := data_manage.GetEdbInfoByCondition(condition, pars) if err != nil { return residual_analysis_model.ResidualAnalysisChartEdbInfoMapping{}, err } if edbInfo == nil { return residual_analysis_model.ResidualAnalysisChartEdbInfoMapping{}, fmt.Errorf("指标不存在") } dataList, err := data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, "", "") var resp residual_analysis_model.ResidualAnalysisChartEdbInfoMapping resp.EdbInfoId = edbInfo.EdbInfoId resp.EdbCode = edbInfo.EdbCode resp.ChartColor = "#F00" resp.SourceName = edbInfo.SourceName resp.EdbName = edbInfo.EdbName resp.Unit = edbInfo.Unit resp.Frequency = edbInfo.Frequency resp.MinValue = edbInfo.MinValue resp.MaxValue = edbInfo.MaxValue resp.DataList = dataList return resp, nil } func SaveResidualAnalysis(req residual_analysis_model.ResidualAnalysisIndexSaveReq, sysUser *system.Admin) error { // 验证分类是否存在 classifyCount, err := data_manage.GetEdbClassifyCountById(req.ClassifyId) if err != nil { return err } if classifyCount <= 0 { return fmt.Errorf("分类不存在") } // 校验名称是否重复 var condition string var pars []interface{} condition += " and source = ? AND edb_name=?" pars = append(pars, req.Source, req.EdbName) edbInfoByCondition, err := data_manage.GetEdbInfoByCondition(condition, pars) if err != nil && err.Error() != utils.ErrNoRow() { return err } // 获取指标数据最大值 最小值 最后更新时间 最后更新时间对应的值 var indexMax, indexMin, indexLatestValue float64 var indexLatestDate string if len(req.DataList) > 0 { 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 } } } // 更新保存指标和配置的映射关系 mappingList, err := residual_analysis_model.GetConfigMappingListByConfigId(req.ConfigId) if err != nil { return err } // 判断是更新还是修改 看指标配置映射中,是否存在对应指标 存在 则更新 不存在 则新增 var edbInfoMapping residual_analysis_model.CalculateResidualAnalysisConfigMapping for _, mapping := range mappingList { if req.IndexType == mapping.IndexType && req.IndexType != 0 { edbInfoMapping = mapping } } var edbInfoId int64 var edbCode string // 更新or新增 if edbInfoMapping.EdbInfoId > 0 { // 查询指标库指标 edbInfo, err := data_manage.GetEdbInfoById(int(edbInfoMapping.EdbInfoId)) if err != nil { return err } if edbInfo == nil { return fmt.Errorf("指标不存在") } edbInfoId = int64(edbInfo.EdbInfoId) edbCode = edbInfo.EdbCode if edbInfoByCondition != nil && edbInfoByCondition.EdbInfoId != edbInfo.EdbInfoId { return fmt.Errorf("指标名称重复") } // 须补充更新指标最大值,最小值,数据最新时间,数据最新值 edbInfo.MaxValue = indexMax edbInfo.MinValue = indexMin edbInfo.LatestDate = indexLatestDate edbInfo.LatestValue = indexLatestValue edbInfo.Unit = req.Unit edbInfo.Frequency = req.Frequency edbInfo.ClassifyId = req.ClassifyId edbInfo.EdbName = req.EdbName err = edbInfo.Update([]string{"min_value", "max_value", "latest_date", "latest_value", "unit", "frequency", "classify_id", "edb_name"}) if err != nil { return err } // 删除对应得指标数据 err = residual_analysis_model.DeleteResidualAnalysisDataByEdbCode(edbInfo.EdbCode) if err != nil { return fmt.Errorf("删除指标数据失败") } } else { if edbInfoByCondition != nil { return fmt.Errorf("指标名称重复") } // 新增指标 edbCode, err = utils.GenerateEdbCode(1, "") 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: "残差分析", Calendar: req.Calendar, SysUserRealName: sysUser.RealName, SysUserId: sysUser.AdminId, LatestDate: indexLatestDate, LatestValue: indexLatestValue, MinValue: indexMin, MaxValue: indexMax, CreateTime: time.Now(), ModifyTime: time.Now(), }) if err != nil { return err } // 新增指标配置关系 _, err = residual_analysis_model.SaveConfigMapping(residual_analysis_model.CalculateResidualAnalysisConfigMapping{ CalculateResidualAnalysisConfigId: req.ConfigId, EdbInfoId: edbInfoId, ResidualType: req.ResidualType, IndexType: req.IndexType, CreateTime: time.Now(), ModifyTime: time.Now(), }) if err != nil { return err } } // 新增数据 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 } // 新增自变量 因变量与配置得关系 配置中不存在该指标,则新增 var indexMap = make(map[int64]residual_analysis_model.CalculateResidualAnalysisConfigMapping) for _, mapping := range mappingList { indexMap[mapping.EdbInfoId] = mapping } if _, ok := indexMap[int64(req.EdbInfoIdA)]; !ok { _, err = residual_analysis_model.SaveConfigMapping(residual_analysis_model.CalculateResidualAnalysisConfigMapping{ CalculateResidualAnalysisConfigId: req.ConfigId, EdbInfoId: int64(req.EdbInfoIdA), ResidualType: req.ResidualType, IndexType: 3, CreateTime: time.Now(), ModifyTime: time.Now(), }) if err != nil { return err } } if _, ok := indexMap[int64(req.EdbInfoIdB)]; !ok { _, err = residual_analysis_model.SaveConfigMapping(residual_analysis_model.CalculateResidualAnalysisConfigMapping{ CalculateResidualAnalysisConfigId: req.ConfigId, EdbInfoId: int64(req.EdbInfoIdB), ResidualType: req.ResidualType, IndexType: 4, CreateTime: time.Now(), ModifyTime: time.Now(), }) if err != nil { return err } } return nil } func ResidualAnalysisDetail(edbInfoId int) (residual_analysis_model.ResidualAnalysisDetailResp, error) { // 通过指标配置映射表 拿到配置id,再获取关联的所有指标信息 var condition string var pars []interface{} condition += " and edb_info_id=?" pars = append(pars, edbInfoId) mappingList, err := residual_analysis_model.GetConfigMappingListByCondition(condition, pars) if err != nil { return residual_analysis_model.ResidualAnalysisDetailResp{}, err } if len(mappingList) <= 0 { return residual_analysis_model.ResidualAnalysisDetailResp{}, fmt.Errorf("指标不存在") } mapping := mappingList[0] configMappingList, err := residual_analysis_model.GetConfigMappingListByConfigId(mapping.CalculateResidualAnalysisConfigId) if err != nil { return residual_analysis_model.ResidualAnalysisDetailResp{}, err } var edbInfoIdList []int64 var edbInfoMap = make(map[int64]residual_analysis_model.CalculateResidualAnalysisConfigMapping) var mappgingFlag = false var residualFlag = false for _, v := range configMappingList { edbInfoIdList = append(edbInfoIdList, v.EdbInfoId) edbInfoMap[v.EdbInfoId] = v if v.IndexType == 1 { mappgingFlag = true } else if v.IndexType == 2 { residualFlag = true } } condition = "" pars = []interface{}{} condition += ` and edb_info_id in(` + utils.GetOrmInReplace(len(edbInfoIdList)) + `)` for _, id := range edbInfoIdList { pars = append(pars, id) } edbInfoList, err := data_manage.GetEdbInfoListByCond(condition, pars) if err != nil { return residual_analysis_model.ResidualAnalysisDetailResp{}, err } // 获取配置 configInfo, err := residual_analysis_model.GetResidualAnalysisConfigById(mapping.CalculateResidualAnalysisConfigId) if err != nil { return residual_analysis_model.ResidualAnalysisDetailResp{}, err } var edbInfoListResp []*residual_analysis_model.DetailEdbInfoList var dependentEdbInfo residual_analysis_model.DetailEdbInfoList var independentEdbInfo residual_analysis_model.DetailEdbInfoList for _, edbInfo := range edbInfoList { var indexType int if _, ok := edbInfoMap[int64(edbInfo.EdbInfoId)]; ok { indexType = edbInfoMap[int64(edbInfo.EdbInfoId)].IndexType } info := residual_analysis_model.DetailEdbInfoList{ EdbInfoId: edbInfo.EdbInfoId, EdbInfoType: edbInfo.EdbInfoType, IndexType: indexType, SourceName: edbInfo.SourceName, Source: edbInfo.Source, EdbCode: edbInfo.EdbCode, EdbName: edbInfo.EdbName, EdbNameEn: edbInfo.EdbNameEn, Unit: edbInfo.Unit, UnitEn: edbInfo.UnitEn, Frequency: edbInfo.Frequency, FrequencyEn: edbInfo.FrequencyEn, ClassifyId: edbInfo.ClassifyId, } edbInfoListResp = append(edbInfoListResp, &info) if indexType == 3 { dependentEdbInfo = info } else if indexType == 4 { independentEdbInfo = info } } // 补充表格中 映射指标或者残差指标 if mappgingFlag && !residualFlag { info := residual_analysis_model.DetailEdbInfoList{ IndexType: 2, EdbName: independentEdbInfo.EdbName + "映射残差/" + dependentEdbInfo.EdbName, EdbNameEn: dependentEdbInfo.EdbNameEn, Unit: dependentEdbInfo.Unit, UnitEn: dependentEdbInfo.UnitEn, Frequency: dependentEdbInfo.Frequency, FrequencyEn: dependentEdbInfo.FrequencyEn, ClassifyId: dependentEdbInfo.ClassifyId, } edbInfoListResp = append(edbInfoListResp, &info) } else if !mappgingFlag && residualFlag { info := residual_analysis_model.DetailEdbInfoList{ IndexType: 1, EdbName: dependentEdbInfo.EdbName + "映射" + independentEdbInfo.EdbName, EdbNameEn: dependentEdbInfo.EdbNameEn, Unit: dependentEdbInfo.Unit, UnitEn: dependentEdbInfo.UnitEn, Frequency: dependentEdbInfo.Frequency, FrequencyEn: dependentEdbInfo.FrequencyEn, ClassifyId: dependentEdbInfo.ClassifyId, } edbInfoListResp = append(edbInfoListResp, &info) } resp := residual_analysis_model.ResidualAnalysisDetailResp{ ConfigInfo: &configInfo, EdbInfoList: edbInfoListResp, ResidualType: mapping.ResidualType, } return resp, nil } func SaveResidualAnalysisConfig(req residual_analysis_model.ResidualAnalysisReq, sysUser *system.Admin) (int64, error) { config := residual_analysis_model.ResidualAnalysisConfigVo{ DateType: req.DateType, StartDate: req.StartDate, EndDate: req.EndDate, IsOrder: req.IsOrder, IndexType: req.IndexType, LeadValue: req.LeadValue, LeadFrequency: req.LeadFrequency, LeftIndexMin: req.LeftIndexMin, LeftIndexMax: req.LeftIndexMax, RightIndexMin: req.RightIndexMin, RightIndexMax: req.RightIndexMax, ResidualIndexMin: req.ResidualIndexMin, ResidualIndexMax: req.ResidualIndexMax, ContrastIndexMin: req.ContrastIndexMin, ContrastIndexMax: req.ContrastIndexMax, } // 转换为json格式 configJson, err := json.Marshal(config) if err != nil { return 0, err } // 新增or更新 /* var condition string var pars []interface{} if req.EdbInfoId > 0 { condition += " and edb_info_id=?" pars = append(pars, req.EdbInfoId) configMappings, err := residual_analysis_model.GetConfigMappingListByCondition(condition, pars) if err != nil { return 0, err } if len(configMappings) > 0 { mapping := configMappings[0] configInfo, err := residual_analysis_model.GetResidualAnalysisConfigById(mapping.CalculateResidualAnalysisConfigId) if err != nil { return 0, err } configInfo.Config = string(configJson) err = residual_analysis_model.UpdateResidualAnalysisConfig(configInfo) if err != nil { return 0, err } } }*/ var configId int64 if req.ConfigId > 0 { configInfo, err := residual_analysis_model.GetResidualAnalysisConfigById(req.ConfigId) if err != nil { return 0, err } if configInfo.CalculateResidualAnalysisConfigId == 0 { return 0, fmt.Errorf("未找到配置信息") } configId = int64(configInfo.CalculateResidualAnalysisConfigId) configInfo.Config = string(configJson) err = residual_analysis_model.UpdateResidualAnalysisConfig(configInfo) if err != nil { return 0, err } } else { analysisConfig := residual_analysis_model.CalculateResidualAnalysisConfig{ Config: string(configJson), SysUserId: sysUser.AdminId, CreateTime: time.Now(), ModifyTime: time.Now(), } configId, err = residual_analysis_model.SaveResidualAnalysisConfig(analysisConfig) if err != nil { return 0, err } } return configId, nil } func CheckResidualAnalysisExist(configId int) (int64, error) { configMappingList, err := residual_analysis_model.GetConfigMappingListByConfigId(configId) if err != nil { return 0, err } var configMapping residual_analysis_model.CalculateResidualAnalysisConfigMapping for _, mapping := range configMappingList { if mapping.IndexType == 2 { configMapping = mapping } } return configMapping.EdbInfoId, nil }