Merge remote-tracking branch 'origin/lichang' into lite

This commit is contained in:
TsMask
2025-05-09 14:30:43 +08:00
45 changed files with 3659 additions and 725 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,358 @@
package backup_export_log
import (
"encoding/json"
"fmt"
"path/filepath"
"runtime"
"strconv"
"time"
"be.ems/src/framework/cron"
"be.ems/src/framework/i18n"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/file"
neDataService "be.ems/src/modules/network_data/service"
systemModel "be.ems/src/modules/system/model"
systemService "be.ems/src/modules/system/service"
)
var NewProcessor = &BackupExportLogProcessor{
count: 0,
backupService: neDataService.NewBackup,
sysLogLoginService: systemService.NewSysLogLogin,
sysOperateService: systemService.NewSysLogOperate,
}
// BackupExportLog 队列任务处理
type BackupExportLogProcessor struct {
count int // 执行次数
backupService *neDataService.Backup // 备份相关服务
sysLogLoginService *systemService.SysLogLogin // 系统登录日志服务
sysOperateService *systemService.SysLogOperate // 系统操作日志服务
}
func (s *BackupExportLogProcessor) Execute(data any) (any, error) {
s.count++ // 执行次数加一
options := data.(cron.JobData)
sysJob := options.SysJob
logger.Infof("重复:%v 任务ID:%d 执行次数:%d", options.Repeat, sysJob.JobId, s.count)
// 返回结果,用于记录执行结果
result := map[string]any{
"count": s.count,
}
var params struct {
DataType []string `json:"dataType"` // 类型支持 operate/login
FileType string `json:"fileType"` // 文件类型 csv/xlsx
Hour int `json:"hour"` // 数据时间从任务执行时间前的小时数
}
if err := json.Unmarshal([]byte(sysJob.TargetParams), &params); err != nil {
return nil, err
}
if !(params.FileType == "csv" || params.FileType == "xlsx") {
return nil, fmt.Errorf("file type error, only support csv,xlsx")
}
for _, v := range params.DataType {
switch v {
case "operate":
result[v] = s.exportOperate(params.Hour, params.FileType)
case "login":
result[v] = s.exportLogin(params.Hour, params.FileType)
}
}
// 返回结果,用于记录执行结果
return result, nil
}
// exportOperate 导出系统操作日志数据
func (s BackupExportLogProcessor) exportOperate(hour int, fileType string) string {
// 前 hour 小时
now := time.Now()
end := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
start := end.Add(-time.Duration(hour) * time.Hour)
language := "en"
query := map[string]string{
"beginTime": fmt.Sprint(start.UnixMilli()),
"endTime": fmt.Sprint(end.UnixMilli()),
"pageNum": "1",
"pageSize": "30000",
}
rows, total := s.sysOperateService.FindByPage(query, "")
if total == 0 {
return "no data"
}
// 闭包函数处理多语言
converI18n := func(language string, arr *[]systemModel.SysLogOperate) {
for i := range *arr {
(*arr)[i].Title = i18n.TKey(language, (*arr)[i].Title)
(*arr)[i].OperaLocation = i18n.TKey(language, (*arr)[i].OperaLocation)
}
}
converI18n(language, &rows)
// 导出文件名称
fileName := fmt.Sprintf("sys_log_operate_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/log/sys_log_operate", fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}
// 业务类型
businessTypeFunc := func(v string) string {
businessType := ""
switch v {
case "0":
// 业务操作类型-其它
businessType = i18n.TKey(language, "dictData.operType.other")
case "1":
// 业务操作类型-新增
businessType = i18n.TKey(language, "dictData.operType.add")
case "2":
// 业务操作类型-修改
businessType = i18n.TKey(language, "dictData.operType.edit")
case "3":
// 业务操作类型-删除
businessType = i18n.TKey(language, "dictData.operType.delete")
case "4":
// 业务操作类型-授权
businessType = i18n.TKey(language, "dictData.operType.auth")
case "5":
// 业务操作类型-导出
businessType = i18n.TKey(language, "dictData.operType.export")
case "6":
// 业务操作类型-导入
businessType = i18n.TKey(language, "dictData.operType.import")
case "7":
// 业务操作类型-强退
businessType = i18n.TKey(language, "dictData.operType.forced quit")
case "8":
// 业务操作类型-清空数据
businessType = i18n.TKey(language, "dictData.operType.clear")
}
return businessType
}
if fileType == "csv" {
// 转换数据
data := [][]string{
{
i18n.TKey(language, "log.operate.export.id"),
i18n.TKey(language, "log.operate.export.title"),
i18n.TKey(language, "log.operate.export.businessType"),
i18n.TKey(language, "log.operate.export.operName"),
i18n.TKey(language, "log.operate.export.method"),
i18n.TKey(language, "log.operate.export.ip"),
i18n.TKey(language, "log.operate.export.status"),
i18n.TKey(language, "log.operate.export.operTime"),
i18n.TKey(language, "log.operate.export.costTime"),
},
}
for _, row := range rows {
// 业务类型
businessType := businessTypeFunc(row.BusinessType)
// 状态
statusValue := i18n.TKey(language, "dictData.fail")
if row.StatusFlag == "1" {
statusValue = i18n.TKey(language, "dictData.success")
}
data = append(data, []string{
fmt.Sprint(row.ID),
row.Title,
businessType,
row.OperaBy,
row.OperaUrlMethod,
row.OperaIp,
statusValue,
date.ParseDateToStr(row.OperaTime, date.YYYY_MM_DDTHH_MM_SSZ),
fmt.Sprint(row.CostTime),
})
}
// 输出到文件
if err := file.WriterFileCSV(data, filePath); err != nil {
logger.Errorf("export operate log err => %v", err.Error())
return "export err"
}
}
if fileType == "xlsx" {
// 第一行表头标题
headerCells := map[string]string{
"A1": i18n.TKey(language, "log.operate.export.id"),
"B1": i18n.TKey(language, "log.operate.export.title"),
"C1": i18n.TKey(language, "log.operate.export.businessType"),
"D1": i18n.TKey(language, "log.operate.export.operName"),
"E1": i18n.TKey(language, "log.operate.export.method"),
"F1": i18n.TKey(language, "log.operate.export.ip"),
"G1": i18n.TKey(language, "log.operate.export.status"),
"H1": i18n.TKey(language, "log.operate.export.operTime"),
"I1": i18n.TKey(language, "log.operate.export.costTime"),
}
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 业务类型
businessType := businessTypeFunc(row.BusinessType)
// 状态
statusValue := i18n.TKey(language, "dictData.fail")
if row.StatusFlag == "1" {
statusValue = i18n.TKey(language, "dictData.success")
}
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: row.Title,
"C" + idx: businessType,
"D" + idx: row.OperaBy,
"E" + idx: row.OperaUrlMethod,
"F" + idx: row.OperaIp,
"G" + idx: statusValue,
"H" + idx: date.ParseDateToStr(row.OperaTime, date.YYYY_MM_DDTHH_MM_SSZ),
"I" + idx: row.CostTime,
})
}
// 导出数据表格
if err := file.WriterFileExecl(headerCells, dataCells, filePath, ""); err != nil {
logger.Errorf("export operate log err => %v", err.Error())
return "export err"
}
}
// 上传到FTP服务器
if err := s.backupService.FTPPushFile(filePath, "log"); err != nil {
return "ok, ftp err:" + err.Error()
}
return "ok"
}
// exportLogin 导出系统登录日志数据
func (s BackupExportLogProcessor) exportLogin(hour int, fileType string) string {
// 前 hour 小时
now := time.Now()
end := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
start := end.Add(-time.Duration(hour) * time.Hour)
language := "en"
query := map[string]string{
"beginTime": fmt.Sprint(start.UnixMilli()),
"endTime": fmt.Sprint(end.UnixMilli()),
"pageNum": "1",
"pageSize": "30000",
}
rows, total := s.sysLogLoginService.FindByPage(query, "")
if total == 0 {
return "no data"
}
// 闭包函数处理多语言
converI18n := func(language string, arr *[]systemModel.SysLogLogin) {
for i := range *arr {
(*arr)[i].LoginLocation = i18n.TKey(language, (*arr)[i].LoginLocation)
(*arr)[i].OS = i18n.TKey(language, (*arr)[i].OS)
(*arr)[i].Browser = i18n.TKey(language, (*arr)[i].Browser)
(*arr)[i].Msg = i18n.TKey(language, (*arr)[i].Msg)
}
}
converI18n(language, &rows)
// 导出文件名称
fileName := fmt.Sprintf("sys_log_login_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/log/sys_log_login", fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}
if fileType == "csv" {
// 转换数据
data := [][]string{
{
i18n.TKey(language, "log.login.export.id"),
i18n.TKey(language, "log.login.export.userName"),
i18n.TKey(language, "log.login.export.ip"),
i18n.TKey(language, "log.login.export.location"),
i18n.TKey(language, "log.login.export.os"),
i18n.TKey(language, "log.login.export.browser"),
i18n.TKey(language, "log.login.export.status"),
i18n.TKey(language, "log.login.export.time"),
i18n.TKey(language, "log.login.export.msg"),
},
}
for _, row := range rows {
// 状态
statusValue := i18n.TKey(language, "dictData.fail")
if row.StatusFlag == "1" {
statusValue = i18n.TKey(language, "dictData.success")
}
data = append(data, []string{
fmt.Sprint(row.ID),
row.UserName,
row.LoginIp,
row.LoginLocation,
row.OS,
row.Browser,
statusValue,
date.ParseDateToStr(row.LoginTime, date.YYYY_MM_DDTHH_MM_SSZ),
row.Msg,
})
}
// 输出到文件
if err := file.WriterFileCSV(data, filePath); err != nil {
logger.Errorf("export login log err => %v", err.Error())
return "export err"
}
}
if fileType == "xlsx" {
// 第一行表头标题
headerCells := map[string]string{
"A1": i18n.TKey(language, "log.login.export.id"),
"B1": i18n.TKey(language, "log.login.export.userName"),
"C1": i18n.TKey(language, "log.login.export.ip"),
"D1": i18n.TKey(language, "log.login.export.location"),
"E1": i18n.TKey(language, "log.login.export.os"),
"F1": i18n.TKey(language, "log.login.export.browser"),
"G1": i18n.TKey(language, "log.login.export.status"),
"H1": i18n.TKey(language, "log.login.export.time"),
"I1": i18n.TKey(language, "log.login.export.msg"),
}
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 状态
statusValue := i18n.TKey(language, "dictData.fail")
if row.StatusFlag == "1" {
statusValue = i18n.TKey(language, "dictData.success")
}
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: row.UserName,
"C" + idx: row.LoginIp,
"D" + idx: row.LoginLocation,
"E" + idx: row.OS,
"F" + idx: row.Browser,
"G" + idx: statusValue,
"H" + idx: date.ParseDateToStr(row.LoginTime, date.YYYY_MM_DDTHH_MM_SSZ),
"I" + idx: row.Msg,
})
}
// 导出数据表格
if err := file.WriterFileExecl(headerCells, dataCells, filePath, ""); err != nil {
logger.Errorf("export login log err => %v", err.Error())
return "export err"
}
}
// 上传到FTP服务器
if err := s.backupService.FTPPushFile(filePath, "log"); err != nil {
return "ok, ftp err:" + err.Error()
}
return "ok"
}

