From 0b51ac719b8bc4d24974b28b83b85d16a7d8ca66 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Wed, 30 Apr 2025 17:02:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E7=BB=88=E7=AB=AF?= =?UTF-8?q?=E7=AD=96=E7=95=A5=E8=A7=84=E5=88=99=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/database/lite/install/sys_i18n.sql | 1 + build/database/std/install/sys_i18n.sql | 2 +- src/modules/network_data/controller/pcf.go | 328 ++++++++++++++++++ src/modules/network_data/network_data.go | 34 ++ src/modules/network_element/fetch_link/pcf.go | 280 +++++++++++++++ 5 files changed, 644 insertions(+), 1 deletion(-) create mode 100644 src/modules/network_data/controller/pcf.go create mode 100644 src/modules/network_element/fetch_link/pcf.go diff --git a/build/database/lite/install/sys_i18n.sql b/build/database/lite/install/sys_i18n.sql index b5341bce..df2f05bb 100644 --- a/build/database/lite/install/sys_i18n.sql +++ b/build/database/lite/install/sys_i18n.sql @@ -754,6 +754,7 @@ INSERT INTO "sys_i18n" VALUES (674, 'config.sys.user.passwdNotAllowedHistory', ' INSERT INTO "sys_i18n" VALUES (675, 'config.sys.user.passwdNotAllowedHistoryRemark', '创建新密码不等于之前使用的x次中的密码', 'Creating a new password that is not equal to the previously used password in x times'); INSERT INTO "sys_i18n" VALUES (676, 'login.errPasswdHistory', '不允许使用最近密码', 'Recent passwords not allowed'); INSERT INTO "sys_i18n" VALUES (677, 'log.operate.title.oauth2client', 'Oauth2客户端授权', 'Oauth2 Client Authorization'); +INSERT INTO "sys_i18n" VALUES (678, 'log.operate.title.pcfRule', '终端策略规则', 'UE PCC Rule'); INSERT INTO "sys_i18n" VALUES (679, 'dictType.trace_msg_type', '跟踪消息类型', 'Trace Message Type'); INSERT INTO "sys_i18n" VALUES (680, 'dictData.trace_msg_type.0', '请求', 'Request'); INSERT INTO "sys_i18n" VALUES (681, 'dictData.trace_msg_type.1', '响应', 'Response'); diff --git a/build/database/std/install/sys_i18n.sql b/build/database/std/install/sys_i18n.sql index 1e317b14..38e80800 100644 --- a/build/database/std/install/sys_i18n.sql +++ b/build/database/std/install/sys_i18n.sql @@ -692,7 +692,7 @@ INSERT INTO `sys_i18n` VALUES (674, 'config.sys.user.passwdNotAllowedHistory', ' INSERT INTO `sys_i18n` VALUES (675, 'config.sys.user.passwdNotAllowedHistoryRemark', '创建新密码不等于之前使用的x次中的密码', 'Creating a new password that is not equal to the previously used password in x times'); INSERT INTO `sys_i18n` VALUES (676, 'login.errPasswdHistory', '不允许使用最近密码', 'Recent passwords not allowed'); INSERT INTO `sys_i18n` VALUES (677, 'log.operate.title.oauth2client', 'Oauth2客户端授权', 'Oauth2 Client Authorization'); --- INSERT INTO `sys_i18n` VALUES (678, 'config.ne.neConfigBackupFTPRemark', '请通过配置文件备份页面进行设置FTP信息', 'Please set the FTP information through the configuration file backup page.'); +INSERT INTO `sys_i18n` VALUES (678, 'log.operate.title.pcfRule', '终端策略规则', 'UE PCC Rule'); INSERT INTO `sys_i18n` VALUES (679, 'dictType.trace_msg_type', '跟踪消息类型', 'Trace Message Type'); INSERT INTO `sys_i18n` VALUES (680, 'dictData.trace_msg_type.0', '请求', 'Request'); INSERT INTO `sys_i18n` VALUES (681, 'dictData.trace_msg_type.1', '响应', 'Response'); diff --git a/src/modules/network_data/controller/pcf.go b/src/modules/network_data/controller/pcf.go new file mode 100644 index 00000000..697746f6 --- /dev/null +++ b/src/modules/network_data/controller/pcf.go @@ -0,0 +1,328 @@ +package controller + +import ( + "fmt" + + "be.ems/src/framework/i18n" + "be.ems/src/framework/reqctx" + "be.ems/src/framework/resp" + neFetchlink "be.ems/src/modules/network_element/fetch_link" + neService "be.ems/src/modules/network_element/service" + + "github.com/gin-gonic/gin" +) + +// 实例化控制层 PCFController 结构体 +var NewPCF = &PCFController{ + neInfoService: neService.NewNeInfo, +} + +// 网元PCF +// +// PATH /pcf +type PCFController struct { + neInfoService *neService.NeInfo // 网元信息服务 +} + +// 策略配置列表 +// +// GET /rule/list +// +// @Tags network_data/pcf +// @Accept json +// @Produce json +// @Param neId query string true "NE ID" default(001) +// @Param imsi query string false "IMSI" +// @Param msisdn query string false "MSISDN" +// @Success 200 {object} object "Response Results" +// @Security TokenAuth +// @Summary Policy Configuration List +// @Description Policy Configuration List +// @Router /neData/pcf/rule/list [get] +func (s PCFController) RuleInfoList(c *gin.Context) { + language := reqctx.AcceptLanguage(c) + var query struct { + NeId string `form:"neId" binding:"required"` + IMSI string `form:"imsi"` + MSISDN string `form:"msisdn"` + } + if err := c.ShouldBindQuery(&query); err != nil { + errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err)) + c.JSON(422, resp.CodeMsg(422001, errMsgs)) + return + } + + // 查询网元信息 + neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", query.NeId) + if neInfo.NeId != query.NeId || neInfo.IP == "" { + c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + data, err := neFetchlink.PCFRuleInfo(neInfo, map[string]string{ + "imsi": query.IMSI, + "msisdn": query.MSISDN, + }) + if err != nil { + c.JSON(200, resp.ErrMsg(err.Error())) + return + } + + c.JSON(200, resp.OkData(data)) +} + +// 策略配置添加 +// +// POST /rule +// +// @Tags network_data/pcf +// @Accept json +// @Produce json +// @Param data body object true "Request Param" +// @Success 200 {object} object "Response Results" +// @Security TokenAuth +// @Summary Policy Configuration Additions +// @Description Policy Configuration Additions +// @Router /neData/pcf/rule [post] +func (s PCFController) RuleInfoAdd(c *gin.Context) { + language := reqctx.AcceptLanguage(c) + var body struct { + NeId string `json:"neId" binding:"required"` // 网元ID + Num int64 `json:"num"` // 批量添加,默认0单条,大于1时imsi/msisdn会累加数值 + ParamData map[string]any `json:"paramData" binding:"required"` // 参数数据 + // Imsi string `json:"imsi" binding:"required"` + // Msisdn string `json:"msisdn" binding:"required"` + // Sar string `json:"sar"` // 根据PCF参数配置Service Area Restriction -> Name + // PccRules string `json:"pccRules"` // 根据PCF参数配置PCC Rules -> Rule ID + // QosAudio string `json:"qosAudio"` // 根据PCF参数配置QoS Template -> QoS ID + // QosVideo string `json:"qosVideo"` // 根据PCF参数配置QoS Template -> QoS ID + // SessRules string `json:"sessRules"` // 根据PCF参数配置Session Rules -> Rule ID + // HdrEnrich string `json:"hdrEnrich"` // 根据PCF参数配置Header Enrich Template -> Template Name + // UePolicy string `json:"uePolicy"` // UE策略模板(样例: uep_001) + // Rfsp int64 `json:"rfsp"` // 无线频率选择优先级 + } + if err := c.ShouldBindBodyWithJSON(&body); err != nil { + errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err)) + c.JSON(422, resp.CodeMsg(422001, errMsgs)) + return + } + + // 查询网元信息 + neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", body.NeId) + if neInfo.NeId != body.NeId || neInfo.IP == "" { + c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + var err error + if body.Num > 0 { // 批量添加 + err = neFetchlink.PCFRuleAddBatch(neInfo, body.ParamData, body.Num) + } else { // 单条添加 + err = neFetchlink.PCFRuleAdd(neInfo, body.ParamData) + } + if err != nil { + c.JSON(200, resp.ErrMsg(err.Error())) + return + } + c.JSON(200, resp.Ok(nil)) +} + +// 策略配置更新 +// +// PUT /rule +// +// @Tags network_data/pcf +// @Accept json +// @Produce json +// @Param data body object true "Request Param" +// @Success 200 {object} object "Response Results" +// @Security TokenAuth +// @Summary Policy Configuration Updates +// @Description Policy Configuration Updates +// @Router /neData/pcf/rule [put] +func (s PCFController) RuleInfoEdit(c *gin.Context) { + language := reqctx.AcceptLanguage(c) + var body struct { + NeId string `json:"neId" binding:"required"` // 网元ID + Num int64 `json:"num"` // 更新数量 + ParamData map[string]any `json:"paramData" binding:"required"` // 参数数据 + // Imsi string `json:"imsi" binding:"required"` + // Msisdn string `json:"msisdn" binding:"required"` + // Sar string `json:"sar"` // 根据PCF参数配置Service Area Restriction -> Name + // PccRules string `json:"pccRules"` // 根据PCF参数配置PCC Rules -> Rule ID + // QosAudio string `json:"qosAudio"` // 根据PCF参数配置QoS Template -> QoS ID + // QosVideo string `json:"qosVideo"` // 根据PCF参数配置QoS Template -> QoS ID + // SessRules string `json:"sessRules"` // 根据PCF参数配置Session Rules -> Rule ID + // HdrEnrich string `json:"hdrEnrich"` // 根据PCF参数配置Header Enrich Template -> Template Name + // UePolicy string `json:"uePolicy"` // UE策略模板(样例: uep_001) + // Rfsp int64 `json:"rfsp"` // 无线频率选择优先级 + } + if err := c.ShouldBindBodyWithJSON(&body); err != nil { + errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err)) + c.JSON(422, resp.CodeMsg(422001, errMsgs)) + return + } + + // 查询网元信息 + neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", body.NeId) + if neInfo.NeId != body.NeId || neInfo.IP == "" { + c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + var err error + if body.Num > 0 { // 批量更新 + err = neFetchlink.PCFRuleUpdateBatch(neInfo, body.ParamData, body.Num) + } else { // 单条更新 + err = neFetchlink.PCFRuleUpdate(neInfo, body.ParamData) + } + if err != nil { + c.JSON(200, resp.ErrMsg(err.Error())) + return + } + c.JSON(200, resp.Ok(nil)) +} + +// 策略配置删除 +// +// DELETE /rule +// +// @Tags network_data/pcf +// @Accept json +// @Produce json +// @Param neId query string true "NE ID" default(001) +// @Param imsi query string true "IMSi, batch deletion with quantity" +// @Param num query number false "Number of deletions" +// @Success 200 {object} object "Response Results" +// @Security TokenAuth +// @Summary Policy Configuration Deletion +// @Description Policy Configuration Deletion +// @Router /neData/pcf/rule [delete] +func (s PCFController) RuleInfoRemove(c *gin.Context) { + language := reqctx.AcceptLanguage(c) + var query struct { + NeId string `form:"neId" binding:"required"` // 网元ID + IMSI string `form:"imsi" binding:"required"` // IMSi, 带数量时为批量删除 + Num int64 `form:"num"` // 删除数量 + } + if err := c.ShouldBindQuery(&query); err != nil { + errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err)) + c.JSON(422, resp.CodeMsg(422001, errMsgs)) + return + } + + // 查询网元信息 + neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", query.NeId) + if neInfo.NeId != query.NeId || neInfo.IP == "" { + c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + var err error + if query.Num > 0 { // 批量删除 + err = neFetchlink.PCFRuleDeleteBatch(neInfo, query.IMSI, query.Num) + } else { // 单条删除 + err = neFetchlink.PCFRuleDelete(neInfo, query.IMSI) + } + if err != nil { + c.JSON(200, resp.ErrMsg(err.Error())) + return + } + c.JSON(200, resp.Ok(nil)) +} + +// 策略配置导出 +// +// GET /rule/export +// +// @Tags network_data/pcf +// @Accept json +// @Produce json +// @Param neId query string true "NE ID" default(001) +// @Param fileType query string true "File Type" default(txt) +// @Success 200 {object} object "Response Results" +// @Security TokenAuth +// @Summary Policy Configuration Export +// @Description Policy Configuration Export +// @Router /neData/pcf/rule/export [get] +func (s PCFController) RuleInfoExport(c *gin.Context) { + language := reqctx.AcceptLanguage(c) + var query struct { + NeId string `form:"neId" binding:"required"` + FileType string `form:"fileType" binding:"required,oneof=txt"` // 文件类型 + } + if err := c.ShouldBindQuery(&query); err != nil { + errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err)) + c.JSON(422, resp.CodeMsg(422001, errMsgs)) + return + } + + // 查询网元信息 + neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", query.NeId) + if neInfo.NeId != query.NeId || neInfo.IP == "" { + c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + data, err := neFetchlink.PCFRuleExport(neInfo, map[string]string{ + "fileType": query.FileType, + }) + if err != nil { + c.JSON(200, resp.ErrMsg(err.Error())) + return + } + c.Writer.Header().Set("Content-Disposition", `attachment; filename="pcf_rule_export.txt"`) + c.Data(200, "application/octet-stream", data) +} + +// 策略配置导入 +// +// PUT /rule/import +// +// @Tags network_data/pcf +// @Accept json +// @Produce json +// @Param neId query string true "NE ID" default(001) +// @Param fileType query string true "File Type" default(txt) +// @Param filePath query string true "File Path" default(/tmp/pcfuser.txt) +// @Success 200 {object} object "Response Results" +// @Security TokenAuth +// @Summary Policy Configuration Import +// @Description Policy Configuration Import +// @Router /neData/pcf/rule/import [put] +func (s PCFController) RuleInfoImport(c *gin.Context) { + language := reqctx.AcceptLanguage(c) + var body struct { + NeId string `json:"neId" binding:"required"` + FileType string `json:"fileType" binding:"required,oneof=txt"` // 文件类型 + FilePath string `json:"filePath" binding:"required"` // 网元端文件所在绝对路径 + } + if err := c.ShouldBindBodyWithJSON(&body); err != nil { + errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err)) + c.JSON(422, resp.CodeMsg(422001, errMsgs)) + return + } + + // 查询网元信息 + neInfo := s.neInfoService.FindByNeTypeAndNeID("PCF", body.NeId) + if neInfo.NeId != body.NeId || neInfo.IP == "" { + c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + output, err := neFetchlink.PCFRuleImport(neInfo, map[string]any{ + "type": body.FileType, + "filePath": body.FilePath, + }) + if err != nil { + c.JSON(200, resp.ErrMsg(err.Error())) + return + } + c.JSON(200, resp.OkMsg(output)) +} diff --git a/src/modules/network_data/network_data.go b/src/modules/network_data/network_data.go index 3c809701..bab4b2c5 100644 --- a/src/modules/network_data/network_data.go +++ b/src/modules/network_data/network_data.go @@ -476,6 +476,40 @@ func Setup(router *gin.Engine) { controller.NewSGWC.CDRExport, ) } + + // 网元PCF + pcfGroup := neDataGroup.Group("/pcf") + { + pcfGroup.GET("/rule/list", + middleware.AuthorizeUser(nil), + controller.NewPCF.RuleInfoList, + ) + pcfGroup.POST("/rule", + middleware.AuthorizeUser(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.pcfRule", collectlogs.BUSINESS_TYPE_IMPORT)), + controller.NewPCF.RuleInfoAdd, + ) + pcfGroup.PUT("/rule", + middleware.AuthorizeUser(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.pcfRule", collectlogs.BUSINESS_TYPE_UPDATE)), + controller.NewPCF.RuleInfoEdit, + ) + pcfGroup.DELETE("/rule", + middleware.AuthorizeUser(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.pcfRule", collectlogs.BUSINESS_TYPE_DELETE)), + controller.NewPCF.RuleInfoRemove, + ) + pcfGroup.GET("/rule/export", + middleware.AuthorizeUser(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.pcfRule", collectlogs.BUSINESS_TYPE_EXPORT)), + controller.NewPCF.RuleInfoExport, + ) + pcfGroup.PUT("/rule/import", + middleware.AuthorizeUser(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.pcfRule", collectlogs.BUSINESS_TYPE_IMPORT)), + controller.NewPCF.RuleInfoImport, + ) + } } // InitLoad 初始参数 diff --git a/src/modules/network_element/fetch_link/pcf.go b/src/modules/network_element/fetch_link/pcf.go new file mode 100644 index 00000000..924cedb9 --- /dev/null +++ b/src/modules/network_element/fetch_link/pcf.go @@ -0,0 +1,280 @@ +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" +) + +// PCFRuleInfo PCF策略配置查询信息 +func PCFRuleInfo(neInfo model.NeInfo, data map[string]string) ([]map[string]any, error) { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/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 len(query) > 0 { + neUrl = fmt.Sprintf("%s?%s", neUrl, strings.Join(query, "&")) + } + + var resData map[string]any + resBytes, err := fetch.Get(neUrl, nil, 60_000) + if err != nil { + errStr := err.Error() + logger.Warnf("PCFRuleInfo Get \"%s\"", neUrl) + logger.Errorf("PCFRuleInfo %s", errStr) + return nil, fmt.Errorf("NeService PCF API Error") + } + + // 序列化结果 + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("PCFRuleInfo Unmarshal %s", err.Error()) + return nil, err + } + + // 固定返回字段,方便前端解析 + if v, ok := resData["data"]; ok && v != nil { + if arr := v.([]any); len(arr) > 0 { + result := make([]map[string]any, len(arr)) + for i, item := range arr { + result[i] = item.(map[string]any) + } + return result, nil + } + } + return []map[string]any{}, nil +} + +// PCFRuleAdd PCF策略配置添加 +func PCFRuleAdd(neInfo model.NeInfo, data any) error { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo", neInfo.IP, neInfo.Port) + resBytes, err := fetch.PostJSON(neUrl, data, nil) + if err != nil { + errStr := err.Error() + // 正常 + if strings.HasPrefix(errStr, "201") { + return nil + } + // 错误结果 + if strings.HasPrefix(errStr, "400") { + // 序列化结果 + var resData map[string]any + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("PCFRuleAdd Unmarshal %s", err.Error()) + return err + } + return fmt.Errorf("%s", resData["cause"]) + } + + logger.Warnf("PCFRuleAdd Put \"%s\"", neUrl) + logger.Errorf("PCFRuleAdd %s", errStr) + return fmt.Errorf("NeService PCF API Error") + } + return nil +} + +// PCFRuleAddBatch PCF策略配置批量添加 +func PCFRuleAddBatch(neInfo model.NeInfo, data map[string]any, num int64) error { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/%d", neInfo.IP, neInfo.Port, num) + resBytes, err := fetch.PostJSON(neUrl, data, nil) + if err != nil { + errStr := err.Error() + // 正常 + if strings.HasPrefix(errStr, "201") { + return nil + } + // 错误结果 + if strings.HasPrefix(errStr, "400") { + // 序列化结果 + var resData map[string]any + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("PCFRuleAddBatch Unmarshal %s", err.Error()) + return err + } + return fmt.Errorf("%s", resData["cause"]) + } + + logger.Warnf("PCFRuleAddBatch Put \"%s\"", neUrl) + logger.Errorf("PCFRuleAddBatch %s", errStr) + return fmt.Errorf("NeService PCF API Error") + } + return nil +} + +// PCFRuleUpdate PCF策略配置修改 +func PCFRuleUpdate(neInfo model.NeInfo, data map[string]any) error { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo", neInfo.IP, neInfo.Port) + resBytes, err := fetch.PutJSON(neUrl, data, nil) + if err != nil { + errStr := err.Error() + // 错误结果 + if strings.HasPrefix(errStr, "400") { + // 序列化结果 + var resData map[string]any + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("PCFRuleUpdate Unmarshal %s", err.Error()) + return err + } + return fmt.Errorf("%s", resData["cause"]) + } + + logger.Warnf("PCFRuleUpdate Put \"%s\"", neUrl) + logger.Errorf("PCFRuleUpdate %s", errStr) + return fmt.Errorf("NeService PCF API Error") + } + return nil +} + +// PCFRuleUpdateBatch PCF策略配置批量修改 +func PCFRuleUpdateBatch(neInfo model.NeInfo, data map[string]any, num int64) error { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/%d", neInfo.IP, neInfo.Port, num) + resBytes, err := fetch.PutJSON(neUrl, data, nil) + if err != nil { + errStr := err.Error() + // 错误结果 + if strings.HasPrefix(errStr, "400") { + // 序列化结果 + var resData map[string]any + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("PCFRuleUpdateBatch Unmarshal %s", err.Error()) + return err + } + return fmt.Errorf("%s", resData["cause"]) + } + + logger.Warnf("PCFRuleUpdateBatch Put \"%s\"", neUrl) + logger.Errorf("PCFRuleUpdateBatch %s", errStr) + return fmt.Errorf("NeService PCF API Error") + } + return nil +} + +// PCFRuleDelete PCF策略配置删除 +func PCFRuleDelete(neInfo model.NeInfo, imsi string) error { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo?imsi=%s", neInfo.IP, neInfo.Port, imsi) + resBytes, err := fetch.Delete(neUrl, nil) + if err != nil { + errStr := err.Error() + // 正常 + if strings.HasPrefix(errStr, "204") { + return nil + } + // 错误结果 + if strings.HasPrefix(errStr, "400") { + // 序列化结果 + var resData map[string]any + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("PCFRuleDelete Unmarshal %s", err.Error()) + return err + } + return fmt.Errorf("%s", resData["cause"]) + } + + logger.Warnf("PCFRuleDelete Delete \"%s\"", neUrl) + logger.Errorf("PCFRuleDelete %s", errStr) + return fmt.Errorf("NeService PCF API Error") + } + return nil +} + +// PCFRuleDeleteBatch PCF策略配置批量删除 +func PCFRuleDeleteBatch(neInfo model.NeInfo, imsi string, num int64) error { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/%d?imsi=%s", neInfo.IP, neInfo.Port, num, imsi) + resBytes, err := fetch.Delete(neUrl, nil) + if err != nil { + errStr := err.Error() + // 正常 + if strings.HasPrefix(errStr, "204") { + return nil + } + // 错误结果 + if strings.HasPrefix(errStr, "400") { + // 序列化结果 + var resData map[string]any + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("PCFRuleDeleteBatch Unmarshal %s", err.Error()) + return err + } + return fmt.Errorf("%s", resData["cause"]) + } + + logger.Warnf("PCFRuleDeleteBatch Delete \"%s\"", neUrl) + logger.Errorf("PCFRuleDeleteBatch %s", errStr) + return fmt.Errorf("NeService PCF API Error") + } + return nil +} + +// PCFRuleExport PCF策略配置导出 +func PCFRuleExport(neInfo model.NeInfo, data map[string]string) ([]byte, error) { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/file/export", neInfo.IP, neInfo.Port) + // 查询参数拼接 + query := []string{} + if v, ok := data["fileType"]; ok && v != "" { + query = append(query, fmt.Sprintf("fileType=%s", v)) + } + if len(query) > 0 { + neUrl = fmt.Sprintf("%s?%s", neUrl, strings.Join(query, "&")) + } + + resBytes, err := fetch.Get(neUrl, nil, 30_000) + if err != nil { + logger.Warnf("PCFRuleExport Get \"%s\"", neUrl) + logger.Errorf("PCFRuleExport %s", err.Error()) + return nil, fmt.Errorf("NeService PCF API Error") + } + return resBytes, nil +} + +// PCFRuleImport PCF策略配置导入 +func PCFRuleImport(neInfo model.NeInfo, data map[string]any) (string, error) { + neUrl := fmt.Sprintf("http://%s:%d/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/file/import", neInfo.IP, neInfo.Port) + resBytes, err := fetch.PutJSON(neUrl, data, nil) + var resData map[string]any + if err != nil { + errStr := err.Error() + // 错误结果 + if strings.HasPrefix(errStr, "400") { + // 序列化结果 + var resData map[string]any + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("PCFRuleDeleteBatch Unmarshal %s", err.Error()) + return "", err + } + return "", fmt.Errorf("%s", resData["cause"]) + } + + logger.Warnf("PCFRuleImport Put \"%s\"", neUrl) + logger.Errorf("PCFRuleImport %s", errStr) + return "", fmt.Errorf("NeService PCF API Error") + } + + // 200 成功无数据时 + if len(resBytes) == 0 { + return "", nil + } + + // 序列化结果 + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("PCFRuleImport Unmarshal %s", err.Error()) + return "", err + } + return fmt.Sprint(resData["data"]), nil +}