From bd4a52316d5c18dad0ce129ee178510084ee1e3c Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Sep 2025 18:14:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0KPI=E5=BF=99=E6=97=B6?= =?UTF-8?q?0609=E7=BB=9F=E8=AE=A1=E6=8E=A5=E5=8F=A3=E5=8F=8A=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=9C=8D=E5=8A=A1=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/network_data/controller/ims.go | 51 ++++++++++++-- src/modules/network_data/network_data.go | 4 ++ .../network_data/repository/kpi_report.go | 19 ++++- .../network_data/service/kpi_report.go | 69 +++++++++++++++++++ 4 files changed, 138 insertions(+), 5 deletions(-) diff --git a/src/modules/network_data/controller/ims.go b/src/modules/network_data/controller/ims.go index c4ca4431..ffe7d3d1 100644 --- a/src/modules/network_data/controller/ims.go +++ b/src/modules/network_data/controller/ims.go @@ -17,16 +17,18 @@ import ( // 实例化控制层 IMSController 结构体 var NewIMS = &IMSController{ - neInfoService: neService.NewNeInfo, - cdrEventService: neDataService.NewCDREvent, + neInfoService: neService.NewNeInfo, + cdrEventService: neDataService.NewCDREvent, + kpiReportService: neDataService.NewKpiReport, } // 网元IMS // // PATH /ims type IMSController struct { - neInfoService *neService.NeInfo // 网元信息服务 - cdrEventService *neDataService.CDREvent // CDR会话事件服务 + neInfoService *neService.NeInfo // 网元信息服务 + cdrEventService *neDataService.CDREvent // CDR会话事件服务 + kpiReportService *neDataService.KpiReport // 统计信息服务 } // CDR会话列表 @@ -236,3 +238,44 @@ func (s *IMSController) UeSessionList(c *gin.Context) { c.JSON(200, resp.OkData(data)) } + +// KPI 忙时统计 +// +// GET /kpi/busy-hour +// +// @Tags network_data/ims +// @Accept json +// @Produce json +// @Param neId query string true "NE ID" default(001) +// @Param timestamp query int64 false "timestamp" +// @Success 200 {object} object "Response Results" +// @Security TokenAuth +// @Summary Busy hour statistics +// @Description Busy hour statistics +// @Router /neData/ims/kpi/busy-hour [get] +func (s IMSController) KPIBusyHour(c *gin.Context) { + language := reqctx.AcceptLanguage(c) + var query struct { + NeID string `form:"neId" binding:"required"` + Timestamp int64 `form:"timestamp" binding:"required"` // 时间戳毫秒 年月日返回每小时的总和 年月日时返回该小时的总和 + } + if err := c.ShouldBindQuery(&query); err != nil { + errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err)) + c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs)) + return + } + if query.Timestamp < 1e12 || query.Timestamp > 1e13 { + c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "timestamp format is ms")) + return + } + + // 查询网元获取IP + neInfo := s.neInfoService.FindByNeTypeAndNeID("IMS", query.NeID) + if neInfo.NeId != query.NeID || neInfo.IP == "" { + c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + data := s.kpiReportService.IMSBusyHour(neInfo.RmUID, query.Timestamp) + c.JSON(200, resp.OkData(data)) +} diff --git a/src/modules/network_data/network_data.go b/src/modules/network_data/network_data.go index 61681b1b..9b2ff65b 100644 --- a/src/modules/network_data/network_data.go +++ b/src/modules/network_data/network_data.go @@ -184,6 +184,10 @@ func Setup(router *gin.Engine) { middleware.AuthorizeUser(nil), controller.NewIMS.UeSessionList, ) + imsGroup.GET("/kpi/busy-hour", + // middleware.AuthorizeUser(nil), + controller.NewIMS.KPIBusyHour, + ) } // 网元SMSC diff --git a/src/modules/network_data/repository/kpi_report.go b/src/modules/network_data/repository/kpi_report.go index 351814b4..1ff0b8b1 100644 --- a/src/modules/network_data/repository/kpi_report.go +++ b/src/modules/network_data/repository/kpi_report.go @@ -115,7 +115,24 @@ func (r KpiReport) SelectUPF(rmUID string, beginTime, endTime int64) []model.Kpi tx = tx.Where("created_at <= ?", endTime) // 查询数据 rows := []model.KpiReport{} - if err := tx.Select("kpi_values").Find(&rows).Error; err != nil { + if err := tx.Select("kpi_values", "created_at").Find(&rows).Error; err != nil { + logger.Errorf("query find err => %v", err.Error()) + return rows + } + return rows +} + +// SelectIMS 查询IMS数据 +func (r KpiReport) SelectIMS(rmUID string, beginTime, endTime int64) []model.KpiReport { + tx := db.DB("").Model(&model.KpiReport{}) + // 表名 + tx = tx.Table("kpi_report_ims") + tx = tx.Where("rm_uid = ?", rmUID) + tx = tx.Where("created_at >= ?", beginTime) + tx = tx.Where("created_at <= ?", endTime) + // 查询数据 + rows := []model.KpiReport{} + if err := tx.Select("kpi_values", "created_at").Find(&rows).Error; err != nil { logger.Errorf("query find err => %v", err.Error()) return rows } diff --git a/src/modules/network_data/service/kpi_report.go b/src/modules/network_data/service/kpi_report.go index 10e96e10..319d1ade 100644 --- a/src/modules/network_data/service/kpi_report.go +++ b/src/modules/network_data/service/kpi_report.go @@ -280,3 +280,72 @@ func (r KpiReport) UPFTodayFlowLoad(day int) { } } } + +// IMSBusyHour IMS忙时流量统计 +// SCSCF.06呼叫尝试次数 SCSCF.09呼叫成功次数 +func (r KpiReport) IMSBusyHour(rmUID string, timestamp int64) []map[string]any { + t := time.UnixMilli(timestamp) + beginTime := t + endTime := t + // 检查时分秒是否都为零 + if t.Hour() == 0 && t.Minute() == 0 && t.Second() == 0 { + // 获取当天起始时间(00:00:00) + beginTime = t.Truncate(time.Hour) + // 计算当天结束时间(23:59:59) + endTime = beginTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second) + } else { + // 起始时间:当前小时的 00 分 00 秒 + beginTime = t.Truncate(time.Hour) + // 结束时间:当前小时的 59 分 59 秒 999 毫秒 + endTime = beginTime.Add(time.Hour - time.Millisecond) + } + // 转换为毫秒级时间戳 + rows := r.kpiReportRepository.SelectIMS(rmUID, beginTime.UnixMilli(), endTime.UnixMilli()) + + // 创建一个map来存储按时间段合并后的数据 + timeGroup := make(map[int64]map[string]int64) + // 遍历每个数据项 + for _, row := range rows { + // 将毫秒时间戳转换为小时级时间戳(保留到小时的起始毫秒) + timeHour := row.CreatedAt / 3600000 * 3600000 // 1小时 = 3600000毫秒 + + // 解析 JSON 字符串为 map + var kpiValues []map[string]any + err := json.Unmarshal([]byte(row.KpiValues), &kpiValues) + if err != nil { + continue + } + + var callAttempts, callCompletions int64 + for _, v := range kpiValues { + if k, ok := v["kpiId"]; ok { + if k == "SCSCF.06" { + callAttempts = parse.Number(v["value"]) + } + if k == "SCSCF.09" { + callCompletions = parse.Number(v["value"]) + } + } + } + // 合并到对应的小时段 + if _, exists := timeGroup[timeHour]; !exists { + timeGroup[timeHour] = map[string]int64{ + "callAttempts": 0, + "callCompletions": 0, + } + } + timeGroup[timeHour]["callAttempts"] += callAttempts + timeGroup[timeHour]["callCompletions"] += callCompletions + } + + // 时间组合输出 + data := make([]map[string]any, 0, len(timeGroup)) + for hour, sums := range timeGroup { + data = append(data, map[string]any{ + "timeGroup": fmt.Sprintf("%d", hour), + "callAttempts": sums["callAttempts"], + "callCompletions": sums["callCompletions"], + }) + } + return data +}