View File

@@ -88,13 +88,13 @@ func (s *NeAlarmStateCheckProcessor) Execute(data any) (any, error) {
// 在线且状态为活动告警
if isOnline && alarmStatus == "1" {
// 进行清除
newAlarm, err := s.alarmClear(neInfo, alarmIdArr[0])
clearAlarm, err := s.alarmClear(neInfo, alarmIdArr[0])
if err != nil {
result[neTypeAndId] = err.Error()
continue
}
groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId)
s.wsSendService.ByGroupID(groupID, newAlarm)
s.wsSendService.ByGroupID(groupID, clearAlarm)
result[neTypeAndId] = "alarm clear"
}
@@ -151,19 +151,19 @@ func (s NeAlarmStateCheckProcessor) alarmNew(neInfo neModel.NeInfo, v alarmParam
AlarmTitle: v.AlarmTitle,
AlarmCode: constants.ALARM_STATE_CHECK,
EventTime: lastTime,
AlarmType: "2",
OrigSeverity: "2",
PerceivedSeverity: "2",
AlarmType: v.AlarmType,
OrigSeverity: v.OrigSeverity,
PerceivedSeverity: v.OrigSeverity,
ObjectUid: neInfo.RmUID,
ObjectName: "SystemManagement;Heartbeat",
ObjectType: "SystemState",
LocationInfo: "SystemManagement.State: NE Heartbeat",
AlarmStatus: "1",
ObjectName: "NE State",
ObjectType: "state",
LocationInfo: "NE State: Heartbeat",
AlarmStatus: "1", // 活动告警
SpecificProblem: v.SpecificProblem,
SpecificProblemId: v.SpecificProblemID,
AddInfo: v.AddInfo,
}
insertId := s.alarmService.Insert(alarm)
insertId := s.alarmService.InsertAndForword(alarm)
if insertId > 0 {
alarm.ID = insertId
return alarm, nil

View File

@@ -0,0 +1,288 @@
package ne_alarm_state_check_cmd
import (
"encoding/json"
"fmt"
"sort"
"strconv"
"strings"
"time"
"be.ems/src/framework/constants"
"be.ems/src/framework/cron"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
neDataModel "be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neModel "be.ems/src/modules/network_element/model"
neService "be.ems/src/modules/network_element/service"
wsService "be.ems/src/modules/ws/service"
)
var NewProcessor = &NeAlarmStateCheckCMDProcessor{
neConfigBackupService: neService.NewNeConfigBackup,
neInfoService: neService.NewNeInfo,
neStateService: neDataService.NewNEState,
alarmService: neDataService.NewAlarm,
wsSendService: wsService.NewWSSend,
count: 0,
}
// NeAlarmStateCheckCMDProcessor 网元告警内存/CPU/磁盘检查
type NeAlarmStateCheckCMDProcessor struct {
neConfigBackupService *neService.NeConfigBackup // 网元配置文件备份记录服务
neInfoService *neService.NeInfo // 网元信息服务
neStateService *neDataService.NEState // 网元状态信息服务
alarmService *neDataService.Alarm // 告警信息服务
wsSendService *wsService.WSSend // ws发送服务
count int // 执行次数
}
// alarmParams 告警参数
type alarmParams struct {
AlarmTitle string `json:"alarmTitle"` // NE State Check Alarm CPU/Menory/Disk
AlarmType string `json:"alarmType"` // EquipmentAlarm=2
OrigSeverity string `json:"origSeverity"` // Major=2
SpecificProblem string `json:"specificProblem"` // Alarm Cause: CPU/Menory/Disk status received from target NE reaches the threshold
SpecificProblemID string `json:"specificProblemId"` // AC10100
AddInfo string `json:"addInfo"` // 告警补充信息
CPUUseGt int64 `json:"cpuUseGt"` // CPU使用率大于 范围0~100%
MemUseGt int64 `json:"memUseGt"` // 内存使用率大于, 范围0~100%
DiskUseGt int64 `json:"diskUseGt"` // 磁盘使用率大于, 范围0~100%
// === 非参数字段 ===
AlarmId string // 告警ID
}
func (s *NeAlarmStateCheckCMDProcessor) Execute(data any) (any, error) {
s.count++ // 执行次数加一
options := data.(cron.JobData)
sysJob := options.SysJob
logger.Infof("重复:%v 任务ID:%d 执行次数:%d", options.Repeat, sysJob.JobId, s.count)
// 返回结果,用于记录执行结果
result := map[string]any{
"count": s.count,
}
// 读取参数值
var params alarmParams
err := json.Unmarshal([]byte(sysJob.TargetParams), &params)
if err != nil {
return nil, fmt.Errorf("json params err: %v", err)
}
// 检查使用率
if params.CPUUseGt > 100 || params.CPUUseGt < 0 {
return nil, fmt.Errorf("cpuUseGt must be between 0 and 100")
}
if params.MemUseGt > 100 || params.MemUseGt < 0 {
return nil, fmt.Errorf("memUseGt must be between 0 and 100")
}
if params.DiskUseGt > 100 || params.DiskUseGt < 0 {
return nil, fmt.Errorf("diskUseGt must be between 0 and 100")
}
neList := s.neInfoService.Find(neModel.NeInfo{}, true, false)
for _, neInfo := range neList {
if neInfo.CreateTime == 0 {
continue
}
// 网元在线状态
isOnline := parse.Boolean(neInfo.ServerState["online"])
if !isOnline {
continue
}
// 检查状态
err := s.serverState(neInfo.ServerState, params.CPUUseGt, params.MemUseGt, params.DiskUseGt)
if err == nil {
continue
}
neTypeAndId := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
// 告警ID
params.AlarmId = fmt.Sprintf("%d%d", constants.ALARM_CMD_CHECK, neInfo.CreateTime)
// 检查网元告警ID是否唯一
alarmIdArr := s.alarmService.Find(neDataModel.Alarm{
NeType: neInfo.NeType,
NeId: neInfo.NeId,
AlarmId: params.AlarmId,
})
// 告警状态, 存在的需要手动清除
alarmStatus := ""
if len(alarmIdArr) > 0 {
alarmStatus = fmt.Sprint(alarmIdArr[0].AlarmStatus)
}
// 活动告警进行清除
if alarmStatus == "1" {
clearAlarm, err := s.alarmClear(neInfo, alarmIdArr[0])
if err != nil {
result[neTypeAndId] = err.Error()
continue
}
groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId)
s.wsSendService.ByGroupID(groupID, clearAlarm)
result[neTypeAndId] = "alarm clear"
alarmStatus = "" // 标记为未记录再次发起新告警
}
// 未记录
if alarmStatus == "" {
addInfo := params.AddInfo
if params.AddInfo != "" {
params.AddInfo = params.AddInfo + ", " + err.Error()
} else {
params.AddInfo = err.Error()
}
// 进行新增
newAlarm, err := s.alarmNew(neInfo, params)
params.AddInfo = addInfo // 恢复附加信息
if err != nil {
result[neTypeAndId] = err.Error()
continue
}
groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId)
s.wsSendService.ByGroupID(groupID, newAlarm)
result[neTypeAndId] = "alarm new"
}
}
// 返回结果,用于记录执行结果
return result, nil
}
// serverState 网元状态
func (s NeAlarmStateCheckCMDProcessor) serverState(state map[string]any, cpuUseGt, memUseGt, diskUseGt int64) error {
// 网元CPU使用率
var nfCpuUsage float64 = 0
// CPU使用率
var sysCpuUsage float64 = 0
if state["cpu"] != nil {
cpu := state["cpu"].(map[string]any)
v := parse.Number(cpu["sysCpuUsage"])
sysCpuUsage = float64(v) / 100
nfv := parse.Number(cpu["nfCpuUsage"])
nfCpuUsage = float64(nfv) / 100
}
// 网元内存使用KB
var nfMemUsed int64 = 0
// 内存使用率
var sysMemUsage float64 = 0
if state["mem"] != nil {
mem := state["mem"].(map[string]any)
v := parse.Number(mem["sysMemUsage"])
sysMemUsage = float64(v) / 100
nfMemUsed = parse.Number(mem["nfUsedMem"])
}
// 磁盘使用率
var sysDiskUsage float64 = 0
if state["disk"] != nil {
mem := state["disk"].(map[string]any)
disks := mem["partitionInfo"].([]any)
sort.Slice(disks, func(i, j int) bool {
iUsed := parse.Number(disks[i].(map[string]any)["used"])
jUsed := parse.Number(disks[j].(map[string]any)["used"])
return iUsed > jUsed
})
disk := disks[0].(map[string]any)
total := parse.Number(disk["total"])
used := parse.Number(disk["used"])
sysDiskUsage = (float64(used) / float64(total)) * 100
sysDiskUsage, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", sysDiskUsage), 64)
}
// 插入网元状态记录
neState := neDataModel.NEState{
NeType: fmt.Sprint(state["neType"]),
NeId: fmt.Sprint(state["neId"]),
Version: fmt.Sprint(state["version"]),
Capability: parse.Number(state["capability"]),
SerialNum: fmt.Sprint(state["sn"]),
ExpiryDate: fmt.Sprint(state["expire"]),
SysCpuUsage: sysCpuUsage,
SysMemUsage: sysMemUsage,
SysDiskUsage: sysDiskUsage,
NfCpuUsage: nfCpuUsage,
NfMemUsed: nfMemUsed,
CreateTime: parse.Number(state["refreshTime"]),
}
s.neStateService.Insert(neState)
// 删除网元状态记录7天前
s.neStateService.DeleteByTime(time.Now().UnixMilli() - 7*24*60*60*1000)
// 推送ws消息
groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_NE_STATE, neState.NeType, neState.NeId)
s.wsSendService.ByGroupID(groupID, neState)
// 检查CPU/Menory/Disk使用率
warnMsg := []string{}
if int64(sysCpuUsage) >= cpuUseGt {
warnMsg = append(warnMsg, fmt.Sprintf("cpu usage %.2f%%", sysCpuUsage))
}
if int64(sysMemUsage) >= memUseGt {
warnMsg = append(warnMsg, fmt.Sprintf("memory usage %.2f%%", sysMemUsage))
}
if int64(sysDiskUsage) >= diskUseGt {
warnMsg = append(warnMsg, fmt.Sprintf("disk usage %.2f%%", sysDiskUsage))
}
if len(warnMsg) > 0 {
return fmt.Errorf("greater than %s", strings.Join(warnMsg, ", "))
}
return nil
}
// alarmClear 清除告警
func (s NeAlarmStateCheckCMDProcessor) alarmClear(neInfo neModel.NeInfo, v neDataModel.Alarm) (neDataModel.Alarm, error) {
// 变更告警ID为告警清除ID
v.AlarmId = fmt.Sprintf("%d%d", v.AlarmCode, v.EventTime)
v.AlarmStatus = "0"
// 告警清除
v.ClearType = 1
v.ClearTime = neInfo.UpdateTime
v.ClearUser = "system"
rows := s.alarmService.Update(v)
if rows > 0 {
return v, nil
}
return neDataModel.Alarm{}, fmt.Errorf("clear alarm fail")
}
// alarmNew 新增告警
func (s NeAlarmStateCheckCMDProcessor) alarmNew(neInfo neModel.NeInfo, v alarmParams) (neDataModel.Alarm, error) {
// seq 告警序号
lastSeq := s.alarmService.FindAlarmSeqLast(neInfo.NeType, neInfo.NeId)
lastTime := neInfo.UpdateTime // 网元最后更新时间
if lastTime < neInfo.CreateTime {
lastTime = time.Now().UnixMilli()
}
alarm := neDataModel.Alarm{
NeType: neInfo.NeType,
NeId: neInfo.NeId,
NeName: neInfo.NeName,
Province: neInfo.Province,
PvFlag: neInfo.PvFlag,
AlarmSeq: lastSeq + 1,
AlarmId: v.AlarmId,
AlarmTitle: v.AlarmTitle,
AlarmCode: constants.ALARM_CMD_CHECK,
EventTime: lastTime,
AlarmType: v.AlarmType,
OrigSeverity: v.OrigSeverity,
PerceivedSeverity: v.OrigSeverity,
ObjectUid: neInfo.RmUID,
ObjectName: "NE CPU/Menory/Disk",
ObjectType: "cmd",
LocationInfo: "NE CPU/Menory/Disk: Heartbeat",
AlarmStatus: "1", // 活动告警
SpecificProblem: v.SpecificProblem,
SpecificProblemId: v.SpecificProblemID,
AddInfo: v.AddInfo,
}
insertId := s.alarmService.InsertAndForword(alarm)
if insertId > 0 {
alarm.ID = insertId
return alarm, nil
}
return neDataModel.Alarm{}, fmt.Errorf("new alarm fail")
}

