1
0

feat: 合并代码

This commit is contained in:
TsMask
2024-01-12 15:54:24 +08:00
parent 3cec6077d3
commit 94392e13fd
52 changed files with 1962 additions and 191 deletions

View File

@@ -8,6 +8,7 @@ import (
"ems.agt/src/framework/errorcatch"
"ems.agt/src/framework/middleware"
"ems.agt/src/framework/middleware/security"
"ems.agt/src/modules/chart"
"ems.agt/src/modules/common"
"ems.agt/src/modules/crontask"
"ems.agt/src/modules/monitor"
@@ -120,6 +121,8 @@ func initModulesRoute(app *gin.Engine) {
networkelement.Setup(app)
// 跟踪模块
trace.Setup(app)
// 图表模块
chart.Setup(app)
// 调度任务模块--暂无接口
crontask.Setup(app)
// 监控模块 - 含调度处理加入队列,放最后

View File

@@ -1,7 +1,7 @@
# 项目信息
framework:
name: "CN EMS"
version: "2.2312.10"
version: "2.2401.1"
# 应用服务配置
server:

View File

@@ -11,6 +11,7 @@ import (
type localeItem struct {
Key string `json:"key"`
Value string `json:"value"`
Code string `json:"code"`
}
// localeMap 国际化数据组
@@ -30,30 +31,43 @@ func LoadLocaleData(language string) []localeItem {
localeData = append(localeData, localeItem{
Key: v.DictLabel,
Value: v.DictValue,
Code: v.DictCode,
})
}
localeMap[language] = localeData
return localeData
}
// ValueKey 值转换键
func ValueKey(language, value string) string {
key := value
if value == "" {
return key
}
// UpdateKeyValue 更新键对应的值
func UpdateKeyValue(language, key, value string) bool {
arr, ok := localeMap[language]
if !ok || len(arr) == 0 {
arr = LoadLocaleData(language)
}
code := ""
if key == "" {
return false
}
for _, v := range arr {
if v.Value == value {
key = v.Key
if v.Key == key {
code = v.Code
break
}
}
return key
// 更新字典数据
sysDictDataService := systemService.NewSysDictDataImpl
item := sysDictDataService.SelectDictDataByCode(code)
if item.DictCode == code && item.DictLabel == key {
item.DictValue = value
row := sysDictDataService.UpdateDictData(item)
if row > 0 {
delete(localeMap, language)
return true
}
}
return false
}
// TKey 翻译键

View File

@@ -14,7 +14,7 @@ import (
)
/**无Token可访问白名单 */
var URL_WHITE_LIST = []string{"/performanceManagement", "/faultManagement", "/systemState"}
var URL_WHITE_LIST = []string{"/performanceManagement", "/faultManagement", "/systemState", "/omcNeConfig"}
// PreAuthorize 用户身份授权认证校验
//

View File

@@ -15,13 +15,13 @@ import (
)
// Get 发送 GET 请求
// timeout 超时时间(秒)
func Get(url string, headers map[string]string, timeout uint8) ([]byte, error) {
if timeout < 1 || timeout > 180 {
timeout = 1
// timeout 超时时间(秒)
func Get(url string, headers map[string]string, timeout int) ([]byte, error) {
if timeout < 100 || timeout > 180_000 {
timeout = 100
}
client := &http.Client{
Timeout: time.Duration(timeout) * time.Second, // 设置超时时间为 5 秒
Timeout: time.Duration(timeout) * time.Millisecond, // 超时时间
}
req, err := http.NewRequest("GET", url, nil)

View File

@@ -0,0 +1,238 @@
package ip2region
import (
"encoding/binary"
"fmt"
"os"
)
const (
HeaderInfoLength = 256
VectorIndexRows = 256
VectorIndexCols = 256
VectorIndexSize = 8
SegmentIndexBlockSize = 14
)
// --- Index policy define
type IndexPolicy int
const (
VectorIndexPolicy IndexPolicy = 1
BTreeIndexPolicy IndexPolicy = 2
)
func (i IndexPolicy) String() string {
switch i {
case VectorIndexPolicy:
return "VectorIndex"
case BTreeIndexPolicy:
return "BtreeIndex"
default:
return "unknown"
}
}
// --- Header define
type Header struct {
// data []byte
Version uint16
IndexPolicy IndexPolicy
CreatedAt uint32
StartIndexPtr uint32
EndIndexPtr uint32
}
func NewHeader(input []byte) (*Header, error) {
if len(input) < 16 {
return nil, fmt.Errorf("invalid input buffer")
}
return &Header{
Version: binary.LittleEndian.Uint16(input),
IndexPolicy: IndexPolicy(binary.LittleEndian.Uint16(input[2:])),
CreatedAt: binary.LittleEndian.Uint32(input[4:]),
StartIndexPtr: binary.LittleEndian.Uint32(input[8:]),
EndIndexPtr: binary.LittleEndian.Uint32(input[12:]),
}, nil
}
// --- searcher implementation
type Searcher struct {
handle *os.File
ioCount int
// use it only when this feature enabled.
// Preload the vector index will reduce the number of IO operations
// thus speedup the search process
vectorIndex []byte
// content buffer.
// running with the whole xdb file cached
contentBuff []byte
}
func baseNew(dbFile string, vIndex []byte, cBuff []byte) (*Searcher, error) {
var err error
// content buff first
if cBuff != nil {
return &Searcher{
vectorIndex: nil,
contentBuff: cBuff,
}, nil
}
// open the xdb binary file
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, err
}
return &Searcher{
handle: handle,
vectorIndex: vIndex,
}, nil
}
func NewWithFileOnly(dbFile string) (*Searcher, error) {
return baseNew(dbFile, nil, nil)
}
func NewWithVectorIndex(dbFile string, vIndex []byte) (*Searcher, error) {
return baseNew(dbFile, vIndex, nil)
}
func NewWithBuffer(cBuff []byte) (*Searcher, error) {
return baseNew("", nil, cBuff)
}
func (s *Searcher) Close() {
if s.handle != nil {
err := s.handle.Close()
if err != nil {
return
}
}
}
// GetIOCount return the global io count for the last search
func (s *Searcher) GetIOCount() int {
return s.ioCount
}
// SearchByStr find the region for the specified ip string
func (s *Searcher) SearchByStr(str string) (string, error) {
ip, err := CheckIP(str)
if err != nil {
return "", err
}
return s.Search(ip)
}
// Search find the region for the specified long ip
func (s *Searcher) Search(ip uint32) (string, error) {
// reset the global ioCount
s.ioCount = 0
// locate the segment index block based on the vector index
var il0 = (ip >> 24) & 0xFF
var il1 = (ip >> 16) & 0xFF
var idx = il0*VectorIndexCols*VectorIndexSize + il1*VectorIndexSize
var sPtr, ePtr = uint32(0), uint32(0)
if s.vectorIndex != nil {
sPtr = binary.LittleEndian.Uint32(s.vectorIndex[idx:])
ePtr = binary.LittleEndian.Uint32(s.vectorIndex[idx+4:])
} else if s.contentBuff != nil {
sPtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx:])
ePtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx+4:])
} else {
// read the vector index block
var buff = make([]byte, VectorIndexSize)
err := s.read(int64(HeaderInfoLength+idx), buff)
if err != nil {
return "", fmt.Errorf("read vector index block at %d: %w", HeaderInfoLength+idx, err)
}
sPtr = binary.LittleEndian.Uint32(buff)
ePtr = binary.LittleEndian.Uint32(buff[4:])
}
// fmt.Printf("sPtr=%d, ePtr=%d", sPtr, ePtr)
// binary search the segment index to get the region
var dataLen, dataPtr = 0, uint32(0)
var buff = make([]byte, SegmentIndexBlockSize)
var l, h = 0, int((ePtr - sPtr) / SegmentIndexBlockSize)
for l <= h {
m := (l + h) >> 1
p := sPtr + uint32(m*SegmentIndexBlockSize)
err := s.read(int64(p), buff)
if err != nil {
return "", fmt.Errorf("read segment index at %d: %w", p, err)
}
// decode the data step by step to reduce the unnecessary operations
sip := binary.LittleEndian.Uint32(buff)
if ip < sip {
h = m - 1
} else {
eip := binary.LittleEndian.Uint32(buff[4:])
if ip > eip {
l = m + 1
} else {
dataLen = int(binary.LittleEndian.Uint16(buff[8:]))
dataPtr = binary.LittleEndian.Uint32(buff[10:])
break
}
}
}
//fmt.Printf("dataLen: %d, dataPtr: %d", dataLen, dataPtr)
if dataLen == 0 {
return "", nil
}
// load and return the region data
var regionBuff = make([]byte, dataLen)
err := s.read(int64(dataPtr), regionBuff)
if err != nil {
return "", fmt.Errorf("read region at %d: %w", dataPtr, err)
}
return string(regionBuff), nil
}
// do the data read operation based on the setting.
// content buffer first or will read from the file.
// this operation will invoke the Seek for file based read.
func (s *Searcher) read(offset int64, buff []byte) error {
if s.contentBuff != nil {
cLen := copy(buff, s.contentBuff[offset:])
if cLen != len(buff) {
return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
} else {
_, err := s.handle.Seek(offset, 0)
if err != nil {
return fmt.Errorf("seek to %d: %w", offset, err)
}
s.ioCount++
rLen, err := s.handle.Read(buff)
if err != nil {
return fmt.Errorf("handle read: %w", err)
}
if rLen != len(buff) {
return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
}
return nil
}

View File

@@ -1,12 +1,59 @@
package ip2region
import (
"embed"
"strings"
"time"
"ems.agt/src/framework/logger"
)
// 网络地址(内网)
const LOCAT_HOST = "127.0.0.1"
// 全局查询对象
var searcher *Searcher
//go:embed ip2region.xdb
var ip2regionDB embed.FS
func init() {
// 从 dbPath 加载整个 xdb 到内存
buf, err := ip2regionDB.ReadFile("ip2region.xdb")
if err != nil {
logger.Fatalf("failed error load xdb from : %s\n", err)
return
}
// 用全局的 cBuff 创建完全基于内存的查询对象。
base, err := NewWithBuffer(buf)
if err != nil {
logger.Errorf("failed error create searcher with content: %s\n", err)
return
}
// 赋值到全局查询对象
searcher = base
}
// RegionSearchByIp 查询IP所在地
//
// 国家|区域|省份|城市|ISP
func RegionSearchByIp(ip string) (string, int, int64) {
ip = ClientIP(ip)
if ip == LOCAT_HOST {
// "0|0|0|内网IP|内网IP"
return "0|0|0|app.common.noIPregion|app.common.noIPregion", 0, 0
}
tStart := time.Now()
region, err := searcher.SearchByStr(ip)
if err != nil {
logger.Errorf("failed to SearchIP(%s): %s\n", ip, err)
return "0|0|0|0|0", 0, 0
}
return region, 0, time.Since(tStart).Milliseconds()
}
// RealAddressByIp 地址IP所在地
//
// 218.4.167.70 江苏省 苏州市
@@ -15,7 +62,21 @@ func RealAddressByIp(ip string) string {
if ip == LOCAT_HOST {
return "app.common.noIPregion" // 内网IP
}
return "app.common.noIPregion" // 内网IP
region, err := searcher.SearchByStr(ip)
if err != nil {
logger.Errorf("failed to SearchIP(%s): %s\n", ip, err)
return "app.common.unknown" // 未知
}
parts := strings.Split(region, "|")
province := parts[2]
city := parts[3]
if province == "0" && city != "0" {
if city == "内网IP" {
return "app.common.noIPregion" // 内网IP
}
return city
}
return province + " " + city
}
// ClientIP 处理客户端IP地址显示iPv4

Binary file not shown.

View File

@@ -0,0 +1,175 @@
package ip2region
import (
"fmt"
"os"
"strconv"
"strings"
)
var shiftIndex = []int{24, 16, 8, 0}
func CheckIP(ip string) (uint32, error) {
var ps = strings.Split(strings.TrimSpace(ip), ".")
if len(ps) != 4 {
return 0, fmt.Errorf("invalid ip address `%s`", ip)
}
var val = uint32(0)
for i, s := range ps {
d, err := strconv.Atoi(s)
if err != nil {
return 0, fmt.Errorf("the %dth part `%s` is not an integer", i, s)
}
if d < 0 || d > 255 {
return 0, fmt.Errorf("the %dth part `%s` should be an integer bettween 0 and 255", i, s)
}
val |= uint32(d) << shiftIndex[i]
}
// convert the ip to integer
return val, nil
}
func Long2IP(ip uint32) string {
return fmt.Sprintf("%d.%d.%d.%d", (ip>>24)&0xFF, (ip>>16)&0xFF, (ip>>8)&0xFF, ip&0xFF)
}
func MidIP(sip uint32, eip uint32) uint32 {
return uint32((uint64(sip) + uint64(eip)) >> 1)
}
// LoadHeader load the header info from the specified handle
func LoadHeader(handle *os.File) (*Header, error) {
_, err := handle.Seek(0, 0)
if err != nil {
return nil, fmt.Errorf("seek to the header: %w", err)
}
var buff = make([]byte, HeaderInfoLength)
rLen, err := handle.Read(buff)
if err != nil {
return nil, err
}
if rLen != len(buff) {
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
return NewHeader(buff)
}
// LoadHeaderFromFile load header info from the specified db file path
func LoadHeaderFromFile(dbFile string) (*Header, error) {
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
}
defer func(handle *os.File) {
_ = handle.Close()
}(handle)
header, err := LoadHeader(handle)
if err != nil {
return nil, err
}
return header, nil
}
// LoadHeaderFromBuff wrap the header info from the content buffer
func LoadHeaderFromBuff(cBuff []byte) (*Header, error) {
return NewHeader(cBuff[0:256])
}
// LoadVectorIndex util function to load the vector index from the specified file handle
func LoadVectorIndex(handle *os.File) ([]byte, error) {
// load all the vector index block
_, err := handle.Seek(HeaderInfoLength, 0)
if err != nil {
return nil, fmt.Errorf("seek to vector index: %w", err)
}
var buff = make([]byte, VectorIndexRows*VectorIndexCols*VectorIndexSize)
rLen, err := handle.Read(buff)
if err != nil {
return nil, err
}
if rLen != len(buff) {
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
return buff, nil
}
// LoadVectorIndexFromFile load vector index from a specified file path
func LoadVectorIndexFromFile(dbFile string) ([]byte, error) {
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
}
defer func() {
_ = handle.Close()
}()
vIndex, err := LoadVectorIndex(handle)
if err != nil {
return nil, err
}
return vIndex, nil
}
// LoadContent load the whole xdb content from the specified file handle
func LoadContent(handle *os.File) ([]byte, error) {
// get file size
fi, err := handle.Stat()
if err != nil {
return nil, fmt.Errorf("stat: %w", err)
}
size := fi.Size()
// seek to the head of the file
_, err = handle.Seek(0, 0)
if err != nil {
return nil, fmt.Errorf("seek to get xdb file length: %w", err)
}
var buff = make([]byte, size)
rLen, err := handle.Read(buff)
if err != nil {
return nil, err
}
if rLen != len(buff) {
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
return buff, nil
}
// LoadContentFromFile load the whole xdb content from the specified db file path
func LoadContentFromFile(dbFile string) ([]byte, error) {
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
}
defer func() {
_ = handle.Close()
}()
cBuff, err := LoadContent(handle)
if err != nil {
return nil, err
}
return cBuff, nil
}

View File

@@ -0,0 +1,40 @@
package chart
import (
"ems.agt/src/framework/logger"
"ems.agt/src/framework/middleware"
"ems.agt/src/framework/middleware/collectlogs"
"ems.agt/src/modules/chart/controller"
"github.com/gin-gonic/gin"
)
// 模块路由注册
func Setup(router *gin.Engine) {
logger.Infof("开始加载 ====> chart 模块路由")
chartGroup := router.Group("/chart")
// 关系图
chartGraphGroup := chartGroup.Group("/graph")
{
chartGraphGroup.GET("",
middleware.PreAuthorize(nil),
controller.NewChartGraph.Load,
)
chartGraphGroup.GET("/groups",
middleware.PreAuthorize(nil),
controller.NewChartGraph.GroupNames,
)
chartGraphGroup.POST("",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.chartGraph", collectlogs.BUSINESS_TYPE_UPDATE)),
controller.NewChartGraph.Save,
)
chartGraphGroup.DELETE("/:group",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.chartGraph", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewChartGraph.Delete,
)
}
}

View File

@@ -0,0 +1,100 @@
package controller
import (
"ems.agt/src/framework/i18n"
"ems.agt/src/framework/utils/ctx"
"ems.agt/src/framework/vo/result"
chartService "ems.agt/src/modules/chart/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
// 实例化控制层 ChartGraphController 结构体
var NewChartGraph = &ChartGraphController{
chartGraphService: chartService.NewChartGraphImpl,
}
// G6关系图
//
// PATH /graph
type ChartGraphController struct {
// G6关系图数据表服务
chartGraphService chartService.IChartGraph
}
// 获取关系图组名
//
// GET /groups
func (s *ChartGraphController) GroupNames(c *gin.Context) {
data := s.chartGraphService.SelectGroup()
c.JSON(200, result.OkData(data))
}
// 获取关系图数据
//
// GET /
func (s *ChartGraphController) Load(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var querys struct {
Group string `form:"group" binding:"required"`
Type string `form:"type" binding:"omitempty,oneof=node edge combo"`
}
if err := c.ShouldBindQuery(&querys); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
data := s.chartGraphService.LoadData(querys.Group, querys.Type)
c.JSON(200, result.OkData(data))
}
// 保存关系图数据
//
// POST /
func (s *ChartGraphController) Save(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var body struct {
Group string `json:"group" binding:"required"`
Data struct {
Nodes []map[string]any `json:"nodes" binding:"required"`
Edges []map[string]any `json:"edges" binding:"required"`
Combos []map[string]any `json:"combos" binding:"required"`
} `json:"data" binding:"required"`
}
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
data := map[string]any{
"nodes": body.Data.Nodes,
"edges": body.Data.Edges,
"combos": body.Data.Combos,
}
saveNum := s.chartGraphService.SaveData(body.Group, data)
if saveNum > 0 {
c.JSON(200, result.Ok(nil))
return
}
c.JSON(200, result.Err(nil))
}
// 删除关系图数据
//
// DELETE /:group
func (s *ChartGraphController) Delete(c *gin.Context) {
language := ctx.AcceptLanguage(c)
group := c.Param("group")
if group == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
deleteNum := s.chartGraphService.DeleteGroup(group)
if deleteNum > 0 {
c.JSON(200, result.Ok(nil))
return
}
c.JSON(200, result.Err(nil))
}

View File

@@ -0,0 +1,31 @@
package model
// ChartGraph G6关系图数据对象 chart_graph
type ChartGraph struct {
RowID int64 `json:"rowId,omitempty" gorm:"column:row_id;primaryKey;autoIncrement"` // 记录ID
RowType string `json:"rowType,omitempty" gorm:"column:row_type"` // 记录类型(node/edge/combo)
RowGroup string `json:"rowGroup,omitempty" gorm:"column:row_group"` // 记录组名
ID string `json:"id,omitempty" gorm:"column:id"` // 元素ID
Type string `json:"type,omitempty" gorm:"column:type"` // node/combo 类型
Depth int `json:"depth,omitempty" gorm:"column:depth"` // node/combo 深度
X float64 `json:"x,omitempty" gorm:"column:x"` // node/combo 横向坐标
Y float64 `json:"y,omitempty" gorm:"column:y"` // node/combo 纵向坐标
Size string `json:"size,omitempty" gorm:"column:size"` // node/combo 大小-JSON数组
Icon string `json:"icon,omitempty" gorm:"column:icon"` // node-部分类型支持图标JSON配置
Img string `json:"img,omitempty" gorm:"column:img"` // node-img 图片
ClipCfg string `json:"clipCfg,omitempty" gorm:"column:clip_cfg"` // node-img 图片裁剪JSON配置
Direction string `json:"direction,omitempty" gorm:"column:direction"` // node-triangle 三角形的方向(up/down/left/right)
Source string `json:"source,omitempty" gorm:"column:source"` // edge-边起始
Target string `json:"target,omitempty" gorm:"column:target"` // edge-边目标
ComboID string `json:"combo_id,omitempty" gorm:"column:combo_id"` // combo-分组
Padding string `json:"padding,omitempty" gorm:"column:padding"` // combo-JSON分组内边距
ParentID string `json:"parentId,omitempty" gorm:"column:parent_id"` // combo-父级分组
Children string `json:"children,omitempty" gorm:"column:children"` // combo-JSON分组内含元素
Style string `json:"style,omitempty" gorm:"column:style"` // 元素样式-JONS配置
Label string `json:"label,omitempty" gorm:"column:label"` // 标签文本
LabelCfg string `json:"labelCfg,omitempty" gorm:"column:label_cfg"` // 标签文本-JSON配置
}
func (ChartGraph) TableName() string {
return "chart_graph"
}

View File

@@ -0,0 +1,21 @@
package repository
import "ems.agt/src/modules/chart/model"
// G6关系图数据 数据层接口
type IChartGraph interface {
// SelectPage 根据条件分页查询字典类型
SelectPage(query map[string]any) map[string]any
// SelectList 根据实体查询
SelectList(graph model.ChartGraph) []model.ChartGraph
// SelectGroup 查询组名
SelectGroup() []string
// Insert 批量添加
Inserts(graphs []model.ChartGraph) int64
// Delete 删除组数据
DeleteGroup(rowGroup string) int64
}

View File

@@ -0,0 +1,194 @@
package repository
import (
"strings"
"ems.agt/src/framework/datasource"
"ems.agt/src/framework/logger"
"ems.agt/src/framework/utils/parse"
"ems.agt/src/framework/utils/repo"
"ems.agt/src/modules/chart/model"
)
// 实例化数据层 NewChartGraphImpl 结构体
var NewChartGraphImpl = &ChartGraphImpl{
selectSql: `select
row_id, row_type, row_group,
id, type, depth, x, y, size, icon, img,
clip_cfg, direction,
source, target, combo_id,
padding, parent_id, children,
style, label, label_cfg
from chart_graph`,
resultMap: map[string]string{
"row_id": "RowID",
"row_type": "RowType",
"row_group": "RowGroup",
"id": "ID",
"type": "Type",
"depth": "Depth",
"x": "X",
"y": "Y",
"size": "Size",
"icon": "Icon",
"img": "Img",
"clip_cfg": "ClipCfg",
"direction": "Direction",
"source": "Source",
"target": "Target",
"combo_id": "ComboID",
"padding": "Padding",
"parent_id": "ParentID",
"children": "Children",
"style": "Style",
"label": "Label",
"label_cfg": "LabelCfg",
},
}
// ChartGraphImpl G6关系图数据表 数据层处理
type ChartGraphImpl struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
resultMap map[string]string
}
// convertResultRows 将结果记录转实体结果组
func (r *ChartGraphImpl) convertResultRows(rows []map[string]any) []model.ChartGraph {
arr := make([]model.ChartGraph, 0)
for _, row := range rows {
item := model.ChartGraph{}
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 *ChartGraphImpl) SelectPage(query map[string]any) map[string]any {
// 查询条件拼接
var conditions []string
var params []any
if v, ok := query["rowType"]; ok && v != "" {
conditions = append(conditions, "row_type = ?")
params = append(params, strings.Trim(v.(string), " "))
}
if v, ok := query["rowGroup"]; ok && v != "" {
conditions = append(conditions, "row_group = ?")
params = append(params, strings.Trim(v.(string), " "))
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
result := map[string]any{
"total": 0,
"rows": []model.ChartGraph{},
}
// 查询数量 长度为0直接返回
totalSql := "select count(1) as 'total' from chart_graph"
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)
return result
}
// 转换实体
result["rows"] = r.convertResultRows(results)
return result
}
// SelectList 根据实体查询
func (r *ChartGraphImpl) SelectList(graph model.ChartGraph) []model.ChartGraph {
// 查询条件拼接
var conditions []string
var params []any
if graph.RowType != "" {
conditions = append(conditions, "row_type = ?")
params = append(params, graph.RowType)
}
if graph.RowGroup != "" {
conditions = append(conditions, "row_group = ?")
params = append(params, graph.RowGroup)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
// 查询数据
querySql := r.selectSql + whereSql + " order by depth asc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
return r.convertResultRows(results)
}
// SelectGroup 查询组名
func (r *ChartGraphImpl) SelectGroup() []string {
rows := []string{}
// 查询数量 长度为0直接返回
querySql := "select row_group as 'str' from chart_graph GROUP BY row_group"
strRows, err := datasource.RawDB("", querySql, nil)
if err != nil {
logger.Errorf("Query err => %v", err)
return rows
}
for _, v := range strRows {
rows = append(rows, v["str"].(string))
}
return rows
}
// Insert 批量添加
func (r *ChartGraphImpl) Inserts(graphs []model.ChartGraph) int64 {
tx := datasource.DefaultDB().CreateInBatches(graphs, 2000)
if err := tx.Error; err != nil {
logger.Errorf("CreateInBatches err => %v", err)
}
return tx.RowsAffected
}
// Delete 删除组数据
func (r *ChartGraphImpl) DeleteGroup(rowGroup string) int64 {
tx := datasource.DefaultDB().Where("row_group = ?", rowGroup).Delete(&model.ChartGraph{})
if err := tx.Error; err != nil {
logger.Errorf("Delete err => %v", err)
}
return tx.RowsAffected
}

View File

@@ -0,0 +1,16 @@
package service
// G6关系图数据 服务层接口
type IChartGraph interface {
// SelectGroup 查询组名
SelectGroup() []string
// LoadData 查询所组图数据
LoadData(rowGroup, rowType string) map[string]any
// SaveData 添加组图数据
SaveData(rowGroup string, data map[string]any) int64
// DeleteGroup 删除所组图数据
DeleteGroup(rowGroup string) int64
}

View File

@@ -0,0 +1,359 @@
package service
import (
"strings"
"ems.agt/src/framework/utils/parse"
"ems.agt/src/modules/chart/model"
chartRepository "ems.agt/src/modules/chart/repository"
"github.com/goccy/go-json"
)
// 实例化服务层 ChartGraphImpl 结构体
var NewChartGraphImpl = &ChartGraphImpl{
graphRepository: chartRepository.NewChartGraphImpl,
}
// ChartGraphImpl G6关系图数据表 服务层处理
type ChartGraphImpl struct {
// G6关系图数据服务
graphRepository chartRepository.IChartGraph
}
// SelectGroup 查询组名
func (s *ChartGraphImpl) SelectGroup() []string {
return s.graphRepository.SelectGroup()
}
// LoadData 查询所组图数据
func (s *ChartGraphImpl) LoadData(rowGroup, rowType string) map[string]any {
// 查询数据
graph := model.ChartGraph{
RowGroup: rowGroup,
}
if rowType != "" {
graph.RowType = rowType
}
data := s.graphRepository.SelectList(graph)
// 数据项
nodes := []map[string]any{}
edges := []map[string]any{}
combos := []map[string]any{}
for _, v := range data {
if v.RowType == "node" {
nodes = append(nodes, s.loadNode(v))
}
if v.RowType == "edge" {
edges = append(edges, s.loadEdge(v))
}
if v.RowType == "combo" {
combos = append(combos, s.loadCombo(v))
}
}
return map[string]any{
"nodes": nodes,
"edges": edges,
"combos": combos,
}
}
// loadNode 图数据Node
func (s *ChartGraphImpl) loadNode(v model.ChartGraph) map[string]any {
node := map[string]any{
"id": v.ID,
"comboId": v.ComboID,
"x": v.X,
"y": v.Y,
"type": v.Type,
"depth": v.Depth,
}
// 元素样式
style := map[string]any{}
if len(v.Style) > 7 {
json.Unmarshal([]byte(v.Style), &style)
}
node["style"] = style
// 元素大小
if strings.Contains(v.Size, "[") {
sizeArr := []int64{}
json.Unmarshal([]byte(v.Size), &sizeArr)
node["size"] = sizeArr
} else {
node["size"] = parse.Number(v.Size)
}
// 标签文本
node["label"] = v.Label
labelCfg := map[string]any{}
if len(v.LabelCfg) > 7 {
json.Unmarshal([]byte(v.LabelCfg), &labelCfg)
}
node["labelCfg"] = labelCfg
// 三角形属性
if v.Type == "triangle" {
node["direction"] = v.Direction
}
// 图片属性
if strings.Index(v.Type, "image") == 0 {
node["img"] = v.Img
clipCfg := map[string]any{}
if len(v.ClipCfg) > 7 {
json.Unmarshal([]byte(v.ClipCfg), &clipCfg)
}
node["clipCfg"] = clipCfg
}
// 图标属性
if v.Icon != "" {
icon := map[string]any{}
if len(v.Icon) > 7 {
json.Unmarshal([]byte(v.Icon), &icon)
}
node["icon"] = icon
}
return node
}
// loadEdge 图数据Edge
func (s *ChartGraphImpl) loadEdge(v model.ChartGraph) map[string]any {
edge := map[string]any{
"id": v.ID,
"source": v.Source,
"target": v.Target,
"type": v.Type,
}
// 元素样式
style := map[string]any{}
if len(v.Style) > 7 {
json.Unmarshal([]byte(v.Style), &style)
}
edge["style"] = style
// 标签文本
edge["label"] = v.Label
labelCfg := map[string]any{}
if len(v.LabelCfg) > 7 {
json.Unmarshal([]byte(v.LabelCfg), &labelCfg)
}
edge["labelCfg"] = labelCfg
return edge
}
// loadCombo 图数据Combo
func (s *ChartGraphImpl) loadCombo(v model.ChartGraph) map[string]any {
combo := map[string]any{
"id": v.ID,
"x": v.X,
"y": v.Y,
"type": v.Type,
"depth": v.Depth,
}
// 元素样式
style := map[string]any{}
if len(v.Style) > 7 {
json.Unmarshal([]byte(v.Style), &style)
}
combo["style"] = style
// 元素大小
if strings.Contains(v.Size, "[") {
sizeArr := []int64{}
json.Unmarshal([]byte(v.Size), &sizeArr)
combo["size"] = sizeArr
} else {
combo["size"] = parse.Number(v.Size)
}
// 元素内边距
if strings.Contains(v.Padding, "[") {
paddingArr := []int64{}
json.Unmarshal([]byte(v.Padding), &paddingArr)
combo["padding"] = paddingArr
} else {
combo["padding"] = parse.Number(v.Padding)
}
// 标签文本
combo["label"] = v.Label
labelCfg := map[string]any{}
if len(v.LabelCfg) > 7 {
json.Unmarshal([]byte(v.LabelCfg), &labelCfg)
}
combo["labelCfg"] = labelCfg
// 分组内元素
if v.Children != "" {
children := []map[string]any{}
if len(v.Children) > 7 {
json.Unmarshal([]byte(v.Children), &children)
}
combo["children"] = children
}
return combo
}
// SaveData 添加组图数据
func (s *ChartGraphImpl) SaveData(rowGroup string, data map[string]any) int64 {
graphs := []model.ChartGraph{}
nodes := data["nodes"].([]map[string]any)
graphNodes := s.saveNode(rowGroup, nodes)
graphs = append(graphs, graphNodes...)
edges := data["edges"].([]map[string]any)
graphEdges := s.saveEdge(rowGroup, edges)
graphs = append(graphs, graphEdges...)
combos := data["combos"].([]map[string]any)
graphCombos := s.saveCombo(rowGroup, combos)
graphs = append(graphs, graphCombos...)
// 删除组数据后插入
if len(graphs) > 0 {
s.graphRepository.DeleteGroup(rowGroup)
}
return s.graphRepository.Inserts(graphs)
}
// saveNode 图数据Node
func (s *ChartGraphImpl) saveNode(rowGroup string, nodes []map[string]any) []model.ChartGraph {
var graphs []model.ChartGraph
for _, v := range nodes {
node := model.ChartGraph{
RowType: "node",
RowGroup: rowGroup,
ID: v["id"].(string),
X: v["x"].(float64),
Y: v["y"].(float64),
Type: v["type"].(string),
}
if comboId, ok := v["comboId"]; ok && comboId != nil {
node.ComboID = comboId.(string)
}
if depth, ok := v["depth"]; ok && depth != nil {
node.Depth = int(depth.(float64))
}
if styleByte, err := json.Marshal(v["style"]); err == nil {
node.Style = string(styleByte)
}
// 元素大小
if sizeByte, err := json.Marshal(v["size"]); err == nil {
node.Size = string(sizeByte)
}
// 标签文本
if label, ok := v["label"]; ok && label != nil {
node.Label = label.(string)
}
if labelCfgByte, err := json.Marshal(v["labelCfg"]); err == nil {
node.LabelCfg = string(labelCfgByte)
}
// 三角形属性
if direction, ok := v["direction"]; ok && direction != nil && node.Type == "triangle" {
node.Direction = direction.(string)
}
// 图片属性
if img, ok := v["img"]; ok && img != nil {
node.Img = img.(string)
if clipCfgByte, err := json.Marshal(v["clipCfg"]); err == nil {
node.ClipCfg = string(clipCfgByte)
}
}
// 图标属性
if icon, ok := v["icon"]; ok && icon != nil {
if iconByte, err := json.Marshal(icon); err == nil {
node.Icon = string(iconByte)
}
}
graphs = append(graphs, node)
}
return graphs
}
// saveEdge 图数据Edge
func (s *ChartGraphImpl) saveEdge(rowGroup string, edges []map[string]any) []model.ChartGraph {
var graphs []model.ChartGraph
for _, v := range edges {
edge := model.ChartGraph{
RowType: "edge",
RowGroup: rowGroup,
ID: v["id"].(string),
Source: v["source"].(string),
Target: v["target"].(string),
Type: v["type"].(string),
}
if styleByte, err := json.Marshal(v["style"]); err == nil {
edge.Style = string(styleByte)
}
// 标签文本
if label, ok := v["label"]; ok && label != nil {
edge.Label = label.(string)
}
if labelCfgByte, err := json.Marshal(v["labelCfg"]); err == nil {
edge.LabelCfg = string(labelCfgByte)
}
graphs = append(graphs, edge)
}
return graphs
}
// saveCombo 图数据Combo
func (s *ChartGraphImpl) saveCombo(rowGroup string, combos []map[string]any) []model.ChartGraph {
var graphs []model.ChartGraph
for _, v := range combos {
combo := model.ChartGraph{
RowType: "combo",
RowGroup: rowGroup,
ID: v["id"].(string),
X: v["x"].(float64),
Y: v["y"].(float64),
Type: v["type"].(string),
}
if depth, ok := v["depth"]; ok && depth != nil {
combo.Depth = int(depth.(float64))
}
if styleByte, err := json.Marshal(v["style"]); err == nil {
combo.Style = string(styleByte)
}
if paddingByte, err := json.Marshal(v["padding"]); err == nil {
combo.Padding = string(paddingByte)
}
if childrenByte, err := json.Marshal(v["children"]); err == nil {
combo.Children = string(childrenByte)
}
// 元素大小
if sizeByte, err := json.Marshal(v["size"]); err == nil {
combo.Size = string(sizeByte)
}
// 标签文本
if label, ok := v["label"]; ok && label != nil {
combo.Label = label.(string)
}
if labelCfgByte, err := json.Marshal(v["labelCfg"]); err == nil {
combo.LabelCfg = string(labelCfgByte)
}
graphs = append(graphs, combo)
}
return graphs
}
// Delete 删除所组图数据
func (s *ChartGraphImpl) DeleteGroup(rowGroup string) int64 {
return s.graphRepository.DeleteGroup(rowGroup)
}

View File

@@ -92,7 +92,7 @@ func (s *BarProcessor) Execute(data any) (any, error) {
var alarmDefine BarParams
err = json.Unmarshal([]byte(sysJob.TargetParams), &alarmDefine)
if err == nil {
if err != nil {
log.Error("Failed to Unmarshal:", err)
return nil, err
}

View File

@@ -10,6 +10,7 @@ import (
"ems.agt/lib/dborm"
"ems.agt/lib/log"
"ems.agt/restagent/config"
"ems.agt/src/framework/cron"
"github.com/go-resty/resty/v2"
)
@@ -68,36 +69,22 @@ type SystemState struct {
var client = resty.New()
func init() {
/*
client.
SetTimeout(10 * time.Second).
SetRetryCount(1).
SetRetryWaitTime(1 * time.Second).
SetRetryMaxWaitTime(2 * time.Second).
SetRetryAfter(func(client *resty.Client, resp *resty.Response) (time.Duration, error) {
return 0, errors.New("quota exceeded")
})
*/
client.
SetTimeout(time.Duration(400 * time.Millisecond))
// SetRetryCount(1).
// SetRetryWaitTime(time.Duration(1 * time.Second)).
// SetRetryMaxWaitTime(time.Duration(2 * time.Second))
//client.SetTimeout(2 * time.Second)
}
func (s *BarProcessor) Execute(data any) (any, error) {
var err error
s.count++
// options := data.(cron.JobData)
// // sysJob := options.SysJob
// // var params BarParams
options := data.(cron.JobData)
sysJob := options.SysJob
var params BarParams
// // // err := json.Unmarshal([]byte(sysJob.TargetParams), &params)
// // // if err == nil {
// // // duration = params.Duration
// // // }
_ = json.Unmarshal([]byte(sysJob.TargetParams), &params)
// if err == nil {
// duration = params.Duration
// }
var nes []dborm.NeInfo
_, err = dborm.XormGetAllNeInfo(&nes)
@@ -112,7 +99,6 @@ func (s *BarProcessor) Execute(data any) (any, error) {
requestURI := fmt.Sprintf("/api/rest/systemManagement/v1/elementType/%s/objectType/systemState", strings.ToLower(ne.NeType))
requestURL := fmt.Sprintf("http://%s:%s%s", ne.Ip, ne.Port, requestURI)
log.Debug("requestURL: Get", requestURL)
client := resty.New()
response, err := client.R().
EnableTrace().
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
@@ -127,23 +113,27 @@ func (s *BarProcessor) Execute(data any) (any, error) {
log.Debug("StatusCode: ", response.StatusCode())
switch response.StatusCode() {
case http.StatusOK, http.StatusCreated, http.StatusNoContent, http.StatusAccepted:
log.Debug("response body:", string(response.Body()))
log.Trace("response body:", string(response.Body()))
state := new(SystemState)
_ = json.Unmarshal(response.Body(), &state)
var dateStr *string = nil
if state.ExpiryDate != "" {
dateStr = &state.ExpiryDate
}
neState := new(dborm.NeState)
neState.NeType = ne.NeType
neState.NeId = ne.NeId
neState.Version = state.Version
neState.Capability = state.Capability
neState.SerialNum = state.SerialNum
neState.ExpiryDate = state.ExpiryDate
neState.ExpiryDate = *dateStr
cu, _ := json.Marshal(state.CpuUsage)
neState.CpuUsage = string(cu)
mu, _ := json.Marshal(state.MemUsage)
neState.MemUsage = string(mu)
ds, _ := json.Marshal(state.DiskSpace)
neState.DiskSpace = string(ds)
log.Debug("neState:", neState)
log.Trace("neState:", neState)
_, err := dborm.XormInsertNeState(neState)
if err != nil {
log.Error("Failed to insert ne_state:", err)
@@ -152,7 +142,7 @@ func (s *BarProcessor) Execute(data any) (any, error) {
}
succNum++
default:
log.Debug("response body:", string(response.Body()))
log.Trace("response body:", string(response.Body()))
body := new(map[string]interface{})
_ = json.Unmarshal(response.Body(), &body)
failNum++

View File

@@ -165,6 +165,14 @@ func (s *SysJobController) Edit(c *gin.Context) {
}
}
// 检查是否存在
job := s.sysJobService.SelectJobById(body.JobID)
if job.JobID != body.JobID {
// 没有可访问调度任务数据!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "job.noData")))
return
}
// 检查属性值唯一
uniqueJob := s.sysJobService.CheckUniqueJobName(body.JobName, body.JobGroup, body.JobID)
if !uniqueJob {
@@ -174,6 +182,19 @@ func (s *SysJobController) Edit(c *gin.Context) {
return
}
// 多语言非原始值
i18nValue := i18n.TKey(language, job.JobName)
if i18nValue != job.JobName {
i18n.UpdateKeyValue(language, job.JobName, body.JobName)
body.JobName = job.JobName
}
// 多语言非原始值
i18nValue2 := i18n.TKey(language, job.Remark)
if i18nValue2 != job.Remark {
i18n.UpdateKeyValue(language, job.Remark, body.Remark)
body.Remark = job.Remark
}
body.UpdateBy = ctx.LoginUserToUserName(c)
rows := s.sysJobService.UpdateJob(body)
if rows > 0 {
@@ -244,8 +265,8 @@ func (s *SysJobController) Status(c *gin.Context) {
// 更新状态
job.Status = body.Status
job.UpdateBy = ctx.LoginUserToUserName(c)
ok := s.sysJobService.ChangeStatus(job)
if ok {
rows := s.sysJobService.UpdateJob(job)
if rows > 0 {
c.JSON(200, result.Ok(nil))
return
}

View File

@@ -41,6 +41,12 @@ type SysJobLogController struct {
func (s *SysJobLogController) List(c *gin.Context) {
// 查询参数转换map
querys := ctx.QueryMap(c)
// 任务ID优先级更高
if v, ok := querys["jobId"]; ok && v != nil {
jobInfo := service.NewSysJobImpl.SelectJobById(v.(string))
querys["jobName"] = jobInfo.JobName
querys["jobGroup"] = jobInfo.JobGroup
}
data := s.sysJobLogService.SelectJobLogPage(querys)
rows := data["rows"].([]model.SysJobLog)

View File

@@ -59,7 +59,7 @@ func (r *SysJobLogImpl) SelectJobLogPage(query map[string]any) map[string]any {
var conditions []string
var params []any
if v, ok := query["jobName"]; ok && v != "" {
conditions = append(conditions, "job_name like concat(?, '%')")
conditions = append(conditions, "job_name = ?")
params = append(params, v)
}
if v, ok := query["jobGroup"]; ok && v != "" {

View File

@@ -27,9 +27,6 @@ type ISysJob interface {
// DeleteJobByIds 批量删除调度任务信息
DeleteJobByIds(jobIds []string) (int64, error)
// ChangeStatus 任务调度状态修改
ChangeStatus(sysJob model.SysJob) bool
// RunQueueJob 立即运行一次调度任务
RunQueueJob(sysJob model.SysJob) bool

View File

@@ -86,7 +86,7 @@ func (r *SysJobImpl) DeleteJobByIds(jobIds []string) (int64, error) {
jobs := r.sysJobRepository.SelectJobByIds(jobIds)
if len(jobs) <= 0 {
// 没有可访问调度任务数据!
return 0, fmt.Errorf("There is no accessible scheduling task data!")
return 0, fmt.Errorf("there is no accessible scheduling task data")
}
if len(jobs) == len(jobIds) {
// 清除任务
@@ -97,30 +97,7 @@ func (r *SysJobImpl) DeleteJobByIds(jobIds []string) (int64, error) {
return rows, nil
}
// 删除调度任务信息失败!
return 0, fmt.Errorf("Failed to delete scheduling task information!")
}
// ChangeStatus 任务调度状态修改
func (r *SysJobImpl) ChangeStatus(sysJob model.SysJob) bool {
// 更新状态
newSysJob := model.SysJob{
JobID: sysJob.JobID,
Status: sysJob.Status,
UpdateBy: sysJob.UpdateBy,
}
rows := r.sysJobRepository.UpdateJob(newSysJob)
if rows > 0 {
//状态正常添加队列任务
if sysJob.Status == common.STATUS_YES {
r.insertQueueJob(sysJob, true)
}
// 状态禁用删除队列任务
if sysJob.Status == common.STATUS_NO {
r.deleteQueueJob(sysJob)
}
return true
}
return false
return 0, fmt.Errorf("failed to delete scheduling task information")
}
// ResetQueueJob 重置初始调度任务

View File

@@ -1,6 +1,8 @@
package controller
import (
"fmt"
"ems.agt/src/framework/i18n"
"ems.agt/src/framework/utils/ctx"
"ems.agt/src/framework/utils/parse"
@@ -23,6 +25,9 @@ type NeInfoController struct {
neInfoService neService.INeInfo
}
// neStateCacheMap 网元状态缓存最后一次成功的信息
var neStateCacheMap map[string]map[string]any = make(map[string]map[string]any)
// 网元状态
//
// GET /state
@@ -42,14 +47,31 @@ func (s *NeInfoController) NeState(c *gin.Context) {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
neKey := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
// 网元直连
resData, err := neService.NeState(neInfo)
if err != nil {
c.JSON(200, result.ErrMsg("connection failure"))
// 异常取上次缓存
if v, ok := neStateCacheMap[neKey]; ok && v != nil {
v["online"] = false
neStateCacheMap[neKey] = v
} else {
neStateCacheMap[neKey] = map[string]any{
"online": false,
"neId": neInfo.NeId,
"neName": neInfo.NeName,
"neType": neInfo.NeType,
"neIP": neInfo.IP,
}
}
c.JSON(200, result.OkData(neStateCacheMap[neKey]))
return
}
// 存入缓存
resData["online"] = true
neStateCacheMap[neKey] = resData
c.JSON(200, result.OkData(resData))
}

View File

@@ -187,7 +187,7 @@ func (s *UDMAuthController) Adds(c *gin.Context) {
return
}
msg := fmt.Sprintf("bad authdat:start_imsi=%s,sub_num=%s,ki=%s,amf=%s,algo=%s,opc=%s", body.Imsi, num, body.Ki, body.Amf, body.AlgoIndex, body.Opc)
msg := fmt.Sprintf("baa authdat:start_imsi=%s,sub_num=%s,ki=%s,amf=%s,algo=%s,opc=%s", body.Imsi, num, body.Ki, body.Amf, body.AlgoIndex, body.Opc)
// 发送MML
data, err := mmlclient.MMLSendMsgToString(neInfo.IP, msg)

View File

@@ -216,7 +216,7 @@ func (s *UDMSubController) Adds(c *gin.Context) {
return
}
msg := fmt.Sprintf("bad 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",
msg := 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)
// static_ip指给4G UE分配的静态IP没有可不带此字段名批量添加IP会自动递增
if body.StaticIp != "" {

View File

@@ -135,7 +135,7 @@ func (r *NeInfoImpl) SelectNeList(ne model.NeInfo) []model.NeInfo {
}
// 查询数据
querySql := r.selectSql + whereSql + " order by ne_type asc "
querySql := r.selectSql + whereSql + " order by ne_type asc, ne_id desc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)

View File

@@ -52,8 +52,14 @@ func (r *PerfKPIImpl) SelectGoldKPI(query model.GoldKPIQuery, kpiIds []string) [
"min(CASE WHEN gk.ne_name != '' THEN gk.ne_name ELSE 0 END) AS neName",
}
for _, kid := range kpiIds {
str := fmt.Sprintf("sum(CASE WHEN gk.kpi_id = '%s' THEN gk.value ELSE 0 END) AS '%s'", kid, kid)
fields = append(fields, str)
// 特殊字段只取最后一次收到的非0值
if kid == "AMF.01" || kid == "UDM.01" || kid == "UDM.02" || kid == "UDM.03" {
str := fmt.Sprintf("IFNULL(SUBSTRING_INDEX(GROUP_CONCAT( CASE WHEN gk.kpi_id = '%s' and gk.VALUE != 0 THEN gk.VALUE END ), ',', 1), 0) AS '%s'", kid, kid)
fields = append(fields, str)
} else {
str := fmt.Sprintf("sum(CASE WHEN gk.kpi_id = '%s' THEN gk.value ELSE 0 END) AS '%s'", kid, kid)
fields = append(fields, str)
}
}
fieldsSql := strings.Join(fields, ",")

View File

@@ -15,7 +15,7 @@ import (
func NeState(neInfo model.NeInfo) (map[string]any, error) {
// 网元直连
neUrl := fmt.Sprintf("http://%s:%d/api/rest/systemManagement/v1/elementType/%s/objectType/systemState", neInfo.IP, neInfo.Port, strings.ToLower(neInfo.NeType))
resBytes, err := fetch.Get(neUrl, nil, 1)
resBytes, err := fetch.Get(neUrl, nil, 200)
if err != nil {
logger.Warnf("NeState %s", err.Error())
return nil, err
@@ -33,6 +33,7 @@ func NeState(neInfo model.NeInfo) (map[string]any, error) {
"neType": neInfo.NeType,
"neId": neInfo.NeId,
"neName": neInfo.NeName,
"neIP": neInfo.IP,
"refreshTime": time.Now().UnixMilli(), // 获取时间
"version": resData["version"],
"capability": resData["capability"],

View File

@@ -128,13 +128,32 @@ func (s *SysConfigController) Edit(c *gin.Context) {
}
// 检查是否存在
config := s.sysConfigService.SelectConfigById(body.ConfigID)
if config.ConfigID != body.ConfigID {
configInfo := s.sysConfigService.SelectConfigById(body.ConfigID)
if configInfo.ConfigID != body.ConfigID {
// 没有可访问参数配置数据!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "config.noData")))
return
}
// 多语言非原始值
i18nValue := i18n.TKey(language, configInfo.ConfigName)
if i18nValue != configInfo.ConfigName {
i18n.UpdateKeyValue(language, configInfo.ConfigName, body.ConfigName)
body.ConfigName = configInfo.ConfigName
}
// 多语言非原始值
i18nValue2 := i18n.TKey(language, configInfo.ConfigValue)
if i18nValue2 != configInfo.ConfigValue {
i18n.UpdateKeyValue(language, configInfo.ConfigValue, body.ConfigValue)
body.ConfigValue = configInfo.ConfigValue
}
// 多语言非原始值
i18nValue3 := i18n.TKey(language, configInfo.Remark)
if i18nValue3 != configInfo.Remark {
i18n.UpdateKeyValue(language, configInfo.Remark, body.Remark)
body.Remark = configInfo.Remark
}
body.UpdateBy = ctx.LoginUserToUserName(c)
rows := s.sysConfigService.UpdateConfig(body)
if rows > 0 {
@@ -262,7 +281,7 @@ func (s *SysConfigController) Export(c *gin.Context) {
// 参数配置修改配置参数
//
// PUT /changeConfigValue
// PUT /changeValue
func (s *SysConfigController) ConfigValue(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var body struct {
@@ -282,13 +301,21 @@ func (s *SysConfigController) ConfigValue(c *gin.Context) {
return
}
// 与旧值相等不变更
if info.ConfigValue == body.Value {
// 与旧值相等 不变更
i18nValue := i18n.TKey(language, info.ConfigValue)
if i18nValue == body.Value {
// 变更状态与旧值相等!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "config.errValueEq")))
return
}
info.ConfigValue = body.Value
// 多语言非原始值
if i18nValue != info.ConfigValue {
i18n.UpdateKeyValue(language, info.ConfigValue, body.Value)
} else {
info.ConfigValue = body.Value
}
info.UpdateBy = ctx.LoginUserToUserName(c)
rows := s.sysConfigService.UpdateConfig(info)
if rows > 0 {

View File

@@ -201,6 +201,13 @@ func (s *SysDeptController) Edit(c *gin.Context) {
}
}
// 多语言非原始值
i18nValue := i18n.TKey(language, deptInfo.DeptName)
if i18nValue != deptInfo.DeptName {
i18n.UpdateKeyValue(language, deptInfo.DeptName, body.DeptName)
body.DeptName = deptInfo.DeptName
}
body.UpdateBy = ctx.LoginUserToUserName(c)
rows := s.sysDeptService.UpdateDept(body)
if rows > 0 {

View File

@@ -140,8 +140,8 @@ func (s *SysDictDataController) Edit(c *gin.Context) {
}
// 检查字典编码是否存在
SysDictDataController := s.sysDictDataService.SelectDictDataByCode(body.DictCode)
if SysDictDataController.DictCode != body.DictCode {
sysDictData := s.sysDictDataService.SelectDictDataByCode(body.DictCode)
if sysDictData.DictCode != body.DictCode {
// 没有可访问字典编码数据!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "dictData.noData")))
return
@@ -156,6 +156,19 @@ func (s *SysDictDataController) Edit(c *gin.Context) {
return
}
// 多语言非原始值
i18nValue := i18n.TKey(language, sysDictData.DictLabel)
if i18nValue != sysDictData.DictLabel {
i18n.UpdateKeyValue(language, sysDictData.DictLabel, body.DictLabel)
body.DictLabel = sysDictData.DictLabel
}
// 多语言非原始值
i18nValue2 := i18n.TKey(language, sysDictData.Remark)
if i18nValue2 != sysDictData.Remark {
i18n.UpdateKeyValue(language, sysDictData.Remark, body.Remark)
body.Remark = sysDictData.Remark
}
body.UpdateBy = ctx.LoginUserToUserName(c)
rows := s.sysDictDataService.UpdateDictData(body)
if rows > 0 {

View File

@@ -151,6 +151,19 @@ func (s *SysDictTypeController) Edit(c *gin.Context) {
return
}
// 多语言非原始值
i18nValue := i18n.TKey(language, dictInfo.DictName)
if i18nValue != dictInfo.DictName {
i18n.UpdateKeyValue(language, dictInfo.DictName, body.DictName)
body.DictName = dictInfo.DictName
}
// 多语言非原始值
i18nValue2 := i18n.TKey(language, dictInfo.Remark)
if i18nValue2 != dictInfo.Remark {
i18n.UpdateKeyValue(language, dictInfo.Remark, body.Remark)
body.Remark = dictInfo.Remark
}
body.UpdateBy = ctx.LoginUserToUserName(c)
rows := s.sysDictTypeService.UpdateDictType(body)
if rows > 0 {

View File

@@ -216,6 +216,19 @@ func (s *SysMenuController) Edit(c *gin.Context) {
}
}
// 多语言非原始值
i18nValue := i18n.TKey(language, menuInfo.MenuName)
if i18nValue != menuInfo.MenuName {
i18n.UpdateKeyValue(language, menuInfo.MenuName, body.MenuName)
body.MenuName = menuInfo.MenuName
}
// 多语言非原始值
i18nValue2 := i18n.TKey(language, menuInfo.Remark)
if i18nValue2 != menuInfo.Remark {
i18n.UpdateKeyValue(language, menuInfo.Remark, body.Remark)
body.Remark = menuInfo.Remark
}
body.UpdateBy = ctx.LoginUserToUserName(c)
rows := s.sysMenuService.UpdateMenu(body)
if rows > 0 {

View File

@@ -125,8 +125,8 @@ func (s *SysPostController) Edit(c *gin.Context) {
}
// 检查是否存在
post := s.sysPostService.SelectPostById(body.PostID)
if post.PostID != body.PostID {
postInfo := s.sysPostService.SelectPostById(body.PostID)
if postInfo.PostID != body.PostID {
// 没有可访问岗位数据!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "post.noData")))
return
@@ -150,6 +150,19 @@ func (s *SysPostController) Edit(c *gin.Context) {
return
}
// 多语言非原始值
i18nValue := i18n.TKey(language, postInfo.PostName)
if i18nValue != postInfo.PostName {
i18n.UpdateKeyValue(language, postInfo.PostName, body.PostName)
body.PostName = postInfo.PostName
}
// 多语言非原始值
i18nValue2 := i18n.TKey(language, postInfo.Remark)
if i18nValue2 != postInfo.Remark {
i18n.UpdateKeyValue(language, postInfo.Remark, body.Remark)
body.Remark = postInfo.Remark
}
body.UpdateBy = ctx.LoginUserToUserName(c)
rows := s.sysPostService.UpdatePost(body)
if rows > 0 {

View File

@@ -139,8 +139,8 @@ func (s *SysRoleController) Edit(c *gin.Context) {
}
// 检查是否存在
role := s.sysRoleService.SelectRoleById(body.RoleID)
if role.RoleID != body.RoleID {
roleInfo := s.sysRoleService.SelectRoleById(body.RoleID)
if roleInfo.RoleID != body.RoleID {
// 没有可访问角色数据!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "role.noData")))
return
@@ -166,6 +166,19 @@ func (s *SysRoleController) Edit(c *gin.Context) {
return
}
// 多语言非原始值
i18nValue := i18n.TKey(language, roleInfo.RoleName)
if i18nValue != roleInfo.RoleName {
i18n.UpdateKeyValue(language, roleInfo.RoleName, body.RoleName)
body.RoleName = roleInfo.RoleName
}
// 多语言非原始值
i18nValue2 := i18n.TKey(language, roleInfo.Remark)
if i18nValue2 != roleInfo.Remark {
i18n.UpdateKeyValue(language, roleInfo.Remark, body.Remark)
body.Remark = roleInfo.Remark
}
body.UpdateBy = ctx.LoginUserToUserName(c)
rows := s.sysRoleService.UpdateRole(body)
if rows > 0 {

View File

@@ -70,6 +70,10 @@ func (s *TcpdumpController) NeTask(c *gin.Context) {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
if strings.Contains(msg, "command not found") {
c.JSON(200, result.ErrMsg("Command [tcpdump] Not Found"))
return
}
c.JSON(200, result.OkData(map[string]any{
"cmd": cmdStr,
@@ -211,6 +215,14 @@ func (s *TcpdumpController) NeUPFTask(c *gin.Context) {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
if strings.Contains(msg, "command not found") {
c.JSON(200, result.ErrMsg("Command [expect] Not Found"))
return
}
if strings.Contains(msg, "Unable to connect to remote host") {
c.JSON(200, result.ErrMsg("Connection Refused"))
return
}
s := strings.Index(msg, "pcap dispatch trace:")
if s != -1 {
e := strings.Index(msg, "\r\nupfd1#")
@@ -243,6 +255,14 @@ func (s *TcpdumpController) NeUPFTask(c *gin.Context) {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
if strings.Contains(msg, "command not found") {
c.JSON(200, result.ErrMsg("Command [expect] Not Found"))
return
}
if strings.Contains(msg, "Unable to connect to remote host") {
c.JSON(200, result.ErrMsg("Connection Refused"))
return
}
s := strings.Index(msg, "pcap dispatch trace:")
if s == -1 {
s = strings.Index(msg, "Write ")