feat: 基站状态记录上报和导出功能

This commit is contained in:
TsMask
2025-02-08 16:59:36 +08:00
parent 7d4984e1d8
commit 3cbbfc44dd
14 changed files with 535 additions and 42 deletions

View File

@@ -22,6 +22,7 @@ var URL_WHITE_LIST = []string{
"/omcNeConfig",
"/cdrEvent",
"/ueEvent",
"/objectType/nbState",
"/upload-ue",
"/oauth/token",
}

View File

@@ -0,0 +1,121 @@
package controller
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
"be.ems/src/framework/i18n"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/vo/result"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neService "be.ems/src/modules/network_element/service"
)
// 实例化控制层 NBStateController 结构体
var NewNBState = &NBStateController{
neInfoService: neService.NewNeInfo,
nbStateService: neDataService.NewNBState,
}
// 基站状态历史记录 AMF/MME
//
// PATH /nb-state
type NBStateController struct {
neInfoService *neService.NeInfo // 网元信息服务
nbStateService *neDataService.NBState // 基站状态服务
}
// 历史记录列表
//
// GET /list
//
// @Tags network_data/amf,network_data/mme
// @Accept json
// @Produce json
// @Param neType query string true "NE Type only AMF/MME" Enums(AMF,MME) default(AMF)
// @Param neId query string true "NE ID" default(001)
// @Param pageNum query number true "pageNum" default(1)
// @Param pageSize query number true "pageSize" default(10)
// @Param startTime query number false "Start time (timestamped milliseconds)" default(1729162507596)
// @Param endTime query number false "End time (timestamped milliseconds)" default(1729164187611)
// @Param sortField query string false "Sort fields, fill in result fields" Enums(id,create_time) default(id)
// @Param sortOrder query string false "Sort by ascending or descending order" Enums(asc,desc) default(asc)
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Base Station Status List
// @Description Base Station Status List
// @Router /nb-state/list [get]
func (s NBStateController) List(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var query model.NBStateQuery
if err := c.ShouldBindQuery(&query); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(query.NeType, query.NeID)
if neInfo.NeId != query.NeID || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
query.RmUID = neInfo.RmUID
// 查询数据
rows, total := s.nbStateService.SelectPage(query)
c.JSON(200, result.Ok(map[string]any{"rows": rows, "total": total}))
}
// 历史记录列表导出
//
// POST /export
//
// @Tags network_data/amf,network_data/mme
// @Accept json
// @Produce json
// @Param data body object true "Request Param"
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Base Station Status List Export
// @Description Base Station Status List Export
// @Router /nb-state/export [post]
func (s NBStateController) Export(c *gin.Context) {
language := ctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
var querys model.NBStateQuery
if err := c.ShouldBindBodyWithJSON(&querys); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 限制导出数据集
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
rows, total := s.nbStateService.SelectPage(querys)
if total == 0 {
// 导出数据记录为空
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
return
}
// 导出文件名称
fileName := fmt.Sprintf("nb_state_records_export_%d_%d.xlsx", len(rows), time.Now().UnixMilli())
// 导出数据表格
saveFilePath, err := s.nbStateService.ExportXlsx(rows, fileName, language)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.FileAttachment(saveFilePath, fileName)
}

View File

@@ -0,0 +1,36 @@
package model
// NBState 基站状态记录表 nb_state
type NBState struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"` // 网元类型 AMF MME
NeId string `json:"neId" gorm:"column:ne_id"` // 网元ID
RmUid string `json:"rmUid" gorm:"column:rm_uid"` // 资源唯一标识
CreateTime int64 `json:"createTime" gorm:"column:create_time"` // 创建时间
Address string `json:"address" gorm:"column:address"` // 基站IP地址
NbName string `json:"nbName" gorm:"column:nb_name"` // 基站设备名称
UeNum int64 `json:"ueNum" gorm:"column:ue_num"` // 在线用户数
Name string `json:"name" gorm:"column:name"` // 基站名称
Position string `json:"position" gorm:"column:position"` // 基站位置
State string `json:"state" gorm:"column:state"` // 基站状态 OFF ON
Time string `json:"time" gorm:"column:time"` // 状态时间
}
// TableName 表名称
func (*NBState) TableName() string {
return "nb_state"
}
// NBStateQuery 查询参数结构体
type NBStateQuery struct {
NeType string `json:"neType" form:"neType" binding:"required"`
NeID string `json:"neId" form:"neId" binding:"required"`
PageNum int64 `json:"pageNum" form:"pageNum" binding:"required"`
PageSize int64 `json:"pageSize" form:"pageSize" binding:"required"`
Status string `json:"status" form:"status"`
StartTime string `json:"startTime" form:"startTime"`
EndTime string `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=id create_time"` // 排序字段,填写结果字段
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序asc desc
RmUID string `json:"rmUID" form:"rmUID"`
}

View File

