package line_feature import ( "errors" "eta/eta_api/models/data_manage" "eta/eta_api/models/data_manage/line_feature" "eta/eta_api/models/data_manage/line_feature/response" "eta/eta_api/models/system" "eta/eta_api/services/data" "eta/eta_api/utils" "github.com/shopspring/decimal" "strconv" "strings" "time" ) // GetStandardDeviationData 获取标准差图表的指标数据 func GetStandardDeviationData(chartInfoId int, startDate, endDate string, 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, `公历`, startDate, endDate, []*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, startDate, endDate string, mappingInfo *data_manage.ChartEdbInfoMapping, calculateValue int, calculateUnit string, percentType int) (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, `公历`, startDate, endDate, []*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时不予计算。 if percentType == utils.PercentCalculateTypeRange { for i, tmpData := range dataList { currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local) maxVal := tmpData.Value minVal := tmpData.Value for k := 0; k < calculateDay; k++ { preVal, ok2 := dataMap[currDateTime.AddDate(0, 0, -k)] if ok2 { if preVal > maxVal { maxVal = preVal } if preVal < minVal { minVal = preVal } } } if maxVal == minVal { continue } //百分位=(现值-Min)/(Max-Min) tmpV := (tmpData.Value - minVal) / (maxVal - minVal) * 100 tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64() newDataList = append(newDataList, data_manage.EdbDataList{ EdbDataId: i, EdbInfoId: edb.EdbInfoId, DataTime: dataList[i].DataTime, DataTimestamp: dataList[i].DataTimestamp, Value: tmpV, }) if tmpV < edbMinVal { edbMinVal = tmpV } if tmpV > edbMaxVal { edbMaxVal = tmpV } } } // 百分位数据个数算法 // 数据区间第一个和最后一个数据点的时间和数据分别为(T1,S1)(T2,S2); N=T1到T2指标数据个数, n=小于等于S2的数据个数 // 个数百分位=(n-1)/(N-1) maxDay := len(dataList) // 往前找数据的边界 if percentType == utils.PercentCalculateTypeNum { for i, d := range dataList { // T2为当前日期 s2 := decimal.NewFromFloat(d.Value) t2, _ := time.ParseInLocation(utils.FormatDate, d.DataTime, time.Local) // 计算N和n var bigN, tinyN int for k := 0; k < maxDay; k++ { // 往前找(时间长度)个有数据的, N理论上只有最前面几个日期= calculateDay { break } preVal, preOk := dataMap[t2.AddDate(0, 0, -k)] if !preOk { continue } bigN += 1 if decimal.NewFromFloat(preVal).LessThanOrEqual(s2) { tinyN += 1 } } // N<=1时说明计算无效 if bigN <= 1 { continue } numerator := decimal.NewFromInt(int64(tinyN - 1)) denominator := decimal.NewFromInt(int64(bigN - 1)) // 因为是百分位所以这里是要*100, 跟之前的算法保持同步 percentVal, _ := numerator.Div(denominator).Mul(decimal.NewFromFloat(100)).Round(4).Float64() // 写进数组并判断指标最大最小值 newDataList = append(newDataList, data_manage.EdbDataList{ EdbDataId: i, EdbInfoId: edb.EdbInfoId, DataTime: dataList[i].DataTime, DataTimestamp: dataList[i].DataTimestamp, Value: percentVal, }) if percentVal < edbMinVal { edbMinVal = percentVal } if percentVal > edbMaxVal { edbMaxVal = percentVal } } } 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) { 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 } // 非自定义 if dateType != 8 { endDate = dataList[len(dataList)-1].DataTime endDateTime, err = time.ParseInLocation(utils.FormatDate, endDate, time.Local) if err != nil { return } //日期类型:1:最近3月;2:最近6月;3:最近1年;4:最近2年;5:最近3年;6:最近5年;7:最近10年,8:自定义时间 startDateTime = utils.GetDateByDateType2(dateType, endDateTime) startDate = startDateTime.Format(utils.FormatDate) newDataList := make([]*data_manage.EdbDataList, 0) for _, v := range dataList { tmpDataTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local) if tmpErr != nil { err = tmpErr return } if tmpDataTime.Equal(startDateTime) || tmpDataTime.After(startDateTime) { newDataList = append(newDataList, v) } } dataList = newDataList } maxVal := dataList[0].Value minVal := dataList[0].Value dataValMap := make(map[float64]int) total := 0 // 数据总量 for _, tmpData := range dataList { currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local) if (currDateTime.Equal(startDateTime) || currDateTime.After(startDateTime)) && (endDateTime.IsZero() || currDateTime.Before(endDateTime)) { if maxVal < tmpData.Value { maxVal = tmpData.Value } if minVal > tmpData.Value { minVal = tmpData.Value } num, ok := dataValMap[tmpData.Value] if ok { dataValMap[tmpData.Value] = num + 1 } else { dataValMap[tmpData.Value] = 1 } total++ } } if total <= 0 { errMsg = `没有数据` err = errors.New(errMsg) return } // 最大最小值 向上/下取整 minVal = utils.GetFloorNewNum(minVal, 2) maxVal = utils.GetCeilNewNum(maxVal, 2) //间距 spacing, _ := (decimal.NewFromFloat(maxVal).Sub(decimal.NewFromFloat(minVal))).Div(decimal.NewFromInt(int64(stepVal))).Float64() distributionDataNumMap := make(map[float64]int) for i := 1; i <= stepVal; i++ { tmpMinVal, _ := decimal.NewFromFloat(minVal).Add((decimal.NewFromFloat(spacing)).Mul(decimal.NewFromInt(int64(i - 1)))).Float64() tmpMaxVal, _ := decimal.NewFromFloat(minVal).Add((decimal.NewFromFloat(spacing)).Mul(decimal.NewFromInt(int64(i)))).Float64() XDataList = append(XDataList, tmpMinVal) distributionDataNumMap[tmpMinVal] = 0 for tmpVal, num := range dataValMap { if tmpMinVal <= tmpVal { // 最后一期数据是要小于等于 if i == stepVal { if tmpVal <= tmpMaxVal { distributionDataNumMap[tmpMinVal] += num } } else { if tmpVal < tmpMaxVal { distributionDataNumMap[tmpMinVal] += num } } } } } var minFrequency, maxFrequency float64 tmpNum := 0 for k, tmpMinVal := range XDataList { // 数量 frequencyYNum := distributionDataNumMap[tmpMinVal] // 频率 tmpFrequency, _ := decimal.NewFromInt(int64(frequencyYNum)).Div(decimal.NewFromInt(int64(total))).Mul(decimal.NewFromInt(100)).Round(4).Float64() Y1DataList = append(Y1DataList, response.FrequencyDistributionYData{ X: tmpMinVal, Y: tmpFrequency, }) if k == 0 { minFrequency = tmpFrequency maxFrequency = tmpFrequency } else { if tmpFrequency < minFrequency { minFrequency = tmpFrequency } if tmpFrequency > maxFrequency { maxFrequency = tmpFrequency } } // 累计数 tmpNum += frequencyYNum // 累计频率 tmpTotalFrequency, _ := decimal.NewFromInt(int64(tmpNum)).Div(decimal.NewFromInt(int64(total))).Mul(decimal.NewFromInt(100)).Round(4).Float64() Y2DataList = append(Y2DataList, response.FrequencyDistributionYData{ X: tmpMinVal, Y: tmpTotalFrequency, }) } newDataList := []response.FrequencyDistributionData{ { Name: "频率", NameEn: "Frequency", Unit: "%", UnitEn: "%", Value: Y1DataList, Color: "#00F", IsAxis: 1, }, { Name: "累计频率", NameEn: "Total Frequency", Unit: "%", UnitEn: "%", Value: Y2DataList, Color: "#F00", IsAxis: 0, }, } edbList[0].DataList = nil dataResp = response.FrequencyDistributionResp{ LeftMinValue: minFrequency, LeftMaxValue: maxFrequency, RightMinValue: 0, RightMaxValue: 100, DataList: newDataList, } return } // AddChartInfo 添加图表 func AddChartInfo(req data_manage.AddChartInfoReq, edbInfoMapping *data_manage.ChartEdbInfoMapping, source int, sysUser *system.Admin, lang string) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) { isSendEmail = true req.ChartName = strings.Trim(req.ChartName, " ") if req.ChartName == "" { errMsg = "请填写图表名称!" err = errors.New(errMsg) isSendEmail = false return } if req.ChartClassifyId <= 0 { errMsg = "分类参数错误!" err = errors.New(errMsg) isSendEmail = false return } chartClassify, err := data_manage.GetChartClassifyById(req.ChartClassifyId) if err != nil { if err.Error() == utils.ErrNoRow() { errMsg = "分类不存在" err = errors.New(errMsg) isSendEmail = false return } errMsg = "获取分类信息失败" err = errors.New("获取分类信息失败,Err:" + err.Error()) return } if chartClassify == nil { errMsg = "分类不存在" err = errors.New(errMsg) isSendEmail = false return } edbInfoIdArr := []int{edbInfoMapping.EdbInfoId} var edbInfoIdArrStr []string for _, v := range edbInfoIdArr { edbInfoIdArrStr = append(edbInfoIdArrStr, strconv.Itoa(v)) } edbInfoIdStr := strings.Join(edbInfoIdArrStr, ",") var chartInfoId int // 判断图表是否存在 { var condition string var pars []interface{} switch lang { case utils.EnLangVersion: condition += " AND chart_name_en = ? AND source = ? " default: condition += " AND chart_name = ? AND source = ? " } pars = append(pars, req.ChartName, source) count, tmpErr := data_manage.GetChartInfoCountByCondition(condition, pars) if tmpErr != nil { errMsg = "判断图表名称是否存在失败" err = errors.New("判断图表名称是否存在失败,Err:" + tmpErr.Error()) return } if count > 0 { errMsg = "图表已存在,请重新填写" err = errors.New(errMsg) isSendEmail = false return } } disableVal := data.CheckIsDisableChart(edbInfoIdArr) chartInfo = new(data_manage.ChartInfo) chartInfo.ChartName = req.ChartName chartInfo.ChartNameEn = req.ChartName chartInfo.EdbInfoIds = edbInfoIdStr chartInfo.ChartClassifyId = req.ChartClassifyId chartInfo.SysUserId = sysUser.AdminId chartInfo.SysUserRealName = sysUser.RealName chartInfo.CreateTime = time.Now() chartInfo.ModifyTime = time.Now() chartInfo.IsSetName = 0 timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) chartInfo.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp) chartInfo.ChartType = 9 // 相关性图 chartInfo.Calendar = "公历" chartInfo.DateType = req.DateType chartInfo.StartDate = req.StartDate chartInfo.EndDate = req.EndDate chartInfo.SeasonStartDate = req.StartDate chartInfo.SeasonEndDate = req.EndDate chartInfo.ChartImage = req.ChartImage chartInfo.LeftMin = req.LeftMin chartInfo.LeftMax = req.LeftMax chartInfo.RightMin = req.RightMin chartInfo.RightMax = req.RightMax chartInfo.Disabled = disableVal chartInfo.Source = source chartInfo.ExtraConfig = req.ExtraConfig // 指标信息 mapList := make([]*data_manage.ChartEdbMapping, 0) { mapItem := new(data_manage.ChartEdbMapping) mapItem.EdbInfoId = edbInfoMapping.EdbInfoId mapItem.CreateTime = time.Now() mapItem.ModifyTime = time.Now() mapItem.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + strconv.Itoa(edbInfoMapping.EdbInfoId)) mapItem.IsOrder = true mapItem.IsAxis = 1 mapItem.EdbInfoType = 1 mapItem.Source = utils.CHART_SOURCE_CORRELATION mapList = append(mapList, mapItem) } // 新增图表和指标mapping chartInfoId, e := line_feature.CreateLineFeatureChartAndEdb(chartInfo, mapList) if e != nil { errMsg = "操作失败" err = errors.New("新增相关性图表失败, Err: " + e.Error()) return } // 添加指标引用记录 _ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartInfo) //添加es数据 go data.EsAddOrEditChartInfo(chartInfoId) return } // EditChartInfo 编辑图表 func EditChartInfo(req data_manage.EditChartInfoReq, edbInfoMapping *data_manage.ChartEdbInfoMapping, sysUser *system.Admin, lang string) (chartItem *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) { isSendEmail = true chartItem, err = data_manage.GetChartInfoById(req.ChartInfoId) if err != nil { if err.Error() == utils.ErrNoRow() { errMsg = "图表已被删除,请刷新页面" err = errors.New(errMsg) isSendEmail = false return } errMsg = "获取图表信息失败" err = errors.New("获取图表信息失败,Err:" + err.Error()) return } if !utils.InArrayByInt([]int{utils.CHART_SOURCE_LINE_FEATURE_STANDARD_DEVIATION, utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE, utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY}, chartItem.Source) { errMsg = "该图不是统计分析图表!" err = errors.New(errMsg) isSendEmail = false return } req.ChartName = strings.Trim(req.ChartName, " ") if req.ChartClassifyId <= 0 { errMsg = "分类参数错误!" err = errors.New(errMsg) isSendEmail = false return } chartClassify, err := data_manage.GetChartClassifyById(req.ChartClassifyId) if err != nil { if err.Error() == utils.ErrNoRow() { errMsg = "分类不存在" err = errors.New(errMsg) isSendEmail = false return } errMsg = "获取分类信息失败" err = errors.New("获取分类信息失败,Err:" + err.Error()) return } if chartClassify == nil { errMsg = "分类不存在" err = errors.New(errMsg) isSendEmail = false return } // 图表操作权限 ok := data.CheckOpChartPermission(sysUser, chartItem.SysUserId, true) if !ok { errMsg = "没有该图表的操作权限" err = errors.New(errMsg) isSendEmail = false return } edbInfoIdArr := []int{edbInfoMapping.EdbInfoId} var edbInfoIdArrStr []string for _, v := range edbInfoIdArr { edbInfoIdArrStr = append(edbInfoIdArrStr, strconv.Itoa(v)) } edbInfoIdStr := strings.Join(edbInfoIdArrStr, ",") //判断图表是否存在 { var condition string var pars []interface{} condition += " AND chart_info_id <> ? AND source = ? " pars = append(pars, req.ChartInfoId, chartItem.Source) switch lang { case utils.EnLangVersion: condition += " AND chart_name_en = ?" default: condition += " AND chart_name=?" } pars = append(pars, req.ChartName) count, tmpErr := data_manage.GetChartInfoCountByCondition(condition, pars) if tmpErr != nil { errMsg = "判断图表名称是否存在失败" err = errors.New("判断图表名称是否存在失败,Err:" + tmpErr.Error()) return } if count > 0 { errMsg = "图表已存在,请重新填写" err = errors.New(errMsg) isSendEmail = false return } } // 图表启用与否 disableVal := data.CheckIsDisableChart(edbInfoIdArr) switch lang { case utils.EnLangVersion: if req.ChartNameEn == `` { req.ChartNameEn = req.ChartName } default: if req.ChartNameEn == `` { req.ChartNameEn = chartItem.ChartNameEn } } // 修改图表与指标mapping err = line_feature.EditLineFeatureChartAndMapping(&req, edbInfoIdStr, "公历", req.DateType, disableVal, req.ExtraConfig) if err != nil { errMsg = "保存失败" err = errors.New("保存失败,Err:" + err.Error()) return } resp := new(data_manage.AddChartInfoResp) resp.ChartInfoId = chartItem.ChartInfoId resp.UniqueCode = chartItem.UniqueCode resp.ChartType = req.ChartType // 添加指标引用记录 _ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartItem) //添加es数据 go data.EsAddOrEditChartInfo(chartItem.ChartInfoId) //修改my eta es数据 go data.EsAddOrEditMyChartInfoByChartInfoId(chartItem.ChartInfoId) return } // CopyChartInfo 复制图表 func CopyChartInfo(configId, configSource, classifyId int, chartName string, edbInfoMapping *data_manage.ChartEdbInfoMapping, oldChartInfo *data_manage.ChartInfo, sysUser *system.Admin, lang string) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) { isSendEmail = true multipleGraphConfig, err := data_manage.GetMultipleGraphConfigById(configId) if err != nil { return } multipleGraphConfig.MultipleGraphConfigId = 0 err = data_manage.AddMultipleGraphConfig(multipleGraphConfig) if err != nil { return } // 添加图 addChartReq := data_manage.AddChartInfoReq{ ChartClassifyId: classifyId, ChartName: chartName, ChartType: utils.CHART_TYPE_CURVE, Calendar: "公历", ExtraConfig: oldChartInfo.ExtraConfig, ChartImage: oldChartInfo.ChartImage, ChartThemeId: oldChartInfo.ChartThemeId, SourcesFrom: oldChartInfo.SourcesFrom, Instructions: oldChartInfo.Instructions, MarkersLines: oldChartInfo.MarkersLines, MarkersAreas: oldChartInfo.MarkersAreas, } chartSource := oldChartInfo.Source // 默认是相关性图 chartInfo, err, errMsg, isSendEmail = AddChartInfo(addChartReq, edbInfoMapping, chartSource, sysUser, lang) if err != nil { return } // 添加关系 multipleGraphConfigChartMapping := &data_manage.MultipleGraphConfigChartMapping{ //Id: 0, MultipleGraphConfigId: multipleGraphConfig.MultipleGraphConfigId, ChartInfoId: chartInfo.ChartInfoId, Source: configSource, ModifyTime: time.Now(), CreateTime: time.Now(), } err = data_manage.AddMultipleGraphConfigChartMapping(multipleGraphConfigChartMapping) if err != nil { return } //添加es数据 go data.EsAddOrEditChartInfo(chartInfo.ChartInfoId) return }