From f5c852d80193ba882fe74345bb31caa3362172d2 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Thu, 21 Dec 2023 20:42:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8E=A5=E5=8F=A3=E7=BD=91=E5=85=83?= =?UTF-8?q?=E7=8A=B6=E6=80=81/=E7=BD=91=E5=85=83=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/framework/utils/fetch/fetch.go | 243 ++++++++++++++++++ src/modules/common/service/commont.impl.go | 5 + .../network_element/controller/ne_info.go | 76 +++++- src/modules/network_element/model/ne_info.go | 5 + .../network_element/network_element.go | 8 + .../network_element/repository/ne_info.go | 3 + .../repository/ne_info.impl.go | 77 ++++++ .../network_element/service/ne_direct_link.go | 44 ++++ .../network_element/service/ne_info.go | 3 + .../network_element/service/ne_info.impl.go | 21 ++ 10 files changed, 480 insertions(+), 5 deletions(-) create mode 100644 src/framework/utils/fetch/fetch.go create mode 100644 src/modules/network_element/service/ne_direct_link.go diff --git a/src/framework/utils/fetch/fetch.go b/src/framework/utils/fetch/fetch.go new file mode 100644 index 00000000..38a8ec36 --- /dev/null +++ b/src/framework/utils/fetch/fetch.go @@ -0,0 +1,243 @@ +package fetch + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/url" + "os" + "strings" + "time" +) + +// Get 发送 GET 请求 +// timeout 超时时间(秒) +func Get(url string, headers map[string]string, timeout uint8) ([]byte, error) { + if timeout < 1 || timeout > 180 { + timeout = 1 + } + client := &http.Client{ + Timeout: time.Duration(timeout) * time.Second, // 设置超时时间为 5 秒 + } + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + for key, value := range headers { + req.Header.Set(key, value) + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, errors.New(resp.Status) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return body, nil +} + +// Post 发送 POST 请求 +func Post(url string, data url.Values, headers map[string]string) ([]byte, error) { + client := &http.Client{} + + req, err := http.NewRequest("POST", url, strings.NewReader(data.Encode())) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + for key, value := range headers { + req.Header.Set(key, value) + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, errors.New(resp.Status) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return body, nil +} + +// PostJSON 发送 POST 请求,并将请求体序列化为 JSON 格式 +func PostJSON(url string, data any, headers map[string]string) ([]byte, error) { + client := &http.Client{} + + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", url, bytes.NewReader(jsonData)) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + + for key, value := range headers { + req.Header.Set(key, value) + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, errors.New(resp.Status) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return body, nil +} + +// UploadFile 上传文件函数,接收 URL 地址、表单参数和文件对象,返回响应内容或错误信息 +func PostUploadFile(url string, params map[string]string, file *os.File) ([]byte, error) { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + part, err := writer.CreateFormFile("file", file.Name()) + if err != nil { + return nil, fmt.Errorf("failed to create form file: %v", err) + } + + _, err = io.Copy(part, file) + if err != nil { + return nil, fmt.Errorf("failed to copy file content: %v", err) + } + + for key, value := range params { + err = writer.WriteField(key, value) + if err != nil { + return nil, fmt.Errorf("failed to write form field: %v", err) + } + } + + err = writer.Close() + if err != nil { + return nil, fmt.Errorf("failed to close writer: %v", err) + } + + req, err := http.NewRequest("POST", url, body) + if err != nil { + return nil, fmt.Errorf("failed to create HTTP request: %v", err) + } + + req.Header.Set("Content-Type", writer.FormDataContentType()) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("HTTP request failed: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP request returned status: %s", resp.Status) + } + + responseBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %v", err) + } + + return responseBody, nil +} + +// PutJSON 发送 PUT 请求,并将请求体序列化为 JSON 格式 +func PutJSON(url string, data any, headers map[string]string) ([]byte, error) { + client := &http.Client{} + + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", url, bytes.NewReader(jsonData)) + if err != nil { + return nil, err + } + + for key, value := range headers { + req.Header.Set(key, value) + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, errors.New(resp.Status) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return body, nil +} + +// Delete 发送 DELETE 请求 +func Delete(url string, headers map[string]string) ([]byte, error) { + client := &http.Client{} + + req, err := http.NewRequest("DELETE", url, nil) + if err != nil { + return nil, err + } + + for key, value := range headers { + req.Header.Set(key, value) + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, errors.New(resp.Status) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return body, nil +} diff --git a/src/modules/common/service/commont.impl.go b/src/modules/common/service/commont.impl.go index 218d20d5..becbfee5 100644 --- a/src/modules/common/service/commont.impl.go +++ b/src/modules/common/service/commont.impl.go @@ -1,6 +1,7 @@ package service import ( + "ems.agt/lib/global" systemService "ems.agt/src/modules/system/service" ) @@ -21,6 +22,10 @@ type CommontImpl struct { // SystemConfigInfo 系统配置信息 func (s *CommontImpl) SystemConfigInfo() map[string]string { infoMap := map[string]string{} + // 获取打包注入的全局变量信息 + infoMap["version"] = global.Version + infoMap["buildTime"] = global.BuildTime + infoMap["goVer"] = global.GoVer // 获取LOGO类型 logoType := s.sysConfigService.SelectConfigValueByKey("sys.logo.type") infoMap["logoType"] = logoType diff --git a/src/modules/network_element/controller/ne_info.go b/src/modules/network_element/controller/ne_info.go index d8f8a7c8..b34116b5 100644 --- a/src/modules/network_element/controller/ne_info.go +++ b/src/modules/network_element/controller/ne_info.go @@ -3,7 +3,9 @@ package controller import ( "ems.agt/src/framework/i18n" "ems.agt/src/framework/utils/ctx" + "ems.agt/src/framework/utils/parse" "ems.agt/src/framework/vo/result" + "ems.agt/src/modules/network_element/model" neService "ems.agt/src/modules/network_element/service" "github.com/gin-gonic/gin" ) @@ -21,22 +23,86 @@ type NeInfoController struct { neInfoService neService.INeInfo } +// 网元状态 +// +// GET /state +func (s *NeInfoController) NeState(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var querys struct { + NeType string `form:"neType" binding:"required"` + NeID string `form:"neId" binding:"required"` + } + if err := c.ShouldBindQuery(&querys); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + 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 + } + + // 网元直连 + resData, err := neService.NeState(neInfo) + if err != nil { + c.JSON(200, result.ErrMsg("connection failure")) + return + } + + c.JSON(200, result.OkData(resData)) +} + // 网元neType和neID查询 // // GET /info func (s *NeInfoController) NeTypeAndID(c *gin.Context) { language := ctx.AcceptLanguage(c) - neType := c.Query("neType") - neId := c.Query("neId") - if neType == "" || neId == "" { + var querys struct { + NeType string `form:"neType" binding:"required"` + NeID string `form:"neId" binding:"required"` + } + if err := c.ShouldBindQuery(&querys); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } - neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId) - if neInfo.NeId != neId || neInfo.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 } c.JSON(200, result.OkData(neInfo)) } + +// 网元列表 +// +// GET /list +func (s *NeInfoController) NeList(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var querys struct { + NeType string `form:"neType"` + NeId string `form:"neId"` + BandStatus string `form:"bandStatus"` + } + if err := c.ShouldBindQuery(&querys); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 查询实体参数 + ne := model.NeInfo{} + if querys.NeType != "" { + ne.NeType = querys.NeType + } + if querys.NeId != "" { + ne.NeId = querys.NeId + } + bandStatus := parse.Boolean(querys.BandStatus) + neList := s.neInfoService.SelectNeList(ne, bandStatus) + if len(neList) == 0 { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + c.JSON(200, result.OkData(neList)) +} diff --git a/src/modules/network_element/model/ne_info.go b/src/modules/network_element/model/ne_info.go index 941e7a09..ad13c4a8 100644 --- a/src/modules/network_element/model/ne_info.go +++ b/src/modules/network_element/model/ne_info.go @@ -16,4 +16,9 @@ type NeInfo struct { NeAddress string `json:"neAddress"` Status string `json:"status"` // 0: 在线 1: 下线 2: 备用 3: 工程 UpdateTime string `json:"updateTime"` + + // ====== 非数据库字段属性 ====== + + // 服务状态 + ServerState map[string]any `json:"serverState,omitempty"` } diff --git a/src/modules/network_element/network_element.go b/src/modules/network_element/network_element.go index 866095ec..6ae71f76 100644 --- a/src/modules/network_element/network_element.go +++ b/src/modules/network_element/network_element.go @@ -20,6 +20,14 @@ func Setup(router *gin.Engine) { middleware.PreAuthorize(nil), controller.NewNeInfo.NeTypeAndID, ) + neGroup.GET("/state", + middleware.PreAuthorize(nil), + controller.NewNeInfo.NeState, + ) + neGroup.GET("/list", + middleware.PreAuthorize(nil), + controller.NewNeInfo.NeList, + ) } // 网元处理 diff --git a/src/modules/network_element/repository/ne_info.go b/src/modules/network_element/repository/ne_info.go index 1f4adcd3..52868390 100644 --- a/src/modules/network_element/repository/ne_info.go +++ b/src/modules/network_element/repository/ne_info.go @@ -8,4 +8,7 @@ import ( type INeInfo interface { // SelectNeInfoByNeTypeAndNeID 通过ne_type和ne_id查询网元信息 SelectNeInfoByNeTypeAndNeID(neType, neID string) model.NeInfo + + // SelectNeList 查询网元列表 + SelectNeList(ne model.NeInfo) []model.NeInfo } diff --git a/src/modules/network_element/repository/ne_info.impl.go b/src/modules/network_element/repository/ne_info.impl.go index f3615bdc..e52cf3f1 100644 --- a/src/modules/network_element/repository/ne_info.impl.go +++ b/src/modules/network_element/repository/ne_info.impl.go @@ -1,12 +1,33 @@ package repository import ( + "sort" + "strings" + "ems.agt/src/framework/datasource" "ems.agt/src/framework/logger" "ems.agt/src/framework/utils/repo" "ems.agt/src/modules/network_element/model" ) +// neListSort 网元列表预设排序 +var neListSort = []string{ + "OMC", + "MME", + "AMF", + "AUSF", + "UDM", + "SMF", + "PCF", + "UPF", + "NRF", + "NSSF", + "IMS", + "N3IWF", + "NEF", + "LMF", +} + // 实例化数据层 NeInfoImpl 结构体 var NewNeInfoImpl = &NeInfoImpl{ selectSql: `select id, ne_type, ne_id, rm_uid, ne_name, ip, port, pv_flag, province, vendor_name, dn, ne_address, status, update_time from ne_info`, @@ -49,6 +70,31 @@ 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 + } + } + // 后一个 + befter := arr[j] + befterIndex := 0 + for i, v := range neListSort { + if befter.NeType == v { + befterIndex = i + break + } + } + // 升序 + return afterIndex < befterIndex + }) + return arr } @@ -67,3 +113,34 @@ func (r *NeInfoImpl) SelectNeInfoByNeTypeAndNeID(neType, neID string) model.NeIn } return model.NeInfo{} } + +// SelectNeList 查询网元列表 +func (r *NeInfoImpl) SelectNeList(ne model.NeInfo) []model.NeInfo { + // 查询条件拼接 + var conditions []string + var params []any + if ne.NeType != "" { + conditions = append(conditions, "ne_type = ?") + params = append(params, ne.NeType) + } + if ne.NeId != "" { + conditions = append(conditions, "ne_id = ?") + params = append(params, ne.NeId) + } + + // 构建查询条件语句 + whereSql := "" + if len(conditions) > 0 { + whereSql += " where " + strings.Join(conditions, " and ") + } + + // 查询数据 + querySql := r.selectSql + whereSql + " order by ne_type asc " + results, err := datasource.RawDB("", querySql, params) + if err != nil { + logger.Errorf("query err => %v", err) + } + + // 转换实体 + return r.convertResultRows(results) +} diff --git a/src/modules/network_element/service/ne_direct_link.go b/src/modules/network_element/service/ne_direct_link.go new file mode 100644 index 00000000..ef29ed60 --- /dev/null +++ b/src/modules/network_element/service/ne_direct_link.go @@ -0,0 +1,44 @@ +package service + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "ems.agt/src/framework/logger" + "ems.agt/src/framework/utils/fetch" + "ems.agt/src/modules/network_element/model" +) + +// NeState 获取网元端服务状态 +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) + if err != nil { + logger.Warnf("NeState %s", err.Error()) + return nil, err + } + + // 序列化结果 + var resData map[string]any + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Warnf("NeState Unmarshal %s", err.Error()) + return nil, err + } + + return map[string]any{ + "neType": neInfo.NeType, + "neId": neInfo.NeId, + "neName": neInfo.NeName, + "refreshTime": time.Now().UnixMilli(), // 获取时间 + "version": resData["version"], + "capability": resData["capability"], + "sn": resData["serialNum"], + "expire": resData["expiryDate"], + "cpu": resData["cpuUsage"], + "mem": resData["memUsage"], + }, nil +} diff --git a/src/modules/network_element/service/ne_info.go b/src/modules/network_element/service/ne_info.go index 715892b9..e6e611b6 100644 --- a/src/modules/network_element/service/ne_info.go +++ b/src/modules/network_element/service/ne_info.go @@ -6,4 +6,7 @@ import "ems.agt/src/modules/network_element/model" type INeInfo interface { // SelectNeInfoByNeTypeAndNeID 通过ne_type和ne_id查询网元信息 SelectNeInfoByNeTypeAndNeID(neType, neID string) model.NeInfo + + // SelectNeList 查询网元列表 + SelectNeList(ne model.NeInfo, bandStatus bool) []model.NeInfo } diff --git a/src/modules/network_element/service/ne_info.impl.go b/src/modules/network_element/service/ne_info.impl.go index bd5d41a3..484d7c87 100644 --- a/src/modules/network_element/service/ne_info.impl.go +++ b/src/modules/network_element/service/ne_info.impl.go @@ -20,3 +20,24 @@ type NeInfoImpl struct { func (r *NeInfoImpl) SelectNeInfoByNeTypeAndNeID(neType, neID string) model.NeInfo { return r.NeInfoRepository.SelectNeInfoByNeTypeAndNeID(neType, neID) } + +// SelectNeList 查询网元列表 +func (r *NeInfoImpl) SelectNeList(ne model.NeInfo, bandStatus bool) []model.NeInfo { + list := r.NeInfoRepository.SelectNeList(ne) + + // 网元直连读取网元服务状态 + if bandStatus { + neList := &list + for i := range *neList { + v := (*neList)[i] + result, err := NeState(v) + if err != nil { + (*neList)[i].ServerState = map[string]any{} + continue + } + (*neList)[i].ServerState = result + } + } + + return list +}