feat: 添加moc/cct统计

This commit is contained in:
TsMask
2025-09-11 20:11:12 +08:00
parent b2e18fabfc
commit 0ff49d198d
4 changed files with 299 additions and 11 deletions

View File

@@ -170,9 +170,11 @@ func (s *IMSController) CDRExport(c *gin.Context) {
"G1": "Duration",
"H1": "Result Code",
"I1": "Result Cause",
"J1": "Call Start Time",
"K1": "Hangup Time",
"L1": "Tenant Name", // for multi-tenancy
"J1": "MOS Average",
"K1": "Call Connection Time",
"L1": "Call Start Time",
"M1": "Hangup Time",
"N1": "Tenant Name", // for multi-tenancy
}
// 读取字典数据 CDR SIP响应代码类别类型
dictCDRSipCode := sysService.NewSysDictData.SelectDictDataByType("cdr_sip_code")
@@ -265,6 +267,16 @@ func (s *IMSController) CDRExport(c *gin.Context) {
}
// for multi-tenant, get tenant name
tenantName := row.TenantName
// 通话质量
var mosAverage int64 = 0
if v, ok := cdrJSON["mosAverage"]; ok && v != nil && callType != "sms" {
mosAverage = parse.Number(v)
}
// 通话连接时间
callConnectionTime := "-"
if v, ok := cdrJSON["callConnectionTime"]; ok && v != nil && callType != "sms" {
callConnectionTime = fmt.Sprintf("%ds", parse.Number(v))
}
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
@@ -275,10 +287,12 @@ func (s *IMSController) CDRExport(c *gin.Context) {
"F" + idx: called,
"G" + idx: duration,
"H" + idx: callResult,
"I" + idx: callCause,
"J" + idx: seizureTimeStr,
"K" + idx: releaseTimeStr,
"L" + idx: tenantName,
"I" + idx: mosAverage,
"J" + idx: callConnectionTime,
"K" + idx: callCause,
"L" + idx: seizureTimeStr,
"M" + idx: releaseTimeStr,
"N" + idx: tenantName,
})
}
@@ -476,3 +490,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.CDRMOSHour(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.CDRCCTHour(neInfo.RmUID, query.Timestamp)
c.JSON(200, resp.OkData(data))
}

View File

@@ -113,6 +113,14 @@ func Setup(router *gin.Engine) {
middleware.PreAuthorize(nil),
controller.NewIMS.KPIBusyWeek,
)
imsGroup.GET("/cdr/mos-hour",
// middleware.PreAuthorize(nil),
controller.NewIMS.CDRMOSHour,
)
imsGroup.GET("/cdr/cct-hour",
// middleware.PreAuthorize(nil),
controller.NewIMS.CDRCCTHour,
)
}
// 网元SMSC

View File

@@ -354,3 +354,28 @@ func (r *CDREventIMS) DeleteByIds(ids []string) int64 {
}
return tx.RowsAffected
}
// SelectF 查询
func (r *CDREventIMS) Select(querys model.CDREventIMSQuery) []model.CDREventIMS {
tx := datasource.DB("").Model(&model.CDREventIMS{})
// 查询条件拼接
if querys.RmUID != "" {
tx = tx.Where("rm_uid = ?", querys.RmUID)
}
if querys.StartTime > 0 {
tx = tx.Where("timestamp >= ?", querys.StartTime)
}
if querys.EndTime > 0 {
tx = tx.Where("timestamp <= ?", querys.EndTime)
}
if querys.RecordType != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.recordType') = ?", querys.RecordType)
}
rows := []model.CDREventIMS{}
err := tx.Find(&rows).Error
if err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}

View File

@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"strconv"
"time"
"be.ems/src/framework/i18n"
"be.ems/src/framework/logger"
@@ -63,8 +64,10 @@ func (r CDREventIMS) ExportXlsx(rows []model.CDREventIMS, fileName, language str
"F1": "Duration",
"G1": "Result Code",
"H1": "Result Cause",
"I1": "Call Start Time",
"J1": "Hangup Time",
"I1": "MOS Average",
"J1": "Call Connection Time",
"K1": "Call Start Time",
"L1": "Hangup Time",
}
// 读取字典数据 CDR SIP响应代码类别类型
dictCDRSipCode := sysService.NewSysDictData.SelectDictDataByType("cdr_sip_code")
@@ -156,6 +159,16 @@ func (r CDREventIMS) ExportXlsx(rows []model.CDREventIMS, fileName, language str
releaseTimeStr = v.(string)
}
}
// 通话质量
var mosAverage int64 = 0
if v, ok := cdrJSON["mosAverage"]; ok && v != nil && callType != "sms" {
mosAverage = parse.Number(v)
}
// 通话连接时间
callConnectionTime := "-"
if v, ok := cdrJSON["callConnectionTime"]; ok && v != nil && callType != "sms" {
callConnectionTime = fmt.Sprintf("%ds", parse.Number(v))
}
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
@@ -166,11 +179,157 @@ func (r CDREventIMS) ExportXlsx(rows []model.CDREventIMS, fileName, language str
"F" + idx: duration,
"G" + idx: callResult,
"H" + idx: callCause,
"I" + idx: seizureTimeStr,
"J" + idx: releaseTimeStr,
"I" + idx: mosAverage,
"J" + idx: callConnectionTime,
"K" + idx: seizureTimeStr,
"L" + idx: releaseTimeStr,
})
}
// 导出数据表格
return file.WriteSheet(headerCells, dataCells, fileName, "")
}
// CDRMOSHour CDR MOS 统计
func (r CDREventIMS) CDRMOSHour(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)
}
querys := model.CDREventIMSQuery{
RmUID: rmUID,
RecordType: "MOC",
StartTime: beginTime.Unix(),
EndTime: endTime.Unix(),
}
rows := r.cdrEventIMSRepository.Select(querys)
// 创建一个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.CDRJSONStr), &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": sums["mosSum"],
"mosAvg": sums["mosAvg"] / sums["total"],
})
}
return data
}
// CDRCCTHour CDR CCT 统计
func (r CDREventIMS) CDRCCTHour(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)
}
querys := model.CDREventIMSQuery{
RmUID: rmUID,
RecordType: "MOC",
StartTime: beginTime.Unix(),
EndTime: endTime.Unix(),
}
rows := r.cdrEventIMSRepository.Select(querys)
// 创建一个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.CDRJSONStr), &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": sums["cctSum"],
"cctAvg": sums["cctSum"] / sums["total"],
})
}
return data
}