@@ -42,6 +42,19 @@ func Setup(router *gin.Engine) {
)
}
// 基站状态历史记录信息 含AMF/MME
nbStateGroup := neDataGroup.Group("/nb-state")
{
nbStateGroup.GET("/list",
middleware.PreAuthorize(nil),
controller.NewNBState.List,
)
nbStateGroup.POST("/export",
middleware.PreAuthorize(nil),
controller.NewNBState.Export,
)
}
// 网元IMS
imsGroup := neDataGroup.Group("/ims")
{

View File

@@ -0,0 +1,116 @@
package repository
import (
"time"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 NBState 结构体
var NewNBState = &NBState{}
// NBState 基站状态记录表 数据层处理
type NBState struct{}
// SelectByPage 分页查询集合
func (r NBState) SelectByPage(query model.NBStateQuery) ([]model.NBState, int64) {
tx := datasource.DB("").Model(&model.NBState{})
// 查询条件拼接
if query.NeType != "" {
tx = tx.Where("ne_type = ?", query.NeType)
}
if query.NeID != "" {
tx = tx.Where("ne_id = ?", query.NeID)
}
if query.RmUID != "" {
tx = tx.Where("rm_uid = ?", query.RmUID)
}
if query.Status != "" {
tx = tx.Where("state = ?", query.Status)
}
if query.StartTime != "" {
startTime := query.StartTime
if len(startTime) == 10 {
startTime = startTime + "000"
}
tx = tx.Where("create_time >= ?", startTime)
}
if query.EndTime != "" {
endTime := query.EndTime
if len(endTime) == 10 {
endTime = endTime + "999"
}
tx = tx.Where("create_time <= ?", endTime)
}
// 查询结果
var total int64 = 0
rows := []model.NBState{}
// 查询数量为0直接返回
if err := tx.Count(&total).Error; err != nil || total <= 0 {
return rows, total
}
// 排序
if query.SortField != "" {
sortField := query.SortField
if query.SortOrder == "desc" {
sortField = sortField + " desc"
}
tx = tx.Order(sortField)
}
// 查询数据分页
pageNum, pageSize := datasource.PageNumSize(query.PageNum, query.PageSize)
tx = tx.Limit(pageSize).Offset(pageSize * pageNum)
err := tx.Find(&rows).Error
if err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows, total
}
return rows, total
}
// SelectByIds 通过ID查询
func (r NBState) SelectByIds(ids []string) []model.NBState {
rows := []model.NBState{}
if len(ids) <= 0 {
return rows
}
tx := datasource.DB("").Model(&model.NBState{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// DeleteByIds 批量删除信息
func (r NBState) DeleteByIds(ids []string) int64 {
if len(ids) <= 0 {
return 0
}
tx := datasource.DB("").Where("id in ?", ids)
if err := tx.Delete(&model.NBState{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}
// Insert 新增信息
func (r NBState) Insert(param model.NBState) int64 {
param.CreateTime = time.Now().UnixMilli()
// 执行插入
if err := datasource.DB("").Create(&param).Error; err != nil {
logger.Errorf("insert err => %v", err.Error())
return 0
}
return param.ID
}

View File

@@ -0,0 +1,86 @@
package service
import (
"fmt"
"strconv"
"be.ems/src/framework/i18n"
"be.ems/src/framework/utils/file"
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
)
// 实例化数据层 NBState 结构体
var NewNBState = &NBState{
nbStateRepository: repository.NewNBState,
}
// NBState 基站状态记录表 服务层处理
type NBState struct {
nbStateRepository *repository.NBState // 基站状态记录信息
}
// SelectPage 根据条件分页查询
func (r NBState) SelectPage(query model.NBStateQuery) ([]model.NBState, int64) {
return r.nbStateRepository.SelectByPage(query)
}
// Insert 插入数据
func (r NBState) Insert(item model.NBState) int64 {
return r.nbStateRepository.Insert(item)
}
// DeleteByIds 批量删除信息
func (r NBState) DeleteByIds(ids []string) (int64, error) {
// 检查是否存在
arr := r.nbStateRepository.SelectByIds(ids)
if len(arr) <= 0 {
return 0, fmt.Errorf("not data")
}
if len(arr) == len(ids) {
rows := r.nbStateRepository.DeleteByIds(ids)
return rows, nil
}
// 删除信息失败!
return 0, fmt.Errorf("delete fail")
}
// ExportXlsx 导出数据到 xlsx 文件
func (r NBState) ExportXlsx(rows []model.NBState, fileName, language string) (string, error) {
// 第一行表头标题
headerCells := map[string]string{
"A1": i18n.TKey(language, "nbState.export.id"),
"B1": i18n.TKey(language, "nbState.export.name"),
"C1": i18n.TKey(language, "nbState.export.position"),
"D1": i18n.TKey(language, "nbState.export.address"),
"E1": i18n.TKey(language, "nbState.export.nbName"),
"F1": i18n.TKey(language, "nbState.export.ueNum"),
"G1": i18n.TKey(language, "nbState.export.state"),
"H1": i18n.TKey(language, "nbState.export.time"),
}
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 角色状态
statusValue := i18n.TKey(language, "dictData.offline")
if row.State == "ON" {
statusValue = i18n.TKey(language, "dictData.online")
}
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: row.Name,
"C" + idx: row.Position,
"D" + idx: row.Address,
"E" + idx: row.NbName,
"F" + idx: row.UeNum,
"G" + idx: statusValue,
"H" + idx: row.Time,
})
}
// 导出数据表格
return file.WriteSheet(headerCells, dataCells, fileName, "Sheet1")
}