From 9ddae2cb90ac042955c3c71fbe89521ad6af2853 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Wed, 18 Dec 2024 15:26:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BD=91=E5=85=83=E7=9B=B4=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=A1=A5=E5=85=85AMF/SMF/MME/IMS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/network_data/controller/amf.go | 34 +++++++ src/modules/network_data/controller/ims.go | 66 +++++++++++++ src/modules/network_data/controller/mme.go | 34 +++++++ src/modules/network_data/controller/smf.go | 34 ++++++- src/modules/network_data/network_data.go | 22 ++++- src/modules/network_element/fetch_link/amf.go | 50 ++++++++++ src/modules/network_element/fetch_link/ims.go | 94 +++++++++++++++++++ src/modules/network_element/fetch_link/mme.go | 50 ++++++++++ src/modules/network_element/fetch_link/smf.go | 31 ++++++ 9 files changed, 412 insertions(+), 3 deletions(-) create mode 100644 src/modules/network_element/fetch_link/amf.go create mode 100644 src/modules/network_element/fetch_link/ims.go create mode 100644 src/modules/network_element/fetch_link/mme.go diff --git a/src/modules/network_data/controller/amf.go b/src/modules/network_data/controller/amf.go index cc78b1e7..2638b866 100644 --- a/src/modules/network_data/controller/amf.go +++ b/src/modules/network_data/controller/amf.go @@ -15,6 +15,7 @@ import ( "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" sysService "be.ems/src/modules/system/service" "github.com/gin-gonic/gin" @@ -206,3 +207,36 @@ func (s *AMFController) UEExport(c *gin.Context) { c.FileAttachment(saveFilePath, fileName) } + +// 接入基站信息列表 +// +// GET /nb/list +func (s *AMFController) NbInfoList(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var query struct { + NeId string `form:"neId" binding:"required"` + NbId string `form:"nbId"` + } + if err := c.ShouldBindQuery(&query); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 查询网元信息 + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("AMF", query.NeId) + if neInfo.NeId != query.NeId || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + data, err := neFetchlink.AMFNbInfoList(neInfo, map[string]string{ + "nbId": query.NbId, + }) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + + c.JSON(200, result.OkData(data)) +} diff --git a/src/modules/network_data/controller/ims.go b/src/modules/network_data/controller/ims.go index 60aff2bb..13d1fb9f 100644 --- a/src/modules/network_data/controller/ims.go +++ b/src/modules/network_data/controller/ims.go @@ -16,6 +16,7 @@ import ( "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" sysService "be.ems/src/modules/system/service" "github.com/gin-gonic/gin" @@ -215,3 +216,68 @@ func (s *IMSController) CDRExport(c *gin.Context) { c.FileAttachment(saveFilePath, fileName) } + +// 在线会话用户数量 +// +// GET /session/num +func (s *IMSController) UeSessionNum(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("IMS", query.NeId) + if neInfo.NeId != query.NeId || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + num, err := neFetchlink.IMSUeSessionNum(neInfo) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + + c.JSON(200, result.OkData(num)) +} + +// 在线会话用户列表信息 +// +// GET /session/list +func (s *IMSController) UeSessionList(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var query struct { + NeId string `form:"neId" binding:"required"` + IMSI string `form:"imsi"` + MSISDN string `form:"msisdn"` + } + if err := c.ShouldBindQuery(&query); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 查询网元信息 + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("IMS", query.NeId) + if neInfo.NeId != query.NeId || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + data, err := neFetchlink.IMSUeSessionList(neInfo, map[string]string{ + "imsi": query.IMSI, + "msisdn": query.MSISDN, + }) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + + c.JSON(200, result.OkData(data)) +} diff --git a/src/modules/network_data/controller/mme.go b/src/modules/network_data/controller/mme.go index ac26d60b..f9802a36 100644 --- a/src/modules/network_data/controller/mme.go +++ b/src/modules/network_data/controller/mme.go @@ -16,6 +16,7 @@ import ( "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" sysService "be.ems/src/modules/system/service" "github.com/gin-gonic/gin" @@ -196,3 +197,36 @@ func (s *MMEController) UEExport(c *gin.Context) { c.FileAttachment(saveFilePath, fileName) } + +// 接入基站信息列表 +// +// GET /nb/list +func (s *MMEController) NbInfoList(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var query struct { + NeId string `form:"neId" binding:"required"` + NbId string `form:"nbId"` + } + if err := c.ShouldBindQuery(&query); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 查询网元信息 + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("MME", query.NeId) + if neInfo.NeId != query.NeId || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + data, err := neFetchlink.MMENbInfoList(neInfo, map[string]string{ + "nbId": query.NbId, + }) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + + c.JSON(200, result.OkData(data)) +} diff --git a/src/modules/network_data/controller/smf.go b/src/modules/network_data/controller/smf.go index 094b4843..527dd52e 100644 --- a/src/modules/network_data/controller/smf.go +++ b/src/modules/network_data/controller/smf.go @@ -317,9 +317,39 @@ func (s *SMFController) CDRExport(c *gin.Context) { 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 /subscribers +// GET /sub/list func (s *SMFController) SubUserList(c *gin.Context) { language := ctx.AcceptLanguage(c) var query struct { @@ -334,7 +364,7 @@ func (s *SMFController) SubUserList(c *gin.Context) { return } - // 查询网元信息 rmUID + // 查询网元信息 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"))) diff --git a/src/modules/network_data/network_data.go b/src/modules/network_data/network_data.go index a114f75b..c37539d8 100644 --- a/src/modules/network_data/network_data.go +++ b/src/modules/network_data/network_data.go @@ -59,6 +59,14 @@ func Setup(router *gin.Engine) { collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.imsCDR", collectlogs.BUSINESS_TYPE_EXPORT)), controller.NewIMS.CDRExport, ) + imsGroup.GET("/session/num", + middleware.PreAuthorize(nil), + controller.NewIMS.UeSessionNum, + ) + imsGroup.GET("/session/list", + middleware.PreAuthorize(nil), + controller.NewIMS.UeSessionList, + ) } // 网元SMSC @@ -97,7 +105,11 @@ func Setup(router *gin.Engine) { collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.smfCDR", collectlogs.BUSINESS_TYPE_EXPORT)), controller.NewSMF.CDRExport, ) - smfGroup.GET("/subscribers", + smfGroup.GET("/sub/num", + middleware.PreAuthorize(nil), + controller.NewSMF.SubUserNum, + ) + smfGroup.GET("/sub/list", middleware.PreAuthorize(nil), controller.NewSMF.SubUserList, ) @@ -120,6 +132,10 @@ func Setup(router *gin.Engine) { collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.amfUE", collectlogs.BUSINESS_TYPE_EXPORT)), controller.NewAMF.UEExport, ) + amfGroup.GET("/nb/list", + middleware.PreAuthorize(nil), + controller.NewAMF.NbInfoList, + ) } // 网元UPF @@ -256,5 +272,9 @@ func Setup(router *gin.Engine) { collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.mmeUE", collectlogs.BUSINESS_TYPE_EXPORT)), controller.NewMME.UEExport, ) + mmeGroup.GET("/nb/list", + middleware.PreAuthorize(nil), + controller.NewMME.NbInfoList, + ) } } diff --git a/src/modules/network_element/fetch_link/amf.go b/src/modules/network_element/fetch_link/amf.go new file mode 100644 index 00000000..ae4d44ef --- /dev/null +++ b/src/modules/network_element/fetch_link/amf.go @@ -0,0 +1,50 @@ +package fetchlink + +import ( + "encoding/json" + "fmt" + "strings" + + "be.ems/src/framework/logger" + "be.ems/src/framework/utils/fetch" + "be.ems/src/modules/network_element/model" +) + +// AMFNbInfoList AMF基站信息 +// +// 查询参数 {"nbId":"7"} +// +// 返回结果 [] +func AMFNbInfoList(neInfo model.NeInfo, data map[string]string) ([]map[string]any, error) { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/amf/objectType/nbInfo", neInfo.IP, neInfo.Port) + // 查询参数拼接 + query := []string{} + if v, ok := data["nbId"]; ok && v != "" { + query = append(query, fmt.Sprintf("nbId=%s", v)) + } + if len(query) > 0 { + neUrl = fmt.Sprintf("%s?%s", neUrl, strings.Join(query, "&")) + } + + var resData map[string]any + resBytes, err := fetch.Get(neUrl, nil, 60_000) + if err != nil { + errStr := err.Error() + logger.Warnf("AMFNbInfoList Get \"%s\"", neUrl) + logger.Errorf("AMFNbInfoList %s", errStr) + return nil, fmt.Errorf("NeService AMF API Error") + } + + // 序列化结果 {"data":[{"id":"7","name":"NR-SA-GNB","address":"192.168.5.100:60110","ueNum":0}]} + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("AMFNbInfoList Unmarshal %s", err.Error()) + return nil, err + } + + // 固定返回字段,方便前端解析 + if v, ok := resData["data"]; ok && v != nil { + return v.([]map[string]any), nil + } + return []map[string]any{}, nil +} diff --git a/src/modules/network_element/fetch_link/ims.go b/src/modules/network_element/fetch_link/ims.go new file mode 100644 index 00000000..2139ab6d --- /dev/null +++ b/src/modules/network_element/fetch_link/ims.go @@ -0,0 +1,94 @@ +package fetchlink + +import ( + "encoding/json" + "fmt" + "strings" + + "be.ems/src/framework/logger" + "be.ems/src/framework/utils/fetch" + "be.ems/src/framework/utils/parse" + "be.ems/src/modules/network_element/model" +) + +// IMSUeSessionNum IMS会话数量 +// +// 返回结果 0 +func IMSUeSessionNum(neInfo model.NeInfo) (int64, error) { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/ims/objectType/ueNum", neInfo.IP, neInfo.Port) + resBytes, err := fetch.Get(neUrl, nil, 60_000) + if err != nil { + errStr := err.Error() + logger.Warnf("IMSUeSessionNum Get \"%s\"", neUrl) + logger.Errorf("IMSUeSessionNum %s", errStr) + return 0, fmt.Errorf("NeService IMS API Error") + } + // 序列化结果 {"data":{"ueNum":0}} + var resData map[string]any + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("IMSUeSessionNum Unmarshal %s", err.Error()) + return 0, err + } + + // 固定返回字段,方便前端解析 + var ueNum int64 = 0 + if v, ok := resData["data"]; ok && v != nil { + if num := v.(map[string]any)["ueNum"]; num != nil { + ueNum = parse.Number(num) + } + } + return ueNum, nil +} + +// IMSUeSessionList IMS会话列表 +// +// 查询参数 {"imsi":"460001230000002","msisdn":"12307551232"} +// +// 返回结果 [] +func IMSUeSessionList(neInfo model.NeInfo, data map[string]string) ([]map[string]any, error) { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/ims/objectType/ueInfo", neInfo.IP, neInfo.Port) + // 查询参数拼接 + query := []string{} + if v, ok := data["imsi"]; ok && v != "" { + query = append(query, fmt.Sprintf("imsi=%s", v)) + } + if v, ok := data["msisdn"]; ok && v != "" { + query = append(query, fmt.Sprintf("msisdn=%s", v)) + } + if len(query) > 0 { + neUrl = fmt.Sprintf("%s?%s", neUrl, strings.Join(query, "&")) + } + + resBytes, err := fetch.Get(neUrl, nil, 60_000) + if err != nil { + errStr := err.Error() + logger.Warnf("IMSUeSessionList Get \"%s\"", neUrl) + logger.Errorf("IMSUeSessionList %s", errStr) + return nil, fmt.Errorf("NeService IMS API Error") + } + + // 序列化结果 + // { "data":[ + // { + // "activeTime": "2023-11-29 17:04:54", + // "barring": 0, + // "impu": "sip:12307551232@ims.mnc000.mcc460.3gppnetwork.org", + // "imsi": "460001230000002", + // "msisdn": "12307551232", + // "regState": 1 + // } + // ] } + var resData map[string]any + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("IMSUeSessionList Unmarshal %s", err.Error()) + return nil, err + } + + // 固定返回字段,方便前端解析 + if v, ok := resData["data"]; ok && v != nil { + return v.([]map[string]any), nil + } + return []map[string]any{}, nil +} diff --git a/src/modules/network_element/fetch_link/mme.go b/src/modules/network_element/fetch_link/mme.go new file mode 100644 index 00000000..a7383f35 --- /dev/null +++ b/src/modules/network_element/fetch_link/mme.go @@ -0,0 +1,50 @@ +package fetchlink + +import ( + "encoding/json" + "fmt" + "strings" + + "be.ems/src/framework/logger" + "be.ems/src/framework/utils/fetch" + "be.ems/src/modules/network_element/model" +) + +// MMENbInfoList AMF基站信息 +// +// 查询参数 {"nbId":"7"} +// +// 返回结果 [] +func MMENbInfoList(neInfo model.NeInfo, data map[string]string) ([]map[string]any, error) { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/mme/objectType/nbInfo", neInfo.IP, neInfo.Port) + // 查询参数拼接 + query := []string{} + if v, ok := data["nbId"]; ok && v != "" { + query = append(query, fmt.Sprintf("nbId=%s", v)) + } + if len(query) > 0 { + neUrl = fmt.Sprintf("%s?%s", neUrl, strings.Join(query, "&")) + } + + var resData map[string]any + resBytes, err := fetch.Get(neUrl, nil, 60_000) + if err != nil { + errStr := err.Error() + logger.Warnf("MMENbInfoList Get \"%s\"", neUrl) + logger.Errorf("MMENbInfoList %s", errStr) + return nil, fmt.Errorf("NeService AMF API Error") + } + + // 序列化结果 {"data":[{"id":"7","name":"NR-SA-GNB","address":"192.168.5.100:60110","ueNum":0}]} + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("MMENbInfoList Unmarshal %s", err.Error()) + return nil, err + } + + // 固定返回字段,方便前端解析 + if v, ok := resData["data"]; ok && v != nil { + return v.([]map[string]any), nil + } + return []map[string]any{}, nil +} diff --git a/src/modules/network_element/fetch_link/smf.go b/src/modules/network_element/fetch_link/smf.go index bbbb0ee3..93dc7748 100644 --- a/src/modules/network_element/fetch_link/smf.go +++ b/src/modules/network_element/fetch_link/smf.go @@ -7,9 +7,40 @@ import ( "be.ems/src/framework/logger" "be.ems/src/framework/utils/fetch" + "be.ems/src/framework/utils/parse" "be.ems/src/modules/network_element/model" ) +// SMFSubNum SMF在线订阅用户数量 只含5G用户数据 +// +// 返回结果 0 +func SMFSubNum(neInfo model.NeInfo) (int64, error) { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/smf/objectType/ueNum", neInfo.IP, neInfo.Port) + resBytes, err := fetch.Get(neUrl, nil, 60_000) + if err != nil { + errStr := err.Error() + logger.Warnf("SMFSubNum Get \"%s\"", neUrl) + logger.Errorf("SMFSubNum %s", errStr) + return 0, fmt.Errorf("NeService SMF API Error") + } + // 序列化结果 {"data":{"ueNum":0}} + var resData map[string]any + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("IMSUeSession Unmarshal %s", err.Error()) + return 0, err + } + + // 固定返回字段,方便前端解析 + var ueNum int64 = 0 + if v, ok := resData["data"]; ok && v != nil { + if num := v.(map[string]any)["ueNum"]; num != nil { + ueNum = parse.Number(num) + } + } + return ueNum, nil +} + // SMFSubInfoList SMF在线订阅用户列表信息 // // 查询参数 {"imsi":"360000100000130","msisdn":"8612300000130","upstate":"Inactive","pageNum":"1"}