View File

@@ -0,0 +1,213 @@
package ne_alarm_state_check_license
import (
"encoding/json"
"fmt"
"time"
"be.ems/src/framework/constants"
"be.ems/src/framework/cron"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
neDataModel "be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neModel "be.ems/src/modules/network_element/model"
neService "be.ems/src/modules/network_element/service"
wsService "be.ems/src/modules/ws/service"
)
var NewProcessor = &NeAlarmStateCheckLicenseProcessor{
neConfigBackupService: neService.NewNeConfigBackup,
neInfoService: neService.NewNeInfo,
alarmService: neDataService.NewAlarm,
wsSendService: wsService.NewWSSend,
count: 0,
}
// NeAlarmStateCheckLicenseProcessor 网元告警License到期检查
type NeAlarmStateCheckLicenseProcessor struct {
neConfigBackupService *neService.NeConfigBackup // 网元配置文件备份记录服务
neInfoService *neService.NeInfo // 网元信息服务
alarmService *neDataService.Alarm // 告警信息服务
wsSendService *wsService.WSSend // ws发送服务
count int // 执行次数
}
// alarmParams 告警参数
type alarmParams struct {
AlarmTitle string `json:"alarmTitle"` // NE State Check Alarm License
AlarmType string `json:"alarmType"` // EquipmentAlarm=2
OrigSeverity string `json:"origSeverity"` // Major=2
SpecificProblem string `json:"specificProblem"` // Alarm Cause: License received from target NE is about to expire
SpecificProblemID string `json:"specificProblemId"` // AC10200
AddInfo string `json:"addInfo"` // 告警补充信息
DayLt int64 `json:"dayLt"` // 天数小于默认30天
// === 非参数字段 ===
AlarmId string // 告警ID
}
func (s *NeAlarmStateCheckLicenseProcessor) Execute(data any) (any, error) {
s.count++ // 执行次数加一
options := data.(cron.JobData)
sysJob := options.SysJob
logger.Infof("重复:%v 任务ID:%d 执行次数:%d", options.Repeat, sysJob.JobId, s.count)
// 返回结果,用于记录执行结果
result := map[string]any{
"count": s.count,
}
// 读取参数值
var params alarmParams
err := json.Unmarshal([]byte(sysJob.TargetParams), &params)
if err != nil {
return nil, fmt.Errorf("json params err: %v", err)
}
// 检查参数值
if params.DayLt == 0 {
params.DayLt = 30
}
neList := s.neInfoService.Find(neModel.NeInfo{}, true, false)
for _, neInfo := range neList {
if neInfo.CreateTime == 0 {
continue
}
// 网元在线状态
isOnline := parse.Boolean(neInfo.ServerState["online"])
if !isOnline {
continue
}
// 检查状态
err := s.serverState(neInfo.ServerState, params.DayLt)
if err == nil {
continue
}
if params.AddInfo != "" {
params.AddInfo = params.AddInfo + ", " + err.Error()
} else {
params.AddInfo = err.Error()
}
neTypeAndId := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
// 告警ID
params.AlarmId = fmt.Sprintf("%d%d", constants.ALARM_LICENSE_CHECK, neInfo.CreateTime)
// 检查网元告警ID是否唯一
alarmIdArr := s.alarmService.Find(neDataModel.Alarm{
NeType: neInfo.NeType,
NeId: neInfo.NeId,
AlarmId: params.AlarmId,
})
// 告警状态, 存在的需要手动清除
alarmStatus := ""
if len(alarmIdArr) > 0 {
alarmStatus = fmt.Sprint(alarmIdArr[0].AlarmStatus)
}
// 活动告警进行清除
if alarmStatus == "1" {
clearAlarm, err := s.alarmClear(neInfo, alarmIdArr[0])
if err != nil {
result[neTypeAndId] = err.Error()
continue
}
groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId)
s.wsSendService.ByGroupID(groupID, clearAlarm)
result[neTypeAndId] = "alarm clear"
alarmStatus = "" // 标记为未记录再次发起新告警
}
// 未记录
if alarmStatus == "" {
// 进行新增
newAlarm, err := s.alarmNew(neInfo, params)
if err != nil {
result[neTypeAndId] = err.Error()
continue
}
groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId)
s.wsSendService.ByGroupID(groupID, newAlarm)
result[neTypeAndId] = "alarm new"
}
}
// 返回结果,用于记录执行结果
return result, nil
}
// serverState 网元状态
func (s NeAlarmStateCheckLicenseProcessor) serverState(state map[string]any, dayLt int64) error {
expire := fmt.Sprint(state["expire"])
if expire == "" || expire == "<nil>" || expire == "2099-12-31" {
return nil
}
// 解析过期时间
expireTime, err := time.Parse("2006-01-02", expire)
if err != nil {
return fmt.Errorf("parse expire time error: %v", err)
}
// 计算距离天数
daysLeft := int64(time.Since(expireTime).Hours() / 24)
if daysLeft < dayLt {
return fmt.Errorf("license will expire in %d days", daysLeft)
}
return nil
}
// alarmClear 清除告警
func (s NeAlarmStateCheckLicenseProcessor) alarmClear(neInfo neModel.NeInfo, v neDataModel.Alarm) (neDataModel.Alarm, error) {
// 变更告警ID为告警清除ID
v.AlarmId = fmt.Sprintf("%d%d", v.AlarmCode, v.EventTime)
v.AlarmStatus = "0"
// 告警清除
v.ClearType = 1
v.ClearTime = neInfo.UpdateTime
v.ClearUser = "system"
rows := s.alarmService.Update(v)
if rows > 0 {
return v, nil
}
return neDataModel.Alarm{}, fmt.Errorf("clear alarm fail")
}
// alarmNew 新增告警
func (s NeAlarmStateCheckLicenseProcessor) alarmNew(neInfo neModel.NeInfo, v alarmParams) (neDataModel.Alarm, error) {
// seq 告警序号
lastSeq := s.alarmService.FindAlarmSeqLast(neInfo.NeType, neInfo.NeId)
lastTime := neInfo.UpdateTime // 网元最后更新时间
if lastTime < neInfo.CreateTime {
lastTime = time.Now().UnixMilli()
}
alarm := neDataModel.Alarm{
NeType: neInfo.NeType,
NeId: neInfo.NeId,
NeName: neInfo.NeName,
Province: neInfo.Province,
PvFlag: neInfo.PvFlag,
AlarmSeq: lastSeq + 1,
AlarmId: v.AlarmId,
AlarmTitle: v.AlarmTitle,
AlarmCode: constants.ALARM_LICENSE_CHECK,
EventTime: lastTime,
AlarmType: v.AlarmType,
OrigSeverity: v.OrigSeverity,
PerceivedSeverity: v.OrigSeverity,
ObjectUid: neInfo.RmUID,
ObjectName: "NE License",
ObjectType: "license",
LocationInfo: "NE License: Heartbeat",
AlarmStatus: "1", // 活动告警
SpecificProblem: v.SpecificProblem,
SpecificProblemId: v.SpecificProblemID,
AddInfo: v.AddInfo,
}
insertId := s.alarmService.InsertAndForword(alarm)
if insertId > 0 {
alarm.ID = insertId
return alarm, nil
}
return neDataModel.Alarm{}, fmt.Errorf("new alarm fail")
}

