Merge branch 'main-v2' into lite-ba

This commit is contained in:
TsMask
2025-09-19 15:10:55 +08:00
56 changed files with 2929 additions and 515 deletions

View File

@@ -107,9 +107,10 @@ func processSQLFile(db *gorm.DB, filePath string) {
// 忽略重复记录错误
// Error 1062 (23000): Duplicate entry 'value' for key 'key_name'
log.Println(err.Error())
} else if strings.Contains(errorStr, "unknown column") {
} else if strings.Contains(errorStr, "unknown column") || strings.Contains(errorStr, "no such column") {
// 忽略未知字段错误
// Error 1054 (42S22): Unknown column 'field_name' in 'table'
// sql logic error: no such column: "field_name" (1)
} else if strings.Contains(errorStr, "can't drop") {
// 忽略删除字段或索引错误
// Error 1091 (42000): Can't DROP COLUMN `field_name`; check that it exists

View File

@@ -31,6 +31,13 @@ type Oauth2ClientController struct {
// GET /list
func (s Oauth2ClientController) List(c *gin.Context) {
query := reqctx.QueryMap(c)
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
rows, total := s.oauth2ClientService.FindByPage(query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}

View File

@@ -61,26 +61,49 @@ func (s *BackupExportCDRProcessor) Execute(data any) (any, error) {
}
for _, v := range params.DataType {
// 前 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(params.Hour) * time.Hour)
query := map[string]string{
"neType": "",
"neId": "",
"rmUID": "",
"beginTime": fmt.Sprint(start.UnixMilli()),
"endTime": fmt.Sprint(end.UnixMilli()),
}
switch v {
case "ims":
neList := s.neInfoService.Find(neModel.NeInfo{NeType: "IMS"}, false, false)
for _, ne := range neList {
result[ne.NeName] = s.exportIMS(params.Hour, ne.RmUID, params.FileType)
query["neType"] = "IMS"
query["neId"] = ne.NeId
query["rmUID"] = ne.RmUID
result[ne.NeName] = s.exportIMS(query, params.FileType)
}
case "smsc":
neList := s.neInfoService.Find(neModel.NeInfo{NeType: "SMSC"}, false, false)
for _, ne := range neList {
result[ne.NeName] = s.exportSMSC(params.Hour, ne.RmUID, params.FileType)
query["neType"] = "SMSC"
query["neId"] = ne.NeId
query["rmUID"] = ne.RmUID
result[ne.NeName] = s.exportSMSC(query, params.FileType)
}
case "smf":
neList := s.neInfoService.Find(neModel.NeInfo{NeType: "SMF"}, false, false)
for _, ne := range neList {
result[ne.NeName] = s.exportSMF(params.Hour, ne.RmUID, params.FileType)
query["neType"] = "SMF"
query["neId"] = ne.NeId
query["rmUID"] = ne.RmUID
result[ne.NeName] = s.exportSMF(query, params.FileType)
}
case "sgwc":
neList := s.neInfoService.Find(neModel.NeInfo{NeType: "SGWC"}, false, false)
for _, ne := range neList {
result[ne.NeName] = s.exportSGWC(params.Hour, ne.RmUID, params.FileType)
query["neType"] = "SGWC"
query["neId"] = ne.NeId
query["rmUID"] = ne.RmUID
result[ne.NeName] = s.exportSGWC(query, params.FileType)
}
}
}
@@ -90,30 +113,21 @@ func (s *BackupExportCDRProcessor) Execute(data any) (any, error) {
}
// exportIMS 导出IMS-CDR会话事件数据
func (s BackupExportCDRProcessor) exportIMS(hour int, rmUID, 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)
func (s BackupExportCDRProcessor) exportIMS(query map[string]string, fileType string) string {
language := "en"
query := map[string]string{
"sortField": "timestamp",
"sortOrder": "asc",
"rmUID": rmUID,
"beginTime": fmt.Sprint(start.UnixMilli()),
"endTime": fmt.Sprint(end.UnixMilli()),
"pageNum": fmt.Sprint(1),
"pageSize": fmt.Sprint(30000),
}
rows, total := s.cdrEventService.FindByPage("IMS", query)
neType := query["neType"]
neTypeLower := strings.ToLower(neType)
neId := query["neId"]
endTime := parse.Number(query["endTime"])
rows, total := s.cdrEventService.FindByPage(neType, query)
if total == 0 {
return "no data"
}
// 导出文件名称
fileName := fmt.Sprintf("ims_cdr_event_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/cdr/ims_cdr_event", fileName)
dateStr := date.ParseDateToStr(endTime, date.YYYYMMDDHHMMSS)
fileName := fmt.Sprintf("%s_%s_cdr_export_%d_%s.%s", neTypeLower, neId, len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, fmt.Sprintf("/cdr/%s", neTypeLower), fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}
@@ -379,30 +393,21 @@ func (s BackupExportCDRProcessor) exportIMS(hour int, rmUID, fileType string) st
}
// exportSMSC 导出SMSC-CDR会话事件数据
func (s BackupExportCDRProcessor) exportSMSC(hour int, rmUID, 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)
func (s BackupExportCDRProcessor) exportSMSC(query map[string]string, fileType string) string {
language := "en"
query := map[string]string{
"sortField": "timestamp",
"sortOrder": "asc",
"rmUID": rmUID,
"beginTime": fmt.Sprint(start.UnixMilli()),
"endTime": fmt.Sprint(end.UnixMilli()),
"pageNum": fmt.Sprint(1),
"pageSize": fmt.Sprint(30000),
}
rows, total := s.cdrEventService.FindByPage("SMSC", query)
neType := query["neType"]
neTypeLower := strings.ToLower(neType)
neId := query["neId"]
endTime := parse.Number(query["endTime"])
rows, total := s.cdrEventService.FindByPage(neType, query)
if total == 0 {
return "no data"
}
// 导出文件名称
fileName := fmt.Sprintf("smsc_cdr_event_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/cdr/smsc_cdr_event", fileName)
dateStr := date.ParseDateToStr(endTime, date.YYYYMMDDHHMMSS)
fileName := fmt.Sprintf("%s_%s_cdr_export_%d_%s.%s", neTypeLower, neId, len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, fmt.Sprintf("/cdr/%s", neTypeLower), fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}
@@ -596,29 +601,20 @@ func (s BackupExportCDRProcessor) exportSMSC(hour int, rmUID, fileType string) s
}
// exportSMF 导出SMF-CDR会话事件数据
func (s BackupExportCDRProcessor) exportSMF(hour int, rmUID, 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)
query := map[string]string{
"sortField": "timestamp",
"sortOrder": "asc",
"rmUID": rmUID,
"beginTime": fmt.Sprint(start.UnixMilli()),
"endTime": fmt.Sprint(end.UnixMilli()),
"pageNum": fmt.Sprint(1),
"pageSize": fmt.Sprint(30000),
}
rows, total := s.cdrEventService.FindByPage("SMF", query)
func (s BackupExportCDRProcessor) exportSMF(query map[string]string, fileType string) string {
neType := query["neType"]
neTypeLower := strings.ToLower(neType)
neId := query["neId"]
endTime := parse.Number(query["endTime"])
rows, total := s.cdrEventService.FindByPage(neType, query)
if total == 0 {
return "no data"
}
// 导出文件名称
fileName := fmt.Sprintf("smf_cdr_event_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/cdr/smf_cdr_event", fileName)
dateStr := date.ParseDateToStr(endTime, date.YYYYMMDDHHMMSS)
fileName := fmt.Sprintf("%s_%s_cdr_export_%d_%s.%s", neTypeLower, neId, len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, fmt.Sprintf("/cdr/%s", neTypeLower), fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}
@@ -1004,29 +1000,20 @@ func (s BackupExportCDRProcessor) exportSMF(hour int, rmUID, fileType string) st
}
// exportSGWC 导出SGWC-CDR会话事件数据
func (s BackupExportCDRProcessor) exportSGWC(hour int, rmUID, 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)
query := map[string]string{
"sortField": "timestamp",
"sortOrder": "asc",
"rmUID": rmUID,
"beginTime": fmt.Sprint(start.UnixMilli()),
"endTime": fmt.Sprint(end.UnixMilli()),
"pageNum": fmt.Sprint(1),
"pageSize": fmt.Sprint(30000),
}
rows, total := s.cdrEventService.FindByPage("SGWC", query)
func (s BackupExportCDRProcessor) exportSGWC(query map[string]string, fileType string) string {
neType := query["neType"]
neTypeLower := strings.ToLower(neType)
neId := query["neId"]
endTime := parse.Number(query["endTime"])
rows, total := s.cdrEventService.FindByPage(neType, query)
if total == 0 {
return "no data"
}
// 导出文件名称
fileName := fmt.Sprintf("sgwc_cdr_event_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/cdr/sgwc_cdr_event", fileName)
dateStr := date.ParseDateToStr(endTime, date.YYYYMMDDHHMMSS)
fileName := fmt.Sprintf("%s_%s_cdr_export_%d_%s.%s", neTypeLower, neId, len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, fmt.Sprintf("/cdr/%s", neTypeLower), fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}

View File

@@ -0,0 +1,416 @@
package backup_export_kpi
import (
"encoding/json"
"fmt"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"time"
"be.ems/src/framework/cron"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
neModel "be.ems/src/modules/ne/model"
neService "be.ems/src/modules/ne/service"
neDataModel "be.ems/src/modules/ne_data/model"
neDataService "be.ems/src/modules/ne_data/service"
)
var NewProcessor = &BackupExportKPIProcessor{
count: 0,
backupService: neDataService.NewBackup,
neInfoService: neService.NewNeInfo,
kpiReportService: neDataService.NewKpiReport,
kpicReportService: neDataService.NewKpiCReport,
}
// BackupExportKPI 队列任务处理
type BackupExportKPIProcessor struct {
count int // 执行次数
backupService *neDataService.Backup // 备份相关服务
neInfoService *neService.NeInfo // 网元信息服务
kpiReportService *neDataService.KpiReport // 统计信息服务
kpicReportService *neDataService.KpiCReport // 统计信息服务
}
func (s *BackupExportKPIProcessor) 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"` // 类型支持 ims/amf/udm/smf/pcf/upf/mme/smsc
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 {
neList := s.neInfoService.Find(neModel.NeInfo{NeType: strings.ToUpper(v)}, false, false)
for _, ne := range neList {
// 前 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(params.Hour) * time.Hour)
// 密度
var interval int64 = 60
if ne.NeType == "UPF" {
interval = 5
}
// 指标
query := neDataModel.KPIQuery{
NeType: ne.NeType,
NeID: ne.NeId,
RmUID: ne.RmUID,
Interval: interval,
BeginTime: start.UnixMilli(),
EndTime: end.UnixMilli(),
}
result[ne.NeName+"_kpi"] = s.exportKPI(query, params.FileType)
// 自定义指标
queryC := neDataModel.KPICQuery{
NeType: ne.NeType,
NeID: ne.NeId,
RmUID: ne.RmUID,
Interval: interval,
BeginTime: start.UnixMilli(),
EndTime: end.UnixMilli(),
}
result[ne.NeName+"_kpic"] = s.exportKPIC(queryC, params.FileType)
}
}
// 返回结果,用于记录执行结果
return result, nil
}
// exportKPI 导出KPI数据
func (s *BackupExportKPIProcessor) exportKPI(query neDataModel.KPIQuery, fileType string) string {
rows := s.kpiReportService.FindData(query)
if len(rows) == 0 {
return "no data"
}
// 获取数据指标id
kpiIdMap := make(map[string]string, 0)
kpiTitles := s.kpiReportService.FindTitle(query.NeType)
for _, v := range kpiTitles {
kpiIdMap[v.KpiId] = v.EnTitle
}
// 导出文件名称
dateStr := date.ParseDateToStr(parse.Number(query.EndTime), date.YYYYMMDDHHMMSS)
fileName := fmt.Sprintf("%s_%s_kpi_export_%d_%s.%s", strings.ToLower(query.NeType), query.NeID, len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, fmt.Sprintf("/kpi/%s", strings.ToLower(query.NeType)), fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}
if fileType == "csv" {
data := [][]string{}
// 获取kpiIdMap的键并排序
var sortedKpiIds []string
for kpiId := range kpiIdMap {
sortedKpiIds = append(sortedKpiIds, kpiId)
}
sort.Strings(sortedKpiIds) // 按字母排序
// 头 - 按排序后的顺序添加标题
header := []string{}
for _, kpiId := range sortedKpiIds {
header = append(header, kpiIdMap[kpiId])
}
header = append(header, "NE Name")
header = append(header, "Time")
data = append(data, header)
for _, row := range rows {
// 取时间
timeStr := ""
if v, ok := row["timeGroup"]; ok && v != nil {
if releaseTime := parse.Number(v); releaseTime > 0 {
timeStr = date.ParseDateToStr(releaseTime, date.YYYY_MM_DDTHH_MM_SSZ)
} else {
timeStr = fmt.Sprintf("%s", v)
}
}
// 取网元名称
neName := ""
if v, ok := row["neName"]; ok && v != nil {
neName = fmt.Sprintf("%s", v)
}
// 按排序后的顺序获取数据
dataRow := []string{}
for _, kpiId := range sortedKpiIds {
value := ""
if v, ok := row[kpiId]; ok && v != nil {
value = fmt.Sprint(v)
}
dataRow = append(dataRow, value)
}
dataRow = append(dataRow, neName)
dataRow = append(dataRow, timeStr)
data = append(data, dataRow)
}
// 输出到文件
if err := file.WriterFileCSV(data, filePath); err != nil {
logger.Errorf("export operate log err => %v", err.Error())
return "export err"
}
}
if fileType == "xlsx" {
// 获取kpiIdMap的键并排序
var sortedKpiIds []string
for kpiId := range kpiIdMap {
sortedKpiIds = append(sortedKpiIds, kpiId)
}
sort.Strings(sortedKpiIds) // 按字母排序
// 第一行表头标题 - 按排序后的顺序
headerCells := map[string]string{}
colIndex := 0
// 先添加KPI标题列
for i, kpiId := range sortedKpiIds {
colLetter := string(rune('A' + i))
headerCells[colLetter+"1"] = kpiIdMap[kpiId]
colIndex++
}
// 添加NE Name和Time列
neNameCol := string(rune('A' + colIndex))
headerCells[neNameCol+"1"] = "NE Name"
colIndex++
timeCol := string(rune('A' + colIndex))
headerCells[timeCol+"1"] = "Time"
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 取时间和网元名称
timeStr := ""
if v, ok := row["timeGroup"]; ok && v != nil {
if releaseTime := parse.Number(v); releaseTime > 0 {
timeStr = date.ParseDateToStr(releaseTime, date.YYYY_MM_DDTHH_MM_SSZ)
} else {
timeStr = fmt.Sprintf("%s", v)
}
}
neName := ""
if v, ok := row["neName"]; ok && v != nil {
neName = fmt.Sprintf("%s", v)
}
// 按排序后的顺序填充数据
dataCell := map[string]any{}
// 填充KPI数据
for j, kpiId := range sortedKpiIds {
value := ""
if v, ok := row[kpiId]; ok && v != nil {
value = fmt.Sprint(v)
}
colLetter := string(rune('A' + j))
dataCell[colLetter+idx] = value
}
// 填充NE Name和Time
dataCell[neNameCol+idx] = neName
dataCell[timeCol+idx] = timeStr
dataCells = append(dataCells, dataCell)
}
// 导出数据表格
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, ""); err != nil {
return "ok, ftp err:" + err.Error()
}
return "ok"
}
// exportKPI 导出KPI数据
func (s *BackupExportKPIProcessor) exportKPIC(query neDataModel.KPICQuery, fileType string) string {
rows := s.kpicReportService.FindData(query)
if len(rows) == 0 {
return "no data"
}
// 获取数据指标id
kpiIdMap := make(map[string]string, 0)
kpiTitles := s.kpiReportService.FindTitle(query.NeType)
for _, v := range kpiTitles {
kpiIdMap[v.KpiId] = v.EnTitle
}
// 导出文件名称
dateStr := date.ParseDateToStr(parse.Number(query.EndTime), date.YYYYMMDDHHMMSS)
fileName := fmt.Sprintf("%s_%s_kpic_export_%d_%s.%s", strings.ToLower(query.NeType), query.NeID, len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, fmt.Sprintf("/kpi/%s", strings.ToLower(query.NeType)), fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}
if fileType == "csv" {
data := [][]string{}
// 获取kpiIdMap的键并排序
var sortedKpiIds []string
for kpiId := range kpiIdMap {
sortedKpiIds = append(sortedKpiIds, kpiId)
}
sort.Strings(sortedKpiIds) // 按字母排序
// 头 - 按排序后的顺序添加标题
header := []string{}
for _, kpiId := range sortedKpiIds {
header = append(header, kpiIdMap[kpiId])
}
header = append(header, "NE Name")
header = append(header, "Time")
data = append(data, header)
for _, row := range rows {
// 取时间
timeStr := ""
if v, ok := row["timeGroup"]; ok && v != nil {
if releaseTime := parse.Number(v); releaseTime > 0 {
timeStr = date.ParseDateToStr(releaseTime, date.YYYY_MM_DDTHH_MM_SSZ)
} else {
timeStr = fmt.Sprintf("%s", v)
}
}
// 取网元名称
neName := ""
if v, ok := row["neName"]; ok && v != nil {
neName = fmt.Sprintf("%s", v)
}
// 按排序后的顺序获取数据
dataRow := []string{}
for _, kpiId := range sortedKpiIds {
value := ""
if v, ok := row[kpiId]; ok && v != nil {
value = fmt.Sprint(v)
}
dataRow = append(dataRow, value)
}
dataRow = append(dataRow, neName)
dataRow = append(dataRow, timeStr)
data = append(data, dataRow)
}
// 输出到文件
if err := file.WriterFileCSV(data, filePath); err != nil {
logger.Errorf("export operate log err => %v", err.Error())
return "export err"
}
}
if fileType == "xlsx" {
// 获取kpiIdMap的键并排序
var sortedKpiIds []string
for kpiId := range kpiIdMap {
sortedKpiIds = append(sortedKpiIds, kpiId)
}
sort.Strings(sortedKpiIds) // 按字母排序
// 第一行表头标题 - 按排序后的顺序
headerCells := map[string]string{}
colIndex := 0
// 先添加KPI标题列
for i, kpiId := range sortedKpiIds {
colLetter := string(rune('A' + i))
headerCells[colLetter+"1"] = kpiIdMap[kpiId]
colIndex++
}
// 添加NE Name和Time列
neNameCol := string(rune('A' + colIndex))
headerCells[neNameCol+"1"] = "NE Name"
colIndex++
timeCol := string(rune('A' + colIndex))
headerCells[timeCol+"1"] = "Time"
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 取时间和网元名称
timeStr := ""
if v, ok := row["timeGroup"]; ok && v != nil {
if releaseTime := parse.Number(v); releaseTime > 0 {
timeStr = date.ParseDateToStr(releaseTime, date.YYYY_MM_DDTHH_MM_SSZ)
} else {
timeStr = fmt.Sprintf("%s", v)
}
}
neName := ""
if v, ok := row["neName"]; ok && v != nil {
neName = fmt.Sprintf("%s", v)
}
// 按排序后的顺序填充数据
dataCell := map[string]any{}
// 填充KPI数据
for j, kpiId := range sortedKpiIds {
value := ""
if v, ok := row[kpiId]; ok && v != nil {
value = fmt.Sprint(v)
}
colLetter := string(rune('A' + j))
dataCell[colLetter+idx] = value
}
// 填充NE Name和Time
dataCell[neNameCol+idx] = neName
dataCell[timeCol+idx] = timeStr
dataCells = append(dataCells, dataCell)
}
// 导出数据表格
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, ""); err != nil {
return "ok, ftp err:" + err.Error()
}
return "ok"
}

View File

@@ -79,8 +79,6 @@ func (s BackupExportLogProcessor) exportOperate(hour int, fileType string) strin
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 {
@@ -97,7 +95,8 @@ func (s BackupExportLogProcessor) exportOperate(hour int, fileType string) strin
converI18n(language, &rows)
// 导出文件名称
fileName := fmt.Sprintf("sys_log_operate_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType)
dateStr := date.ParseDateToStr(end.UnixMilli(), date.YYYYMMDDHHMMSS)
fileName := fmt.Sprintf("sys_log_operate_export_%d_%s.%s", len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/log/sys_log_operate", fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
@@ -243,8 +242,6 @@ func (s BackupExportLogProcessor) exportLogin(hour int, fileType string) string
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 {
@@ -263,7 +260,8 @@ func (s BackupExportLogProcessor) exportLogin(hour int, fileType string) string
converI18n(language, &rows)
// 导出文件名称
fileName := fmt.Sprintf("sys_log_login_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType)
dateStr := date.ParseDateToStr(end.UnixMilli(), date.YYYYMMDDHHMMSS)
fileName := fmt.Sprintf("sys_log_login_export_%d_%s.%s", len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/log/sys_log_login", fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)

View File

@@ -85,9 +85,10 @@ func (s BackupExportUDMProcessor) exportAuth(neId, fileType string) string {
return "no data"
}
// 文件名
fileName := fmt.Sprintf("auth_%s_export_%s.%s", neId, time.Now().Format("20060102150405"), fileType)
filePath := filepath.Join("/usr/local/omc/backup/udm_data/auth", fileName)
// 导出文件名
dateStr := time.Now().Format("20060102150405")
fileName := fmt.Sprintf("auth_%s_export_%d_%s.%s", neId, len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/udm/auth", fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}
@@ -139,9 +140,10 @@ func (s BackupExportUDMProcessor) exportSub(neId, fileType string) string {
return "no data"
}
// 文件名
fileName := fmt.Sprintf("sub_%s_export_%s.%s", neId, time.Now().Format("20060102150405"), fileType)
filePath := filepath.Join("/usr/local/omc/backup/udm_data/sub", fileName)
// 导出文件名
dateStr := time.Now().Format("20060102150405")
fileName := fmt.Sprintf("sub_%s_export_%d_%s.%s", neId, len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/udm/sub", fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}
@@ -187,9 +189,10 @@ func (s BackupExportUDMProcessor) exportVOIP(neId, fileType string) string {
return "no data"
}
// 文件名
fileName := fmt.Sprintf("voip_%s_export_%s.%s", neId, time.Now().Format("20060102150405"), fileType)
filePath := filepath.Join("/usr/local/omc/backup/udm_data/voip", fileName)
// 导出文件名
dateStr := time.Now().Format("20060102150405")
fileName := fmt.Sprintf("voip_%s_export_%d_%s.%s", neId, len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/udm/voip", fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}
@@ -233,9 +236,10 @@ func (s BackupExportUDMProcessor) exportVolte(neId, fileType string) string {
return "no data"
}
// 文件名
fileName := fmt.Sprintf("volte_%s_export_%s.%s", neId, time.Now().Format("20060102150405"), fileType)
filePath := filepath.Join("/usr/local/omc/backup/udm_data/volte", fileName)
// 导出文件名
dateStr := time.Now().Format("20060102150405")
fileName := fmt.Sprintf("volte_%s_export_%d_%s.%s", neId, len(rows), dateStr, fileType)
filePath := filepath.Join(s.backupService.BACKUP_DIR, "/udm/volte", fileName)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}

View File

@@ -25,9 +25,9 @@ import (
)
var (
triggerMax int64 = 3 // 阈值:连续触发次数大于该值才会产生告警
triggerCount sync.Map // 阈值连续触发次数,存储每个事件的触发记录
triggerWindow time.Duration = 5 * time.Second // 事件触发的时间窗口
triggerMax int64 = 3 // 阈值:连续触发次数大于该值才会产生告警
triggerCount sync.Map // 阈值连续触发次数,存储每个事件的触发记录
triggerWindow time.Duration = 30 * time.Second // 事件触发的时间窗口
)
var NewProcessor = &NeAlarmStateCheckCMDProcessor{

View File

@@ -3,6 +3,7 @@ package processor
import (
"be.ems/src/framework/cron"
processorBackupExportCDR "be.ems/src/modules/crontask/processor/backup_export_cdr"
processorBackupExportKPI "be.ems/src/modules/crontask/processor/backup_export_kpi"
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"
@@ -60,4 +61,6 @@ func InitCronQueue() {
cron.CreateQueue("backup_export_cdr", processorBackupExportCDR.NewProcessor)
// 备份-导出Log数据
cron.CreateQueue("backup_export_log", processorBackupExportLog.NewProcessor)
// 备份-导出KPI数据
cron.CreateQueue("backup_export_kpi", processorBackupExportKPI.NewProcessor)
}

View File

@@ -395,8 +395,6 @@ func (s NeInfoController) Add(c *gin.Context) {
// 已有网元可获取的信息
if body.ServerState != nil {
if v, ok := body.ServerState["version"]; ok && v != nil {
neVersion.Name = "-"
neVersion.Path = "-"
neVersion.Version = fmt.Sprint(v)
}
if v, ok := body.ServerState["sn"]; ok && v != nil {
@@ -499,8 +497,6 @@ func (s NeInfoController) Edit(c *gin.Context) {
// 已有网元可获取的信息
if body.ServerState != nil {
if v, ok := body.ServerState["version"]; ok && v != nil {
neVersion.Name = "-"
neVersion.Path = "-"
neVersion.Version = fmt.Sprint(v)
neVersion.UpdateBy = loginUserName
}

View File

@@ -80,28 +80,99 @@ func (s NeConfigBackup) FileLocalToNe(neInfo model.NeInfo, localFile string) err
return fmt.Errorf("unzip err")
}
// 网元主机的SSH客户
// sshClient, err := NewNeInfo.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
// if err != nil {
// return fmt.Errorf("ne info ssh client err")
// }
// defer sshClient.Close()
// // 网元主机的SSH客户端进行文件传输
// sftpClient, err := sshClient.NewClientSFTP()
// if err != nil {
// return fmt.Errorf("ne info sftp client err")
// }
// defer sftpClient.Close()
// 本地复制到网元端
if err := s.FileLocalToNePath(neInfo, localDirPath); err != nil {
return err
}
_ = os.RemoveAll(localDirPath) // 删除本地临时目录
return nil
}
// FileNeToLocal 网元备份文件网元端复制到本地
func (s NeConfigBackup) FileNeToLocal(neInfo model.NeInfo) (string, error) {
neTypeLower := strings.ToLower(neInfo.NeType)
// 网管本地路径
omcPath := "/usr/local/omc/backup/ne_config"
if runtime.GOOS == "windows" {
omcPath = fmt.Sprintf("C:%s", omcPath)
}
localDirPath := fmt.Sprintf("%s/%s/%s/from_ne_tmp", omcPath, neTypeLower, neInfo.NeId)
// 网元端复制到本地
if err := s.FileNeToLocalPath(neInfo, localDirPath); err != nil {
return "", err
}
// 压缩zip文件名
zipFileName := fmt.Sprintf("%s-%s-etc-%s.zip", neTypeLower, neInfo.NeId, date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS))
zipFilePath := fmt.Sprintf("%s/%s/%s/%s", omcPath, neTypeLower, neInfo.NeId, zipFileName)
if err := file.CompressZipByDir(zipFilePath, localDirPath); err != nil {
return "", fmt.Errorf("compress zip err")
}
_ = os.RemoveAll(localDirPath) // 删除本地临时目录
return zipFilePath, nil
}
// FileNeToLocalPath 网元备份文件网元端复制到本地路径
func (s NeConfigBackup) FileNeToLocalPath(neInfo model.NeInfo, localDirPath string) error {
neTypeLower := strings.ToLower(neInfo.NeType)
// 网元配置文件先复制到临时目录
cmd.Exec("sudo mkdir -p /tmp/omc/ne_config && sudo chmod 777 -R /tmp/omc")
neDirTemp := fmt.Sprintf("/tmp/omc/ne_config/%s/%s", neTypeLower, neInfo.NeId)
switch neTypeLower {
case "ims":
// ims目录
cmd.Exec(fmt.Sprintf("mkdir -p %s/ims", neDirTemp))
imsDirArr := [...]string{"bgcf", "icscf", "ismc", "mmtel", "mrf", "oam_manages.yaml", "pcscf", "scscf", "vars.cfg", "zlog"}
for _, v := range imsDirArr {
cmd.Exec(fmt.Sprintf("sudo cp -rf /usr/local/etc/ims/%s %s/ims", v, neDirTemp))
}
// mf目录
cmd.Exec(fmt.Sprintf("mkdir -p %s/mf && sudo cp -rf /usr/local/etc/mf %s", neDirTemp, neDirTemp))
// rtproxy目录
cmd.Exec(fmt.Sprintf("mkdir -p %s/rtproxy && sudo cp -rf /usr/local/etc/rtproxy/rtproxy.conf %s/rtproxy", neDirTemp, neDirTemp))
// iwf目录
cmd.Exec(fmt.Sprintf("mkdir -p %s/iwf && sudo cp -rf /usr/local/etc/iwf/*.yaml %s/iwf", neDirTemp, neDirTemp))
case "udm":
// udm目录
cmd.Exec(fmt.Sprintf("mkdir -p %s && sudo cp -rf /usr/local/etc/udm/*.yaml %s", neDirTemp, neDirTemp))
// kvdb目录
cmd.Exec(fmt.Sprintf("mkdir -p %s/kvdb && sudo cp -rf /usr/local/etc/kvdb/*.{rdb,conf} %s/kvdb", neDirTemp, neDirTemp))
cmd.Exec(fmt.Sprintf("mkdir -p %s/kvdb && sudo cp -rf /usr/local/etc/kvdb/log %s/kvdb", neDirTemp, neDirTemp))
case "smsc":
cmd.Exec(fmt.Sprintf("mkdir -p %s && sudo cp -rf /usr/local/etc/smsc/{*.yaml,*.conf,*conf.txt} %s", neDirTemp, neDirTemp))
cmd.Exec(fmt.Sprintf("sudo cp -rf /usr/local/etc/smsc/conf %s/conf", neDirTemp))
default:
nePath := fmt.Sprintf("/usr/local/etc/%s/*.yaml", neTypeLower)
if neTypeLower == "amf" {
nePath = fmt.Sprintf("/usr/local/etc/%s/*.{yaml,csv}", neTypeLower)
}
if neTypeLower == "mme" {
nePath = fmt.Sprintf("/usr/local/etc/%s/*.{yaml,conf,csv}", neTypeLower)
}
cmd.Exec(fmt.Sprintf("mkdir -p %s && sudo cp -rf %s %s", neDirTemp, nePath, neDirTemp))
}
// 网元端复制到本地
if err := file.CopyDir(neDirTemp, localDirPath); err != nil {
return fmt.Errorf("copy config err")
}
cmd.Exec(fmt.Sprintf("sudo rm -rf %s", neDirTemp)) // 删除临时目录
return nil
}
// FileLocalToNePath 网元备份文件本地路径复制到网元端
func (s NeConfigBackup) FileLocalToNePath(neInfo model.NeInfo, localDirPath string) error {
neTypeLower := strings.ToLower(neInfo.NeType)
// 网元配置端上的临时目录
neDirTemp := fmt.Sprintf("/tmp/omc/ne_config/%s/%s", neTypeLower, neInfo.NeId)
// sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p /tmp/omc/ne_config && sudo chmod 777 -R /tmp/omc && sudo rm -rf %s", neDirTemp))
cmd.Execf("sudo mkdir -p /tmp/omc/ne_config && sudo chmod 777 -R /tmp/omc && sudo rm -rf %s", neDirTemp)
// 复制到网元端
// if err = sftpClient.CopyDirLocalToRemote(localDirPath, neDirTemp); err != nil {
// return fmt.Errorf("copy config to ne err")
// }
if err := file.CopyDir(localDirPath, neDirTemp); err != nil {
cmd.Exec(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 777 -R /tmp/omc && sudo rm -rf %s", neDirTemp, neDirTemp))
// 网元端复制到本地
if err := file.CopyDir(neDirTemp, localDirPath); err != nil {
return fmt.Errorf("copy config to ne err")
}
@@ -122,85 +193,24 @@ func (s NeConfigBackup) FileLocalToNe(neInfo model.NeInfo, localFile string) err
case "smsc":
chmodFile := "sudo chmod 777 /usr/local/etc/smsc/{*sys.conf,*conf.txt,conf/is41_operation.conf}"
cmd.Exec(fmt.Sprintf("sudo mkdir -p /usr/local/etc/smsc/conf && sudo cp -rf %s/* /usr/local/etc/smsc && %s", neDirTemp, chmodFile))
case "udm":
// udm目录
chmodFile := "sudo chmod 777 /usr/local/etc/udm/*.yaml"
cmd.Exec(fmt.Sprintf("sudo cp -rf %s/*.yaml /usr/local/etc/udm && %s", neDirTemp, chmodFile))
// kvdb目录
cmd.Exec(fmt.Sprintf("sudo mkdir -p /usr/local/etc/kvdb && sudo cp -rf %s/kvdb/* /usr/local/etc/kvdb && sudo chmod 777 /usr/local/etc/kvdb/*.{rdb,conf}", neDirTemp))
default:
neEtcPath := fmt.Sprintf("/usr/local/etc/%s", neTypeLower)
chmodFile := fmt.Sprintf("sudo chmod 777 %s/*.yaml", neEtcPath)
if neTypeLower == "mme" {
chmodFile = fmt.Sprintf("sudo chmod 777 %s/*.{yaml,conf}", neEtcPath)
if neTypeLower == "amf" {
chmodFile = fmt.Sprintf("sudo chmod 777 %s/*.{yaml,csv}", neEtcPath)
}
cmd.Execf("sudo cp -rf %s/* %s && %s", neDirTemp, neEtcPath, chmodFile)
if neTypeLower == "mme" {
chmodFile = fmt.Sprintf("sudo chmod 777 %s/*.{yaml,conf,csv}", neEtcPath)
}
cmd.Exec(fmt.Sprintf("sudo cp -rf %s/* %s && %s", neDirTemp, neEtcPath, chmodFile))
}
_ = os.RemoveAll(localDirPath) // 删除本地临时目录
cmd.Execf("sudo rm -rf %s", neDirTemp) // 删除临时目录
cmd.Exec(fmt.Sprintf("sudo rm -rf %s", neDirTemp)) // 删除临时目录
return nil
}
// FileNeToLocal 网元备份文件网元端复制到本地
func (s NeConfigBackup) FileNeToLocal(neInfo model.NeInfo) (string, error) {
// 网元主机的SSH客户端
// sshClient, err := NewNeInfo.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
// if err != nil {
// return "", fmt.Errorf("ne info ssh client err")
// }
// defer sshClient.Close()
// // 网元主机的SSH客户端进行文件传输
// sftpClient, err := sshClient.NewClientSFTP()
// if err != nil {
// return "", fmt.Errorf("ne info sftp client err")
// }
// defer sftpClient.Close()
neTypeLower := strings.ToLower(neInfo.NeType)
// 网管本地路径
omcPath := "/usr/local/omc/backup/ne_config"
if runtime.GOOS == "windows" {
omcPath = fmt.Sprintf("C:%s", omcPath)
}
localDirPath := fmt.Sprintf("%s/%s/%s/from_ne_tmp", omcPath, neTypeLower, neInfo.NeId)
// 网元配置文件先复制到临时目录
// sshClient.RunCMD("sudo mkdir -p /tmp/omc/ne_config && sudo chmod 777 -R /tmp/omc")
cmd.Exec("sudo mkdir -p /tmp/omc/ne_config && sudo chmod 777 -R /tmp/omc")
neDirTemp := fmt.Sprintf("/tmp/omc/ne_config/%s/%s", neTypeLower, neInfo.NeId)
switch neTypeLower {
case "ims":
// ims目录
cmd.Execf("mkdir -p %s/ims", neDirTemp)
imsDirArr := [...]string{"bgcf", "icscf", "ismc", "mmtel", "mrf", "oam_manages.yaml", "pcscf", "scscf", "vars.cfg", "zlog"}
for _, v := range imsDirArr {
cmd.Execf("sudo cp -rf /usr/local/etc/ims/%s %s/ims", v, neDirTemp)
}
// mf目录
cmd.Execf("mkdir -p %s/mf && sudo cp -rf /usr/local/etc/mf %s", neDirTemp, neDirTemp)
// rtproxy目录
cmd.Execf("mkdir -p %s/rtproxy && sudo cp -rf /usr/local/etc/rtproxy/rtproxy.conf %s/rtproxy", neDirTemp, neDirTemp)
// iwf目录
cmd.Exec(fmt.Sprintf("mkdir -p %s/iwf && sudo cp -rf /usr/local/etc/iwf/*.yaml %s/iwf", neDirTemp, neDirTemp))
case "smsc":
cmd.Exec(fmt.Sprintf("mkdir -p %s && sudo cp -rf /usr/local/etc/smsc/{*.yaml,*.conf,*conf.txt} %s", neDirTemp, neDirTemp))
cmd.Exec(fmt.Sprintf("sudo cp -rf /usr/local/etc/smsc/conf %s/conf", neDirTemp))
default:
nePath := fmt.Sprintf("/usr/local/etc/%s/*.yaml", neTypeLower)
if neTypeLower == "mme" {
nePath = fmt.Sprintf("/usr/local/etc/%s/*.{yaml,conf}", neTypeLower)
}
cmd.Execf("mkdir -p %s && sudo cp -rf %s %s", neDirTemp, nePath, neDirTemp)
}
// 网元端复制到本地
if err := file.CopyDir(neDirTemp, localDirPath); err != nil {
return "", fmt.Errorf("copy config err")
}
// 压缩zip文件名
zipFileName := fmt.Sprintf("%s-%s-etc-%s.zip", neTypeLower, neInfo.NeId, date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS))
zipFilePath := fmt.Sprintf("%s/%s/%s/%s", omcPath, neTypeLower, neInfo.NeId, zipFileName)
if err := file.CompressZipByDir(zipFilePath, localDirPath); err != nil {
return "", fmt.Errorf("compress zip err")
}
_ = os.RemoveAll(localDirPath) // 删除本地临时目录
cmd.Execf("sudo rm -rf %s", neDirTemp) // 删除临时目录
return zipFilePath, nil
}

View File

@@ -10,6 +10,7 @@ import (
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/file"
"be.ems/src/modules/ne_data/model"
"be.ems/src/modules/ne_data/service"
@@ -99,3 +100,42 @@ func (s BackupController) FTPPush(c *gin.Context) {
}
c.JSON(200, resp.Ok(nil))
}
// 备份文件-导入OMC
//
// POST /import-omc
func (s BackupController) ImportOMC(c *gin.Context) {
var body struct {
NeType string `json:"neType" binding:"required,oneof=OMC"`
Path string `json:"path" binding:"required"` // 文件路径
}
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
if !strings.HasSuffix(body.Path, ".zip") {
c.JSON(200, resp.ErrMsg("Only supports decompression of zip files"))
return
}
// 将zip文件解压到本地后复制到网元端
localFilePath := file.ParseUploadFileAbsPath(body.Path)
if err := s.backupService.BackupOMCImport(localFilePath); err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// 备份文件-导出OMC
//
// POST /export-omc
func (s BackupController) ExportOMC(c *gin.Context) {
zipFilePath, err := s.backupService.BackupOMCExport()
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.FileAttachment(zipFilePath, filepath.Base(zipFilePath))
}

View File

@@ -49,10 +49,12 @@ type AMFController struct {
func (s *AMFController) UEList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
@@ -119,10 +121,12 @@ func (s *AMFController) UERemove(c *gin.Context) {
func (s *AMFController) UEExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])

View File

@@ -17,16 +17,18 @@ import (
// 实例化控制层 IMSController 结构体
var NewIMS = &IMSController{
neInfoService: neService.NewNeInfo,
cdrEventService: neDataService.NewCDREvent,
neInfoService: neService.NewNeInfo,
cdrEventService: neDataService.NewCDREvent,
kpiReportService: neDataService.NewKpiReport,
}
// 网元IMS
//
// PATH /ims
type IMSController struct {
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREvent // CDR会话事件服务
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREvent // CDR会话事件服务
kpiReportService *neDataService.KpiReport // 统计信息服务
}
// CDR会话列表
@@ -50,10 +52,12 @@ type IMSController struct {
func (s *IMSController) CDRList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
@@ -120,10 +124,12 @@ func (s *IMSController) CDRRemove(c *gin.Context) {
func (s *IMSController) CDRExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
@@ -236,3 +242,101 @@ func (s *IMSController) UeSessionList(c *gin.Context) {
c.JSON(200, resp.OkData(data))
}
// KPI 忙时统计
//
// GET /kpi/busy-hour
//
// @Tags network_data/ims
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Param timestamp query int64 false "timestamp"
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Busy hour statistics
// @Description Busy hour statistics
// @Router /neData/ims/kpi/busy-hour [get]
func (s IMSController) KPIBusyHour(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var query struct {
NeID string `form:"neId" binding:"required"`
Timestamp int64 `form:"timestamp" binding:"required"` // 时间戳毫秒 年月日返回每小时的总和 年月日时返回该小时的总和
}
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.Timestamp < 1e12 || query.Timestamp > 1e13 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "timestamp format is ms"))
return
}
// 查询网元获取IP
neInfo := s.neInfoService.FindByNeTypeAndNeID("IMS", query.NeID)
if neInfo.NeId != query.NeID || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
data := s.kpiReportService.IMSBusyHour(neInfo.RmUID, query.Timestamp)
c.JSON(200, resp.OkData(data))
}
// KPI 忙时统计 周
//
// GET /kpi/busy-week
//
// @Tags network_data/ims
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Param timestamp query int64 false "timestamp"
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Busy week statistics
// @Description Busy week statistics
// @Router /neData/ims/kpi/busy-week [get]
func (s IMSController) KPIBusyWeek(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var query struct {
NeID string `form:"neId" binding:"required"`
WeekStart int64 `form:"weekStart" binding:"required"` // 时间戳毫秒 年月日
WeekEnd int64 `form:"weekEnd" binding:"required"` // 时间戳毫秒 年月日
}
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.WeekStart < 1e12 || query.WeekStart > 1e13 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "weekStart format is ms"))
return
}
if query.WeekEnd < 1e12 || query.WeekEnd > 1e13 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "weekEnd format is ms"))
return
}
if query.WeekEnd < query.WeekStart || query.WeekEnd == query.WeekStart {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "weekEnd must be greater than weekStart and not equal to weekStart"))
return
}
// 计算周差
weekDiff := query.WeekEnd - query.WeekStart
// 周差是否7天
if weekDiff-7*24*60*60*1000 != -1000 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "weekEnd must be 7 days after weekStart"))
return
}
// 查询网元获取IP
neInfo := s.neInfoService.FindByNeTypeAndNeID("IMS", query.NeID)
if neInfo.NeId != query.NeID || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
data := s.kpiReportService.IMSBusyWeek(neInfo.RmUID, query.WeekStart, query.WeekEnd)
c.JSON(200, resp.OkData(data))
}

View File

@@ -49,10 +49,12 @@ type MMEController struct {
func (s *MMEController) UEList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
@@ -119,10 +121,12 @@ func (s *MMEController) UERemove(c *gin.Context) {
func (s *MMEController) UEExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])

View File

@@ -50,10 +50,12 @@ type SGWCController struct {
func (s *SGWCController) CDRList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
@@ -120,10 +122,12 @@ func (s *SGWCController) CDRRemove(c *gin.Context) {
func (s *SGWCController) CDRExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])

View File

@@ -52,10 +52,12 @@ type SMFController struct {
func (s *SMFController) CDRList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
@@ -122,10 +124,12 @@ func (s *SMFController) CDRRemove(c *gin.Context) {
func (s *SMFController) CDRExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])

View File

@@ -49,10 +49,12 @@ type SMSCController struct {
func (s *SMSCController) CDRList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
@@ -119,10 +121,12 @@ func (s *SMSCController) CDRRemove(c *gin.Context) {
func (s *SMSCController) CDRExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := reqctx.QueryMap(c)
// 限制导出数据集
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])

View File

@@ -184,6 +184,14 @@ func Setup(router *gin.Engine) {
middleware.AuthorizeUser(nil),
controller.NewIMS.UeSessionList,
)
imsGroup.GET("/kpi/busy-hour",
middleware.AuthorizeUser(nil),
controller.NewIMS.KPIBusyHour,
)
imsGroup.GET("/kpi/busy-week",
middleware.AuthorizeUser(nil),
controller.NewIMS.KPIBusyWeek,
)
}
// 网元SMSC
@@ -308,6 +316,16 @@ func Setup(router *gin.Engine) {
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.backup", collectlogs.BUSINESS_TYPE_OTHER)),
controller.NewBackup.FTPPush,
)
backupGroup.POST("/import-omc",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.backup", collectlogs.BUSINESS_TYPE_IMPORT)),
controller.NewBackup.ImportOMC,
)
backupGroup.POST("/export-omc",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.backup", collectlogs.BUSINESS_TYPE_EXPORT)),
controller.NewBackup.ExportOMC,
)
}
// 网元UDM 鉴权用户信息

View File

@@ -91,11 +91,10 @@ func (r CDREvent) SelectByPage(neType string, query map[string]string) ([]model.
}
// 分页
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
if pageNum == 0 && pageSize > int(total) {
pageSize = int(total)
if query["pageNum"] != "" && query["pageSize"] != "" {
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
tx = tx.Offset(int(pageNum * pageSize)).Limit(int(pageSize))
}
tx = tx.Offset(int(pageNum * pageSize)).Limit(int(pageSize))
// 查询数据
if err := tx.Find(&rows).Error; err != nil {

View File

@@ -115,7 +115,24 @@ func (r KpiReport) SelectUPF(rmUID string, beginTime, endTime int64) []model.Kpi
tx = tx.Where("created_at <= ?", endTime)
// 查询数据
rows := []model.KpiReport{}
if err := tx.Select("kpi_values").Find(&rows).Error; err != nil {
if err := tx.Select("kpi_values", "created_at").Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// SelectIMS 查询IMS数据
func (r KpiReport) SelectIMS(rmUID string, beginTime, endTime int64) []model.KpiReport {
tx := db.DB("").Model(&model.KpiReport{})
// 表名
tx = tx.Table("kpi_report_ims")
tx = tx.Where("rm_uid = ?", rmUID)
tx = tx.Where("created_at >= ?", beginTime)
tx = tx.Where("created_at <= ?", endTime)
// 查询数据
rows := []model.KpiReport{}
if err := tx.Select("kpi_values", "created_at").Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}

View File

@@ -56,8 +56,10 @@ func (r UEEvent) SelectByPage(neType string, query map[string]string) ([]model.U
}
// 分页
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
tx = tx.Offset(int(pageNum * pageSize)).Limit(int(pageSize))
if query["pageNum"] != "" && query["pageSize"] != "" {
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
tx = tx.Offset(int(pageNum * pageSize)).Limit(int(pageSize))
}
// 查询数据
if err := tx.Find(&rows).Error; err != nil {

View File

@@ -3,10 +3,17 @@ package service
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"time"
"be.ems/src/framework/cmd"
"be.ems/src/framework/ssh"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
neModel "be.ems/src/modules/ne/model"
neService "be.ems/src/modules/ne/service"
"be.ems/src/modules/ne_data/model"
systemService "be.ems/src/modules/system/service"
@@ -15,14 +22,12 @@ import (
// 实例化数据层 Backup 结构体
var NewBackup = &Backup{
BACKUP_DIR: "/usr/local/omc/backup",
neInfoService: neService.NewNeInfo,
sysConfigService: systemService.NewSysConfig,
}
// Backup 备份相关 服务层处理
type Backup struct {
BACKUP_DIR string // 备份目录
neInfoService *neService.NeInfo // 网元信息服务
sysConfigService *systemService.SysConfig // 参数配置服务
}
@@ -84,3 +89,194 @@ func (r Backup) FTPPushFile(localFilePath, tag string) error {
}
return nil
}
// BackupOMCImport 网元配置文件复制到网元端覆盖
func (r Backup) BackupOMCImport(localZipFile string) error {
neInfoService := neService.NewNeInfo
neVersionService := neService.NewNeVersion
neLicenseService := neService.NewNeLicense
neConfigBackupService := neService.NewNeConfigBackup
// 网管本地路径
omcPath := "/usr/local/omc/backup_omc"
if runtime.GOOS == "windows" {
omcPath = fmt.Sprintf("C:%s", omcPath)
}
// 解压到临时目录
omcPathTmp := fmt.Sprintf("%s/import", omcPath)
if err := file.UnZip(localZipFile, omcPathTmp); err != nil {
return fmt.Errorf("unzip err")
}
defer os.RemoveAll(omcPathTmp) // 删除本地临时目录
// 还原OMC-网元信息
neInfos := make([]neModel.NeInfo, 0) // 网元信息列表
neVersions := make([]neModel.NeVersion, 0) // 网元版本列表
neLicenses := make([]neModel.NeLicense, 0) // 网元许可证列表
neHosts := make([]neModel.NeHost, 0) // 网元主机列表
localDirPath := fmt.Sprintf("%s/omc/db", omcPathTmp)
neInfoStrArr := file.ReadFileJSONLine(fmt.Sprintf("%s/ne_info.json", localDirPath))
for _, str := range neInfoStrArr {
var neInfo neModel.NeInfo
if err := json.Unmarshal([]byte(str), &neInfo); err != nil {
continue
}
neInfos = append(neInfos, neInfo)
}
neVersionStrArr := file.ReadFileJSONLine(fmt.Sprintf("%s/ne_version.json", localDirPath))
for _, str := range neVersionStrArr {
var neVersion neModel.NeVersion
if err := json.Unmarshal([]byte(str), &neVersion); err != nil {
continue
}
neVersions = append(neVersions, neVersion)
}
neLicenseStrArr := file.ReadFileJSONLine(fmt.Sprintf("%s/ne_license.json", localDirPath))
for _, str := range neLicenseStrArr {
var neLicense neModel.NeLicense
if err := json.Unmarshal([]byte(str), &neLicense); err != nil {
continue
}
neLicenses = append(neLicenses, neLicense)
}
neHostStrArr := file.ReadFileJSONLine(fmt.Sprintf("%s/ne_host.json", localDirPath))
for _, str := range neHostStrArr {
var neHost neModel.NeHost
if err := json.Unmarshal([]byte(str), &neHost); err != nil {
continue
}
neHosts = append(neHosts, neHost)
}
// 新增网元信息
for _, neInfo := range neInfos {
if neInfo.NeId == "" || neInfo.NeType == "OMC" {
continue
}
// 删除网元信息
neInfoService.FindByNeTypeAndNeID(neInfo.NeType, neInfo.NeId)
if neInfo.ID > 0 {
neInfoService.DeleteByIds([]int64{neInfo.ID})
}
neVersion := neModel.NeVersion{}
for _, v := range neVersions {
if v.NeId == neInfo.NeId && v.NeType == neInfo.NeType {
v.ID = 0
neVersion = v
break
}
}
neLicense := neModel.NeLicense{}
for _, v := range neLicenses {
if v.NeId == neInfo.NeId && v.NeType == neInfo.NeType {
v.ID = 0
neLicense = v
break
}
}
neHostArr := make([]neModel.NeHost, 0) // 网元主机列表
neHostIDs := strings.Split(neInfo.HostIDs, ",")
for _, hostID := range neHostIDs {
hostIDNum := parse.Number(hostID)
for _, v := range neHosts {
if v.ID == hostIDNum {
v.ID = 0
neHostArr = append(neHostArr, v)
}
}
}
neVersionService.Insert(neVersion)
neLicenseService.Insert(neLicense)
neInfo.Hosts = neHostArr
neInfo.CreateBy = "system"
neInfoService.Insert(neInfo)
}
// 还原网元备份文件
neList := neInfoService.Find(neModel.NeInfo{}, false, false)
for _, neInfo := range neList {
if neInfo.NeType == "OMC" {
continue
}
neTypeLower := strings.ToLower(neInfo.NeType)
// 配置文件-本地复制到网元端
localDirPath := fmt.Sprintf("%s/ne_config/%s/%s", omcPathTmp, neTypeLower, neInfo.NeId)
if err := neConfigBackupService.FileLocalToNePath(neInfo, localDirPath); err != nil {
continue
}
}
return nil
}
// BackupOMCExport 备份OMC数据导出
func (r Backup) BackupOMCExport() (string, error) {
neInfoService := neService.NewNeInfo
neVersionService := neService.NewNeVersion
neLicenseService := neService.NewNeLicense
neHostService := neService.NewNeHost
neConfigBackupService := neService.NewNeConfigBackup
// 网管本地路径
omcPath := "/usr/local/omc/backup_omc"
if runtime.GOOS == "windows" {
omcPath = fmt.Sprintf("C:%s", omcPath)
}
omcPathTmp := fmt.Sprintf("%s/tmp", omcPath)
defer os.RemoveAll(omcPathTmp) // 删除本地临时目录
// 备份网元配置文件
neList := neInfoService.Find(neModel.NeInfo{}, false, false)
for _, neInfo := range neList {
if neInfo.NeType == "OMC" {
continue
}
neTypeLower := strings.ToLower(neInfo.NeType)
// 配置文件-网元端复制到本地
localDirPath := fmt.Sprintf("%s/ne_config/%s/%s", omcPathTmp, neTypeLower, neInfo.NeId)
cmd.Execf("sudo mkdir -p %s", localDirPath)
if err := neConfigBackupService.FileNeToLocalPath(neInfo, localDirPath); err != nil {
continue
}
}
// 备份OMC
neInfos := make([]any, 0) // 网元信息列表
neVersions := make([]any, 0) // 网元版本列表
neLicenses := make([]any, 0) // 网元许可证列表
neHosts := make([]any, 0) // 网元主机列表
for _, neInfo := range neList {
neVersion := neVersionService.FindByNeTypeAndNeID(neInfo.NeType, neInfo.NeId)
if neVersion.ID > 0 {
neVersions = append(neVersions, neVersion)
}
neLicense := neLicenseService.FindByNeTypeAndNeID(neInfo.NeType, neInfo.NeId)
if neLicense.ID > 0 {
neLicenses = append(neLicenses, neLicense)
}
neHostIDs := strings.Split(neInfo.HostIDs, ",")
for _, hostID := range neHostIDs {
neHost := neHostService.FindById(parse.Number(hostID))
if neHost.ID > 0 {
neHosts = append(neHosts, neHost)
}
}
neInfos = append(neInfos, neInfo)
}
localDirPath := fmt.Sprintf("%s/omc/db", omcPathTmp)
cmd.Execf("sudo mkdir -p %s", localDirPath)
file.WriterFileJSONLine(neInfos, fmt.Sprintf("%s/ne_info.json", localDirPath))
file.WriterFileJSONLine(neVersions, fmt.Sprintf("%s/ne_version.json", localDirPath))
file.WriterFileJSONLine(neLicenses, fmt.Sprintf("%s/ne_license.json", localDirPath))
file.WriterFileJSONLine(neHosts, fmt.Sprintf("%s/ne_host.json", localDirPath))
// 压缩zip文件名
zipFileName := fmt.Sprintf("BackupOMC-%s.zip", time.Now().Format("20060102150405"))
zipFilePath := fmt.Sprintf("%s/%s", omcPath, zipFileName)
if err := file.CompressZipByDir(zipFilePath, omcPathTmp); err != nil {
return "", fmt.Errorf("compress zip err")
}
return zipFilePath, nil
}

View File

@@ -280,3 +280,222 @@ func (r KpiReport) UPFTodayFlowLoad(day int) {
}
}
}
// IMSBusyHour IMS忙时流量统计
// SCSCF.06呼叫尝试次数 SCSCF.09呼叫成功次数
func (r KpiReport) IMSBusyHour(rmUID string, timestamp int64) []map[string]any {
t := time.UnixMilli(timestamp)
beginTime := t
endTime := t
// 检查时分秒是否都为零
if t.Hour() == 0 && t.Minute() == 0 && t.Second() == 0 {
// 获取当天起始时间00:00:00
beginTime = t.Truncate(time.Hour)
// 计算当天结束时间23:59:59
endTime = beginTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
} else {
// 起始时间:当前小时的 00 分 00 秒
beginTime = t.Truncate(time.Hour)
// 结束时间:当前小时的 59 分 59 秒 999 毫秒
endTime = beginTime.Add(time.Hour - time.Millisecond)
}
// 转换为毫秒级时间戳
rows := r.kpiReportRepository.SelectIMS(rmUID, beginTime.UnixMilli(), endTime.UnixMilli())
// 创建一个map来存储按时间段合并后的数据
timeGroup := make(map[int64]map[string]int64)
// 遍历每个数据项
for _, row := range rows {
// 将毫秒时间戳转换为小时级时间戳(保留到小时的起始毫秒)
timeHour := row.CreatedAt / 3600000 * 3600000 // 1小时 = 3600000毫秒
// 解析 JSON 字符串为 map
var kpiValues []map[string]any
err := json.Unmarshal([]byte(row.KpiValues), &kpiValues)
if err != nil {
continue
}
var callAttempts, callCompletions int64
for _, v := range kpiValues {
if k, ok := v["kpiId"]; ok {
if k == "SCSCF.06" {
callAttempts = parse.Number(v["value"])
}
if k == "SCSCF.09" {
callCompletions = parse.Number(v["value"])
}
}
}
// 合并到对应的小时段
if _, exists := timeGroup[timeHour]; !exists {
timeGroup[timeHour] = map[string]int64{
"callAttempts": 0,
"callCompletions": 0,
}
}
timeGroup[timeHour]["callAttempts"] += callAttempts
timeGroup[timeHour]["callCompletions"] += callCompletions
}
// 时间组合输出
data := make([]map[string]any, 0, len(timeGroup))
for hour, sums := range timeGroup {
data = append(data, map[string]any{
"timeGroup": fmt.Sprintf("%d", hour),
"callAttempts": sums["callAttempts"],
"callCompletions": sums["callCompletions"],
})
}
return data
}
// 定义结构体用于存储话务量值和对应的时间
type TrafficData struct {
Time int64 `json:"time"` // 时间戳(毫秒)
Value float64 `json:"value"` // 话务量值
}
// IMSBusyWeek IMS忙时流量统计 周
func (r KpiReport) IMSBusyWeek(rmUID string, weekStart, weekEnd int64) map[string]any {
weekStartTime := time.UnixMilli(weekStart)
weekEndTime := time.UnixMilli(weekEnd)
// 1. 获取一周内每小时的呼叫数据
// 转换为毫秒级时间戳
rows := r.kpiReportRepository.SelectIMS(rmUID, weekStartTime.UnixMilli(), weekEndTime.UnixMilli())
// 创建一个map来存储按时间段合并后的数据
timeGroup := make(map[int64]map[string]int64)
// 遍历每个数据项
for _, row := range rows {
// 将毫秒时间戳转换为小时级时间戳(保留到小时的起始毫秒)
timeHour := row.CreatedAt / 3600000 * 3600000 // 1小时 = 3600000毫秒
// 解析 JSON 字符串为 map
var kpiValues []map[string]any
err := json.Unmarshal([]byte(row.KpiValues), &kpiValues)
if err != nil {
continue
}
var callAttempts, callCompletions int64
for _, v := range kpiValues {
if k, ok := v["kpiId"]; ok {
if k == "SCSCF.06" {
callAttempts = parse.Number(v["value"])
}
if k == "SCSCF.09" {
callCompletions = parse.Number(v["value"])
}
}
}
// 合并到对应的小时段
if _, exists := timeGroup[timeHour]; !exists {
timeGroup[timeHour] = map[string]int64{
"callAttempts": 0,
"callCompletions": 0,
}
}
timeGroup[timeHour]["callAttempts"] += callAttempts
timeGroup[timeHour]["callCompletions"] += callCompletions
}
// 时间组合输出
data := make([]map[string]any, 0, len(timeGroup))
for hour, sums := range timeGroup {
data = append(data, map[string]any{
"timeGroup": fmt.Sprintf("%d", hour),
"callAttempts": sums["callAttempts"],
"callCompletions": sums["callCompletions"],
})
}
if len(data) == 0 {
return map[string]any{
"busyHourAverageBHCA": 0,
"busyHourAverageBHCC": 0,
"topFourHoursBHCA": []float64{},
"topFourHoursBHCC": []float64{},
"totalHours": 0,
}
}
// 2. 分离BHCA和BHCC数据并按降序排序
var bhcaData []TrafficData
var bhccData []TrafficData
for _, row := range data {
// 获取时间戳
timeValue := int64(0)
if t, ok := row["timeGroup"]; ok {
timeValue = parse.Number(t)
}
// 处理BHCA数据
if value, ok := row["callAttempts"]; ok {
bhcaVal := parse.Number(value)
bhcaData = append(bhcaData, TrafficData{
Time: timeValue,
Value: float64(bhcaVal),
})
}
// 处理BHCC数据
if value, ok := row["callCompletions"]; ok {
bhccVal := parse.Number(value)
bhccData = append(bhccData, TrafficData{
Time: timeValue,
Value: float64(bhccVal),
})
}
}
// 按降序排序(值大的在前)
sort.Slice(bhcaData, func(i, j int) bool { return bhcaData[i].Value > bhcaData[j].Value })
sort.Slice(bhccData, func(i, j int) bool { return bhccData[i].Value > bhccData[j].Value })
// 3. 取前四个最高值并计算平均值
topFourBHCA := getTopFourTrafficData(bhcaData)
topFourBHCC := getTopFourTrafficData(bhccData)
avgBHCA := calculateTrafficDataAverage(topFourBHCA)
avgBHCC := calculateTrafficDataAverage(topFourBHCC)
// 4. 返回结果
return map[string]any{
"busyHourAverageBHCA": avgBHCA,
"busyHourAverageBHCC": avgBHCC,
"topFourHoursBHCA": topFourBHCA,
"topFourHoursBHCC": topFourBHCC,
"totalHours": len(data),
}
}
// 辅助函数获取前四个最高值的TrafficData
func getTopFourTrafficData(data []TrafficData) []TrafficData {
if len(data) == 0 {
return []TrafficData{}
}
// 最多取前四个值
maxCount := 4
if len(data) < maxCount {
maxCount = len(data)
}
return data[:maxCount]
}
// 辅助函数计算TrafficData的平均值
func calculateTrafficDataAverage(data []TrafficData) float64 {
if len(data) == 0 {
return 0
}
var sum float64 = 0
for _, v := range data {
sum += v.Value
}
return sum / float64(len(data))
}

View File

@@ -10,6 +10,7 @@ import (
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
authService "be.ems/src/modules/auth/service"
"be.ems/src/modules/system/model"
"be.ems/src/modules/system/service"
@@ -48,6 +49,13 @@ type SysLogLoginController struct {
// @Router /system/log/login/list [get]
func (s *SysLogLoginController) List(c *gin.Context) {
query := reqctx.QueryMap(c)
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
dataScopeSQL := reqctx.LoginUserToDataScopeSQL(c, "sys_user", "sys_user")
rows, total := s.sysLogLoginService.FindByPage(query, dataScopeSQL)
@@ -99,6 +107,13 @@ func (s SysLogLoginController) Export(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
query := reqctx.QueryMap(c)
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
dataScopeSQL := reqctx.LoginUserToDataScopeSQL(c, "sys_user", "sys_user")
rows, total := s.sysLogLoginService.FindByPage(query, dataScopeSQL)
if total == 0 {

View File

@@ -10,6 +10,7 @@ import (
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/system/model"
"be.ems/src/modules/system/service"
@@ -50,6 +51,13 @@ func (s *SysLogOperateController) List(c *gin.Context) {
if v, ok := query["title"]; ok && v != "" {
query["title"] = i18n.TFindKeyPrefix(language, "log.operate.title", v)
}
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
dataScopeSQL := reqctx.LoginUserToDataScopeSQL(c, "sys_user", "sys_user")
rows, total := s.sysLogOperateService.FindByPage(query, dataScopeSQL)
@@ -81,6 +89,13 @@ func (s SysLogOperateController) Export(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
query := reqctx.QueryMap(c)
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageNum == 0 || pageSize == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: pageNum or pageSize not is empty"))
return
}
dataScopeSQL := reqctx.LoginUserToDataScopeSQL(c, "sys_user", "sys_user")
rows, total := s.sysLogOperateService.FindByPage(query, dataScopeSQL)
if total == 0 {

View File

@@ -29,20 +29,10 @@ func (r SysLogLogin) SelectByPage(query map[string]string, dataScopeSQL string)
tx = tx.Where("status_flag = ?", v)
}
if v, ok := query["beginTime"]; ok && v != "" {
if len(v) == 10 {
v = fmt.Sprintf("%s000", v)
tx = tx.Where("login_time >= ?", v)
} else if len(v) == 13 {
tx = tx.Where("login_time >= ?", v)
}
tx = tx.Where("login_time >= ?", v)
}
if v, ok := query["endTime"]; ok && v != "" {
if len(v) == 10 {
v = fmt.Sprintf("%s999", v)
tx = tx.Where("login_time <= ?", v)
} else if len(v) == 13 {
tx = tx.Where("login_time <= ?", v)
}
tx = tx.Where("login_time <= ?", v)
}
if dataScopeSQL != "" {
dataScopeSQL = fmt.Sprintf("select distinct user_name from sys_user where %s", dataScopeSQL)
@@ -59,8 +49,11 @@ func (r SysLogLogin) SelectByPage(query map[string]string, dataScopeSQL string)
}
// 查询数据分页
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
tx = tx.Limit(pageSize).Offset(pageSize * pageNum)
if query["pageNum"] != "" && query["pageSize"] != "" {
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
tx = tx.Limit(pageSize).Offset(pageSize * pageNum)
}
err := tx.Order("id desc").Find(&rows).Error
if err != nil {
logger.Errorf("query find err => %v", err.Error())

View File

@@ -35,20 +35,10 @@ func (r SysLogOperate) SelectByPage(query map[string]string, dataScopeSQL string
tx = tx.Where("status_flag = ?", v)
}
if v, ok := query["beginTime"]; ok && v != "" {
if len(v) == 10 {
v = fmt.Sprintf("%s000", v)
tx = tx.Where("opera_time >= ?", v)
} else if len(v) == 13 {
tx = tx.Where("opera_time >= ?", v)
}
tx = tx.Where("opera_time <= ?", v)
}
if v, ok := query["endTime"]; ok && v != "" {
if len(v) == 10 {
v = fmt.Sprintf("%s999", v)
tx = tx.Where("opera_time <= ?", v)
} else if len(v) == 13 {
tx = tx.Where("opera_time <= ?", v)
}
tx = tx.Where("opera_time <= ?", v)
}
if dataScopeSQL != "" {
dataScopeSQL = fmt.Sprintf("select distinct user_name from sys_user where %s", dataScopeSQL)
@@ -65,8 +55,11 @@ func (r SysLogOperate) SelectByPage(query map[string]string, dataScopeSQL string
}
// 查询数据分页
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
tx = tx.Limit(pageSize).Offset(pageSize * pageNum)
if query["pageNum"] != "" && query["pageSize"] != "" {
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
tx = tx.Limit(pageSize).Offset(pageSize * pageNum)
}
err := tx.Order("id desc").Find(&rows).Error
if err != nil {
logger.Errorf("query find err => %v", err.Error())

View File

@@ -167,7 +167,7 @@ func (r SysMenu) CheckUnique(sysMenu model.SysMenu) int64 {
tx := db.DB("").Model(&model.SysMenu{})
tx = tx.Where("del_flag = '0'")
// 查询条件拼接
if sysMenu.ParentId <= 0 {
if sysMenu.ParentId >= 0 {
tx = tx.Where("parent_id = ?", sysMenu.ParentId)
}
if sysMenu.MenuName != "" {

View File

@@ -6,6 +6,7 @@ import (
"be.ems/src/framework/logger"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/parse"
neInfoService "be.ems/src/modules/ne/service"
neDataService "be.ems/src/modules/ne_data/service"
)
@@ -21,7 +22,12 @@ func GetCDRConnect(requestID string, data any) ([]byte, error) {
for k, v := range dataMap {
query[k] = fmt.Sprintf("%v", v)
}
// 分页检查
pageNum := parse.Number(query["pageNum"])
pageSize := parse.Number(query["pageSize"])
if pageNum == 0 || pageSize == 0 {
return nil, fmt.Errorf("pageNum or pageSize not is empty")
}
// 查询网元信息 rmUID
neInfo := neInfoService.NewNeInfo.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {