Files
be.ems/src/modules/network_data/controller/amf.go
T

501 lines
14 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"
"sort"
"strconv"
"strings"
"time"
"be.ems/src/framework/i18n"
"be.ems/src/framework/logger"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/date"
"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"
neDataRepository "be.ems/src/modules/network_data/repository"
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"
)
// 实例化控制层 AMFController 结构体
var NewAMF = &AMFController{
neInfoService: neService.NewNeInfo,
ueEventService: neDataService.NewUEEventAMF,
}
// 网元AMF
//
// PATH /amf
type AMFController struct {
neInfoService *neService.NeInfo // 网元信息服务
ueEventService *neDataService.UEEventAMF // UE会话事件服务
}
// UE会话列表
//
// GET /ue/list
//
// @Tags network_data/amf
// @Accept json
// @Produce json
// @Param neType query string true "NE Type only AMF" Enums(AMF)
// @Param neId query string true "NE ID" default(001)
// @Param imsi query string false "imsi"
// @Param pageNum query number true "pageNum" default(1)
// @Param pageSize query number true "pageSize" default(10)
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary UE Session List
// @Description UE Session List
// @Router /neData/amf/ue/list [get]
func (s *AMFController) UEList(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var querys model.UEEventAMFQuery
if err := c.ShouldBindQuery(&querys); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询网元获取IP
// 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
// for multi-tenancy
querys.UserName = ctx.LoginUserToUserName(c)
// 查询数据
data := s.ueEventService.SelectPageMT(querys)
c.JSON(200, result.Ok(data))
}
// UE会话删除
//
// DELETE /ue/:ueIds
//
// @Tags network_data/amf
// @Accept json
// @Produce json
// @Param ueIds path string true "list data id, multiple separated by a , sign"
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary UE Session Deletion
// @Description UE Session Deletion
// @Router /neData/amf/ue/{ueIds} [delete]
func (s *AMFController) UERemove(c *gin.Context) {
language := ctx.AcceptLanguage(c)
ueIds := c.Param("ueIds")
if ueIds == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 处理字符转id数组后去重
ids := strings.Split(ueIds, ",")
uniqueIDs := parse.RemoveDuplicates(ids)
if len(uniqueIDs) <= 0 {
c.JSON(200, result.Err(nil))
return
}
rows, err := s.ueEventService.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))
}
// UE会话列表导出
//
// POST /ue/export
//
// @Tags network_data/amf
// @Accept json
// @Produce json
// @Param data body object true "Request Param"
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary UE Session List Export
// @Description UE Session List Export
// @Router /neData/amf/ue/export [post]
func (s *AMFController) UEExport(c *gin.Context) {
language := ctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
var querys model.UEEventAMFQuery
if err := c.ShouldBindBodyWithJSON(&querys); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// for multi-tenancy
querys.UserName = ctx.LoginUserToUserName(c)
data := s.ueEventService.SelectPageMT(querys)
if parse.Number(data["total"]) == 0 {
// 导出数据记录为空
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
return
}
rows := data["rows"].([]model.UEEventAMF)
// 导出文件名称
fileName := fmt.Sprintf("amf_ue_event_export_%d_%d.xlsx", len(rows), time.Now().UnixMilli())
// 第一行表头标题
headerCells := map[string]string{
"A1": "ID",
"B1": "IMSI",
"C1": "Event Type",
"D1": "Result",
"E1": "Time",
"F1": "Tenant Name",
}
// 读取字典数据 UE 事件类型
dictUEEventType := sysService.NewSysDictData.SelectDictDataByType("ue_event_type")
// 读取字典数据 UE 事件认证代码类型
dictUEAauthCode := sysService.NewSysDictData.SelectDictDataByType("ue_auth_code")
// 读取字典数据 UE 事件CM状态
dictUEEventCmState := sysService.NewSysDictData.SelectDictDataByType("ue_event_cm_state")
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 解析 JSON 字符串为 map
var eventJSON map[string]interface{}
err := json.Unmarshal([]byte(row.EventJSONStr), &eventJSON)
if err != nil {
logger.Warnf("UEExport Error parsing JSON: %s", err.Error())
continue
}
// 取IMSI
imsi := ""
if v, ok := eventJSON["imsi"]; ok && v != nil {
imsi = v.(string)
}
// 取类型
eventType := ""
for _, v := range dictUEEventType {
if row.EventType == v.DictValue {
eventType = i18n.TKey(language, v.DictLabel)
break
}
}
// 取结果
eventResult := ""
// 取时间
timeStr := ""
if row.EventType == "auth-result" {
if v, ok := eventJSON["authTime"]; ok && v != nil {
timeStr = v.(string)
}
if v, ok := eventJSON["authCode"]; ok && v != nil {
eventResult = v.(string)
for _, v := range dictUEAauthCode {
if eventResult == v.DictValue {
eventResult = i18n.TKey(language, v.DictLabel)
break
}
}
}
}
if row.EventType == "detach" {
if v, ok := eventJSON["detachTime"]; ok && v != nil {
timeStr = v.(string)
}
eventResult = "Success"
}
if row.EventType == "cm-state" {
if v, ok := eventJSON["changeTime"]; ok && v != nil {
timeStr = v.(string)
}
if v, ok := eventJSON["status"]; ok && v != nil {
eventResult = fmt.Sprint(v)
for _, v := range dictUEEventCmState {
if eventResult == v.DictValue {
eventResult = i18n.TKey(language, v.DictLabel)
break
}
}
}
}
if timeStr == "" {
if v, ok := eventJSON["timestamp"]; ok && v != nil {
rowTime := parse.Number(v) * 1000
timeStr = date.ParseDateToStr(rowTime, date.YYYY_MM_DD_HH_MM_SS_GMT_TZ)
}
}
if eventResult == "" {
if v, ok := eventJSON["result"]; ok && v != nil {
eventResult = v.(string)
if row.EventType == "cm-state" {
for _, v := range dictUEEventCmState {
if eventResult == v.DictValue {
eventResult = i18n.TKey(language, v.DictLabel)
break
}
}
} else {
for _, v := range dictUEAauthCode {
if eventResult == v.DictValue {
eventResult = i18n.TKey(language, v.DictLabel)
break
}
}
}
}
}
// for multi-tenancy, get tenant name
tenantName := row.TenantName
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: imsi,
"C" + idx: eventType,
"D" + idx: eventResult,
"E" + idx: timeStr,
"F" + 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 /nb/list
//
// @Tags network_data/amf
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Param id query string false "Base Station ID"
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Access Base Station Information List
// @Description Access Base Station Information List
// @Router /neData/amf/nb/list [get]
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))
}
// 接入基站状态信息列表
//
// GET /nb/list-cfg
//
// @Tags network_data/amf
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Access to the base station status information list
// @Description Access to the base station status information list
// @Router /neData/amf/nb/list-cfg [get]
func (s *AMFController) NbStateList(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("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.AMFGnbStateList(neInfo)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
// 添加租户信息
for i, v := range data {
ranId, ok := v["ranId"]
if !ok {
continue
}
tenantID, tenantName := neDataRepository.NewSysTenant.Query(map[string]string{
"radioKey": fmt.Sprintf("5G_%v", ranId),
})
data[i]["tenantID"] = tenantID
data[i]["tenantName"] = tenantName
}
c.JSON(200, result.OkData(data))
}
// 获取审计日志
//
// GET /log/audit
//
// @Tags network_data/amf
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Access to the audit log
// @Description Access to the audit log
// @Router /neData/amf/log/audit [get]
func (s *AMFController) AuditLog(c *gin.Context) {
var query struct {
NeId string `form:"neId" binding:"required"`
BeginTime int64 `json:"beginTime" form:"beginTime"`
EndTime int64 `json:"endTime" form:"endTime"`
}
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.BeginTime != 0 && query.BeginTime < 1e12 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "begin time must ms"))
return
}
if query.EndTime != 0 && query.EndTime < 1e12 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "end time must ms"))
return
}
auditLog, err := neDataService.NewAMF.GetAuditLog(query.NeId)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
if len(auditLog) == 0 {
c.JSON(200, result.ErrMsg("audit log is empty"))
return
}
// 对auditLog按time字段排序
sort.SliceStable(auditLog, func(i, j int) bool {
// 获取i的time值
timeI, okI := auditLog[i]["time"].(string)
// 获取j的time值
timeJ, okJ := auditLog[j]["time"].(string)
// 如果任何一个不包含有效time字段,将它们放在后面
if !okI || !okJ {
return okI && !okJ
}
// 解析时间字符串
tI, errI := time.Parse(time.DateTime, timeI)
tJ, errJ := time.Parse(time.DateTime, timeJ)
// 如果任何一个解析失败,将它们放在后面
if errI != nil || errJ != nil {
return errJ != nil && errI == nil
}
// 按时间升序排序(如果需要降序,改为tI.After(tJ))
return tI.After(tJ)
})
// 响应格式
timeStr := ""
data := make([]map[string]any, 0)
if query.BeginTime > 1e12 && query.EndTime > 1e12 {
// 时间处理
beginT := time.UnixMilli(query.BeginTime)
endT := time.UnixMilli(query.EndTime)
timeStr = fmt.Sprintf("%s - %s", beginT.Format(time.DateTime), endT.Format(time.DateTime))
for _, v := range auditLog {
timeV, ok := v["time"].(string)
if !ok {
continue
}
// 解析时间
t, err := time.Parse(time.DateTime, timeV)
if err != nil {
continue
}
// 检查时间是否在范围内
if t.After(beginT) && t.After(endT) {
data = append(data, map[string]any{
"supi": fmt.Sprint(v["supi"]),
"amf_ue_ngap_id": parse.Number(v["amf_ue_ngap_id"]),
"ran_ue_ngap_id": parse.Number(v["ran_ue_ngap_id"]),
"gnb_id": parse.Number(v["gnb_id"]),
"time": timeV,
})
}
}
} else {
lastItem := auditLog[0]
lastTime := ""
if timeV, ok := lastItem["time"].(string); ok {
lastTime = timeV[:19]
}
timeStr = lastTime
for _, v := range auditLog {
timeV, ok := v["time"].(string)
if !ok {
continue
}
if !strings.HasPrefix(timeV, lastTime) {
continue
}
data = append(data, map[string]any{
"supi": fmt.Sprint(v["supi"]),
"amf_ue_ngap_id": parse.Number(v["amf_ue_ngap_id"]),
"ran_ue_ngap_id": parse.Number(v["ran_ue_ngap_id"]),
"gnb_id": parse.Number(v["gnb_id"]),
})
}
}
c.JSON(200, map[string]any{
"time": timeStr,
"ngap_context": data,
})
}