View File

@@ -2,6 +2,8 @@ package processor
import (
"be.ems/src/framework/cron"
processorBackupExportCDR "be.ems/src/modules/crontask/processor/backup_export_cdr"
processorBackupExportLog "be.ems/src/modules/crontask/processor/backup_export_log"
processorBackupExportTable "be.ems/src/modules/crontask/processor/backup_export_table"
processorBackupExportUDM "be.ems/src/modules/crontask/processor/backup_export_udm"
processorBackupRemoveFile "be.ems/src/modules/crontask/processor/backup_remove_file"
@@ -11,6 +13,8 @@ import (
processorDeleteNeConfigBackup "be.ems/src/modules/crontask/processor/delete_ne_config_backup"
processorMonitorSysResource "be.ems/src/modules/crontask/processor/monitor_sys_resource"
processorNeAlarmStateCheck "be.ems/src/modules/crontask/processor/ne_alarm_state_check"
processorNeAlarmStateCheckCMD "be.ems/src/modules/crontask/processor/ne_alarm_state_check_cmd"
processorNeAlarmStateCheckLicense "be.ems/src/modules/crontask/processor/ne_alarm_state_check_license"
processorNeConfigBackup "be.ems/src/modules/crontask/processor/ne_config_backup"
processorNeDataUDM "be.ems/src/modules/crontask/processor/ne_data_udm"
)
@@ -23,8 +27,13 @@ func InitCronQueue() {
cron.CreateQueue("ne_config_backup", processorNeConfigBackup.NewProcessor)
// 网元数据-UDM用户数据同步
cron.CreateQueue("ne_data_udm", processorNeDataUDM.NewProcessor)
// 网元告警-状态检查
cron.CreateQueue("ne_alarm_state_check", processorNeAlarmStateCheck.NewProcessor)
// 网元告警-内存/CPU/磁盘检查
cron.CreateQueue("ne_alarm_state_check_cmd", processorNeAlarmStateCheckCMD.NewProcessor)
// 网元告警-License到期检查
cron.CreateQueue("ne_alarm_state_check_license", processorNeAlarmStateCheckLicense.NewProcessor)
// 删除-表内数据记录
cron.CreateQueue("delete_data_record", processorDeleteDataRecord.NewProcessor)
@@ -41,4 +50,8 @@ func InitCronQueue() {
cron.CreateQueue("backup_remove_file", processorBackupRemoveFile.NewProcessor)
// 备份-导出UDM用户数据
cron.CreateQueue("backup_export_udm", processorBackupExportUDM.NewProcessor)
// 备份-导出CDR数据
cron.CreateQueue("backup_export_cdr", processorBackupExportCDR.NewProcessor)
// 备份-导出Log数据
cron.CreateQueue("backup_export_log", processorBackupExportLog.NewProcessor)
}

View File

@@ -0,0 +1,59 @@
package controller
import (
"fmt"
"github.com/gin-gonic/gin"
"be.ems/src/framework/resp"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neService "be.ems/src/modules/network_element/service"
)
// 实例化控制层 NEStateController 结构体
var NewNEState = &NEStateController{
neInfoService: neService.NewNeInfo,
neStateService: neDataService.NewNEState,
}
// 网元状态记录
//
// PATH /ne-state
type NEStateController struct {
neInfoService *neService.NeInfo // 网元信息服务
neStateService *neDataService.NEState // 网元状态服务
}
// 网元状态记录-内存/CPU/磁盘列表
//
// GET /list
//
// @Tags network_data
// @Accept json
// @Produce json
// @Param neType query string true "NE Type" Enums(IMS,AMF,AUSF,UDM,SMF,PCF,NSSF,NRF,UPF,MME,CBC,OMC,SGWC,SMSC) 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 NE Status Record - Memory/CPU/Disk List
// @Description NE Status Record - Memory/CPU/Disk List
// @Router /ne-state/list [get]
func (s NEStateController) List(c *gin.Context) {
var query model.NEStateQuery
if err := c.ShouldBindQuery(&query); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(422001, errMsgs))
return
}
// 查询数据
rows, total := s.neStateService.FindByPage(query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}

View File

@@ -0,0 +1,328 @@
package controller
import (
"fmt"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neService "be.ems/src/modules/network_element/service"
"github.com/gin-gonic/gin"
)
// 实例化控制层 PCFController 结构体
var NewPCF = &PCFController{
neInfoService: neService.NewNeInfo,
}
// 网元PCF
//
// PATH /pcf
type PCFController struct {
neInfoService *neService.NeInfo // 网元信息服务
}
// 策略配置列表
//
// GET /rule/list
//
// @Tags network_data/pcf
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Param imsi query string false "IMSI"
// @Param msisdn query string false "MSISDN"
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Policy Configuration List
// @Description Policy Configuration List
// @Router /neData/pcf/rule/list [get]
func (s PCFController) RuleInfoList(c *gin.Context) {
language := reqctx.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 {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(422001, errMsgs))
return
}
// 查询网元信息
neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", query.NeId)
if neInfo.NeId != query.NeId || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
data, err := neFetchlink.PCFRuleInfo(neInfo, map[string]string{
"imsi": query.IMSI,
"msisdn": query.MSISDN,
})
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.OkData(data))
}
// 策略配置添加
//
// POST /rule
//
// @Tags network_data/pcf
// @Accept json
// @Produce json
// @Param data body object true "Request Param"
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Policy Configuration Additions
// @Description Policy Configuration Additions
// @Router /neData/pcf/rule [post]
func (s PCFController) RuleInfoAdd(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body struct {
NeId string `json:"neId" binding:"required"` // 网元ID
Num int64 `json:"num"` // 批量添加默认0单条大于1时imsi/msisdn会累加数值
ParamData map[string]any `json:"paramData" binding:"required"` // 参数数据
// Imsi string `json:"imsi" binding:"required"`
// Msisdn string `json:"msisdn" binding:"required"`
// Sar string `json:"sar"` // 根据PCF参数配置Service Area Restriction -> Name
// PccRules string `json:"pccRules"` // 根据PCF参数配置PCC Rules -> Rule ID
// QosAudio string `json:"qosAudio"` // 根据PCF参数配置QoS Template -> QoS ID
// QosVideo string `json:"qosVideo"` // 根据PCF参数配置QoS Template -> QoS ID
// SessRules string `json:"sessRules"` // 根据PCF参数配置Session Rules -> Rule ID
// HdrEnrich string `json:"hdrEnrich"` // 根据PCF参数配置Header Enrich Template -> Template Name
// UePolicy string `json:"uePolicy"` // UE策略模板(样例: uep_001)
// Rfsp int64 `json:"rfsp"` // 无线频率选择优先级
}
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(422001, errMsgs))
return
}
// 查询网元信息
neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", body.NeId)
if neInfo.NeId != body.NeId || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
var err error
if body.Num > 0 { // 批量添加
err = neFetchlink.PCFRuleAddBatch(neInfo, body.ParamData, body.Num)
} else { // 单条添加
err = neFetchlink.PCFRuleAdd(neInfo, body.ParamData)
}
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// 策略配置更新
//
// PUT /rule
//
// @Tags network_data/pcf
// @Accept json
// @Produce json
// @Param data body object true "Request Param"
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Policy Configuration Updates
// @Description Policy Configuration Updates
// @Router /neData/pcf/rule [put]
func (s PCFController) RuleInfoEdit(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body struct {
NeId string `json:"neId" binding:"required"` // 网元ID
Num int64 `json:"num"` // 更新数量
ParamData map[string]any `json:"paramData" binding:"required"` // 参数数据
// Imsi string `json:"imsi" binding:"required"`
// Msisdn string `json:"msisdn" binding:"required"`
// Sar string `json:"sar"` // 根据PCF参数配置Service Area Restriction -> Name
// PccRules string `json:"pccRules"` // 根据PCF参数配置PCC Rules -> Rule ID
// QosAudio string `json:"qosAudio"` // 根据PCF参数配置QoS Template -> QoS ID
// QosVideo string `json:"qosVideo"` // 根据PCF参数配置QoS Template -> QoS ID
// SessRules string `json:"sessRules"` // 根据PCF参数配置Session Rules -> Rule ID
// HdrEnrich string `json:"hdrEnrich"` // 根据PCF参数配置Header Enrich Template -> Template Name
// UePolicy string `json:"uePolicy"` // UE策略模板(样例: uep_001)
// Rfsp int64 `json:"rfsp"` // 无线频率选择优先级
}
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(422001, errMsgs))
return
}
// 查询网元信息
neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", body.NeId)
if neInfo.NeId != body.NeId || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
var err error
if body.Num > 0 { // 批量更新
err = neFetchlink.PCFRuleUpdateBatch(neInfo, body.ParamData, body.Num)
} else { // 单条更新
err = neFetchlink.PCFRuleUpdate(neInfo, body.ParamData)
}
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// 策略配置删除
//
// DELETE /rule
//
// @Tags network_data/pcf
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Param imsi query string true "IMSi, batch deletion with quantity"
// @Param num query number false "Number of deletions"
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Policy Configuration Deletion
// @Description Policy Configuration Deletion
// @Router /neData/pcf/rule [delete]
func (s PCFController) RuleInfoRemove(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var query struct {
NeId string `form:"neId" binding:"required"` // 网元ID
IMSI string `form:"imsi" binding:"required"` // IMSi, 带数量时为批量删除
Num int64 `form:"num"` // 删除数量
}
if err := c.ShouldBindQuery(&query); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(422001, errMsgs))
return
}
// 查询网元信息
neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", query.NeId)
if neInfo.NeId != query.NeId || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
var err error
if query.Num > 0 { // 批量删除
err = neFetchlink.PCFRuleDeleteBatch(neInfo, query.IMSI, query.Num)
} else { // 单条删除
err = neFetchlink.PCFRuleDelete(neInfo, query.IMSI)
}
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// 策略配置导出
//
// GET /rule/export
//
// @Tags network_data/pcf
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Param fileType query string true "File Type" default(txt)
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Policy Configuration Export
// @Description Policy Configuration Export
// @Router /neData/pcf/rule/export [get]
func (s PCFController) RuleInfoExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var query struct {
NeId string `form:"neId" binding:"required"`
FileType string `form:"fileType" binding:"required,oneof=txt"` // 文件类型
}
if err := c.ShouldBindQuery(&query); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(422001, errMsgs))
return
}
// 查询网元信息
neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", query.NeId)
if neInfo.NeId != query.NeId || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
data, err := neFetchlink.PCFRuleExport(neInfo, map[string]string{
"fileType": query.FileType,
})
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.Writer.Header().Set("Content-Disposition", `attachment; filename="pcf_rule_export.txt"`)
c.Data(200, "application/octet-stream", data)
}
// 策略配置导入
//
// PUT /rule/import
//
// @Tags network_data/pcf
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Param fileType query string true "File Type" default(txt)
// @Param filePath query string true "File Path" default(/tmp/pcfuser.txt)
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Policy Configuration Import
// @Description Policy Configuration Import
// @Router /neData/pcf/rule/import [put]
func (s PCFController) RuleInfoImport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body struct {
NeId string `json:"neId" binding:"required"`
FileType string `json:"fileType" binding:"required,oneof=txt"` // 文件类型
FilePath string `json:"filePath" binding:"required"` // 网元端文件所在绝对路径
}
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(422001, errMsgs))
return
}
// 查询网元信息
neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", body.NeId)
if neInfo.NeId != body.NeId || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
output, err := neFetchlink.PCFRuleImport(neInfo, map[string]any{
"type": body.FileType,
"filePath": body.FilePath,
})
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.OkMsg(output))
}

