Merge remote-tracking branch 'origin/main' into multi-tenant

This commit is contained in:
TsMask
2024-09-20 20:23:39 +08:00
142 changed files with 7687 additions and 2096 deletions

View File

@@ -1,7 +1,7 @@
# 项目信息
framework:
name: "OMC"
version: "2.2408.1"
version: "2.2409.3"
# 应用服务配置
server:

View File

@@ -0,0 +1,96 @@
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 := fmt.Sprintf("%s:%d", c.Addr, 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

@@ -0,0 +1,83 @@
package socket
import (
"fmt"
"net"
"strings"
"be.ems/src/framework/logger"
)
// SocketTCP TCP服务端
type SocketTCP struct {
Addr string `json:"addr"` // 主机地址
Port int64 `json:"port"` // 端口
Listen *net.Listener `json:"listen"`
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)
ln, err := net.Listen(proto, address)
if err != nil {
return nil, err
}
s.Listen = &ln
s.StopChan = make(chan struct{}, 1)
return s, nil
}
// Close 关闭当前TCP服务端
func (s *SocketTCP) Close() {
if s.Listen != nil {
s.StopChan <- struct{}{}
(*s.Listen).Close()
}
}
// Resolve 处理消息
func (s *SocketTCP) Resolve(bufferSize int, callback func([]byte, int)) error {
if s.Listen == nil {
return fmt.Errorf("tcp service not created")
}
ln := *s.Listen
buffer := make([]byte, bufferSize)
for {
select {
case <-s.StopChan:
return fmt.Errorf("udp service stop")
default:
conn, err := ln.Accept()
if err != nil {
logger.Errorf("Error accepting connection: %v ", err)
continue
}
defer conn.Close()
// 读取数据
n, err := conn.Read(buffer)
if err != nil {
fmt.Println("Error reading from TCP connection:", err)
continue
}
callback(buffer, n)
// 发送响应
if _, err = conn.Write([]byte("tcp>")); err != nil {
fmt.Println("Error sending response:", err)
}
}
}
}

View File

