Merge branch 'main-v2' into lite

This commit is contained in:
TsMask
2025-08-18 11:10:48 +08:00
358 changed files with 11898 additions and 36289 deletions

View File

@@ -15,7 +15,8 @@ import (
"be.ems/src/modules/monitor"
networkdata "be.ems/src/modules/network_data"
networkelement "be.ems/src/modules/network_element"
"be.ems/src/modules/oauth2"
"be.ems/src/modules/notification"
"be.ems/src/modules/oam"
"be.ems/src/modules/system"
"be.ems/src/modules/tool"
"be.ems/src/modules/trace"
@@ -27,10 +28,6 @@ import (
// 初始应用引擎
func AppEngine() *gin.Engine {
var app *gin.Engine
// 禁止控制台日志输出的颜色
gin.DisableConsoleColor()
// 根据运行环境注册引擎
if config.Env() == "prod" {
gin.SetMode(gin.ReleaseMode)
@@ -39,6 +36,8 @@ func AppEngine() *gin.Engine {
} else {
app = gin.Default()
}
// 禁止控制台日志输出的颜色
gin.DisableConsoleColor()
app.ForwardedByClientIP = true
return app
}
@@ -84,8 +83,9 @@ func ModulesRoute(app *gin.Engine) {
system.Setup(app)
// 认证模块
auth.Setup(app)
// 开放客户端模块
oauth2.Setup(app)
// 网元OAM对接
oam.Setup(app)
oam.SetupOauth2(app)
// 通用模块
common.Setup(app)
@@ -94,6 +94,8 @@ func ModulesRoute(app *gin.Engine) {
// 网元数据模块
networkdata.Setup(app)
// 通知模块
notification.Setup(app)
// 跟踪模块
trace.Setup(app)
// 图表模块

View File

@@ -7,6 +7,29 @@ serverLoginAuth: true
# 接口加密,默认关闭
serverCryptoApi: false
# 路由服务配置
# 服务监听 ipv4/v6 端口,支持多路由
routeServer:
- addr: "0.0.0.0:33030" # 路由 http 端口
schema: "http"
- addr: "0.0.0.0:33443" # 路由 https 端口
schema: "https"
certFile: "/usr/local/etc/omc/certs/www.omc.net.crt"
keyFile: "/usr/local/etc/omc/certs/www.omc.net.key"
# Web 服务配置
# 如果使用 nginx 接管,可以禁用
webServer:
enabled: true # Web 服务启用,默认 false
rootDir: /usr/local/etc/omc/web
listen:
- addr: "0.0.0.0:80" # Web http 端口
schema: "http"
- addr: "0.0.0.0:443" # Web https 端口
schema: "https"
certFile: "/usr/local/etc/omc/certs/www.omc.net.crt"
keyFile: "/usr/local/etc/omc/certs/www.omc.net.key"
# 日志
logger:
fileDir: "/var/log"
@@ -158,7 +181,7 @@ database:
# 内置轻量级数据库
lite:
type: "sqlite"
database: "<database path>"
database: "<database path>"
logging: false
# 多个数据源时可以用这个指定默认的数据源
defaultDataSourceName: "std"

View File

@@ -1,49 +1,47 @@
# 日志
# log
logger:
fileDir: "/var/log"
fileName: "omc.log"
level: 0 # 日志记录的等级 0:silent<1:info<2:warn<3:error
maxDay: 7 # 日志会保留 180 天
maxSize: 10 # 调整按 10MB 大小的切割
fileDir: "/var/log" # Log File Dir
fileName: "omc.log" # Log File Name
level: 0 # Log Level of 0:silent<1:info<2:warn<3:error
maxDay: 7 # Log retention Days
maxSize: 10 # Log File cutting by size
# 静态文件配置, 相对项目根路径或填绝对路径
# static file resource access
staticFile:
# 默认资源dir目录需要预先创建
default:
prefix: "/static"
dir: "/usr/local/omc/static"
# 文件上传资源目录映射,与项目目录同级
prefix: "/static" # Static File Prefix
dir: "/usr/local/omc/static" # Static File Dir
upload:
prefix: "/upload"
dir: "/usr/local/omc/upload"
prefix: "/upload" # Upload File Prefix
dir: "/usr/local/omc/upload" # Upload File Dir
# DB 数据源
# data source
database:
dataSource:
# 默认数据库实例
# std: standard
std:
type: "mysql"
host: "127.0.0.1"
port: 33066
username: "root"
password: "1000omc@kp!"
database: "omc_db"
host: "127.0.0.1" # mysql host
port: 33066 # mysql port
username: "root" # mysql username
password: "1000omc@kp!" # mysql password
database: "omc_db" # mysql database
logging: true
# 内置轻量级数据库
# lite: lite
lite:
type: "sqlite"
database: "/usr/local/etc/omc/database/omc_db.sqlite"
database: "/usr/local/etc/omc/database/omc_db.sqlite" # sqlite database
logging: true
# 多个数据源时可以用这个指定默认的数据源
# default data source name
defaultDataSourceName: "std"
# Redis 缓存数据
# redis cached data
redis:
dataSource:
default:
port: 6379 # Redis port
host: "127.0.0.1" # Redis host
password: "helloearth"
port: 6379 # redis port
host: "127.0.0.1" # redis host
password: "helloearth" # redis password
db: 10 # Redis db_num
# 多个数据源时可以用这个指定默认的数据源
# default data source name
defaultDataSourceName: "default"

View File

@@ -1,30 +1,28 @@
# DB 数据源
# data source
database:
dataSource:
# 默认数据库实例
# std: standard
std:
type: "mysql"
host: "127.0.0.1"
port: 33066
username: "root"
password: "1000omc@kp!"
database: "omc_db"
logging: false
# 内置轻量级数据库
host: "127.0.0.1" # mysql host
port: 33066 # mysql port
username: "root" # mysql username
password: "1000omc@kp!" # mysql password
database: "omc_db" # mysql database
# lite: lite
lite:
type: "sqlite"
database: "/usr/local/etc/omc/database/omc_db.sqlite"
logging: false
# 多个数据源时可以用这个指定默认的数据源
database: "/usr/local/etc/omc/database/omc_db.sqlite" # sqlite database
# default data source name
defaultDataSourceName: "std"
# Redis 缓存数据
# redis cached data
redis:
dataSource:
default:
port: 6379 # Redis port
host: "127.0.0.1" # Redis host
password: "helloearth"
port: 6379 # redis port
host: "127.0.0.1" # redis host
password: "helloearth" # redis password
db: 10 # Redis db_num
# 多个数据源时可以用这个指定默认的数据源
# default data source name
defaultDataSourceName: "default"

View File

@@ -10,8 +10,6 @@ import (
"github.com/spf13/pflag"
"github.com/spf13/viper"
libConfig "be.ems/lib/config"
)
var (
@@ -21,11 +19,10 @@ var (
)
// 程序配置
var conf *viper.Viper
var conf *viper.Viper = viper.New()
// 初始化程序配置
func InitConfig(configDir *embed.FS) {
conf = viper.New()
initFlag()
initViper(configDir)
}
@@ -97,16 +94,14 @@ func initViper(configDir *embed.FS) {
// 外部文件配置
externalConfig := conf.GetString("config")
if externalConfig != "" {
// readExternalConfig(externalConfig)
// 处理旧配置,存在相同的配置项处理
configInMerge(externalConfig)
readExternalConfig(externalConfig)
}
// 记录程序开始运行的时间点
conf.Set("runTime", time.Now())
}
// readExternalConfig 读取外部文件配置(放弃旧的配置序列化时候才用)
// readExternalConfig 读取外部文件配置
func readExternalConfig(configPaht string) {
f, err := os.Open(configPaht)
if err != nil {
@@ -121,38 +116,6 @@ func readExternalConfig(configPaht string) {
}
}
// 配置文件读取进行内部参数合并
func configInMerge(configFile string) {
// 指定配置文件读取序列化
libConfig.ReadConfig(configFile)
uriPrefix := libConfig.GetYamlConfig().OMC.UriPrefix
if uriPrefix != "" {
libConfig.UriPrefix = uriPrefix
}
if libConfig.GetYamlConfig().TestConfig.Enabled {
libConfig.ReadTestConfigYaml(libConfig.GetYamlConfig().TestConfig.File)
}
// 配置文件读取
var v = viper.New()
// 设置配置文件路径
v.SetConfigFile(configFile)
v.SetConfigType("yaml")
// 读取配置文件
if err := v.ReadInConfig(); err != nil {
fmt.Printf("failure to read configuration file: %v \n", err)
return
}
// 合并外层lib和features使用配置
for key, value := range v.AllSettings() {
// 跳过配置
if key == "testconfig" || key == "logger" {
continue
}
conf.Set(key, value)
}
}
// Env 获取运行服务环境
// local prod
func Env() string {

View File

@@ -0,0 +1,30 @@
package config
import (
"fmt"
"os"
"regexp"
)
// SedReplace 替换文件内容文件来自外部文件配置config传入
//
// sed 's/port: [0-9]\+ # trace port/port: 6964 # trace port/' /usr/local/etc/omc/omc.yaml
func SedReplace(pattern, replacement string) error {
// 外部文件配置
externalConfig := conf.GetString("config")
if externalConfig == "" {
return fmt.Errorf("config file path not found")
}
// 读取文件内容
data, err := os.ReadFile(externalConfig)
if err != nil {
return err
}
// 定义正则表达式
re := regexp.MustCompile(pattern)
// 使用正则替换,将匹配到的部分替换为新的内容
replacedData := re.ReplaceAll(data, []byte(replacement))
// 写回文件
return os.WriteFile(externalConfig, replacedData, 0644)
}

View File

@@ -1,13 +1,19 @@
package constants
// 告警 alarmCode 常量
const (
// ALARM_EVENT_REBOOT 事件-网元重启
ALARM_EVENT_REBOOT = 9000
// ALARM_STATE_CHECK 告警-状态检查
ALARM_STATE_CHECK = 10000
// ALARM_RAM_CPU_CHECK 告警-内存/CPU/磁盘检查
ALARM_CMD_CHECK = 10001
// ALARM_LICENSE_CHECK 告警-网元License到期检查
ALARM_LICENSE_CHECK = 10002
ALARM_EVENT_REBOOT = 9000 // 告警Code-事件-网元重启
ALARM_STATE_CHECK = 10000 // 告警Code-状态检查
ALARM_CMD_CHECK = 10001 // 告警Code-内存/CPU/磁盘检查
ALARM_LICENSE_CHECK = 10002 // 告警Code-网元License到期检查
)
const (
ALARM_ACK_STATE_NOT_ACK = "NotAck" // 告警确认状态-未确认
ALARM_ACK_STATE_ACK = "Ack" // 告警确认状态-已确认
)
const (
ALARM_CLEAR_TYPE_NOT_CLEAR = "NotClear" // 告警清除状态-未清除
ALARM_CLEAR_TYPE_AUTO_CLEAR = "AutoClear" // 告警清除状态-自动清除
ALARM_CLEAR_TYPE_MANUAL_CLEAR = "ManualClear" // 告警清除状态-手动清除
)

View File

@@ -103,6 +103,10 @@ func processSQLFile(db *gorm.DB, filePath string) {
} else if strings.Contains(errorStr, "duplicate key") {
// 忽略重复索引错误
// Error 1061 (42000): Duplicate key name 'key_name'
} else if strings.Contains(errorStr, "duplicate entry") {
// 忽略重复记录错误
// Error 1062 (23000): Duplicate entry 'value' for key 'key_name'
log.Println(err.Error())
} else if strings.Contains(errorStr, "unknown column") {
// 忽略未知字段错误
// Error 1054 (42S22): Unknown column 'field_name' in 'table'
@@ -110,6 +114,10 @@ func processSQLFile(db *gorm.DB, filePath string) {
// 忽略删除字段或索引错误
// Error 1091 (42000): Can't DROP COLUMN `field_name`; check that it exists
// Error 1091 (42000): Can't DROP 'idx_ne_type_id'; check that column/key exists
} else if strings.Contains(errorStr, "doesn't match") {
// 忽略列数不匹配错误
// Error 1136 (21S01): Column count doesn't match value count at row 1
log.Println(err.Error())
} else {
// 其他错误终止程序
log.Fatalln(errorStr)

View File

@@ -2,7 +2,6 @@ package middleware
import (
"encoding/json"
"fmt"
"reflect"
"strings"
"time"
@@ -134,8 +133,15 @@ func OperateLog(options Options) gin.HandlerFunc {
contentDisposition := c.Writer.Header().Get("Content-Disposition")
contentType := c.Writer.Header().Get("Content-Type")
content := contentType + contentDisposition
msg := fmt.Sprintf(`{"status":"%d","size":%d,"content-type":"%s"}`, status, c.Writer.Size(), content)
operaLog.OperaMsg = msg
msgByte, err := json.Marshal(map[string]any{
"status": status,
"size": c.Writer.Size(),
"content-type": content,
})
if err != nil {
operaLog.OperaMsg = ""
}
operaLog.OperaMsg = string(msgByte)
}
// 日志记录时间

View File

@@ -1,96 +0,0 @@
package socket
import (
"bytes"
"fmt"
"net"
"strings"
"time"
)
// ConnTCP 连接TCP客户端
type ConnTCP struct {
Addr string `json:"addr"` // 主机地址
Port int64 `json:"port"` // 端口
DialTimeOut time.Duration `json:"dialTimeOut"` // 连接超时断开
Client *net.Conn `json:"client"`
LastResult string `json:"lastResult"` // 记最后一次发送消息的结果
}
// New 创建TCP客户端
func (c *ConnTCP) New() (*ConnTCP, error) {
// IPV6地址协议
proto := "tcp"
if strings.Contains(c.Addr, ":") {
proto = "tcp6"
c.Addr = fmt.Sprintf("[%s]", c.Addr)
}
address := net.JoinHostPort(c.Addr, fmt.Sprint(c.Port))
// 默认等待5s
if c.DialTimeOut == 0 {
c.DialTimeOut = 5 * time.Second
}
// 连接到服务端
client, err := net.DialTimeout(proto, address, c.DialTimeOut)
if err != nil {
return nil, err
}
c.Client = &client
return c, nil
}
// Close 关闭当前TCP客户端
func (c *ConnTCP) Close() {
if c.Client != nil {
(*c.Client).Close()
}
}
// Send 发送消息
func (c *ConnTCP) Send(msg []byte, timer time.Duration) (string, error) {
if c.Client == nil {
return "", fmt.Errorf("tcp client not connected")
}
conn := *c.Client
// 写入信息
if len(msg) > 0 {
if _, err := conn.Write(msg); err != nil {
return "", err
}
}
var buf bytes.Buffer
defer buf.Reset()
tmp := make([]byte, 1024)
for {
select {
case <-time.After(timer):
c.LastResult = buf.String()
return c.LastResult, fmt.Errorf("timeout")
default:
// 读取命令消息
n, err := conn.Read(tmp)
if n == 0 || err != nil {
tmp = nil
break
}
tmpStr := string(tmp[:n])
buf.WriteString(tmpStr)
// 是否有终止符
if strings.HasSuffix(tmpStr, ">") || strings.HasSuffix(tmpStr, "> ") || strings.HasSuffix(tmpStr, "# ") {
tmp = nil
c.LastResult = buf.String()
return c.LastResult, nil
}
}
}
}

View File

@@ -1,83 +0,0 @@
package socket
import (
"fmt"
"net"
"strings"
"be.ems/src/framework/logger"
)
// SocketTCP TCP服务端
type SocketTCP struct {
Addr string `json:"addr"` // 主机地址
Port int64 `json:"port"` // 端口
Listener *net.TCPListener `json:"listener"`
StopChan chan struct{} `json:"stop"` // 停止信号
}
// New 创建TCP服务端
func (s *SocketTCP) New() (*SocketTCP, error) {
// IPV6地址协议
proto := "tcp"
if strings.Contains(s.Addr, ":") {
proto = "tcp6"
s.Addr = fmt.Sprintf("[%s]", s.Addr)
}
address := fmt.Sprintf("%s:%d", s.Addr, s.Port)
// 解析 TCP 地址
tcpAddr, err := net.ResolveTCPAddr(proto, address)
if err != nil {
return nil, err
}
// 监听 TCP 地址
listener, err := net.ListenTCP(proto, tcpAddr)
if err != nil {
return nil, err
}
s.Listener = listener
s.StopChan = make(chan struct{}, 1)
return s, nil
}
// Close 关闭当前TCP服务端
func (s *SocketTCP) Close() {
if s.Listener != nil {
s.StopChan <- struct{}{}
(*s.Listener).Close()
}
}
// Resolve 处理消息
func (s *SocketTCP) Resolve(callback func(conn *net.Conn, err error)) {
if s.Listener == nil {
callback(nil, fmt.Errorf("tcp service not created"))
return
}
defer func() {
if err := recover(); err != nil {
callback(nil, fmt.Errorf("tcp service panic err"))
}
}()
listener := *s.Listener
for {
select {
case <-s.StopChan:
callback(nil, fmt.Errorf("udp service stop"))
return
default:
conn, err := listener.Accept()
if err != nil {
logger.Errorf("Error accepting connection: %v ", err)
continue
}
defer conn.Close()
callback(&conn, nil)
}
}
}

View File

@@ -1,96 +0,0 @@
package socket
import (
"bytes"
"fmt"
"net"
"strings"
"time"
)
// ConnUDP 连接UDP客户端
type ConnUDP struct {
Addr string `json:"addr"` // 主机地址
Port int64 `json:"port"` // 端口
DialTimeOut time.Duration `json:"dialTimeOut"` // 连接超时断开
Client *net.Conn `json:"client"`
LastResult string `json:"lastResult"` // 记最后一次发送消息的结果
}
// New 创建UDP客户端
func (c *ConnUDP) New() (*ConnUDP, error) {
// IPV6地址协议
proto := "udp"
if strings.Contains(c.Addr, ":") {
proto = "udp6"
c.Addr = fmt.Sprintf("[%s]", c.Addr)
}
address := net.JoinHostPort(c.Addr, fmt.Sprint(c.Port))
// 默认等待5s
if c.DialTimeOut == 0 {
c.DialTimeOut = 5 * time.Second
}
// 连接到服务端
client, err := net.DialTimeout(proto, address, c.DialTimeOut)
if err != nil {
return nil, err
}
c.Client = &client
return c, nil
}
// Close 关闭当前UDP客户端
func (c *ConnUDP) Close() {
if c.Client != nil {
(*c.Client).Close()
}
}
// Send 发送消息
func (c *ConnUDP) Send(msg []byte, ms int) (string, error) {
if c.Client == nil {
return "", fmt.Errorf("udp client not connected")
}
conn := *c.Client
// 写入信息
if len(msg) > 0 {
if _, err := conn.Write(msg); err != nil {
return "", err
}
}
var buf bytes.Buffer
defer buf.Reset()
tmp := make([]byte, 1024)
for {
select {
case <-time.After(time.Duration(time.Duration(ms).Milliseconds())):
c.LastResult = buf.String()
return c.LastResult, fmt.Errorf("timeout")
default:
// 读取命令消息
n, err := conn.Read(tmp)
if n == 0 || err != nil {
tmp = nil
break
}
tmpStr := string(tmp[:n])
buf.WriteString(tmpStr)
// 是否有终止符
if strings.HasSuffix(tmpStr, ">") || strings.HasSuffix(tmpStr, "> ") || strings.HasSuffix(tmpStr, "# ") {
tmp = nil
c.LastResult = buf.String()
return c.LastResult, nil
}
}
}
}

View File

@@ -1,74 +0,0 @@
package socket
import (
"fmt"
"net"
"strings"
)
// SocketUDP UDP服务端
type SocketUDP struct {
Addr string `json:"addr"` // 主机地址
Port int64 `json:"port"` // 端口
Conn *net.UDPConn `json:"conn"`
StopChan chan struct{} `json:"stop"` // 停止信号
}
// New 创建UDP服务端
func (s *SocketUDP) New() (*SocketUDP, error) {
// IPV6地址协议
proto := "udp"
if strings.Contains(s.Addr, ":") {
proto = "udp6"
s.Addr = fmt.Sprintf("[%s]", s.Addr)
}
address := fmt.Sprintf("%s:%d", s.Addr, s.Port)
// 解析 UDP 地址
udpAddr, err := net.ResolveUDPAddr(proto, address)
if err != nil {
return nil, err
}
// 监听 UDP 地址
conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
return nil, err
}
s.Conn = conn
s.StopChan = make(chan struct{}, 1)
return s, nil
}
// CloseService 关闭当前UDP服务端
func (s *SocketUDP) Close() {
if s.Conn != nil {
s.StopChan <- struct{}{}
(*s.Conn).Close()
}
}
// Resolve 处理消息
func (s *SocketUDP) Resolve(callback func(*net.UDPConn, error)) {
if s.Conn == nil {
callback(nil, fmt.Errorf("udp service not created"))
return
}
defer func() {
if err := recover(); err != nil {
callback(nil, fmt.Errorf("udp service panic err"))
}
}()
for {
select {
case <-s.StopChan:
callback(nil, fmt.Errorf("udp service not created"))
return
default:
callback(s.Conn, nil)
}
}
}

View File

@@ -42,10 +42,14 @@ func (c *ConnTelnet) NewClient() (*ConnTelnet, error) {
}
// 进行登录
time.Sleep(100 * time.Millisecond)
client.Write([]byte(c.User + "\r\n"))
time.Sleep(100 * time.Millisecond)
client.Write([]byte(c.Password + "\r\n"))
if c.User != "" {
time.Sleep(100 * time.Millisecond)
client.Write([]byte(c.User + "\r\n"))
}
if c.Password != "" {
time.Sleep(100 * time.Millisecond)
client.Write([]byte(c.Password + "\r\n"))
}
// fmt.Fprintln(client, c.User)
// fmt.Fprintln(client, c.Password)
@@ -103,6 +107,19 @@ func (c *ConnTelnet) RunCMD(cmd string) (string, error) {
return c.LastResult, nil
}
// WindowChange informs the remote host about a terminal window dimension change to h rows and w columns.
func (s *ConnTelnet) WindowChange(h, w int) error {
if s.Client == nil {
return fmt.Errorf("client is nil to content write failed")
}
conn := *s.Client
// 需要确保接收方理解并正确处理发送窗口大小设置命令
conn.Write([]byte{255, 251, 31})
conn.Write([]byte{255, 250, 31, byte(w >> 8), byte(w & 0xFF), byte(h >> 8), byte(h & 0xFF), 255, 240})
return nil
}
// NewClient 创建Telnet客户端会话对象
func (c *ConnTelnet) NewClientSession(cols, rows int) (*TelnetClientSession, error) {
if c.Client == nil {

View File

@@ -54,9 +54,9 @@ func ParseDateToStr(date any, formatStr string) string {
if v == 0 {
return ""
}
if v > 9999999999 {
if v > 1e12 {
t = time.UnixMilli(v)
} else if v > 999999999 {
} else if v > 1e9 {
t = time.Unix(v, 0)
} else {
logger.Infof("utils ParseDateToStr err %v", "Invalid timestamp")

View File

@@ -0,0 +1,44 @@
package expr
import (
"fmt"
"regexp"
"strings"
"github.com/expr-lang/expr"
)
// Eval 计算表达式返回结果
func Eval(exprStr string, env map[string]any) (any, error) {
return expr.Eval(exprStr, env)
}
// ParseExprEnv 解析表达式环境变量
// 比如 "('SMF.03'/'SMF.04')*100"
// 变量传入"SMF.03": 3
func ParseExprEnv(exprStr string, env map[string]any) (string, map[string]any) {
// 使用正则表达式匹配带单引号的变量名
re := regexp.MustCompile(`'([^']+)'`)
tempEnv := make(map[string]any)
tempExpr := exprStr
varCount := 0
matches := re.FindAllStringSubmatch(exprStr, -1)
for _, match := range matches {
paramName := match[1]
tempVarName := fmt.Sprintf("var%d", varCount)
tempEnv[tempVarName] = env[paramName]
tempExpr = strings.Replace(tempExpr, match[0], tempVarName, 1)
varCount++
}
// 合并临时环境变量和原环境变量
combinedEnv := make(map[string]any)
for k, v := range env {
combinedEnv[k] = v
}
for k, v := range tempEnv {
combinedEnv[k] = v
}
return tempExpr, combinedEnv
}

View File

@@ -19,7 +19,7 @@ func ValidUsername(username string) bool {
if username == "" {
return false
}
pattern := `^[a-zA-Z][a-z0-9A-Z]{5,}`
pattern := `^.{4,}$` //`^[a-zA-Z][a-z0-9A-Z]{5,}`
match, err := regexp.MatchString(pattern, username)
if err != nil {
return false

View File

@@ -23,38 +23,84 @@ func Setup(router *gin.Engine) {
)
// 账号身份操作
account := controller.NewAccount
accountGroup := router.Group("/auth")
{
router.POST("/auth/login",
accountGroup.POST("/login",
middleware.RateLimit(middleware.LimitOption{
Time: 180,
Count: 15,
Type: middleware.LIMIT_IP,
}),
controller.NewAccount.Login,
account.Login,
)
router.POST("/auth/logout",
accountGroup.POST("/logout",
middleware.RateLimit(middleware.LimitOption{
Time: 120,
Count: 15,
Type: middleware.LIMIT_IP,
}),
controller.NewAccount.Logout,
account.Logout,
)
router.POST("/auth/refresh-token",
accountGroup.POST("/refresh-token",
middleware.RateLimit(middleware.LimitOption{
Time: 60,
Count: 5,
Type: middleware.LIMIT_IP,
}),
controller.NewAccount.RefreshToken,
account.RefreshToken,
)
router.GET("/me",
middleware.AuthorizeUser(nil),
controller.NewAccount.Me,
account.Me,
)
router.GET("/router",
middleware.AuthorizeUser(nil),
controller.NewAccount.Router,
account.Router,
)
}
// 登录认证源
{
accountGroup.GET("/login/source",
middleware.RateLimit(middleware.LimitOption{
Time: 300,
Count: 60,
Type: middleware.LIMIT_IP,
}),
account.LoginSource,
)
accountGroup.POST("/login/ldap",
middleware.RateLimit(middleware.LimitOption{
Time: 180,
Count: 15,
Type: middleware.LIMIT_IP,
}),
account.LDAP,
)
accountGroup.POST("/login/smtp",
middleware.RateLimit(middleware.LimitOption{
Time: 180,
Count: 15,
Type: middleware.LIMIT_IP,
}),
account.SMTP,
)
accountGroup.GET("/login/oauth2/authorize",
middleware.RateLimit(middleware.LimitOption{
Time: 180,
Count: 15,
Type: middleware.LIMIT_IP,
}),
account.OAuth2CodeURL,
)
accountGroup.POST("/login/oauth2/token",
middleware.RateLimit(middleware.LimitOption{
Time: 180,
Count: 15,
Type: middleware.LIMIT_IP,
}),
account.OAuth2Token,
)
}
@@ -70,4 +116,62 @@ func Setup(router *gin.Engine) {
)
}
// 客户端授权管理
oauth2Client := controller.NewOauth2Client
oauth2ClientGroup := router.Group("/oauth2/client")
{
oauth2ClientGroup.GET("/list",
middleware.AuthorizeUser(map[string][]string{"matchRoles": {"admin"}}),
oauth2Client.List,
)
oauth2ClientGroup.GET("/:clientId",
middleware.AuthorizeUser(map[string][]string{"matchRoles": {"admin"}}),
oauth2Client.Info,
)
oauth2ClientGroup.POST("",
middleware.AuthorizeUser(map[string][]string{"matchRoles": {"admin"}}),
middleware.OperateLog(middleware.OptionNew("log.operate.title.oauth2client", middleware.BUSINESS_TYPE_INSERT)),
oauth2Client.Add,
)
oauth2ClientGroup.PUT("",
middleware.AuthorizeUser(map[string][]string{"matchRoles": {"admin"}}),
middleware.OperateLog(middleware.OptionNew("log.operate.title.oauth2client", middleware.BUSINESS_TYPE_UPDATE)),
oauth2Client.Edit,
)
oauth2ClientGroup.DELETE("/:id",
middleware.AuthorizeUser(map[string][]string{"matchRoles": {"admin"}}),
middleware.OperateLog(middleware.OptionNew("log.operate.title.oauth2client", middleware.BUSINESS_TYPE_DELETE)),
oauth2Client.Remove,
)
}
// 授权认证
oauth2 := controller.NewOauth2
oauth2Group := router.Group("/oauth2")
{
oauth2Group.GET("/authorize",
middleware.RateLimit(middleware.LimitOption{
Time: 60,
Count: 30,
Type: middleware.LIMIT_IP,
}),
oauth2.Authorize,
)
oauth2Group.POST("/token",
middleware.RateLimit(middleware.LimitOption{
Time: 180,
Count: 15,
Type: middleware.LIMIT_IP,
}),
oauth2.Token,
)
oauth2Group.POST("/refresh-token",
middleware.RateLimit(middleware.LimitOption{
Time: 60,
Count: 5,
Type: middleware.LIMIT_IP,
}),
oauth2.RefreshToken,
)
}
}

View File

@@ -4,16 +4,13 @@ import (
"fmt"
"time"
"be.ems/src/framework/config"
"be.ems/src/framework/constants"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/token"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/auth/model"
"be.ems/src/modules/auth/service"
systemModelVO "be.ems/src/modules/system/model/vo"
systemService "be.ems/src/modules/system/service"
"github.com/gin-gonic/gin"
@@ -33,95 +30,6 @@ type AccountController struct {
sysLogLoginService *systemService.SysLogLogin // 系统登录访问
}
// Login 系统登录
//
// POST /auth/login
//
// @Tags common/authorization
// @Accept json
// @Produce json
// @Param data body object true "Request Param"
// @Success 200 {object} object "Response Results"
// @Summary System Login
// @Description System Login
// @Router /auth/login [post]
func (s AccountController) Login(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body model.LoginBody
if err := c.ShouldBindJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
// 当前请求信息
ipaddr, location := reqctx.IPAddrLocation(c)
os, browser := reqctx.UaOsBrowser(c)
// 校验验证码 根据错误信息,创建系统访问记录
if err := s.accountService.ValidateCaptcha(body.Code, body.UUID); err != nil {
msg := fmt.Sprintf("%s code %s", err.Error(), body.Code)
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_NO, msg,
[4]string{ipaddr, location, os, browser},
)
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
// 登录用户信息
info, err := s.accountService.ByUsername(body.Username, body.Password)
if err != nil {
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_NO, err.Error(),
[4]string{ipaddr, location, os, browser},
)
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
data := map[string]any{}
if !config.IsSystemUser(info.UserId) {
// 强制改密码
forcePasswdChange, err := s.accountService.PasswordCountOrExpireTime(info.User.LoginCount, info.User.PasswordUpdateTime)
if err != nil {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
if forcePasswdChange {
data["forcePasswdChange"] = true
}
}
deviceFingerprint := reqctx.DeviceFingerprint(c, info.UserId)
// 生成访问令牌
accessToken, expiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "access")
if accessToken == "" || expiresIn == 0 {
c.JSON(200, resp.ErrMsg("token generation failed"))
return
}
// 生成刷新令牌
refreshToken, refreshExpiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "refresh")
// 记录令牌,创建系统访问记录
token.UserInfoCreate(&info, deviceFingerprint, [4]string{ipaddr, location, os, browser})
s.accountService.UpdateLoginDateAndIP(info)
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_YES, "app.common.loginSuccess",
[4]string{ipaddr, location, os, browser},
)
data["tokenType"] = constants.HEADER_PREFIX
data["accessToken"] = accessToken
data["expiresIn"] = expiresIn
data["refreshToken"] = refreshToken
data["refreshExpiresIn"] = refreshExpiresIn
data["userId"] = info.UserId
c.JSON(200, resp.OkData(data))
}
// Logout 系统登出
//
// POST /auth/logout
@@ -222,82 +130,10 @@ func (s AccountController) RefreshToken(c *gin.Context) {
}))
}
// Me 登录用户信息
// LoginSource 登录认证源
//
// GET /me
//
// @Tags common/authorization
// @Accept json
// @Produce json
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Login User Information
// @Description Login User Information
// @Router /me [get]
func (s AccountController) Me(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
info, err := reqctx.LoginUser(c)
if err != nil {
c.JSON(401, resp.CodeMsg(resp.CODE_AUTH_INVALID, err.Error()))
return
}
// 角色权限集合,系统管理员拥有所有权限
isSystemUser := config.IsSystemUser(info.UserId)
roles, perms := s.accountService.RoleAndMenuPerms(info.UserId, isSystemUser)
info.User.NickName = i18n.TKey(language, info.User.NickName)
info.User.Remark = i18n.TKey(language, info.User.Remark)
info.User.Dept.DeptName = i18n.TKey(language, info.User.Dept.DeptName)
for ri := range info.User.Roles {
info.User.Roles[ri].RoleName = i18n.TKey(language, info.User.Roles[ri].RoleName)
}
data := map[string]any{
"user": info.User,
"roles": roles,
"permissions": perms,
}
if !isSystemUser {
// 强制改密码
forcePasswdChange, _ := s.accountService.PasswordCountOrExpireTime(info.User.LoginCount, info.User.PasswordUpdateTime)
if forcePasswdChange {
data["forcePasswdChange"] = true
}
}
// GET /auth/login/source
func (s AccountController) LoginSource(c *gin.Context) {
data := s.accountService.LoginSource()
c.JSON(200, resp.OkData(data))
}
// Router 登录用户路由信息
//
// GET /router
//
// @Tags common/authorization
// @Accept json
// @Produce json
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Login User Routing Information
// @Description Login User Routing Information
// @Router /router [get]
func (s AccountController) Router(c *gin.Context) {
loginUserId := reqctx.LoginUserToUserID(c)
// 前端路由,系统管理员拥有所有
isSystemUser := config.IsSystemUser(loginUserId)
buildMenus := s.accountService.RouteMenus(loginUserId, isSystemUser)
// 闭包函数处理多语言
language := reqctx.AcceptLanguage(c)
var converI18n func(language string, arr *[]systemModelVO.Router)
converI18n = func(language string, arr *[]systemModelVO.Router) {
for i := range *arr {
(*arr)[i].Meta.Title = i18n.TKey(language, (*arr)[i].Meta.Title)
if len((*arr)[i].Children) > 0 {
converI18n(language, &(*arr)[i].Children)
}
}
}
converI18n(language, &buildMenus)
c.JSON(200, resp.OkData(buildMenus))
}

View File

@@ -0,0 +1,93 @@
package controller
import (
"be.ems/src/framework/config"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
systemModelVO "be.ems/src/modules/system/model/vo"
"github.com/gin-gonic/gin"
)
// Me 登录用户信息
//
// GET /me
//
// @Tags common/authorization
// @Accept json
// @Produce json
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Login User Information
// @Description Login User Information
// @Router /me [get]
func (s AccountController) Me(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
info, err := reqctx.LoginUser(c)
if err != nil {
c.JSON(401, resp.CodeMsg(resp.CODE_AUTH_INVALID, err.Error()))
return
}
// 角色权限集合,系统管理员拥有所有权限
isSystemUser := config.IsSystemUser(info.UserId)
roles, perms := s.accountService.RoleAndMenuPerms(info.UserId, isSystemUser)
info.User.NickName = i18n.TKey(language, info.User.NickName)
info.User.Remark = i18n.TKey(language, info.User.Remark)
if info.User.Dept != nil {
info.User.Dept.DeptName = i18n.TKey(language, info.User.Dept.DeptName)
}
for ri := range info.User.Roles {
info.User.Roles[ri].RoleName = i18n.TKey(language, info.User.Roles[ri].RoleName)
}
data := map[string]any{
"user": info.User,
"roles": roles,
"permissions": perms,
}
if !isSystemUser {
// 强制改密码
forcePasswdChange, _ := s.accountService.PasswordCountOrExpireTime(info.User.LoginCount, info.User.PasswordUpdateTime)
if forcePasswdChange {
data["forcePasswdChange"] = true
}
}
c.JSON(200, resp.OkData(data))
}
// Router 登录用户路由信息
//
// GET /router
//
// @Tags common/authorization
// @Accept json
// @Produce json
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Login User Routing Information
// @Description Login User Routing Information
// @Router /router [get]
func (s AccountController) Router(c *gin.Context) {
loginUserId := reqctx.LoginUserToUserID(c)
// 前端路由,系统管理员拥有所有
isSystemUser := config.IsSystemUser(loginUserId)
buildMenus := s.accountService.RouteMenus(loginUserId, isSystemUser)
// 闭包函数处理多语言
language := reqctx.AcceptLanguage(c)
var converI18n func(language string, arr *[]systemModelVO.Router)
converI18n = func(language string, arr *[]systemModelVO.Router) {
for i := range *arr {
(*arr)[i].Meta.Title = i18n.TKey(language, (*arr)[i].Meta.Title)
if len((*arr)[i].Children) > 0 {
converI18n(language, &(*arr)[i].Children)
}
}
}
converI18n(language, &buildMenus)
c.JSON(200, resp.OkData(buildMenus))
}

View File

@@ -0,0 +1,80 @@
package controller
import (
"fmt"
"be.ems/src/framework/constants"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/token"
"be.ems/src/modules/auth/model"
"github.com/gin-gonic/gin"
)
// LDAP LDAP认证登录
//
// POST /auth/ldap
//
// @Tags common/authorization
// @Accept json
// @Produce json
// @Param data body object true "Request Param"
// @Success 200 {object} object "Response Results"
// @Summary System Login
// @Description System Login
// @Router /auth/ldap [post]
func (s AccountController) LDAP(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body model.LoginSourceBody
if err := c.ShouldBindJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
// 当前请求信息
ipaddr, location := reqctx.IPAddrLocation(c)
os, browser := reqctx.UaOsBrowser(c)
// 登录用户信息
info, err := s.accountService.ByLDAP(body)
if err != nil {
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_NO, err.Error(),
[4]string{ipaddr, location, os, browser},
)
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
data := map[string]any{}
deviceFingerprint := reqctx.DeviceFingerprint(c, info.UserId)
// 生成访问令牌
accessToken, expiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "access")
if accessToken == "" || expiresIn == 0 {
c.JSON(200, resp.ErrMsg("token generation failed"))
return
}
// 生成刷新令牌
refreshToken, refreshExpiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "refresh")
// 记录令牌,创建系统访问记录
token.UserInfoCreate(&info, deviceFingerprint, [4]string{ipaddr, location, os, browser})
s.accountService.UpdateLoginDateAndIP(info)
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_YES, "app.common.loginSuccess",
[4]string{ipaddr, location, os, browser},
)
data["tokenType"] = constants.HEADER_PREFIX
data["accessToken"] = accessToken
data["expiresIn"] = expiresIn
data["refreshToken"] = refreshToken
data["refreshExpiresIn"] = refreshExpiresIn
data["userId"] = info.UserId
c.JSON(200, resp.OkData(data))
}

View File

@@ -0,0 +1,107 @@
package controller
import (
"fmt"
"be.ems/src/framework/constants"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/token"
"be.ems/src/modules/auth/model"
"github.com/gin-gonic/gin"
)
// OAuth2CodeURL OAuth2认证跳转登录URL
//
// GET /auth/login/oauth2/authorize
//
// @Tags common/authorization
// @Accept json
// @Produce json
// @Param data body object true "Request Param"
// @Success 200 {object} object "Response Results"
// @Summary System Login
// @Description System Login
// @Router /auth/login/oauth2/authorize [get]
func (s AccountController) OAuth2CodeURL(c *gin.Context) {
state := c.Query("state")
if state == "" {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: state is empty"))
return
}
redirectURL, err := s.accountService.ByOAuth2CodeURL(state)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.Redirect(302, redirectURL)
}
// OAuth2 OAuth2认证登录
//
// POST /auth/login/oauth2/token
//
// @Tags common/authorization
// @Accept json
// @Produce json
// @Param data body object true "Request Param"
// @Success 200 {object} object "Response Results"
// @Summary System Login
// @Description System Login
// @Router /auth/login/oauth2/token [post]
func (s AccountController) OAuth2Token(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body model.LoginSourceOauth2Body
if err := c.ShouldBindJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
// 当前请求信息
ipaddr, location := reqctx.IPAddrLocation(c)
os, browser := reqctx.UaOsBrowser(c)
// 登录用户信息
info, err := s.accountService.ByOAuth2(body)
if err != nil {
s.sysLogLoginService.Insert(
body.State, constants.STATUS_NO, err.Error(),
[4]string{ipaddr, location, os, browser},
)
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
data := map[string]any{}
deviceFingerprint := reqctx.DeviceFingerprint(c, info.UserId)
// 生成访问令牌
accessToken, expiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "access")
if accessToken == "" || expiresIn == 0 {
c.JSON(200, resp.ErrMsg("token generation failed"))
return
}
// 生成刷新令牌
refreshToken, refreshExpiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "refresh")
// 记录令牌,创建系统访问记录
token.UserInfoCreate(&info, deviceFingerprint, [4]string{ipaddr, location, os, browser})
s.accountService.UpdateLoginDateAndIP(info)
s.sysLogLoginService.Insert(
body.State, constants.STATUS_YES, "app.common.loginSuccess",
[4]string{ipaddr, location, os, browser},
)
data["tokenType"] = constants.HEADER_PREFIX
data["accessToken"] = accessToken
data["expiresIn"] = expiresIn
data["refreshToken"] = refreshToken
data["refreshExpiresIn"] = refreshExpiresIn
data["userId"] = info.UserId
c.JSON(200, resp.OkData(data))
}

View File

@@ -0,0 +1,80 @@
package controller
import (
"fmt"
"be.ems/src/framework/constants"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/token"
"be.ems/src/modules/auth/model"
"github.com/gin-gonic/gin"
)
// SMTP SMTP认证登录
//
// POST /auth/smtp
//
// @Tags common/authorization
// @Accept json
// @Produce json
// @Param data body object true "Request Param"
// @Success 200 {object} object "Response Results"
// @Summary System Login
// @Description System Login
// @Router /auth/smtp [post]
func (s AccountController) SMTP(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body model.LoginSourceBody
if err := c.ShouldBindJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
// 当前请求信息
ipaddr, location := reqctx.IPAddrLocation(c)
os, browser := reqctx.UaOsBrowser(c)
// 登录用户信息
info, err := s.accountService.BySMTP(body)
if err != nil {
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_NO, err.Error(),
[4]string{ipaddr, location, os, browser},
)
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
data := map[string]any{}
deviceFingerprint := reqctx.DeviceFingerprint(c, info.UserId)
// 生成访问令牌
accessToken, expiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "access")
if accessToken == "" || expiresIn == 0 {
c.JSON(200, resp.ErrMsg("token generation failed"))
return
}
// 生成刷新令牌
refreshToken, refreshExpiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "refresh")
// 记录令牌,创建系统访问记录
token.UserInfoCreate(&info, deviceFingerprint, [4]string{ipaddr, location, os, browser})
s.accountService.UpdateLoginDateAndIP(info)
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_YES, "app.common.loginSuccess",
[4]string{ipaddr, location, os, browser},
)
data["tokenType"] = constants.HEADER_PREFIX
data["accessToken"] = accessToken
data["expiresIn"] = expiresIn
data["refreshToken"] = refreshToken
data["refreshExpiresIn"] = refreshExpiresIn
data["userId"] = info.UserId
c.JSON(200, resp.OkData(data))
}

View File

@@ -0,0 +1,104 @@
package controller
import (
"fmt"
"be.ems/src/framework/config"
"be.ems/src/framework/constants"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/token"
"be.ems/src/modules/auth/model"
"github.com/gin-gonic/gin"
)
// Login 系统登录
//
// POST /auth/login
//
// @Tags common/authorization
// @Accept json
// @Produce json
// @Param data body object true "Request Param"
// @Success 200 {object} object "Response Results"
// @Summary System Login
// @Description System Login
// @Router /auth/login [post]
func (s AccountController) Login(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body model.LoginBody
if err := c.ShouldBindJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
// 当前请求信息
ipaddr, location := reqctx.IPAddrLocation(c)
os, browser := reqctx.UaOsBrowser(c)
// 校验验证码 根据错误信息,创建系统访问记录
if err := s.accountService.ValidateCaptcha(body.Code, body.UUID); err != nil {
msg := fmt.Sprintf("%s code %s", err.Error(), body.Code)
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_NO, msg,
[4]string{ipaddr, location, os, browser},
)
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
// 登录用户信息
info, err := s.accountService.ByUsername(body.Username, body.Password)
if err != nil {
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_NO, err.Error(),
[4]string{ipaddr, location, os, browser},
)
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
data := map[string]any{}
if !config.IsSystemUser(info.UserId) {
// 强制改密码
forcePasswdChange, err := s.accountService.PasswordCountOrExpireTime(info.User.LoginCount, info.User.PasswordUpdateTime)
if err != nil {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
if forcePasswdChange {
data["forcePasswdChange"] = true
}
}
deviceFingerprint := reqctx.DeviceFingerprint(c, info.UserId)
// 生成访问令牌
accessToken, expiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "access")
if accessToken == "" || expiresIn == 0 {
c.JSON(200, resp.ErrMsg("token generation failed"))
return
}
// 生成刷新令牌
refreshToken, refreshExpiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "refresh")
// 记录令牌,创建系统访问记录
token.UserInfoCreate(&info, deviceFingerprint, [4]string{ipaddr, location, os, browser})
s.accountService.UpdateLoginDateAndIP(info)
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_YES, "app.common.loginSuccess",
[4]string{ipaddr, location, os, browser},
)
data["tokenType"] = constants.HEADER_PREFIX
data["accessToken"] = accessToken
data["expiresIn"] = expiresIn
data["refreshToken"] = refreshToken
data["refreshExpiresIn"] = refreshExpiresIn
data["userId"] = info.UserId
c.JSON(200, resp.OkData(data))
}

View File

@@ -11,8 +11,8 @@ import (
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/token"
"be.ems/src/modules/oauth2/model"
"be.ems/src/modules/oauth2/service"
"be.ems/src/modules/auth/model"
"be.ems/src/modules/auth/service"
)
// NewOauth2 实例化控制层

View File

@@ -10,8 +10,8 @@ import (
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/oauth2/model"
"be.ems/src/modules/oauth2/service"
"be.ems/src/modules/auth/model"
"be.ems/src/modules/auth/service"
)
// NewOauth2Client 实例化控制层

View File

@@ -2,15 +2,8 @@ package model
// LoginBody 用户登录对象
type LoginBody struct {
// Username 用户名
Username string `json:"username" binding:"required"`
// Password 用户密码
Password string `json:"password" binding:"required"`
// Code 验证码
Code string `json:"code"`
// UUID 验证码唯一标识
UUID string `json:"uuid"`
Username string `json:"username" binding:"required"` // Username 用户名
Password string `json:"password" binding:"required"` // Password 用户密码
Code string `json:"code"` // Code 验证码
UUID string `json:"uuid"` // UUID 验证码唯一标识
}

View File

@@ -0,0 +1,22 @@
package model
// LoginSourceVo 认证源登录对象
type LoginSourceVo struct {
UID string `json:"uid"` // UID 认证源UID
Name string `json:"name"` // Name 认证源名称
Type string `json:"type"` // Type 认证源类型
Icon string `json:"icon"` // Icon 认证源图标
}
// LoginSourceBody 认证源用户登录对象
type LoginSourceBody struct {
Username string `json:"username" binding:"required"` // Username 用户名
Password string `json:"password" binding:"required"` // Password 用户密码
UID string `json:"uid" binding:"required"` // UID 认证源唯一标识
}
// LoginSourceOauth2Body 认证源OAuth2用户登录对象
type LoginSourceOauth2Body struct {
Code string `json:"code" binding:"required"` // Code 授权码
State string `json:"state" binding:"required"` // State 状态-认证源唯一标识
}

View File

@@ -8,3 +8,10 @@ type TokenBody struct {
Code string `json:"code"` // 授权拿到的code值
RefreshToken string `json:"refreshToken"` // 刷新令牌
}
// CodeQuery 重定向授权码参数
type CodeQuery struct {
RedirectUrl string `form:"redirectUrl" binding:"required"` // 授权回调地址
ClientId string `form:"clientId" binding:"required"` // 申请得到的客户端ID
State string `form:"state" binding:"required"` // 随机字符串,认证服务器会原封不动地返回这个值
}

View File

@@ -2,21 +2,10 @@ package model
// RegisterBody 用户注册对象
type RegisterBody struct {
// Username 用户名
Username string `json:"username" binding:"required"`
// Password 用户密
Password string `json:"password" binding:"required"`
// ConfirmPassword 用户确认密码
ConfirmPassword string `json:"confirmPassword" binding:"required"`
// Code 验证码
Code string `json:"code"`
// UUID 验证码唯一标识
UUID string `json:"uuid"`
// UserType 标记用户类型
UserType string `json:"userType"`
Username string `json:"username" binding:"required"` // Username 用户名
Password string `json:"password" binding:"required"` // Password 用户密码
ConfirmPassword string `json:"confirmPassword" binding:"required"` // ConfirmPassword 用户确认密码
Code string `json:"code"` // Code 验证
UUID string `json:"uuid"` // UUID 验证码唯一标识
UserType string `json:"userType"` // UserType 标记用户类型
}

View File

@@ -6,7 +6,7 @@ import (
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/oauth2/model"
"be.ems/src/modules/auth/model"
)
// NewOauth2Client 实例化数据层

View File

@@ -7,7 +7,7 @@ import (
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/oauth2/model"
"be.ems/src/modules/auth/model"
)
// NewOauth2LogLogin 实例化数据层
@@ -69,9 +69,10 @@ func (r Oauth2LogLoginRepository) SelectByPage(query map[string]string) ([]model
sortOrder := sortOrderArr[i]
// 排序字段
sort := "id"
if sortBy == "loginIp" {
switch sortBy {
case "loginIp":
sort = "login_ip"
} else if sortBy == "createTime" {
case "createTime":
sort = "create_time"
}
// 排序方式

View File

@@ -2,102 +2,32 @@ package service
import (
"fmt"
"time"
"be.ems/src/framework/config"
"be.ems/src/framework/constants"
"be.ems/src/framework/database/redis"
"be.ems/src/framework/token"
"be.ems/src/framework/utils/crypto"
"be.ems/src/framework/utils/parse"
systemModelVO "be.ems/src/modules/system/model/vo"
"be.ems/src/modules/auth/model"
systemModel "be.ems/src/modules/system/model"
systemService "be.ems/src/modules/system/service"
)
// 实例化服务层 Account 结构体
var NewAccount = &Account{
sysUserService: systemService.NewSysUser,
sysConfigService: systemService.NewSysConfig,
sysRoleService: systemService.NewSysRole,
sysMenuService: systemService.NewSysMenu,
sysUserService: systemService.NewSysUser,
sysConfigService: systemService.NewSysConfig,
sysRoleService: systemService.NewSysRole,
sysMenuService: systemService.NewSysMenu,
sysLogSourceService: systemService.NewSysLoginSource,
}
// 账号身份操作服务 服务层处理
type Account struct {
sysUserService *systemService.SysUser // 用户信息服务
sysConfigService *systemService.SysConfig // 参数配置服务
sysRoleService *systemService.SysRole // 角色服务
sysMenuService *systemService.SysMenu // 菜单服务
}
// ValidateCaptcha 校验验证码
func (s *Account) ValidateCaptcha(code, uuid string) error {
// 验证码检查,从数据库配置获取验证码开关 true开启false关闭
captchaEnabledStr := s.sysConfigService.FindValueByKey("sys.account.captchaEnabled")
if !parse.Boolean(captchaEnabledStr) {
return nil
}
if code == "" || uuid == "" {
// 验证码信息错误
return fmt.Errorf("captcha.err")
}
verifyKey := constants.CACHE_CAPTCHA_CODE + ":" + uuid
captcha, _ := redis.Get("", verifyKey)
if captcha == "" {
// 验证码已失效
return fmt.Errorf("captcha.errValid")
}
_ = redis.Del("", verifyKey)
if captcha != code {
// 验证码错误
return fmt.Errorf("captcha.err")
}
return nil
}
// ByUsername 登录创建用户信息
func (s Account) ByUsername(username, password string) (token.UserInfo, error) {
info := token.UserInfo{}
// 检查密码重试次数
retryKey, retryCount, lockTime, err := s.passwordRetryCount(username)
if err != nil {
return info, err
}
// 查询用户登录账号
sysUser := s.sysUserService.FindByUserName(username)
if sysUser.UserName != username {
return info, fmt.Errorf("login.errNameOrPasswd")
}
if sysUser.DelFlag == constants.STATUS_YES {
return info, fmt.Errorf("login.errDelFlag")
}
if sysUser.StatusFlag == constants.STATUS_NO {
return info, fmt.Errorf("login.errStatus")
}
// 检验用户密码
compareBool := crypto.BcryptCompare(password, sysUser.Password)
if compareBool {
s.CleanLoginRecordCache(sysUser.UserName) // 清除错误记录次数
} else {
_ = redis.Set("", retryKey, retryCount+1, lockTime)
return info, fmt.Errorf("login.errNameOrPasswd")
}
// 登录用户信息
info.UserId = sysUser.UserId
info.DeptId = sysUser.DeptId
info.User = sysUser
// 用户权限组标识
if config.IsSystemUser(sysUser.UserId) {
info.Permissions = []string{constants.SYS_PERMISSION_SYSTEM}
} else {
perms := s.sysMenuService.FindPermsByUserId(sysUser.UserId)
info.Permissions = parse.RemoveDuplicates(perms)
}
return info, nil
sysUserService *systemService.SysUser // 用户信息服务
sysConfigService *systemService.SysConfig // 参数配置服务
sysRoleService *systemService.SysRole // 角色服务
sysMenuService *systemService.SysMenu // 菜单服务
sysLogSourceService *systemService.SysLoginSource // 认证源
}
// ByUserId 用户ID刷新令牌创建用户信息
@@ -140,86 +70,43 @@ func (s Account) UpdateLoginDateAndIP(info token.UserInfo) bool {
return s.sysUserService.Update(user) > 0
}
// CleanLoginRecordCache 清除错误记录次数
func (s Account) CleanLoginRecordCache(userName string) bool {
cacheKey := fmt.Sprintf("%s:%s", constants.CACHE_PWD_ERR_COUNT, userName)
hasKey, err := redis.Has("", cacheKey)
if hasKey > 0 && err == nil {
return redis.Del("", cacheKey) == nil
// LoginSource 登录认证源
func (s Account) LoginSource() []model.LoginSourceVo {
rows := s.sysLogSourceService.FindByActive("")
data := make([]model.LoginSourceVo, 0)
for _, v := range rows {
data = append(data, model.LoginSourceVo{
UID: v.UID,
Name: v.Name,
Type: v.Type,
Icon: v.Icon,
})
}
return false
return data
}
// passwordRetryCount 密码重试次数
func (s Account) passwordRetryCount(userName string) (string, int64, time.Duration, error) {
// 从数据库配置获取登录次数和错误锁定时间
maxRetryCountStr := s.sysConfigService.FindValueByKey("sys.user.maxRetryCount")
lockTimeStr := s.sysConfigService.FindValueByKey("sys.user.lockTime")
// 验证登录次数和错误锁定时间
maxRetryCount := parse.Number(maxRetryCountStr)
lockTime := parse.Number(lockTimeStr)
// initLoginSourceUser 初始化登录源用户
func (s *Account) initLoginSourceUser(uid, sType, username, password string) systemModel.SysUser {
sysUser := systemModel.SysUser{
UserName: username,
NickName: username, // 昵称使用名称账号
Password: password, // 原始密码
UserType: sType,
UserSource: uid,
Sex: "0", // 性别未选择
StatusFlag: constants.STATUS_YES, // 账号状态激活
DeptId: 101, // 归属部门为根节点
CreateBy: sType, // 创建来源
}
// 验证缓存记录次数
retryKey := fmt.Sprintf("%s:%s", constants.CACHE_PWD_ERR_COUNT, userName)
retryCount, err := redis.Get("", retryKey)
if retryCount == "" || err != nil {
retryCount = "0"
// 新增用户的角色管理
sysUser.RoleIds = []int64{5}
// 新增用户的岗位管理
sysUser.PostIds = []int64{}
insertId := s.sysUserService.Insert(sysUser)
if insertId > 0 {
sysUser.UserId = insertId
}
// 是否超过错误值
retryCountInt64 := parse.Number(retryCount)
if retryCountInt64 >= int64(maxRetryCount) {
// msg := fmt.Sprintf("密码输入错误 %d 次,帐户锁定 %d 分钟", maxRetryCount, lockTime)
msg := fmt.Errorf("login.errRetryPasswd") // 密码输入错误多次,帐户已被锁定
return retryKey, retryCountInt64, time.Duration(lockTime) * time.Minute, fmt.Errorf("%s", msg)
}
return retryKey, retryCountInt64, time.Duration(lockTime) * time.Minute, nil
}
// PasswordCountOrExpireTime 首次登录或密码过期时间
func (s Account) PasswordCountOrExpireTime(loginCount, passwordUpdateTime int64) (bool, error) {
forcePasswdChange := false
// 从数据库配置获取-首次登录密码修改
fristPasswdChangeStr := s.sysConfigService.FindValueByKey("sys.user.fristPasswdChange")
if parse.Boolean(fristPasswdChangeStr) {
forcePasswdChange = loginCount < 1 || passwordUpdateTime == 0
}
// 非首次登录,判断密码是否过期
if !forcePasswdChange {
alert, err := s.sysUserService.ValidatePasswordExpireTime(passwordUpdateTime)
if err != nil {
return alert, err
}
forcePasswdChange = alert
}
return forcePasswdChange, nil
}
// RoleAndMenuPerms 角色和菜单数据权限
func (s Account) RoleAndMenuPerms(userId int64, isSystemUser bool) ([]string, []string) {
if isSystemUser {
return []string{constants.SYS_ROLE_SYSTEM_KEY}, []string{constants.SYS_PERMISSION_SYSTEM}
}
// 角色key
var roleGroup []string
roles := s.sysRoleService.FindByUserId(userId)
for _, role := range roles {
roleGroup = append(roleGroup, role.RoleKey)
}
// 菜单权限key
perms := s.sysMenuService.FindPermsByUserId(userId)
return parse.RemoveDuplicates(roleGroup), parse.RemoveDuplicates(perms)
}
// RouteMenus 前端路由所需要的菜单
func (s Account) RouteMenus(userId int64, isSystemUser bool) []systemModelVO.Router {
var buildMenus []systemModelVO.Router
if isSystemUser {
menus := s.sysMenuService.BuildTreeMenusByUserId(0)
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
} else {
menus := s.sysMenuService.BuildTreeMenusByUserId(userId)
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
}
return buildMenus
return s.sysUserService.FindByUserName(username, sType, uid)
}

View File

@@ -0,0 +1,36 @@
package service
import (
"be.ems/src/framework/constants"
"be.ems/src/framework/utils/parse"
systemModelVO "be.ems/src/modules/system/model/vo"
)
// RoleAndMenuPerms 角色和菜单数据权限
func (s Account) RoleAndMenuPerms(userId int64, isSystemUser bool) ([]string, []string) {
if isSystemUser {
return []string{constants.SYS_ROLE_SYSTEM_KEY}, []string{constants.SYS_PERMISSION_SYSTEM}
}
// 角色key
var roleGroup []string
roles := s.sysRoleService.FindByUserId(userId)
for _, role := range roles {
roleGroup = append(roleGroup, role.RoleKey)
}
// 菜单权限key
perms := s.sysMenuService.FindPermsByUserId(userId)
return parse.RemoveDuplicates(roleGroup), parse.RemoveDuplicates(perms)
}
// RouteMenus 前端路由所需要的菜单
func (s Account) RouteMenus(userId int64, isSystemUser bool) []systemModelVO.Router {
var buildMenus []systemModelVO.Router
if isSystemUser {
menus := s.sysMenuService.BuildTreeMenusByUserId(0)
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
} else {
menus := s.sysMenuService.BuildTreeMenusByUserId(userId)
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
}
return buildMenus
}

View File

@@ -0,0 +1,108 @@
package service
import (
"encoding/json"
"fmt"
"github.com/go-ldap/ldap/v3"
"be.ems/src/framework/config"
"be.ems/src/framework/constants"
"be.ems/src/framework/token"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/auth/model"
systemModelVo "be.ems/src/modules/system/model/vo"
)
// ByLDAP 登录创建用户信息
func (s *Account) ByLDAP(body model.LoginSourceBody) (token.UserInfo, error) {
info := token.UserInfo{}
rows := s.sysLogSourceService.FindByActive(body.UID)
if len(rows) != 1 {
return info, fmt.Errorf("ldap auth source not exist")
}
item := rows[0]
if item.Config == "" {
return info, fmt.Errorf("ldap auth source config is empty")
}
var source systemModelVo.SysLoginSourceLDAP
if err := json.Unmarshal([]byte(item.Config), &source); err != nil {
return info, err
}
// 校验LDAP用户
err := ldapAuth(source, body.Username, body.Password)
if err != nil {
return info, err
}
// 查询用户登录账号
sysUser := s.sysUserService.FindByUserName(body.Username, item.Type, item.UID)
if sysUser.UserId == 0 || sysUser.UserName == "" {
sysUser = s.initLoginSourceUser(item.UID, item.Type, body.Username, body.Password)
}
if sysUser.UserId == 0 || sysUser.UserName != body.Username {
return info, fmt.Errorf("login.errNameOrPasswd")
}
if sysUser.DelFlag == constants.STATUS_YES {
return info, fmt.Errorf("login.errDelFlag")
}
if sysUser.StatusFlag == constants.STATUS_NO {
return info, fmt.Errorf("login.errStatus")
}
// 登录用户信息
info.UserId = sysUser.UserId
info.DeptId = sysUser.DeptId
info.User = sysUser
// 用户权限组标识
if config.IsSystemUser(sysUser.UserId) {
info.Permissions = []string{constants.SYS_PERMISSION_SYSTEM}
} else {
perms := s.sysMenuService.FindPermsByUserId(sysUser.UserId)
info.Permissions = parse.RemoveDuplicates(perms)
}
return info, nil
}
// ldapAuth 校验LDAP用户
func ldapAuth(source systemModelVo.SysLoginSourceLDAP, username, password string) error {
// 连接LDAP
l, err := ldap.DialURL(source.URL)
if err != nil {
return err
}
defer l.Close()
// 绑定DN校验
if source.BindDN != "" && source.BindPassword != "" {
if err := l.Bind(source.BindDN, source.BindPassword); err != nil {
return fmt.Errorf("ldap user bind %s", err)
}
}
// 搜索用户
searchRequest := ldap.NewSearchRequest(
source.BaseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf(source.UserFilter, ldap.EscapeFilter(username)),
[]string{"dn", "uid"},
nil,
)
sr, err := l.Search(searchRequest)
if err != nil {
return fmt.Errorf("ldap user search %s", err)
}
// for _, entry := range sr.Entries {
// fmt.Printf("%s ==== %v\n", entry.DN, entry.GetAttributeValue("uid"))
// }
if len(sr.Entries) != 1 {
return fmt.Errorf("ldap user does not exist or too many entries returned")
}
// 校验密码
if err = l.Bind(sr.Entries[0].DN, password); err != nil {
return fmt.Errorf("ldap user bind %s", err)
}
return nil
}

View File

@@ -0,0 +1,168 @@
package service
import (
"context"
"encoding/json"
"fmt"
"reflect"
"strings"
"time"
"golang.org/x/oauth2"
"be.ems/src/framework/config"
"be.ems/src/framework/constants"
"be.ems/src/framework/token"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/auth/model"
systemModelVo "be.ems/src/modules/system/model/vo"
)
// ByOAuth2CodeURL 获取OAuth2登录URL
func (s *Account) ByOAuth2CodeURL(state string) (string, error) {
rows := s.sysLogSourceService.FindByActive(state)
if len(rows) != 1 {
return "", fmt.Errorf("oauth2 auth source not exist")
}
item := rows[0]
if item.Config == "" {
return "", fmt.Errorf("oauth2 auth source config is empty")
}
var source systemModelVo.SysLoginSourceOAuth2
json.Unmarshal([]byte(item.Config), &source)
conf := oauth2.Config{
ClientID: source.ClientID,
ClientSecret: source.ClientSecret,
RedirectURL: source.RedirectURL,
Scopes: source.Scopes,
Endpoint: oauth2.Endpoint{
AuthURL: source.AuthURL,
TokenURL: source.TokenURL,
},
}
return conf.AuthCodeURL(state, oauth2.AccessTypeOffline), nil
}
// ByOAuth2 登录创建用户信息
func (s *Account) ByOAuth2(body model.LoginSourceOauth2Body) (token.UserInfo, error) {
info := token.UserInfo{}
rows := s.sysLogSourceService.FindByActive(body.State)
if len(rows) != 1 {
return info, fmt.Errorf("oauth2 auth source not exist")
}
item := rows[0]
if item.Config == "" {
return info, fmt.Errorf("oauth2 auth source config is empty")
}
var source systemModelVo.SysLoginSourceOAuth2
if err := json.Unmarshal([]byte(item.Config), &source); err != nil {
return info, err
}
// 校验OAuth2用户
account, err := oauth2Auth(source, body.Code)
if err != nil {
return info, err
}
// 查询用户登录账号
sysUser := s.sysUserService.FindByUserName(account, item.Type, item.UID)
if sysUser.UserId == 0 || sysUser.UserName == "" {
sysUser = s.initLoginSourceUser(item.UID, item.Type, account, account)
}
if sysUser.UserId == 0 || sysUser.UserName != account {
return info, fmt.Errorf("login.errNameOrPasswd")
}
if sysUser.DelFlag == constants.STATUS_YES {
return info, fmt.Errorf("login.errDelFlag")
}
if sysUser.StatusFlag == constants.STATUS_NO {
return info, fmt.Errorf("login.errStatus")
}
// 登录用户信息
info.UserId = sysUser.UserId
info.DeptId = sysUser.DeptId
info.User = sysUser
// 用户权限组标识
if config.IsSystemUser(sysUser.UserId) {
info.Permissions = []string{constants.SYS_PERMISSION_SYSTEM}
} else {
perms := s.sysMenuService.FindPermsByUserId(sysUser.UserId)
info.Permissions = parse.RemoveDuplicates(perms)
}
return info, nil
}
// oauth2Auth 校验OAuth2用户
func oauth2Auth(source systemModelVo.SysLoginSourceOAuth2, code string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
conf := oauth2.Config{
ClientID: source.ClientID,
ClientSecret: source.ClientSecret,
RedirectURL: source.RedirectURL,
Scopes: source.Scopes,
Endpoint: oauth2.Endpoint{
AuthURL: source.AuthURL,
TokenURL: source.TokenURL,
},
}
token, err := conf.Exchange(ctx, code)
if err != nil {
return "", err
}
// 使用token创建HTTP客户端 请求用户信息
resp, err := conf.Client(ctx, token).Get(source.UserURL)
if err != nil {
return "", err
}
defer resp.Body.Close()
// 解析用户信息
var userInfo map[string]any
if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
return "", err
}
// 读取嵌套数据
value, found := getValueByPath(userInfo, source.AccountField)
if !found {
return "", fmt.Errorf("oauth2 auth source account field not exist")
}
return fmt.Sprintf("%v", value), nil
}
// getValueByPath 从嵌套的 map[string]any 获取嵌套键对应的值
func getValueByPath(data map[string]any, path string) (any, bool) {
keys := strings.Split(path, ".") // 按照 "." 拆分路径
return getValue(data, keys)
}
// getValue 是递归查找嵌套 map 的函数
func getValue(data map[string]any, keys []string) (any, bool) {
if len(keys) == 0 {
return data, false
}
// 获取当前键
key := keys[0]
// 获取当前键的值
val, ok := data[key]
if !ok {
return nil, false // 找不到键,返回 false
}
// 如果还有嵌套键,继续查找
if len(keys) > 1 {
// 递归查找嵌套 map
if reflect.TypeOf(val).Kind() == reflect.Map {
// 将 `any` 转换为 `map[string]any` 类型
if nestedMap, ok := val.(map[string]any); ok {
return getValue(nestedMap, keys[1:])
}
}
}
return val, true
}

View File

@@ -0,0 +1,88 @@
package service
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"github.com/wneessen/go-mail"
"be.ems/src/framework/config"
"be.ems/src/framework/constants"
"be.ems/src/framework/token"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/auth/model"
systemModelVo "be.ems/src/modules/system/model/vo"
)
// BySMTP 登录创建用户信息
func (s *Account) BySMTP(body model.LoginSourceBody) (token.UserInfo, error) {
info := token.UserInfo{}
rows := s.sysLogSourceService.FindByActive(body.UID)
if len(rows) != 1 {
return info, fmt.Errorf("smtp auth source not exist")
}
item := rows[0]
if item.Config == "" {
return info, fmt.Errorf("smtp auth source config is empty")
}
var source systemModelVo.SysLoginSourceSMTP
if err := json.Unmarshal([]byte(item.Config), &source); err != nil {
return info, err
}
// 校验SMTP用户
err := smtpAuth(source, body.Username, body.Password)
if err != nil {
return info, err
}
// 查询用户登录账号
sysUser := s.sysUserService.FindByUserName(body.Username, item.Type, item.UID)
if sysUser.UserId == 0 || sysUser.UserName == "" {
sysUser = s.initLoginSourceUser(item.UID, item.Type, body.Username, body.Password)
}
if sysUser.UserId == 0 || sysUser.UserName != body.Username {
return info, fmt.Errorf("login.errNameOrPasswd")
}
if sysUser.DelFlag == constants.STATUS_YES {
return info, fmt.Errorf("login.errDelFlag")
}
if sysUser.StatusFlag == constants.STATUS_NO {
return info, fmt.Errorf("login.errStatus")
}
// 登录用户信息
info.UserId = sysUser.UserId
info.DeptId = sysUser.DeptId
info.User = sysUser
// 用户权限组标识
if config.IsSystemUser(sysUser.UserId) {
info.Permissions = []string{constants.SYS_PERMISSION_SYSTEM}
} else {
perms := s.sysMenuService.FindPermsByUserId(sysUser.UserId)
info.Permissions = parse.RemoveDuplicates(perms)
}
return info, nil
}
// smtpAuth 校验SMTP用户
func smtpAuth(source systemModelVo.SysLoginSourceSMTP, username, password string) error {
client, err := mail.NewClient(source.Host,
mail.WithSMTPAuth(mail.SMTPAuthAutoDiscover),
mail.WithUsername(username),
mail.WithPort(source.Port),
mail.WithPassword(password),
mail.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}),
)
if err != nil {
return fmt.Errorf("failed to create mail client %s", err)
}
// 连接到邮件SMTP服务器
if err = client.DialWithContext(context.Background()); err != nil {
return err
}
defer client.Close()
return nil
}

View File

@@ -0,0 +1,138 @@
package service
import (
"fmt"
"time"
"be.ems/src/framework/config"
"be.ems/src/framework/constants"
"be.ems/src/framework/database/redis"
"be.ems/src/framework/token"
"be.ems/src/framework/utils/crypto"
"be.ems/src/framework/utils/parse"
)
// ValidateCaptcha 校验验证码
func (s Account) ValidateCaptcha(code, uuid string) error {
// 验证码检查,从数据库配置获取验证码开关 true开启false关闭
captchaEnabledStr := s.sysConfigService.FindValueByKey("sys.account.captchaEnabled")
if !parse.Boolean(captchaEnabledStr) {
return nil
}
if code == "" || uuid == "" {
// 验证码信息错误
return fmt.Errorf("captcha.err")
}
verifyKey := constants.CACHE_CAPTCHA_CODE + ":" + uuid
captcha, _ := redis.Get("", verifyKey)
if captcha == "" {
// 验证码已失效
return fmt.Errorf("captcha.errValid")
}
_ = redis.Del("", verifyKey)
if captcha != code {
// 验证码错误
return fmt.Errorf("captcha.err")
}
return nil
}
// ByUsername 登录创建用户信息
func (s Account) ByUsername(username, password string) (token.UserInfo, error) {
info := token.UserInfo{}
// 检查密码重试次数
retryKey, retryCount, lockTime, err := s.passwordRetryCount(username)
if err != nil {
return info, err
}
// 查询用户登录账号
sysUser := s.sysUserService.FindByUserName(username, "System", "#")
if sysUser.UserName != username {
return info, fmt.Errorf("login.errNameOrPasswd")
}
if sysUser.DelFlag == constants.STATUS_YES {
return info, fmt.Errorf("login.errDelFlag")
}
if sysUser.StatusFlag == constants.STATUS_NO {
return info, fmt.Errorf("login.errStatus")
}
// 检验用户密码
compareBool := crypto.BcryptCompare(password, sysUser.Password)
if compareBool {
s.CleanLoginRecordCache(sysUser.UserName) // 清除错误记录次数
} else {
_ = redis.Set("", retryKey, retryCount+1, lockTime)
return info, fmt.Errorf("login.errNameOrPasswd")
}
// 登录用户信息
info.UserId = sysUser.UserId
info.DeptId = sysUser.DeptId
info.User = sysUser
// 用户权限组标识
if config.IsSystemUser(sysUser.UserId) {
info.Permissions = []string{constants.SYS_PERMISSION_SYSTEM}
} else {
perms := s.sysMenuService.FindPermsByUserId(sysUser.UserId)
info.Permissions = parse.RemoveDuplicates(perms)
}
return info, nil
}
// CleanLoginRecordCache 清除错误记录次数
func (s Account) CleanLoginRecordCache(userName string) bool {
cacheKey := fmt.Sprintf("%s:%s", constants.CACHE_PWD_ERR_COUNT, userName)
hasKey, err := redis.Has("", cacheKey)
if hasKey > 0 && err == nil {
return redis.Del("", cacheKey) == nil
}
return false
}
// passwordRetryCount 密码重试次数
func (s Account) passwordRetryCount(userName string) (string, int64, time.Duration, error) {
// 从数据库配置获取登录次数和错误锁定时间
maxRetryCountStr := s.sysConfigService.FindValueByKey("sys.user.maxRetryCount")
lockTimeStr := s.sysConfigService.FindValueByKey("sys.user.lockTime")
// 验证登录次数和错误锁定时间
maxRetryCount := parse.Number(maxRetryCountStr)
lockTime := parse.Number(lockTimeStr)
// 验证缓存记录次数
retryKey := fmt.Sprintf("%s:%s", constants.CACHE_PWD_ERR_COUNT, userName)
retryCount, err := redis.Get("", retryKey)
if retryCount == "" || err != nil {
retryCount = "0"
}
// 是否超过错误值
retryCountInt64 := parse.Number(retryCount)
if retryCountInt64 >= int64(maxRetryCount) {
// msg := fmt.Sprintf("密码输入错误 %d 次,帐户锁定 %d 分钟", maxRetryCount, lockTime)
msg := fmt.Errorf("login.errRetryPasswd") // 密码输入错误多次,帐户已被锁定
return retryKey, retryCountInt64, time.Duration(lockTime) * time.Minute, fmt.Errorf("%s", msg)
}
return retryKey, retryCountInt64, time.Duration(lockTime) * time.Minute, nil
}
// PasswordCountOrExpireTime 首次登录或密码过期时间
func (s Account) PasswordCountOrExpireTime(loginCount, passwordUpdateTime int64) (bool, error) {
forcePasswdChange := false
// 从数据库配置获取-首次登录密码修改
fristPasswdChangeStr := s.sysConfigService.FindValueByKey("sys.user.fristPasswdChange")
if parse.Boolean(fristPasswdChangeStr) {
forcePasswdChange = loginCount < 1 || passwordUpdateTime == 0
}
// 非首次登录,判断密码是否过期
if !forcePasswdChange {
alert, err := s.sysUserService.ValidatePasswordExpireTime(passwordUpdateTime)
if err != nil {
return alert, err
}
forcePasswdChange = alert
}
return forcePasswdChange, nil
}

View File

@@ -10,8 +10,8 @@ import (
"be.ems/src/framework/token"
"be.ems/src/framework/utils/crypto"
"be.ems/src/framework/utils/generate"
"be.ems/src/modules/oauth2/model"
"be.ems/src/modules/oauth2/repository"
"be.ems/src/modules/auth/model"
"be.ems/src/modules/auth/repository"
)
// NewOauth2Service 实例化服务层

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"be.ems/src/framework/utils/generate"
"be.ems/src/modules/oauth2/model"
"be.ems/src/modules/oauth2/repository"
"be.ems/src/modules/auth/model"
"be.ems/src/modules/auth/repository"
)
// NewOauth2ClientService 实例化服务层
@@ -55,11 +55,11 @@ func (s Oauth2ClientService) DeleteByIds(ids []int64) (int64, error) {
arr := s.oauth2ClientRepository.SelectByIds(ids)
if len(arr) <= 0 {
// return 0, fmt.Errorf("没有权限访问用户授权第三方应用数据!")
return 0, fmt.Errorf("No permission to access user-authorized third-party application data!")
return 0, fmt.Errorf("no permission to access user-authorized third-party application data")
}
if len(arr) == len(ids) {
return s.oauth2ClientRepository.DeleteByIds(ids), nil
}
// return 0, fmt.Errorf("删除用户授权第三方应用信息失败!")
return 0, fmt.Errorf("Failed to delete user-authorized third-party application information!")
return 0, fmt.Errorf("failed to delete user-authorized third-party application information")
}

View File

@@ -6,8 +6,8 @@ import (
"be.ems/src/framework/constants"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/file"
"be.ems/src/modules/oauth2/model"
"be.ems/src/modules/oauth2/repository"
"be.ems/src/modules/auth/model"
"be.ems/src/modules/auth/repository"
)
// NewOauth2LogLogin 实例化服务层

View File

@@ -61,12 +61,14 @@ func (s Register) ByUserName(username, password string) (int64, error) {
sysUser := systemModel.SysUser{
UserName: username,
NickName: username, // 昵称使用名称账号
Password: password, // 原始密码
NickName: username, // 昵称使用名称账号
Password: password, // 原始密码
UserType: "System",
UserSource: "#",
Sex: "0", // 性别未选择
StatusFlag: constants.STATUS_YES, // 账号状态激活
DeptId: 100, // 归属部门为根节点
CreateBy: "register", // 创建来源
DeptId: 101, // 归属部门为根节点
CreateBy: "System", // 创建来源
}
// 新增用户的角色管理

View File

@@ -15,7 +15,6 @@ import (
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/file"
"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"
@@ -23,26 +22,20 @@ import (
)
var NewProcessor = &BackupExportCDRProcessor{
count: 0,
backupService: neDataService.NewBackup,
sysDictService: systemService.NewSysDictData,
neInfoService: neService.NewNeInfo,
imsCDREventService: neDataService.NewCDREventIMS,
smscCDREventService: neDataService.NewCDREventSMSC,
smfCDREventService: neDataService.NewCDREventSMF,
sgwcCDREventService: neDataService.NewCDREventSGWC,
count: 0,
backupService: neDataService.NewBackup,
sysDictService: systemService.NewSysDictData,
neInfoService: neService.NewNeInfo,
cdrEventService: neDataService.NewCDREvent,
}
// BackupExportCDR 队列任务处理
type BackupExportCDRProcessor struct {
count int // 执行次数
backupService *neDataService.Backup // 备份相关服务
sysDictService *systemService.SysDictData // 字典类型数据服务
neInfoService *neService.NeInfo // 网元信息服务
imsCDREventService *neDataService.CDREventIMS // IMS-CDR会话事件服务
smscCDREventService *neDataService.CDREventSMSC // SMSC-CDR会话事件服务
smfCDREventService *neDataService.CDREventSMF // SMF-CDR会话事件服务
sgwcCDREventService *neDataService.CDREventSGWC // SGWC-CDR会话事件服务
count int // 执行次数
backupService *neDataService.Backup // 备份相关服务
sysDictService *systemService.SysDictData // 字典类型数据服务
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREvent // CDR会话事件服务
}
func (s *BackupExportCDRProcessor) Execute(data any) (any, error) {
@@ -104,16 +97,16 @@ func (s BackupExportCDRProcessor) exportIMS(hour int, rmUID, fileType string) st
start := end.Add(-time.Duration(hour) * time.Hour)
language := "en"
query := neDataModel.CDREventIMSQuery{
SortField: "timestamp",
SortOrder: "asc",
RmUID: rmUID,
BeginTime: start.UnixMilli(),
EndTime: end.UnixMilli(),
PageNum: 1,
PageSize: 30000,
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.imsCDREventService.FindByPage(query)
rows, total := s.cdrEventService.FindByPage("IMS", query)
if total == 0 {
return "no data"
}
@@ -393,16 +386,16 @@ func (s BackupExportCDRProcessor) exportSMSC(hour int, rmUID, fileType string) s
start := end.Add(-time.Duration(hour) * time.Hour)
language := "en"
query := neDataModel.CDREventSMSCQuery{
SortField: "timestamp",
SortOrder: "asc",
RmUID: rmUID,
BeginTime: start.UnixMilli(),
EndTime: end.UnixMilli(),
PageNum: 1,
PageSize: 30000,
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.smscCDREventService.FindByPage(query)
rows, total := s.cdrEventService.FindByPage("SMSC", query)
if total == 0 {
return "no data"
}
@@ -609,16 +602,16 @@ func (s BackupExportCDRProcessor) exportSMF(hour int, rmUID, fileType string) st
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 := neDataModel.CDREventSMFQuery{
SortField: "timestamp",
SortOrder: "asc",
RmUID: rmUID,
BeginTime: start.UnixMilli(),
EndTime: end.UnixMilli(),
PageNum: 1,
PageSize: 30000,
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.smfCDREventService.FindByPage(query)
rows, total := s.cdrEventService.FindByPage("SMF", query)
if total == 0 {
return "no data"
}
@@ -1017,16 +1010,16 @@ func (s BackupExportCDRProcessor) exportSGWC(hour int, rmUID, fileType string) s
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 := neDataModel.CDREventSGWCQuery{
SortField: "timestamp",
SortOrder: "asc",
RmUID: rmUID,
BeginTime: start.UnixMilli(),
EndTime: end.UnixMilli(),
PageNum: 1,
PageSize: 30000,
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.sgwcCDREventService.FindByPage(query)
rows, total := s.cdrEventService.FindByPage("SGWC", query)
if total == 0 {
return "no data"
}

View File

@@ -15,21 +15,22 @@ import (
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
neDataModel "be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
systemModel "be.ems/src/modules/system/model"
systemService "be.ems/src/modules/system/service"
)
var NewProcessor = &BackupExportTableProcessor{
backupService: neDataService.NewBackup,
count: 0,
backupService: neDataService.NewBackup,
cdrEventService: neDataService.NewCDREvent,
count: 0,
}
// BackupExportTable 备份导出数据表
type BackupExportTableProcessor struct {
backupService *neDataService.Backup // 备份相关服务
count int // 执行次数
backupService *neDataService.Backup // 备份相关服务
cdrEventService *neDataService.CDREvent // CDR会话事件服务
count int // 执行次数
}
func (s *BackupExportTableProcessor) Execute(data any) (any, error) {
@@ -197,13 +198,13 @@ func (s BackupExportTableProcessor) exportSMF(hour int, columns []string, filePa
start := end.Add(-time.Duration(hour) * time.Hour)
// 查询数据
rows := []neDataModel.CDREventSMF{}
tx := db.DB("").Model(&neDataModel.CDREventSMF{})
tx = tx.Where("created_at >= ? and created_at <= ?", start.UnixMilli(), end.UnixMilli())
if err := tx.Find(&rows).Error; err != nil {
return 0, err
}
if len(rows) <= 0 {
rows, total := s.cdrEventService.FindByPage("SMF", map[string]string{
"sortField": "created_at",
"sortOrder": "asc",
"beginTime": fmt.Sprint(start.UnixMilli()),
"endTime": fmt.Sprint(end.UnixMilli()),
})
if total <= 0 {
return 0, nil
}
@@ -417,7 +418,7 @@ func (s BackupExportTableProcessor) exportSMF(hour int, columns []string, filePa
err := file.WriterFileCSV(data, filePath)
return tx.RowsAffected, err
return total, err
}
// exportIMS 导出csv
@@ -428,13 +429,13 @@ func (s BackupExportTableProcessor) exportIMS(hour int, columns []string, filePa
start := end.Add(-time.Duration(hour) * time.Hour)
// 查询数据
rows := []neDataModel.CDREventIMS{}
tx := db.DB("").Model(&neDataModel.CDREventIMS{})
tx = tx.Where("created_at >= ? and created_at <= ?", start.UnixMilli(), end.UnixMilli())
if err := tx.Find(&rows).Error; err != nil {
return 0, err
}
if len(rows) <= 0 {
rows, total := s.cdrEventService.FindByPage("IMS", map[string]string{
"sortField": "created_at",
"sortOrder": "asc",
"beginTime": fmt.Sprint(start.UnixMilli()),
"endTime": fmt.Sprint(end.UnixMilli()),
})
if total <= 0 {
return 0, nil
}
@@ -561,7 +562,7 @@ func (s BackupExportTableProcessor) exportIMS(hour int, columns []string, filePa
err := file.WriterFileCSV(data, filePath)
return tx.RowsAffected, err
return total, err
}
// exportSMSC 导出csv
@@ -572,13 +573,13 @@ func (s BackupExportTableProcessor) exportSMSC(hour int, columns []string, fileP
start := end.Add(-time.Duration(hour) * time.Hour)
// 查询数据
rows := []neDataModel.CDREventSMSC{}
tx := db.DB("").Model(&neDataModel.CDREventSMSC{})
tx = tx.Where("created_at >= ? and created_at <= ?", start.UnixMilli(), end.UnixMilli())
if err := tx.Find(&rows).Error; err != nil {
return 0, err
}
if len(rows) <= 0 {
rows, total := s.cdrEventService.FindByPage("SMS", map[string]string{
"sortField": "created_at",
"sortOrder": "asc",
"beginTime": fmt.Sprint(start.UnixMilli()),
"endTime": fmt.Sprint(end.UnixMilli()),
})
if total <= 0 {
return 0, nil
}
@@ -686,7 +687,7 @@ func (s BackupExportTableProcessor) exportSMSC(hour int, columns []string, fileP
err := file.WriterFileCSV(data, filePath)
return tx.RowsAffected, err
return total, err
}
// exportSGWC 导出csv
@@ -697,13 +698,13 @@ func (s BackupExportTableProcessor) exportSGWC(hour int, columns []string, fileP
start := end.Add(-time.Duration(hour) * time.Hour)
// 查询数据
rows := []neDataModel.CDREventSMSC{}
tx := db.DB("").Model(&neDataModel.CDREventSMSC{})
tx = tx.Where("created_at >= ? and created_at <= ?", start.UnixMilli(), end.UnixMilli())
if err := tx.Find(&rows).Error; err != nil {
return 0, err
}
if len(rows) <= 0 {
rows, total := s.cdrEventService.FindByPage("SGWC", map[string]string{
"sortField": "created_at",
"sortOrder": "asc",
"beginTime": fmt.Sprint(start.UnixMilli()),
"endTime": fmt.Sprint(end.UnixMilli()),
})
if total <= 0 {
return 0, nil
}
@@ -923,5 +924,5 @@ func (s BackupExportTableProcessor) exportSGWC(hour int, columns []string, fileP
err := file.WriterFileCSV(data, filePath)
return tx.RowsAffected, err
return total, err
}

View File

@@ -5,33 +5,26 @@ import (
"fmt"
"time"
"github.com/tsmask/go-oam"
"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"
oamService "be.ems/src/modules/oam/service"
)
var NewProcessor = &NeAlarmStateCheckProcessor{
neConfigBackupService: neService.NewNeConfigBackup,
neInfoService: neService.NewNeInfo,
alarmService: neDataService.NewAlarm,
wsSendService: wsService.NewWSSend,
count: 0,
neInfoService: neService.NewNeInfo,
count: 0,
}
// NeAlarmStateCheckProcessor 网元告警状态检查
type NeAlarmStateCheckProcessor struct {
neConfigBackupService *neService.NeConfigBackup // 网元配置文件备份记录服务
neInfoService *neService.NeInfo // 网元信息服务
alarmService *neDataService.Alarm // 告警信息服务
wsSendService *wsService.WSSend // ws发送服务
count int // 执行次数
neInfoService *neService.NeInfo // 网元信息服务
count int // 执行次数
}
// alarmParams 告警参数
@@ -69,104 +62,37 @@ func (s *NeAlarmStateCheckProcessor) Execute(data any) (any, error) {
if neInfo.CreateTime == 0 {
continue
}
neTypeAndId := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
// 网元在线状态
isOnline := parse.Boolean(neInfo.ServerState["online"])
// 告警ID
params.AlarmId = fmt.Sprintf("%d%d", constants.ALARM_STATE_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 isOnline && 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"
}
// 不在线
if !isOnline && 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"
// 告警状态
alarmStatus := oam.ALARM_STATUS_ACTIVE
if isOnline {
alarmStatus = oam.ALARM_STATUS_CLEAR
}
// 创建告警
alarm := oam.Alarm{
NeUid: neInfo.RmUID, // 网元唯一标识
AlarmTime: time.Now().UnixMilli(), // 事件产生时间
AlarmId: params.AlarmId, // 告警ID 唯一,清除时对应
AlarmCode: constants.ALARM_STATE_CHECK, // 告警状态码
AlarmType: params.AlarmType, // 告警类型
AlarmTitle: params.AlarmTitle, // 告警标题
PerceivedSeverity: params.OrigSeverity, // 告警级别
AlarmStatus: alarmStatus, // 告警状态
SpecificProblem: params.SpecificProblem, // 告警问题原因
SpecificProblemID: params.SpecificProblemID, // 告警问题原因ID
AddInfo: params.AddInfo, // 告警辅助信息
LocationInfo: "NE State: Heartbeat", // 告警定位信息
}
if err = oamService.NewAlarm.Resolve(alarm); err == nil {
result[neInfo.RmUID] = "state alarm"
}
}
// 返回结果,用于记录执行结果
return result, nil
}
// alarmClear 清除告警
func (s NeAlarmStateCheckProcessor) 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 NeAlarmStateCheckProcessor) 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_STATE_CHECK,
EventTime: lastTime,
AlarmType: v.AlarmType,
OrigSeverity: v.OrigSeverity,
PerceivedSeverity: v.OrigSeverity,
ObjectUid: neInfo.RmUID,
ObjectName: "NE State",
ObjectType: "state",
LocationInfo: "NE State: 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

@@ -10,6 +10,8 @@ import (
"sync"
"time"
"github.com/tsmask/go-oam"
"be.ems/src/framework/constants"
"be.ems/src/framework/cron"
"be.ems/src/framework/logger"
@@ -18,6 +20,7 @@ import (
neDataService "be.ems/src/modules/network_data/service"
neModel "be.ems/src/modules/network_element/model"
neService "be.ems/src/modules/network_element/service"
oamService "be.ems/src/modules/oam/service"
wsService "be.ems/src/modules/ws/service"
)
@@ -28,22 +31,18 @@ var (
)
var NewProcessor = &NeAlarmStateCheckCMDProcessor{
neConfigBackupService: neService.NewNeConfigBackup,
neInfoService: neService.NewNeInfo,
neStateService: neDataService.NewNEState,
alarmService: neDataService.NewAlarm,
wsSendService: wsService.NewWSSend,
count: 0,
neInfoService: neService.NewNeInfo,
neStateService: neDataService.NewNEState,
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 // 执行次数
neInfoService *neService.NeInfo // 网元信息服务
neStateService *neDataService.NEState // 网元状态信息服务
wsSendService *wsService.WSSend // ws发送服务
count int // 执行次数
}
// alarmParams 告警参数
@@ -103,65 +102,84 @@ func (s *NeAlarmStateCheckCMDProcessor) Execute(data any) (any, error) {
}
// 检查状态
err := s.serverState(neInfo.ServerState, params.CPUUseGt, params.MemUseGt, params.DiskUseGt)
sysCpuUsage, sysMemUsage, sysDiskUsage := s.serverState(neInfo.ServerState)
// 检查CPU/Menory/Disk使用率
warnMsg := []string{}
if int64(sysCpuUsage) >= params.CPUUseGt {
warnMsg = append(warnMsg, fmt.Sprintf("cpu usage %.2f%%", sysCpuUsage))
}
if int64(sysMemUsage) >= params.MemUseGt {
warnMsg = append(warnMsg, fmt.Sprintf("memory usage %.2f%%", sysMemUsage))
}
if int64(sysDiskUsage) >= params.DiskUseGt {
warnMsg = append(warnMsg, fmt.Sprintf("disk usage %.2f%%", sysDiskUsage))
}
var err error
if len(warnMsg) > 0 {
currentTime := time.Now()
validTimes := []time.Time{}
if v, ok := triggerCount.Load(neInfo.RmUID); ok {
times := v.([]time.Time)
// 清理过期的记录10秒前的触发记录不再计入
for _, t := range times {
if currentTime.Sub(t) <= triggerWindow {
validTimes = append(validTimes, t)
}
}
validTimes = append(validTimes, currentTime)
triggerCount.Store(neInfo.RmUID, validTimes)
} else {
// 事件第一次触发,初始化记录
validTimes = append(validTimes, currentTime)
triggerCount.Store(neInfo.RmUID, validTimes)
}
if int64(len(validTimes)) >= triggerMax {
err = fmt.Errorf("greater than %s", strings.Join(warnMsg, ", "))
}
}
// 检查状态连续触发
if err == nil {
continue
}
addInfo := params.AddInfo
if addInfo != "" {
addInfo = addInfo + ", " + err.Error()
} else {
addInfo = err.Error()
}
neTypeAndId := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
// 事件产生时间
alarmTime := time.Now().UnixMilli()
// 告警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)
params.AlarmId = fmt.Sprintf("%d%d", constants.ALARM_CMD_CHECK, alarmTime)
// 创建告警
alarm := oam.Alarm{
NeUid: neInfo.RmUID, // 网元唯一标识
AlarmTime: alarmTime, // 事件产生时间
AlarmId: params.AlarmId, // 告警ID 唯一,清除时对应
AlarmCode: constants.ALARM_CMD_CHECK, // 告警状态码
AlarmType: params.AlarmType, // 告警类型
AlarmTitle: params.AlarmTitle, // 告警标题
PerceivedSeverity: params.OrigSeverity, // 告警级别
AlarmStatus: oam.ALARM_STATUS_ACTIVE, // 告警状态
SpecificProblem: params.SpecificProblem, // 告警问题原因
SpecificProblemID: params.SpecificProblemID, // 告警问题原因ID
AddInfo: addInfo, // 告警辅助信息
LocationInfo: "NE CPU/Menory/Disk: Heartbeat", // 告警定位信息
}
// 活动告警进行清除
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 // 恢复附加信息
triggerCount.Clear() // 重置连续触发次数
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"
if err = oamService.NewAlarm.Resolve(alarm); err == nil {
result[neInfo.RmUID] = "cmd alarm"
}
triggerCount.Delete(neInfo.RmUID)
}
// 返回结果,用于记录执行结果
return result, nil
}
// serverState 网元状态
func (s NeAlarmStateCheckCMDProcessor) serverState(state map[string]any, cpuUseGt, memUseGt, diskUseGt int64) error {
// serverState 网元状态记录
func (s NeAlarmStateCheckCMDProcessor) serverState(state map[string]any) (float64, float64, float64) {
// 网元CPU使用率
var nfCpuUsage float64 = 0
// CPU使用率
@@ -224,94 +242,5 @@ func (s NeAlarmStateCheckCMDProcessor) serverState(state map[string]any, cpuUseG
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 {
currentTime := time.Now()
neTypeAndId := fmt.Sprintf("%s_%s", neState.NeType, neState.NeId)
validTimes := []time.Time{}
if v, ok := triggerCount.Load(neTypeAndId); ok {
times := v.([]time.Time)
// 清理过期的记录10秒前的触发记录不再计入
for _, t := range times {
if currentTime.Sub(t) <= triggerWindow {
validTimes = append(validTimes, t)
}
}
validTimes = append(validTimes, currentTime)
triggerCount.Store(neTypeAndId, validTimes)
} else {
// 事件第一次触发,初始化记录
validTimes = append(validTimes, currentTime)
triggerCount.Store(neTypeAndId, validTimes)
}
if int64(len(validTimes)) >= triggerMax {
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")
return sysCpuUsage, sysMemUsage, sysDiskUsage
}

View File

@@ -10,29 +10,22 @@ import (
"be.ems/src/framework/cron"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
"github.com/tsmask/go-oam"
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"
oamService "be.ems/src/modules/oam/service"
)
var NewProcessor = &NeAlarmStateCheckLicenseProcessor{
neConfigBackupService: neService.NewNeConfigBackup,
neInfoService: neService.NewNeInfo,
alarmService: neDataService.NewAlarm,
wsSendService: wsService.NewWSSend,
count: 0,
neInfoService: neService.NewNeInfo,
count: 0,
}
// NeAlarmStateCheckLicenseProcessor 网元告警License到期检查
type NeAlarmStateCheckLicenseProcessor struct {
neConfigBackupService *neService.NeConfigBackup // 网元配置文件备份记录服务
neInfoService *neService.NeInfo // 网元信息服务
alarmService *neDataService.Alarm // 告警信息服务
wsSendService *wsService.WSSend // ws发送服务
count int // 执行次数
neInfoService *neService.NeInfo // 网元信息服务
count int // 执行次数
}
// alarmParams 告警参数
@@ -83,60 +76,36 @@ func (s *NeAlarmStateCheckLicenseProcessor) Execute(data any) (any, error) {
}
// 检查状态
err := s.serverState(neInfo.ServerState, params.DayLt)
err := s.cheackState(neInfo.ServerState, params.DayLt)
if err == nil {
continue
}
if params.AddInfo != "" {
params.AddInfo = params.AddInfo + ", " + err.Error()
addInfo := params.AddInfo
if addInfo != "" {
addInfo = addInfo + ", " + err.Error()
} else {
params.AddInfo = err.Error()
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)
// 创建告警
alarm := oam.Alarm{
NeUid: neInfo.RmUID, // 网元唯一标识
AlarmTime: time.Now().UnixMilli(), // 事件产生时间
AlarmId: params.AlarmId, // 告警ID 唯一,清除时对应
AlarmCode: constants.ALARM_LICENSE_CHECK, // 告警状态码
AlarmType: params.AlarmType, // 告警类型
AlarmTitle: params.AlarmTitle, // 告警标题
PerceivedSeverity: params.OrigSeverity, // 告警级别
AlarmStatus: oam.ALARM_STATUS_ACTIVE, // 告警状态
SpecificProblem: params.SpecificProblem, // 告警问题原因
SpecificProblemID: params.SpecificProblemID, // 告警问题原因ID
AddInfo: addInfo, // 告警辅助信息
LocationInfo: "NE License: Heartbeat", // 告警定位信息
}
// 活动告警进行清除
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"
if err = oamService.NewAlarm.Resolve(alarm); err == nil {
result[neInfo.RmUID] = "license alarm"
}
}
@@ -144,8 +113,8 @@ func (s *NeAlarmStateCheckLicenseProcessor) Execute(data any) (any, error) {
return result, nil
}
// serverState 网元状态
func (s NeAlarmStateCheckLicenseProcessor) serverState(state map[string]any, dayLt int64) error {
// cheackState 检查网元状态
func (s NeAlarmStateCheckLicenseProcessor) cheackState(state map[string]any, dayLt int64) error {
expire := fmt.Sprint(state["expire"])
if expire == "" || expire == "<nil>" || expire == "-" || expire == "2099-12-31" {
return nil
@@ -168,58 +137,3 @@ func (s NeAlarmStateCheckLicenseProcessor) serverState(state map[string]any, day
}
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,7 @@ package service
import (
"fmt"
"sync"
"testing"
"time"
@@ -14,7 +15,7 @@ import (
func TestInfo(t *testing.T) {
s := MonitorInfo{}
s.load(0.5) // 0.5 半分钟
s.Load(5 * time.Second) // 0.5 半分钟
fmt.Println(s)
select {}
@@ -28,34 +29,22 @@ type MonitorInfo struct {
}
// load 执行资源获取
func (m *MonitorInfo) load(interval float64) {
var itemBase MonitorBase
itemBase.CreateTime = time.Now().UnixMilli()
loadInfo, _ := load.Avg()
itemBase.CPULoad1 = loadInfo.Load1
itemBase.CPULoad5 = loadInfo.Load5
itemBase.CPULoad15 = loadInfo.Load15
totalPercent, _ := cpu.Percent(3*time.Second, false)
if len(totalPercent) > 0 {
itemBase.CPU = totalPercent[0]
}
cpuCount, _ := cpu.Counts(false)
cpuAvg := (float64(cpuCount*2) * 0.75) * 100
itemBase.LoadUsage = 0
if cpuAvg > 0 {
itemBase.LoadUsage = loadInfo.Load1 / cpuAvg
}
memoryInfo, _ := mem.VirtualMemory()
itemBase.Memory = memoryInfo.UsedPercent
m.MonitorBase = itemBase
// 求平均
m.MonitorIO = loadDiskIO(interval)
m.MonitorNetwork = loadNetIO(interval)
func (m *MonitorInfo) Load(duration time.Duration) {
var wg sync.WaitGroup
wg.Add(3)
go func() {
defer wg.Done()
m.MonitorBase = loadCPUMem(duration)
}()
go func() {
defer wg.Done()
m.MonitorIO = loadDiskIO(duration)
}()
go func() {
defer wg.Done()
m.MonitorNetwork = loadNetIO(duration)
}()
wg.Wait()
}
// MonitorBase 监控_基本信息 monitor_base
@@ -73,25 +62,49 @@ type MonitorBase struct {
type MonitorIO struct {
CreateTime int64 `json:"createTime"` // 创建时间
Name string `json:"name"` // 磁盘名
Read int64 `json:"read"` // 读取K
Write int64 `json:"write"` // 写入K
Count int64 `json:"count"` // 次数
Time int64 `json:"time"` // 耗时
Read uint64 `json:"read"` // 读取 Bytes
Write uint64 `json:"write"` // 写入 Bytes
Count uint64 `json:"count"` // 次数
Time uint64 `json:"time"` // 耗时
}
// MonitorNetwork 监控_网络IO monitor_network
type MonitorNetwork struct {
CreateTime int64 `json:"createTime"` // 创建时间
Name string `json:"name"` // 网卡名
Up float64 `json:"up"` // 上行
Down float64 `json:"down"` // 下行
CreateTime int64 `json:"createTime"` // 创建时间
Name string `json:"name"` // 网卡名
Up uint64 `json:"up"` // 上行 bytes
Down uint64 `json:"down"` // 下行 bytes
}
// loadCPUMem CPU内存使用率interval表示采集的平均值分钟
func loadCPUMem(duration time.Duration) MonitorBase {
var itemBase MonitorBase
itemBase.CreateTime = time.Now().UnixMilli()
loadInfo, _ := load.Avg()
itemBase.CPULoad1 = loadInfo.Load1
itemBase.CPULoad5 = loadInfo.Load5
itemBase.CPULoad15 = loadInfo.Load15
totalPercent, _ := cpu.Percent(duration, false)
if len(totalPercent) > 0 {
itemBase.CPU = totalPercent[0]
}
if cpuCount, _ := cpu.Counts(false); cpuCount > 0 {
itemBase.LoadUsage = loadInfo.Load1 / float64(cpuCount)
} else {
itemBase.LoadUsage = 0
}
memoryInfo, _ := mem.VirtualMemory()
itemBase.Memory = memoryInfo.UsedPercent
return itemBase
}
// loadDiskIO 磁盘读写interval表示采集的平均值分钟
func loadDiskIO(interval float64) []MonitorIO {
func loadDiskIO(duration time.Duration) []MonitorIO {
ioStat, _ := disk.IOCounters()
time.Sleep(time.Duration(interval) * time.Minute)
time.Sleep(duration)
ioStat2, _ := disk.IOCounters()
var ioList []MonitorIO
@@ -104,32 +117,24 @@ func loadDiskIO(interval float64) []MonitorIO {
itemIO.Name = io1.Name
if io2.ReadBytes != 0 && io1.ReadBytes != 0 && io2.ReadBytes > io1.ReadBytes {
itemIO.Read = int64(float64(io2.ReadBytes-io1.ReadBytes) / interval / 60)
itemIO.Read = io2.ReadBytes - io1.ReadBytes
}
if io2.WriteBytes != 0 && io1.WriteBytes != 0 && io2.WriteBytes > io1.WriteBytes {
itemIO.Write = int64(float64(io2.WriteBytes-io1.WriteBytes) / interval / 60)
itemIO.Write = io2.WriteBytes - io1.WriteBytes
}
if io2.ReadCount != 0 && io1.ReadCount != 0 && io2.ReadCount > io1.ReadCount {
itemIO.Count = int64(float64(io2.ReadCount-io1.ReadCount) / interval / 60)
itemIO.Count = io2.ReadCount - io1.ReadCount
}
writeCount := int64(0)
if io2.WriteCount != 0 && io1.WriteCount != 0 && io2.WriteCount > io1.WriteCount {
writeCount = int64(float64(io2.WriteCount-io1.WriteCount) / interval * 60)
}
if writeCount > itemIO.Count {
itemIO.Count = writeCount
itemIO.Count += io2.WriteCount - io1.WriteCount
}
if io2.ReadTime != 0 && io1.ReadTime != 0 && io2.ReadTime > io1.ReadTime {
itemIO.Time = int64(float64(io2.ReadTime-io1.ReadTime) / interval / 60)
itemIO.Time = io2.ReadTime - io1.ReadTime
}
writeTime := int64(0)
if io2.WriteTime != 0 && io1.WriteTime != 0 && io2.WriteTime > io1.WriteTime {
writeTime = int64(float64(io2.WriteTime-io1.WriteTime) / interval / 60)
}
if writeTime > itemIO.Time {
itemIO.Time = writeTime
itemIO.Time += io2.WriteTime - io1.WriteTime
}
ioList = append(ioList, itemIO)
break
@@ -140,7 +145,7 @@ func loadDiskIO(interval float64) []MonitorIO {
}
// loadNetIO 网络接口包括虚拟接口interval表示采集的平均值分钟
func loadNetIO(interval float64) []MonitorNetwork {
func loadNetIO(duration time.Duration) []MonitorNetwork {
// 获取当前时刻
netStat, _ := net.IOCounters(true)
netStatAll, _ := net.IOCounters(false)
@@ -148,7 +153,7 @@ func loadNetIO(interval float64) []MonitorNetwork {
netStatList = append(netStatList, netStat...)
netStatList = append(netStatList, netStatAll...)
time.Sleep(time.Duration(interval) * time.Minute)
time.Sleep(duration)
// 获取结束时刻
netStat2, _ := net.IOCounters(true)
@@ -168,10 +173,10 @@ func loadNetIO(interval float64) []MonitorNetwork {
// 如果结束时刻发送字节数和当前时刻发送字节数都不为零,并且结束时刻发送字节数大于当前时刻发送字节数
if net2.BytesSent != 0 && net1.BytesSent != 0 && net2.BytesSent > net1.BytesSent {
itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / interval / 60
itemNet.Up = net2.BytesSent - net1.BytesSent
}
if net2.BytesRecv != 0 && net1.BytesRecv != 0 && net2.BytesRecv > net1.BytesRecv {
itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / interval / 60
itemNet.Down = net2.BytesRecv - net1.BytesRecv
}
netList = append(netList, itemNet)
break

View File

@@ -199,9 +199,10 @@ func (s SysJob) ExportData(rows []model.SysJob, fileName string) (string, error)
}
}
misfirePolicy := "放弃执行"
if row.MisfirePolicy == "1" {
switch row.MisfirePolicy {
case "1":
misfirePolicy = "立即执行"
} else if row.MisfirePolicy == "2" {
case "2":
misfirePolicy = "执行一次"
}
concurrent := "禁止"

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"time"
"be.ems/src/framework/constants"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
@@ -42,7 +43,7 @@ type AlarmController struct {
// @Param pvFlag query string false "PV Flag" Enums(PNF,VNF)
// @Param alarmCode query string false "alarm status code"
// @Param alarmType query string false "Alarm type Communication alarms=1, Equipment alarms=2, Processing faults=3, Environmental alarms=4, Quality of service alarms=5" Enums(1,2,3,4,5)
// @Param alarmStatus query string false "Alarm status 0:clear, 1:active" Enums(0,1)
// @Param alarmStatus query string false "Alarm status Clear Active" Enums(0,1)
// @Param origSeverity query string false "Alarm Type 1: Critical, 2: Major, 3: Minor, 4: Warning" Enums(1,2,3,4)
// @Param sortField query string false "Sort fields, fill in result fields" default(event_time)
// @Param sortOrder query string false "Sort by ascending or descending order, asc desc" default(asc)
@@ -107,7 +108,7 @@ func (s AlarmController) Clear(c *gin.Context) {
}
clearUser := reqctx.LoginUserToUserName(c)
rows, err := s.alarmService.AlarmClearByIds(body.Ids, clearUser)
rows, err := s.alarmService.ClearByIds(body.Ids, clearUser, constants.ALARM_CLEAR_TYPE_MANUAL_CLEAR)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
@@ -130,7 +131,7 @@ func (s AlarmController) Ack(c *gin.Context) {
}
ackUser := reqctx.LoginUserToUserName(c)
rows, err := s.alarmService.AlarmAckByIds(body.Ids, ackUser, body.AckState)
rows, err := s.alarmService.AckByIds(body.Ids, ackUser, constants.ALARM_ACK_STATE_ACK)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
@@ -138,6 +139,58 @@ func (s AlarmController) Ack(c *gin.Context) {
c.JSON(200, resp.OkData(rows))
}
// 告警级别数量
//
// GET /count/severity
func (s AlarmController) CountSeverity(c *gin.Context) {
var query struct {
AlarmStatus string `json:"alarmStatus" form:"alarmStatus" binding:"required,oneof=Clear Active"` // 告警状态
}
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
}
data := s.alarmService.CountSeverity(query.AlarmStatus)
c.JSON(200, resp.OkData(data))
}
// 告警类别数量
//
// GET /count/type
func (s AlarmController) CountType(c *gin.Context) {
var query struct {
AlarmStatus string `json:"alarmStatus" form:"alarmStatus" binding:"required,oneof=Clear Active"` // 告警状态
}
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
}
data := s.alarmService.CountType(query.AlarmStatus)
c.JSON(200, resp.OkData(data))
}
// 告警状态前几排名
//
// GET /count/ne
func (s AlarmController) CountNe(c *gin.Context) {
var query struct {
AlarmStatus string `json:"alarmStatus" form:"alarmStatus" binding:"required,oneof=Clear Active"` // 告警状态
Top int `json:"top" form:"top" 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
}
data := s.alarmService.CountNe(query.AlarmStatus, query.Top)
c.JSON(200, resp.OkData(data))
}
// 告警列表导出
//
// GET /export

View File

@@ -0,0 +1,183 @@
package controller
import (
"fmt"
"strconv"
"strings"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neService "be.ems/src/modules/network_element/service"
"github.com/gin-gonic/gin"
)
// 实例化控制层 KPICController 结构体
var NewKPIC = &KPICController{
neInfoService: neService.NewNeInfo,
kpicReportService: neDataService.NewKpiCReport,
}
// 性能统计
//
// PATH /kpic
type KPICController struct {
neInfoService *neService.NeInfo // 网元信息服务
kpicReportService *neDataService.KpiCReport // 指标统计服务
}
// 获取统计数据
//
// GET /data
//
// @Tags network_data/kpi
// @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 beginTime query number true "begin time (timestamped milliseconds)" default(1729162507596)
// @Param endTime query number true "end time (timestamped milliseconds)" default(1729164187611)
// @Param interval query number true "interval" Enums(5,10,15,30,60,300,600,900,1800,3600) default(60)
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Access to statistical data
// @Description Access to statistical data
// @Router /neData/kpic/data [get]
func (s KPICController) KPIData(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var querys model.KPICQuery
if err := c.ShouldBindQuery(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
// 查询网元获取IP
neInfo := s.neInfoService.FindByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
// 查询数据
kpiData := s.kpicReportService.FindData(querys)
c.JSON(200, resp.OkData(kpiData))
}
// 自定义标题列表
//
// GET /titlelist
func (s KPICController) ListTitle(c *gin.Context) {
query := reqctx.QueryMap(c)
if v, ok := query["status"]; ok && v == "" {
query["status"] = "1"
}
rows, total := s.kpicReportService.TitleFindByPage(query)
c.JSON(200, resp.OkData(map[string]any{"total": total, "rows": rows}))
}
// 自定义标题新增
//
// POST /title
func (s KPICController) AddTitle(c *gin.Context) {
var body model.KpiCTitle
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
}
// 校验指标是否存在
kpicTitles := s.kpicReportService.TitleFind(model.KpiCTitle{
NeType: body.NeType,
KpiId: body.KpiId,
})
if len(kpicTitles) > 0 {
for _, v := range kpicTitles {
if v.Status == "2" {
c.JSON(200, resp.ErrMsg("custom indicator already exist"))
return
}
}
}
// 生成自定义指标ID
if body.KpiId == "" {
body.KpiId = fmt.Sprintf("%s.C.01", strings.ToUpper(body.NeType))
} else {
// 网元类型最后指标ID
lastKpiId := s.kpicReportService.TitleLastKPIId(body.NeType)
if lastKpiId != "" {
// title like AMF.C.01 截断 .C. 并获取后面的数字部分
parts := strings.Split(lastKpiId, ".C.")
if len(parts) == 2 {
numStr := parts[1]
if num, err := strconv.Atoi(numStr); err == nil {
num++ // 数字加 1
// 转换为前面补零的 2 位字符串
body.KpiId = fmt.Sprintf("%s.C.%02d", strings.ToUpper(body.NeType), num)
}
}
}
}
body.CreatedBy = reqctx.LoginUserToUserName(c)
insertId := s.kpicReportService.TitleInsert(body)
if insertId > 0 {
c.JSON(200, resp.Ok(nil))
return
}
c.JSON(200, resp.Err(nil))
}
// 自定义标题修改
//
// PUT /title
func (s KPICController) EditTitle(c *gin.Context) {
var body model.KpiCTitle
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
}
rows := s.kpicReportService.TitleUpdate(body)
if rows > 0 {
c.JSON(200, resp.Ok(nil))
return
}
c.JSON(200, resp.Err(nil))
}
// 自定义标题删除
//
// DELETE /title/:id
func (s KPICController) RemoveTitle(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
id := c.Query("id")
if id == "" {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: id is empty"))
return
}
// 处理字符转id数组后去重
uniqueIDs := parse.RemoveDuplicatesToArray(id, ",")
// 转换成int64数组类型
ids := make([]int64, 0)
for _, v := range uniqueIDs {
ids = append(ids, parse.Number(v))
}
rows, err := s.kpicReportService.TitleDeleteByIds(ids)
if err != nil {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
msg := i18n.TTemplate(language, "app.common.deleteSuccess", map[string]any{"num": rows})
c.JSON(200, resp.OkMsg(msg))
}

View File

@@ -72,7 +72,7 @@ func (s NBStateController) List(c *gin.Context) {
// 历史记录列表导出
//
// POST /export
// GET /export
//
// @Tags network_data/amf,network_data/mme
// @Accept json
@@ -82,7 +82,7 @@ func (s NBStateController) List(c *gin.Context) {
// @Security TokenAuth
// @Summary Base Station Status List Export
// @Description Base Station Status List Export
// @Router /nb-state/export [post]
// @Router /nb-state/export [get]
func (s NBStateController) Export(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制

View File

@@ -8,7 +8,6 @@ import (
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neService "be.ems/src/modules/network_element/service"
@@ -19,15 +18,15 @@ import (
// 实例化控制层 AMFController 结构体
var NewAMF = &AMFController{
neInfoService: neService.NewNeInfo,
ueEventService: neDataService.NewUEEventAMF,
ueEventService: neDataService.NewUEEvent,
}
// 网元AMF
//
// PATH /amf
type AMFController struct {
neInfoService *neService.NeInfo // 网元信息服务
ueEventService *neDataService.UEEventAMF // UE会话事件服务
neInfoService *neService.NeInfo // 网元信息服务
ueEventService *neDataService.UEEvent // UE会话事件服务
}
// UE会话列表
@@ -49,23 +48,20 @@ type AMFController struct {
// @Router /neData/amf/ue/list [get]
func (s *AMFController) UEList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var querys model.UEEventAMFQuery
if err := c.ShouldBindQuery(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
query := reqctx.QueryMap(c)
// 限制导出数据集
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元获取IP
neInfo := s.neInfoService.FindByNeTypeAndNeID("AMF", querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
// 查询数据
rows, total := s.ueEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.ueEventService.FindByPage(neInfo.NeType, query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}
@@ -98,7 +94,7 @@ func (s *AMFController) UERemove(c *gin.Context) {
ids = append(ids, parse.Number(v))
}
rows, err := s.ueEventService.DeleteByIds(ids)
rows, err := s.ueEventService.DeleteByIds("AMF", ids)
if err != nil {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
@@ -109,7 +105,7 @@ func (s *AMFController) UERemove(c *gin.Context) {
// UE会话列表导出
//
// POST /ue/export
// GET /ue/export
//
// @Tags network_data/amf
// @Accept json
@@ -119,28 +115,23 @@ func (s *AMFController) UERemove(c *gin.Context) {
// @Security TokenAuth
// @Summary UE Session List Export
// @Description UE Session List Export
// @Router /neData/amf/ue/export [post]
// @Router /neData/amf/ue/export [get]
func (s *AMFController) UEExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
var querys model.UEEventAMFQuery
if err := c.ShouldBindBodyWithJSON(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
query := reqctx.QueryMap(c)
// 限制导出数据集
if querys.PageSize > 10000 {
querys.PageSize = 10000
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元获取IP
neInfo := s.neInfoService.FindByNeTypeAndNeID("AMF", querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
rows, total := s.ueEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.ueEventService.FindByPage(neInfo.NeType, query)
if total == 0 {
// 导出数据记录为空
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
@@ -150,7 +141,7 @@ func (s *AMFController) UEExport(c *gin.Context) {
// 导出文件名称
fileName := fmt.Sprintf("amf_ue_event_export_%d_%d.xlsx", len(rows), time.Now().UnixMilli())
// 导出数据表格
saveFilePath, err := s.ueEventService.ExportXlsx(rows, fileName, language)
saveFilePath, err := s.ueEventService.ExportAMF(rows, fileName, language)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return

View File

@@ -0,0 +1,317 @@
package controller
import (
"encoding/json"
"fmt"
"strconv"
"time"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"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"
"github.com/gin-gonic/gin"
)
const (
neType = "CBC" // 网元类型
)
// 实例化控制层 CBCController 结构体
var NewCBC = &CBCController{
neInfoService: neService.NewNeInfo,
neCBCMessageService: neDataService.NewCBCMessage,
}
// 网元CBC
type CBCController struct {
neInfoService *neService.NeInfo // 网元信息服务
neCBCMessageService *neDataService.CBCMessage // CBC消息服务
}
func (m *CBCController) List(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
neId := c.Query("neId")
if neId == "" {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
var query model.CBCMessageQuery
if err := c.ShouldBindQuery(&query); err != nil {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
query.NeType = neType
query.NeId = neId
data, total, err := neDataService.NewCBCMessage.SelectByPage(query)
if err != nil {
c.JSON(500, resp.ErrMsg(err.Error()))
return
}
// 转换数据格式,确保 MessageJson 正确序列化
processedData := make([]map[string]interface{}, len(data))
for i, msg := range data {
var messageJson interface{}
if len(msg.MessageJson) > 0 {
// 尝试解析为 JSON 对象
if err := json.Unmarshal(msg.MessageJson, &messageJson); err != nil {
// 如果解析失败,就作为字符串
messageJson = string(msg.MessageJson)
}
}
processedData[i] = map[string]interface{}{
"id": msg.Id,
"neType": msg.NeType,
"neId": msg.NeId,
"messageJson": messageJson, // 这里是解析后的 JSON 对象
"status": msg.Status.Enum(),
"detail": msg.Detail,
"createdAt": msg.CreatedAt,
"updatedAt": msg.UpdatedAt,
}
}
c.JSON(200, resp.Ok(gin.H{
"total": total,
"data": processedData,
}))
}
// Update 更新CB消息
func (m *CBCController) Insert(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 绑定请求体
var msg model.CBCMessage
msg.NeType = neType
msg.NeId = c.Query("neId")
msg.Status = model.CBCEventStatusInactive // 默认状态为 INACTIVE
if msg.NeId == "" {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
now := time.Now().UnixMilli()
msg.CreatedAt = now
msg.UpdatedAt = now
// 使用 ShouldBindBodyWithJSON 读取请求体
var jsonData interface{}
if err := c.ShouldBindBodyWithJSON(&jsonData); err != nil {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 将绑定的数据转换为 JSON
jsonBytes, err := json.Marshal(jsonData)
if err != nil {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
msg.MessageJson = json.RawMessage(jsonBytes)
if err := neDataService.NewCBCMessage.Insert(msg); err != nil {
c.JSON(500, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// Update 更新CB消息
func (m *CBCController) Update(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 获取路径参数
messageId := c.Param("id")
if messageId == "" {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
id, err := strconv.ParseInt(messageId, 10, 64)
if err != nil {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 直接读取body为json.RawMessage
var jsonData interface{}
if err := c.ShouldBindBodyWithJSON(&jsonData); err != nil {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 将绑定的数据转换为 JSON
jsonBytes, err := json.Marshal(jsonData)
if err != nil {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
messageJson := json.RawMessage(jsonBytes)
if err := neDataService.NewCBCMessage.Update(id, messageJson); err != nil {
c.JSON(500, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// UpdateStatus 更新CB消息状态
// 这里的 neId 参数是为了兼容性,实际更新状态时不需要使用它
// 但为了保持与原有接口一致,仍然保留该参数
// 如果需要根据 neId 进行特定的逻辑处理,可以在服务层实现
// 但在当前实现中neId 仅用于验证请求的有效性
// 实际的状态更新逻辑不依赖于 neId
// 该接口用于更新 CB 消息的状态,状态值通过查询参数传递
// 例如PUT /:neId/message/status?status=ACTIVE
func (m *CBCController) UpdateStatus(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
neId := c.Query("neId")
status := c.Param("status")
if neId == "" || status == "" {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
messageId := c.Param("id")
if messageId != "" {
id, err := strconv.ParseInt(messageId, 10, 64)
if err != nil {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 如果提供了 messageId则更新特定消息的状态
if err := neDataService.NewCBCMessage.UpdateStatus(id, status); err != nil {
c.JSON(500, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
return
}
// 如果没有提供 messageId则更新所有消息的状态
if err := neDataService.NewCBCMessage.UpdateStatusByNeId(neId, status); err != nil {
c.JSON(500, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// Delete 删除CB消息
func (m *CBCController) Delete(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 获取路径参数
messageId := c.Param("id")
if messageId == "" {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
id, err := strconv.ParseInt(messageId, 10, 64)
if err != nil {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
if err := neDataService.NewCBCMessage.Delete(id); err != nil {
c.JSON(500, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// ListById 根据ID获取CB消息
func (m *CBCController) ListById(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 获取路径参数
idStr := c.Param("id")
if idStr == "" {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
data, err := neDataService.NewCBCMessage.SelectById(id)
if err != nil {
c.JSON(500, resp.ErrMsg(err.Error()))
return
}
if data == nil {
c.JSON(404, resp.CodeMsg(404, i18n.TKey(language, "app.common.err404")))
return
}
// 转换数据格式,确保 MessageJson 正确序列化
var messageJson interface{}
if len(data.MessageJson) > 0 {
// 尝试解析为 JSON 对象
if err := json.Unmarshal(data.MessageJson, &messageJson); err != nil {
// 如果解析失败,就作为字符串
messageJson = string(data.MessageJson)
}
}
processedData := map[string]interface{}{
"id": data.Id,
"neType": data.NeType,
"neId": data.NeId,
"messageJson": messageJson, // 这里是解析后的 JSON 对象
"status": data.Status.Enum(),
"detail": data.Detail,
"createdAt": data.CreatedAt,
"updatedAt": data.UpdatedAt,
}
c.JSON(200, resp.Ok(gin.H{
"data": processedData,
}))
}
func (m *CBCController) Export(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
var query model.CBCMessageQuery
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.PageSize > 10000 {
query.PageSize = 10000
}
// 查询数据
rows, total, err := m.neCBCMessageService.SelectByPage(query)
if err != nil {
c.JSON(500, resp.ErrMsg(err.Error()))
return
}
if total == 0 {
// 导出数据记录为空
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
return
}
// 导出文件名称
fileName := fmt.Sprintf("cbc_message_export_%d_%d.xlsx", len(rows), time.Now().UnixMilli())
// 导出数据表格
saveFilePath, err := m.neCBCMessageService.ExportXlsx(rows, fileName, language)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.FileAttachment(saveFilePath, fileName)
}

View File

@@ -8,7 +8,6 @@ import (
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neService "be.ems/src/modules/network_element/service"
@@ -19,15 +18,15 @@ import (
// 实例化控制层 IMSController 结构体
var NewIMS = &IMSController{
neInfoService: neService.NewNeInfo,
cdrEventService: neDataService.NewCDREventIMS,
cdrEventService: neDataService.NewCDREvent,
}
// 网元IMS
//
// PATH /ims
type IMSController struct {
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREventIMS // CDR会话事件服务
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREvent // CDR会话事件服务
}
// CDR会话列表
@@ -50,23 +49,20 @@ type IMSController struct {
// @Router /neData/ims/cdr/list [get]
func (s *IMSController) CDRList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var querys model.CDREventIMSQuery
if err := c.ShouldBindQuery(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
query := reqctx.QueryMap(c)
// 限制导出数据集
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
// 查询数据
rows, total := s.cdrEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(neInfo.NeType, query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}
@@ -99,7 +95,7 @@ func (s *IMSController) CDRRemove(c *gin.Context) {
ids = append(ids, parse.Number(v))
}
rows, err := s.cdrEventService.DeleteByIds(ids)
rows, err := s.cdrEventService.DeleteByIds("IMS", ids)
if err != nil {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
@@ -110,7 +106,7 @@ func (s *IMSController) CDRRemove(c *gin.Context) {
// CDR会话列表导出
//
// POST /cdr/export
// GET /cdr/export
//
// @Tags network_data/ims
// @Accept json
@@ -120,28 +116,23 @@ func (s *IMSController) CDRRemove(c *gin.Context) {
// @Security TokenAuth
// @Summary CDR Session List Export
// @Description CDR Session List Export
// @Router /neData/ims/cdr/export [post]
// @Router /neData/ims/cdr/export [get]
func (s *IMSController) CDRExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
var querys model.CDREventIMSQuery
if err := c.ShouldBindBodyWithJSON(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
query := reqctx.QueryMap(c)
// 限制导出数据集
if querys.PageSize > 10000 {
querys.PageSize = 10000
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(neInfo.NeType, query)
if total == 0 {
// 导出数据记录为空
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
@@ -151,7 +142,7 @@ func (s *IMSController) CDRExport(c *gin.Context) {
// 导出文件名称
fileName := fmt.Sprintf("ims_cdr_event_export_%d_%d.xlsx", len(rows), time.Now().UnixMilli())
// 导出数据表格
saveFilePath, err := s.cdrEventService.ExportXlsx(rows, fileName, language)
saveFilePath, err := s.cdrEventService.ExportIMS(rows, fileName, language)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return

View File

@@ -8,7 +8,6 @@ import (
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neService "be.ems/src/modules/network_element/service"
@@ -19,15 +18,15 @@ import (
// 实例化控制层 MMEController 结构体
var NewMME = &MMEController{
neInfoService: neService.NewNeInfo,
ueEventService: neDataService.NewUEEventMME,
ueEventService: neDataService.NewUEEvent,
}
// 网元MME
//
// PATH /mme
type MMEController struct {
neInfoService *neService.NeInfo // 网元信息服务
ueEventService *neDataService.UEEventMME // UE会话事件服务
neInfoService *neService.NeInfo // 网元信息服务
ueEventService *neDataService.UEEvent // UE会话事件服务
}
// UE会话列表
@@ -49,23 +48,20 @@ type MMEController struct {
// @Router /neData/mme/ue/list [get]
func (s *MMEController) UEList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var querys model.UEEventMMEQuery
if err := c.ShouldBindQuery(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
query := reqctx.QueryMap(c)
// 限制导出数据集
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元获取IP
neInfo := s.neInfoService.FindByNeTypeAndNeID("MME", querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
// 查询数据
rows, total := s.ueEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.ueEventService.FindByPage(neInfo.NeType, query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}
@@ -98,7 +94,7 @@ func (s *MMEController) UERemove(c *gin.Context) {
ids = append(ids, parse.Number(v))
}
rows, err := s.ueEventService.DeleteByIds(ids)
rows, err := s.ueEventService.DeleteByIds("MME", ids)
if err != nil {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
@@ -109,7 +105,7 @@ func (s *MMEController) UERemove(c *gin.Context) {
// UE会话列表导出
//
// POST /ue/export
// GET /ue/export
//
// @Tags network_data/mme
// @Accept json
@@ -119,28 +115,23 @@ func (s *MMEController) UERemove(c *gin.Context) {
// @Security TokenAuth
// @Summary UE Session List Export
// @Description UE Session List Export
// @Router /neData/mme/ue/export [post]
// @Router /neData/mme/ue/export [get]
func (s *MMEController) UEExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
var querys model.UEEventMMEQuery
if err := c.ShouldBindBodyWithJSON(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
query := reqctx.QueryMap(c)
// 限制导出数据集
if querys.PageSize > 10000 {
querys.PageSize = 10000
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元获取IP
neInfo := s.neInfoService.FindByNeTypeAndNeID("MME", querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
rows, total := s.ueEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.ueEventService.FindByPage(neInfo.NeType, query)
if total == 0 {
// 导出数据记录为空
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
@@ -150,7 +141,7 @@ func (s *MMEController) UEExport(c *gin.Context) {
// 导出文件名称
fileName := fmt.Sprintf("mme_ue_event_export_%d_%d.xlsx", len(rows), time.Now().UnixMilli())
// 导出数据表格
saveFilePath, err := s.ueEventService.ExportXlsx(rows, fileName, language)
saveFilePath, err := s.ueEventService.ExportMME(rows, fileName, language)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return

View File

@@ -0,0 +1,70 @@
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"
)
// 实例化控制层 N3IWFController 结构体
var NewN3IWF = &N3IWFController{
neInfoService: neService.NewNeInfo,
}
// 网元N3IWF
//
// PATH /n3iwf
type N3IWFController struct {
neInfoService *neService.NeInfo // 网元信息服务
}
// 在线订阅用户列表信息
//
// GET /sub/list
//
// @Tags network_data/n3iwf
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Param imsi query string false "imsi"
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Online session user list information
// @Description Online session user list information
// @Router /neData/n3iwf/sub/list [get]
func (s N3IWFController) SubUserList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var query struct {
NeId string `form:"neId" binding:"required"`
IMSI string `form:"imsi"`
}
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
}
// 查询网元信息
neInfo := s.neInfoService.FindByNeTypeAndNeID("N3IWF", query.NeId)
if neInfo.NeId != query.NeId || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
data, err := neFetchlink.N3IWFSubInfoList(neInfo, map[string]string{
"imsi": query.IMSI,
})
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.OkData(data))
}

View File

@@ -0,0 +1,107 @@
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"
)
// 实例化控制层 NSSFController 结构体
var NewNSSF = &NSSFController{
neInfoService: neService.NewNeInfo,
}
// 网元NSSF
//
// PATH /NSSF
type NSSFController struct {
neInfoService *neService.NeInfo // 网元信息服务
}
// 在线订阅用户列表信息
//
// GET /sub/list
//
// @Tags network_data/nssf
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Online session user list information
// @Description Online session user list information
// @Router /neData/nssf/sub/list [get]
func (s NSSFController) SubUserList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var query struct {
NeId string `form:"neId" 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
}
// 查询网元信息
neInfo := s.neInfoService.FindByNeTypeAndNeID("NSSF", query.NeId)
if neInfo.NeId != query.NeId || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
data, err := neFetchlink.NSSFSubInfoList(neInfo)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.OkData(data))
}
// 可用AMF列表信息
//
// GET /amf/list
//
// @Tags network_data/nssf
// @Accept json
// @Produce json
// @Param neId query string true "NE ID" default(001)
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Online session user list information
// @Description Online session user list information
// @Router /neData/nssf/amf/list [get]
func (s NSSFController) AvailableList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var query struct {
NeId string `form:"neId" 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
}
// 查询网元信息
neInfo := s.neInfoService.FindByNeTypeAndNeID("NSSF", query.NeId)
if neInfo.NeId != query.NeId || neInfo.IP == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
data, err := neFetchlink.NSSFAvailableAMFList(neInfo)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.OkData(data))
}

View File

@@ -8,7 +8,6 @@ import (
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neService "be.ems/src/modules/network_element/service"
"github.com/gin-gonic/gin"
@@ -17,7 +16,7 @@ import (
// 实例化控制层 SGWCController 结构体
var NewSGWC = &SGWCController{
neInfoService: neService.NewNeInfo,
cdrEventService: neDataService.NewCDREventSGWC,
cdrEventService: neDataService.NewCDREvent,
UDMExtendService: neDataService.NewUDMExtend,
}
@@ -25,9 +24,9 @@ var NewSGWC = &SGWCController{
//
// PATH /sgwc
type SGWCController struct {
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREventSGWC // CDR会话事件服务
UDMExtendService *neDataService.UDMExtend // UDM用户信息服务
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREvent // CDR会话事件服务
UDMExtendService *neDataService.UDMExtend // UDM用户信息服务
}
// CDR会话列表
@@ -50,23 +49,20 @@ type SGWCController struct {
// @Router /neData/sgwc/cdr/list [get]
func (s *SGWCController) CDRList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var querys model.CDREventSGWCQuery
if err := c.ShouldBindQuery(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
query := reqctx.QueryMap(c)
// 限制导出数据集
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
// 查询数据
rows, total := s.cdrEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(neInfo.NeType, query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}
@@ -99,7 +95,7 @@ func (s *SGWCController) CDRRemove(c *gin.Context) {
ids = append(ids, parse.Number(v))
}
rows, err := s.cdrEventService.DeleteByIds(ids)
rows, err := s.cdrEventService.DeleteByIds("SGWC", ids)
if err != nil {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
@@ -123,25 +119,20 @@ func (s *SGWCController) CDRRemove(c *gin.Context) {
// @Router /neData/sgwc/cdr/export [post]
func (s *SGWCController) CDRExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
var querys model.CDREventSGWCQuery
if err := c.ShouldBindBodyWithJSON(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
query := reqctx.QueryMap(c)
// 限制导出数据集
if querys.PageSize > 10000 {
querys.PageSize = 10000
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(neInfo.NeType, query)
if total == 0 {
// 导出数据记录为空
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
@@ -151,7 +142,7 @@ func (s *SGWCController) CDRExport(c *gin.Context) {
// 导出文件名称
fileName := fmt.Sprintf("sgwc_cdr_event_export_%d_%d.xlsx", len(rows), time.Now().UnixMilli())
// 导出数据表格
saveFilePath, err := s.cdrEventService.ExportXlsx(rows, fileName)
saveFilePath, err := s.cdrEventService.ExportSGWC(rows, fileName)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return

View File

@@ -9,7 +9,6 @@ import (
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neService "be.ems/src/modules/network_element/service"
@@ -20,7 +19,7 @@ import (
// 实例化控制层 SMFController 结构体
var NewSMF = &SMFController{
neInfoService: neService.NewNeInfo,
cdrEventService: neDataService.NewCDREventSMF,
cdrEventService: neDataService.NewCDREvent,
UDMExtendService: neDataService.NewUDMExtend,
}
@@ -28,9 +27,9 @@ var NewSMF = &SMFController{
//
// PATH /smf
type SMFController struct {
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREventSMF // CDR会话事件服务
UDMExtendService *neDataService.UDMExtend // UDM用户信息服务
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREvent // CDR会话事件服务
UDMExtendService *neDataService.UDMExtend // UDM用户信息服务
}
// CDR会话列表
@@ -52,23 +51,20 @@ type SMFController struct {
// @Router /neData/smf/cdr/list [get]
func (s *SMFController) CDRList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var querys model.CDREventSMFQuery
if err := c.ShouldBindQuery(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
query := reqctx.QueryMap(c)
// 限制导出数据集
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
// 查询数据
rows, total := s.cdrEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(neInfo.NeType, query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}
@@ -101,7 +97,7 @@ func (s *SMFController) CDRRemove(c *gin.Context) {
ids = append(ids, parse.Number(v))
}
rows, err := s.cdrEventService.DeleteByIds(ids)
rows, err := s.cdrEventService.DeleteByIds("SMF", ids)
if err != nil {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
@@ -112,7 +108,7 @@ func (s *SMFController) CDRRemove(c *gin.Context) {
// CDR会话列表导出
//
// POST /cdr/export
// GET /cdr/export
//
// @Tags network_data/smf
// @Accept json
@@ -122,28 +118,23 @@ func (s *SMFController) CDRRemove(c *gin.Context) {
// @Security TokenAuth
// @Summary CDR Session List Export
// @Description CDR Session List Export
// @Router /neData/smf/cdr/export [post]
// @Router /neData/smf/cdr/export [get]
func (s *SMFController) CDRExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
var querys model.CDREventSMFQuery
if err := c.ShouldBindBodyWithJSON(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
query := reqctx.QueryMap(c)
// 限制导出数据集
if querys.PageSize > 10000 {
querys.PageSize = 10000
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(neInfo.NeType, query)
if total == 0 {
// 导出数据记录为空
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
@@ -153,7 +144,7 @@ func (s *SMFController) CDRExport(c *gin.Context) {
// 导出文件名称
fileName := fmt.Sprintf("smf_cdr_event_export_%d_%d.xlsx", len(rows), time.Now().UnixMilli())
// 导出数据表格
saveFilePath, err := s.cdrEventService.ExportXlsx(rows, fileName)
saveFilePath, err := s.cdrEventService.ExportSMF(rows, fileName)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
@@ -219,7 +210,7 @@ func (s *SMFController) SubUserNum(c *gin.Context) {
// @Security TokenAuth
// @Summary Online session user list information
// @Description Online session user list information
// @Router /neData/smf/session/list [get]
// @Router /neData/smf/sub/list [get]
func (s *SMFController) SubUserList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var query struct {

View File

@@ -8,7 +8,6 @@ import (
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neService "be.ems/src/modules/network_element/service"
@@ -18,15 +17,15 @@ import (
// 实例化控制层 SMSCController 结构体
var NewSMSC = &SMSCController{
neInfoService: neService.NewNeInfo,
cdrEventService: neDataService.NewCDREventSMSC,
cdrEventService: neDataService.NewCDREvent,
}
// 网元SMSC
//
// PATH /smsc
type SMSCController struct {
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREventSMSC // CDR会话事件服务
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREvent // CDR会话事件服务
}
// CDR会话列表
@@ -49,23 +48,20 @@ type SMSCController struct {
// @Router /neData/smsc/cdr/list [get]
func (s *SMSCController) CDRList(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var querys model.CDREventSMSCQuery
if err := c.ShouldBindQuery(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
query := reqctx.QueryMap(c)
// 限制导出数据集
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
// 查询数据
rows, total := s.cdrEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(neInfo.NeType, query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}
@@ -98,7 +94,7 @@ func (s *SMSCController) CDRRemove(c *gin.Context) {
ids = append(ids, parse.Number(v))
}
rows, err := s.cdrEventService.DeleteByIds(ids)
rows, err := s.cdrEventService.DeleteByIds("SMSC", ids)
if err != nil {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
@@ -109,7 +105,7 @@ func (s *SMSCController) CDRRemove(c *gin.Context) {
// CDR会话列表导出
//
// POST /cdr/export
// GET /cdr/export
//
// @Tags network_data/smsc
// @Accept json
@@ -119,28 +115,23 @@ func (s *SMSCController) CDRRemove(c *gin.Context) {
// @Security TokenAuth
// @Summary CDR Session List Export
// @Description CDR Session List Export
// @Router /neData/smsc/cdr/export [post]
// @Router /neData/smsc/cdr/export [get]
func (s *SMSCController) CDRExport(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
var querys model.CDREventSMSCQuery
if err := c.ShouldBindBodyWithJSON(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
query := reqctx.QueryMap(c)
// 限制导出数据集
if querys.PageSize > 10000 {
querys.PageSize = 10000
pageSize := parse.Number(query["pageSize"])
if pageSize > 10000 {
query["pageSize"] = "10000"
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.FindByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
neInfo := s.neInfoService.FindByNeTypeAndNeID(query["neType"], query["neId"])
if neInfo.NeType == "" {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
querys.RmUID = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(querys)
query["rmUID"] = neInfo.RmUID
rows, total := s.cdrEventService.FindByPage(neInfo.NeType, query)
if total == 0 {
// 导出数据记录为空
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
@@ -150,7 +141,7 @@ func (s *SMSCController) CDRExport(c *gin.Context) {
// 导出文件名称
fileName := fmt.Sprintf("smsc_cdr_event_export_%d_%d.xlsx", len(rows), time.Now().UnixMilli())
// 导出数据表格
saveFilePath, err := s.cdrEventService.ExportXlsx(rows, fileName, language)
saveFilePath, err := s.cdrEventService.ExportSMSC(rows, fileName, language)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return

View File

@@ -77,8 +77,8 @@ func (s *UDMAuthController) ResetData(c *gin.Context) {
// @Router /neData/udm/auth/list [get]
func (s *UDMAuthController) List(c *gin.Context) {
query := reqctx.QueryMap(c)
total, rows := s.udmAuthService.FindByPage(query)
c.JSON(200, resp.OkData(map[string]any{"total": total, "rows": rows}))
rows, total := s.udmAuthService.FindByPage(query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}
// UDM鉴权用户信息
@@ -478,7 +478,7 @@ func (s *UDMAuthController) Export(c *gin.Context) {
}
query := reqctx.QueryMap(c)
total, rows := s.udmAuthService.FindByPage(query)
rows, total := s.udmAuthService.FindByPage(query)
if total == 0 {
// 导出数据记录为空
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))

View File

@@ -77,8 +77,8 @@ func (s *UDMSubController) ResetData(c *gin.Context) {
// @Router /neData/udm/sub/list [get]
func (s *UDMSubController) List(c *gin.Context) {
query := reqctx.QueryMap(c)
total, rows := s.udmSubService.FindByPage(query)
c.JSON(200, resp.OkData(map[string]any{"total": total, "rows": rows}))
rows, total := s.udmSubService.FindByPage(query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}
// UDM签约用户信息
@@ -484,7 +484,7 @@ func (s *UDMSubController) Export(c *gin.Context) {
}
query := reqctx.QueryMap(c)
total, rows := s.udmSubService.FindByPage(query)
rows, total := s.udmSubService.FindByPage(query)
if total == 0 {
// 导出数据记录为空
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))

View File

@@ -76,8 +76,8 @@ func (s *UDMVOIPController) ResetData(c *gin.Context) {
// @Router /neData/udm/voip/list [get]
func (s *UDMVOIPController) List(c *gin.Context) {
query := reqctx.QueryMap(c)
total, rows := s.udmVOIPService.FindByPage(query)
c.JSON(200, resp.OkData(map[string]any{"total": total, "rows": rows}))
rows, total := s.udmVOIPService.FindByPage(query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}
// UDMVOIP用户信息
@@ -416,7 +416,7 @@ func (s *UDMVOIPController) Export(c *gin.Context) {
}
query := reqctx.QueryMap(c)
total, rows := s.udmVOIPService.FindByPage(query)
rows, total := s.udmVOIPService.FindByPage(query)
if total == 0 {
// 导出数据记录为空
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))

View File

@@ -76,8 +76,8 @@ func (s *UDMVolteIMSController) ResetData(c *gin.Context) {
// @Router /neData/udm/volte-ims/list [get]
func (s *UDMVolteIMSController) List(c *gin.Context) {
query := reqctx.QueryMap(c)
total, rows := s.udmVolteIMSService.FindByPage(query)
c.JSON(200, resp.OkData(map[string]any{"total": total, "rows": rows}))
rows, total := s.udmVolteIMSService.FindByPage(query)
c.JSON(200, resp.OkData(map[string]any{"rows": rows, "total": total}))
}
// UDMVolteIMS用户信息
@@ -462,7 +462,7 @@ func (s *UDMVolteIMSController) Export(c *gin.Context) {
}
query := reqctx.QueryMap(c)
total, rows := s.udmVolteIMSService.FindByPage(query)
rows, total := s.udmVolteIMSService.FindByPage(query)
if total == 0 {
// 导出数据记录为空
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))

View File

@@ -8,26 +8,26 @@ type Alarm struct {
NeName string `json:"neName" gorm:"column:ne_name"` // 网元名称
Province string `json:"province" gorm:"column:province"` // 网元省份地域
PvFlag string `json:"pvFlag" gorm:"column:pv_flag"` // 网元标识虚拟化标识
AlarmSeq int64 `json:"alarmSeq" gorm:"column:alarm_seq"` // 告警序号 同网元类型连续递增
AlarmSeq int64 `json:"alarmSeq" gorm:"column:alarm_seq"` // 告警序号 连续递增
AlarmId string `json:"alarmId" gorm:"column:alarm_id"` // 告警ID
AlarmTitle string `json:"alarmTitle" gorm:"column:alarm_title"` // 告警标题
AlarmCode int64 `json:"alarmCode" gorm:"column:alarm_code"` // 告警状态码
EventTime int64 `json:"eventTime" gorm:"column:event_time"` // 事件产生时间 秒级
AlarmType string `json:"alarmType" gorm:"column:alarm_type"` // 告警类型 CommunicationAlarm=1,EquipmentAlarm=2,ProcessingFailure=3,EnvironmentalAlarm=4,QualityOfServiceAlarm=5
OrigSeverity string `json:"origSeverity" gorm:"column:orig_severity"` // 严重程度 1: Critical, 2: Major, 3: Minor, 4: Warning, 5: Event(Only VNF)
PerceivedSeverity string `json:"perceivedSeverity" gorm:"column:perceived_severity"` // 告警级别 1: Critical, 2: Major, 3: Minor, 4: Warning, 5: Event(Only VNF)
EventTime int64 `json:"eventTime" gorm:"column:event_time"` // 事件产生时间
AlarmType string `json:"alarmType" gorm:"column:alarm_type"` // 告警类型
OrigSeverity string `json:"origSeverity" gorm:"column:orig_severity"` // 严重程度
PerceivedSeverity string `json:"perceivedSeverity" gorm:"column:perceived_severity"` // 告警级别
ObjectUid string `json:"objectUid" gorm:"column:object_uid"` // 对象ID
ObjectName string `json:"objectName" gorm:"column:object_name"` // 对象名称
ObjectType string `json:"objectType" gorm:"column:object_type"` // 对象类型
LocationInfo string `json:"locationInfo" gorm:"column:location_info"` // 告警定位信息
AlarmStatus string `json:"alarmStatus" gorm:"column:alarm_status"` // 告警状态 0:clear, 1:active
AlarmStatus string `json:"alarmStatus" gorm:"column:alarm_status"` // 告警状态
SpecificProblem string `json:"specificProblem" gorm:"column:specific_problem"` // 告警问题原因
SpecificProblemId string `json:"specificProblemId" gorm:"column:specific_problem_id"` // 告警问题原因ID
AddInfo string `json:"addInfo" gorm:"column:add_info"` // 告警辅助信息
AckState int64 `json:"ackState" gorm:"column:ack_state"` // 确认状态 0: Unacked, 1: Acked
AckTime int64 `json:"ackTime" gorm:"column:ack_time"` // 确认时间 秒级
AckState string `json:"ackState" gorm:"column:ack_state"` // 确认状态
AckTime int64 `json:"ackTime" gorm:"column:ack_time"` // 确认时间
AckUser string `json:"ackUser" gorm:"column:ack_user"` // 确认用户
ClearType int64 `json:"clearType" gorm:"column:clear_type"` // 清除状态 0: Unclear, 1: AutoClear, 2: ManualClear
ClearType string `json:"clearType" gorm:"column:clear_type"` // 清除状态
ClearTime int64 `json:"clearTime" gorm:"column:clear_time"` // 清除时间
ClearUser string `json:"clearUser" gorm:"column:clear_user"` // 清除用户
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"` // 创建时间
@@ -46,9 +46,9 @@ type AlarmQuery struct {
PvFlag string `json:"pvFlag" form:"pvFlag"`
AlarmCode string `json:"alarmCode" form:"alarmCode"`
AlarmType string `json:"alarmType" form:"alarmType"`
AlarmStatus string `json:"alarmStatus" form:"alarmStatus" binding:"omitempty,oneof=0 1"` // 告警状态 0:clear, 1:active
OrigSeverity string `json:"origSeverity" form:"origSeverity"` // 告警类型 1: Critical, 2: Major, 3: Minor, 4: Warning, 5: Event(Only VNF)
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查event_time
AlarmStatus string `json:"alarmStatus" form:"alarmStatus" binding:"omitempty,oneof=Clear Active"` // 告警状态
OrigSeverity string `json:"origSeverity" form:"origSeverity"` // 告警类型
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查event_time
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=event_time id"` // 排序字段,填写结果字段
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序asc desc

View File

@@ -9,16 +9,16 @@ type AlarmEvent struct {
AlarmId string `json:"alarmId" gorm:"column:alarm_id"` // 告警ID
AlarmTitle string `json:"alarmTitle" gorm:"column:alarm_title"` // 告警标题
AlarmCode int64 `json:"alarmCode" gorm:"column:alarm_code"` // 告警状态码
EventTime int64 `json:"eventTime" gorm:"column:event_time"` // 事件产生时间 秒级
EventTime int64 `json:"eventTime" gorm:"column:event_time"` // 事件产生时间
ObjectUid string `json:"objectUid" gorm:"column:object_uid"` // 对象ID
ObjectName string `json:"objectName" gorm:"column:object_name"` // 对象名称
ObjectType string `json:"objectType" gorm:"column:object_type"` // 对象类型
LocationInfo string `json:"locationInfo" gorm:"column:location_info"` // 告警定位信息
AlarmStatus string `json:"alarmStatus" gorm:"column:alarm_status"` // 告警状态 0:clear, 1:active
AlarmStatus string `json:"alarmStatus" gorm:"column:alarm_status"` // 告警状态
SpecificProblem string `json:"specificProblem" gorm:"column:specific_problem"` // 告警问题原因
SpecificProblemId string `json:"specificProblemId" gorm:"column:specific_problem_id"` // 告警问题原因ID
AddInfo string `json:"addInfo" gorm:"column:add_info"` // 告警辅助信息
ClearType int64 `json:"clearType" gorm:"column:clear_type"` // 清除状态 0: Unclear, 1: AutoClear, 2: ManualClear
ClearType string `json:"clearType" gorm:"column:clear_type"` // 清除状态
ClearTime int64 `json:"clearTime" gorm:"column:clear_time"` // 清除时间
ClearUser string `json:"clearUser" gorm:"column:clear_user"` // 清除用户
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"` // 创建时间
@@ -34,8 +34,8 @@ type AlarmEventQuery struct {
NeType string `json:"neType" form:"neType"` // 网元类型
NeID string `json:"neId" form:"neId"` // 网元ID
AlarmCode string `json:"alarmCode" form:"alarmCode"`
AlarmStatus string `json:"alarmStatus" form:"alarmStatus" binding:"omitempty,oneof=0 1"` // 告警状态 0:clear, 1:active
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查event_time
AlarmStatus string `json:"alarmStatus" form:"alarmStatus" binding:"omitempty,oneof=Clear Active"` // 告警状态
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查event_time
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=event_time id"` // 排序字段,填写结果字段
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序asc desc

View File

@@ -5,16 +5,16 @@ type AlarmForwardLog 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"`
AlarmSeq int64 `json:"alarmSeq" gorm:"column:alarm_seq"` // 告警序号 同网元类型连续递增
AlarmSeq int64 `json:"alarmSeq" gorm:"column:alarm_seq"` // 告警序号 连续递增
AlarmId string `json:"alarmId" gorm:"column:alarm_id"` // 告警ID
AlarmCode int64 `json:"alarmCode" gorm:"column:alarm_code"` // 告警状态码
AlarmTitle string `json:"alarmTitle" gorm:"column:alarm_title"` // 告警标题
AlarmStatus string `json:"alarmStatus" gorm:"column:alarm_status"` // 告警状态 0:clear, 1:active
AlarmType string `json:"alarmType" gorm:"column:alarm_type"` // 告警类型 CommunicationAlarm=1,EquipmentAlarm=2,ProcessingFailure=3,EnvironmentalAlarm=4,QualityOfServiceAlarm=5
OrigSeverity string `json:"origSeverity" gorm:"column:orig_severity"` // 严重程度 1: Critical, 2: Major, 3: Minor, 4: Warning, 5: Event(Only VNF)
EventTime int64 `json:"eventTime" gorm:"column:event_time"` // 事件产生时间 秒级
AlarmStatus string `json:"alarmStatus" gorm:"column:alarm_status"` // 告警状态
AlarmType string `json:"alarmType" gorm:"column:alarm_type"` // 告警类型
OrigSeverity string `json:"origSeverity" gorm:"column:orig_severity"` // 严重程度
EventTime int64 `json:"eventTime" gorm:"column:event_time"` // 事件产生时间
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 创建时间
Type string `json:"type" gorm:"column:type"` // 转发方式 SMS/EMAIL
Type string `json:"type" gorm:"column:type"` // 转发方式 SMS/EMAIL/SMSC
Target string `json:"target" gorm:"column:target"` // 发送目标用户
Result string `json:"result" gorm:"column:result"` // 发送结果
}
@@ -28,7 +28,7 @@ func (*AlarmForwardLog) TableName() string {
type AlarmForwardLogQuery struct {
NeType string `json:"neType" form:"neType"` // 网元类型
NeID string `json:"neId" form:"neId"` // 网元ID
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查event_time
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=event_time id"` // 排序字段,填写结果字段
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序asc desc

View File

@@ -5,14 +5,14 @@ type AlarmLog 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"`
AlarmSeq int64 `json:"alarmSeq" gorm:"column:alarm_seq"` // 告警序号 同网元类型连续递增
AlarmSeq int64 `json:"alarmSeq" gorm:"column:alarm_seq"` // 告警序号 连续递增
AlarmId string `json:"alarmId" gorm:"column:alarm_id"` // 告警ID
AlarmCode int64 `json:"alarmCode" gorm:"column:alarm_code"` // 告警状态码
AlarmTitle string `json:"alarmTitle" gorm:"column:alarm_title"` // 告警标题
AlarmStatus string `json:"alarmStatus" gorm:"column:alarm_status"` // 告警状态 0:clear, 1:active
AlarmType string `json:"alarmType" gorm:"column:alarm_type"` // 告警类型 CommunicationAlarm=1,EquipmentAlarm=2,ProcessingFailure=3,EnvironmentalAlarm=4,QualityOfServiceAlarm=5
OrigSeverity string `json:"origSeverity" gorm:"column:orig_severity"` // 严重程度 1: Critical, 2: Major, 3: Minor, 4: Warning, 5: Event(Only VNF)
EventTime int64 `json:"eventTime" gorm:"column:event_time"` // 事件产生时间 秒级
AlarmStatus string `json:"alarmStatus" gorm:"column:alarm_status"` // 告警状态
AlarmType string `json:"alarmType" gorm:"column:alarm_type"` // 告警类型
OrigSeverity string `json:"origSeverity" gorm:"column:orig_severity"` // 严重程度
EventTime int64 `json:"eventTime" gorm:"column:event_time"` // 事件产生时间
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 创建时间
}
@@ -23,11 +23,11 @@ func (*AlarmLog) TableName() string {
// AlarmLogQuery 告警日志数据查询参数结构体
type AlarmLogQuery struct {
NeType string `json:"neType" form:"neType"` // 网元类型
NeID string `json:"neId" form:"neId"` // 网元ID
AlarmStatus string `json:"alarmStatus" form:"alarmStatus" binding:"omitempty,oneof=0 1"` // 告警状态 0:clear, 1:active
OrigSeverity string `json:"origSeverity" form:"origSeverity"` // 告警类型 1: Critical, 2: Major, 3: Minor, 4: Warning, 5: Event(Only VNF)
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查event_time
NeType string `json:"neType" form:"neType"` // 网元类型
NeID string `json:"neId" form:"neId"` // 网元ID
AlarmStatus string `json:"alarmStatus" form:"alarmStatus" binding:"omitempty,oneof=Clear Active"` // 告警状态
OrigSeverity string `json:"origSeverity" form:"origSeverity"` // 告警类型
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查event_time
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=event_time id"` // 排序字段,填写结果字段
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序asc desc

View File

@@ -0,0 +1,112 @@
package model
import (
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
)
type CBCEventStatus int
// CBCEventStatus CB事件状态枚举
const (
CBCEventStatusNull CBCEventStatus = iota // 未知状态
CBCEventStatusActive
CBCEventStatusInactive
)
func (status CBCEventStatus) Enum() string {
switch status {
case CBCEventStatusNull:
return "NULL"
case CBCEventStatusActive:
return "ACTIVE"
case CBCEventStatusInactive:
return "INACTIVE"
default:
return "UNKNOWN"
}
}
func (status CBCEventStatus) String() string {
return fmt.Sprintf("%d", status)
}
// ParseCBCEventStatus 将字符串转换为 枚举类型
func ParseCBCEventStatus(s string) CBCEventStatus {
if i, err := strconv.Atoi(s); err == nil {
return CBCEventStatus(i)
}
// 如果转换失败,则按名称匹配(忽略大小写)
switch strings.ToUpper(s) {
case "NULL":
return CBCEventStatusNull
case "ACTIVE":
return CBCEventStatusActive
case "INACTIVE":
return CBCEventStatusInactive
case "":
// 如果字符串为空,则返回未知状态
return CBCEventStatusNull
default:
// 默认返回未知状态
return CBCEventStatusNull
}
}
// CBCMessageQuery 查询条件结构体
type CBCMessageQuery struct {
NeType string `form:"neType"` // 网元类型
NeId string `form:"neId"` // 网元ID
EventName string `form:"eventName"` // 事件名称
Status string `form:"status"` // 消息状态
StartTime string `form:"startTime"` // 创建时间范围-起始
EndTime string `form:"endTime"` // 创建时间范围-结束
PageNum int `form:"pageNum" binding:"required"`
PageSize int `form:"pageSize" binding:"required"`
}
// @Description CBCMessage CB消息
type CBCMessage struct {
Id int64 `json:"id" gorm:"column:id"` // CB消息ID
NeType string `json:"neType" gorm:"column:ne_type"` // 网元类型
NeId string `json:"neId" gorm:"column:ne_id"` // 网元ID
MessageJson json.RawMessage `json:"messageJson" gorm:"column:message_json"` // 消息内容JSON
Status CBCEventStatus `json:"status" gorm:"column:status"` // 消息状态
Detail string `json:"detail" gorm:"column:detail"` // 详情
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 创建时间
UpdatedAt int64 `json:"updatedAt" gorm:"column:updated_at"` // 更新时间
}
// TableName 表名称
func (*CBCMessage) TableName() string {
return "cbc_message"
}
// Scan 实现 sql.Scanner 接口,支持从数据库字符串转为 CBCEventStatus
func (s *CBCEventStatus) Scan(value interface{}) error {
switch v := value.(type) {
case string:
*s = ParseCBCEventStatus(v)
return nil
case []byte:
*s = ParseCBCEventStatus(string(v))
return nil
case int64:
*s = CBCEventStatus(v)
return nil
case int:
*s = CBCEventStatus(v)
return nil
default:
return errors.New("unsupported Scan type for CBCEventStatus")
}
}
// Value 实现 driver.Valuer 接口,支持将 CBCEventStatus 存为字符串
func (s CBCEventStatus) Value() (driver.Value, error) {
return s.Enum(), nil
}

View File

@@ -0,0 +1,17 @@
package model
// CDREvent CDR会话对象 cdr_event
type CDREvent struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeName string `json:"neName" gorm:"column:ne_name"`
RmUid string `json:"rmUid" gorm:"column:rm_uid"` // 可能没有
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"` // 接收到的timestamp秒级存储毫秒时间戳
CdrJson string `json:"cdrJSON" gorm:"column:cdr_json"` // data JSON String
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 记录创建存储毫秒
}
// TableName 表名称
func (*CDREvent) TableName() string {
return "cdr_event"
}

View File

@@ -1,33 +0,0 @@
package model
// CDREventIMS CDR会话对象IMS cdr_event_ims
type CDREventIMS struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeName string `json:"neName" gorm:"column:ne_name"`
RmUid string `json:"rmUid" gorm:"column:rm_uid"`
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"` // 接收到的timestamp秒级存储毫秒时间戳
CdrJson string `json:"cdrJSON" gorm:"column:cdr_json"` // data JSON String
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 记录创建存储毫秒
}
// TableName 表名称
func (*CDREventIMS) TableName() string {
return "cdr_event_ims"
}
// CDREventIMSQuery CDR会话对象IMS查询参数结构体
type CDREventIMSQuery struct {
NeType string `json:"neType" form:"neType" binding:"required,oneof=IMS"` // 网元类型IMS
NeID string `json:"neId" form:"neId" binding:"required"`
RmUID string `json:"rmUID" form:"rmUID"`
RecordType string `json:"recordType" form:"recordType"` // 记录行为 MOC MTC
CallerParty string `json:"callerParty" form:"callerParty"` // 主叫号码
CalledParty string `json:"calledParty" form:"calledParty"` // 被叫号码
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查timestamp
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=timestamp"` // 排序字段,填写结果字段
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序asc desc
PageNum int64 `json:"pageNum" form:"pageNum" binding:"required"`
PageSize int64 `json:"pageSize" form:"pageSize" binding:"required"`
}

View File

@@ -1,32 +0,0 @@
package model
// CDREventSGWC CDR会话对象SGWC cdr_event_sgwc
type CDREventSGWC struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeName string `json:"neName" gorm:"column:ne_name"`
RmUid string `json:"rmUid" gorm:"column:rm_uid"`
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"` // 接收到的timestamp秒级存储毫秒时间戳
CdrJson string `json:"cdrJSON" gorm:"column:cdr_json"` // data JSON String
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 记录创建存储毫秒
}
// TableName 表名称
func (*CDREventSGWC) TableName() string {
return "cdr_event_sgwc"
}
// CDREventSGWCQuery CDR会话对象SGWC查询参数结构体
type CDREventSGWCQuery struct {
NeType string `json:"neType" form:"neType" binding:"required,oneof=SGWC"` // SGWC
NeID string `json:"neId" form:"neId" binding:"required"`
RmUID string `json:"rmUID" form:"rmUID"`
IMSI string `json:"imsi" form:"imsi"`
MSISDN string `json:"msisdn" form:"msisdn"`
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查timestamp
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=timestamp"` // 排序字段,填写结果字段
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序asc desc
PageNum int64 `json:"pageNum" form:"pageNum" binding:"required"`
PageSize int64 `json:"pageSize" form:"pageSize" binding:"required"`
}

View File

@@ -1,33 +0,0 @@
package model
// CDREventSMF CDR会话对象SMF cdr_event_smf
type CDREventSMF struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeName string `json:"neName" gorm:"column:ne_name"`
RmUid string `json:"rmUid" gorm:"column:rm_uid"`
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"` // 接收到的timestamp秒级存储毫秒时间戳
CdrJson string `json:"cdrJSON" gorm:"column:cdr_json"` // data JSON String
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 记录创建存储毫秒
}
// TableName 表名称
func (*CDREventSMF) TableName() string {
return "cdr_event_smf"
}
// CDREventSMFQuery CDR会话对象SMF查询参数结构体
type CDREventSMFQuery struct {
NeType string `json:"neType" form:"neType" binding:"required,oneof=SMF"` // 网元类型, 暂时支持SMF
NeID string `json:"neId" form:"neId" binding:"required"`
RmUID string `json:"rmUID" form:"rmUID"`
RecordType string `json:"recordType" form:"recordType"` // 暂时没用到
SubscriberID string `json:"subscriberID" form:"subscriberID"`
DNN string `json:"dnn" form:"dnn"`
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查timestamp
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=timestamp"` // 排序字段,填写结果字段
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序asc desc
PageNum int64 `json:"pageNum" form:"pageNum" binding:"required"`
PageSize int64 `json:"pageSize" form:"pageSize" binding:"required"`
}

View File

@@ -1,33 +0,0 @@
package model
// CDREventSMSC CDR会话对象SMSC cdr_event_smsc
type CDREventSMSC struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeName string `json:"neName" gorm:"column:ne_name"`
RmUid string `json:"rmUid" gorm:"column:rm_uid"` // 可能没有
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"` // 接收到的timestamp秒级存储毫秒时间戳
CdrJson string `json:"cdrJSON" gorm:"column:cdr_json"` // data JSON String
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 记录创建存储毫秒
}
// TableName 表名称
func (*CDREventSMSC) TableName() string {
return "cdr_event_smsc"
}
// CDREventSMSCQuery CDR会话对象SMSC查询参数结构体
type CDREventSMSCQuery struct {
NeType string `json:"neType" form:"neType" binding:"required,oneof=SMSC"` // 网元类型, 暂时支持SMSC
NeID string `json:"neId" form:"neId" binding:"required"`
RmUID string `json:"rmUID" form:"rmUID"`
RecordType string `json:"recordType" form:"recordType"` // 记录行为 MOSM MTSM
CallerParty string `json:"callerParty" form:"callerParty"` // 主叫号码
CalledParty string `json:"calledParty" form:"calledParty"` // 被叫号码
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查timestamp
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=timestamp"` // 排序字段,填写结果字段
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序asc desc
PageNum int64 `json:"pageNum" form:"pageNum" binding:"required"`
PageSize int64 `json:"pageSize" form:"pageSize" binding:"required"`
}

View File

@@ -0,0 +1,18 @@
package model
// UEEvent UE会话对象 ue_event
type UEEvent struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeName string `json:"neName" gorm:"column:ne_name"`
RmUID string `json:"rmUID" gorm:"column:rm_uid"` // 可能没有
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"` // 接收到时间
EventType string `json:"eventType" gorm:"column:event_type"` // 事件类型
EventJSONStr string `json:"eventJSON" gorm:"column:event_json"` // data JSON String
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 记录创建存储毫秒
}
// TableName 表名称
func (*UEEvent) TableName() string {
return "ue_event"
}

View File

@@ -1,33 +0,0 @@
package model
// UEEventAMF UE会话对象AMF ue_event_amf
type UEEventAMF struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeName string `json:"neName" gorm:"column:ne_name"`
RmUID string `json:"rmUID" gorm:"column:rm_uid"` // 可能没有
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"` // 接收到的timestamp秒级存储毫秒时间戳
EventType string `json:"eventType" gorm:"column:event_type"` // 事件类型 auth-result detach cm-state
EventJSONStr string `json:"eventJSON" gorm:"column:event_json"` // data JSON String
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 记录创建存储毫秒
}
// TableName 表名称
func (*UEEventAMF) TableName() string {
return "ue_event_amf"
}
// UEEventAMFQuery UE会话对象AMF查询参数结构体
type UEEventAMFQuery struct {
NeType string `json:"neType" form:"neType" binding:"required,oneof=AMF"` // 网元类型, 暂时支持AMF
NeID string `json:"neId" form:"neId" binding:"required"`
RmUID string `json:"rmUID" form:"rmUID"`
EventType string `json:"eventType" form:"eventType"` // 事件类型 auth-result detach cm-state
IMSI string `json:"imsi" form:"imsi"` // imsi
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查timestamp
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=timestamp"` // 排序字段,填写结果字段
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序asc desc
PageNum int64 `json:"pageNum" form:"pageNum" binding:"required"`
PageSize int64 `json:"pageSize" form:"pageSize" binding:"required"`
}

View File

@@ -1,33 +0,0 @@
package model
// UEEventMME UE会话对象MME ue_event_mme
type UEEventMME struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeName string `json:"neName" gorm:"column:ne_name"`
RmUID string `json:"rmUID" gorm:"column:rm_uid"` // 可能没有
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"` // 接收到的timestamp秒级存储毫秒时间戳
EventType string `json:"eventType" gorm:"column:event_type"` // 事件类型 auth-result detach cm-state
EventJSONStr string `json:"eventJSON" gorm:"column:event_json"` // data JSON String
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 记录创建存储毫秒
}
// TableName 表名称
func (*UEEventMME) TableName() string {
return "ue_event_mme"
}
// UEEventMMEQuery UE会话对象MME查询参数结构体
type UEEventMMEQuery struct {
NeType string `json:"neType" form:"neType" binding:"required,oneof=MME"` // 网元类型, 暂时支持MME
NeID string `json:"neId" form:"neId" binding:"required"`
RmUID string `json:"rmUID" form:"rmUID"`
EventType string `json:"eventType" form:"eventType"` // 事件类型 auth-result detach cm-state
IMSI string `json:"imsi" form:"imsi"` // imsi
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查timestamp
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=timestamp"` // 排序字段,填写结果字段
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序asc desc
PageNum int64 `json:"pageNum" form:"pageNum" binding:"required"`
PageSize int64 `json:"pageSize" form:"pageSize" binding:"required"`
}

View File

@@ -33,6 +33,31 @@ func Setup(router *gin.Engine) {
)
}
// 性能自定义统计信息
kpicGroup := neDataGroup.Group("/kpic")
{
kpicGroup.GET("/data",
middleware.AuthorizeUser(nil),
controller.NewKPIC.KPIData,
)
kpicGroup.GET("/title/list",
middleware.AuthorizeUser(nil),
controller.NewKPIC.ListTitle,
)
kpicGroup.POST("/title",
middleware.AuthorizeUser(nil),
controller.NewKPIC.AddTitle,
)
kpicGroup.PUT("/title",
middleware.AuthorizeUser(nil),
controller.NewKPIC.EditTitle,
)
kpicGroup.DELETE("/title",
middleware.AuthorizeUser(nil),
controller.NewKPIC.RemoveTitle,
)
}
// 告警数据信息
alarmGroup := neDataGroup.Group("/alarm")
{
@@ -59,6 +84,18 @@ func Setup(router *gin.Engine) {
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.alarm", collectlogs.BUSINESS_TYPE_EXPORT)),
controller.NewAlarm.Export,
)
alarmGroup.GET("/count/type",
middleware.AuthorizeUser(nil),
controller.NewAlarm.CountType,
)
alarmGroup.GET("/count/severity",
middleware.AuthorizeUser(nil),
controller.NewAlarm.CountSeverity,
)
alarmGroup.GET("/count/ne",
middleware.AuthorizeUser(nil),
controller.NewAlarm.CountNe,
)
}
// 告警日志数据信息
@@ -100,7 +137,7 @@ func Setup(router *gin.Engine) {
middleware.AuthorizeUser(nil),
controller.NewNBState.List,
)
nbStateGroup.POST("/export",
nbStateGroup.GET("/export",
middleware.AuthorizeUser(nil),
controller.NewNBState.Export,
)
@@ -118,7 +155,7 @@ func Setup(router *gin.Engine) {
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.imsCDR", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewIMS.CDRRemove,
)
imsGroup.POST("/cdr/export",
imsGroup.GET("/cdr/export",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.imsCDR", collectlogs.BUSINESS_TYPE_EXPORT)),
controller.NewIMS.CDRExport,
@@ -145,7 +182,7 @@ func Setup(router *gin.Engine) {
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.smscCDR", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewSMSC.CDRRemove,
)
smscGroup.POST("/cdr/export",
smscGroup.GET("/cdr/export",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.smscCDR", collectlogs.BUSINESS_TYPE_EXPORT)),
controller.NewSMSC.CDRExport,
@@ -164,7 +201,7 @@ func Setup(router *gin.Engine) {
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.smfCDR", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewSMF.CDRRemove,
)
smfGroup.POST("/cdr/export",
smfGroup.GET("/cdr/export",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.smfCDR", collectlogs.BUSINESS_TYPE_EXPORT)),
controller.NewSMF.CDRExport,
@@ -191,7 +228,7 @@ func Setup(router *gin.Engine) {
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.amfUE", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewAMF.UERemove,
)
amfGroup.POST("/ue/export",
amfGroup.GET("/ue/export",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.amfUE", collectlogs.BUSINESS_TYPE_EXPORT)),
controller.NewAMF.UEExport,
@@ -215,6 +252,29 @@ func Setup(router *gin.Engine) {
)
}
// 网元N3IWF
n3iwfGroup := neDataGroup.Group("/n3iwf")
{
n3iwfGroup.GET("/sub/list",
middleware.AuthorizeUser(nil),
controller.NewN3IWF.SubUserList,
)
}
// 网元N3IWF
nssf := controller.NewNSSF
nssfGroup := neDataGroup.Group("/nssf")
{
nssfGroup.GET("/sub/list",
middleware.AuthorizeUser(nil),
nssf.SubUserList,
)
nssfGroup.GET("/amf/list",
middleware.AuthorizeUser(nil),
nssf.AvailableList,
)
}
// 备份数据
backupGroup := neDataGroup.Group("/backup")
{
@@ -452,7 +512,7 @@ func Setup(router *gin.Engine) {
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.mmeUE", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewMME.UERemove,
)
mmeGroup.POST("/ue/export",
mmeGroup.GET("/ue/export",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.mmeUE", collectlogs.BUSINESS_TYPE_EXPORT)),
controller.NewMME.UEExport,
@@ -519,6 +579,44 @@ func Setup(router *gin.Engine) {
controller.NewPCF.RuleInfoImport,
)
}
// 网元CBC
cbcGroup := neDataGroup.Group("/cbc")
{
cbcGroup.GET("/message/list",
middleware.AuthorizeUser(nil),
controller.NewCBC.List,
)
cbcGroup.GET("/message/:id",
middleware.AuthorizeUser(nil),
controller.NewCBC.ListById,
)
cbcGroup.POST("/message",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.cbcMessage", collectlogs.BUSINESS_TYPE_IMPORT)),
controller.NewCBC.Insert,
)
cbcGroup.PUT("/message/:id",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.cbcMessage", collectlogs.BUSINESS_TYPE_UPDATE)),
controller.NewCBC.Update,
)
cbcGroup.PUT("/message/:id/:status",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.cbcMessage", collectlogs.BUSINESS_TYPE_UPDATE)),
controller.NewCBC.UpdateStatus,
)
cbcGroup.DELETE("/message/:id",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.cbcMessage", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewCBC.Delete,
)
cbcGroup.GET("/message/export",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.cbcMessage", collectlogs.BUSINESS_TYPE_EXPORT)),
controller.NewCBC.Export,
)
}
}
// InitLoad 初始参数

View File

@@ -1,6 +1,7 @@
package repository
import (
"fmt"
"strings"
"time"
@@ -32,7 +33,7 @@ func (r Alarm) SelectByPage(query model.AlarmQuery) ([]model.Alarm, int64) {
tx = tx.Where("pv_flag = ?", query.PvFlag)
}
if query.AlarmCode != "" {
tx = tx.Where("alarm_code = ?", query.AlarmCode)
tx = tx.Where("alarm_code like ?", fmt.Sprintf("%%%s%%", query.AlarmCode))
}
if query.AlarmType != "" {
tx = tx.Where("alarm_type in (?)", strings.Split(query.AlarmType, ","))
@@ -192,3 +193,21 @@ func (r Alarm) SelectAlarmSeqLast(neType, neId string) int64 {
}
return alarmSeq
}
// GroupTotal 分组统计
func (r Alarm) GroupTotal(alarmStatus string, group string, limit int) []map[string]any {
tx := db.DB("").Model(&model.Alarm{})
tx = tx.Select("count(*) as total", group)
tx = tx.Where("alarm_status=?", alarmStatus)
tx = tx.Group(group).Order("total DESC")
// 查询数据
var rows []map[string]any = make([]map[string]any, 0)
if limit > 0 {
tx = tx.Limit(limit)
}
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}

View File

@@ -24,6 +24,9 @@ func (r AlarmLog) SelectByPage(query model.AlarmLogQuery) ([]model.AlarmLog, int
if query.NeID != "" {
tx = tx.Where("ne_id = ?", query.NeID)
}
if query.AlarmStatus != "" {
tx = tx.Where("alarm_status = ?", query.AlarmStatus)
}
if query.BeginTime != 0 {
tx = tx.Where("created_at >= ?", query.BeginTime)
}

View File

@@ -0,0 +1,273 @@
package repository
import (
"encoding/json"
"fmt"
"time"
"be.ems/src/framework/database/db"
"be.ems/src/modules/network_data/model"
"gorm.io/gorm"
)
// 实例化数据层 UEEvent 结构体
var NewCBCMessage = &CBCMessage{}
// UEEvent UE会话事件 数据层处理
type CBCMessage struct{}
// SelectCBCMessage 根据条件分页查询CB消息
func (s *CBCMessage) SelectByPage(query model.CBCMessageQuery) ([]model.CBCMessage, int, error) {
var msg []model.CBCMessage
var total int64
tx := db.DB("").Table("cbc_message")
if query.NeType != "" {
tx = tx.Where("ne_type = ?", query.NeType)
}
if query.NeId != "" {
tx = tx.Where("ne_id = ?", query.NeId)
}
if query.EventName != "" {
tx = tx.Where("JSON_EXTRACT(message_json, '$.eventName') = ?", query.EventName)
}
if query.Status != "" {
tx = tx.Where("status = ?", query.Status)
}
var startMicro, endMicro int64
var err error
if query.StartTime != "" {
startMicro, err = parseTimeToMilli(query.StartTime)
if err != nil {
return nil, 0, fmt.Errorf("invalid start time: %w", err)
}
}
if query.EndTime != "" {
endMicro, err = parseTimeToMilli(query.EndTime)
if err != nil {
return nil, 0, fmt.Errorf("invalid end time: %w", err)
}
}
if startMicro > 0 && endMicro > 0 {
tx = tx.Where("created_at BETWEEN ? AND ?", startMicro, endMicro)
} else if startMicro > 0 {
tx = tx.Where("created_at >= ?", startMicro)
} else if endMicro > 0 {
tx = tx.Where("created_at <= ?", endMicro)
}
// 统计总数
if err := tx.Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("failed to count CBC message: %w", err)
}
// 分页查询
offset := (query.PageNum - 1) * query.PageSize
if err := tx.Limit(query.PageSize).Offset(offset).Order("created_at desc").Find(&msg).Error; err != nil {
return nil, 0, fmt.Errorf("failed to select CBC message: %w", err)
}
return msg, int(total), nil
}
// SelectCBCMessageByPage 分页查询CB消息
// @Description 分页查询CB消息
// @param page 页码
// @param pageSize 每页大小
// @return []model.CBCMessage CB消息列表
// @return int 总记录数
// @return error 错误信息
// @example
// SelectByPage(1, 10)
// func (s *CBCMessage) SelectByPage(pageNum int, pageSize int) ([]model.CBCMessage, int, error) {
// var tickets []model.CBCMessage
// var total int64
// // 统计总数
// if err := db.DB("").Table("cbc_message").Count(&total).Error; err != nil {
// return nil, 0, fmt.Errorf("failed to count CBC message: %w", err)
// }
// // 分页查询
// offset := (pageNum - 1) * pageSize
// if err := db.DB("").Table("cbc_message").
// Limit(pageSize).
// Offset(offset).
// Find(&tickets).Error; err != nil {
// return nil, 0, fmt.Errorf("failed to select CBC message: %w", err)
// }
// return tickets, int(total), nil
// }
// InsertCBCMessage 插入CB消息
// @Description 插入CB消息
// @param msg CB消息对象
// @return error 错误信息
// @example
// CBCMessage.InsertCBCMessage(msg)
func (s *CBCMessage) Insert(msg model.CBCMessage) error {
msg.CreatedAt = time.Now().UnixMilli()
// 这里可以使用ORM或其他方式将ticket插入到数据库中
if err := db.DB("").Table("cbc_message").Create(&msg).Error; err != nil {
return fmt.Errorf("failed to insert CBC message: %w", err)
}
return nil
}
// SelectCBCMessageById 根据工单ID查询CB消息
// @Description 根据工单ID查询CB消息
// @param id 工单ID
// @return *model.CBCMessage CB消息对象
// @return error 错误信息
// @example
// CBCMessage.SelectCBCMessageById(12345)
func (s *CBCMessage) SelectById(id int64) (*model.CBCMessage, error) {
var msg model.CBCMessage
if err := db.DB("").Table("cbc_message").
Where("id = ?", id).
First(&msg).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, fmt.Errorf("failed to select CBC message: %w", err)
}
return &msg, nil
}
// SelectByEventName 根据事件名称查询CB消息
func (s *CBCMessage) SelectByEventName(eventName string) (*model.CBCMessage, error) {
var msg model.CBCMessage
if err := db.DB("").Table("cbc_message").
Where("JSON_EXTRACT(message_json, '$.eventName') = ?", eventName).
First(&msg).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &msg, nil
}
// UpdateCBCMessage 更新CB消息
// @Description 更新CB消息
// @param msg CB消息对象
// @return error 错误信息
// @example
// mfCBCMessageService.UpdateCBCMessage(msg)
func (s *CBCMessage) Update(id int64, messageJson json.RawMessage) (*model.CBCMessage, error) {
var msg model.CBCMessage
now := time.Now().UnixMilli()
err := db.DB("").Transaction(func(tx *gorm.DB) error {
// 在事务中更新
if err := tx.Table("cbc_message").
Where("id = ?", id).
Updates(map[string]any{
"message_json": messageJson,
"updated_at": now,
}).Error; err != nil {
return fmt.Errorf("failed to update CBC message: %w", err)
}
// 在事务中查询更新后的记录
if err := tx.Table("cbc_message").
Where("id = ?", id).
First(&msg).Error; err != nil {
return fmt.Errorf("failed to fetch updated CBC message: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
return &msg, nil
}
// UpdateCBCMessage 更新CB消息
// @Description 更新CB消息
// @param msg CB消息对象
// @return error 错误信息
// @example
// UpdateCBCMessageDetail(msg)
func (s *CBCMessage) UpdateDetail(eventName, detail string) error {
now := time.Now().UnixMilli()
if err := db.DB("").Table("cbc_message").
Where("JSON_EXTRACT(message_json, '$.eventName') = ?", eventName).
Updates(map[string]any{
"detail": detail,
"updated_at": now,
}).Error; err != nil {
return fmt.Errorf("failed to update CBC message: %w", err)
}
return nil
}
// DeleteCBCMessage 删除CB消息
// @Description 删除CB消息
// @param id 工单ID
// @return error 错误信息
// @example
// DeleteCBCMessage(12345)
func (s *CBCMessage) Delete(id int64) error {
// 执行删除操作
if err := db.DB("").Table("cbc_message").
Where("id = ?", id).
Delete(&model.CBCMessage{}).Error; err != nil {
return fmt.Errorf("failed to delete CBC message: %w", err)
}
return nil
}
// UpdateCBCMessageStatus 更新CB消息状态
// @Description 更新CB消息状态并根据状态变化发送相应的HTTP请求
// @param status 新状态
// @return error 错误信息
func (s *CBCMessage) UpdateStatus(id int64, status model.CBCEventStatus) error {
// 更新数据库状态
now := time.Now().UnixMilli()
if err := db.DB("").Table("cbc_message").
Where("id = ?", id).
Updates(map[string]interface{}{
"status": status,
"updated_at": now,
}).Error; err != nil {
return fmt.Errorf("failed to update CBC message status: %w", err)
}
return nil
}
// Select 查询所有CB消息
func (s *CBCMessage) Select(msgs *[]model.CBCMessage) error {
if err := db.DB("").Table("cbc_message").Find(&msgs).Error; err != nil {
return fmt.Errorf("failed to query CB messages: %w", err)
}
return nil
}
// SelectByNeId 根据网元ID查询CB消息
func (s *CBCMessage) SelectByNeId(neId string, msgs *[]model.CBCMessage) error {
if err := db.DB("").Table("cbc_message").Where("ne_id = ?", neId).Find(&msgs).Error; err != nil {
return fmt.Errorf("failed to query CB messages: %w", err)
}
return nil
}
// 假设 query.StartTime 和 query.EndTime 是 "2006-01-02 15:04:05" 格式字符串
func parseTimeToMilli(ts string) (int64, error) {
if ts == "" {
return 0, nil
}
t, err := time.ParseInLocation("2006-01-02 15:04:05", ts, time.Local)
if err != nil {
return 0, err
}
return t.UnixMilli(), nil
}

View File

@@ -0,0 +1,167 @@
package repository
import (
"fmt"
"strings"
"time"
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 CDREvent 结构体
var NewCDREvent = &CDREvent{}
// CDREvent CDR会话事件 数据层处理
type CDREvent struct{}
// SelectByPage 分页查询集合
func (r CDREvent) SelectByPage(neType string, query map[string]string) ([]model.CDREvent, int64) {
// 表名
tableName := fmt.Sprintf("cdr_event_%s", strings.ToLower(neType))
tx := db.DB("").Table(tableName).Model(&model.CDREvent{})
// 查询条件拼接
if v, ok := query["rmUID"]; ok && v != "" {
tx = tx.Where("rm_uid = ?", v)
}
if v, ok := query["beginTime"]; ok && v != "" {
if len(v) == 10 {
v = v + "000"
}
tx = tx.Where("created_at >= ?", v)
}
if v, ok := query["endTime"]; ok && v != "" {
if len(v) == 10 {
v = v + "000"
}
tx = tx.Where("created_at <= ?", v)
}
// 各个网元特殊查询条件
switch neType {
case "SMSC":
if v, ok := query["callerParty"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.callerParty') like ?", fmt.Sprintf("%%%s%%", v))
}
if v, ok := query["calledParty"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.calledParty') like ?", fmt.Sprintf("%%%s%%", v))
}
if v, ok := query["recordType"]; ok && v != "" {
recordTypes := strings.Split(v, ",")
var querytrArr []string
for _, recordType := range recordTypes {
querytrArr = append(querytrArr, fmt.Sprintf("JSON_EXTRACT(cdr_json, '$.recordType') = '%s'", recordType))
}
tx = tx.Where(fmt.Sprintf("( %s )", strings.Join(querytrArr, " OR ")))
}
case "SMF":
if v, ok := query["recordType"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.recordType') = ?", v)
}
if v, ok := query["subscriberID"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.subscriberIdentifier.subscriptionIDData') like ?", fmt.Sprintf("%%%s%%", v))
}
if v, ok := query["dnn"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.pDUSessionChargingInformation.dNNID') = ?", v)
}
case "SGWC":
if v, ok := query["imsi"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.servedIMSI') like ?", fmt.Sprintf("%%%s%%", v))
}
if v, ok := query["msisdn"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.servedMSISDN') like ?", fmt.Sprintf("%%%s%%", v))
}
case "IMS":
if v, ok := query["callerParty"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.callerParty') like ?", fmt.Sprintf("%%%s%%", v))
}
if v, ok := query["calledParty"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.calledParty') like ?", fmt.Sprintf("%%%s%%", v))
}
}
var total int64 = 0
rows := []model.CDREvent{}
// 查询数量 长度为0直接返回
if err := tx.Count(&total).Error; err != nil || total <= 0 {
return rows, total
}
// 分页
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
tx = tx.Offset(int(pageNum * pageSize)).Limit(int(pageSize))
// 排序
if v, ok := query["sortField"]; ok && v != "" {
sortSql := v
if o, ok := query["sortOrder"]; ok && o != "" {
if o == "desc" {
sortSql += " desc "
} else {
sortSql += " asc "
}
}
tx = tx.Order(sortSql)
}
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query err => %v", err)
}
return rows, total
}
// SelectByIds 通过ID查询
func (r CDREvent) SelectByIds(neType string, ids []int64) []model.CDREvent {
rows := []model.CDREvent{}
if len(ids) <= 0 {
return rows
}
// 表名
tableName := fmt.Sprintf("cdr_event_%s", strings.ToLower(neType))
tx := db.DB("").Table(tableName).Model(&model.CDREvent{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// DeleteByIds 批量删除信息
func (r CDREvent) DeleteByIds(neType string, ids []int64) int64 {
if len(ids) <= 0 {
return 0
}
// 表名
tableName := fmt.Sprintf("cdr_event_%s", strings.ToLower(neType))
tx := db.DB("").Table(tableName).Where("id in ?", ids)
if err := tx.Delete(&model.CDREvent{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}
// Insert 新增信息 返回新增数据ID
func (r CDREvent) Insert(param model.CDREvent) int64 {
if param.NeType == "" {
return 0
}
if param.CreatedAt == 0 {
param.CreatedAt = time.Now().UnixMilli()
}
// 表名
tableName := fmt.Sprintf("cdr_event_%s", strings.ToLower(param.NeType))
// 执行插入
if err := db.DB("").Table(tableName).Create(&param).Error; err != nil {
logger.Errorf("insert err => %v", err.Error())
return 0
}
return param.ID
}

View File

@@ -1,107 +0,0 @@
package repository
import (
"fmt"
"strings"
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 CDREventIMS 结构体
var NewCDREventIMS = &CDREventIMS{}
// CDREventIMS CDR会话事件 数据层处理
type CDREventIMS struct{}
// SelectByPage 分页查询集合
func (r CDREventIMS) SelectByPage(query model.CDREventIMSQuery) ([]model.CDREventIMS, int64) {
tx := db.DB("").Model(&model.CDREventIMS{})
// 查询条件拼接
if query.NeType != "" {
tx = tx.Where("ne_type = ?", query.NeType)
}
if query.RmUID != "" {
tx = tx.Where("rm_uid = ?", query.RmUID)
}
if query.BeginTime != 0 {
tx = tx.Where("created_at >= ?", query.BeginTime)
}
if query.EndTime != 0 {
tx = tx.Where("created_at <= ?", query.EndTime)
}
if query.CallerParty != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.callerParty') = ?", query.CallerParty)
}
if query.CalledParty != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.calledParty') = ?", query.CalledParty)
}
if query.RecordType != "" {
recordTypes := strings.Split(query.RecordType, ",")
var querytrArr []string
for _, recordType := range recordTypes {
querytrArr = append(querytrArr, fmt.Sprintf("JSON_EXTRACT(cdr_json, '$.recordType') = '%s'", recordType))
}
tx = tx.Where(fmt.Sprintf("( %s )", strings.Join(querytrArr, " OR ")))
}
// 查询结果
var total int64 = 0
rows := []model.CDREventIMS{}
// 查询数量为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 *CDREventIMS) SelectByIds(ids []int64) []model.CDREventIMS {
rows := []model.CDREventIMS{}
if len(ids) <= 0 {
return rows
}
tx := db.DB("").Model(&model.CDREventIMS{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// DeleteByIds 批量删除信息
func (r *CDREventIMS) DeleteByIds(ids []int64) int64 {
if len(ids) <= 0 {
return 0
}
tx := db.DB("").Where("id in ?", ids)
if err := tx.Delete(&model.CDREventIMS{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}

View File

@@ -1,95 +0,0 @@
package repository
import (
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 CDREventSGWC 结构体
var NewCDREventSGWC = &CDREventSGWC{}
// CDREventSGWC CDR会话事件 数据层处理
type CDREventSGWC struct{}
// SelectByPage 分页查询集合
func (r CDREventSGWC) SelectByPage(query model.CDREventSGWCQuery) ([]model.CDREventSGWC, int64) {
tx := db.DB("").Model(&model.CDREventSGWC{})
// 查询条件拼接
if query.NeType != "" {
tx = tx.Where("ne_type = ?", query.NeType)
}
if query.RmUID != "" {
tx = tx.Where("rm_uid = ?", query.RmUID)
}
if query.BeginTime != 0 {
tx = tx.Where("timestamp >= ?", query.BeginTime)
}
if query.EndTime != 0 {
tx = tx.Where("timestamp <= ?", query.EndTime)
}
if query.IMSI != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.servedIMSI') = ?", query.IMSI)
}
if query.MSISDN != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.servedMSISDN') = ?", query.MSISDN)
}
// 查询结果
var total int64 = 0
rows := []model.CDREventSGWC{}
// 查询数量为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 *CDREventSGWC) SelectByIds(ids []int64) []model.CDREventSGWC {
rows := []model.CDREventSGWC{}
if len(ids) <= 0 {
return rows
}
tx := db.DB("").Model(&model.CDREventSGWC{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// DeleteByIds 批量删除信息
func (r *CDREventSGWC) DeleteByIds(ids []int64) int64 {
if len(ids) <= 0 {
return 0
}
tx := db.DB("").Where("id in ?", ids)
if err := tx.Delete(&model.CDREventSGWC{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}

View File

@@ -1,98 +0,0 @@
package repository
import (
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 CDREventSMF 结构体
var NewCDREventSMF = &CDREventSMF{}
// CDREventSMF CDR会话事件 数据层处理
type CDREventSMF struct{}
// SelectByPage 分页查询集合
func (r CDREventSMF) SelectByPage(query model.CDREventSMFQuery) ([]model.CDREventSMF, int64) {
tx := db.DB("").Model(&model.CDREventSMF{})
// 查询条件拼接
if query.NeType != "" {
tx = tx.Where("ne_type = ?", query.NeType)
}
if query.RmUID != "" {
tx = tx.Where("rm_uid = ?", query.RmUID)
}
if query.BeginTime != 0 {
tx = tx.Where("timestamp >= ?", query.BeginTime)
}
if query.EndTime != 0 {
tx = tx.Where("timestamp <= ?", query.EndTime)
}
if query.RecordType != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.recordType') = ?", query.RecordType)
}
if query.SubscriberID != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.subscriberIdentifier.subscriptionIDData') = ?", query.SubscriberID)
}
if query.DNN != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.pDUSessionChargingInformation.dNNID') = ?", query.DNN)
}
// 查询结果
var total int64 = 0
rows := []model.CDREventSMF{}
// 查询数量为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 *CDREventSMF) SelectByIds(ids []int64) []model.CDREventSMF {
rows := []model.CDREventSMF{}
if len(ids) <= 0 {
return rows
}
tx := db.DB("").Model(&model.CDREventSMF{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// DeleteByIds 批量删除信息
func (r *CDREventSMF) DeleteByIds(ids []int64) int64 {
if len(ids) <= 0 {
return 0
}
tx := db.DB("").Where("id in ?", ids)
if err := tx.Delete(&model.CDREventSMF{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}

View File

@@ -1,107 +0,0 @@
package repository
import (
"fmt"
"strings"
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 CDREventSMSC 结构体
var NewCDREventSMSC = &CDREventSMSC{}
// CDREventSMSC CDR会话事件 数据层处理
type CDREventSMSC struct{}
// SelectByPage 分页查询集合
func (r CDREventSMSC) SelectByPage(query model.CDREventSMSCQuery) ([]model.CDREventSMSC, int64) {
tx := db.DB("").Model(&model.CDREventSMSC{})
// 查询条件拼接
if query.NeType != "" {
tx = tx.Where("ne_type = ?", query.NeType)
}
if query.RmUID != "" {
tx = tx.Where("rm_uid = ?", query.RmUID)
}
if query.BeginTime != 0 {
tx = tx.Where("timestamp >= ?", query.BeginTime)
}
if query.EndTime != 0 {
tx = tx.Where("timestamp <= ?", query.EndTime)
}
if query.CallerParty != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.callerParty') = ?", query.CallerParty)
}
if query.CalledParty != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.calledParty') = ?", query.CalledParty)
}
if query.RecordType != "" {
recordTypes := strings.Split(query.RecordType, ",")
var querytrArr []string
for _, recordType := range recordTypes {
querytrArr = append(querytrArr, fmt.Sprintf("JSON_EXTRACT(cdr_json, '$.recordType') = '%s'", recordType))
}
tx = tx.Where(fmt.Sprintf("( %s )", strings.Join(querytrArr, " OR ")))
}
// 查询结果
var total int64 = 0
rows := []model.CDREventSMSC{}
// 查询数量为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 *CDREventSMSC) SelectByIds(ids []int64) []model.CDREventSMSC {
rows := []model.CDREventSMSC{}
if len(ids) <= 0 {
return rows
}
tx := db.DB("").Model(&model.CDREventSMSC{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// DeleteByIds 批量删除信息
func (r *CDREventSMSC) DeleteByIds(ids []int64) int64 {
if len(ids) <= 0 {
return 0
}
tx := db.DB("").Where("id in ?", ids)
if err := tx.Delete(&model.CDREventSMSC{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}

View File

@@ -17,7 +17,7 @@ var NewKpiCReport = &KpiCReport{}
type KpiCReport struct{}
// SelectGoldKPI 通过网元指标数据信息
func (r KpiCReport) SelectKPI(query model.KPIQuery) []model.KpiCReport {
func (r KpiCReport) SelectKPI(query model.KPICQuery) []model.KpiCReport {
rows := []model.KpiCReport{}
if query.NeType == "" {
return rows
@@ -87,3 +87,132 @@ func (r KpiCReport) SelectKPITitle(neType string) []model.KpiCTitle {
}
return rows
}
// TitleLastKPIId 查询指标标题最后kpiid
func (r KpiCReport) TitleLastKPIId(neType string) string {
tx := db.DB("").Model(&model.KpiCTitle{})
tx = tx.Where("ne_type=?", neType)
tx = tx.Select("kpi_id").Order("kpi_id DESC")
// 查询数据
var kpiId string = ""
if err := tx.Limit(1).Find(&kpiId).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return kpiId
}
return kpiId
}
// SelectByPageTitle 分页查询集合
func (r KpiCReport) TitleSelectByPage(query map[string]string) ([]model.KpiCTitle, int64) {
tx := db.DB("").Model(&model.KpiCTitle{})
// 查询条件拼接
if v, ok := query["neType"]; ok && v != "" {
tx = tx.Where("ne_type = ?", v)
}
if v, ok := query["status"]; ok && v != "" {
tx = tx.Where("status = ?", v)
} else {
tx = tx.Where("status != ?", "2")
}
// 查询结果
var total int64 = 0
rows := []model.KpiCTitle{}
// 查询数量 长度为0直接返回
if err := tx.Count(&total).Error; err != nil || total <= 0 {
return rows, total
}
// 分页
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
tx = tx.Offset(int(pageNum * pageSize)).Limit(int(pageSize))
// 排序
if v, ok := query["sortField"]; ok && v != "" {
sortSql := v
if o, ok := query["sortOrder"]; ok && o != "" {
if o == "desc" {
sortSql += " desc "
} else {
sortSql += " asc "
}
}
tx = tx.Order(sortSql)
}
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query err => %v", err)
}
return rows, total
}
// TitleSelect 网元对应的指标名称
func (r KpiCReport) TitleSelect(param model.KpiCTitle) []model.KpiCTitle {
tx := db.DB("").Model(&model.KpiCTitle{})
// 构建查询条件
if param.NeType != "" {
tx = tx.Where("ne_type =?", param.NeType)
}
if param.Title != "" {
tx = tx.Where("title = ?", param.Title)
}
if param.Status != "" {
tx = tx.Where("status = ?", param.Status)
}
// 查询数据
rows := []model.KpiCTitle{}
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// TitleInsert 新增信息
func (r KpiCReport) TitleInsert(param model.KpiCTitle) int64 {
if param.CreatedBy != "" {
param.UpdatedAt = time.Now().UnixMilli()
}
param.Status = "1"
tx := db.DB("").Create(&param)
if err := tx.Error; err != nil {
logger.Errorf("CreateInBatches err => %v", err)
}
return param.ID
}
// TitleUpdate 修改信息
func (r KpiCReport) TitleUpdate(param model.KpiCTitle) int64 {
if param.ID <= 0 {
return 0
}
param.UpdatedAt = time.Now().UnixMilli()
tx := db.DB("").Model(&model.KpiCTitle{})
// 构建查询条件
tx = tx.Where("id = ?", param.ID)
tx = tx.Omit("id", "created_by")
// 执行更新
if err := tx.Updates(param).Error; err != nil {
logger.Errorf("update err => %v", err.Error())
return 0
}
return tx.RowsAffected
}
// TitleDeleteByIds 批量删除信息
func (r KpiCReport) TitleDeleteByIds(ids []int64) int64 {
if len(ids) <= 0 {
return 0
}
tx := db.DB("").Model(&model.KpiCTitle{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
if err := tx.Update("status", 2).Error; err != nil {
logger.Errorf("update err => %v", err.Error())
return 0
}
return tx.RowsAffected
}

View File

@@ -5,7 +5,6 @@ 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"
)
@@ -36,14 +35,14 @@ func (r NBState) SelectByPage(query model.NBStateQuery) ([]model.NBState, int64)
if len(startTime) == 10 {
startTime = startTime + "000"
}
tx = tx.Where("time >= ?", date.ParseDateToStr(startTime, date.YYYY_MM_DDTHH_MM_SSZ))
tx = tx.Where("create_time >= ?", startTime)
}
if query.EndTime != "" {
endTime := query.EndTime
if len(endTime) == 10 {
endTime = endTime + "999"
}
tx = tx.Where("time <= ?", date.ParseDateToStr(endTime, date.YYYY_MM_DDTHH_MM_SSZ))
tx = tx.Where("create_time <= ?", endTime)
}
// 查询结果

View File

@@ -27,7 +27,7 @@ func (r *UDMAuthUser) ClearAndInsert(neId string, uArr []model.UDMAuthUser) int6
}
// SelectPage 根据条件分页查询
func (r *UDMAuthUser) SelectPage(query map[string]string) (int64, []model.UDMAuthUser) {
func (r *UDMAuthUser) SelectPage(query map[string]string) ([]model.UDMAuthUser, int64) {
tx := db.DB("").Model(&model.UDMAuthUser{})
// 查询条件拼接
if v, ok := query["imsi"]; ok && v != "" {
@@ -48,7 +48,7 @@ func (r *UDMAuthUser) SelectPage(query map[string]string) (int64, []model.UDMAut
// 查询数量 长度为0直接返回
if err := tx.Count(&total).Error; err != nil || total <= 0 {
return total, rows
return rows, total
}
// 分页
@@ -73,7 +73,7 @@ func (r *UDMAuthUser) SelectPage(query map[string]string) (int64, []model.UDMAut
logger.Errorf("query err => %v", err)
}
return total, rows
return rows, total
}
// SelectList 根据实体查询

Some files were not shown because too many files have changed in this diff Show More