View File

@@ -0,0 +1,35 @@
package model
// NEState 网元状态记录表 ne_state
type NEState struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeId string `json:"neId" gorm:"column:ne_id"`
Version string `json:"version" gorm:"column:version"` // 版本
Capability int64 `json:"capability" gorm:"column:capability"` // 用户容量
SerialNum string `json:"serialNum" gorm:"column:serial_num"` // 序列号
ExpiryDate string `json:"expiryDate" gorm:"column:expiry_date"` // 许可证到期日期
SysCpuUsage float64 `json:"sysCpuUsage" gorm:"column:sys_cpu_usage"` // cpu使用率-sys
SysMemUsage float64 `json:"sysMemUsage" gorm:"column:sys_mem_usage"` // 内存使用率-sys
SysDiskUsage float64 `json:"sysDiskUsage" gorm:"column:sys_disk_usage"` // 磁盘使用率-sys
NfCpuUsage float64 `json:"nfCpuUsage" gorm:"column:nf_cpu_usage"` // cpu使用率-nf
NfMemUsed int64 `json:"nfMemUsed" gorm:"column:nf_mem_used"` // 内存使用KB-nf
CreateTime int64 `json:"createTime" gorm:"column:create_time"` // 创建时间
}
// TableName 表名称
func (*NEState) TableName() string {
return "ne_state"
}
// NEStateQuery 查询参数结构体
type NEStateQuery 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"`
BeginTime string `json:"beginTime" form:"beginTime"`
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
}

