diff --git a/src/modules/network_data/controller/ims.go b/src/modules/network_data/controller/ims.go index 9a59c461..969ff255 100644 --- a/src/modules/network_data/controller/ims.go +++ b/src/modules/network_data/controller/ims.go @@ -340,3 +340,85 @@ func (s IMSController) KPIBusyWeek(c *gin.Context) { data := s.kpiReportService.IMSBusyWeek(neInfo.RmUID, query.WeekStart, query.WeekEnd) c.JSON(200, resp.OkData(data)) } + +// CDR MOS +// +// GET /cdr/mos-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 MOS hour statistics +// @Description MOS hour statistics +// @Router /neData/ims/cdr/mos-hour [get] +func (s IMSController) CDRMOSHour(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.cdrEventService.IMSCDRMOSHour(neInfo.RmUID, query.Timestamp) + c.JSON(200, resp.OkData(data)) +} + +// CDR Call Connection Time +// +// GET /cdr/cct-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 MOS hour statistics +// @Description MOS hour statistics +// @Router /neData/ims/cdr/cct-hour [get] +func (s IMSController) CDRCCTHour(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.cdrEventService.IMSCDRCCTHour(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 3ac2b2b2..72d83484 100644 --- a/src/modules/network_data/network_data.go +++ b/src/modules/network_data/network_data.go @@ -192,6 +192,14 @@ func Setup(router *gin.Engine) { middleware.AuthorizeUser(nil), controller.NewIMS.KPIBusyWeek, ) + imsGroup.GET("/cdr/mos-hour", + middleware.AuthorizeUser(nil), + controller.NewIMS.CDRMOSHour, + ) + imsGroup.GET("/cdr/cct-hour", + middleware.AuthorizeUser(nil), + controller.NewIMS.CDRCCTHour, + ) } // 网元SMSC diff --git a/src/modules/network_data/repository/cdr_event.go b/src/modules/network_data/repository/cdr_event.go index 37ef1c33..ae9cae73 100644 --- a/src/modules/network_data/repository/cdr_event.go +++ b/src/modules/network_data/repository/cdr_event.go @@ -74,6 +74,9 @@ func (r CDREvent) SelectByPage(neType string, query map[string]string) ([]model. tx = tx.Where("JSON_EXTRACT(cdr_json, '$.servedMSISDN') like ?", fmt.Sprintf("%%%s%%", v)) } case "IMS": + if v, ok := query["recordType"]; ok && v != "" { + tx = tx.Where("JSON_EXTRACT(cdr_json, '$.recordType') = ?", fmt.Sprintf("'%s'", v)) + } if v, ok := query["callerParty"]; ok && v != "" { tx = tx.Where("JSON_EXTRACT(cdr_json, '$.callerParty') like ?", fmt.Sprintf("%%%s%%", v)) } diff --git a/src/modules/network_data/service/cdr_event.go b/src/modules/network_data/service/cdr_event.go index 952e0ebf..06afc70a 100644 --- a/src/modules/network_data/service/cdr_event.go +++ b/src/modules/network_data/service/cdr_event.go @@ -5,6 +5,7 @@ import ( "fmt" "strconv" "strings" + "time" "be.ems/src/framework/i18n" "be.ems/src/framework/logger" @@ -643,3 +644,153 @@ func (r CDREvent) ExportIMS(rows []model.CDREvent, fileName, language string) (s // 导出数据表格 return file.WriteSheet(headerCells, dataCells, fileName, "") } + +// IMSCDRMOSHour CDR MOS 统计 +func (r CDREvent) IMSCDRMOSHour(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) + } + + query := map[string]string{ + "rmUID": rmUID, + "recordType": "MOC", + "beginTime": fmt.Sprintf("%d", beginTime.Unix()), + "endTime": fmt.Sprintf("%d", endTime.Unix()), + } + rows, total := r.cdrEventRepository.SelectByPage("IMS", query) + if total == 0 { + return []map[string]any{} + } + // 创建一个map来存储按时间段合并后的数据 + timeGroup := make(map[int64]map[string]float64) + // 遍历每个数据项 + for _, row := range rows { + // 将毫秒时间戳转换为小时级时间戳(保留到小时的起始毫秒) + timeHour := row.Timestamp / 3600 * 3600 // 1小时 = 3600000毫秒 + + // 解析 JSON 字符串为 map + var cdrJSON map[string]interface{} + err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON) + if err != nil { + logger.Warnf("Unmarshal JSON: %s", err.Error()) + continue + } + + // 记录类型 + var mosAverage float64 = 0 + if v, ok := cdrJSON["mosAverage"]; ok && v != nil { + mosAverage = v.(float64) + } else { + continue + } + + // 合并到对应的小时段 + if _, exists := timeGroup[timeHour]; !exists { + timeGroup[timeHour] = map[string]float64{ + "total": 0, + "mosSum": 0, + } + } + timeGroup[timeHour]["total"] += 1 + timeGroup[timeHour]["mosSum"] += mosAverage + } + + // 时间组合输出 + data := make([]map[string]any, 0, len(timeGroup)) + for hour, sums := range timeGroup { + data = append(data, map[string]any{ + "timeGroup": fmt.Sprintf("%d", hour), + "total": sums["total"], + "mosSum": float64(int(sums["mosSum"]*100)) / 100, + "mosAvg": float64(int(sums["mosSum"]/sums["total"]*100)) / 100, + }) + } + return data +} + +// IMSCDRCCTHour CDR CCT 统计 +func (r CDREvent) IMSCDRCCTHour(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) + } + + query := map[string]string{ + "rmUID": rmUID, + "recordType": "MOC", + "beginTime": fmt.Sprintf("%d", beginTime.Unix()), + "endTime": fmt.Sprintf("%d", endTime.Unix()), + } + rows, total := r.cdrEventRepository.SelectByPage("IMS", query) + if total == 0 { + return []map[string]any{} + } + // 创建一个map来存储按时间段合并后的数据 + timeGroup := make(map[int64]map[string]float64) + // 遍历每个数据项 + for _, row := range rows { + // 将毫秒时间戳转换为小时级时间戳(保留到小时的起始毫秒) + timeHour := row.Timestamp / 3600 * 3600 // 1小时 = 3600000毫秒 + + // 解析 JSON 字符串为 map + var cdrJSON map[string]interface{} + err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON) + if err != nil { + logger.Warnf("Unmarshal JSON: %s", err.Error()) + continue + } + + // 记录类型 + var callConnectionTime float64 = 0 + if v, ok := cdrJSON["callConnectionTime"]; ok && v != nil { + callConnectionTime = v.(float64) + } else { + continue + } + + // 合并到对应的小时段 + if _, exists := timeGroup[timeHour]; !exists { + timeGroup[timeHour] = map[string]float64{ + "total": 0, + "cctSum": 0, + } + } + timeGroup[timeHour]["total"] += 1 + timeGroup[timeHour]["cctSum"] += callConnectionTime + } + + // 时间组合输出 + data := make([]map[string]any, 0, len(timeGroup)) + for hour, sums := range timeGroup { + data = append(data, map[string]any{ + "timeGroup": fmt.Sprintf("%d", hour), + "total": sums["total"], + "cctSum": float64(int(sums["cctSum"]*100)) / 100, + "cctAvg": float64(int(sums["cctSum"]/sums["total"]*100)) / 100, + }) + } + return data +}