Files
be.ems/src/modules/network_data/controller/smf.go

417 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package controller
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"be.ems/src/framework/i18n"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/vo/result"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neService "be.ems/src/modules/network_element/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
// 实例化控制层 SMFController 结构体
var NewSMF = &SMFController{
neInfoService: neService.NewNeInfo,
cdrEventService: neDataService.NewCDREventSMF,
udmUserInfoService: neDataService.NewUDMUserInfo,
}
// 网元SMF
//
// PATH /smf
type SMFController struct {
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREventSMF // CDR会话事件服务
udmUserInfoService *neDataService.UDMUserInfo // UDM用户信息服务
}
// CDR会话列表
//
// GET /cdr/list
func (s *SMFController) CDRList(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var querys model.CDREventSMFQuery
if err := c.ShouldBindQuery(&querys); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// for multi-tenancy
querys.UserName = ctx.LoginUserToUserName(c)
// 查询网元信息 rmUID
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
// 查询数据
data := s.cdrEventService.SelectPageMT(querys)
c.JSON(200, result.Ok(data))
}
// CDR会话删除
//
// DELETE /cdr/:cdrIds
func (s *SMFController) CDRRemove(c *gin.Context) {
language := ctx.AcceptLanguage(c)
cdrIds := c.Param("cdrIds")
if cdrIds == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 处理字符转id数组后去重
ids := strings.Split(cdrIds, ",")
uniqueIDs := parse.RemoveDuplicates(ids)
if len(uniqueIDs) <= 0 {
c.JSON(200, result.Err(nil))
return
}
rows, err := s.cdrEventService.DeleteByIds(uniqueIDs)
if err != nil {
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
return
}
msg := i18n.TTemplate(language, "app.common.deleteSuccess", map[string]any{"num": rows})
c.JSON(200, result.OkMsg(msg))
}
// CDR会话列表导出
//
// POST /cdr/export
func (s *SMFController) CDRExport(c *gin.Context) {
language := ctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
var querys model.CDREventSMFQuery
if err := c.ShouldBindBodyWith(&querys, binding.JSON); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// for multi-tenancy
querys.UserName = ctx.LoginUserToUserName(c)
// 限制导出数据集
if querys.PageSize > 10000 {
querys.PageSize = 10000
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
data := s.cdrEventService.SelectPageMT(querys)
if parse.Number(data["total"]) == 0 {
// 导出数据记录为空
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
return
}
rows := data["rows"].([]model.CDREventSMF)
// 导出文件名称
fileName := fmt.Sprintf("smf_cdr_event_export_%d_%d.xlsx", len(rows), time.Now().UnixMilli())
// 第一行表头标题
headerCells := map[string]string{
"A1": "ID",
"B1": "Charging ID",
"C1": "NE Name",
"D1": "Resource Unique ID",
"E1": "Subscriber ID Data",
"F1": "Subscriber ID Type",
"G1": "Data Volume Uplink",
"H1": "Data Volume Downlink",
"I1": "Data Total Volume",
"J1": "Duration",
"K1": "Invocation Time",
"L1": "User Identifier",
"M1": "SSC Mode",
"N1": "DNN ID",
"O1": "PDU Type",
"P1": "RAT Type",
"Q1": "PDU IPv4 Address",
"R1": "Network Function IPv4",
"S1": "PDU IPv6 Address Swith Prefix",
"T1": "Record Network Function ID",
"U1": "Record Type",
"V1": "Record Opening Time",
"W1": "Tenant Name", // for multi-tenancy
}
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 解析 JSON 字符串为 map
var cdrJSON map[string]interface{}
err := json.Unmarshal([]byte(row.CDRJSONStr), &cdrJSON)
if err != nil {
logger.Warnf("CDRExport Error parsing JSON: %s", err.Error())
continue
}
// 计费ID
chargingID := ""
if v, ok := cdrJSON["chargingID"]; ok && v != nil {
chargingID = fmt.Sprint(parse.Number(v))
}
// 订阅 ID 类型
subscriptionIDType := "-"
// 订阅 ID 数据
subscriptionIDData := "-"
if v, ok := cdrJSON["subscriberIdentifier"]; ok && v != nil {
if sub, subOk := v.(map[string]any); subOk && sub != nil {
subscriptionIDType = sub["subscriptionIDType"].(string)
subscriptionIDData = sub["subscriptionIDData"].(string)
}
}
// 网络功能 IPv4 地址
networkFunctionIPv4Address := ""
if v, ok := cdrJSON["nFunctionConsumerInformation"]; ok && v != nil {
if conInfo, conInfoOk := v.(map[string]any); conInfoOk && conInfo != nil {
networkFunctionIPv4Address = conInfo["networkFunctionIPv4Address"].(string)
}
}
// 数据量上行链路
dataVolumeUplink := []string{}
// 数据量下行链路
dataVolumeDownlink := []string{}
// 数据总量
dataTotalVolume := []string{}
if v, ok := cdrJSON["listOfMultipleUnitUsage"]; ok && v != nil {
usageList := v.([]any)
if len(usageList) > 0 {
for _, used := range usageList {
usedUnit := used.(map[string]any)
usedUnitList := usedUnit["usedUnitContainer"].([]any)
if len(usedUnitList) > 0 {
for _, data := range usedUnitList {
udata := data.(map[string]any)
if dup, dupOk := udata["dataVolumeUplink"]; dupOk {
dataVolumeUplink = append(dataVolumeUplink, fmt.Sprint(parse.Number(dup)))
}
if ddown, ddownOk := udata["dataVolumeDownlink"]; ddownOk {
dataVolumeDownlink = append(dataVolumeDownlink, fmt.Sprint(parse.Number(ddown)))
}
if dt, dtOk := udata["dataTotalVolume"]; dtOk {
dataTotalVolume = append(dataTotalVolume, fmt.Sprint(parse.Number(dt)))
}
}
}
}
}
}
// 时长
duration := "-"
if v, ok := cdrJSON["duration"]; ok && v != nil {
duration = fmt.Sprint(parse.Number(v))
}
// 调用时间
invocationTimestamp := ""
if v, ok := cdrJSON["invocationTimestamp"]; ok && v != nil {
invocationTimestamp = v.(string)
}
// 记录打开时间
User_Identifier := ""
SSC_Mode := ""
RAT_Type := ""
DNN_ID := ""
PDU_Type := ""
PDU_IPv4 := ""
PDU_IPv6 := ""
if v, ok := cdrJSON["pDUSessionChargingInformation"]; ok && v != nil {
pduInfo := v.(map[string]any)
if v, ok := pduInfo["userIdentifier"]; ok && v != nil {
User_Identifier = v.(string)
}
if v, ok := pduInfo["sSCMode"]; ok && v != nil {
SSC_Mode = v.(string)
}
if v, ok := pduInfo["rATType"]; ok && v != nil {
RAT_Type = v.(string)
}
if v, ok := pduInfo["dNNID"]; ok && v != nil {
DNN_ID = v.(string)
}
if v, ok := pduInfo["pDUType"]; ok && v != nil {
PDU_Type = v.(string)
}
if v, ok := pduInfo["pDUAddress"]; ok && v != nil {
pDUAddress := v.(map[string]any)
if addr, ok := pDUAddress["pDUIPv4Address"]; ok && addr != nil {
PDU_IPv4 = addr.(string)
}
if addr, ok := pDUAddress["pDUIPv6AddresswithPrefix"]; ok && addr != nil {
PDU_IPv6 = addr.(string)
}
}
// pduSessionChargingInformation = fmt.Sprintf(`User Identifier: %s
// SSC Mode: %s RAT Type: %s DNN ID: %s
// PDU Type: %s
// PDU IPv4 Address: %s
// PDU IPv6 Addres Swith Prefix: %s`, User_Identifier, SSC_Mode, RAT_Type, DNN_ID, PDU_Type, PDU_IPv4, PDU_IPv6)
}
// 记录网络参数ID
recordNFID := ""
if v, ok := cdrJSON["recordingNetworkFunctionID"]; ok && v != nil {
recordNFID = v.(string)
}
//记录开始时间
recordOpeningTime := ""
if v, ok := cdrJSON["recordOpeningTime"]; ok && v != nil {
recordOpeningTime = v.(string)
}
//记录类型
recordType := ""
if v, ok := cdrJSON["recordType"]; ok && v != nil {
recordType = v.(string)
}
// for multi-tenant, get tenant name
tenantName := row.TenantName
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: chargingID,
"C" + idx: row.NeName,
"D" + idx: row.RmUID,
"E" + idx: subscriptionIDData,
"F" + idx: subscriptionIDType,
"G" + idx: strings.Join(dataVolumeUplink, ","),
"H" + idx: strings.Join(dataVolumeDownlink, ","),
"I" + idx: strings.Join(dataTotalVolume, ","),
"J" + idx: duration,
"K" + idx: invocationTimestamp,
"L" + idx: User_Identifier,
"M" + idx: SSC_Mode,
"N" + idx: DNN_ID,
"O" + idx: PDU_Type,
"P" + idx: RAT_Type,
"Q" + idx: PDU_IPv4,
"R" + idx: networkFunctionIPv4Address,
"S" + idx: PDU_IPv6,
"T" + idx: recordNFID,
"U" + idx: recordType,
"V" + idx: recordOpeningTime,
"W" + idx: tenantName,
})
}
// 导出数据表格
saveFilePath, err := file.WriteSheet(headerCells, dataCells, fileName, "")
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.FileAttachment(saveFilePath, fileName)
}
// 在线订阅用户数量
//
// GET /sub/num
func (s *SMFController) SubUserNum(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var query struct {
NeId string `form:"neId" binding:"required"`
}
if err := c.ShouldBindQuery(&query); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询网元信息
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("SMF", query.NeId)
if neInfo.NeId != query.NeId || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
num, err := neFetchlink.SMFSubNum(neInfo)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.OkData(num))
}
// 在线订阅用户列表信息
//
// GET /sub/list
func (s *SMFController) SubUserList(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var query struct {
NeId string `form:"neId" binding:"required"`
IMSI string `form:"imsi"`
MSISDN string `form:"msisdn"`
Upstate string `form:"upstate"`
PageNum string `form:"pageNum"`
}
if err := c.ShouldBindQuery(&query); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询网元信息
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("SMF", query.NeId)
if neInfo.NeId != query.NeId || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
data, err := neFetchlink.SMFSubInfoList(neInfo, map[string]string{
"imsi": query.IMSI,
"msisdn": query.MSISDN,
"upstate": query.Upstate,
"pageNum": query.PageNum,
})
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
// 对数据进行处理去掉前缀并加入imsi拓展信息
rows := data["rows"].([]any)
if len(rows) > 0 {
arr := &rows
for i := range *arr {
item := (*arr)[i].(map[string]any)
if v, ok := item["imsi"]; ok && v != nil {
imsiStr := v.(string)
imsiStr = strings.TrimPrefix(imsiStr, "imsi-")
item["imsi"] = imsiStr
// 查UDM拓展信息
info := s.udmUserInfoService.SelectByIMSIAndNeID(imsiStr, "")
item["remark"] = info.Remark
}
if v, ok := item["msisdn"]; ok && v != nil {
item["msisdn"] = strings.TrimPrefix(v.(string), "msisdn-")
}
}
}
c.JSON(200, result.Ok(data))
}