View File

@@ -84,6 +84,15 @@ func Setup(router *gin.Engine) {
)
}
// 网元状态记录信息
neStateGroup := neDataGroup.Group("/ne-state")
{
neStateGroup.GET("/list",
middleware.AuthorizeUser(nil),
controller.NewNEState.List,
)
}
// 基站状态历史记录信息 含AMF/MME
nbStateGroup := neDataGroup.Group("/nb-state")
{
@@ -476,6 +485,40 @@ func Setup(router *gin.Engine) {
controller.NewSGWC.CDRExport,
)
}
// 网元PCF
pcfGroup := neDataGroup.Group("/pcf")
{
pcfGroup.GET("/rule/list",
middleware.AuthorizeUser(nil),
controller.NewPCF.RuleInfoList,
)
pcfGroup.POST("/rule",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.pcfRule", collectlogs.BUSINESS_TYPE_IMPORT)),
controller.NewPCF.RuleInfoAdd,
)
pcfGroup.PUT("/rule",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.pcfRule", collectlogs.BUSINESS_TYPE_UPDATE)),
controller.NewPCF.RuleInfoEdit,
)
pcfGroup.DELETE("/rule",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.pcfRule", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewPCF.RuleInfoRemove,
)
pcfGroup.GET("/rule/export",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.pcfRule", collectlogs.BUSINESS_TYPE_EXPORT)),
controller.NewPCF.RuleInfoExport,
)
pcfGroup.PUT("/rule/import",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.pcfRule", collectlogs.BUSINESS_TYPE_IMPORT)),
controller.NewPCF.RuleInfoImport,
)
}
}
// InitLoad 初始参数

View File

@@ -26,10 +26,10 @@ func (r CDREventIMS) SelectByPage(query model.CDREventIMSQuery) ([]model.CDREven
tx = tx.Where("rm_uid = ?", query.RmUID)
}
if query.BeginTime != 0 {
tx = tx.Where("timestamp >= ?", query.BeginTime)
tx = tx.Where("created_at >= ?", query.BeginTime)
}
if query.EndTime != 0 {
tx = tx.Where("timestamp <= ?", query.EndTime)
tx = tx.Where("created_at <= ?", query.EndTime)
}
if query.CallerParty != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.callerParty') = ?", query.CallerParty)

View File

@@ -5,6 +5,7 @@ import (
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/date"
"be.ems/src/modules/network_data/model"
)
@@ -35,14 +36,14 @@ func (r NBState) SelectByPage(query model.NBStateQuery) ([]model.NBState, int64)
if len(startTime) == 10 {
startTime = startTime + "000"
}
tx = tx.Where("create_time >= ?", startTime)
tx = tx.Where("time >= ?", date.ParseDateToStr(startTime, date.YYYY_MM_DDTHH_MM_SSZ))
}
if query.EndTime != "" {
endTime := query.EndTime
if len(endTime) == 10 {
endTime = endTime + "999"
}
tx = tx.Where("create_time <= ?", endTime)
tx = tx.Where("time <= ?", date.ParseDateToStr(endTime, date.YYYY_MM_DDTHH_MM_SSZ))
}
// 查询结果

View File