@@ -0,0 +1,96 @@
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 := fmt.Sprintf("%s:%d", c.Addr, 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

@@ -0,0 +1,80 @@
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(bufferSize int, callback func([]byte, int)) error {
if s.Conn == nil {
return fmt.Errorf("udp service not created")
}
buffer := make([]byte, bufferSize)
for {
select {
case <-s.StopChan:
return fmt.Errorf("udp service stop")
default:
// 读取数据
n, addr, err := s.Conn.ReadFromUDP(buffer)
if err != nil {
fmt.Println("Error reading from UDP connection:", err)
continue
}
callback(buffer, n)
// 发送响应
if _, err = s.Conn.WriteToUDP([]byte("udp>"), addr); err != nil {
fmt.Println("Error sending response:", err)
}
}
}
}

View File

@@ -50,11 +50,6 @@ func (c *ConnTelnet) NewClient() (*ConnTelnet, error) {
// fmt.Fprintln(client, c.User)
// fmt.Fprintln(client, c.Password)
// 调整窗口大小 (120 列 x 128 行)
// 需要确保接收方理解并正确处理发送窗口大小设置命令
client.Write([]byte{255, 251, 31})
client.Write([]byte{255, 250, 31, byte(120 >> 8), byte(120 & 0xFF), byte(128 >> 8), byte(128 & 0xFF), 255, 240})
c.Client = &client
// 排空连接登录的信息
@@ -116,6 +111,6 @@ func (c *ConnTelnet) NewClientSession(cols, rows int) (*TelnetClientSession, err
s := &TelnetClientSession{
Client: *c.Client,
}
s.WindowChange(cols, rows)
// s.WindowChange(cols, rows)
return s, nil
}

View File

@@ -47,11 +47,11 @@ func (s *TelnetClientSession) Read() []byte {
buf := make([]byte, 1024)
// 设置读取超时时间为100毫秒
s.Client.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
_, err := s.Client.Read(buf)
n, err := s.Client.Read(buf)
if err != nil {
return []byte{}
}
return buf
return buf[:n]
}
// CombinedOutput 发送命令带结果返回

View File

@@ -87,7 +87,7 @@ func Post(url string, data url.Values, headers map[string]string) ([]byte, error
// PostJSON 发送 POST 请求,并将请求体序列化为 JSON 格式
func PostJSON(url string, data any, headers map[string]string) ([]byte, error) {
client := &http.Client{
Timeout: 3 * time.Second, // 超时时间
Timeout: 10 * time.Second, // 超时时间
}
jsonData, err := json.Marshal(data)
@@ -180,7 +180,7 @@ func PostUploadFile(url string, params map[string]string, file *os.File) ([]byte
// PutJSON 发送 PUT 请求,并将请求体序列化为 JSON 格式
func PutJSON(url string, data any, headers map[string]string) ([]byte, error) {
client := &http.Client{
Timeout: 3 * time.Second, // 超时时间
Timeout: 10 * time.Second, // 超时时间
}
jsonData, err := json.Marshal(data)

View File

@@ -17,46 +17,53 @@ import (
)
// Number 解析数值型
func Number(str any) int64 {
switch str := str.(type) {
func Number(value any) int64 {
switch v := value.(type) {
case string:
if str == "" {
if v == "" {
return 0
}
num, err := strconv.ParseInt(str, 10, 64)
num, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return 0
}
return num
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
return reflect.ValueOf(str).Int()
case int, int8, int16, int32, int64:
return reflect.ValueOf(v).Int()
case uint, uint8, uint16, uint32, uint64:
return int64(reflect.ValueOf(v).Uint())
case float32, float64:
return int64(reflect.ValueOf(str).Float())
return int64(reflect.ValueOf(v).Float())
case bool:
if v {
return 1
}
return 0
default:
return 0
}
}
// Boolean 解析布尔型
func Boolean(str any) bool {
switch str := str.(type) {
func Boolean(value any) bool {
switch v := value.(type) {
case string:
if str == "" || str == "false" || str == "0" {
b, err := strconv.ParseBool(v)
if err != nil {
return false
}
// 尝试将字符串解析为数字
if num, err := strconv.ParseFloat(str, 64); err == nil {
return num != 0
}
return true
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
num := reflect.ValueOf(str).Int()
return b
case int, int8, int16, int32, int64:
num := reflect.ValueOf(v).Int()
return num != 0
case uint, uint8, uint16, uint32, uint64:
num := int64(reflect.ValueOf(v).Uint())
return num != 0
case float32, float64:
num := reflect.ValueOf(str).Float()
num := reflect.ValueOf(v).Float()
return num != 0
case bool:
return str
return v
default:
return false
}

View File

@@ -8,7 +8,7 @@ import (
"os"
"time"
"be.ems/lib/core/datasource"
"be.ems/lib/dborm"
"be.ems/lib/log"
"be.ems/src/framework/cron"
)
@@ -103,7 +103,7 @@ func (s *BarProcessor) Execute(data any) (any, error) {
}
func (s *BarProcessor) exportData(query, filePath string) (int64, error) {
rows, err := datasource.DefaultDB().DB().Query(query)
rows, err := dborm.XCoreDB().Query(query)
if err != nil {
return 0, err
}

View File

@@ -141,10 +141,8 @@ func (s *SysCacheController) ClearCacheKey(c *gin.Context) {
func (s *SysCacheController) ClearCacheSafe(c *gin.Context) {
language := ctx.AcceptLanguage(c)
caches := []model.SysCache{
model.NewSysCacheNames(i18n.TKey(language, "cache.name.user"), cachekey.LOGIN_TOKEN_KEY),
model.NewSysCacheNames(i18n.TKey(language, "cache.name.sys_config"), cachekey.SYS_CONFIG_KEY),
model.NewSysCacheNames(i18n.TKey(language, "cache.name.sys_dict"), cachekey.SYS_DICT_KEY),
model.NewSysCacheNames(i18n.TKey(language, "cache.name.captcha_codes"), cachekey.CAPTCHA_CODE_KEY),
model.NewSysCacheNames(i18n.TKey(language, "cache.name.repeat_submit"), cachekey.REPEAT_SUBMIT_KEY),
model.NewSysCacheNames(i18n.TKey(language, "cache.name.rate_limit"), cachekey.RATE_LIMIT_KEY),
model.NewSysCacheNames(i18n.TKey(language, "cache.name.pwd_err_cnt"), cachekey.PWD_ERR_CNT_KEY),

View File

@@ -152,11 +152,11 @@ func (s *SystemInfoImpl) NetworkInfo() map[string]string {
func (s *SystemInfoImpl) DiskInfo() []map[string]string {
disks := make([]map[string]string, 0)
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
partitions, err := disk.PartitionsWithContext(ctx, false)
if err != context.DeadlineExceeded {
if err != nil && err != context.DeadlineExceeded {
return disks
}

View File

@@ -15,6 +15,7 @@ import (
"be.ems/src/framework/vo/result"
"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"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
@@ -22,8 +23,9 @@ import (
// 实例化控制层 SMFController 结构体
var NewSMFController = &SMFController{
neInfoService: neService.NewNeInfoImpl,
cdrEventService: neDataService.NewCDREventSMFImpl,
neInfoService: neService.NewNeInfoImpl,
cdrEventService: neDataService.NewCDREventSMFImpl,
udmUserInfoService: *neDataService.NewUDMUserInfo,
}
// 网元SMF
@@ -34,6 +36,8 @@ type SMFController struct {
neInfoService neService.INeInfo
// CDR会话事件服务
cdrEventService neDataService.ICDREventSMF
// UDM用户信息服务
udmUserInfoService neDataService.UDMUserInfo
}
// CDR会话列表
@@ -263,3 +267,63 @@ PDU IPv6 Addres Swith Prefix: %s`, User_Identifier, SSC_Mode, RAT_Type, DNN_ID,
c.FileAttachment(saveFilePath, fileName)
}
// 在线订阅用户列表信息
//
// GET /subscribers
func (s *SMFController) SubUserList(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var query struct {
NeId string `form:"neId" binding:"required"`
IMSI string `form:"imsi"`
MSISDN string `form:"msisdn"`
Upstate string `form:"upstate"`
PageNum string `form:"pageNum"`
}
if err := c.ShouldBindQuery(&query); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询网元信息 rmUID
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("SMF", query.NeId)
if neInfo.NeId != query.NeId || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
data, err := neFetchlink.SMFSubInfo(neInfo, map[string]string{
"imsi": query.IMSI,
"msisdn": query.MSISDN,
"upstate": query.Upstate,
"pageNum": query.PageNum,
})
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
// 对数据进行处理去掉前缀并加入imsi拓展信息
rows := data["rows"].([]any)
arr := &rows
for i := range *arr {
item := (*arr)[i].(map[string]any)
if v, ok := item["imsi"]; ok && v != nil {
imsiStr := v.(string)
imsiStr = strings.TrimPrefix(imsiStr, "imsi-")
item["imsi"] = imsiStr
// 查UDM拓展信息
info := s.udmUserInfoService.SelectByIMSIAndNeID(imsiStr, "")
item["remark"] = info.Remark
}
if v, ok := item["msisdn"]; ok && v != nil {
item["msisdn"] = strings.TrimPrefix(v.(string), "msisdn-")
}
}
c.JSON(200, result.Ok(map[string]any{
"total": data["total"],
"rows": data["rows"],
}))
}

View File

@@ -8,10 +8,10 @@ import (
"be.ems/src/framework/constants/uploadsubpath"
"be.ems/src/framework/i18n"
"be.ems/src/framework/telnet"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/telnet"
"be.ems/src/framework/vo/result"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
@@ -23,7 +23,7 @@ import (
// 实例化控制层 UDMAuthController 结构体
var NewUDMAuth = &UDMAuthController{
udmAuthService: neDataService.NewUDMAuthImpl,
udmAuthService: neDataService.NewUDMAuthUser,
neInfoService: neService.NewNeInfoImpl,
}
@@ -32,7 +32,7 @@ var NewUDMAuth = &UDMAuthController{
// PATH /udm/auth
type UDMAuthController struct {
// UDM鉴权信息服务
udmAuthService neDataService.IUDMAuth
udmAuthService *neDataService.UDMAuthUser
// 网元信息服务
neInfoService neService.INeInfo
}
@@ -103,26 +103,9 @@ func (s *UDMAuthController) Info(c *gin.Context) {
}
neId = ""
u := model.UDMAuth{
IMSI: imsi,
Amf: data["amf"],
Status: "1",
Ki: data["ki"],
AlgoIndex: data["algo"],
Opc: data["opc"],
NeId: neId,
}
// 查询imsi存在赋予id用于更新
list := s.udmAuthService.SelectList(u)
if len(list) > 0 {
item := list[0]
if item.ID != "" {
u.ID = item.ID
}
}
go s.udmAuthService.Insert(neId, u)
// 解析返回的数据
u := s.udmAuthService.ParseInfo(imsi, neId, data)
s.udmAuthService.Insert(neId, u)
c.JSON(200, result.OkData(u))
}
@@ -137,7 +120,7 @@ func (s *UDMAuthController) Add(c *gin.Context) {
return
}
var body model.UDMAuth
var body model.UDMAuthUser
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || body.IMSI == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
@@ -169,7 +152,7 @@ func (s *UDMAuthController) Add(c *gin.Context) {
// 命令ok时
if strings.Contains(data, "ok") {
neId = ""
go s.udmAuthService.Insert(neId, body)
s.udmAuthService.Insert(neId, body)
}
c.JSON(200, result.OkData(data))
}
@@ -186,7 +169,7 @@ func (s *UDMAuthController) Adds(c *gin.Context) {
return
}
var body model.UDMAuth
var body model.UDMAuthUser
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || body.IMSI == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
@@ -234,7 +217,7 @@ func (s *UDMAuthController) Edit(c *gin.Context) {
return
}
var body model.UDMAuth
var body model.UDMAuthUser
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || body.IMSI == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
@@ -279,7 +262,7 @@ func (s *UDMAuthController) Edit(c *gin.Context) {
// 命令ok时
if strings.Contains(data, "ok") {
neId = ""
go s.udmAuthService.Insert(neId, body)
s.udmAuthService.Insert(neId, body)
}
c.JSON(200, result.OkData(data))
}
@@ -330,7 +313,7 @@ func (s *UDMAuthController) Remove(c *gin.Context) {
// 命令ok时
if strings.Contains(data, "ok") {
neId = ""
go s.udmAuthService.Delete(neId, imsi)
s.udmAuthService.Delete(imsi, neId)
}
resultData[imsi] = data
}
@@ -402,7 +385,7 @@ func (s *UDMAuthController) Export(c *gin.Context) {
}
neId := ""
list := s.udmAuthService.SelectList(model.UDMAuth{NeId: neId})
list := s.udmAuthService.SelectList(model.UDMAuthUser{NeId: neId})
// 文件名
fileName := fmt.Sprintf("udm_auth_user_export_%s_%d.%s", neId, time.Now().UnixMilli(), body.Type)
filePath := fmt.Sprintf("%s/%s", file.ParseUploadFileDir(uploadsubpath.EXPORT), fileName)

View File

@@ -3,16 +3,15 @@ package controller
import (
"fmt"
"path/filepath"
"strconv"
"strings"
"time"
"be.ems/src/framework/constants/uploadsubpath"
"be.ems/src/framework/i18n"
"be.ems/src/framework/telnet"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/telnet"
"be.ems/src/framework/vo/result"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
@@ -23,7 +22,7 @@ import (
// 实例化控制层 UDMSubController 结构体
var NewUDMSub = &UDMSubController{
udmSubService: neDataService.NewUDMSubImpl,
udmSubService: neDataService.NewUDMSub,
neInfoService: neService.NewNeInfoImpl,
}
@@ -32,7 +31,7 @@ var NewUDMSub = &UDMSubController{
// PATH /udm/sub
type UDMSubController struct {
// UDM签约信息服务
udmSubService neDataService.IUDMSub
udmSubService *neDataService.UDMSubUser
// 网元信息服务
neInfoService neService.INeInfo
}
@@ -103,53 +102,10 @@ func (s *UDMSubController) Info(c *gin.Context) {
return
}
// 解析返回的数据
cnType, _ := strconv.ParseInt(data["CNType"][:4], 0, 64)
rat, _ := strconv.ParseInt(data["RAT"][:4], 0, 64)
msisdn := data["MSISDN"]
imsMsisdnLen := strings.Index(msisdn, ",")
if imsMsisdnLen != -1 {
msisdn = msisdn[:imsMsisdnLen]
}
neId = ""
u := model.UDMSub{
IMSI: imsi,
Msisdn: msisdn,
Ambr: data["AMBR"],
Arfb: data["AreaForbidden"],
Cn: fmt.Sprint(cnType),
SmData: data["SM-Data(snssai+dnn[1..n])"],
Sar: data["ServiceAreaRestriction"],
Nssai: data["NSSAI"],
SmfSel: data["Smf-Selection"],
Rat: fmt.Sprint(rat),
NeId: neId,
}
// 1,64,24,65,def_eps,1,2,010200000000,-
if v, ok := data["EPS-Data"]; ok {
u.EpsDat = v
arr := strings.Split(v, ",")
u.EpsFlag = arr[0]
u.EpsOdb = arr[1]
u.HplmnOdb = arr[2]
u.Ard = arr[3]
u.Epstpl = arr[4]
u.ContextId = arr[5]
u.ApnContext = arr[7]
// [6] 是不要的,导入和导出不用
u.StaticIp = arr[8]
}
// 查询imsi存在赋予id用于更新
list := s.udmSubService.SelectList(u)
if len(list) > 0 {
item := list[0]
if item.ID != "" {
u.ID = item.ID
}
}
go s.udmSubService.Insert(neId, u)
// 解析返回的数据
u := s.udmSubService.ParseInfo(imsi, neId, data)
s.udmSubService.Insert(neId, u)
c.JSON(200, result.OkData(u))
}
@@ -164,9 +120,9 @@ func (s *UDMSubController) Add(c *gin.Context) {
return
}
var body model.UDMSub
var body model.UDMSubUser
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || body.IMSI == "" {
if err != nil || len(body.IMSI) < 15 {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
@@ -186,8 +142,8 @@ func (s *UDMSubController) Add(c *gin.Context) {
defer telnetClient.Close()
// 发送MML
cmd := fmt.Sprintf("add udmuser:imsi=%s,msisdn=%s,ambr=%s,nssai=%s,arfb=%s,sar=%s,rat=%s,cn=%s,smf_sel=%s,sm_data=%s,eps_flag=%s,eps_odb=%s,hplmn_odb=%s,ard=%s,epstpl=%s,context_id=%s,apn_context=%s",
body.IMSI, body.Msisdn, body.Ambr, body.Nssai, body.Arfb, body.Sar, body.Rat, body.Cn, body.SmfSel, body.SmData, body.EpsFlag, body.EpsOdb, body.HplmnOdb, body.Ard, body.Epstpl, body.ContextId, body.ApnContext)
cmd := fmt.Sprintf("add udmuser:imsi=%s,msisdn=%s,ambr=%s,nssai=%s,arfb=%s,sar=%s,rat=%s,cn=%s,smf_sel=%s,sm_data=%s,eps_flag=%s,eps_odb=%s,hplmn_odb=%s,ard=%s,epstpl=%s,context_id=%s,apn_context=%s,cag=%s",
body.IMSI, body.MSISDN, body.Ambr, body.Nssai, body.Arfb, body.Sar, body.Rat, body.Cn, body.SmfSel, body.SmData, body.EpsFlag, body.EpsOdb, body.HplmnOdb, body.Ard, body.Epstpl, body.ContextId, body.ApnContext, body.Cag)
// static_ip指给4G UE分配的静态IP没有可不带此字段名批量添加IP会自动递增
if body.StaticIp != "" {
cmd += fmt.Sprintf(",static_ip=%s", body.StaticIp)
@@ -201,7 +157,8 @@ func (s *UDMSubController) Add(c *gin.Context) {
// 命令ok时
if strings.Contains(data, "ok") {
neId = ""
go s.udmSubService.Insert(neId, body)
body.NeId = neId
s.udmSubService.Insert(neId, body)
}
c.JSON(200, result.OkData(data))
}
@@ -218,9 +175,9 @@ func (s *UDMSubController) Adds(c *gin.Context) {
return
}
var body model.UDMSub
var body model.UDMSubUser
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || body.IMSI == "" {
if err != nil || len(body.IMSI) < 15 {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
@@ -240,8 +197,8 @@ func (s *UDMSubController) Adds(c *gin.Context) {
defer telnetClient.Close()
// 发送MML
cmd := fmt.Sprintf("baa udmuser:start_imsi=%s,start_msisdn=%s,sub_num=%s,ambr=%s,nssai=%s,arfb=%s,sar=%s,rat=%s,cn=%s,smf_sel=%s,sm_data=%s,eps_flag=%s,eps_odb=%s,hplmn_odb=%s,ard=%s,epstpl=%s,context_id=%s,apn_context=%s",
body.IMSI, body.Msisdn, num, body.Ambr, body.Nssai, body.Arfb, body.Sar, body.Rat, body.Cn, body.SmfSel, body.SmData, body.EpsFlag, body.EpsOdb, body.HplmnOdb, body.Ard, body.Epstpl, body.ContextId, body.ApnContext)
cmd := fmt.Sprintf("baa udmuser:start_imsi=%s,start_msisdn=%s,sub_num=%s,ambr=%s,nssai=%s,arfb=%s,sar=%s,rat=%s,cn=%s,smf_sel=%s,sm_data=%s,eps_flag=%s,eps_odb=%s,hplmn_odb=%s,ard=%s,epstpl=%s,context_id=%s,apn_context=%s,cag=%s",
body.IMSI, body.MSISDN, num, body.Ambr, body.Nssai, body.Arfb, body.Sar, body.Rat, body.Cn, body.SmfSel, body.SmData, body.EpsFlag, body.EpsOdb, body.HplmnOdb, body.Ard, body.Epstpl, body.ContextId, body.ApnContext, body.Cag)
// static_ip指给4G UE分配的静态IP没有可不带此字段名批量添加IP会自动递增
if body.StaticIp != "" {
cmd += fmt.Sprintf(",static_ip=%s", body.StaticIp)
@@ -255,7 +212,7 @@ func (s *UDMSubController) Adds(c *gin.Context) {
// 命令ok时
if strings.Contains(data, "ok") {
neId = ""
go s.udmSubService.LoadData(neId, body.IMSI, num)
go s.udmSubService.LoadData(neId, body.IMSI, num, body.Remark)
}
c.JSON(200, result.OkData(data))
}
@@ -271,9 +228,9 @@ func (s *UDMSubController) Edit(c *gin.Context) {
return
}
var body model.UDMSub
var body model.UDMSubUser
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || body.IMSI == "" {
if err != nil || len(body.IMSI) < 15 {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
@@ -295,8 +252,8 @@ func (s *UDMSubController) Edit(c *gin.Context) {
// 发送MML
cmd := fmt.Sprintf("mod udmuser:imsi=%s", body.IMSI)
// 修改的参数名称
if body.Msisdn != "" {
cmd += fmt.Sprintf(",msisdn=%s", body.Msisdn)
if body.MSISDN != "" {
cmd += fmt.Sprintf(",msisdn=%s", body.MSISDN)
}
if body.Ambr != "" {
cmd += fmt.Sprintf(",ambr=%s", body.Ambr)
@@ -346,6 +303,9 @@ func (s *UDMSubController) Edit(c *gin.Context) {
if body.ApnContext != "" {
cmd += fmt.Sprintf(",apn_context=%s", body.ApnContext)
}
if body.Cag != "" {
cmd += fmt.Sprintf(",cag=%s", body.Cag)
}
if body.StaticIp != "" {
cmd += fmt.Sprintf(",static_ip=%s", body.StaticIp)
}
@@ -358,7 +318,8 @@ func (s *UDMSubController) Edit(c *gin.Context) {
// 命令ok时
if strings.Contains(data, "ok") {
neId = ""
go s.udmSubService.Insert(neId, body)
body.NeId = neId
s.udmSubService.Insert(neId, body)
}
c.JSON(200, result.OkData(data))
}
@@ -370,7 +331,7 @@ func (s *UDMSubController) Remove(c *gin.Context) {
language := ctx.AcceptLanguage(c)
neId := c.Param("neId")
imsi := c.Param("imsi")
if neId == "" || imsi == "" {
if neId == "" || len(imsi) < 15 {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
@@ -409,7 +370,7 @@ func (s *UDMSubController) Remove(c *gin.Context) {
// 命令ok时
if strings.Contains(data, "ok") {
neId = ""
go s.udmSubService.Delete(neId, imsi)
s.udmSubService.Delete(neId, imsi)
}
resultData[imsi] = data
}
@@ -425,7 +386,7 @@ func (s *UDMSubController) Removes(c *gin.Context) {
neId := c.Param("neId")
imsi := c.Param("imsi")
num := c.Param("num")
if neId == "" || imsi == "" || num == "" {
if neId == "" || len(imsi) < 15 || num == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
@@ -455,7 +416,8 @@ func (s *UDMSubController) Removes(c *gin.Context) {
// 命令ok时
if strings.Contains(data, "ok") {
neId = ""
go s.udmSubService.LoadData(neId, imsi, num)
go s.udmSubService.LoadData(neId, imsi, num, "-(Deleted)-")
}
c.JSON(200, result.OkData(data))
}
@@ -481,7 +443,7 @@ func (s *UDMSubController) Export(c *gin.Context) {
}
neId := ""
list := s.udmSubService.SelectList(model.UDMSub{NeId: neId})
list := s.udmSubService.SelectList(model.UDMSubUser{NeId: neId})
// 文件名
fileName := fmt.Sprintf("udm_sub_user_export_%s_%d.%s", neId, time.Now().UnixMilli(), body.Type)
filePath := fmt.Sprintf("%s/%s", file.ParseUploadFileDir(uploadsubpath.EXPORT), fileName)
@@ -492,7 +454,7 @@ func (s *UDMSubController) Export(c *gin.Context) {
data = append(data, []string{"imsi", "msisdn", "ambr", "nssai", "arfb", "sar", "rat", "cn", "smf_sel", "sm_dat", "eps_dat"})
for _, v := range list {
epsDat := fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s", v.EpsFlag, v.EpsOdb, v.HplmnOdb, v.Ard, v.Epstpl, v.ContextId, v.ApnContext, v.StaticIp)
data = append(data, []string{v.IMSI, v.Msisdn, v.Ambr, v.Nssai, v.Arfb, v.Sar, v.Rat, v.Cn, v.SmfSel, v.SmData, epsDat})
data = append(data, []string{v.IMSI, v.MSISDN, v.Ambr, v.Nssai, v.Arfb, v.Sar, v.Rat, v.Cn, v.SmfSel, v.SmData, epsDat})
}
// 输出到文件
err = file.WriterFileCSV(data, filePath)
@@ -507,7 +469,7 @@ func (s *UDMSubController) Export(c *gin.Context) {
data := [][]string{}
for _, v := range list {
epsDat := fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s", v.EpsFlag, v.EpsOdb, v.HplmnOdb, v.Ard, v.Epstpl, v.ContextId, v.ApnContext, v.StaticIp)
data = append(data, []string{v.IMSI, v.Msisdn, v.Ambr, v.Nssai, v.Arfb, v.Sar, v.Rat, v.Cn, v.SmfSel, v.SmData, epsDat})
data = append(data, []string{v.IMSI, v.MSISDN, v.Ambr, v.Nssai, v.Arfb, v.Sar, v.Rat, v.Cn, v.SmfSel, v.SmData, epsDat})
}
// 输出到文件
err = file.WriterFileTXT(data, ",", filePath)

View File

@@ -32,9 +32,8 @@ type UPFController struct {
func (s *UPFController) TotalFlow(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var querys struct {
NeType string `json:"neType" form:"neType" binding:"required"`
NeID string `form:"neId" binding:"required"`
Day int `form:"day" binding:"required"`
NeID string `form:"neId" binding:"required"`
Day int `form:"day" binding:"required"`
}
if err := c.ShouldBindQuery(&querys); querys.Day < 0 || err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
@@ -42,7 +41,7 @@ func (s *UPFController) TotalFlow(c *gin.Context) {
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(querys.NeType, querys.NeID)
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("UPF", querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return

View File

@@ -1,17 +1,18 @@
package model
// UDMAuth UDM鉴权用户对象 u_auth_user
type UDMAuth struct {
// UDMAuthUser UDM鉴权用户 u_auth_user
type UDMAuthUser struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"` // 默认ID
IMSI string `json:"imsi" gorm:"column:imsi"` // SIM卡
Amf string `json:"amf" gorm:"column:amf"` // ANF
Status string `json:"status" gorm:"column:status"` // 状态 默认给1
Ki string `json:"ki" gorm:"column:ki"` // ki
AlgoIndex string `json:"algoIndex" gorm:"column:algo_index"` // AlgoIndex
Opc string `json:"opc" gorm:"column:opc"` // opc
NeId string `json:"neId" gorm:"column:ne_id"` // UDM网元标识-子系统
IMSI string `json:"imsi" gorm:"imsi"` // SIM卡/USIM卡ID
NeId string `json:"neId" gorm:"ne_id"` // UDM网元标识
Amf string `json:"amf" gorm:"amf"` // AMF
Status string `json:"status" gorm:"status"` // 状态
Ki string `json:"ki" gorm:"ki"` // ki
AlgoIndex string `json:"algoIndex" gorm:"algo_index"` // algoIndex
Opc string `json:"opc" gorm:"opc"` // OPC
}
func (UDMAuth) TableName() string {
// TableName 表名称
func (*UDMAuthUser) TableName() string {
return "u_auth_user"
}

View File

@@ -1,36 +1,39 @@
package model
// UDMSub UDM签约用户对象 u_sub_user
type UDMSub struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
Msisdn string `json:"msisdn" gorm:"column:msisdn"` // 相当手机号
IMSI string `json:"imsi" gorm:"column:imsi"` // SIM卡号
Ambr string `json:"ambr" gorm:"column:ambr"`
Nssai string `json:"nssai" gorm:"column:nssai"`
Rat string `json:"rat" gorm:"column:rat"`
Arfb string `json:"arfb" gorm:"column:arfb"`
Sar string `json:"sar" gorm:"column:sar"`
Cn string `json:"cn" gorm:"column:cn"`
SmData string `json:"smData" gorm:"column:sm_data"`
SmfSel string `json:"smfSel" gorm:"column:smf_sel"`
EpsDat string `json:"epsDat" gorm:"column:eps_dat"`
NeId string `json:"neId" gorm:"column:ne_id"` // UDM网元标识-子系统
// UDMSubUser UDM签约用户 u_sub_user
type UDMSubUser struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"` // 主键
IMSI string `json:"imsi" gorm:"imsi"` // SIM卡/USIM卡ID
MSISDN string `json:"msisdn" gorm:"msisdn"` // 用户电话号码
NeId string `json:"neId" gorm:"ne_id"` // UDM网元标识
Ambr string `json:"ambr" gorm:"ambr"` // SubUeAMBRTemp
Nssai string `json:"nssai" gorm:"nssai"` // SubSNSSAITemp
Rat string `json:"rat" gorm:"rat"` // rat VIRTUAL|WLAN|EUTRA|NR
Arfb string `json:"arfb" gorm:"arfb"` // forbiddenAreasTemp
Sar string `json:"sar" gorm:"sar"` // serviceAreaRestrictTemp
Cn string `json:"cn" gorm:"cn"` // cnType EPC|5GC
SmData string `json:"smData" gorm:"sm_data"` // smData
SmfSel string `json:"smfSel" gorm:"smf_sel"` // smfSel
EpsDat string `json:"epsDat" gorm:"eps_dat"` // Eps
EpsFlag string `json:"epsFlag" gorm:"eps_flag"` // epsFlag
EpsOdb string `json:"epsOdb" gorm:"eps_odb"` // epsOdb
HplmnOdb string `json:"hplmnOdb" gorm:"hplmn_odb"` // hplmnOdb
Ard string `json:"ard" gorm:"ard"` // Ard
Epstpl string `json:"epstpl" gorm:"epstpl"` // Epstpl
ContextId string `json:"contextId" gorm:"context_id"` // ContextId
ApnContext string `json:"apnContext" gorm:"apn_context"` // apnContext
StaticIp string `json:"staticIp" gorm:"static_ip"` // staticIpstatic_ip指给4G UE分配的静态IP没有可不带此字段名
Cag string `json:"cag" gorm:"cag"` // CAG
EpsFlag string `json:"epsFlag" gorm:"column:eps_flag"`
EpsOdb string `json:"epsOdb" gorm:"column:eps_odb"`
HplmnOdb string `json:"hplmnOdb" gorm:"column:hplmn_odb"`
Ard string `json:"ard" gorm:"column:ard"`
Epstpl string `json:"epstpl" gorm:"column:epstpl"`
ContextId string `json:"contextId" gorm:"column:context_id"`
ApnContext string `json:"apnContext" gorm:"column:apn_context"`
StaticIp string `json:"staticIp" gorm:"column:static_ip"`
TenantID string `json:"tenantID" gorm:"column:tenant_id"`
TenantName string `json:"tenantName" gorm:"-"`
// ====== 非数据库字段属性 ======
Remark string `json:"remark,omitempty" gorm:"-"` // 备注
}
func (UDMSub) TableName() string {
// TableName 表名称
func (*UDMSubUser) TableName() string {
return "u_sub_user"
}

View File

@@ -0,0 +1,15 @@
package model
// UDMUserInfo UDM用户IMSI扩展信息 u_user_info
type UDMUserInfo struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"` // 默认ID
IMSI string `json:"imsi" gorm:"column:imsi"` // SIM卡/USIM卡ID
MSISDN string `json:"msisdn" gorm:"column:msisdn"` // 用户电话号码
NeId string `json:"neId" gorm:"column:ne_id"` // UDM网元标识-子系统
Remark string `json:"remark" gorm:"remark"` // 备注
}
// TableName 表名称
func (*UDMUserInfo) TableName() string {
return "u_user_info"
}

View File

@@ -97,6 +97,10 @@ func Setup(router *gin.Engine) {
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.smfCDR", collectlogs.BUSINESS_TYPE_EXPORT)),
controller.NewSMFController.CDRExport,
)
smfGroup.GET("/subscribers",
middleware.PreAuthorize(nil),
controller.NewSMFController.SubUserList,
)
}
// 网元AMF

View File

@@ -1,26 +1,206 @@
package repository
import (
"fmt"
"strings"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/repo"
"be.ems/src/modules/network_data/model"
)
// UDM鉴权信息 数据层接口
type IUDMAuth interface {
// ClearAndInsert 清空ne_id后新增实体
ClearAndInsert(neId string, uArr []model.UDMAuth) int64
// 实例化数据层 UDMAuthUser 结构体
var NewUDMAuthUser = &UDMAuthUser{
selectSql: `select id, imsi, ne_id, amf, status, ki, algo_index, opc from u_auth_user`,
// SelectPage 根据条件分页查询
SelectPage(query map[string]any) map[string]any
// SelectList 根据实体查询
SelectList(u model.UDMAuth) []model.UDMAuth
// Insert 批量添加
Inserts(uArr []model.UDMAuth) int64
// Delete 删除实体
Delete(neId, imsi string) int64
// DeletePrefixByIMSI 删除前缀匹配的实体
DeletePrefixByIMSI(neId, imsi string) int64
resultMap: map[string]string{
"id": "ID",
"imsi": "IMSI",
"ne_id": "NeId",
"amf": "Amf",
"status": "Status",
"ki": "Ki",
"algo_index": "AlgoIndex",
"opc": "Opc",
},
}
// UDMAuthUser UDM鉴权信息表 数据层处理
type UDMAuthUser struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
resultMap map[string]string
}
// convertResultRows 将结果记录转实体结果组
func (r *UDMAuthUser) convertResultRows(rows []map[string]any) []model.UDMAuthUser {
arr := make([]model.UDMAuthUser, 0)
for _, row := range rows {
item := model.UDMAuthUser{}
for key, value := range row {
if keyMapper, ok := r.resultMap[key]; ok {
repo.SetFieldValue(&item, keyMapper, value)
}
}
arr = append(arr, item)
}
return arr
}
// ClearAndInsert 清空ne_id后新增实体
func (r *UDMAuthUser) ClearAndInsert(neId string, uArr []model.UDMAuthUser) int64 {
// 不指定neID时用 TRUNCATE 清空表快
_, err := datasource.ExecDB("", "TRUNCATE TABLE u_auth_user", nil)
if err != nil {
logger.Errorf("TRUNCATE err => %v", err)
}
return r.Inserts(uArr)
}
// SelectPage 根据条件分页查询
func (r *UDMAuthUser) SelectPage(query map[string]any) map[string]any {
// 查询条件拼接
var conditions []string
var params []any
if v, ok := query["imsi"]; ok && v != "" {
conditions = append(conditions, "imsi like concat(concat('%', ?), '%')")
params = append(params, strings.Trim(v.(string), " "))
}
if v, ok := query["neId"]; ok && v != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, v)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
result := map[string]any{
"total": 0,
"rows": []model.UDMAuthUser{},
}
// 查询数量 长度为0直接返回
totalSql := "select count(1) as 'total' from u_auth_user"
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
if err != nil {
logger.Errorf("total err => %v", err)
return result
}
total := parse.Number(totalRows[0]["total"])
if total == 0 {
return result
} else {
result["total"] = total
}
// 分页
pageNum, pageSize := repo.PageNumSize(query["pageNum"], query["pageSize"])
pageSql := " limit ?,? "
params = append(params, pageNum*pageSize)
params = append(params, pageSize)
// 排序
orderSql := ""
if v, ok := query["sortField"]; ok && v != "" {
sortSql := v.(string)
if o, ok := query["sortOrder"]; ok && o != nil && v != "" {
if o == "desc" {
sortSql += " desc "
} else {
sortSql += " asc "
}
}
orderSql = fmt.Sprintf(" order by %s ", sortSql)
}
// 查询数据
querySql := r.selectSql + whereSql + orderSql + pageSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
result["rows"] = r.convertResultRows(results)
return result
}
// SelectList 根据实体查询
func (r *UDMAuthUser) SelectList(u model.UDMAuthUser) []model.UDMAuthUser {
// 查询条件拼接
var conditions []string
var params []any
if u.IMSI != "" {
conditions = append(conditions, "imsi = ?")
params = append(params, u.IMSI)
}
if u.NeId != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, u.NeId)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
// 查询数据
querySql := r.selectSql + whereSql + " order by imsi asc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
return r.convertResultRows(results)
}
// SelectByIMSIAndNeID 通过imsi和ne_id查询
func (r *UDMAuthUser) SelectByIMSIAndNeID(imsi, neId string) model.UDMAuthUser {
querySql := r.selectSql + " where imsi = ? and ne_id = ?"
results, err := datasource.RawDB("", querySql, []any{imsi, neId})
if err != nil {
logger.Errorf("query err => %v", err)
return model.UDMAuthUser{}
}
// 转换实体
rows := r.convertResultRows(results)
if len(rows) > 0 {
return rows[0]
}
return model.UDMAuthUser{}
}
// Insert 批量添加
func (r *UDMAuthUser) Inserts(uArr []model.UDMAuthUser) int64 {
tx := datasource.DefaultDB().CreateInBatches(uArr, 3000)
if err := tx.Error; err != nil {
logger.Errorf("CreateInBatches err => %v", err)
}
return tx.RowsAffected
}
// Delete 删除实体
func (r *UDMAuthUser) Delete(imsi, neId string) int64 {
tx := datasource.DefaultDB().Where("imsi = ? and ne_id = ?", imsi, neId).Delete(&model.UDMAuthUser{})
if err := tx.Error; err != nil {
logger.Errorf("Delete err => %v", err)
}
return tx.RowsAffected
}
// DeletePrefixByIMSI 删除前缀匹配的实体
func (r *UDMAuthUser) DeletePrefixByIMSI(neId, imsi string) int64 {
tx := datasource.DefaultDB().Where("imsi like concat(?, '%') and ne_id = ?", imsi, neId).Delete(&model.UDMAuthUser{})
if err := tx.Error; err != nil {
logger.Errorf("DeletePrefixByIMSI err => %v", err)
}
return tx.RowsAffected
}

View File

@@ -1,26 +1,288 @@
package repository
import (
"fmt"
"strings"
dborm "be.ems/lib/core/datasource"
"be.ems/lib/log"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/repo"
"be.ems/src/modules/network_data/model"
)
// UDM签约信息 数据层接口
type IUDMSub interface {
// ClearAndInsert 清空ne_id后新增实体
ClearAndInsert(neId string, uArr []model.UDMSub) int64
// 实例化数据层 UDMSubUser 结构体
var NewUDMSub = &UDMSubUser{
selectSql: `select
s.id, s.msisdn, s.imsi, s.ambr, s.nssai, s.rat, s.arfb, s.sar, s.cn, s.sm_data, s.smf_sel, s.eps_dat,
s.ne_id, s.eps_flag, s.eps_odb, s.hplmn_odb, s.ard, s.epstpl, s.context_id, s.apn_context, s.static_ip,
s.cag,
t.tenant_id, t.tenant_name
from u_sub_user s
left join sys_tenant t on t.tenant_id = s.tenant_id`,
// SelectPage 根据条件分页查询
SelectPage(query map[string]any) map[string]any
resultMap: map[string]string{
"id": "ID",
"imsi": "IMSI",
"msisdn": "MSISDN",
"ne_id": "NeId",
"ambr": "Ambr",
"nssai": "Nssai",
"rat": "Rat",
"arfb": "Arfb",
"sar": "Sar",
"cn": "Cn",
"sm_data": "SmData",
"smf_sel": "SmfSel",
"eps_dat": "EpsDat",
"eps_flag": "EpsFlag",
"eps_odb": "EpsOdb",
"hplmn_odb": "HplmnOdb",
"ard": "Ard",
"epstpl": "Epstpl",
"context_id": "ContextId",
"apn_context": "ApnContext",
"static_ip": "StaticIp",
"cag": "Cag",
// SelectList 根据实体查询
SelectList(u model.UDMSub) []model.UDMSub
// Insert 批量添加
Inserts(uArr []model.UDMSub) int64
// Delete 删除实体
Delete(neId, imsi string) int64
// DeletePrefixByIMSI 删除前缀匹配的实体
DeletePrefixByIMSI(neId, imsi string) int64
"tenant_id": "TenantID",
"tenant_name": "TenantName", // Tenant name for multi-tenancy
},
}
// UDMSubUser UDM签约信息表 数据层处理
type UDMSubUser struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
resultMap map[string]string
}
// convertResultRows 将结果记录转实体结果组
func (r *UDMSubUser) convertResultRows(rows []map[string]any) []model.UDMSubUser {
arr := make([]model.UDMSubUser, 0)
for _, row := range rows {
item := model.UDMSubUser{}
for key, value := range row {
if keyMapper, ok := r.resultMap[key]; ok {
repo.SetFieldValue(&item, keyMapper, value)
}
}
arr = append(arr, item)
}
return arr
}
// ClearAndInsert 清空ne_id后新增实体
func (r *UDMSubUser) ClearAndInsert(neID string, u []model.UDMSubUser) int64 {
// 不指定neID时用 TRUNCATE 清空表快
_, err := datasource.ExecDB("", "TRUNCATE TABLE u_sub_user", nil)
if err != nil {
logger.Errorf("TRUNCATE err => %v", err)
}
return r.Inserts(u)
}
// SelectPage 根据条件分页查询字典类型
func (r *UDMSubUser) SelectPage(query map[string]any) map[string]any {
// 查询条件拼接
var conditions []string
var params []any
if v, ok := query["msisdn"]; ok && v != "" {
conditions = append(conditions, "msisdn like concat(concat('%', ?), '%')")
params = append(params, strings.Trim(v.(string), " "))
}
if v, ok := query["imsi"]; ok && v != "" {
//conditions = append(conditions, "imsi like concat(concat('%', ?), '%')")
//params = append(params, strings.Trim(v.(string), " "))
conditions = append(conditions, "imsi like ?")
params = append(params, v)
}
if v, ok := query["neId"]; ok && v != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, v)
}
// for multi-tenancy solution
if v, ok := query["tenantName"]; ok && v != "" {
var tenantID []string
err := dborm.DefaultDB().Table("sys_tenant").
Where("tenant_name=?", v).Cols("tenant_id").Distinct().Find(&tenantID)
if err != nil {
log.Errorf("Find tenant_id from sys_user err => %v", err)
}
log.Tracef("userName=%v, tenantID=%v", v, tenantID)
if len(tenantID) > 0 {
conditions = append(conditions, "s.tenant_id = ?")
params = append(params, tenantID[0])
}
} else if v, ok := query["userName"]; ok && v != "" {
var tenantID string
_, err := dborm.DefaultDB().Table("sys_user").
Where("user_name=?", v).Cols("tenant_id").Distinct().Get(&tenantID)
if err != nil {
log.Errorf("Find tenant_id from sys_user err => %v", err)
}
log.Tracef("userName=%v, tenantID=%v", v, tenantID)
if tenantID != "" {
conditions = append(conditions, "s.tenant_id = ?")
params = append(params, tenantID)
}
// if len(tenantID) > 0 {
// conditions = append(conditions, "s.tenant_id = ?")
// params = append(params, tenantID[0])
// }
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
result := map[string]any{
"total": 0,
"rows": []model.UDMSubUser{},
}
// 查询数量 长度为0直接返回
totalSql := "select count(1) as 'total' from u_sub_user s"
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
if err != nil {
logger.Errorf("total err => %v", err)
return result
}
total := parse.Number(totalRows[0]["total"])
if total == 0 {
return result
} else {
result["total"] = total
}
// 分页
pageNum, pageSize := repo.PageNumSize(query["pageNum"], query["pageSize"])
pageSql := " limit ?,? "
params = append(params, pageNum*pageSize)
params = append(params, pageSize)
// 排序
orderSql := ""
if v, ok := query["sortField"]; ok && v != "" {
sortSql := v.(string)
if o, ok := query["sortOrder"]; ok && o != nil && v != "" {
if o == "desc" {
sortSql += " desc "
} else {
sortSql += " asc "
}
}
orderSql = fmt.Sprintf(" order by %s ", sortSql)
}
// 查询数据
querySql := r.selectSql + whereSql + orderSql + pageSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
return result
}
// 转换实体
result["rows"] = r.convertResultRows(results)
return result
}
// SelectList 根据实体查询
func (r *UDMSubUser) SelectList(u model.UDMSubUser) []model.UDMSubUser {
// 查询条件拼接
var conditions []string
var params []any
if u.IMSI != "" {
conditions = append(conditions, "imsi = ?")
params = append(params, u.IMSI)
}
if u.NeId != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, u.NeId)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
// 查询数据
querySql := r.selectSql + whereSql + " order by imsi asc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
return r.convertResultRows(results)
}
// SelectByIMSIAndNeID 通过imsi和ne_id查询
func (r *UDMSubUser) SelectByIMSIAndNeID(imsi, neId string) model.UDMSubUser {
querySql := r.selectSql + " where imsi = ? and ne_id = ?"
results, err := datasource.RawDB("", querySql, []any{imsi, neId})
if err != nil {
logger.Errorf("query err => %v", err)
return model.UDMSubUser{}
}
// 转换实体
rows := r.convertResultRows(results)
if len(rows) > 0 {
return rows[0]
}
return model.UDMSubUser{}
}
// Insert 批量添加
func (r *UDMSubUser) Inserts(uArr []model.UDMSubUser) int64 {
// multi-tenancy
r.SetTenantID(&uArr)
tx := datasource.DefaultDB().CreateInBatches(uArr, 2000)
if err := tx.Error; err != nil {
logger.Errorf("CreateInBatches err => %v", err)
}
return tx.RowsAffected
}
// Delete 删除实体
func (r *UDMSubUser) Delete(imsi, neId string) int64 {
tx := datasource.DefaultDB().Where("imsi = ? and ne_id = ?", imsi, neId).Delete(&model.UDMSubUser{})
if err := tx.Error; err != nil {
logger.Errorf("Delete err => %v", err)
}
return tx.RowsAffected
}
// DeletePrefixByIMSI 删除前缀匹配的实体
func (r *UDMSubUser) DeletePrefixByIMSI(imsiPrefix, neId string) int64 {
tx := datasource.DefaultDB().Where("imsi like concat(?, '%') and ne_id = ?", imsiPrefix, neId).Delete(&model.UDMSubUser{})
if err := tx.Error; err != nil {
logger.Errorf("DeletePrefixByIMSI err => %v", err)
}
return tx.RowsAffected
}
func (r *UDMSubUser) SetTenantID(subArr *[]model.UDMSubUser) {
for s := 0; s < len(*subArr); s++ {
var tenantID []string
err := dborm.DefaultDB().Table("sys_tenant").
Where("status='1' and tenancy_type='IMSI' and ? like tenancy_key", (*subArr)[s].IMSI).
Cols("parent_id").Distinct().Find(&tenantID)
if err != nil {
logger.Errorf("Find tenant_id err => %v", err)
continue
}
if len(tenantID) > 0 {
(*subArr)[s].TenantID = tenantID[0]
}
}
}

View File

@@ -1,268 +0,0 @@
package repository
import (
"fmt"
"strings"
dborm "be.ems/lib/core/datasource"
"be.ems/lib/log"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/repo"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 UDMSubImpl 结构体
var NewUDMSubImpl = &UDMSubImpl{
selectSql: `select
s.id, s.msisdn, s.imsi, s.ambr, s.nssai, s.rat, s.arfb, s.sar, s.cn, s.sm_data, s.smf_sel, s.eps_dat,
s.ne_id, s.eps_flag, s.eps_odb, s.hplmn_odb, s.ard, s.epstpl, s.context_id, s.apn_context, s.static_ip,
t.tenant_id, t.tenant_name
from u_sub_user s
left join sys_tenant t on t.tenant_id = s.tenant_id`,
resultMap: map[string]string{
"id": "ID",
"msisdn": "Msisdn",
"imsi": "IMSI",
"ambr": "Ambr",
"nssai": "Nssai",
"rat": "Rat",
"arfb": "Arfb",
"sar": "Sar",
"cn": "Cn",
"sm_data": "SmData",
"smf_sel": "SmfSel",
"eps_dat": "EpsDat",
"ne_id": "NeId",
"eps_flag": "EpsFlag",
"eps_odb": "EpsOdb",
"hplmn_odb": "HplmnOdb",
"ard": "Ard",
"epstpl": "Epstpl",
"context_id": "ContextId",
"apn_context": "ApnContext",
"static_ip": "StaticIp",
"tenant_id": "TenantID",
"tenant_name": "TenantName", // Tenant name for multi-tenancy
},
}
// UDMSubImpl UDM签约信息表 数据层处理
type UDMSubImpl struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
resultMap map[string]string
}
// convertResultRows 将结果记录转实体结果组
func (r *UDMSubImpl) convertResultRows(rows []map[string]any) []model.UDMSub {
arr := make([]model.UDMSub, 0)
for _, row := range rows {
item := model.UDMSub{}
for key, value := range row {
if keyMapper, ok := r.resultMap[key]; ok {
repo.SetFieldValue(&item, keyMapper, value)
}
}
arr = append(arr, item)
}
return arr
}
// ClearAndInsert 清空ne_id后新增实体
func (r *UDMSubImpl) ClearAndInsert(neID string, u []model.UDMSub) int64 {
// 不指定neID时用 TRUNCATE 清空表快
_, err := datasource.ExecDB("", "TRUNCATE TABLE u_sub_user", nil)
if err != nil {
logger.Errorf("TRUNCATE err => %v", err)
}
return r.Inserts(u)
}
// SelectPage 根据条件分页查询字典类型
func (r *UDMSubImpl) SelectPage(query map[string]any) map[string]any {
// 查询条件拼接
var conditions []string
var params []any
if v, ok := query["msisdn"]; ok && v != "" {
conditions = append(conditions, "msisdn like concat(concat('%', ?), '%')")
params = append(params, strings.Trim(v.(string), " "))
}
if v, ok := query["imsi"]; ok && v != "" {
//conditions = append(conditions, "imsi like concat(concat('%', ?), '%')")
//params = append(params, strings.Trim(v.(string), " "))
conditions = append(conditions, "imsi like ?")
params = append(params, v)
}
if v, ok := query["neId"]; ok && v != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, v)
}
// for multi-tenancy solution
if v, ok := query["tenantName"]; ok && v != "" {
var tenantID []string
err := dborm.DefaultDB().Table("sys_tenant").
Where("tenant_name=?", v).Cols("tenant_id").Distinct().Find(&tenantID)
if err != nil {
log.Errorf("Find tenant_id from sys_user err => %v", err)
}
log.Tracef("userName=%v, tenantID=%v", v, tenantID)
if len(tenantID) > 0 {
conditions = append(conditions, "s.tenant_id = ?")
params = append(params, tenantID[0])
}
} else if v, ok := query["userName"]; ok && v != "" {
var tenantID string
_, err := dborm.DefaultDB().Table("sys_user").
Where("user_name=?", v).Cols("tenant_id").Distinct().Get(&tenantID)
if err != nil {
log.Errorf("Find tenant_id from sys_user err => %v", err)
}
log.Tracef("userName=%v, tenantID=%v", v, tenantID)
if tenantID != "" {
conditions = append(conditions, "s.tenant_id = ?")
params = append(params, tenantID)
}
// if len(tenantID) > 0 {
// conditions = append(conditions, "s.tenant_id = ?")
// params = append(params, tenantID[0])
// }
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
result := map[string]any{
"total": 0,
"rows": []model.UDMSub{},
}
// 查询数量 长度为0直接返回
totalSql := "select count(1) as 'total' from u_sub_user s"
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
if err != nil {
logger.Errorf("total err => %v", err)
return result
}
total := parse.Number(totalRows[0]["total"])
if total == 0 {
return result
} else {
result["total"] = total
}
// 分页
pageNum, pageSize := repo.PageNumSize(query["pageNum"], query["pageSize"])
pageSql := " limit ?,? "
params = append(params, pageNum*pageSize)
params = append(params, pageSize)
// 排序
orderSql := ""
if v, ok := query["sortField"]; ok && v != "" {
sortSql := v.(string)
if o, ok := query["sortOrder"]; ok && o != nil && v != "" {
if o == "desc" {
sortSql += " desc "
} else {
sortSql += " asc "
}
}
orderSql = fmt.Sprintf(" order by %s ", sortSql)
}
// 查询数据
querySql := r.selectSql + whereSql + orderSql + pageSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
return result
}
// 转换实体
result["rows"] = r.convertResultRows(results)
return result
}
// SelectList 根据实体查询
func (r *UDMSubImpl) SelectList(u model.UDMSub) []model.UDMSub {
// 查询条件拼接
var conditions []string
var params []any
if u.IMSI != "" {
conditions = append(conditions, "imsi = ?")
params = append(params, u.IMSI)
}
if u.NeId != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, u.NeId)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
// 查询数据
querySql := r.selectSql + whereSql + " order by imsi asc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
return r.convertResultRows(results)
}
// Insert 批量添加
func (r *UDMSubImpl) Inserts(uArr []model.UDMSub) int64 {
// multi-tenancy
r.SetTenantID(&uArr)
tx := datasource.DefaultDB().CreateInBatches(uArr, 2000)
if err := tx.Error; err != nil {
logger.Errorf("CreateInBatches err => %v", err)
}
return tx.RowsAffected
}
// Delete 删除实体
func (r *UDMSubImpl) Delete(neId, imsi string) int64 {
tx := datasource.DefaultDB().Where("imsi = ? and ne_id = ?", imsi, neId).Delete(&model.UDMSub{})
if err := tx.Error; err != nil {
logger.Errorf("Delete err => %v", err)
}
return tx.RowsAffected
}
// DeletePrefixByIMSI 删除前缀匹配的实体
func (r *UDMSubImpl) DeletePrefixByIMSI(neId, imsi string) int64 {
tx := datasource.DefaultDB().Where("imsi like concat(?, '%') and ne_id = ?", imsi, neId).Delete(&model.UDMSub{})
if err := tx.Error; err != nil {
logger.Errorf("DeletePrefixByIMSI err => %v", err)
}
return tx.RowsAffected
}
func (r *UDMSubImpl) SetTenantID(subArr *[]model.UDMSub) {
for s := 0; s < len(*subArr); s++ {
var tenantID []string
err := dborm.DefaultDB().Table("sys_tenant").
Where("status='1' and tenancy_type='IMSI' and ? like tenancy_key", (*subArr)[s].IMSI).
Cols("parent_id").Distinct().Find(&tenantID)
if err != nil {
logger.Errorf("Find tenant_id err => %v", err)
continue
}
if len(tenantID) > 0 {
(*subArr)[s].TenantID = tenantID[0]
}
}
}

View File

@@ -11,24 +11,21 @@ import (
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 UDMAuthImpl 结构体
var NewUDMAuthImpl = &UDMAuthImpl{
selectSql: `select id, imsi, amf, status, ki, algo_index, opc, ne_id from u_auth_user`,
// 实例化数据层 UDMUserInfo 结构体
var NewUDMUserInfo = &UDMUserInfo{
selectSql: `select id, imsi, msisdn, ne_id, remark from u_user_info`,
resultMap: map[string]string{
"id": "ID",
"imsi": "IMSI",
"amf": "Amf",
"status": "Status",
"ki": "Ki",
"algo_index": "AlgoIndex",
"opc": "Opc",
"ne_id": "NeId",
"id": "ID",
"imsi": "IMSI",
"msisdn": "MSISDN",
"ne_id": "NeId",
"remark": "Remark",
},
}
// UDMAuthImpl UDM鉴权信息表 数据层处理
type UDMAuthImpl struct {
// UDMUserInfo UDM鉴权信息表 数据层处理
type UDMUserInfo struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
@@ -36,10 +33,10 @@ type UDMAuthImpl struct {
}
// convertResultRows 将结果记录转实体结果组
func (r *UDMAuthImpl) convertResultRows(rows []map[string]any) []model.UDMAuth {
arr := make([]model.UDMAuth, 0)
func (r *UDMUserInfo) convertResultRows(rows []map[string]any) []model.UDMUserInfo {
arr := make([]model.UDMUserInfo, 0)
for _, row := range rows {
item := model.UDMAuth{}
item := model.UDMUserInfo{}
for key, value := range row {
if keyMapper, ok := r.resultMap[key]; ok {
repo.SetFieldValue(&item, keyMapper, value)
@@ -50,18 +47,8 @@ func (r *UDMAuthImpl) convertResultRows(rows []map[string]any) []model.UDMAuth {
return arr
}
// ClearAndInsert 清空ne_id后新增实体
func (r *UDMAuthImpl) ClearAndInsert(neId string, uArr []model.UDMAuth) int64 {
// 不指定neID时用 TRUNCATE 清空表快
_, err := datasource.ExecDB("", "TRUNCATE TABLE u_auth_user", nil)
if err != nil {
logger.Errorf("TRUNCATE err => %v", err)
}
return r.Inserts(uArr)
}
// SelectPage 根据条件分页查询
func (r *UDMAuthImpl) SelectPage(query map[string]any) map[string]any {
func (r *UDMUserInfo) SelectPage(query map[string]any) map[string]any {
// 查询条件拼接
var conditions []string
var params []any
@@ -82,11 +69,11 @@ func (r *UDMAuthImpl) SelectPage(query map[string]any) map[string]any {
result := map[string]any{
"total": 0,
"rows": []model.UDMAuth{},
"rows": []model.UDMUserInfo{},
}
// 查询数量 长度为0直接返回
totalSql := "select count(1) as 'total' from u_auth_user"
totalSql := "select count(1) as 'total' from u_user_info"
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
if err != nil {
logger.Errorf("total err => %v", err)
@@ -132,7 +119,7 @@ func (r *UDMAuthImpl) SelectPage(query map[string]any) map[string]any {
}
// SelectList 根据实体查询
func (r *UDMAuthImpl) SelectList(u model.UDMAuth) []model.UDMAuth {
func (r *UDMUserInfo) SelectList(u model.UDMUserInfo) []model.UDMUserInfo {
// 查询条件拼接
var conditions []string
var params []any
@@ -162,8 +149,24 @@ func (r *UDMAuthImpl) SelectList(u model.UDMAuth) []model.UDMAuth {
return r.convertResultRows(results)
}
// SelectByIMSIAndNeID 通过imsi和ne_id查询
func (r *UDMUserInfo) SelectByIMSIAndNeID(imsi, neId string) model.UDMUserInfo {
querySql := r.selectSql + " where imsi = ? and ne_id = ?"
results, err := datasource.RawDB("", querySql, []any{imsi, neId})
if err != nil {
logger.Errorf("query err => %v", err)
return model.UDMUserInfo{}
}
// 转换实体
rows := r.convertResultRows(results)
if len(rows) > 0 {
return rows[0]
}
return model.UDMUserInfo{}
}
// Insert 批量添加
func (r *UDMAuthImpl) Inserts(uArr []model.UDMAuth) int64 {
func (r *UDMUserInfo) Inserts(uArr []model.UDMUserInfo) int64 {
tx := datasource.DefaultDB().CreateInBatches(uArr, 3000)
if err := tx.Error; err != nil {
logger.Errorf("CreateInBatches err => %v", err)
@@ -172,8 +175,8 @@ func (r *UDMAuthImpl) Inserts(uArr []model.UDMAuth) int64 {
}
// Delete 删除实体
func (r *UDMAuthImpl) Delete(neId, imsi string) int64 {
tx := datasource.DefaultDB().Where("imsi = ? and ne_id = ?", imsi, neId).Delete(&model.UDMAuth{})
func (r *UDMUserInfo) Delete(imsi, neId string) int64 {
tx := datasource.DefaultDB().Where("imsi = ? and ne_id = ?", imsi, neId).Delete(&model.UDMUserInfo{})
if err := tx.Error; err != nil {
logger.Errorf("Delete err => %v", err)
}
@@ -181,8 +184,8 @@ func (r *UDMAuthImpl) Delete(neId, imsi string) int64 {
}
// DeletePrefixByIMSI 删除前缀匹配的实体
func (r *UDMAuthImpl) DeletePrefixByIMSI(neId, imsi string) int64 {
tx := datasource.DefaultDB().Where("imsi like concat(?, '%') and ne_id = ?", imsi, neId).Delete(&model.UDMAuth{})
func (r *UDMUserInfo) DeletePrefixByIMSI(imsiPrefix, neId string) int64 {
tx := datasource.DefaultDB().Where("imsi like concat(?, '%') and ne_id = ?", imsiPrefix, neId).Delete(&model.UDMUserInfo{})
if err := tx.Error; err != nil {
logger.Errorf("DeletePrefixByIMSI err => %v", err)
}

View File

@@ -1,28 +1,172 @@
package service
import "be.ems/src/modules/network_data/model"
import (
"fmt"
"strconv"
"strings"
// UDM鉴权信息 服务层接口
type IUDMAuth interface {
// ResetData 重置鉴权用户数据清空数据库重新同步Redis数据
ResetData(neId string) int64
"be.ems/src/framework/redis"
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
)
// SelectPage 分页查询数据库
SelectPage(query map[string]any) map[string]any
// SelectList 查询数据库
SelectList(u model.UDMAuth) []model.UDMAuth
// Insert 从数据中读取后删除imsi再存入数据库
// imsi长度15ki长度32opc长度0或者32
Insert(neId string, u model.UDMAuth) int64
// InsertData 导入文件数据 dataType目前两种txt/csv
InsertData(neId, dataType string, data any) int64
// Delete 删除单个不重新加载
Delete(neID, imsi string) int64
// LoadData 删除范围后重新加载 num表示imsi后几位
LoadData(neID, imsi, num string) int64
// 实例化服务层 UDMAuthUser 结构体
var NewUDMAuthUser = &UDMAuthUser{
udmAuthRepository: repository.NewUDMAuthUser,
}
// UDM鉴权信息 服务层处理
type UDMAuthUser struct {
// UDM鉴权信息数据信息
udmAuthRepository *repository.UDMAuthUser
}
// dataByRedis UDM鉴权用户 db:0 中 ausf:*
func (r *UDMAuthUser) dataByRedis(imsi, neId string) []model.UDMAuthUser {
arr := []model.UDMAuthUser{}
key := fmt.Sprintf("ausf:%s", imsi)
ausfArr, err := redis.GetKeys("udmuser", key)
if err != nil {
return arr
}
for _, key := range ausfArr {
m, err := redis.GetHash("udmuser", key)
if err != nil {
continue
}
// 跳过-号数据 ausf:360000100000130
imsi := key[5:]
if strings.Contains(imsi, "-") {
continue
}
amf := ""
if v, ok := m["amf"]; ok {
amf = strings.Replace(v, "\r\n", "", 1)
}
a := model.UDMAuthUser{
IMSI: imsi,
Amf: amf,
Status: "1", // 默认给1
Ki: m["ki"],
AlgoIndex: m["algo"],
Opc: m["opc"],
NeId: neId,
}
arr = append(arr, a)
}
return arr
}
// ResetData 重置鉴权用户数据清空数据库重新同步Redis数据
func (r *UDMAuthUser) ResetData(neId string) int64 {
authArr := r.dataByRedis("*", neId)
// 数据清空后添加
go r.udmAuthRepository.ClearAndInsert(neId, authArr)
return int64(len(authArr))
}
// ParseInfo 解析单个用户imsi鉴权信息 data从命令MML得到的结果
func (r *UDMAuthUser) ParseInfo(imsi, neId string, data map[string]string) model.UDMAuthUser {
u := model.UDMAuthUser{
IMSI: imsi,
NeId: neId,
Amf: data["amf"],
Status: "1",
Ki: data["ki"],
AlgoIndex: data["algo"],
Opc: data["opc"],
}
// 补充用户ID用于更新
auth := r.udmAuthRepository.SelectByIMSIAndNeID(imsi, neId)
if auth.IMSI == imsi {
u.ID = auth.ID
}
return u
}
// SelectPage 分页查询数据库
func (r *UDMAuthUser) SelectPage(query map[string]any) map[string]any {
return r.udmAuthRepository.SelectPage(query)
}
// SelectList 查询数据库
func (r *UDMAuthUser) SelectList(u model.UDMAuthUser) []model.UDMAuthUser {
return r.udmAuthRepository.SelectList(u)
}
// Insert 从数据中读取后删除imsi再存入数据库
// imsi长度15ki长度32opc长度0或者32
func (r *UDMAuthUser) Insert(neId string, u model.UDMAuthUser) int64 {
uArr := r.dataByRedis(u.IMSI, neId)
if len(uArr) > 0 {
r.udmAuthRepository.Delete(u.IMSI, neId)
return r.udmAuthRepository.Inserts(uArr)
}
return 0
}
// InsertData 导入文件数据 dataType目前两种txt/csv
func (r *UDMAuthUser) InsertData(neId, dataType string, data any) int64 {
// imsi截取前缀,重新获取部分数据
prefixes := make(map[string]struct{})
if dataType == "csv" {
for _, v := range data.([]map[string]string) {
imsi := v["imsi"]
if len(imsi) < 6 {
continue
}
prefix := imsi[:len(imsi)-4]
prefixes[prefix] = struct{}{}
}
}
if dataType == "txt" {
for _, v := range data.([][]string) {
imsi := v[0]
if len(imsi) < 6 {
continue
}
prefix := imsi[:len(imsi)-4]
prefixes[prefix] = struct{}{}
}
}
// 根据前缀重新加载插入
var num int64 = 0
for prefix := range prefixes {
// 直接删除前缀的记录
r.udmAuthRepository.DeletePrefixByIMSI(neId, prefix)
// keys ausf:4600001000004*
arr := r.dataByRedis(prefix+"*", neId)
if len(arr) > 0 {
num += r.udmAuthRepository.Inserts(arr)
}
}
return num
}
// Delete 删除单个不重新加载
func (r *UDMAuthUser) Delete(imsi, neId string) int64 {
return r.udmAuthRepository.Delete(imsi, neId)
}
// LoadData 重新加载从imsi开始num的数据
func (r *UDMAuthUser) LoadData(neId, imsi, num string) {
startIMSI, _ := strconv.ParseInt(imsi, 10, 64)
subNum, _ := strconv.ParseInt(num, 10, 64)
var i int64
for i = 0; i < subNum; i++ {
keyIMSI := fmt.Sprintf("%015d", startIMSI+i)
// 删除原数据
r.udmAuthRepository.Delete(keyIMSI, neId)
// 加载数据
arr := r.dataByRedis(keyIMSI, neId)
if len(arr) < 1 {
continue
}
r.udmAuthRepository.Inserts(arr)
}
}

View File

@@ -1,146 +0,0 @@
package service
import (
"fmt"
"strings"
"be.ems/src/framework/redis"
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
)
// 实例化服务层 UDMAuthImpl 结构体
var NewUDMAuthImpl = &UDMAuthImpl{
udmAuthRepository: repository.NewUDMAuthImpl,
}
// UDM鉴权信息 服务层处理
type UDMAuthImpl struct {
// UDM鉴权信息数据信息
udmAuthRepository repository.IUDMAuth
}
// dataByRedis UDM鉴权用户 db:0 中 ausf:*
func (r *UDMAuthImpl) dataByRedis(imsi, neId string) []model.UDMAuth {
arr := []model.UDMAuth{}
key := fmt.Sprintf("ausf:%s", imsi)
ausfArr, err := redis.GetKeys("udmuser", key)
if err != nil {
return arr
}
for _, key := range ausfArr {
m, err := redis.GetHash("udmuser", key)
if err != nil {
continue
}
// 跳过-号数据
imsi := key[5:]
if strings.Contains(imsi, "-") {
continue
}
amf := ""
if v, ok := m["amf"]; ok {
amf = strings.Replace(v, "\r\n", "", 1)
}
a := model.UDMAuth{
IMSI: imsi,
Amf: amf,
Status: "1", // 默认给1
Ki: m["ki"],
AlgoIndex: m["algo"],
Opc: m["opc"],
NeId: neId,
}
arr = append(arr, a)
}
return arr
}
// ResetData 重置鉴权用户数据清空数据库重新同步Redis数据
func (r *UDMAuthImpl) ResetData(neId string) int64 {
authArr := r.dataByRedis("*", neId)
// 数据清空后添加
go r.udmAuthRepository.ClearAndInsert(neId, authArr)
return int64(len(authArr))
}
// SelectPage 分页查询数据库
func (r *UDMAuthImpl) SelectPage(query map[string]any) map[string]any {
return r.udmAuthRepository.SelectPage(query)
}
// SelectList 查询数据库
func (r *UDMAuthImpl) SelectList(u model.UDMAuth) []model.UDMAuth {
return r.udmAuthRepository.SelectList(u)
}
// Insert 从数据中读取后删除imsi再存入数据库
// imsi长度15ki长度32opc长度0或者32
func (r *UDMAuthImpl) Insert(neId string, u model.UDMAuth) int64 {
uArr := r.dataByRedis(u.IMSI, neId)
if len(uArr) > 0 {
r.udmAuthRepository.Delete(neId, u.IMSI)
return r.udmAuthRepository.Inserts(uArr)
}
return 0
}
// InsertData 导入文件数据 dataType目前两种txt/csv
func (r *UDMAuthImpl) InsertData(neId, dataType string, data any) int64 {
// imsi截取前缀,重新获取部分数据
prefixes := make(map[string]struct{})
if dataType == "csv" {
for _, v := range data.([]map[string]string) {
imsi := v["imsi"]
if len(imsi) < 6 {
continue
}
prefix := imsi[:len(imsi)-4]
prefixes[prefix] = struct{}{}
}
}
if dataType == "txt" {
for _, v := range data.([][]string) {
imsi := v[0]
if len(imsi) < 6 {
continue
}
prefix := imsi[:len(imsi)-4]
prefixes[prefix] = struct{}{}
}
}
// 根据前缀重新加载插入
var num int64 = 0
for prefix := range prefixes {
// 直接删除前缀的记录
r.udmAuthRepository.DeletePrefixByIMSI(neId, prefix)
// keys ausf:4600001000004*
authArr := r.dataByRedis(prefix+"*", neId)
if len(authArr) > 0 {
num += r.udmAuthRepository.Inserts(authArr)
}
}
return num
}
// Delete 删除单个不重新加载
func (r *UDMAuthImpl) Delete(neId, imsi string) int64 {
return r.udmAuthRepository.Delete(neId, imsi)
}
// LoadData 删除范围后重新加载 num表示imsi后几位
func (r *UDMAuthImpl) LoadData(neId, imsi, num string) int64 {
prefix := imsi[:len(imsi)-len(num)-1]
// 直接删除前缀的记录
delNum := r.udmAuthRepository.DeletePrefixByIMSI(neId, prefix)
// keys ausf:4600001000004*
authArr := r.dataByRedis(prefix+"*", neId)
if len(authArr) > 0 {
return r.udmAuthRepository.Inserts(authArr)
}
return delNum
}

View File

@@ -1,28 +1,254 @@
package service
import "be.ems/src/modules/network_data/model"
import (
"fmt"
"strconv"
"strings"
// UDM签约用户信息 服务层接口
type IUDMSub interface {
// ResetData 重置鉴权用户数据清空数据库重新同步Redis数据
ResetData(neId string) int64
"be.ems/src/framework/redis"
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
)
// SelectPage 分页查询数据库
SelectPage(query map[string]any) map[string]any
// SelectList 查询数据库
SelectList(u model.UDMSub) []model.UDMSub
// Insert 从数据中读取后删除imsi再存入数据库
// imsi长度15ki长度32opc长度0或者32
Insert(neId string, u model.UDMSub) int64
// InsertData 导入文件数据 dataType目前两种txt/csv
InsertData(neId, dataType string, data any) int64
// Delete 删除单个不重新加载
Delete(neId, imsi string) int64
// LoadData 删除范围后重新加载 num表示imsi后几位
LoadData(neId, imsi, num string) int64
// 实例化服务层 UDMSubUser 结构体
var NewUDMSub = &UDMSubUser{
udmSubRepository: repository.NewUDMSub,
udmUserInfoRepository: repository.NewUDMUserInfo,
}
// UDM签约信息 服务层处理
type UDMSubUser struct {
// UDM签约信息数据信息
udmSubRepository *repository.UDMSubUser
// UDM用户IMSI信息数据信息
udmUserInfoRepository *repository.UDMUserInfo
}
// dataByRedis UDM签约用户 db:0 中 udm-sd:*
func (r *UDMSubUser) dataByRedis(imsi, neId string) []model.UDMSubUser {
arr := []model.UDMSubUser{}
key := fmt.Sprintf("udm-sd:%s", imsi)
udmsdArr, err := redis.GetKeys("udmuser", key)
if err != nil {
return arr
}
for _, key := range udmsdArr {
m, err := redis.GetHash("udmuser", key)
if err != nil {
continue
}
a := model.UDMSubUser{
IMSI: key[7:], // udm-sd:360000100000130
MSISDN: m["gpsi"], // 8612300000130
NeId: neId,
SmfSel: m["smf-sel"], // def_snssai
SmData: m["sm-dat"], // 1-000001&cmnet&ims&3gnet
Cag: m["cag"], // def_cag
}
// def_ambr,def_nssai,0,def_arfb,def_sar,3,1,12000,1,1000,0,1,-
if v, ok := m["am-dat"]; ok {
arr := strings.Split(v, ",")
a.Ambr = arr[0]
a.Nssai = arr[1]
a.Rat = arr[2]
a.Arfb = arr[3]
a.Sar = arr[4]
a.Cn = arr[5]
}
// 1,64,24,65,def_eps,1,2,010200000000,-
if v, ok := m["eps-dat"]; ok {
arr := strings.Split(v, ",")
// 跳过非常规数据
if len(arr) > 9 {
continue
}
a.EpsDat = v
a.EpsFlag = arr[0]
a.EpsOdb = arr[1]
a.HplmnOdb = arr[2]
a.Ard = arr[3]
a.Epstpl = arr[4]
a.ContextId = arr[5]
a.ApnContext = arr[7]
// [6] 是不要的,导入和导出不用
a.StaticIp = arr[8]
}
arr = append(arr, a)
}
return arr
}
// ResetData 重置鉴权用户数据清空数据库重新同步Redis数据
func (r *UDMSubUser) ResetData(neId string) int64 {
subArr := r.dataByRedis("*", neId)
// 数据清空后添加
go r.udmSubRepository.ClearAndInsert(neId, subArr)
return int64(len(subArr))
}
// ParseInfo 解析单个用户imsi签约信息 data从命令MML得到的结果
func (r *UDMSubUser) ParseInfo(imsi, neId string, data map[string]string) model.UDMSubUser {
cnType, _ := strconv.ParseInt(data["CNType"][:4], 0, 64)
rat, _ := strconv.ParseInt(data["RAT"][:4], 0, 64)
msisdn := data["MSISDN"]
if imsMsisdnLen := strings.Index(msisdn, ","); imsMsisdnLen != -1 {
msisdn = msisdn[:imsMsisdnLen]
}
u := model.UDMSubUser{
IMSI: imsi,
MSISDN: msisdn,
NeId: neId,
Ambr: data["AMBR"],
Arfb: data["AreaForbidden"],
Cn: fmt.Sprint(cnType),
SmData: data["SM-Data(snssai+dnn[1..n])"],
Sar: data["ServiceAreaRestriction"],
Nssai: data["NSSAI"],
SmfSel: data["Smf-Selection"],
Cag: data["cag"],
Rat: fmt.Sprint(rat),
}
// 1,64,24,65,def_eps,1,2,010200000000,-
if v, ok := data["EPS-Data"]; ok {
u.EpsDat = v
arr := strings.Split(v, ",")
u.EpsFlag = arr[0]
u.EpsOdb = arr[1]
u.HplmnOdb = arr[2]
u.Ard = arr[3]
u.Epstpl = arr[4]
u.ContextId = arr[5]
u.ApnContext = arr[7]
// [6] 是不要的,导入和导出不用
u.StaticIp = arr[8]
}
// 补充用户ID用于更新
sub := r.udmSubRepository.SelectByIMSIAndNeID(imsi, neId)
if sub.IMSI == imsi {
u.ID = sub.ID
}
// 补充用户拓展信息
info := r.udmUserInfoRepository.SelectByIMSIAndNeID(imsi, neId)
if info.IMSI == imsi {
u.Remark = info.Remark
}
return u
}
// SelectPage 分页查询数据库
func (r *UDMSubUser) SelectPage(query map[string]any) map[string]any {
return r.udmSubRepository.SelectPage(query)
}
// SelectList 查询数据库
func (r *UDMSubUser) SelectList(u model.UDMSubUser) []model.UDMSubUser {
return r.udmSubRepository.SelectList(u)
}
// Insert 从数据中读取后删除imsi再存入数据库
// imsi长度15ki长度32opc长度0或者32
func (r *UDMSubUser) Insert(neId string, u model.UDMSubUser) int64 {
uArr := r.dataByRedis(u.IMSI, neId)
if len(uArr) > 0 {
r.udmSubRepository.Delete(u.IMSI, neId)
// 新增到拓展信息
if u.Remark != "" {
r.udmUserInfoRepository.Delete(u.IMSI, neId)
r.udmUserInfoRepository.Inserts([]model.UDMUserInfo{{
IMSI: u.IMSI,
MSISDN: u.MSISDN,
NeId: u.NeId,
Remark: u.Remark,
}})
}
return r.udmSubRepository.Inserts(uArr)
}
return 0
}
// InsertData 导入文件数据 dataType目前两种txt/csv
func (r *UDMSubUser) InsertData(neId, dataType string, data any) int64 {
// imsi截取前缀,重新获取部分数据
prefixes := make(map[string]struct{})
if dataType == "csv" {
for _, v := range data.([]map[string]string) {
imsi := v["imsi"]
if len(imsi) < 6 {
continue
}
prefix := imsi[:len(imsi)-4]
prefixes[prefix] = struct{}{}
}
}
if dataType == "txt" {
for _, v := range data.([][]string) {
imsi := v[0]
if len(imsi) < 6 {
continue
}
prefix := imsi[:len(imsi)-4]
prefixes[prefix] = struct{}{}
}
}
// 根据前缀重新加载插入
var num int64 = 0
for prefix := range prefixes {
// keys udm-sd:4600001000004*
arr := r.dataByRedis(prefix+"*", neId)
if len(arr) > 0 {
r.udmSubRepository.DeletePrefixByIMSI(prefix, neId)
num += r.udmSubRepository.Inserts(arr)
}
}
return num
}
// Delete 删除单个不重新加载
func (r *UDMSubUser) Delete(neId, imsi string) int64 {
// 删除拓展信息
r.udmUserInfoRepository.Delete(imsi, neId)
return r.udmSubRepository.Delete(imsi, neId)
}
// LoadData 重新加载从imsi开始num的数据
// remark不为空则新增到拓展信息删除标记为-(Deleted)-
func (r *UDMSubUser) LoadData(neId, imsi, num, remark string) {
startIMSI, _ := strconv.ParseInt(imsi, 10, 64)
subNum, _ := strconv.ParseInt(num, 10, 64)
var i int64
for i = 0; i < subNum; i++ {
keyIMSI := fmt.Sprintf("%015d", startIMSI+i)
// 删除原数据
r.udmSubRepository.Delete(keyIMSI, neId)
if remark == "-(Deleted)-" {
r.udmUserInfoRepository.Delete(keyIMSI, neId)
}
// 加载数据
arr := r.dataByRedis(keyIMSI, neId)
if len(arr) < 1 {
continue
}
r.udmSubRepository.Inserts(arr)
// 拓展信息
if remark != "" {
uarr := make([]model.UDMUserInfo, 0, len(arr))
for _, v := range arr {
uarr = append(uarr, model.UDMUserInfo{
IMSI: v.IMSI,
MSISDN: v.MSISDN,
NeId: v.NeId,
Remark: remark,
})
}
r.udmUserInfoRepository.Delete(keyIMSI, neId)
r.udmUserInfoRepository.Inserts(uarr)
}
}
}

View File

@@ -1,164 +0,0 @@
package service
import (
"fmt"
"strings"
"be.ems/src/framework/redis"
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
)
// 实例化服务层 UDMSubImpl 结构体
var NewUDMSubImpl = &UDMSubImpl{
udmSubRepository: repository.NewUDMSubImpl,
}
// UDM签约信息 服务层处理
type UDMSubImpl struct {
// UDM签约信息数据信息
udmSubRepository repository.IUDMSub
}
// dataByRedis UDM签约用户 db:0 中 udm-sd:*
func (r *UDMSubImpl) dataByRedis(imsi, neId string) []model.UDMSub {
arr := []model.UDMSub{}
key := fmt.Sprintf("udm-sd:%s", imsi)
udmsdArr, err := redis.GetKeys("udmuser", key)
if err != nil {
return arr
}
for _, key := range udmsdArr {
m, err := redis.GetHash("udmuser", key)
if err != nil {
continue
}
a := model.UDMSub{
IMSI: key[7:],
Msisdn: m["gpsi"], // 46003550072
SmfSel: m["smf-sel"],
SmData: m["sm-dat"], // 1-000001&cmnet&ims&3gnet
NeId: neId,
}
// def_ambr,def_nssai,0,def_arfb,def_sar,3,1,12000,1,1000,0,1,-
if v, ok := m["am-dat"]; ok {
arr := strings.Split(v, ",")
a.Ambr = arr[0]
a.Nssai = arr[1]
a.Rat = arr[2]
a.Arfb = arr[3]
a.Sar = arr[4]
a.Cn = arr[5]
}
// 1,64,24,65,def_eps,1,2,010200000000,-
if v, ok := m["eps-dat"]; ok {
arr := strings.Split(v, ",")
// 跳过非常规数据
if len(arr) > 9 {
continue
}
a.EpsDat = v
a.EpsFlag = arr[0]
a.EpsOdb = arr[1]
a.HplmnOdb = arr[2]
a.Ard = arr[3]
a.Epstpl = arr[4]
a.ContextId = arr[5]
a.ApnContext = arr[7]
// [6] 是不要的,导入和导出不用
a.StaticIp = arr[8]
}
arr = append(arr, a)
}
return arr
}
// ResetData 重置鉴权用户数据清空数据库重新同步Redis数据
func (r *UDMSubImpl) ResetData(neId string) int64 {
subArr := r.dataByRedis("*", neId)
// 数据清空后添加
go r.udmSubRepository.ClearAndInsert(neId, subArr)
return int64(len(subArr))
}
// SelectPage 分页查询数据库
func (r *UDMSubImpl) SelectPage(query map[string]any) map[string]any {
return r.udmSubRepository.SelectPage(query)
}
// SelectList 查询数据库
func (r *UDMSubImpl) SelectList(u model.UDMSub) []model.UDMSub {
return r.udmSubRepository.SelectList(u)
}
// Insert 从数据中读取后删除imsi再存入数据库
// imsi长度15ki长度32opc长度0或者32
func (r *UDMSubImpl) Insert(neId string, u model.UDMSub) int64 {
uArr := r.dataByRedis(u.IMSI, neId)
if len(uArr) > 0 {
r.udmSubRepository.Delete(neId, u.IMSI)
return r.udmSubRepository.Inserts(uArr)
}
return 0
}
// InsertData 导入文件数据 dataType目前两种txt/csv
func (r *UDMSubImpl) InsertData(neId, dataType string, data any) int64 {
// imsi截取前缀,重新获取部分数据
prefixes := make(map[string]struct{})
if dataType == "csv" {
for _, v := range data.([]map[string]string) {
imsi := v["imsi"]
if len(imsi) < 6 {
continue
}
prefix := imsi[:len(imsi)-4]
prefixes[prefix] = struct{}{}
}
}
if dataType == "txt" {
for _, v := range data.([][]string) {
imsi := v[0]
if len(imsi) < 6 {
continue
}
prefix := imsi[:len(imsi)-4]
prefixes[prefix] = struct{}{}
}
}
// 根据前缀重新加载插入
var num int64 = 0
for prefix := range prefixes {
// 直接删除前缀的记录
r.udmSubRepository.DeletePrefixByIMSI(neId, prefix)
// keys udm-sd:4600001000004*
subArr := r.dataByRedis(prefix+"*", neId)
if len(subArr) > 0 {
num += r.udmSubRepository.Inserts(subArr)
}
}
return num
}
// Delete 删除单个不重新加载
func (r *UDMSubImpl) Delete(neId, imsi string) int64 {
return r.udmSubRepository.Delete(neId, imsi)
}
// LoadData 删除范围后重新加载 num表示imsi后几位
func (r *UDMSubImpl) LoadData(neId, imsi, num string) int64 {
prefix := imsi[:len(imsi)-len(num)-1]
// 直接删除前缀的记录
delNum := r.udmSubRepository.DeletePrefixByIMSI(neId, prefix)
// keys udm-sd:4600001000004*
authArr := r.dataByRedis(prefix+"*", neId)
if len(authArr) > 0 {
return r.udmSubRepository.Inserts(authArr)
}
return delNum
}

View File

@@ -0,0 +1,33 @@
package service
import (
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
)
// 实例化服务层 UDMUserInfo 结构体
var NewUDMUserInfo = &UDMUserInfo{
udmUserInfoRepository: repository.NewUDMUserInfo,
}
// UDM用户IMSI拓展信息 服务层处理
type UDMUserInfo struct {
// UDM用户IMSI信息数据信息
udmUserInfoRepository *repository.UDMUserInfo
}
// SelectByIMSIAndNeID 通过IMSI和网元标识查询信息
func (r *UDMUserInfo) SelectByIMSIAndNeID(imsi, neId string) model.UDMUserInfo {
return r.udmUserInfoRepository.SelectByIMSIAndNeID(imsi, neId)
}
// Save 新增或修改信息
func (r *UDMUserInfo) Save(u model.UDMUserInfo) bool {
r.udmUserInfoRepository.Delete(u.IMSI, u.NeId)
return r.udmUserInfoRepository.Inserts([]model.UDMUserInfo{u}) > 0
}
// Delete 删除信息
func (r *UDMUserInfo) Delete(imsi, neId string) int64 {
return r.udmUserInfoRepository.Delete(imsi, neId)
}

View File

@@ -4,6 +4,7 @@ import (
"encoding/json"
"strings"
cm_omc "be.ems/features/cm/omc"
"be.ems/src/framework/i18n"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/parse"
@@ -191,14 +192,25 @@ func (s *NeConfigController) DataInfo(c *gin.Context) {
return
}
// 网元直连
resData, err := neFetchlink.NeConfigInfo(neInfo, query.ParamName)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
if query.NeType == "OMC" {
var o *cm_omc.ConfigOMC
resData, err := o.Query(query.ParamName)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.OkData(resData))
return
}
} else {
// 网元直连
resData, err := neFetchlink.NeConfigInfo(neInfo, query.ParamName)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.Ok(resData))
c.JSON(200, result.Ok(resData))
}
}
// 网元参数配置数据修改
@@ -223,14 +235,24 @@ func (s *NeConfigController) DataEdit(c *gin.Context) {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元直连
resData, err := neFetchlink.NeConfigUpdate(neInfo, body.ParamName, body.Loc, body.ParamData)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
if body.NeType == "OMC" {
var o *cm_omc.ConfigOMC
resData, err := o.Modify(body.ParamName, body.ParamData)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.OkData(resData))
return
} else {
// 网元直连
resData, err := neFetchlink.NeConfigUpdate(neInfo, body.ParamName, body.Loc, body.ParamData)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.OkData(resData))
}
c.JSON(200, result.OkData(resData))
}
// 网元参数配置数据新增array

View File

@@ -4,10 +4,10 @@ import (
"strings"
"be.ems/src/framework/i18n"
"be.ems/src/framework/telnet"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/ssh"
"be.ems/src/framework/utils/telnet"
"be.ems/src/framework/vo/result"
"be.ems/src/modules/network_element/model"
neService "be.ems/src/modules/network_element/service"

View File

@@ -193,7 +193,7 @@ func (s *NeInfoController) OAMFileRead(c *gin.Context) {
return
}
data, err := s.neInfoService.NeConfOAMRead(querys.NeType, querys.NeID)
data, err := s.neInfoService.NeConfOAMReadSync(querys.NeType, querys.NeID)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
@@ -224,7 +224,7 @@ func (s *NeInfoController) OAMFileWrite(c *gin.Context) {
return
}
err := s.neInfoService.NeConfOAMSync(neInfo, body.Content, body.Sync)
err := s.neInfoService.NeConfOAMWirteSync(neInfo, body.Content, body.Sync)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return

View File

@@ -0,0 +1,68 @@
// 网元HLR服务8080端口。
// 融合到UDM网元也许是UDM的HLR服务。
package fetchlink
import (
"encoding/json"
"fmt"
"strings"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/fetch"
"be.ems/src/modules/network_element/model"
)
// HLRTraceStart HLR跟踪任务开始
//
// data参数 {traceID:"跟踪任务ID", imsi:"IMSI和MSISDN必填一个都带的话以IMSI为准", msisdn:""}
func HLRTraceStart(neInfo model.NeInfo, data map[string]any) (string, error) {
// 网元参数配置新增array
neUrl := fmt.Sprintf("http://%s:%d/trace-manage/v1/add-task", neInfo.IP, neInfo.Port)
resBytes, err := fetch.PostJSON(neUrl, data, nil)
var resData map[string]string
if err != nil {
errStr := err.Error()
logger.Warnf("HLRTraceStart Post \"%s\"", neUrl)
logger.Errorf("HLRTraceStart %s", errStr)
return "", fmt.Errorf("NeService HLR API Error")
}
// 序列化结果
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("HLRTraceStart Unmarshal %s", err.Error())
return "", err
}
if v, ok := resData["code"]; ok && v == "0" {
return strings.TrimSpace(strings.ToLower(resData["message"])), nil
}
return "", fmt.Errorf(resData["message"])
}
// HLRTraceStop HLR跟踪任务停止
//
// data参数 {traceIDArray: ["跟踪任务ID数组"]}
func HLRTraceStop(neInfo model.NeInfo, data map[string]any) (string, error) {
// 网元参数配置新增array
neUrl := fmt.Sprintf("http://%s:%d/trace-manage/v1/delete-task", neInfo.IP, neInfo.Port)
resBytes, err := fetch.PostJSON(neUrl, data, nil)
var resData map[string]string
if err != nil {
errStr := err.Error()
logger.Warnf("HLRTraceStop Post \"%s\"", neUrl)
logger.Errorf("HLRTraceStop %s", errStr)
return "", fmt.Errorf("NeService HLR API Error")
}
// 序列化结果
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("HLRTraceStop Unmarshal %s", err.Error())
return "", err
}
if v, ok := resData["code"]; ok && v == "0" {
return strings.TrimSpace(strings.ToLower(resData["message"])), nil
}
return "", fmt.Errorf(resData["message"])
}

View File

@@ -52,9 +52,9 @@ func NeConfigOMC(neInfo model.NeInfo) (map[string]any, error) {
// NeConfigInfo 网元配置信息
func NeConfigInfo(neInfo model.NeInfo, paramName string) (map[string]any, error) {
// 网元配置对端网管信息
// 网元参数配置信息
neUrl := fmt.Sprintf("http://%s:%d/api/rest/systemManagement/v1/elementType/%s/objectType/config/%s", neInfo.IP, neInfo.Port, strings.ToLower(neInfo.NeType), paramName)
resBytes, err := fetch.Get(neUrl, nil, 60_000)
resBytes, err := fetch.Get(neUrl, nil, 30_000)
if err != nil {
logger.Warnf("NeConfigInfo Get \"%s\"", neUrl)
logger.Errorf("NeConfigInfo %s", err.Error())

View File

@@ -0,0 +1,121 @@
package fetchlink
import (
"encoding/json"
"fmt"
"strings"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/fetch"
"be.ems/src/modules/network_element/model"
)
// NeTraceInfo 网元跟踪任务信息
func NeTraceInfo(neInfo model.NeInfo, traceId string) (map[string]any, error) {
// 跟踪任务信息
neUrl := fmt.Sprintf("http://%s:%d/api/rest/traceManagement/v1/subscriptions?id=%s", neInfo.IP, neInfo.Port, traceId)
resBytes, err := fetch.Get(neUrl, nil, 30_000)
if err != nil {
logger.Warnf("NeTraceInfo Get \"%s\"", neUrl)
logger.Errorf("NeTraceInfo %s", err.Error())
return nil, fmt.Errorf("NeService Trace Info API Error")
}
// 序列化结果
var resData map[string]any
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("NeTraceInfo Unmarshal %s", err.Error())
return nil, err
}
return resData, nil
}
// NeTraceAdd 网元跟踪任务新增
func NeTraceAdd(neInfo model.NeInfo, data map[string]any) (map[string]any, error) {
// 跟踪任务创建
neUrl := fmt.Sprintf("http://%s:%d/api/rest/traceManagement/v1/subscriptions", neInfo.IP, neInfo.Port)
resBytes, err := fetch.PostJSON(neUrl, data, nil)
var resData map[string]any
if err != nil {
errStr := err.Error()
logger.Warnf("NeTraceAdd POST \"%s\"", neUrl)
if !(strings.HasPrefix(errStr, "201") || strings.HasPrefix(errStr, "400")) {
logger.Errorf("NeTraceAdd %s", errStr)
return nil, fmt.Errorf("NeService Trace Add API Error")
}
}
// 200 成功无数据时
if len(resBytes) == 0 {
return resData, nil
}
// 序列化结果
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("NeTraceAdd Unmarshal %s", err.Error())
return nil, err
}
return resData, nil
}
// NeTraceEdit 网元跟踪任务编辑
func NeTraceEdit(neInfo model.NeInfo, data map[string]any) (map[string]any, error) {
// 网元参数配置新增array
neUrl := fmt.Sprintf("http://%s:%d/api/rest/traceManagement/v1/subscriptions", neInfo.IP, neInfo.Port)
resBytes, err := fetch.PutJSON(neUrl, data, nil)
var resData map[string]any
if err != nil {
errStr := err.Error()
logger.Warnf("NeTraceEdit PUT \"%s\"", neUrl)
if strings.HasPrefix(errStr, "201") || strings.HasPrefix(errStr, "204") {
return resData, nil
}
logger.Errorf("NeTraceEdit %s", errStr)
return nil, fmt.Errorf("NeService Trace Edit API Error")
}
// 200 成功无数据时
if len(resBytes) == 0 {
return resData, nil
}
// 序列化结果
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("NeTraceEdit Unmarshal %s", err.Error())
return nil, err
}
return resData, nil
}
// NeTraceDelete 网元跟踪任务删除
func NeTraceDelete(neInfo model.NeInfo, traceId string) (map[string]any, error) {
// 网元参数配置删除array
neUrl := fmt.Sprintf("http://%s:%d/api/rest/traceManagement/v1/subscriptions?id=%s", neInfo.IP, neInfo.Port, traceId)
resBytes, err := fetch.Delete(neUrl, nil)
var resData map[string]any
if err != nil {
errStr := err.Error()
logger.Warnf("NeTraceDelete Delete \"%s\"", neUrl)
if strings.HasPrefix(errStr, "201") || strings.HasPrefix(errStr, "204") {
return resData, nil
}
logger.Errorf("NeTraceDelete %s", errStr)
return nil, fmt.Errorf("NeService Trace Delete API Error")
}
// 200 成功无数据时
if len(resBytes) == 0 {
return resData, nil
}
// 序列化结果
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("NeTraceDelete Unmarshal %s", err.Error())
return nil, err
}
return resData, nil
}

View File

@@ -0,0 +1,68 @@
package fetchlink
import (
"encoding/json"
"fmt"
"strings"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/fetch"
"be.ems/src/modules/network_element/model"
)
// SMFSubInfo SMF在线订阅用户列表信息
//
// 查询参数 {"imsi":"360000100000130","msisdn":"8612300000130","upstate":"Inactive","pageNum":"1"}
//
// 返回结果 {"rows":[],"total":0}
func SMFSubInfo(neInfo model.NeInfo, data map[string]string) (map[string]any, error) {
// neUrl := "http://127.0.0.1:4523/m1/3157310-1528434-82b449ee/api/rest/ueManagement/v1/elementType/smf/objectType/ueInfo?apifoxApiId=150640017"
neUrl := fmt.Sprintf("http://%s:%s/api/rest/ueManagement/v1/elementType/smf/objectType/ueInfo", "172.16.20.150", "33030")
// neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/smf/objectType/ueInfo", neInfo.IP, neInfo.Port)
// 查询参数拼接
query := []string{}
if v, ok := data["imsi"]; ok && v != "" {
query = append(query, fmt.Sprintf("imsi=%s", v))
}
if v, ok := data["msisdn"]; ok && v != "" {
query = append(query, fmt.Sprintf("msisdn=%s", v))
}
if v, ok := data["upstate"]; ok && v != "" {
query = append(query, fmt.Sprintf("upstate=%s", v))
}
// 固定页数量50条
if v, ok := data["pageNum"]; ok && v != "" {
query = append(query, fmt.Sprintf("pageNum=%s", v))
}
if len(query) > 0 {
neUrl = fmt.Sprintf("%s?%s", neUrl, strings.Join(query, "&"))
}
resBytes, err := fetch.Get(neUrl, nil, 60_000)
if err != nil {
logger.Warnf("SMFSubInfo Get \"%s\"", neUrl)
logger.Errorf("SMFSubInfo %s", err.Error())
return nil, fmt.Errorf("NeService SMF API Error")
}
// 序列化结果 {"data":[],"total":0}
var resData map[string]any
err = json.Unmarshal(resBytes, &resData)
if err != nil {
logger.Errorf("SMFSubInfo Unmarshal %s", err.Error())
return nil, err
}
// 固定返回字段,方便前端解析
if v, ok := resData["data"]; ok && v != nil {
resData["rows"] = v.([]any)
delete(resData, "data")
} else {
resData["rows"] = []any{}
}
if v, ok := resData["total"]; !ok || v == nil {
resData["total"] = 0
}
return resData, nil
}

View File

@@ -1,3 +1,6 @@
// 网元UDM服务可能是8080、33030端口服务
// 融合的UDM网元视情况调整。
package fetchlink
import (
@@ -25,8 +28,7 @@ func UDMImportAuth(udmIP string, data map[string]any) (string, error) {
}
// 序列化结果
err = json.Unmarshal(resBytes, &resData)
if err != nil {
if err = json.Unmarshal(resBytes, &resData); err != nil {
logger.Errorf("UDMImportAuth Unmarshal %s", err.Error())
return "", err
}

View File

@@ -21,7 +21,7 @@ import (
const (
// 数据库
DbHost = "192.168.8.58"
DbHost = "127.0.0.1"
DbPort = 33066
DbUser = "root"
DbPassswd = "1000omc@kp!"
@@ -29,10 +29,10 @@ const (
// 配置文件路径
configParamDir = "../../../config/param"
// configParamFile = "*" // 目录下全部更新
configParamFile = "cbc_param_config.yaml" // 单文件更新
configParamFile = "omc_param_config.yaml" // 单文件更新
)
func TestEncrypt(t *testing.T) {
func TestConfig(t *testing.T) {
fileNameList, err := getDirFileNameList(configParamDir)
if err != nil {
log.Fatal(err)

View File

@@ -2,7 +2,6 @@ package repository
import (
"fmt"
"sort"
"strings"
"time"
@@ -82,29 +81,21 @@ func (r *NeInfoImpl) convertResultRows(rows []map[string]any) []model.NeInfo {
arr = append(arr, item)
}
// 排序
sort.Slice(arr, func(i, j int) bool {
// 前一个
after := arr[i]
afterIndex := 0
for i, v := range neListSort {
if after.NeType == v {
afterIndex = i
break
// 创建优先级映射
priority := make(map[string]int)
for i, v := range neListSort {
priority[v] = i
}
// 冒泡排序
n := len(arr)
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
if priority[arr[j].NeType] > priority[arr[j+1].NeType] {
// 交换元素
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
// 后一个
befter := arr[j]
befterIndex := 0
for i, v := range neListSort {
if befter.NeType == v {
befterIndex = i
break
}
}
// 升序
return afterIndex < befterIndex
})
}
return arr
}
@@ -188,7 +179,7 @@ func (r *NeInfoImpl) SelectPage(query map[string]any) map[string]any {
params = append(params, pageSize)
// 查询数据
querySql := r.selectSql + whereSql + " order by ne_type asc, ne_id desc " + pageSql
querySql := r.selectSql + whereSql + " order by ne_type asc, ne_id asc " + pageSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
@@ -221,7 +212,7 @@ func (r *NeInfoImpl) SelectList(neInfo model.NeInfo) []model.NeInfo {
}
// 查询数据
querySql := r.selectSql + whereSql + " order by ne_type asc, ne_id desc "
querySql := r.selectSql + whereSql + " order by ne_type asc, ne_id asc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)

View File

@@ -10,7 +10,7 @@ type INeHost interface {
// SelectList 根据实体查询
SelectList(neHost model.NeHost) []model.NeHost
// SelectByIds 通过ID查询
// SelectById 通过ID查询
SelectById(hostId string) model.NeHost
// CheckUniqueHostTitle 校验分组组和主机名称是否唯一

View File

@@ -1,8 +1,8 @@
package service
import (
"be.ems/src/framework/telnet"
"be.ems/src/framework/utils/ssh"
"be.ems/src/framework/utils/telnet"
"be.ems/src/modules/network_element/model"
)
@@ -58,11 +58,11 @@ type INeInfo interface {
// num 是网元主机telnet 14100 25200
NeRunTelnetClient(neType, neId string, num int) (*telnet.ConnTelnet, error)
// neConfOAMRead 网元OAM配置文件读取
NeConfOAMRead(neType, neId string) (map[string]any, error)
// NeConfOAMReadSync 网元OAM配置文件读取
NeConfOAMReadSync(neType, neId string) (map[string]any, error)
// NeConfOAMSync 网元OAM配置文件生成并同步
NeConfOAMSync(neInfo model.NeInfo, content map[string]any, sync bool) error
// NeConfOAMWirteSync 网元OAM配置文件生成并同步
NeConfOAMWirteSync(neInfo model.NeInfo, content map[string]any, sync bool) error
// NeConfPara5GRead 网元公共配置文件读取
NeConfPara5GRead() (map[string]any, error)

View File

@@ -11,9 +11,9 @@ import (
"be.ems/src/framework/constants/cachekey"
"be.ems/src/framework/logger"
"be.ems/src/framework/redis"
"be.ems/src/framework/telnet"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/ssh"
"be.ems/src/framework/utils/telnet"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
"be.ems/src/modules/network_element/model"
"be.ems/src/modules/network_element/repository"
@@ -429,13 +429,74 @@ func (r *NeInfoImpl) NeRunTelnetClient(neType, neId string, num int) (*telnet.Co
return telnetClient, nil
}
// NeConfOAMReadSync 网元OAM配置文件读取
func (r *NeInfoImpl) NeConfOAMReadSync(neType, neId string) (map[string]any, error) {
oamData, err := r.neConfOAMRead(neType, neId, true)
if err != nil {
return nil, err
}
// UPF和SMF 全小写的key
if _, ok := oamData["httpmanagecfg"]; ok {
content := map[string]any{}
// 网元HTTP服务
// if v, ok := oamData["httpmanagecfg"]; ok {
// item := v.(map[string]any)
// }
// 对网管HTTP配置
if v, ok := oamData["oamconfig"]; ok {
item := v.(map[string]any)
if v, ok := item["iptype"]; ok && v != "" && v != nil {
ipType := v.(string)
if ipType == "ipv6" {
content["omcIP"] = item["ipv6"]
}
if ipType == "ipv4" {
content["omcIP"] = item["ipv4"]
}
}
content["oamEnable"] = item["enable"]
content["oamPort"] = item["port"]
}
// 对网管SNMP配置
if v, ok := oamData["snmpconfig"]; ok {
item := v.(map[string]any)
content["snmpEnable"] = item["enable"]
content["snmpPort"] = item["port"]
}
// 对网管KPI上报配置
if v, ok := oamData["kpiconfig"]; ok {
item := v.(map[string]any)
content["kpiEnable"] = item["enable"]
content["kpiTimer"] = item["timer"]
}
oamData := r.neConfOAMData()
r.neConfOAMWirte(neType, neId, oamData, false)
r.NeConfOAMWirteSync(model.NeInfo{
NeType: neType,
NeId: neId,
}, content, false)
return r.neConfOAMRead(neType, neId, false)
}
// NSSF和MME 配置KPIconfig名不一致时
if v, ok := oamData["KPIconfig"]; ok && v != nil {
item := v.(map[string]any)
oamData["kpiConfig"] = item
delete(oamData, "KPIconfig")
r.neConfOAMWirte(neType, neId, oamData, false)
}
return oamData, nil
}
// neConfOAMData 网元OAM配置文件默认格式数据
func (r *NeInfoImpl) neConfOAMData() map[string]any {
return map[string]any{
"httpManageCfg": map[string]any{
"ipType": "ipv4",
// 必改
"ipv4": "172.60.5.2",
"ipv4": "172.16.5.1", // 必改
"ipv6": "",
"port": 33030,
"scheme": "http",
@@ -443,11 +504,12 @@ func (r *NeInfoImpl) neConfOAMData() map[string]any {
"oamConfig": map[string]any{
"enable": true,
"ipType": "ipv4",
"ipv4": "172.60.5.1", // 必改
"ipv4": "172.16.5.100", // 必改
"ipv6": "",
"port": 33030,
"scheme": "http",
"neConfig": map[string]any{ // 必改
// 必改
"neConfig": map[string]any{
"neId": "001",
"rmUid": "4400HX1XXX001",
"neName": "XXX_001",
@@ -460,7 +522,7 @@ func (r *NeInfoImpl) neConfOAMData() map[string]any {
"snmpConfig": map[string]any{
"enable": false,
"ipType": "ipv4",
"ipv4": "172.60.5.2", // 必改
"ipv4": "172.16.5.1", // 必改
"ipv6": "",
"port": 4957,
},
@@ -472,15 +534,39 @@ func (r *NeInfoImpl) neConfOAMData() map[string]any {
}
}
// neConfOAMRead 网元OAM配置文件读取
func (r *NeInfoImpl) NeConfOAMRead(neType, neId string) (map[string]any, error) {
// neConfOAMRead 网元OAM配置文件读取 sync从网元端同步到本地
func (r *NeInfoImpl) neConfOAMRead(neType, neId string, sync bool) (map[string]any, error) {
neTypeLower := strings.ToLower(neType)
fileName := "oam_manager.yaml"
// 网管本地路径
omcPath := "/usr/local/etc/omc/ne_config"
localFilePath := fmt.Sprintf("/usr/local/etc/omc/ne_config/%s/%s/%s", neTypeLower, neId, fileName)
if runtime.GOOS == "windows" {
omcPath = fmt.Sprintf("C:%s", omcPath)
localFilePath = fmt.Sprintf("C:%s", localFilePath)
}
// 从网元端同步到本地
if sync {
// 网元主机的SSH客户端
sshClient, err := NewNeInfoImpl.NeRunSSHClient(neType, neId)
if err != nil {
return nil, fmt.Errorf("ne info ssh client err")
}
defer sshClient.Close()
// 网元主机的SSH客户端进行文件传输
sftpClient, err := sshClient.NewClientSFTP()
if err != nil {
return nil, fmt.Errorf("ne info sftp client err")
}
defer sftpClient.Close()
// 网元端文件路径
neFilePath := fmt.Sprintf("/usr/local/etc/%s/%s", neTypeLower, fileName)
// 修改网元文件权限
sshClient.RunCMD(fmt.Sprintf("sudo touch %s && sudo chmod o+rw %s", neFilePath, neFilePath))
// 网元端复制到本地
if err = sftpClient.CopyFileRemoteToLocal(neFilePath, localFilePath); err != nil {
return nil, fmt.Errorf("copy oam config err")
}
}
localFilePath := fmt.Sprintf("%s/%s/%s/%s", omcPath, neTypeLower, neId, "oam_manager.yaml")
// 读取文件内容
bytes, err := os.ReadFile(localFilePath)
@@ -538,7 +624,7 @@ func (r *NeInfoImpl) neConfOAMWirte(neType, neId string, content any, sync bool)
neFilePath := fmt.Sprintf("/usr/local/etc/%s/%s", neTypeLower, fileName)
neFileDir := filepath.ToSlash(filepath.Dir(neFilePath))
// 修改网元文件权限
sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 775 %s && sudo touch %s && sudo chmod o+w %s", neFileDir, neFileDir, neFilePath, neFilePath))
sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 775 %s && sudo touch %s && sudo chmod o+rw %s", neFileDir, neFileDir, neFilePath, neFilePath))
// 复制到网元进行覆盖
if err = sftpClient.CopyFileLocalToRemote(localFilePath, neFilePath); err != nil {
return fmt.Errorf("please check if scp remote copy is allowed")
@@ -548,9 +634,9 @@ func (r *NeInfoImpl) neConfOAMWirte(neType, neId string, content any, sync bool)
return nil
}
// NeConfOAMSync 网元OAM配置文件生成并同步
func (r *NeInfoImpl) NeConfOAMSync(neInfo model.NeInfo, content map[string]any, sync bool) error {
oamData, err := r.NeConfOAMRead(neInfo.NeType, neInfo.NeId)
// NeConfOAMWirteSync 网元OAM配置文件生成并同步
func (r *NeInfoImpl) NeConfOAMWirteSync(neInfo model.NeInfo, content map[string]any, sync bool) error {
oamData, err := r.neConfOAMRead(neInfo.NeType, neInfo.NeId, false)
if oamData == nil || err != nil {
return fmt.Errorf("error read OAM file info")
}
@@ -638,6 +724,8 @@ func (r *NeInfoImpl) NeConfOAMSync(neInfo model.NeInfo, content map[string]any,
item := v.(map[string]any)
if neInfo.NeType == "UPF" {
item["timer"] = 5
} else {
item["timer"] = 60
}
if kpiEnable, ok := content["kpiEnable"]; ok && kpiEnable != nil {

View File

@@ -200,18 +200,26 @@ func (r *NeVersionImpl) operateCommand(action, neType string, neFilePaths []stri
cmdStrArr := []string{}
if neType == "OMC" {
omcStrArr := []string{}
omcStrArr = append(omcStrArr, pkgCmdStr)
if action == "install" {
omcStrArr = append(omcStrArr, "/usr/local/omc/bin/setomc.sh -m install") // 初始化数据库
// 安装软件包
pkgCmdStr = fmt.Sprintf("sudo M_PARAM=install dpkg -i %s", strings.Join(neFilePaths, " "))
if strings.HasSuffix(fileExt, "rpm") {
pkgCmdStr = fmt.Sprintf("sudo M_PARAM=install rpm -Uvh %s", strings.Join(neFilePaths, " "))
}
omcStrArr = append(omcStrArr, pkgCmdStr)
} else {
omcStrArr = append(omcStrArr, "/usr/local/omc/bin/setomc.sh -m upgrade") // 升级数据库
// 升级软件包
pkgCmdStr = fmt.Sprintf("sudo M_PARAM=upgrade dpkg -i %s", strings.Join(neFilePaths, " "))
if strings.HasSuffix(fileExt, "rpm") {
pkgCmdStr = fmt.Sprintf("sudo M_PARAM=upgrade rpm -Uvh %s", strings.Join(neFilePaths, " "))
}
omcStrArr = append(omcStrArr, pkgCmdStr)
}
omcStrArr = append(omcStrArr, "sudo systemctl restart restagent") // 重启服务
omcStrArr = append(omcStrArr, fmt.Sprintf("sudo rm %s", strings.Join(neFilePaths, " "))) // 删除软件包
// 删除软件包
omcStrArr = append(omcStrArr, fmt.Sprintf("sudo rm %s", strings.Join(neFilePaths, " ")))
// 2s后安装
// 2s后执行omc相关命令
cmdStrArr = append(cmdStrArr, fmt.Sprintf("nohup sh -c \"sleep 2s && %s\" > /tmp/omc_%s.out 2>&1 & \n", strings.Join(omcStrArr, " && "), action))
// 结束
cmdStrArr = append(cmdStrArr, fmt.Sprintf("echo '%s' \n", okFlagStr))
return okFlagStr, cmdStrArr, nil
} else if neType == "IMS" {
@@ -473,14 +481,14 @@ func (r *NeVersionImpl) operateCommand(action, neType string, neFilePaths []stri
if strings.Contains(pkgCmdStr, "adb") {
para5GData := NewNeInfoImpl.Para5GData
cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/adb/default/adb.conf /usr/local/etc/adb/adb.conf \n")
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/bind 127.0.0.1/bind %s/g\" /usr/local/etc/adb/adb.conf \n", para5GData["DB_IP"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/bind 127.0.0.1/bind 127.0.0.1 %s/g\" /usr/local/etc/adb/adb.conf \n", para5GData["DB_IP"]))
cmdStrArr = append(cmdStrArr, "sudo service adb restart \n")
}
// kvdb
if strings.Contains(pkgCmdStr, "kvdb") {
para5GData := NewNeInfoImpl.Para5GData
cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/kvdb/default/kvdb.conf /usr/local/etc/kvdb/kvdb.conf \n")
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/bind 127.0.0.1/bind %s/g\" /usr/local/etc/kvdb/kvdb.conf \n", para5GData["DB_IP"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/bind 127.0.0.1/bind 127.0.0.1 %s/g\" /usr/local/etc/kvdb/kvdb.conf \n", para5GData["DB_IP"]))
cmdStrArr = append(cmdStrArr, "sudo service kvdb restart \n")
}
}
@@ -592,7 +600,7 @@ func (r *NeVersionImpl) operateDome(action string, neVersion model.NeVersion) er
return fmt.Errorf("error found neinfo")
}
// ========= 网元OAM配置文件 start ==========
if err := NewNeInfoImpl.NeConfOAMSync(neInfo, nil, true); err != nil {
if err := NewNeInfoImpl.NeConfOAMWirteSync(neInfo, nil, true); err != nil {
return fmt.Errorf("error wirte OAM file info")
}
// ========= 网元OAM配置文件 end ===========

View File

@@ -15,7 +15,7 @@ import (
// 实例化控制层 TcpdumpController 结构体
var NewTcpdump = &TcpdumpController{
TcpdumpService: traceService.NewTcpdumpImpl,
TcpdumpService: traceService.NewTCPdump,
neInfoService: neService.NewNeInfoImpl,
}
@@ -24,7 +24,7 @@ var NewTcpdump = &TcpdumpController{
// PATH /tcpdump
type TcpdumpController struct {
// 信令抓包服务
TcpdumpService traceService.ITcpdump
TcpdumpService *traceService.TCPdump
// 网元信息服务
neInfoService neService.INeInfo
}

View File

@@ -0,0 +1,62 @@
package controller
import (
"strings"
"be.ems/src/framework/i18n"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/vo/result"
traceService "be.ems/src/modules/trace/service"
"github.com/gin-gonic/gin"
)
// 实例化控制层 TraceDataController 结构体
var NewTraceData = &TraceDataController{
traceDataService: traceService.NewTraceData,
}
// 跟踪任务数据
//
// PATH /data
type TraceDataController struct {
// 跟踪_数据信息服务
traceDataService *traceService.TraceData
}
// 跟踪任务数据列表
//
// GET /list
func (s *TraceDataController) List(c *gin.Context) {
query := ctx.QueryMap(c)
// 查询数据
data := s.traceDataService.SelectPage(query)
c.JSON(200, result.Ok(data))
}
// 跟踪任务数据删除
//
// DELETE /:ids
func (s *TraceDataController) Remove(c *gin.Context) {
language := ctx.AcceptLanguage(c)
rowIds := c.Param("ids")
if rowIds == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 处理字符转id数组后去重
ids := strings.Split(rowIds, ",")
uniqueIDs := parse.RemoveDuplicates(ids)
if len(uniqueIDs) <= 0 {
c.JSON(200, result.Err(nil))
return
}
rows, err := s.traceDataService.DeleteByIds(uniqueIDs)
if err != nil {
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
return
}
msg := i18n.TTemplate(language, "app.common.deleteSuccess", map[string]any{"num": rows})
c.JSON(200, result.OkMsg(msg))
}

View File

@@ -0,0 +1,131 @@
package controller
import (
"strings"
"be.ems/src/framework/i18n"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/vo/result"
"be.ems/src/modules/trace/model"
traceService "be.ems/src/modules/trace/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
// 实例化控制层 TraceTaskController 结构体
var NewTraceTask = &TraceTaskController{
traceTaskService: traceService.NewTraceTask,
}
// 跟踪任务
//
// PATH /task
type TraceTaskController struct {
// 跟踪_任务信息服务
traceTaskService *traceService.TraceTask
}
// 跟踪任务列表
//
// GET /list
func (s *TraceTaskController) List(c *gin.Context) {
query := ctx.QueryMap(c)
// 查询数据
data := s.traceTaskService.SelectPage(query)
c.JSON(200, result.Ok(data))
}
// 跟踪任务信息
//
// GET /:id
func (s *TraceTaskController) Info(c *gin.Context) {
language := ctx.AcceptLanguage(c)
id := c.Param("id")
if id == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
data := s.traceTaskService.SelectById(id)
if data.ID == id {
c.JSON(200, result.OkData(data))
return
}
c.JSON(200, result.Err(nil))
}
// 跟踪任务新增
//
// POST /
func (s *TraceTaskController) Add(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var body model.TraceTask
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || body.ID != "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
body.CreateBy = ctx.LoginUserToUserName(c)
if err = s.traceTaskService.Insert(body); err != nil {
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
return
}
c.JSON(200, result.Ok(nil))
}
// 跟踪任务修改
//
// PUT /
func (s *TraceTaskController) Edit(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var body model.TraceTask
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || body.ID == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 检查是否存在
taskInfo := s.traceTaskService.SelectById(body.ID)
if taskInfo.ID != body.ID {
// 没有可访问任务信息数据!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "task.noData")))
return
}
body.UpdateBy = ctx.LoginUserToUserName(c)
if err = s.traceTaskService.Update(body); err != nil {
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
return
}
c.JSON(200, result.Ok(nil))
}
// 跟踪任务删除
//
// DELETE /:ids
func (s *TraceTaskController) Remove(c *gin.Context) {
language := ctx.AcceptLanguage(c)
rowIds := c.Param("ids")
if rowIds == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 处理字符转id数组后去重
ids := strings.Split(rowIds, ",")
uniqueIDs := parse.RemoveDuplicates(ids)
if len(uniqueIDs) <= 0 {
c.JSON(200, result.Err(nil))
return
}
rows, err := s.traceTaskService.DeleteByIds(uniqueIDs)
if err != nil {
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
return
}
msg := i18n.TTemplate(language, "app.common.deleteSuccess", map[string]any{"num": rows})
c.JSON(200, result.OkMsg(msg))
}

View File

@@ -0,0 +1,242 @@
package controller
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"be.ems/src/framework/i18n"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/generate"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/vo/result"
neService "be.ems/src/modules/network_element/service"
"be.ems/src/modules/trace/model"
traceService "be.ems/src/modules/trace/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
// 实例化控制层 TraceTaskHlrController 结构体
var NewTraceTaskHlr = &TraceTaskHlrController{
neInfoService: neService.NewNeInfoImpl,
traceTaskHlrService: traceService.NewTraceTaskHlr,
}
// 跟踪任务网元HLR
//
// PATH /task/hlr
type TraceTaskHlrController struct {
// 网元信息服务
neInfoService neService.INeInfo
// 跟踪_任务给HRL网元信息服务
traceTaskHlrService *traceService.TraceTaskHlr
}
// 跟踪任务列表
//
// GET /list
func (s *TraceTaskHlrController) List(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var querys model.TraceTaskHlrQuery
if err := c.ShouldBindQuery(&querys); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询数据
data := s.traceTaskHlrService.SelectPage(querys)
c.JSON(200, result.Ok(data))
}
// 跟踪任务删除
//
// DELETE /:ids
func (s *TraceTaskHlrController) Remove(c *gin.Context) {
language := ctx.AcceptLanguage(c)
rowIds := c.Param("ids")
if rowIds == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 处理字符转id数组后去重
ids := strings.Split(rowIds, ",")
uniqueIDs := parse.RemoveDuplicates(ids)
if len(uniqueIDs) <= 0 {
c.JSON(200, result.Err(nil))
return
}
rows, err := s.traceTaskHlrService.DeleteByIds(uniqueIDs)
if err != nil {
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
return
}
msg := i18n.TTemplate(language, "app.common.deleteSuccess", map[string]any{"num": rows})
c.JSON(200, result.OkMsg(msg))
}
// 跟踪任务创建
//
// POST /start
func (s *TraceTaskHlrController) Start(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var body struct {
IMSI string `json:"imsi"` // IMSI
MSISDN string `json:"msisdn"` // MSISDN
StartTime int64 `json:"startTime"` // 开始时间
EndTime int64 `json:"endTime"` // 结束时间
Remark string `json:"remark"` // 备注说明
}
if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
if body.IMSI == "" && body.MSISDN == "" {
c.JSON(400, result.CodeMsg(400, "imsi amd msisdn is empty"))
return
}
task := model.TraceTaskHlr{
IMSI: body.IMSI,
MSISDN: body.MSISDN,
StartTime: body.StartTime,
EndTime: body.EndTime,
Remark: body.Remark,
CreateBy: ctx.LoginUserToUserName(c),
}
id, err := s.traceTaskHlrService.Start(task)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.OkData(id))
}
// 跟踪任务停止
//
// POST /stop
func (s *TraceTaskHlrController) Stop(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var body struct {
ID string `json:"id" binding:"required"` // 任务ID
}
if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 处理字符转id数组后去重
ids := strings.Split(body.ID, ",")
uniqueIDs := parse.RemoveDuplicates(ids)
if len(uniqueIDs) <= 0 {
c.JSON(200, result.Err(nil))
return
}
errArr := []map[string]any{}
for _, id := range uniqueIDs {
task := s.traceTaskHlrService.SelectById(id)
if task.ID != id || task.ID == "" {
errArr = append(errArr, map[string]any{"id": id, "err": "task not found"})
continue
}
task.UpdateBy = ctx.LoginUserToUserName(c)
err := s.traceTaskHlrService.Stop(task)
if err != nil {
errArr = append(errArr, map[string]any{"id": id, "err": err.Error()})
continue
}
}
c.JSON(200, result.OkData(errArr))
}
// 跟踪任务文件
//
// POST /file
func (s *TraceTaskHlrController) File(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var body struct {
ID string `json:"id" binding:"required"` // 任务ID
Dir string `json:"dir" binding:"required"` // 网元文件目录
}
if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
task := s.traceTaskHlrService.SelectById(body.ID)
if task.ID != body.ID || task.ID == "" {
c.JSON(200, result.CodeMsg(400, "task not found"))
return
}
list, err := s.traceTaskHlrService.File(task.TraceId, body.Dir)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.OkData(list))
}
// 跟踪任务文件从网元到本地
//
// GET /filePull
func (s *TraceTaskHlrController) FilePull(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var querys struct {
NeType string `form:"neType" binding:"required"`
NeID string `form:"neId" binding:"required"`
Path string `form:"path" binding:"required"`
FileName string `form:"fileName" binding:"required"`
DelTemp bool `form:"delTemp"` // 删除本地临时文件
}
if err := c.ShouldBindQuery(&querys); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(querys.NeType, querys.NeID)
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer sshClient.Close()
// 网元主机的SSH客户端进行文件传输
sftpClient, err := sshClient.NewClientSFTP()
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer sftpClient.Close()
nePath := filepath.ToSlash(filepath.Join(querys.Path, querys.FileName))
fileName := generate.Code(6) + "_" + querys.FileName
localFilePath := filepath.Join("/tmp/omc/pull", fileName)
if runtime.GOOS == "windows" {
localFilePath = fmt.Sprintf("C:%s", localFilePath)
}
// 复制到本地
if err = sftpClient.CopyFileRemoteToLocal(nePath, localFilePath); err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer func() {
if querys.DelTemp {
_ = os.Remove(localFilePath)
}
}()
c.FileAttachment(localFilePath, fileName)
}

View File

@@ -0,0 +1,23 @@
package model
// TraceData 跟踪_数据 trace_data
type TraceData struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
TaskId int64 `json:"taskId" gorm:"task_id"` // 任务ID
IMSI string `json:"imsi" gorm:"imsi"`
MSISDN string `json:"msisdn" gorm:"msisdn"` // 可能存在
SrcAddr string `json:"srcAddr" gorm:"src_addr"` // 源地址带端口
DstAddr string `json:"dstAddr" gorm:"dst_addr"` // 目标地址带端口
IfType int64 `json:"ifType" gorm:"if_type"` // 接口类型,未分类
MsgType int64 `json:"msgType" gorm:"msg_type"`
MsgDirect int64 `json:"msgDirect" gorm:"msg_direct"`
Length int64 `json:"length" gorm:"length"` // 去除头后的原始数据byte长度
Timestamp int64 `json:"timestamp" gorm:"timestamp"` // 毫秒
RawMsg string `json:"rawMsg" gorm:"raw_msg"` // 去除头后的原始数据byteBase64
DecMsg string `json:"decMsg" gorm:"dec_msg"` // TCP内容消息
}
// TableName 表名称
func (*TraceData) TableName() string {
return "trace_data"
}

View File

@@ -0,0 +1,31 @@
package model
// TraceTask 跟踪_任务
type TraceTask struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"` // 跟踪任务ID
TraceId string `json:"traceId" gorm:"trace_id"` // 任务编号
TraceType string `json:"traceType" gorm:"trace_type"` // 1-Interface,2-Device,3-User
StartTime int64 `json:"startTime" gorm:"start_time"` // 开始时间 毫秒
EndTime int64 `json:"endTime" gorm:"end_time"` // 结束时间 毫秒
Interfaces string `json:"interfaces" gorm:"interfaces"` // 接口跟踪必须 例如 N8,N10
IMSI string `json:"imsi" gorm:"imsi"` // 用户跟踪必须
MSISDN string `json:"msisdn" gorm:"msisdn"` // 用户跟踪可选
UeIp string `json:"ueIp" gorm:"ue_ip"` // 设备跟踪必须 IP
SrcIp string `json:"srcIp" gorm:"src_ip"` // 源地址IP
DstIp string `json:"dstIp" gorm:"dst_ip"` // 目标地址IP
SignalPort int64 `json:"signalPort" gorm:"signal_port"` // 地址IP端口
CreateBy string `json:"createBy" gorm:"create_by"` // 创建者
CreateTime int64 `json:"createTime" gorm:"create_time"` // 创建时间
UpdateBy string `json:"updateBy" gorm:"update_by"` // 更新者
UpdateTime int64 `json:"updateTime" gorm:"update_time"` // 更新时间
Remark string `json:"remark" gorm:"remark"` // 备注
NeType string `json:"neType" gorm:"ne_type"` // 网元类型
NeId string `json:"neId" gorm:"ne_id"` // 网元ID
NotifyUrl string `json:"notifyUrl" gorm:"notify_url"` // 信息数据通知回调地址UDP 例如udp:192.168.5.58:29500
FetchMsg string `json:"fetchMsg" gorm:"fetch_msg"` // 任务下发请求响应消息
}
// TableName 表名称
func (*TraceTask) TableName() string {
return "trace_task"
}

View File

@@ -0,0 +1,35 @@
package model
// TraceTaskHlr 跟踪_任务给HRL网元 trace_task_hlr
type TraceTaskHlr struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
TraceId string `json:"traceId" gorm:"trace_id"` // 任务编号
IMSI string `json:"imsi" gorm:"imsi"` // IMSI
MSISDN string `json:"msisdn" gorm:"msisdn"` // MSISDN
StartTime int64 `json:"startTime" gorm:"start_time"` // 开始时间
EndTime int64 `json:"endTime" gorm:"end_time"` // 结束时间
Status string `json:"status" gorm:"status"` // 任务状态0停止 1进行
Msg string `json:"msg" gorm:"msg"` // 任务信息
Remark string `json:"remark" gorm:"remark"` // 备注说明
CreateBy string `json:"createBy" gorm:"create_by"` // 创建者
CreateTime int64 `json:"createTime" gorm:"create_time"` // 创建时间
UpdateBy string `json:"updateBy" gorm:"update_by"` // 更新者
UpdateTime int64 `json:"updateTime" gorm:"update_time"` // 更新时间
}
// TableName 表名称
func (*TraceTaskHlr) TableName() string {
return "trace_task_hlr"
}
// TraceTaskHlrQuery 查询参数结构体
type TraceTaskHlrQuery struct {
IMSI string `json:"imsi" form:"imsi"` // imsi
MSISDN string `json:"msisdn" form:"msisdn"` // msisdn
StartTime string `json:"startTime" form:"startTime"`
EndTime string `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=imsi msisdn"` // 排序字段,填写结果字段
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,246 @@
package repository
import (
"strings"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/repo"
"be.ems/src/modules/trace/model"
)
// 实例化数据层 TraceData 结构体
var NewTraceData = &TraceData{
selectSql: `select id, task_id, imsi, msisdn, src_addr, dst_addr, if_type, msg_type, msg_direct, length, timestamp, raw_msg, dec_msg from trace_data`,
resultMap: map[string]string{
"id": "ID",
"task_id": "TaskId",
"imsi": "IMSI",
"msisdn": "MSISDN",
"src_addr": "SrcAddr",
"dst_addr": "DstAddr",
"if_type": "IfType",
"msg_type": "MsgType",
"msg_direct": "MsgDirect",
"length": "Length",
"timestamp": "Timestamp",
"raw_msg": "RawMsg",
"dec_msg": "DecMsg",
},
}
// CDREventIMSImpl 跟踪_任务给HRL网元 数据层处理
type TraceData struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
resultMap map[string]string
}
// convertResultRows 将结果记录转实体结果组
func (r *TraceData) convertResultRows(rows []map[string]any) []model.TraceData {
arr := make([]model.TraceData, 0)
for _, row := range rows {
item := model.TraceData{}
for key, value := range row {
if keyMapper, ok := r.resultMap[key]; ok {
repo.SetFieldValue(&item, keyMapper, value)
}
}
arr = append(arr, item)
}
return arr
}
// SelectPage 根据条件分页查询
func (r *TraceData) SelectPage(query map[string]any) map[string]any {
// 查询条件拼接
var conditions []string
var params []any
if v, ok := query["imsi"]; ok && v != "" {
conditions = append(conditions, "imsi like concat(?, '%')")
params = append(params, v)
}
if v, ok := query["msisdn"]; ok && v != "" {
conditions = append(conditions, "msisdn like concat(?, '%')")
params = append(params, v)
}
if v, ok := query["startTime"]; ok && v != "" {
conditions = append(conditions, "timestamp >= ?")
params = append(params, v)
}
if v, ok := query["endTime"]; ok && v != "" {
conditions = append(conditions, "timestamp <= ?")
params = append(params, v)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
result := map[string]any{
"total": 0,
"rows": []model.TraceData{},
}
// 查询数量 长度为0直接返回
totalSql := "select count(1) as 'total' from trace_data"
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
if err != nil {
logger.Errorf("total err => %v", err)
return result
}
total := parse.Number(totalRows[0]["total"])
if total == 0 {
return result
} else {
result["total"] = total
}
// 分页
pageNum, pageSize := repo.PageNumSize(query["pageNum"], query["pageSize"])
pageSql := " limit ?,? "
params = append(params, pageNum*pageSize)
params = append(params, pageSize)
// 查询数据
querySql := r.selectSql + whereSql + pageSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
result["rows"] = r.convertResultRows(results)
return result
}
// SelectList 根据实体查询
func (r *TraceData) SelectList(data model.TraceData) []model.TraceData {
// 查询条件拼接
var conditions []string
var params []any
if data.IMSI != "" {
conditions = append(conditions, "imsi = ?")
params = append(params, data.IMSI)
}
if data.MSISDN != "" {
conditions = append(conditions, "msisdn = ?")
params = append(params, data.MSISDN)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
// 查询数据
querySql := r.selectSql + whereSql + " order by id desc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
return r.convertResultRows(results)
}
// SelectByIds 通过ID查询
func (r *TraceData) SelectByIds(ids []string) []model.TraceData {
placeholder := repo.KeyPlaceholderByQuery(len(ids))
querySql := r.selectSql + " where id in (" + placeholder + ")"
parameters := repo.ConvertIdsSlice(ids)
results, err := datasource.RawDB("", querySql, parameters)
if err != nil {
logger.Errorf("query err => %v", err)
return []model.TraceData{}
}
// 转换实体
return r.convertResultRows(results)
}
// Insert 新增信息
func (r *TraceData) Insert(data model.TraceData) string {
// 参数拼接
params := make(map[string]any)
if data.TaskId > 0 {
params["task_id"] = data.TaskId
}
if data.IMSI != "" {
params["imsi"] = data.IMSI
}
if data.MSISDN != "" {
params["msisdn"] = data.MSISDN
}
if data.SrcAddr != "" {
params["src_addr"] = data.SrcAddr
}
if data.DstAddr != "" {
params["dst_addr"] = data.DstAddr
}
if data.IfType > -1 {
params["if_type"] = data.IfType
}
if data.MsgType > -1 {
params["msg_type"] = data.MsgType
}
if data.MsgDirect > -1 {
params["msg_direct"] = data.MsgDirect
}
if data.Length > 0 {
params["length"] = data.Length
}
if data.Timestamp > 0 {
params["timestamp"] = data.Timestamp
}
if data.RawMsg != "" {
params["raw_msg"] = data.RawMsg
}
if data.DecMsg != "" {
params["dec_msg"] = data.DecMsg
}
// 构建执行语句
keys, placeholder, values := repo.KeyPlaceholderValueByInsert(params)
sql := "insert into trace_data (" + strings.Join(keys, ",") + ")values(" + placeholder + ")"
db := datasource.DefaultDB()
// 开启事务
tx := db.Begin()
// 执行插入
err := tx.Exec(sql, values...).Error
if err != nil {
logger.Errorf("insert row : %v", err.Error())
tx.Rollback()
return ""
}
// 获取生成的自增 ID
var insertedID string
err = tx.Raw("select last_insert_id()").Row().Scan(&insertedID)
if err != nil {
logger.Errorf("insert last id : %v", err.Error())
tx.Rollback()
return ""
}
// 提交事务
tx.Commit()
return insertedID
}
// DeleteByIds 批量删除信息
func (r *TraceData) DeleteByIds(ids []string) int64 {
placeholder := repo.KeyPlaceholderByQuery(len(ids))
sql := "delete from trace_data where id in (" + placeholder + ")"
parameters := repo.ConvertIdsSlice(ids)
results, err := datasource.ExecDB("", sql, parameters)
if err != nil {
logger.Errorf("delete err => %v", err)
return 0
}
return results
}

View File

@@ -0,0 +1,358 @@
package repository
import (
"fmt"
"strings"
"time"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/repo"
"be.ems/src/modules/trace/model"
)
// 实例化数据层 TraceTask 结构体
var NewTraceTask = &TraceTask{
selectSql: `select id, trace_id, trace_type, start_time, end_time,
interfaces, imsi, msisdn,
ue_ip, src_ip, dst_ip, signal_port,
create_by, create_time, update_by, update_time, remark,
ne_type, ne_id, notify_url, fetch_msg
from trace_task`,
resultMap: map[string]string{
"id": "ID",
"trace_id": "TraceId",
"trace_type": "TraceType",
"start_time": "StartTime",
"end_time": "EndTime",
"interfaces": "Interfaces",
"imsi": "IMSI",
"msisdn": "MSISDN",
"ue_ip": "UeIp",
"src_ip": "SrcIp",
"dst_ip": "DstIp",
"signal_port": "SignalPort",
"create_by": "CreateBy",
"create_time": "CreateTime",
"update_by": "UpdateBy",
"update_time": "UpdateTime",
"remark": "Remark",
"ne_type": "NeType",
"ne_id": "NeId",
"notify_url": "NotifyUrl",
"fetch_msg": "FetchMsg",
},
}
// TraceTask 跟踪_任务 数据层处理
type TraceTask struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
resultMap map[string]string
}
// convertResultRows 将结果记录转实体结果组
func (r *TraceTask) convertResultRows(rows []map[string]any) []model.TraceTask {
arr := make([]model.TraceTask, 0)
for _, row := range rows {
item := model.TraceTask{}
for key, value := range row {
if keyMapper, ok := r.resultMap[key]; ok {
repo.SetFieldValue(&item, keyMapper, value)
}
}
arr = append(arr, item)
}
return arr
}
// SelectPage 根据条件分页查询
func (r *TraceTask) SelectPage(query map[string]any) map[string]any {
// 查询条件拼接
var conditions []string
var params []any
if v, ok := query["neType"]; ok && v != "" {
conditions = append(conditions, "ne_type = ?")
params = append(params, v)
}
if v, ok := query["imsi"]; ok && v != "" {
conditions = append(conditions, "imsi like concat(?, '%')")
params = append(params, v)
}
if v, ok := query["msisdn"]; ok && v != "" {
conditions = append(conditions, "msisdn like concat(?, '%')")
params = append(params, v)
}
if v, ok := query["startTime"]; ok && v != "" {
conditions = append(conditions, "start_time >= ?")
params = append(params, v)
}
if v, ok := query["endTime"]; ok && v != "" {
conditions = append(conditions, "end_time <= ?")
params = append(params, v)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
result := map[string]any{
"total": 0,
"rows": []model.TraceTask{},
}
// 查询数量 长度为0直接返回
totalSql := "select count(1) as 'total' from trace_task"
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
if err != nil {
logger.Errorf("total err => %v", err)
return result
}
total := parse.Number(totalRows[0]["total"])
if total == 0 {
return result
} else {
result["total"] = total
}
// 分页
pageNum, pageSize := repo.PageNumSize(query["pageNum"], query["pageSize"])
pageSql := " limit ?,? "
params = append(params, pageNum*pageSize)
params = append(params, pageSize)
// 排序
orderSql := ""
if v, ok := query["sortField"]; ok && v != "" {
sortSql := v.(string)
if v, ok := query["sortOrder"]; ok && v != "" {
if v.(string) == "desc" {
sortSql += " desc "
} else {
sortSql += " asc "
}
}
orderSql = fmt.Sprintf(" order by %s ", sortSql)
}
// 查询数据
querySql := r.selectSql + whereSql + orderSql + pageSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
result["rows"] = r.convertResultRows(results)
return result
}
// SelectList 根据实体查询
func (r *TraceTask) SelectList(task model.TraceTask) []model.TraceTask {
// 查询条件拼接
var conditions []string
var params []any
if task.IMSI != "" {
conditions = append(conditions, "imsi = ?")
params = append(params, task.IMSI)
}
if task.SrcIp != "" {
conditions = append(conditions, "src_ip = ?")
params = append(params, task.SrcIp)
}
if task.DstIp != "" {
conditions = append(conditions, "dst_ip = ?")
params = append(params, task.DstIp)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
// 查询数据
querySql := r.selectSql + whereSql + " order by id desc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
return r.convertResultRows(results)
}
// SelectByIds 通过ID查询
func (r *TraceTask) SelectByIds(ids []string) []model.TraceTask {
placeholder := repo.KeyPlaceholderByQuery(len(ids))
querySql := r.selectSql + " where id in (" + placeholder + ")"
parameters := repo.ConvertIdsSlice(ids)
results, err := datasource.RawDB("", querySql, parameters)
if err != nil {
logger.Errorf("query err => %v", err)
return []model.TraceTask{}
}
// 转换实体
return r.convertResultRows(results)
}
// Insert 新增信息
func (r *TraceTask) Insert(task model.TraceTask) string {
// 参数拼接
params := make(map[string]any)
if task.TraceId != "" {
params["trace_id"] = task.TraceId
}
if task.TraceType != "" {
params["trace_type"] = task.TraceType
}
if task.StartTime > 0 {
params["start_time"] = task.StartTime
}
if task.EndTime > 0 {
params["end_time"] = task.EndTime
}
if task.Interfaces != "" {
params["interfaces"] = task.Interfaces
}
if task.IMSI != "" {
params["imsi"] = task.IMSI
}
if task.MSISDN != "" {
params["msisdn"] = task.MSISDN
}
if task.UeIp != "" {
params["ue_ip"] = task.UeIp
}
if task.SrcIp != "" {
params["src_ip"] = task.SrcIp
}
if task.DstIp != "" {
params["dst_ip"] = task.DstIp
}
if task.SignalPort != 0 {
params["signal_port"] = task.SignalPort
}
if task.NeType != "" {
params["ne_type"] = task.NeType
}
if task.NeId != "" {
params["ne_id"] = task.NeId
}
if task.NotifyUrl != "" {
params["notify_url"] = task.NotifyUrl
}
if task.FetchMsg != "" {
params["fetch_msg"] = task.FetchMsg
}
if task.Remark != "" {
params["remark"] = task.Remark
}
if task.CreateBy != "" {
params["create_by"] = task.CreateBy
params["create_time"] = time.Now().UnixMilli()
}
// 构建执行语句
keys, placeholder, values := repo.KeyPlaceholderValueByInsert(params)
sql := "insert into trace_task (" + strings.Join(keys, ",") + ")values(" + placeholder + ")"
db := datasource.DefaultDB()
// 开启事务
tx := db.Begin()
// 执行插入
err := tx.Exec(sql, values...).Error
if err != nil {
logger.Errorf("insert row : %v", err.Error())
tx.Rollback()
return ""
}
// 获取生成的自增 ID
var insertedID string
err = tx.Raw("select last_insert_id()").Row().Scan(&insertedID)
if err != nil {
logger.Errorf("insert last id : %v", err.Error())
tx.Rollback()
return ""
}
// 提交事务
tx.Commit()
return insertedID
}
// Update 修改信息
func (r *TraceTask) Update(task model.TraceTask) int64 {
// 参数拼接
params := make(map[string]any)
params["trace_id"] = task.TraceId
params["trace_type"] = task.TraceType
params["ne_type"] = task.NeType
params["ne_id"] = task.NeId
params["notify_url"] = task.NotifyUrl
params["start_time"] = task.StartTime
params["end_time"] = task.EndTime
params["fetch_msg"] = task.FetchMsg
params["remark"] = task.Remark
params["interfaces"] = task.Interfaces
params["imsi"] = task.IMSI
params["msisdn"] = task.MSISDN
params["ue_ip"] = task.UeIp
params["src_ip"] = task.SrcIp
params["dst_ip"] = task.DstIp
params["signal_port"] = task.SignalPort
if task.UpdateBy != "" {
params["update_by"] = task.UpdateBy
params["update_time"] = time.Now().UnixMilli()
}
// 构建执行语句
keys, values := repo.KeyValueByUpdate(params)
sql := "update trace_task set " + strings.Join(keys, ",") + " where id = ?"
// 执行更新
values = append(values, task.ID)
rows, err := datasource.ExecDB("", sql, values)
if err != nil {
logger.Errorf("update row : %v", err.Error())
return 0
}
return rows
}
// DeleteByIds 批量删除信息
func (r *TraceTask) DeleteByIds(ids []string) int64 {
placeholder := repo.KeyPlaceholderByQuery(len(ids))
sql := "delete from trace_task where id in (" + placeholder + ")"
parameters := repo.ConvertIdsSlice(ids)
results, err := datasource.ExecDB("", sql, parameters)
if err != nil {
logger.Errorf("delete err => %v", err)
return 0
}
return results
}
// LastID 最后一条ID
func (r *TraceTask) LastID() int64 {
// 查询数据
querySql := "SELECT id as 'str' FROM trace_task ORDER BY id DESC LIMIT 1"
results, err := datasource.RawDB("", querySql, nil)
if err != nil {
logger.Errorf("query err %v", err)
return 0
}
if len(results) > 0 {
return parse.Number(results[0]["str"])
}
return 0
}

View File

@@ -0,0 +1,316 @@
package repository
import (
"fmt"
"strings"
"time"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/repo"
"be.ems/src/modules/trace/model"
)
// 实例化数据层 TraceTaskHlr 结构体
var NewTraceTaskHlr = &TraceTaskHlr{
selectSql: `select id, trace_id, imsi, msisdn, start_time, end_time, status, msg, remark, create_by, create_time, update_by, update_time from trace_task_hlr`,
resultMap: map[string]string{
"id": "ID",
"trace_id": "TraceId",
"imsi": "IMSI",
"msisdn": "MSISDN",
"start_time": "StartTime",
"end_time": "EndTime",
"status": "Status",
"msg": "Msg",
"remark": "Remark",
"create_by": "CreateBy",
"create_time": "CreateTime",
"update_by": "UpdateBy",
"update_time": "UpdateTime",
},
}
// TraceTaskHlr 跟踪_任务给HRL网元 数据层处理
type TraceTaskHlr struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
resultMap map[string]string
}
// convertResultRows 将结果记录转实体结果组
func (r *TraceTaskHlr) convertResultRows(rows []map[string]any) []model.TraceTaskHlr {
arr := make([]model.TraceTaskHlr, 0)
for _, row := range rows {
item := model.TraceTaskHlr{}
for key, value := range row {
if keyMapper, ok := r.resultMap[key]; ok {
repo.SetFieldValue(&item, keyMapper, value)
}
}
arr = append(arr, item)
}
return arr
}
// SelectPage 根据条件分页查询
func (r *TraceTaskHlr) SelectPage(querys model.TraceTaskHlrQuery) map[string]any {
// 查询条件拼接
var conditions []string
var params []any
if querys.IMSI != "" {
conditions = append(conditions, "imsi like concat(?, '%')")
params = append(params, querys.IMSI)
}
if querys.MSISDN != "" {
conditions = append(conditions, "msisdn like concat(?, '%')")
params = append(params, querys.MSISDN)
}
if querys.StartTime != "" && len(querys.StartTime) == 13 {
conditions = append(conditions, "start_time >= ?")
params = append(params, querys.StartTime)
}
if querys.EndTime != "" && len(querys.EndTime) == 13 {
conditions = append(conditions, "end_time <= ?")
params = append(params, querys.EndTime)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
result := map[string]any{
"total": 0,
"rows": []model.TraceTaskHlr{},
}
// 查询数量 长度为0直接返回
totalSql := "select count(1) as 'total' from trace_task_hlr"
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
if err != nil {
logger.Errorf("total err => %v", err)
return result
}
total := parse.Number(totalRows[0]["total"])
if total == 0 {
return result
} else {
result["total"] = total
}
// 分页
pageNum, pageSize := repo.PageNumSize(querys.PageNum, querys.PageSize)
pageSql := " limit ?,? "
params = append(params, pageNum*pageSize)
params = append(params, pageSize)
// 排序
orderSql := ""
if querys.SortField != "" {
sortSql := querys.SortField
if querys.SortOrder != "" {
if querys.SortOrder == "desc" {
sortSql += " desc "
} else {
sortSql += " asc "
}
}
orderSql = fmt.Sprintf(" order by id desc, %s ", sortSql)
}
// 查询数据
querySql := r.selectSql + whereSql + orderSql + pageSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
result["rows"] = r.convertResultRows(results)
return result
}
// SelectList 根据实体查询
func (r *TraceTaskHlr) SelectList(task model.TraceTaskHlr) []model.TraceTaskHlr {
// 查询条件拼接
var conditions []string
var params []any
if task.IMSI != "" {
conditions = append(conditions, "imsi = ?")
params = append(params, task.IMSI)
}
if task.MSISDN != "" {
conditions = append(conditions, "msisdn = ?")
params = append(params, task.MSISDN)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
// 查询数据
querySql := r.selectSql + whereSql + " order by id desc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
return r.convertResultRows(results)
}
// SelectByIds 通过ID查询
func (r *TraceTaskHlr) SelectByIds(ids []string) []model.TraceTaskHlr {
placeholder := repo.KeyPlaceholderByQuery(len(ids))
querySql := r.selectSql + " where id in (" + placeholder + ")"
parameters := repo.ConvertIdsSlice(ids)
results, err := datasource.RawDB("", querySql, parameters)
if err != nil {
logger.Errorf("query err => %v", err)
return []model.TraceTaskHlr{}
}
// 转换实体
return r.convertResultRows(results)
}
// Insert 新增信息
func (r *TraceTaskHlr) Insert(task model.TraceTaskHlr) string {
// 参数拼接
params := make(map[string]any)
if task.TraceId != "" {
params["trace_id"] = task.TraceId
}
if task.IMSI != "" {
params["imsi"] = task.IMSI
}
if task.MSISDN != "" {
params["msisdn"] = task.MSISDN
}
if task.StartTime != 0 {
params["start_time"] = task.StartTime
}
if task.EndTime != 0 {
params["end_time"] = task.EndTime
}
if task.Status != "" {
params["status"] = task.Status
}
if task.Msg != "" {
params["msg"] = task.Msg
}
if task.Remark != "" {
params["remark"] = task.Remark
}
if task.CreateBy != "" {
params["create_by"] = task.CreateBy
params["create_time"] = time.Now().UnixMilli()
}
// 构建执行语句
keys, placeholder, values := repo.KeyPlaceholderValueByInsert(params)
sql := "insert into trace_task_hlr (" + strings.Join(keys, ",") + ")values(" + placeholder + ")"
db := datasource.DefaultDB()
// 开启事务
tx := db.Begin()
// 执行插入
err := tx.Exec(sql, values...).Error
if err != nil {
logger.Errorf("insert row : %v", err.Error())
tx.Rollback()
return ""
}
// 获取生成的自增 ID
var insertedID string
err = tx.Raw("select last_insert_id()").Row().Scan(&insertedID)
if err != nil {
logger.Errorf("insert last id : %v", err.Error())
tx.Rollback()
return ""
}
// 提交事务
tx.Commit()
return insertedID
}
// Update 修改信息
func (r *TraceTaskHlr) Update(task model.TraceTaskHlr) int64 {
// 参数拼接
params := make(map[string]any)
if task.TraceId != "" {
params["trace_id"] = task.TraceId
}
if task.IMSI != "" {
params["imsi"] = task.IMSI
}
if task.MSISDN != "" {
params["msisdn"] = task.MSISDN
}
if task.StartTime != 0 {
params["start_time"] = task.StartTime
}
if task.EndTime != 0 {
params["end_time"] = task.EndTime
}
if task.Status != "" {
params["status"] = task.Status
}
if task.Msg != "" {
params["msg"] = task.Msg
}
if task.Remark != "" {
params["remark"] = task.Remark
}
if task.UpdateBy != "" {
params["update_by"] = task.UpdateBy
params["update_time"] = time.Now().UnixMilli()
}
// 构建执行语句
keys, values := repo.KeyValueByUpdate(params)
sql := "update trace_task_hlr set " + strings.Join(keys, ",") + " where id = ?"
// 执行更新
values = append(values, task.ID)
rows, err := datasource.ExecDB("", sql, values)
if err != nil {
logger.Errorf("update row : %v", err.Error())
return 0
}
return rows
}
// DeleteByIds 批量删除信息
func (r *TraceTaskHlr) DeleteByIds(ids []string) int64 {
placeholder := repo.KeyPlaceholderByQuery(len(ids))
sql := "delete from trace_task_hlr where id in (" + placeholder + ")"
parameters := repo.ConvertIdsSlice(ids)
results, err := datasource.ExecDB("", sql, parameters)
if err != nil {
logger.Errorf("delete err => %v", err)
return 0
}
return results
}
// LastID 最后一条ID
func (r *TraceTaskHlr) LastID() int64 {
// 查询数据
querySql := "SELECT id as 'str' FROM trace_task_hlr ORDER BY id DESC LIMIT 1"
results, err := datasource.RawDB("", querySql, nil)
if err != nil {
logger.Errorf("query err %v", err)
return 0
}
if len(results) > 0 {
return parse.Number(results[0]["str"])
}
return 0
}

View File

@@ -1,16 +1,248 @@
package service
// 信令抓包 服务层接口
type ITcpdump interface {
// DumpStart 触发tcpdump开始抓包
DumpStart(neType, neId, cmdStr string) (string, error)
import (
"fmt"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"sync"
"time"
// DumpStop 停止已存在抓包句柄
DumpStop(neType, neId, taskCode string) (string, error)
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/file"
neService "be.ems/src/modules/network_element/service"
)
// DumpDownload 抓包文件网元端复制到本地输出zip文件
DumpDownload(neType, neId, taskCode string) (string, error)
// UPFTrace UPF标准版内部抓包
UPFTrace(neType, neId, cmdStr string) (string, error)
// 实例化服务层 TCPdump 结构体
var NewTCPdump = &TCPdump{
neInfoService: neService.NewNeInfoImpl,
}
// 信令抓包 服务层处理
type TCPdump struct {
// 网元信息服务
neInfoService neService.INeInfo
}
// 抓包进程PID
var dumpPIDMap sync.Map
// DumpStart 触发tcpdump开始抓包
func (s *TCPdump) DumpStart(neType, neId, cmdStr string) (string, error) {
// 命令检查
if strings.Contains(cmdStr, "w") {
return "", fmt.Errorf("command cannot contain -w")
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" {
return "", fmt.Errorf("app.common.noNEInfo")
}
// 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
return "", err
}
defer sshClient.Close()
// 检查是否安装tcpdump
if msg, err := sshClient.RunCMD("sudo tcpdump --version"); err != nil {
// bash: tcpdump: command not found
msg = strings.TrimSpace(msg)
logger.Errorf("DumpStart err: %s => %s", msg, err.Error())
return "", fmt.Errorf(msg)
}
taskCode := time.Now().Format("20060102150405")
// 存放文件目录 /tmp/omc/tcpdump/udm/001/20240817104241
neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
sshClient.RunCMD(fmt.Sprintf("mkdir -p %s && sudo chmod 777 -R /tmp/omc", neDirTemp))
// 命令拼装
logPath := fmt.Sprintf("%s/tcpdump.log", neDirTemp)
filePath := fmt.Sprintf("%s/part_%s.pcap ", neDirTemp, taskCode)
if strings.Contains(cmdStr, "-G") {
filePath = fmt.Sprintf("%s/part_%%Y%%m%%d%%H%%M%%S.pcap ", neDirTemp)
}
sendCmd := fmt.Sprintf("sudo timeout 60m sudo tcpdump -i any %s -w %s > %s 2>&1 & echo $!", cmdStr, filePath, logPath)
// sudo timeout 60m sudo tcpdump -i any -n -s 0 -v -G 60 -W 6 -w /tmp/omc/tcpdump/udm/001/20240817104241/part_%Y-%m-%d_%H:%M:%S.pcap > /tmp/omc/tcpdump/udm/001/20240817104241/tcpdump.log 2>&1 & echo $!
// sudo timeout 60m sudo tcpdump -i any -n -s 0 -v -w /tmp/omc/tcpdump/udm/001/20240817105440/part_2024-08-17_10:54:40.pcap > /tmp/omc/tcpdump/udm/001/20240817105440/tcpdump.log 2>&1 & echo $!
//
// timeout 超时60分钟后发送kill命令1分钟后强制终止命令。tcpdump -G 文件轮转间隔时间(秒) -W 文件轮转保留最近数量
// sudo timeout --kill-after=1m 60m sudo tcpdump -i any -n -s 0 -v -G 10 -W 7 -w /tmp/part_%Y%m%d%H%M%S.pcap > /tmp/part.log 2>&1 & echo $!
// sudo kill $(pgrep -P 722729)
outputPID, err := sshClient.RunCMD(sendCmd)
outputPID = strings.TrimSpace(outputPID)
if err != nil || strings.HasPrefix(outputPID, "stderr:") {
logger.Errorf("DumpStart err: %s => %s", outputPID, err.Error())
return "", err
}
// 检查进程 ps aux | grep tcpdump
// 强杀 sudo pkill tcpdump
pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
dumpPIDMap.Store(pidKey, outputPID)
return taskCode, err
}
// DumpStop 停止已存在抓包句柄
func (s *TCPdump) DumpStop(neType, neId, taskCode string) (string, error) {
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" {
return "", fmt.Errorf("app.common.noNEInfo")
}
// 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
return "", err
}
defer sshClient.Close()
// 是否存在执行过的进程
pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
pid, ok := dumpPIDMap.Load(pidKey)
if !ok || pid == "" {
return "", fmt.Errorf("tcpdump is not running")
}
defer dumpPIDMap.Delete(pidKey)
// 存放文件目录 /tmp/omc/tcpdump/udm/001/20240817104241
neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
// 命令拼装
sendCmd := fmt.Sprintf("pids=$(pgrep -P %s) && [ -n \"$pids\" ] && sudo kill $pids;sudo timeout 2s cat %s/tcpdump.log", pid, neDirTemp)
// pids=$(pgrep -P 1914341) && [ -n "$pids" ] && sudo kill $pids;sudo timeout 2s cat tcpdump.log
output, err := sshClient.RunCMD(sendCmd)
if err != nil || strings.HasPrefix(output, "stderr:") {
logger.Warnf("DumpStop err: %s => %s", strings.TrimSpace(output), err.Error())
return "", err
}
return output, nil
}
// DumpDownload 抓包文件网元端复制到本地输出zip文件
func (s *TCPdump) DumpDownload(neType, neId, taskCode string) (string, error) {
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" {
return "", fmt.Errorf("app.common.noNEInfo")
}
// 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
return "", err
}
defer sshClient.Close()
// 网元主机的SSH客户端进行文件传输
sftpClient, err := sshClient.NewClientSFTP()
if err != nil {
return "", fmt.Errorf("ne info sftp client err")
}
defer sftpClient.Close()
neTypeLower := strings.ToLower(neInfo.NeType)
// 网管本地路径
localDirPath := fmt.Sprintf("/tmp/omc/tcpdump/zip/%s/%s", neTypeLower, neInfo.NeId)
if runtime.GOOS == "windows" {
localDirPath = fmt.Sprintf("C:%s", localDirPath)
}
// 网元pcap目录 /tmp/omc/tcpdump/udm/001/20240817104241
sshClient.RunCMD("mkdir -p /tmp/omc && sudo chmod 777 -R /tmp/omc")
neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", neTypeLower, neInfo.NeId, taskCode)
// 网元端复制到本地
localDirFilePath := filepath.Join(localDirPath, taskCode)
if err = sftpClient.CopyDirRemoteToLocal(neDirTemp, localDirFilePath); err != nil {
return "", fmt.Errorf("copy tcpdump file err")
}
// 压缩zip文件名
zipFileName := fmt.Sprintf("%s-%s-pcap-%s.zip", neTypeLower, neInfo.NeId, taskCode)
zipFilePath := filepath.Join(localDirPath, zipFileName)
if err := file.CompressZipByDir(zipFilePath, localDirFilePath); err != nil {
return "", fmt.Errorf("compress zip err")
}
_ = os.RemoveAll(localDirFilePath) // 删除本地临时目录
return zipFilePath, nil
}
// UPFTrace UPF标准版内部抓包
func (s *TCPdump) UPFTrace(neType, neId, cmdStr string) (string, error) {
// 命令检查
if strings.Contains(cmdStr, "file") {
return "", fmt.Errorf("command cannot contain file")
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" {
return "", fmt.Errorf("app.common.noNEInfo")
}
// 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
return "", err
}
defer sshClient.Close()
// 网元主机的Telnet客户端
telnetClient, err := s.neInfoService.NeRunTelnetClient("UPF", neInfo.NeId, 2)
if err != nil {
return "", err
}
defer telnetClient.Close()
// 命令拼装
fileName := fmt.Sprintf("%s_%s_part_%s.pcap ", neInfo.NeType, neInfo.NeId, time.Now().Format("20060102150405"))
pcapCmd := fmt.Sprintf("%s\r\n", cmdStr)
// 以off结尾是停止抓包不需要写文件
if !strings.Contains(cmdStr, "off") {
// pcap trace rx tx max 100000 intfc any file UPF_001_part_20240817164516.pcap
pcapCmd = fmt.Sprintf("%s file %s\r\n", cmdStr, fileName)
}
// 发送命令 UPF内部默认输出路径/tmp只能写文件名
// pcap trace rx tx max 100000 intfc any file upf_test.pcap
// pcap trace rx tx off
output, err := telnetClient.RunCMD(pcapCmd)
if err != nil {
logger.Warnf("DumpUPF err: %s => %s", output, err.Error())
return "", err
}
// 结果截取
arr := strings.Split(output, "\r\n")
if len(arr) == 2 {
return "", fmt.Errorf("trace pacp run failed")
}
if len(arr) > 3 {
resMsg := arr[2]
// pcap trace: unknown input `f file UPF_001_part_2024-08-19...'
// pcap trace: dispatch trace already enabled...
// pcap trace: dispatch trace already disabled...
// pcap trace: No packets captured...
// Write 100000 packets to /tmp/UPF_001_part_20240817164516.pcap, and stop capture...
if strings.Contains(resMsg, "unknown input") {
return "", fmt.Errorf("trace pacp unknown input")
}
if strings.Contains(resMsg, "already enabled") {
return "", fmt.Errorf("trace pacp already running")
}
if strings.Contains(resMsg, "already disabled") {
return "", fmt.Errorf("trace pacp not running")
}
if strings.Contains(resMsg, "No packets") {
return "", fmt.Errorf("trace pacp not packets")
}
if strings.Contains(resMsg, "packets to") {
matches := regexp.MustCompile(`(/tmp/[^,\s]+)`).FindStringSubmatch(resMsg)
if len(matches) == 0 {
return "", fmt.Errorf("file path not found")
}
return matches[0], nil
}
}
return "trace pacp running", nil
}

View File

@@ -1,248 +0,0 @@
package service
import (
"fmt"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"sync"
"time"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/file"
neService "be.ems/src/modules/network_element/service"
)
// 实例化服务层 TcpdumpImpl 结构体
var NewTcpdumpImpl = &TcpdumpImpl{
neInfoService: neService.NewNeInfoImpl,
}
// 信令抓包 服务层处理
type TcpdumpImpl struct {
// 网元信息服务
neInfoService neService.INeInfo
}
// 抓包进程PID
var dumpPIDMap sync.Map
// DumpStart 触发tcpdump开始抓包
func (s *TcpdumpImpl) DumpStart(neType, neId, cmdStr string) (string, error) {
// 命令检查
if strings.Contains(cmdStr, "w") {
return "", fmt.Errorf("command cannot contain -w")
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" {
return "", fmt.Errorf("app.common.noNEInfo")
}
// 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
return "", err
}
defer sshClient.Close()
// 检查是否安装tcpdump
if msg, err := sshClient.RunCMD("sudo tcpdump --version"); err != nil {
// bash: tcpdump: command not found
msg = strings.TrimSpace(msg)
logger.Errorf("DumpStart err: %s => %s", msg, err.Error())
return "", fmt.Errorf(msg)
}
taskCode := time.Now().Format("20060102150405")
// 存放文件目录 /tmp/omc/tcpdump/udm/001/20240817104241
neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
sshClient.RunCMD(fmt.Sprintf("mkdir -p %s && sudo chmod 777 -R /tmp/omc", neDirTemp))
// 命令拼装
logPath := fmt.Sprintf("%s/tcpdump.log", neDirTemp)
filePath := fmt.Sprintf("%s/part_%s.pcap ", neDirTemp, taskCode)
if strings.Contains(cmdStr, "-G") {
filePath = fmt.Sprintf("%s/part_%%Y%%m%%d%%H%%M%%S.pcap ", neDirTemp)
}
sendCmd := fmt.Sprintf("sudo timeout 60m sudo tcpdump -i any %s -w %s > %s 2>&1 & echo $!", cmdStr, filePath, logPath)
// sudo timeout 60m sudo tcpdump -i any -n -s 0 -v -G 60 -W 6 -w /tmp/omc/tcpdump/udm/001/20240817104241/part_%Y-%m-%d_%H:%M:%S.pcap > /tmp/omc/tcpdump/udm/001/20240817104241/tcpdump.log 2>&1 & echo $!
// sudo timeout 60m sudo tcpdump -i any -n -s 0 -v -w /tmp/omc/tcpdump/udm/001/20240817105440/part_2024-08-17_10:54:40.pcap > /tmp/omc/tcpdump/udm/001/20240817105440/tcpdump.log 2>&1 & echo $!
//
// timeout 超时60分钟后发送kill命令1分钟后强制终止命令。tcpdump -G 文件轮转间隔时间(秒) -W 文件轮转保留最近数量
// sudo timeout --kill-after=1m 60m sudo tcpdump -i any -n -s 0 -v -G 10 -W 7 -w /tmp/part_%Y%m%d%H%M%S.pcap > /tmp/part.log 2>&1 & echo $!
// sudo kill $(pgrep -P 722729)
outputPID, err := sshClient.RunCMD(sendCmd)
outputPID = strings.TrimSpace(outputPID)
if err != nil || strings.HasPrefix(outputPID, "stderr:") {
logger.Errorf("DumpStart err: %s => %s", outputPID, err.Error())
return "", err
}
// 检查进程 ps aux | grep tcpdump
// 强杀 sudo pkill tcpdump
pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
dumpPIDMap.Store(pidKey, outputPID)
return taskCode, err
}
// DumpStop 停止已存在抓包句柄
func (s *TcpdumpImpl) DumpStop(neType, neId, taskCode string) (string, error) {
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" {
return "", fmt.Errorf("app.common.noNEInfo")
}
// 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
return "", err
}
defer sshClient.Close()
// 是否存在执行过的进程
pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
pid, ok := dumpPIDMap.Load(pidKey)
if !ok || pid == "" {
return "", fmt.Errorf("tcpdump is not running")
}
defer dumpPIDMap.Delete(pidKey)
// 存放文件目录 /tmp/omc/tcpdump/udm/001/20240817104241
neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
// 命令拼装
sendCmd := fmt.Sprintf("pids=$(pgrep -P %s) && [ -n \"$pids\" ] && sudo kill $pids;sudo timeout 2s cat %s/tcpdump.log", pid, neDirTemp)
// pids=$(pgrep -P 1914341) && [ -n "$pids" ] && sudo kill $pids;sudo timeout 2s cat tcpdump.log
output, err := sshClient.RunCMD(sendCmd)
if err != nil || strings.HasPrefix(output, "stderr:") {
logger.Warnf("DumpStop err: %s => %s", strings.TrimSpace(output), err.Error())
return "", err
}
return output, nil
}
// DumpDownload 抓包文件网元端复制到本地输出zip文件
func (s *TcpdumpImpl) DumpDownload(neType, neId, taskCode string) (string, error) {
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" {
return "", fmt.Errorf("app.common.noNEInfo")
}
// 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
return "", err
}
defer sshClient.Close()
// 网元主机的SSH客户端进行文件传输
sftpClient, err := sshClient.NewClientSFTP()
if err != nil {
return "", fmt.Errorf("ne info sftp client err")
}
defer sftpClient.Close()
neTypeLower := strings.ToLower(neInfo.NeType)
// 网管本地路径
localDirPath := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s", neTypeLower, neInfo.NeId)
if runtime.GOOS == "windows" {
localDirPath = fmt.Sprintf("C:%s", localDirPath)
}
// 网元pcap目录 /tmp/omc/tcpdump/udm/001/20240817104241
sshClient.RunCMD("mkdir -p /tmp/omc && sudo chmod 777 -R /tmp/omc")
neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", neTypeLower, neInfo.NeId, taskCode)
// 网元端复制到本地
localDirFilePath := filepath.Join(localDirPath, taskCode)
if err = sftpClient.CopyDirRemoteToLocal(neDirTemp, localDirFilePath); err != nil {
return "", fmt.Errorf("copy tcpdump file err")
}
// 压缩zip文件名
zipFileName := fmt.Sprintf("%s-%s-pcap-%s.zip", neTypeLower, neInfo.NeId, taskCode)
zipFilePath := filepath.Join(localDirPath, zipFileName)
if err := file.CompressZipByDir(zipFilePath, localDirFilePath); err != nil {
return "", fmt.Errorf("compress zip err")
}
_ = os.RemoveAll(localDirFilePath) // 删除本地临时目录
return zipFilePath, nil
}
// UPFTrace UPF标准版内部抓包
func (s *TcpdumpImpl) UPFTrace(neType, neId, cmdStr string) (string, error) {
// 命令检查
if strings.Contains(cmdStr, "file") {
return "", fmt.Errorf("command cannot contain file")
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" {
return "", fmt.Errorf("app.common.noNEInfo")
}
// 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
return "", err
}
defer sshClient.Close()
// 网元主机的Telnet客户端
telnetClient, err := s.neInfoService.NeRunTelnetClient("UPF", neInfo.NeId, 2)
if err != nil {
return "", err
}
defer telnetClient.Close()
// 命令拼装
fileName := fmt.Sprintf("%s_%s_part_%s.pcap ", neInfo.NeType, neInfo.NeId, time.Now().Format("20060102150405"))
pcapCmd := fmt.Sprintf("%s\r\n", cmdStr)
// 以off结尾是停止抓包不需要写文件
if !strings.Contains(cmdStr, "off") {
// pcap trace rx tx max 100000 intfc any file UPF_001_part_20240817164516.pcap
pcapCmd = fmt.Sprintf("%s file %s\r\n", cmdStr, fileName)
}
// 发送命令 UPF内部默认输出路径/tmp只能写文件名
// pcap trace rx tx max 100000 intfc any file upf_test.pcap
// pcap trace rx tx off
output, err := telnetClient.RunCMD(pcapCmd)
if err != nil {
logger.Warnf("DumpUPF err: %s => %s", output, err.Error())
return "", err
}
// 结果截取
arr := strings.Split(output, "\r\n")
if len(arr) == 2 {
return "", fmt.Errorf("trace pacp run failed")
}
if len(arr) > 3 {
resMsg := arr[2]
// pcap trace: unknown input `f file UPF_001_part_2024-08-19...'
// pcap trace: dispatch trace already enabled...
// pcap trace: dispatch trace already disabled...
// pcap trace: No packets captured...
// Write 100000 packets to /tmp/UPF_001_part_20240817164516.pcap, and stop capture...
if strings.Contains(resMsg, "unknown input") {
return "", fmt.Errorf("trace pacp unknown input")
}
if strings.Contains(resMsg, "already enabled") {
return "", fmt.Errorf("trace pacp already running")
}
if strings.Contains(resMsg, "already disabled") {
return "", fmt.Errorf("trace pacp not running")
}
if strings.Contains(resMsg, "No packets") {
return "", fmt.Errorf("trace pacp not packets")
}
if strings.Contains(resMsg, "packets to") {
matches := regexp.MustCompile(`(/tmp/[^,\s]+)`).FindStringSubmatch(resMsg)
if len(matches) == 0 {
return "", fmt.Errorf("file path not found")
}
return matches[0], nil
}
}
return "trace pacp running", nil
}

View File

@@ -0,0 +1,54 @@
package service
import (
"fmt"
"be.ems/src/modules/trace/model"
"be.ems/src/modules/trace/repository"
)
// 实例化数据层 TraceData 结构体
var NewTraceData = &TraceData{
traceDataRepository: repository.NewTraceData,
}
// TraceData 跟踪_数据 服务层处理
type TraceData struct {
// 跟踪_数据信息
traceDataRepository *repository.TraceData
}
// SelectPage 根据条件分页查询
func (r *TraceData) SelectPage(query map[string]any) map[string]any {
return r.traceDataRepository.SelectPage(query)
}
// SelectById 通过ID查询
func (r *TraceData) SelectById(id string) model.TraceData {
tasks := r.traceDataRepository.SelectByIds([]string{id})
if len(tasks) > 0 {
return tasks[0]
}
return model.TraceData{}
}
// Insert 新增信息
func (r *TraceData) Insert(task model.TraceData) string {
return r.traceDataRepository.Insert(task)
}
// DeleteByIds 批量删除信息
func (r *TraceData) DeleteByIds(ids []string) (int64, error) {
// 检查是否存在
rows := r.traceDataRepository.SelectByIds(ids)
if len(rows) <= 0 {
return 0, fmt.Errorf("not data")
}
if len(rows) == len(ids) {
rows := r.traceDataRepository.DeleteByIds(ids)
return rows, nil
}
// 删除信息失败!
return 0, fmt.Errorf("delete fail")
}

View File

@@ -0,0 +1,294 @@
package service
import (
"encoding/json"
"fmt"
"strings"
"be.ems/src/framework/config"
"be.ems/src/framework/logger"
"be.ems/src/framework/socket"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/parse"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neService "be.ems/src/modules/network_element/service"
"be.ems/src/modules/trace/model"
"be.ems/src/modules/trace/repository"
)
// 实例化数据层 TraceTask 结构体
var NewTraceTask = &TraceTask{
udpService: socket.SocketUDP{},
traceTaskRepository: repository.NewTraceTask,
traceDataRepository: repository.NewTraceData,
}
// TraceTask 跟踪任务 服务层处理
type TraceTask struct {
// UDP服务对象
udpService socket.SocketUDP
// 跟踪_任务数据信息
traceTaskRepository *repository.TraceTask
// 跟踪_数据信息
traceDataRepository *repository.TraceData
}
// CreateUDP 创建UDP数据通道
func (r *TraceTask) CreateUDP() error {
// 跟踪配置是否开启
if v := config.Get("trace.enabled"); v != nil {
if !v.(bool) {
return nil
}
}
host := "127.0.0.1"
if v := config.Get("trace.host"); v != nil {
host = v.(string)
}
var port int64 = 33033
if v := config.Get("trace.port"); v != nil {
port = parse.Number(v)
}
// 初始化UDP服务
r.udpService = socket.SocketUDP{Addr: host, Port: port}
if _, err := r.udpService.New(); err != nil {
return err
}
// 接收处理UDP数据
go r.udpService.Resolve(2048, func(data []byte, n int) {
logger.Infof("socket UDP: %s", string(data))
mData, err := UDPDataHandler(data, n)
if err != nil {
logger.Errorf("udp resolve data fail: %s", err.Error())
return
}
// 插入数据库做记录
r.traceDataRepository.Insert(model.TraceData{
TaskId: parse.Number(mData["taskId"]),
IMSI: mData["imsi"].(string),
SrcAddr: mData["srcAddr"].(string),
DstAddr: mData["dstAddr"].(string),
IfType: parse.Number(mData["ifType"]),
MsgType: parse.Number(mData["msgType"]),
MsgDirect: parse.Number(mData["msgDirect"]),
Length: parse.Number(mData["dataLen"]),
RawMsg: mData["dataInfo"].(string),
Timestamp: parse.Number(mData["timestamp"]),
DecMsg: mData["decMsg"].(string),
})
// 推送文件
if v, ok := mData["pcapFile"]; ok && v != "" {
logger.Infof("pcapFile: %s", v)
}
})
// ============ 测试接收网元UDP发过来的数据
// 初始化TCP服务 后续调整TODO
tcpService := socket.SocketTCP{Addr: host, Port: port + 1}
if _, err := tcpService.New(); err != nil {
return err
}
// 接收处理TCP数据
go tcpService.Resolve(1024, func(data []byte, n int) {
logger.Infof("socket TCP: %s", string(data))
mData, err := UDPDataHandler(data, n)
if err != nil {
logger.Errorf("tcp resolve data fail: %s", err.Error())
return
}
// 插入数据库做记录
r.traceDataRepository.Insert(model.TraceData{
TaskId: parse.Number(mData["taskId"]),
IMSI: mData["imsi"].(string),
SrcAddr: mData["srcAddr"].(string),
DstAddr: mData["dstAddr"].(string),
IfType: parse.Number(mData["ifType"]),
MsgType: parse.Number(mData["msgType"]),
MsgDirect: parse.Number(mData["msgDirect"]),
Length: parse.Number(mData["dataLen"]),
RawMsg: mData["dataInfo"].(string),
Timestamp: parse.Number(mData["timestamp"]),
DecMsg: mData["decMsg"].(string),
})
// 推送文件
if v, ok := mData["pcapFile"]; ok && v != "" {
logger.Infof("pcapFile: %s", v)
}
})
return nil
}
// CloseUDP 关闭UDP数据通道
func (r *TraceTask) CloseUDP() {
r.udpService.Close()
}
// SelectPage 根据条件分页查询
func (r *TraceTask) SelectPage(query map[string]any) map[string]any {
return r.traceTaskRepository.SelectPage(query)
}
// SelectById 通过ID查询
func (r *TraceTask) SelectById(id string) model.TraceTask {
tasks := r.traceTaskRepository.SelectByIds([]string{id})
if len(tasks) > 0 {
return tasks[0]
}
return model.TraceTask{}
}
// Insert 新增信息
func (r *TraceTask) Insert(task model.TraceTask) error {
// 跟踪配置是否开启
if v := config.Get("trace.enabled"); v != nil {
if !v.(bool) {
return fmt.Errorf("tracking is not enabled")
}
}
host := "127.0.0.1"
if v := config.Get("trace.host"); v != nil {
host = v.(string)
}
var port int64 = 33033
if v := config.Get("trace.port"); v != nil {
port = parse.Number(v)
}
task.NotifyUrl = fmt.Sprintf("udp:%s:%d", host, port)
// 查询网元获取IP
neInfo := neService.NewNeInfoImpl.SelectNeInfoByNeTypeAndNeID(task.NeType, task.NeId)
if neInfo.NeId != task.NeId || neInfo.IP == "" {
return fmt.Errorf("app.common.noNEInfo")
}
traceId := r.traceTaskRepository.LastID() + 1 // 生成任务ID < 65535
task.TraceId = fmt.Sprint(traceId)
// 发送任务给网元
data := map[string]any{
"neType": neInfo.NeType,
"neId": neInfo.NeId,
"notifyUrl": task.NotifyUrl,
"id": traceId,
"startTime": date.ParseDateToStr(task.StartTime, date.YYYY_MM_DD_HH_MM_SS),
"endTime": date.ParseDateToStr(task.EndTime, date.YYYY_MM_DD_HH_MM_SS),
}
switch task.TraceType {
case "1": // Interface
data["traceType"] = "Interface"
data["interfaces"] = strings.Split(task.Interfaces, ",")
case "2": // Device
data["traceType"] = "Device"
data["ueIp"] = task.UeIp
data["srcIp"] = task.SrcIp
data["dstIp"] = task.DstIp
data["signalPort"] = task.SignalPort
task.UeIp = neInfo.IP
case "3": // UE
data["traceType"] = "UE"
data["imsi"] = task.IMSI
data["msisdn"] = task.MSISDN
default:
return fmt.Errorf("trace type is not disabled")
}
msg, err := neFetchlink.NeTraceAdd(neInfo, data)
if err != nil {
return err
}
s, _ := json.Marshal(msg)
task.FetchMsg = string(s)
// 插入数据库
r.traceTaskRepository.Insert(task)
return nil
}
// Update 修改信息
func (r *TraceTask) Update(task model.TraceTask) error {
// 跟踪配置是否开启
if v := config.Get("trace.enabled"); v != nil {
if !v.(bool) {
return fmt.Errorf("tracking is not enabled")
}
}
host := "127.0.0.1"
if v := config.Get("trace.host"); v != nil {
host = v.(string)
}
var port int64 = 33033
if v := config.Get("trace.port"); v != nil {
port = parse.Number(v)
}
task.NotifyUrl = fmt.Sprintf("udp:%s:%d", host, port)
// 查询网元获取IP
neInfo := neService.NewNeInfoImpl.SelectNeInfoByNeTypeAndNeID(task.NeType, task.NeId)
if neInfo.NeId != task.NeId || neInfo.IP == "" {
return fmt.Errorf("app.common.noNEInfo")
}
// 查询网元任务信息
if msg, err := neFetchlink.NeTraceInfo(neInfo, task.TraceId); err == nil {
s, _ := json.Marshal(msg)
task.FetchMsg = string(s)
// 修改任务信息
data := map[string]any{
"neType": neInfo.NeType,
"neId": neInfo.NeId,
"notifyUrl": task.NotifyUrl,
"id": parse.Number(task.TraceId),
"startTime": date.ParseDateToStr(task.StartTime, date.YYYY_MM_DD_HH_MM_SS),
"endTime": date.ParseDateToStr(task.EndTime, date.YYYY_MM_DD_HH_MM_SS),
}
switch task.TraceType {
case "1": // Interface
data["traceType"] = "Interface"
data["interfaces"] = strings.Split(task.Interfaces, ",")
case "2": // Device
task.UeIp = neInfo.IP
data["traceType"] = "Device"
data["ueIp"] = task.UeIp
data["srcIp"] = task.SrcIp
data["dstIp"] = task.DstIp
data["signalPort"] = task.SignalPort
case "3": // UE
data["traceType"] = "UE"
data["imsi"] = task.IMSI
data["msisdn"] = task.MSISDN
default:
return fmt.Errorf("trace type is not disabled")
}
neFetchlink.NeTraceEdit(neInfo, data)
}
// 更新数据库
r.traceTaskRepository.Update(task)
return nil
}
// DeleteByIds 批量删除信息
func (r *TraceTask) DeleteByIds(ids []string) (int64, error) {
// 检查是否存在
rows := r.traceTaskRepository.SelectByIds(ids)
if len(rows) <= 0 {
return 0, fmt.Errorf("not data")
}
if len(rows) == len(ids) {
// 停止任务
for _, v := range rows {
neInfo := neService.NewNeInfoImpl.SelectNeInfoByNeTypeAndNeID(v.NeType, v.NeId)
if neInfo.NeId != v.NeId || neInfo.IP == "" {
continue
}
neFetchlink.NeTraceDelete(neInfo, v.TraceId)
}
num := r.traceTaskRepository.DeleteByIds(ids)
return num, nil
}
// 删除信息失败!
return 0, fmt.Errorf("delete fail")
}

View File

@@ -0,0 +1,208 @@
package service
import (
"encoding/json"
"fmt"
"path/filepath"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/ssh"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neModel "be.ems/src/modules/network_element/model"
neService "be.ems/src/modules/network_element/service"
"be.ems/src/modules/trace/model"
"be.ems/src/modules/trace/repository"
)
// 实例化数据层 TraceTaskHlr 结构体
var NewTraceTaskHlr = &TraceTaskHlr{
traceTaskHlrRepository: repository.NewTraceTaskHlr,
neInfoService: neService.NewNeInfoImpl,
}
// TraceTaskHlr 跟踪_任务给HRL网元 服务层处理
type TraceTaskHlr struct {
// 跟踪_任务给HRL网元数据信息
traceTaskHlrRepository *repository.TraceTaskHlr
// 网元信息服务
neInfoService neService.INeInfo
}
// SelectPage 根据条件分页查询
func (r *TraceTaskHlr) SelectPage(querys model.TraceTaskHlrQuery) map[string]any {
return r.traceTaskHlrRepository.SelectPage(querys)
}
// SelectById 通过ID查询
func (r *TraceTaskHlr) SelectById(id string) model.TraceTaskHlr {
tasks := r.traceTaskHlrRepository.SelectByIds([]string{id})
if len(tasks) > 0 {
return tasks[0]
}
return model.TraceTaskHlr{}
}
// Insert 新增信息
func (r *TraceTaskHlr) Insert(task model.TraceTaskHlr) string {
return r.traceTaskHlrRepository.Insert(task)
}
// Update 修改信息
func (r *TraceTaskHlr) Update(task model.TraceTaskHlr) int64 {
return r.traceTaskHlrRepository.Update(task)
}
// DeleteByIds 批量删除信息
func (r *TraceTaskHlr) DeleteByIds(ids []string) (int64, error) {
// 检查是否存在
rows := r.traceTaskHlrRepository.SelectByIds(ids)
if len(rows) <= 0 {
return 0, fmt.Errorf("not data")
}
if len(rows) == len(ids) {
// 停止任务
neInfos := r.neInfoService.SelectList(neModel.NeInfo{NeType: "UDM"}, false, false)
for _, r := range rows {
if r.Status == "0" {
continue
}
for _, v := range neInfos {
neFetchlink.HLRTraceStop(v, map[string]any{
"traceIDArray": []string{r.TraceId},
})
}
}
num := r.traceTaskHlrRepository.DeleteByIds(ids)
return num, nil
}
// 删除信息失败!
return 0, fmt.Errorf("delete fail")
}
// Start 创建任务
func (r *TraceTaskHlr) Start(task model.TraceTaskHlr) (string, error) {
hlrList := []map[string]any{}
traceId := r.traceTaskHlrRepository.LastID() + 1 // 生成任务ID < 65535
data := map[string]any{
"traceID": traceId,
"imsi": task.IMSI,
"msisdn": task.MSISDN,
}
if task.StartTime > task.EndTime {
return "", fmt.Errorf("startTime must less than endTime")
}
if task.StartTime > 0 {
data["startTime"] = date.ParseDateToStr(task.StartTime, date.YYYY_MM_DDTHH_MM_SSZ)
}
if task.StartTime > 0 {
data["endTime"] = date.ParseDateToStr(task.EndTime, date.YYYY_MM_DDTHH_MM_SSZ)
}
// 发送创建任务
neInfos := r.neInfoService.SelectList(neModel.NeInfo{NeType: "UDM"}, false, false)
for _, neInfo := range neInfos {
hlrItem := map[string]any{
"neType": neInfo.NeType,
"neId": neInfo.NeId,
"msg": "",
}
msg, err := neFetchlink.HLRTraceStart(neInfo, data)
if err != nil {
hlrItem["err"] = err.Error()
} else {
hlrItem["err"] = msg
}
hlrList = append(hlrList, hlrItem)
}
msg, _ := json.Marshal(hlrList)
task.Msg = string(msg)
task.Status = "1"
task.TraceId = fmt.Sprint(traceId)
id := r.traceTaskHlrRepository.Insert(task)
if id == "" {
return "", fmt.Errorf("start task fail")
}
return id, nil
}
// Stop 停止任务
func (r *TraceTaskHlr) Stop(task model.TraceTaskHlr) error {
hlrList := []map[string]any{}
// 发送停止任务
neInfos := r.neInfoService.SelectList(neModel.NeInfo{NeType: "UDM"}, false, false)
for _, neInfo := range neInfos {
hlrItem := map[string]any{
"neType": neInfo.NeType,
"neId": neInfo.NeId,
"msg": "",
}
msg, err := neFetchlink.HLRTraceStop(neInfo, map[string]any{
"traceIDArray": []string{task.TraceId},
})
if err != nil {
hlrItem["err"] = err.Error()
} else {
hlrItem["err"] = msg
}
hlrList = append(hlrList, hlrItem)
}
msg, _ := json.Marshal(hlrList)
task.Msg = string(msg)
task.Status = "0"
rows := r.traceTaskHlrRepository.Update(task)
if rows <= 0 {
return fmt.Errorf("stop task fail")
}
return nil
}
// File 任务文件
func (r *TraceTaskHlr) File(traceId, dirPath string) ([]map[string]any, error) {
hlrList := []map[string]any{}
// 查询所有匹配的网元类型
neInfos := r.neInfoService.SelectList(neModel.NeInfo{NeType: "UDM"}, false, false)
if len(neInfos) == 0 {
return nil, fmt.Errorf("not found network element")
}
// 遍历多个网元主机获取文件
for _, neInfo := range neInfos {
hlrItem := map[string]any{
"neType": neInfo.NeType,
"neId": neInfo.NeId,
"neName": neInfo.NeName,
"err": "",
}
// 网元主机的SSH客户端
sshClient, err := r.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
hlrItem["err"] = "ssh link fail"
hlrList = append(hlrList, hlrItem)
continue
}
defer sshClient.Close()
// 获取文件列表
fileName := fmt.Sprintf("%s_%s", neInfo.NeName, traceId)
_, rows, err := ssh.FileList(sshClient, filepath.ToSlash(dirPath), fileName)
if err != nil {
hlrItem["err"] = "file not found"
hlrList = append(hlrList, hlrItem)
continue
}
// 遍历组装
for _, v := range rows {
if v.FileType == "file" {
hlrItem["fileName"] = v.FileName
hlrItem["filePath"] = filepath.ToSlash(filepath.Join(dirPath, v.FileName))
hlrList = append(hlrList, hlrItem)
}
}
}
return hlrList, nil
}

View File

@@ -0,0 +1,330 @@
package service
import (
"encoding/base64"
"encoding/binary"
"fmt"
"os"
"runtime"
"strings"
"time"
"golang.org/x/net/http/httpguts"
"golang.org/x/net/http2/hpack"
)
const (
GTPU_V1_VERSION = 1 << 5
GTPU_VER_MASK = 7 << 5
GTPU_PT_GTP = 1 << 4
GTPU_HEADER_LEN = 12
GTPU_E_S_PB_BIT = 7
GTPU_E_BI = 1 << 2
)
const (
GTPU_HEADER_VERSION_INDEX = 0
GTPU_HEADER_MSG_TYPE_INDEX = 1
GTPU_HEADER_LENGTH_INDEX = 2
GTPU_HEADER_TEID_INDEX = 4
)
type ExtHeader struct {
TaskId uint32
IMSI string
IfType int
MsgType int
MsgDirect int // 0-recv,1-send
TimeStamp int64
SrcIP string
DstIP string
SrcPort uint16
DstPort uint16
Proto int // Protocol
PPI int // only for SCTP
DataLen uint16
DataInfo []byte
}
// parseUDPData 解析UDP数据
func parseUDPData(rvMsg []byte, rvLen int) (ExtHeader, error) {
var extHdr ExtHeader
// var tr dborm.TraceData
var off int
msg := rvMsg
verFlags := msg[GTPU_HEADER_VERSION_INDEX]
gtpuHdrLen := GTPU_HEADER_LEN
localTeid := binary.BigEndian.Uint32(msg[GTPU_HEADER_TEID_INDEX:])
extHdr.TaskId = localTeid
if (verFlags & GTPU_E_S_PB_BIT) != 0 {
if (verFlags & GTPU_E_BI) != 0 {
extTypeIndex := GTPU_HEADER_LEN - 1
extType := msg[extTypeIndex]
if extType == 0xFE {
extHdr.IMSI = string(msg[extTypeIndex+2 : extTypeIndex+17])
extHdr.IfType = int(msg[extTypeIndex+17])
extHdr.MsgType = int(msg[extTypeIndex+18])
extHdr.MsgDirect = int(msg[extTypeIndex+19])
extHdr.TimeStamp = time.Now().UTC().UnixMilli()
// extHdr.TimeStamp = int64(binary.BigEndian.Uint64(msg[extTypeIndex+19:]))
// fmt.Printf("ext info %v %s %d %d %d \n", msg[(extTypeIndex+2):(extTypeIndex+20)], extHdr.IMSI, extHdr.IfType, extHdr.MsgType, extHdr.MsgDirect)
// set offset of IP Packet
off = 40 + 4
//src ip: msg+40+12
extHdr.SrcIP = fmt.Sprintf("%d.%d.%d.%d", msg[off+12], msg[off+13], msg[off+14], msg[off+15])
//dst ip: msg+40+12+4
extHdr.DstIP = fmt.Sprintf("%d.%d.%d.%d", msg[off+16], msg[off+17], msg[off+18], msg[off+19])
extHdr.SrcPort = uint16(binary.BigEndian.Uint16(msg[off+20:]))
extHdr.DstPort = uint16(binary.BigEndian.Uint16(msg[off+22:]))
// fmt.Printf("info %s:%d %s:%d \n", extHdr.SrcIP, extHdr.SrcPort, extHdr.DstIP, extHdr.DstPort)
// ip header start msg+40
extHdr.DataLen = uint16(rvLen - off)
extHdr.DataInfo = make([]byte, int(rvLen-off))
copy(extHdr.DataInfo, []byte(msg[off:]))
// 132 SCTP
// 6 TCP
// 17 UDP
extHdr.Proto = int(msg[off+9])
if extHdr.Proto == 132 {
extHdr.PPI = int(msg[off+47])
extHdr.DataLen = uint16(binary.BigEndian.Uint16(msg[(off+34):]) - 16)
// fmt.Printf("dat len %d %d \n", extHdr.DataLen, extHdr.PPI)
}
}
for extType != 0 && extTypeIndex < rvLen {
extLen := msg[extTypeIndex+1] << 2
if extLen == 0 {
return extHdr, fmt.Errorf("error, extLen is zero")
}
gtpuHdrLen += int(extLen)
extTypeIndex += int(extLen)
extType = msg[extTypeIndex]
}
}
} else {
gtpuHdrLen -= 4
}
return extHdr, nil
}
// UDPDataHandler UDP数据处理
func UDPDataHandler(data []byte, n int) (map[string]any, error) {
extHdr, err := parseUDPData(data, n)
if err != nil {
return nil, err
}
if extHdr.TaskId == 0 || extHdr.DataLen < 1 {
return nil, fmt.Errorf("data error")
}
m := map[string]any{
"taskId": extHdr.TaskId,
"imsi": extHdr.IMSI,
"ifType": extHdr.IfType,
"srcAddr": fmt.Sprintf("%s:%d", extHdr.SrcIP, extHdr.SrcPort),
"dstAddr": fmt.Sprintf("%s:%d", extHdr.DstIP, extHdr.DstPort),
"msgType": extHdr.MsgType,
"msgDirect": extHdr.MsgDirect,
"timestamp": extHdr.TimeStamp,
"dataLen": extHdr.DataLen,
// "dataInfo": extHdr.DataInfo,
"decMsg": "",
}
// Base64 编码
m["dataInfo"] = base64.StdEncoding.EncodeToString(extHdr.DataInfo)
if extHdr.Proto == 6 { // TCP
// 取响应数据
iplen := uint16(binary.BigEndian.Uint16(extHdr.DataInfo[2:]))
tcplen := uint16(iplen - 32 - 20)
hdrlen := uint16(binary.BigEndian.Uint16(extHdr.DataInfo[20+32+1:]))
offset := uint16(52)
// fmt.Printf("HTTP %d %d %d \n", iplen, tcplen, hdrlen)
if tcplen > (hdrlen + 9) { // has data
doffset := uint16(offset + hdrlen + 9)
datlen := uint16(binary.BigEndian.Uint16(extHdr.DataInfo[doffset+1:]))
// fmt.Printf("HTTP datlen %d \n", datlen)
m["decMsg"], _ = httpDataMsg(extHdr.DataInfo[offset+9:offset+9+hdrlen], extHdr.DataInfo[doffset+9:doffset+datlen+9])
} else {
m["decMsg"], _ = httpDataMsg(extHdr.DataInfo[offset+9:hdrlen], nil)
}
}
// pcap文件
m["pcapFile"] = writePcap(extHdr)
return m, nil
}
// =========== TCP协议Body ===========
// httpDataMsg Http数据信息处理
func httpDataMsg(header []byte, data []byte) (string, error) {
var remainSize = uint32(16 << 20)
var sawRegular bool
var invalid bool // pseudo header field errors
var Fields []hpack.HeaderField
invalid = false
hdec := hpack.NewDecoder(4096, nil)
hdec.SetEmitEnabled(true)
hdec.SetMaxStringLength(int(16 << 20))
hdec.SetEmitFunc(func(hf hpack.HeaderField) {
if !httpguts.ValidHeaderFieldValue(hf.Value) {
// Don't include the value in the error, because it may be sensitive.
invalid = true
}
isPseudo := strings.HasPrefix(hf.Name, ":")
if isPseudo {
if sawRegular {
invalid = true
}
} else {
sawRegular = true
if !validWireHeaderFieldName(hf.Name) {
invalid = true
}
}
if invalid {
hdec.SetEmitEnabled(false)
return
}
size := hf.Size()
if size > remainSize {
hdec.SetEmitEnabled(false)
//mh.Truncated = true
return
}
remainSize -= size
Fields = append(Fields, hf)
})
// defer hdec.SetEmitFunc(func(hf hpack.HeaderField) {})
frag := header
if _, err := hdec.Write(frag); err != nil {
return "", err
}
if err := hdec.Close(); err != nil {
return "", err
}
// hdec.SetEmitFunc(func(hf hpack.HeaderField) {})
var headers []byte
var line string
for i := range Fields {
line = fmt.Sprintf("\"%s\":\"%s\",", Fields[i].Name, Fields[i].Value)
headers = append(headers, []byte(line)...)
}
if len(data) > 0 {
return fmt.Sprintf("{ %s \"content\":%s }", string(headers), string(data)), nil
} else {
return fmt.Sprintf("{ %s }", string(headers)), nil
}
}
// validWireHeaderFieldName 校验报文头字段名称
func validWireHeaderFieldName(v string) bool {
if len(v) == 0 {
return false
}
for _, r := range v {
if !httpguts.IsTokenRune(r) {
return false
}
if 'A' <= r && r <= 'Z' {
return false
}
}
return true
}
// =========== writePcap 写Pcap文件 ===========
const magicMicroseconds = 0xA1B2C3D4
const versionMajor = 2
const versionMinor = 4
func writeEmptyPcap(filename string, timeStamp int64, length int, data []byte) error {
var err error
var file *os.File
if _, err = os.Stat(filename); os.IsNotExist(err) {
file, err = os.Create(filename)
// File Header
var fileHeaderBuf [24]byte
binary.LittleEndian.PutUint32(fileHeaderBuf[0:4], magicMicroseconds)
binary.LittleEndian.PutUint16(fileHeaderBuf[4:6], versionMajor)
binary.LittleEndian.PutUint16(fileHeaderBuf[6:8], versionMinor)
// bytes 8:12 stay 0 (timezone = UTC)
// bytes 12:16 stay 0 (sigfigs is always set to zero, according to
// http://wiki.wireshark.org/Development/LibpcapFileFormat
binary.LittleEndian.PutUint32(fileHeaderBuf[16:20], 0x00040000)
binary.LittleEndian.PutUint32(fileHeaderBuf[20:24], 0x00000071)
if _, err := file.Write(fileHeaderBuf[:]); err != nil {
return err
}
} else {
file, err = os.OpenFile(filename, os.O_WRONLY|os.O_APPEND, 0666)
}
if err != nil {
return err
}
defer file.Close()
// Packet Header
var packetHeaderBuf [24]byte
t := time.UnixMilli(timeStamp)
if t.IsZero() {
t = time.Now()
}
secs := t.Unix()
usecs := t.Nanosecond() / 1000
binary.LittleEndian.PutUint32(packetHeaderBuf[0:4], uint32(secs))
binary.LittleEndian.PutUint32(packetHeaderBuf[4:8], uint32(usecs))
binary.LittleEndian.PutUint32(packetHeaderBuf[8:12], uint32(length+16))
binary.LittleEndian.PutUint32(packetHeaderBuf[12:16], uint32(length+16))
if _, err := file.Write(packetHeaderBuf[:]); err != nil {
return err
}
// 数据包内容的定义
cooked := [...]byte{0x00, 0x00, 0x03, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00}
if _, err := file.Write(cooked[:]); err != nil {
return err
}
// Packet Data
if _, err := file.Write(data); err != nil {
return err
}
return nil
}
// writePcap 写Pcap文件并返回文件路径
func writePcap(extHdr ExtHeader) string {
filePath := fmt.Sprintf("/tmp/trace_%d .pcap", extHdr.TaskId)
if runtime.GOOS == "windows" {
filePath = fmt.Sprintf("C:%s", filePath)
}
err := writeEmptyPcap(filePath, extHdr.TimeStamp, int(extHdr.DataLen), extHdr.DataInfo)
if err != nil {
return ""
}
return filePath
}

View File

@@ -5,6 +5,7 @@ import (
"be.ems/src/framework/middleware"
"be.ems/src/framework/middleware/collectlogs"
"be.ems/src/modules/trace/controller"
"be.ems/src/modules/trace/service"
"github.com/gin-gonic/gin"
)
@@ -13,6 +14,9 @@ import (
func Setup(router *gin.Engine) {
logger.Infof("开始加载 ====> trace 模块路由")
// 启动时需要的初始参数
InitLoad()
traceGroup := router.Group("/trace")
// 信令抓包
@@ -38,4 +42,77 @@ func Setup(router *gin.Engine) {
controller.NewTcpdump.UPFTrace,
)
}
// 跟踪任务 网元HLR (免登录)
taskHLRGroup := traceGroup.Group("/task/hlr")
{
taskHLRGroup.GET("/list",
controller.NewTraceTaskHlr.List,
)
taskHLRGroup.DELETE("/:ids",
controller.NewTraceTaskHlr.Remove,
)
taskHLRGroup.POST("/start",
controller.NewTraceTaskHlr.Start,
)
taskHLRGroup.POST("/stop",
controller.NewTraceTaskHlr.Stop,
)
taskHLRGroup.POST("/file",
controller.NewTraceTaskHlr.File,
)
taskHLRGroup.GET("/filePull",
controller.NewTraceTaskHlr.FilePull,
)
}
// 跟踪任务
taskGroup := traceGroup.Group("/task")
{
taskGroup.GET("/list",
middleware.PreAuthorize(nil),
controller.NewTraceTask.List,
)
taskGroup.GET("/:id",
middleware.PreAuthorize(nil),
controller.NewTraceTask.Info,
)
taskGroup.POST("",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.task", collectlogs.BUSINESS_TYPE_INSERT)),
controller.NewTraceTask.Add,
)
taskGroup.PUT("",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.task", collectlogs.BUSINESS_TYPE_UPDATE)),
controller.NewTraceTask.Edit,
)
taskGroup.DELETE("/:ids",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.task", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewTraceTask.Remove,
)
}
// 跟踪数据
taskDataGroup := traceGroup.Group("/data")
{
taskDataGroup.GET("/list",
middleware.PreAuthorize(nil),
controller.NewTraceData.List,
)
taskDataGroup.DELETE("/:ids",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.taskData", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewTraceData.Remove,
)
}
}
// InitLoad 初始参数
func InitLoad() {
// 创建跟踪任务信令数据通道UDP
if err := service.NewTraceTask.CreateUDP(); err != nil {
logger.Errorf("socket udp init fail: %s", err.Error())
}
}

View File

@@ -10,10 +10,10 @@ import (
"be.ems/src/framework/i18n"
"be.ems/src/framework/logger"
"be.ems/src/framework/telnet"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/ssh"
"be.ems/src/framework/utils/telnet"
"be.ems/src/framework/vo/result"
"be.ems/src/modules/ws/service"
"github.com/gin-gonic/gin"
@@ -286,10 +286,6 @@ func (s *WSController) Telnet(c *gin.Context) {
go s.wsService.ClientWriteListen(wsClient)
go s.wsService.ClientReadListen(wsClient, service.ReceiveTelnet)
// 等待1秒排空首次消息
time.Sleep(1 * time.Second)
_ = clientSession.Read()
// 实时读取Telnet消息直接输出
msTicker := time.NewTicker(100 * time.Millisecond)
defer msTicker.Stop()
@@ -298,7 +294,7 @@ func (s *WSController) Telnet(c *gin.Context) {
case ms := <-msTicker.C:
outputByte := clientSession.Read()
if len(outputByte) > 0 {
outputStr := strings.TrimRight(string(outputByte), "\x00")
outputStr := string(outputByte)
msgByte, _ := json.Marshal(result.Ok(map[string]any{
"requestId": fmt.Sprintf("telnet_%s_%d", neHost.HostID, ms.UnixMilli()),
"data": outputStr,

View File

@@ -8,13 +8,13 @@ type NetConnectData struct {
Status string `json:"status"`
Laddr net.Addr `json:"localaddr"`
Raddr net.Addr `json:"remoteaddr"`
PID int32 `json:"PID"`
PID int32 `json:"pid"`
Name string `json:"name"`
}
// NetConnectQuery 网络连接进程查询
type NetConnectQuery struct {
Port int32 `json:"port"`
ProcessName string `json:"processName"`
ProcessID int32 `json:"processID"`
Port int32 `json:"port"`
Name string `json:"name"`
PID int32 `json:"pid"`
}

View File

@@ -2,37 +2,33 @@ package model
// PsProcessData 进程数据
type PsProcessData struct {
PID int32 `json:"PID"`
PID int32 `json:"pid"`
Name string `json:"name"`
PPID int32 `json:"PPID"`
PPID int32 `json:"ppid"`
Username string `json:"username"`
Status string `json:"status"`
StartTime string `json:"startTime"`
StartTime int64 `json:"startTime"`
NumThreads int32 `json:"numThreads"`
NumConnections int `json:"numConnections"`
CpuPercent string `json:"cpuPercent"`
DiskRead string `json:"diskRead"`
DiskWrite string `json:"diskWrite"`
CmdLine string `json:"cmdLine"`
DiskRead uint64 `json:"diskRead"`
DiskWrite uint64 `json:"diskWrite"`
Rss string `json:"rss"`
VMS string `json:"vms"`
HWM string `json:"hwm"`
Data string `json:"data"`
Stack string `json:"stack"`
Locked string `json:"locked"`
Swap string `json:"swap"`
Rss uint64 `json:"rss"`
VMS uint64 `json:"vms"`
HWM uint64 `json:"hwm"`
Data uint64 `json:"data"`
Stack uint64 `json:"stack"`
Locked uint64 `json:"locked"`
Swap uint64 `json:"swap"`
CpuValue float64 `json:"cpuValue"`
RssValue uint64 `json:"rssValue"`
Envs []string `json:"envs"`
CmdLine string `json:"cmdLine"`
}
// PsProcessQuery 进程查询
type PsProcessQuery struct {
Pid int32 `json:"pid"`
PID int32 `json:"pid"`
Name string `json:"name"`
Username string `json:"username"`
}

View File

@@ -29,13 +29,16 @@ func GetNetConnections(requestID string, data any) ([]byte, error) {
continue
}
for _, conn := range connections {
if query.ProcessID > 0 && query.ProcessID != conn.Pid {
if query.PID > 0 && query.PID != conn.Pid {
continue
}
proc, err := process.NewProcess(conn.Pid)
if err == nil {
name, _ := proc.Name()
if name != "" && query.ProcessName != "" && !strings.Contains(name, query.ProcessName) {
name, err := proc.Name()
if err != nil {
continue
}
if query.Name != "" && !strings.Contains(name, query.Name) {
continue
}
if query.Port > 0 && query.Port != int32(conn.Laddr.Port) && query.Port != int32(conn.Raddr.Port) {

View File

@@ -8,8 +8,6 @@ import (
"sync"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/vo/result"
"be.ems/src/modules/ws/model"
"github.com/shirou/gopsutil/v4/process"
@@ -31,86 +29,68 @@ func GetProcessData(requestID string, data any) ([]byte, error) {
return nil, err
}
var (
dataArr = []model.PsProcessData{}
resultMutex sync.Mutex
wg sync.WaitGroup
numWorkers = 4
)
handleData := func(proc *process.Process) {
// 解析数据
handleData := func(proc *process.Process) (model.PsProcessData, bool) {
procData := model.PsProcessData{
PID: proc.Pid,
}
if query.Pid > 0 && query.Pid != proc.Pid {
return
}
procName, err := proc.Name()
if procName == "" || err != nil {
return
} else {
if procName, err := proc.Name(); err == nil {
procData.Name = procName
}
if query.Name != "" && !strings.Contains(procData.Name, query.Name) {
return
}
if username, err := proc.Username(); err == nil {
procData.Username = username
}
// 查询过滤
if query.PID > 0 && procData.PID != query.PID {
return procData, false
}
if query.Name != "" && !strings.Contains(procData.Name, query.Name) {
return procData, false
}
if query.Username != "" && !strings.Contains(procData.Username, query.Username) {
return
return procData, false
}
procData.PPID, _ = proc.Ppid()
statusArray, _ := proc.Status()
if len(statusArray) > 0 {
if statusArray, err := proc.Status(); err == nil && len(statusArray) > 0 {
procData.Status = strings.Join(statusArray, ",")
}
createTime, procErr := proc.CreateTime()
if procErr == nil {
procData.StartTime = date.ParseDateToStr(createTime, date.YYYY_MM_DD_HH_MM_SS)
if createTime, err := proc.CreateTime(); err == nil {
procData.StartTime = createTime
}
procData.NumThreads, _ = proc.NumThreads()
procData.CpuValue, _ = proc.CPUPercent()
procData.CpuPercent = fmt.Sprintf("%.2f", procData.CpuValue) + "%"
if connections, err := proc.Connections(); err == nil {
procData.NumConnections = len(connections)
}
cpuPercent, _ := proc.CPUPercent()
procData.CpuPercent = fmt.Sprintf("%.2f", cpuPercent)
menInfo, procErr := proc.MemoryInfo()
if procErr == nil {
procData.Rss = parse.Bit(float64(menInfo.RSS))
procData.Data = parse.Bit(float64(menInfo.Data))
procData.VMS = parse.Bit(float64(menInfo.VMS))
procData.HWM = parse.Bit(float64(menInfo.HWM))
procData.Stack = parse.Bit(float64(menInfo.Stack))
procData.Locked = parse.Bit(float64(menInfo.Locked))
procData.Swap = parse.Bit(float64(menInfo.Swap))
procData.RssValue = menInfo.RSS
} else {
procData.Rss = "--"
procData.Data = "--"
procData.VMS = "--"
procData.HWM = "--"
procData.Stack = "--"
procData.Locked = "--"
procData.Swap = "--"
procData.RssValue = 0
procData.Rss = menInfo.RSS
procData.Data = menInfo.Data
procData.VMS = menInfo.VMS
procData.HWM = menInfo.HWM
procData.Stack = menInfo.Stack
procData.Locked = menInfo.Locked
procData.Swap = menInfo.Swap
}
ioStat, procErr := proc.IOCounters()
if procErr == nil {
procData.DiskWrite = parse.Bit(float64(ioStat.WriteBytes))
procData.DiskRead = parse.Bit(float64(ioStat.ReadBytes))
} else {
procData.DiskWrite = "--"
procData.DiskRead = "--"
if ioStat, err := proc.IOCounters(); err == nil {
procData.DiskWrite = ioStat.WriteBytes
procData.DiskRead = ioStat.ReadBytes
}
procData.CmdLine, _ = proc.Cmdline()
procData.Envs, _ = proc.Environ()
resultMutex.Lock()
dataArr = append(dataArr, procData)
resultMutex.Unlock()
return procData, true
}
var (
dataArr = []model.PsProcessData{}
mu sync.Mutex
wg sync.WaitGroup
numWorkers = 4
)
chunkSize := (len(processes) + numWorkers - 1) / numWorkers
for i := 0; i < numWorkers; i++ {
wg.Add(1)
@@ -122,9 +102,15 @@ func GetProcessData(requestID string, data any) ([]byte, error) {
go func(start, end int) {
defer wg.Done()
localDataArr := make([]model.PsProcessData, 0, end-start) // 本地切片避免竞态
for j := start; j < end; j++ {
handleData(processes[j])
if data, ok := handleData(processes[j]); ok {
localDataArr = append(localDataArr, data)
}
}
mu.Lock()
dataArr = append(dataArr, localDataArr...)
mu.Unlock()
}(start, end)
}

View File

@@ -7,8 +7,8 @@ import (
"time"
"be.ems/src/framework/logger"
"be.ems/src/framework/telnet"
"be.ems/src/framework/utils/ssh"
"be.ems/src/framework/utils/telnet"
"be.ems/src/framework/vo/result"
"be.ems/src/modules/ws/model"
"be.ems/src/modules/ws/processor"
@@ -234,9 +234,8 @@ func (s *WSReceiveImpl) Telnet(client *model.WSClient, reqMsg model.WSRequest) {
}
err = json.Unmarshal(msgByte, &data)
if err == nil {
telnetClientSession := client.ChildConn.(*telnet.TelnetClientSession)
err = telnetClientSession.WindowChange(data.Rows, data.Cols)
_ = telnetClientSession.Read()
// telnetClientSession := client.ChildConn.(*telnet.TelnetClientSession)
// _ = telnetClientSession.WindowChange(data.Rows, data.Cols)
}
default:
err = fmt.Errorf("message type %s not supported", reqMsg.Type)