@@ -0,0 +1,110 @@
package repository
import (
"time"
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 NEState 结构体
var NewNEState = &NEState{}
// NEState 网元状态记录表 数据层处理
type NEState struct{}
// SelectByPage 分页查询集合
func (r NEState) SelectByPage(query model.NEStateQuery) ([]model.NEState, int64) {
tx := db.DB("").Model(&model.NEState{})
// 查询条件拼接
if query.NeType != "" {
tx = tx.Where("ne_type = ?", query.NeType)
}
if query.NeID != "" {
tx = tx.Where("ne_id = ?", query.NeID)
}
if query.BeginTime != "" {
startTime := query.BeginTime
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.NEState{}
// 查询数量为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 := db.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 NEState) SelectByIds(ids []string) []model.NEState {
rows := []model.NEState{}
if len(ids) <= 0 {
return rows
}
tx := db.DB("").Model(&model.NEState{})
// 构建查询条件
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
}
// DeleteByTime 删除信息
func (r NEState) DeleteByTime(ltTime int64) int64 {
if ltTime <= 0 {
return 0
}
tx := db.DB("").Where("create_time < ?", ltTime)
if err := tx.Delete(&model.NEState{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}
// Insert 新增信息
func (r NEState) Insert(param model.NEState) int64 {
param.CreateTime = time.Now().UnixMilli()
// 执行插入
if err := db.DB("").Create(&param).Error; err != nil {
logger.Errorf("insert err => %v", err.Error())
return 0
}
return param.ID
}

View File

@@ -77,7 +77,7 @@ func (r Alarm) AlarmClearByIds(ids []int64, clearUser string) (int64, error) {
var rows int64 = 0
for _, v := range arr {
// 状态检查AlarmCode变更告警ID
if v.AlarmCode == constants.ALARM_STATE_CHECK {
if v.AlarmCode == constants.ALARM_STATE_CHECK || v.AlarmCode == constants.ALARM_CMD_CHECK || v.AlarmCode == constants.ALARM_LICENSE_CHECK {
v.AlarmId = fmt.Sprintf("%d%d", v.AlarmCode, v.EventTime)
}
v.AlarmStatus = "0"
@@ -120,6 +120,15 @@ func (r Alarm) AlarmAckByIds(ids []int64, ackUser string, ackState bool) (int64,
return 0, fmt.Errorf("ack fail")
}
// InsertAndForword 新增信息并转发通知
func (s Alarm) InsertAndForword(param model.Alarm) int64 {
rows := s.alarmRepository.Insert(param)
if rows > 0 {
// 转发通知 TODO
}
return rows
}
// ExportXlsx 导出数据到 xlsx 文件
func (r Alarm) ExportXlsx(rows []model.Alarm, fileName, language, alarmStatus string) (string, error) {
// 第一行表头标题

View File

@@ -51,15 +51,16 @@ func (r CDREventIMS) ExportXlsx(rows []model.CDREventIMS, fileName, language str
// 第一行表头标题
headerCells := map[string]string{
"A1": "ID",
"B1": "Record Behavior",
"C1": "Type",
"D1": "Caller",
"E1": "Called",
"F1": "Duration",
"G1": "Result Code",
"H1": "Result Cause",
"I1": "Call Start Time",
"J1": "Hangup Time",
"B1": "NE Name",
"C1": "Record Behavior",
"D1": "Type",
"E1": "Caller",
"F1": "Called",
"G1": "Duration",
"H1": "Result Code",
"I1": "Result Cause",
"J1": "Call Start Time",
"K1": "Hangup Time",
}
// 读取字典数据 CDR SIP响应代码类别类型
dictCDRSipCode := sysService.NewSysDictData.FindByType("cdr_sip_code")
@@ -154,15 +155,16 @@ func (r CDREventIMS) ExportXlsx(rows []model.CDREventIMS, fileName, language str
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: recordType,
"C" + idx: callTypeLable,
"D" + idx: caller,
"E" + idx: called,
"F" + idx: duration,
"G" + idx: callResult,
"H" + idx: callCause,
"I" + idx: seizureTimeStr,
"J" + idx: releaseTimeStr,
"B" + idx: row.NeName,
"C" + idx: recordType,
"D" + idx: callTypeLable,
"E" + idx: caller,
"F" + idx: called,
"G" + idx: duration,
"H" + idx: callResult,
"I" + idx: callCause,
"J" + idx: seizureTimeStr,
"K" + idx: releaseTimeStr,
})
}

View File

@@ -72,7 +72,6 @@ func (r CDREventSMF) ExportXlsx(rows []model.CDREventSMF, fileName string) (stri
}
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 解析 JSON 字符串为 map

View File

@@ -51,12 +51,13 @@ func (r CDREventSMSC) ExportXlsx(rows []model.CDREventSMSC, fileName, language s
// 第一行表头标题
headerCells := map[string]string{
"A1": "ID",
"B1": "Record Behavior",
"C1": "Service Type",
"D1": "Caller",
"E1": "Called",
"F1": "Result",
"G1": "Time",
"B1": "NE Name",
"C1": "Record Behavior",
"D1": "Service Type",
"E1": "Caller",
"F1": "Called",
"G1": "Result",
"H1": "Time",
}
// 读取字典数据 CDR 原因码
dictCDRCauseCode := sysService.NewSysDictData.FindByType("cdr_cause_code")
@@ -121,12 +122,13 @@ func (r CDREventSMSC) ExportXlsx(rows []model.CDREventSMSC, fileName, language s
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: recordType,
"C" + idx: serviceType,
"D" + idx: caller,
"E" + idx: called,
"F" + idx: callResult,
"G" + idx: timeStr,
"B" + idx: row.NeName,
"C" + idx: recordType,
"D" + idx: serviceType,
"E" + idx: caller,
"F" + idx: called,
"G" + idx: callResult,
"H" + idx: timeStr,
})
}

View File

@@ -0,0 +1,31 @@
package service
import (
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
)
// 实例化数据层 NEState 结构体
var NewNEState = &NEState{
neStateRepository: repository.NewNEState,
}
// NEState 网元状态记录表 服务层处理
type NEState struct {
neStateRepository *repository.NEState // 网元状态记录信息
}
// FindByPage 根据条件分页查询
func (r NEState) FindByPage(query model.NEStateQuery) ([]model.NEState, int64) {
return r.neStateRepository.SelectByPage(query)
}
// Insert 插入数据
func (r NEState) Insert(item model.NEState) int64 {
return r.neStateRepository.Insert(item)
}
// DeleteByTime 删除数据
func (r NEState) DeleteByTime(ltTime int64) int64 {
return r.neStateRepository.DeleteByTime(ltTime)
}

View File

@@ -335,7 +335,7 @@ func (s NeConfigController) DataAdd(c *gin.Context) {
}
// 网元直连
resData, err := neFetchlink.NeConfigInstall(neInfo, body.ParamName, body.Loc, body.ParamData)
resData, err := neFetchlink.NeConfigAdd(neInfo, body.ParamName, body.Loc, body.ParamData)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return

View File

@@ -397,13 +397,16 @@ func (s NeInfoController) Add(c *gin.Context) {
if v, ok := body.ServerState["version"]; ok && v != nil {
neVersion.Name = "-"
neVersion.Path = "-"
neVersion.Version = v.(string)
neVersion.Version = fmt.Sprint(v)
}
if v, ok := body.ServerState["capability"]; ok && v != nil {
neLicense.Capability = parse.Number(v)
}
if v, ok := body.ServerState["sn"]; ok && v != nil {
neLicense.SerialNum = v.(string)
neLicense.SerialNum = fmt.Sprint(v)
}
if v, ok := body.ServerState["expire"]; ok && v != nil {
neLicense.ExpiryDate = v.(string)
neLicense.ExpiryDate = fmt.Sprint(v)
neLicense.Status = "1"
}
}
@@ -495,14 +498,17 @@ func (s NeInfoController) Edit(c *gin.Context) {
if v, ok := body.ServerState["version"]; ok && v != nil {
neVersion.Name = "-"
neVersion.Path = "-"
neVersion.Version = v.(string)
neVersion.Version = fmt.Sprint(v)
neVersion.UpdateBy = loginUserName
}
if v, ok := body.ServerState["capability"]; ok && v != nil {
neLicense.Capability = parse.Number(v)
}
if v, ok := body.ServerState["sn"]; ok && v != nil {
neLicense.SerialNum = v.(string)
neLicense.SerialNum = fmt.Sprint(v)
}
if v, ok := body.ServerState["expire"]; ok && v != nil {
neLicense.ExpiryDate = v.(string)
neLicense.ExpiryDate = fmt.Sprint(v)
neLicense.Status = "1"
neLicense.UpdateBy = loginUserName
}

View File

@@ -156,6 +156,7 @@ func (s *NeLicenseController) Code(c *gin.Context) {
if licensePath != "" {
neLicense.LicensePath = licensePath
} else {
neLicense.Capability = 0
neLicense.SerialNum = ""
neLicense.ExpiryDate = ""
neLicense.Status = "0"
@@ -259,6 +260,7 @@ func (s *NeLicenseController) State(c *gin.Context) {
}
if neState, err := neFetchlink.NeState(neInfo); err == nil && neState["sn"] != nil {
neLicense.Status = "1"
neLicense.Capability = parse.Number(neState["capability"])
neLicense.SerialNum = fmt.Sprint(neState["sn"])
neLicense.ExpiryDate = fmt.Sprint(neState["expire"])
} else {
@@ -274,9 +276,10 @@ func (s *NeLicenseController) State(c *gin.Context) {
s.neLicenseService.Update(neLicense)
if neLicense.Status == "1" {
c.JSON(200, resp.OkData(map[string]string{
"sn": neLicense.SerialNum,
"expire": neLicense.ExpiryDate,
c.JSON(200, resp.OkData(map[string]any{
"capability": neLicense.Capability,
"sn": neLicense.SerialNum,
"expire": neLicense.ExpiryDate,
}))
return
}

View File

@@ -109,19 +109,19 @@ func NeConfigUpdate(neInfo model.NeInfo, paramName, loc string, data map[string]
return resData, nil
}
// NeConfigInstall 网元配置新增 array
func NeConfigInstall(neInfo model.NeInfo, paramName, loc string, data map[string]any) (map[string]any, error) {
// NeConfigAdd 网元配置新增 array
func NeConfigAdd(neInfo model.NeInfo, paramName, loc string, data map[string]any) (map[string]any, error) {
// 网元参数配置新增array
neUrl := fmt.Sprintf("http://%s:%d/api/rest/systemManagement/v1/elementType/%s/objectType/config/%s?loc=%v", neInfo.IP, neInfo.Port, strings.ToLower(neInfo.NeType), paramName, loc)
resBytes, err := fetch.PostJSON(neUrl, data, nil)
var resData map[string]any
if err != nil {
errStr := err.Error()
logger.Warnf("NeConfigInfoAdd Post \"%s\"", neUrl)
logger.Warnf("NeConfigAdd Post \"%s\"", neUrl)
if strings.HasPrefix(errStr, "201") || strings.HasPrefix(errStr, "204") {
return resData, nil
}
logger.Errorf("NeConfigInfoAdd %s", errStr)
logger.Errorf("NeConfigAdd %s", errStr)
return nil, fmt.Errorf("NeService Config Add API Error")
}
@@ -133,7 +133,7 @@ func NeConfigInstall(neInfo model.NeInfo, paramName, loc string, data map[string
// 序列化结果
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("NeConfigInfoAdd Unmarshal %s", err.Error())
logger.Errorf("NeConfigAdd Unmarshal %s", err.Error())
return nil, err
}
return resData, nil

View File

@@ -0,0 +1,280 @@
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"
)
// PCFRuleInfo PCF策略配置查询信息
func PCFRuleInfo(neInfo model.NeInfo, data map[string]string) ([]map[string]any, error) {
neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/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, "&"))
}
var resData map[string]any
resBytes, err := fetch.Get(neUrl, nil, 60_000)
if err != nil {
errStr := err.Error()
logger.Warnf("PCFRuleInfo Get \"%s\"", neUrl)
logger.Errorf("PCFRuleInfo %s", errStr)
return nil, fmt.Errorf("NeService PCF API Error")
}
// 序列化结果
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("PCFRuleInfo Unmarshal %s", err.Error())
return nil, err
}
// 固定返回字段,方便前端解析
if v, ok := resData["data"]; ok && v != nil {
if arr := v.([]any); len(arr) > 0 {
result := make([]map[string]any, len(arr))
for i, item := range arr {
result[i] = item.(map[string]any)
}
return result, nil
}
}
return []map[string]any{}, nil
}
// PCFRuleAdd PCF策略配置添加
func PCFRuleAdd(neInfo model.NeInfo, data any) error {
neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo", neInfo.IP, neInfo.Port)
resBytes, err := fetch.PostJSON(neUrl, data, nil)
if err != nil {
errStr := err.Error()
// 正常
if strings.HasPrefix(errStr, "201") {
return nil
}
// 错误结果
if strings.HasPrefix(errStr, "400") {
// 序列化结果
var resData map[string]any
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("PCFRuleAdd Unmarshal %s", err.Error())
return err
}
return fmt.Errorf("%s", resData["cause"])
}
logger.Warnf("PCFRuleAdd Put \"%s\"", neUrl)
logger.Errorf("PCFRuleAdd %s", errStr)
return fmt.Errorf("NeService PCF API Error")
}
return nil
}
// PCFRuleAddBatch PCF策略配置批量添加
func PCFRuleAddBatch(neInfo model.NeInfo, data map[string]any, num int64) error {
neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/%d", neInfo.IP, neInfo.Port, num)
resBytes, err := fetch.PostJSON(neUrl, data, nil)
if err != nil {
errStr := err.Error()
// 正常
if strings.HasPrefix(errStr, "201") {
return nil
}
// 错误结果
if strings.HasPrefix(errStr, "400") {
// 序列化结果
var resData map[string]any
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("PCFRuleAddBatch Unmarshal %s", err.Error())
return err
}
return fmt.Errorf("%s", resData["cause"])
}
logger.Warnf("PCFRuleAddBatch Put \"%s\"", neUrl)
logger.Errorf("PCFRuleAddBatch %s", errStr)
return fmt.Errorf("NeService PCF API Error")
}
return nil
}
// PCFRuleUpdate PCF策略配置修改
func PCFRuleUpdate(neInfo model.NeInfo, data map[string]any) error {
neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo", neInfo.IP, neInfo.Port)
resBytes, err := fetch.PutJSON(neUrl, data, nil)
if err != nil {
errStr := err.Error()
// 错误结果
if strings.HasPrefix(errStr, "400") {
// 序列化结果
var resData map[string]any
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("PCFRuleUpdate Unmarshal %s", err.Error())
return err
}
return fmt.Errorf("%s", resData["cause"])
}
logger.Warnf("PCFRuleUpdate Put \"%s\"", neUrl)
logger.Errorf("PCFRuleUpdate %s", errStr)
return fmt.Errorf("NeService PCF API Error")
}
return nil
}
// PCFRuleUpdateBatch PCF策略配置批量修改
func PCFRuleUpdateBatch(neInfo model.NeInfo, data map[string]any, num int64) error {
neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/%d", neInfo.IP, neInfo.Port, num)
resBytes, err := fetch.PutJSON(neUrl, data, nil)
if err != nil {
errStr := err.Error()
// 错误结果
if strings.HasPrefix(errStr, "400") {
// 序列化结果
var resData map[string]any
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("PCFRuleUpdateBatch Unmarshal %s", err.Error())
return err
}
return fmt.Errorf("%s", resData["cause"])
}
logger.Warnf("PCFRuleUpdateBatch Put \"%s\"", neUrl)
logger.Errorf("PCFRuleUpdateBatch %s", errStr)
return fmt.Errorf("NeService PCF API Error")
}
return nil
}
// PCFRuleDelete PCF策略配置删除
func PCFRuleDelete(neInfo model.NeInfo, imsi string) error {
neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo?imsi=%s", neInfo.IP, neInfo.Port, imsi)
resBytes, err := fetch.Delete(neUrl, nil)
if err != nil {
errStr := err.Error()
// 正常
if strings.HasPrefix(errStr, "204") {
return nil
}
// 错误结果
if strings.HasPrefix(errStr, "400") {
// 序列化结果
var resData map[string]any
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("PCFRuleDelete Unmarshal %s", err.Error())
return err
}
return fmt.Errorf("%s", resData["cause"])
}
logger.Warnf("PCFRuleDelete Delete \"%s\"", neUrl)
logger.Errorf("PCFRuleDelete %s", errStr)
return fmt.Errorf("NeService PCF API Error")
}
return nil
}
// PCFRuleDeleteBatch PCF策略配置批量删除
func PCFRuleDeleteBatch(neInfo model.NeInfo, imsi string, num int64) error {
neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/%d?imsi=%s", neInfo.IP, neInfo.Port, num, imsi)
resBytes, err := fetch.Delete(neUrl, nil)
if err != nil {
errStr := err.Error()
// 正常
if strings.HasPrefix(errStr, "204") {
return nil
}
// 错误结果
if strings.HasPrefix(errStr, "400") {
// 序列化结果
var resData map[string]any
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("PCFRuleDeleteBatch Unmarshal %s", err.Error())
return err
}
return fmt.Errorf("%s", resData["cause"])
}
logger.Warnf("PCFRuleDeleteBatch Delete \"%s\"", neUrl)
logger.Errorf("PCFRuleDeleteBatch %s", errStr)
return fmt.Errorf("NeService PCF API Error")
}
return nil
}
// PCFRuleExport PCF策略配置导出
func PCFRuleExport(neInfo model.NeInfo, data map[string]string) ([]byte, error) {
neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/file/export", neInfo.IP, neInfo.Port)
// 查询参数拼接
query := []string{}
if v, ok := data["fileType"]; ok && v != "" {
query = append(query, fmt.Sprintf("fileType=%s", v))
}
if len(query) > 0 {
neUrl = fmt.Sprintf("%s?%s", neUrl, strings.Join(query, "&"))
}
resBytes, err := fetch.Get(neUrl, nil, 30_000)
if err != nil {
logger.Warnf("PCFRuleExport Get \"%s\"", neUrl)
logger.Errorf("PCFRuleExport %s", err.Error())
return nil, fmt.Errorf("NeService PCF API Error")
}
return resBytes, nil
}
// PCFRuleImport PCF策略配置导入
func PCFRuleImport(neInfo model.NeInfo, data map[string]any) (string, error) {
neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/file/import", neInfo.IP, neInfo.Port)
resBytes, err := fetch.PutJSON(neUrl, data, nil)
var resData map[string]any
if err != nil {
errStr := err.Error()
// 错误结果
if strings.HasPrefix(errStr, "400") {
// 序列化结果
var resData map[string]any
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("PCFRuleDeleteBatch Unmarshal %s", err.Error())
return "", err
}
return "", fmt.Errorf("%s", resData["cause"])
}
logger.Warnf("PCFRuleImport Put \"%s\"", neUrl)
logger.Errorf("PCFRuleImport %s", errStr)
return "", fmt.Errorf("NeService PCF API Error")
}
// 200 成功无数据时
if len(resBytes) == 0 {
return "", nil
}
// 序列化结果
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("PCFRuleImport Unmarshal %s", err.Error())
return "", err
}
return fmt.Sprint(resData["data"]), nil
}

View File

@@ -7,6 +7,7 @@ type NeLicense struct {
NeId string `json:"neId" gorm:"column:ne_id" binding:"required"` // 网元ID
ActivationRequestCode string `json:"activationRequestCode" gorm:"column:activation_request_code"` // 激活申请代码
LicensePath string `json:"licensePath" gorm:"column:license_path"` // 激活授权文件
Capability int64 `json:"capability" gorm:"column:capability"` // 用户容量
SerialNum string `json:"serialNum" gorm:"column:serial_num"` // 序列号
ExpiryDate string `json:"expiryDate" gorm:"column:expiry_date"` // 许可证到期日期
Status string `json:"status" gorm:"column:status"` // 状态 0无效 1有效

View File

@@ -655,7 +655,7 @@ func (s *SysUserController) Import(c *gin.Context) {
// 表格文件绝对地址
filePath := file.ParseUploadFileAbsPath(body.FilePath)
// 读取表格数据
rows, err := file.ReadSheet(filePath, "")
rows, err := file.ReadFileExecl(filePath, "")
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return

View File

@@ -12,10 +12,11 @@ type Router struct {
// RouterMeta 路由元信息对象
type RouterMeta struct {
Title string `json:"title"` // 设置该菜单在侧边栏和面包屑中展示的名字
Icon string `json:"icon"` // 设置该菜单的图标
Cache bool `json:"cache"` // 设置为true则不会被 <keep-alive>缓存
Target string `json:"target"` // 内链地址http(s)://开头), 打开目标位置 '_blank' | '_self' | ''
HideChildInMenu bool `json:"hideChildInMenu"` // 在菜单中隐藏子节点
HideInMenu bool `json:"hideInMenu"` // 在菜单中隐藏自己和子节点
Title string `json:"title"` // 设置该菜单在侧边栏和面包屑中展示的名字
Icon string `json:"icon"` // 设置该菜单的图标
Cache bool `json:"cache"` // 设置为true则不会被 <keep-alive>缓存
Target string `json:"target"` // 内链地址http(s)://开头), 打开目标位置 '_blank' | '_self' | ''
HideChildInMenu bool `json:"hideChildInMenu"` // 在菜单中隐藏子节点
HideInMenu bool `json:"hideInMenu"` // 在菜单中隐藏自己和子节点
NeType []string `json:"neType,omitempty"` // 网元类型,默认全部,有指定类型的,需要添加网元才会显示
}

View File

@@ -329,6 +329,12 @@ func (s SysMenu) getRouteMeta(sysMenu model.SysMenu) vo.RouterMeta {
meta.Target = "_blank"
}
// 网元类型菜单显示权限
if strings.Contains(sysMenu.Perms, "#") {
firstIndex := strings.Index(sysMenu.Perms, "#")
meta.NeType = strings.Split(strings.ToUpper(sysMenu.Perms[:firstIndex]), ",")
}
return meta
}

View File

@@ -203,7 +203,9 @@ func (s *Packet) capturePacketSource(taskInfo *Task) {
if packet == nil {
continue
}
if packet.Metadata().Timestamp.Before(time.Now()) {
// 如果延迟超过1秒跳过
timeDiff := time.Since(packet.Metadata().Timestamp)
if timeDiff > time.Second {
continue
}

View File

@@ -16,6 +16,8 @@ const (
GROUP_TRACE_NE = "2"
// 组号-信令跟踪Packet 4_taskNo
GROUP_TRACE_PACKET = "4"
// 组号-网元状态 8_neType_neId
GROUP_NE_STATE = "8"
// 组号-指标通用 10_neType_neId
GROUP_KPI = "10"
// 组号-自定义KPI指标 20_neType_neId
@@ -34,7 +36,7 @@ const (
GROUP_MME_UE = "1011"
// 组号-告警 2000_neType_neId
GROUP_ALARM = "2000"
// 组号-告警事件 2000_neType_neId
// 组号-告警事件 2002_neType_neId
GROUP_ALARM_EVENT = "2002"
)