feat: support CBC broadcast

This commit is contained in:
zhangsz
2025-08-01 14:50:55 +08:00
parent 2c1e55bd0c
commit 1b4cb6399e
16 changed files with 1348 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
/*
Navicat Premium Data Transfer
Source Server : root@192.168.2.242
Source Server Type : MariaDB
Source Server Version : 100622 (10.6.22-MariaDB-0ubuntu0.22.04.1)
Source Host : 192.168.2.242:33066
Source Schema : omc_db
Target Server Type : MariaDB
Target Server Version : 100622 (10.6.22-MariaDB-0ubuntu0.22.04.1)
File Encoding : 65001
Date: 01/08/2025 10:07:00
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for cbc_message
-- ----------------------------
DROP TABLE IF EXISTS `cbc_message`;
CREATE TABLE `cbc_message` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`ne_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`ne_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`message_json` varchar(10240) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`status` enum('ACTIVE','INACTIVE') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'INACTIVE',
`detail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`created_at` bigint(20) NULL DEFAULT current_timestamp(),
`updated_at` bigint(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `id`(`id`) USING BTREE,
INDEX `idx_ne_time`(`ne_type`, `ne_id`, `created_at`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 64 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'CDR事件_MF' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -775,4 +775,23 @@ INSERT INTO `sys_i18n` VALUES (757, 'dictData.trace_interfaces.5', 'N5', 'N5');
INSERT INTO `sys_i18n` VALUES (758, "alarm.export.alarmCode", "告警编码", "Alarm Code");
INSERT INTO `sys_i18n` VALUES (759, "config.sys.user.fristPasswdChangeRemark", "关闭改为false 开启改为true, 建议同时设置密码有效期", "Off to false On to true, it is recommended to set the password expiration date at the same time.");
INSERT INTO `sys_i18n` VALUES (2000, 'menu.psap.agent', '座席', 'Agent');
INSERT INTO `sys_i18n` VALUES (2001, 'menu.psap.agent.callings', '并行话务', 'Calling Information');
INSERT INTO `sys_i18n` VALUES (2002, 'menu.psap.agent.callback', '回拨管理', 'Callback Management');
INSERT INTO `sys_i18n` VALUES (2003, 'callback.status.NEW', '新建', 'New');
INSERT INTO `sys_i18n` VALUES (2004, 'callback.status.IN_PROGRESS', '处理中', 'In Progress');
INSERT INTO `sys_i18n` VALUES (2005, 'callback.status.NO_ANSWER_1', '未应答1', 'No Answer 1');
INSERT INTO `sys_i18n` VALUES (2006, 'callback.status.NO_ANSWER_2', '未应答2', 'No Answer 2');
INSERT INTO `sys_i18n` VALUES (2007, 'callback.status.TIMEOUT', '超时', 'Timeout');
INSERT INTO `sys_i18n` VALUES (2008, 'callback.status.PENDING', '挂起', 'Pending');
INSERT INTO `sys_i18n` VALUES (2009, 'callback.status.CLOSED', '关闭', 'Closed');
INSERT INTO `sys_i18n` VALUES (2010, 'job.export.cdr.mf', '定期导出MF话单', 'Periodic Export of MF Call Records');
INSERT INTO `sys_i18n` VALUES (2011, 'job.psap.ticket.monitor', '回拨工单监控', 'Callback Ticket Monitoring');
INSERT INTO `sys_i18n` VALUES (2012, 'menu.omc.cdr', '话单', 'Call Records');
INSERT INTO `sys_i18n` VALUES (2013, 'menu.omc.cdr.mf', '紧急呼叫话单', 'Emergency Call Records');
INSERT INTO `sys_i18n` VALUES (2014, 'menu.omc.cdr.crbt', '彩铃话单', 'Color Ring Back Tone Records');
INSERT INTO `sys_i18n` VALUES (2015, 'menu.omc.cdr.mms', '彩信话单', 'Multimedia Message Service Records');
INSERT INTO `sys_i18n` VALUES (2016, 'menu.ue.cbc.cbe', '广播', 'Broadcast');
INSERT INTO `sys_i18n` VALUES (2017, 'log.operate.title.cbcMessage', '广播事件', 'Broadcast Event');
-- Dump completed on 2025-02-14 15:26:56

View File

@@ -209,6 +209,8 @@ INSERT INTO `sys_menu` VALUES (2167, 'menu.dashboard.overview.imsUeNum', 2132, 2
INSERT INTO `sys_menu` VALUES (2168, 'menu.dashboard.overview.gnbBase', 2132, 6, '', '', '1', '1', 'B', '1', '1', 'dashboard:overview:gnbBase', '#', '0', 'system', 1728641403588, 'system', 1728641403588, '');
INSERT INTO `sys_menu` VALUES (2169, 'menu.dashboard.overview.enbBase', 2132, 8, '', '', '1', '1', 'B', '1', '1', 'dashboard:overview:enbBase', '#', '0', 'system', 1728641403588, 'system', 1728641403588, '');
INSERT INTO `sys_menu` VALUES (20000, 'menu.ue.cbc.cbe', 5, 20, 'cbe', 'cbc/cbe/index', '1', '0', 'M', '1', '1', 'cbc#dashboard:cdr:index', 'icon-tubiaoku', '0', 'system', 1711352709786, 'system', 1747796007372, '');
SET FOREIGN_KEY_CHECKS = 1;
-- Dump completed on 2025-02-14 15:26:56

View File

@@ -163,6 +163,7 @@ INSERT IGNORE INTO `sys_role_menu` VALUES (2, 2166);
INSERT IGNORE INTO `sys_role_menu` VALUES (2, 2167);
INSERT IGNORE INTO `sys_role_menu` VALUES (2, 2168);
INSERT IGNORE INTO `sys_role_menu` VALUES (2, 2169);
INSERT IGNORE INTO `sys_role_menu` VALUES (2, 20000);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 1);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 4);
@@ -238,6 +239,7 @@ INSERT IGNORE INTO `sys_role_menu` VALUES (3, 2166);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 2167);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 2168);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 2169);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 20000);
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 1);
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 4);

View File

@@ -0,0 +1,37 @@
/*
Navicat Premium Data Transfer
Source Server : root@192.168.2.242
Source Server Type : MariaDB
Source Server Version : 100622 (10.6.22-MariaDB-0ubuntu0.22.04.1)
Source Host : 192.168.2.242:33066
Source Schema : omc_db
Target Server Type : MariaDB
Target Server Version : 100622 (10.6.22-MariaDB-0ubuntu0.22.04.1)
File Encoding : 65001
Date: 01/08/2025 10:07:00
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for cbc_message
-- ----------------------------
CREATE TABLE IF NOT EXISTS `cbc_message` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`ne_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`ne_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`message_json` varchar(10240) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`status` enum('ACTIVE','INACTIVE') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'INACTIVE',
`detail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`created_at` bigint(20) NULL DEFAULT current_timestamp(),
`updated_at` bigint(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `id`(`id`) USING BTREE,
INDEX `idx_ne_time`(`ne_type`, `ne_id`, `created_at`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 64 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'CDR事件_MF' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -772,4 +772,23 @@ REPLACE INTO `sys_i18n` VALUES (757, 'dictData.trace_interfaces.5', 'N5', 'N5');
REPLACE INTO `sys_i18n` VALUES (758, "alarm.export.alarmCode", "告警编码", "Alarm Code");
REPLACE INTO `sys_i18n` VALUES (759, "config.sys.user.fristPasswdChangeRemark", "关闭改为false 开启改为true, 建议同时设置密码有效期", "Off to false On to true, it is recommended to set the password expiration date at the same time.");
REPLACE INTO `sys_i18n` VALUES (2000, 'menu.psap.agent', '座席', 'Agent');
REPLACE INTO `sys_i18n` VALUES (2001, 'menu.psap.agent.callings', '并行话务', 'Calling Information');
REPLACE INTO `sys_i18n` VALUES (2002, 'menu.psap.agent.callback', '回拨管理', 'Callback Management');
REPLACE INTO `sys_i18n` VALUES (2003, 'callback.status.NEW', '新建', 'New');
REPLACE INTO `sys_i18n` VALUES (2004, 'callback.status.IN_PROGRESS', '处理中', 'In Progress');
REPLACE INTO `sys_i18n` VALUES (2005, 'callback.status.NO_ANSWER_1', '未应答1', 'No Answer 1');
REPLACE INTO `sys_i18n` VALUES (2006, 'callback.status.NO_ANSWER_2', '未应答2', 'No Answer 2');
REPLACE INTO `sys_i18n` VALUES (2007, 'callback.status.TIMEOUT', '超时', 'Timeout');
REPLACE INTO `sys_i18n` VALUES (2008, 'callback.status.PENDING', '挂起', 'Pending');
REPLACE INTO `sys_i18n` VALUES (2009, 'callback.status.CLOSED', '关闭', 'Closed');
REPLACE INTO `sys_i18n` VALUES (2010, 'job.export.cdr.mf', '定期导出MF话单', 'Periodic Export of MF Call Records');
REPLACE INTO `sys_i18n` VALUES (2011, 'job.psap.ticket.monitor', '回拨工单监控', 'Callback Ticket Monitoring');
REPLACE INTO `sys_i18n` VALUES (2012, 'menu.omc.cdr', '话单', 'Call Records');
REPLACE INTO `sys_i18n` VALUES (2013, 'menu.omc.cdr.mf', '紧急呼叫话单', 'Emergency Call Records');
REPLACE INTO `sys_i18n` VALUES (2014, 'menu.omc.cdr.crbt', '彩铃话单', 'Color Ring Back Tone Records');
REPLACE INTO `sys_i18n` VALUES (2015, 'menu.omc.cdr.mms', '彩信话单', 'Multimedia Message Service Records');
REPLACE INTO `sys_i18n` VALUES (2016, 'menu.ue.cbc.cbe', '广播', 'Broadcast');
REPLACE INTO `sys_i18n` VALUES (2017, 'log.operate.title.cbcMessage', '广播事件', 'Broadcast Event');
-- Dump completed on 2025-02-14 15:26:56

View File

@@ -231,6 +231,8 @@ REPLACE INTO `sys_menu` VALUES (2167, 'menu.dashboard.overview.imsUeNum', 2132,
REPLACE INTO `sys_menu` VALUES (2168, 'menu.dashboard.overview.gnbBase', 2132, 6, '', '', '1', '1', 'B', '1', '1', 'dashboard:overview:gnbBase', '#', '0', 'system', 1728641403588, 'system', 1728641403588, '');
REPLACE INTO `sys_menu` VALUES (2169, 'menu.dashboard.overview.enbBase', 2132, 8, '', '', '1', '1', 'B', '1', '1', 'dashboard:overview:enbBase', '#', '0', 'system', 1728641403588, 'system', 1728641403588, '');
REPLACE INTO `sys_menu` VALUES (20000, 'menu.ue.cbc.cbe', 5, 20, 'cbe', 'cbc/cbe/index', '1', '0', 'M', '1', '1', 'cbc#dashboard:cdr:index', 'icon-tubiaoku', '0', 'system', 1711352709786, 'system', 1747796007372, '');
SET FOREIGN_KEY_CHECKS = 1;
-- Dump completed on 2025-02-14 15:26:56

View File

@@ -162,6 +162,7 @@ INSERT IGNORE INTO `sys_role_menu` VALUES (2, 2166);
INSERT IGNORE INTO `sys_role_menu` VALUES (2, 2167);
INSERT IGNORE INTO `sys_role_menu` VALUES (2, 2168);
INSERT IGNORE INTO `sys_role_menu` VALUES (2, 2169);
INSERT IGNORE INTO `sys_role_menu` VALUES (2, 20000);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 1);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 4);
@@ -237,6 +238,7 @@ INSERT IGNORE INTO `sys_role_menu` VALUES (3, 2166);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 2167);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 2168);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 2169);
INSERT IGNORE INTO `sys_role_menu` VALUES (3, 20000);
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 1);
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 4);

View File

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

View File

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

View File

@@ -579,6 +579,44 @@ func Setup(router *gin.Engine) {
controller.NewPCF.RuleInfoImport,
)
}
// 网元CBC
cbcGroup := neDataGroup.Group("/cbc")
{
cbcGroup.GET("/message/list",
middleware.AuthorizeUser(nil),
controller.NewCBC.List,
)
cbcGroup.GET("/message/:id",
middleware.AuthorizeUser(nil),
controller.NewCBC.ListById,
)
cbcGroup.POST("/message",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.cbcMessage", collectlogs.BUSINESS_TYPE_IMPORT)),
controller.NewCBC.Insert,
)
cbcGroup.PUT("/message/:id",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.cbcMessage", collectlogs.BUSINESS_TYPE_UPDATE)),
controller.NewCBC.Update,
)
cbcGroup.PUT("/message/:id/:status",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.cbcMessage", collectlogs.BUSINESS_TYPE_UPDATE)),
controller.NewCBC.UpdateStatus,
)
cbcGroup.DELETE("/message/:id",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.cbcMessage", collectlogs.BUSINESS_TYPE_DELETE)),
controller.NewCBC.Delete,
)
cbcGroup.GET("/message/export",
middleware.AuthorizeUser(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.cbcMessage", collectlogs.BUSINESS_TYPE_EXPORT)),
controller.NewCBC.Export,
)
}
}
// InitLoad 初始参数

View File

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

View File

@@ -0,0 +1,453 @@
package service
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"time"
"be.ems/src/framework/i18n"
"be.ems/src/framework/utils/file"
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
neService "be.ems/src/modules/network_element/service"
"gorm.io/gorm"
)
// 实例化数据层 CBCMessage 结构体
var NewCBCMessage = &CBCMessage{
cbcMessageRepository: repository.NewCBCMessage,
}
// CBCMessage CB消息 服务层处理
type CBCMessage struct {
cbcMessageRepository *repository.CBCMessage // UE会话事件数据信息
}
// SelectByPage 根据条件分页查询CB消息
func (s *CBCMessage) SelectByPage(query model.CBCMessageQuery) ([]model.CBCMessage, int, error) {
return s.cbcMessageRepository.SelectByPage(query)
}
// SelectCBCMessageById 根据工单ID查询CB消息
// @Description 根据工单ID查询CB消息
// @param id 工单ID
// @return *model.CBCMessage CB消息对象
// @return error 错误信息
// @example
// CBCMessage.SelectCBCMessageById(12345)
func (s *CBCMessage) SelectById(id int64) (*model.CBCMessage, error) {
return s.cbcMessageRepository.SelectById(id)
}
func (s *CBCMessage) Insert(msg model.CBCMessage) error {
// 解析messageJson获取eventName
var messageData map[string]interface{}
if err := json.Unmarshal(msg.MessageJson, &messageData); err != nil {
return fmt.Errorf("failed to parse message_json: %w", err)
}
// 提取eventName
eventName, ok := messageData["eventName"].(string)
if !ok || eventName == "" {
return fmt.Errorf("eventName is required in message_json")
}
// 检查是否已存在相同的eventName
var err error
var existingMsg *model.CBCMessage
if existingMsg, err = s.cbcMessageRepository.SelectByEventName(eventName); err != nil {
return fmt.Errorf("failed to check existing CBC message: %w", err)
}
if existingMsg != nil {
return fmt.Errorf("CBC message with eventName '%s' already exists for neType '%s' and neId '%s'",
eventName, existingMsg.NeType, existingMsg.NeId)
}
return s.cbcMessageRepository.Insert(msg)
}
// UpdateCBCMessage 更新CB消息
// @Description 更新CB消息
// @param msg CB消息对象
// @return error 错误信息
// @example
// mfCBCMessageService.UpdateCBCMessage(msg)
func (s *CBCMessage) Update(id int64, messageJson json.RawMessage) error {
// 解析messageJson获取eventName
var messageData map[string]interface{}
if err := json.Unmarshal(messageJson, &messageData); err != nil {
return fmt.Errorf("failed to parse message_json: %w", err)
}
// 提取eventName
eventName, ok := messageData["eventName"].(string)
if !ok || eventName == "" {
return fmt.Errorf("eventName is required in message_json")
}
// 检查是否已存在相同的eventName
var err error
var msg *model.CBCMessage
if msg, err = s.cbcMessageRepository.SelectByEventName(eventName); err != nil {
if err == gorm.ErrRecordNotFound {
return fmt.Errorf("no existing CBC message found with eventName: %s", eventName)
}
return fmt.Errorf("failed to query existing CBC message: %w", err)
}
// 如果存在,更新记录
if err := s.cbcMessageRepository.Update(id, messageJson); err != nil {
return fmt.Errorf("failed to update CBC message: %w", err)
}
// 如果状态是ACTIVE发送更新请求
if msg.Status == model.CBCEventStatusActive {
if err := s.sendUpdateRequest(*msg); err != nil {
return fmt.Errorf("failed to send update request: %w", err)
}
}
return nil
}
// UpdateCBCMessage 更新CB消息
// @Description 更新CB消息
// @param msg CB消息对象
// @return error 错误信息
// @example
// UpdateCBCMessageDetail(msg)
func (s *CBCMessage) UpdateDetail(eventName, detail string) error {
if err := s.cbcMessageRepository.UpdateDetail(eventName, detail); err != nil {
return fmt.Errorf("failed to update CBC message detail: %w", err)
}
return nil
}
// DeleteCBCMessage 删除CB消息
// @Description 删除CB消息
// @param id 工单ID
// @return error 错误信息
// @example
// mfCBCMessageService.DeleteCBCMessage(12345)
func (s *CBCMessage) Delete(id int64) error {
// 先查询记录状态
var err error
var msg *model.CBCMessage
if msg, err = s.cbcMessageRepository.SelectById(id); err != nil {
if err == gorm.ErrRecordNotFound {
return fmt.Errorf("CBC message with ID %d not found", id)
}
return fmt.Errorf("failed to query CBC message: %w", err)
}
// 检查状态是否为ACTIVE
if msg.Status == model.CBCEventStatusActive {
return fmt.Errorf("cannot delete an active CBC message (ID: %d)", id)
}
// 执行删除操作
if err := s.cbcMessageRepository.Delete(id); err != nil {
return fmt.Errorf("failed to delete CBC message: %w", err)
}
return nil
}
// UpdateCBCMessageStatus 更新CB消息状态
// @Description 更新CB消息状态并根据状态变化发送相应的HTTP请求
// @param status 新状态
// @return error 错误信息
func (s *CBCMessage) UpdateStatus(id int64, status string) error {
newStatus := model.ParseCBCEventStatus(status)
// 查询所有需要更新的记录
var err error
var msg *model.CBCMessage
if msg, err = s.cbcMessageRepository.SelectById(id); err != nil {
if err == gorm.ErrRecordNotFound {
return fmt.Errorf("CBC message with ID %d not found", id)
}
return fmt.Errorf("failed to query CBC message: %w", err)
}
oldStatus := msg.Status
// 检查状态是否发生变化
if oldStatus == newStatus {
return fmt.Errorf("CBC message status is already %s", newStatus.Enum())
}
// 更新数据库状态
if err := s.cbcMessageRepository.UpdateStatus(id, newStatus); err != nil {
return fmt.Errorf("failed to update CBC message status: %w", err)
}
// 根据状态变化发送HTTP请求
if err := s.handleStatusChange(*msg, oldStatus, newStatus); err != nil {
// 记录错误但不中断处理其他消息
fmt.Printf("Failed to handle status change for message %d: %v\n", msg.Id, err)
}
return nil
}
// UpdateCBCMessageStatus 更新CB消息状态
// @Description 更新CB消息状态并根据状态变化发送相应的HTTP请求
// @param status 新状态
// @return error 错误信息
func (s *CBCMessage) UpdateStatusByNeId(neId string, status string) error {
newStatus := model.ParseCBCEventStatus(status)
// 查询所有需要更新的记录
msgs := make([]model.CBCMessage, 0)
if err := s.cbcMessageRepository.SelectByNeId(neId, &msgs); err != nil {
return fmt.Errorf("failed to query CB messages: %w", err)
}
for _, msg := range msgs {
oldStatus := msg.Status
// 检查状态是否发生变化
if oldStatus == newStatus {
continue // 状态没有变化,跳过
}
// 更新数据库状态
if err := s.cbcMessageRepository.UpdateStatus(msg.Id, newStatus); err != nil {
return fmt.Errorf("failed to update CBC message status: %w", err)
}
// 根据状态变化发送HTTP请求
if err := s.handleStatusChange(msg, oldStatus, newStatus); err != nil {
// 记录错误但不中断处理其他消息
fmt.Printf("Failed to handle status change for message %d: %v\n", msg.Id, err)
}
}
return nil
}
// UpdateCBCMessageStatus 更新CB消息状态
// @Description 更新CB消息状态并根据状态变化发送相应的HTTP请求
// @param status 新状态
// @return error 错误信息
func (s *CBCMessage) UpdateAllStatus(status string) error {
newStatus := model.ParseCBCEventStatus(status)
// 查询所有需要更新的记录
msgs := make([]model.CBCMessage, 0)
if err := s.cbcMessageRepository.Select(&msgs); err != nil {
return fmt.Errorf("failed to query CB messages: %w", err)
}
for _, msg := range msgs {
oldStatus := msg.Status
// 检查状态是否发生变化
if oldStatus == newStatus {
continue // 状态没有变化,跳过
}
// 更新数据库状态
if err := s.cbcMessageRepository.UpdateStatus(msg.Id, newStatus); err != nil {
return fmt.Errorf("failed to update CBC message status: %w", err)
}
// 根据状态变化发送HTTP请求
if err := s.handleStatusChange(msg, oldStatus, newStatus); err != nil {
// 记录错误但不中断处理其他消息
fmt.Printf("Failed to handle status change for message %d: %v\n", msg.Id, err)
}
}
return nil
}
// ExportXlsx 导出数据到 xlsx 文件
func (r CBCMessage) ExportXlsx(rows []model.CBCMessage, fileName, language string) (string, error) {
// 第一行表头标题
headerCells := map[string]string{
"A1": i18n.TKey(language, "alarm.export.alarmType"),
"B1": i18n.TKey(language, "alarm.export.origSeverity"),
"C1": i18n.TKey(language, "alarm.export.alarmTitle"),
"D1": i18n.TKey(language, "alarm.export.eventTime"),
"E1": i18n.TKey(language, "alarm.export.alarmId"),
"F1": i18n.TKey(language, "alarm.export.alarmCode"),
"G1": i18n.TKey(language, "ne.common.neType"),
"H1": i18n.TKey(language, "ne.common.neName"),
"I1": i18n.TKey(language, "ne.common.neId"),
}
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
cells := map[string]any{
"A" + idx: row.NeType,
"B" + idx: row.NeId,
"C" + idx: row.MessageJson, // 这里假设 MessageJson 已经是字符串格式
"D" + idx: row.Status.Enum(),
"E" + idx: row.Detail,
"F" + idx: time.Unix(row.CreatedAt, 0).Format(time.RFC3339),
"G" + idx: time.Unix(*row.UpdatedAt, 0).Format(time.RFC3339),
}
dataCells = append(dataCells, cells)
}
// 导出数据表格
return file.WriteSheet(headerCells, dataCells, fileName, "")
}
// handleStatusChange 处理状态变化时的HTTP请求
func (s *CBCMessage) handleStatusChange(msg model.CBCMessage, oldStatus, newStatus model.CBCEventStatus) error {
// 从NULL/INACTIVE状态修改为ACTIVE
if (oldStatus == model.CBCEventStatusNull || oldStatus == model.CBCEventStatusInactive) &&
newStatus == model.CBCEventStatusActive {
return s.sendActivateRequest(msg)
}
// 从ACTIVE更改为INACTIVE状态
if oldStatus == model.CBCEventStatusActive && newStatus == model.CBCEventStatusInactive {
return s.sendDeactivateRequest(msg)
}
return nil
}
// getCBCNetworkElement 获取CBC网元的IP和端口信息
// 这个方法需要根据你的实际网元管理系统来实现
func (s *CBCMessage) getCBCNetworkElement(neId string) (string, int64, error) {
// 查询网元信息
neInfo := neService.NewNeInfo.FindByNeTypeAndNeID("CBC", neId)
if neInfo.IP == "" {
return "", 0, fmt.Errorf("CBC network element not found for neId: %s", neId)
}
return neInfo.IP, neInfo.Port, nil
}
// CBCHTTPClient CBC网元HTTP客户端
type CBCHTTPClient struct {
client *http.Client
baseURL string
}
// NewCBCHTTPClient 创建CBC HTTP客户端
func NewCBCHTTPClient(baseURL string) *CBCHTTPClient {
return &CBCHTTPClient{
client: &http.Client{
Timeout: 10 * time.Second,
},
baseURL: baseURL,
}
}
// PostMessage 发送POST请求创建消息
func (c *CBCHTTPClient) PostMessage(messageData []byte) error {
url := fmt.Sprintf("%s/api/v1/cbe/message", c.baseURL)
return c.sendRequest("POST", url, messageData)
}
// PutMessage 发送PUT请求更新消息
func (c *CBCHTTPClient) PutMessage(messageData []byte) error {
url := fmt.Sprintf("%s/api/v1/cbe/message", c.baseURL)
return c.sendRequest("PUT", url, messageData)
}
// DeleteMessage 发送DELETE请求删除消息
func (c *CBCHTTPClient) DeleteMessage(eventName string, deletePayload []byte) error {
url := fmt.Sprintf("%s/api/v1/cbe/message/%s", c.baseURL, eventName)
return c.sendRequest("DELETE", url, deletePayload)
}
// sendRequest 发送HTTP请求
func (c *CBCHTTPClient) sendRequest(method, url string, body []byte) error {
req, err := http.NewRequest(method, url, bytes.NewReader(body))
if err != nil {
return fmt.Errorf("failed to create %s request: %w", method, err)
}
req.Header.Set("Content-Type", "application/json")
resp, err := c.client.Do(req)
if err != nil {
return fmt.Errorf("failed to send %s request: %w", method, err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("%s request failed with status: %d, body: %s",
method, resp.StatusCode, string(body))
}
return nil
}
// 在CBCMessageService中添加方法
func (s *CBCMessage) getCBCHTTPClient(neId string) (*CBCHTTPClient, error) {
cbcIP, cbcPort, err := s.getCBCNetworkElement(neId)
if err != nil {
return nil, fmt.Errorf("failed to get CBC network element info: %w", err)
}
baseURL := fmt.Sprintf("http://%s:%d", cbcIP, cbcPort)
return NewCBCHTTPClient(baseURL), nil
}
// 重构后的激活请求
func (s *CBCMessage) sendActivateRequest(msg model.CBCMessage) error {
client, err := s.getCBCHTTPClient(msg.NeId)
if err != nil {
return err
}
return client.PostMessage(msg.MessageJson)
}
// 重构后的更新请求
func (s *CBCMessage) sendUpdateRequest(msg model.CBCMessage) error {
client, err := s.getCBCHTTPClient(msg.NeId)
if err != nil {
return err
}
return client.PutMessage(msg.MessageJson)
}
// 重构后的停用请求
func (s *CBCMessage) sendDeactivateRequest(msg model.CBCMessage) error {
client, err := s.getCBCHTTPClient(msg.NeId)
if err != nil {
return err
}
// 解析和构造删除载荷的逻辑保持不变
var messageData map[string]interface{}
if err := json.Unmarshal(msg.MessageJson, &messageData); err != nil {
return fmt.Errorf("failed to parse message_json: %w", err)
}
eventName, ok := messageData["eventName"].(string)
if !ok || eventName == "" {
return fmt.Errorf("eventName not found in message_json")
}
deletePayload := make(map[string]interface{})
if messageIdProfile, exists := messageData["messageIdProfile"]; exists {
deletePayload["messageIdProfile"] = messageIdProfile
}
if warningAreaList, exists := messageData["warningAreaList"]; exists {
deletePayload["warningAreaList"] = warningAreaList
}
payloadBytes, err := json.Marshal(deletePayload)
if err != nil {
return fmt.Errorf("failed to marshal delete payload: %w", err)
}
return client.DeleteMessage(eventName, payloadBytes)
}

View File

@@ -696,3 +696,27 @@ func (s APIRestController) QuerySystemState(c *gin.Context) {
func (s APIRestController) NeConfigOMC(c *gin.Context) {
c.JSON(204, nil)
}
// @Description CBSManagement CB消息
type CBSState struct {
NeName string `json:"neName"` // 网元名称
RmUID string `json:"rmUID"` // 网元唯一标识
EventData []oamService.CBSEventData `json:"eventData"` // 事件数据
}
func (s APIRestController) ResolveCBSState(c *gin.Context) {
var state CBSState
if err := c.ShouldBindBodyWithJSON(&state); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
for _, eventData := range state.EventData {
if err := oamService.NewCBS.Resolve(eventData); err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
}
c.JSON(200, resp.Ok(nil))
}

View File

@@ -33,6 +33,7 @@ func Setup(router *gin.Engine) {
aprRestGroup.POST("/cdrManagement/v1/elementType/:elementTypeValue/objectType/cdrEvent", aprRest.ResolveCDR)
aprRestGroup.POST("/performanceManagement/v1/elementType/:elementTypeValue/objectType/kpiReport/:index", aprRest.ResolveKPI)
aprRestGroup.POST("/ueManagement/v1/elementType/:elementTypeValue/objectType/nbState", aprRest.ResolveNBState)
aprRestGroup.POST("/ueManagement/v1/elementType/:elementTypeValue/objectType/cbsState", aprRest.ResolveCBSState)
aprRestGroup.POST("/logManagement/v1/elementType/:elementTypeValue/objectType/ueEvent", aprRest.ResolveUENB)
router.POST("/upload-ue/v1/:eventType", aprRest.ResolveUENBByAMF) // AMF特殊上报
aprRestGroup.GET("/systemManagement/v1/elementType/:elementTypeValue/objectType/systemState", aprRest.QuerySystemState)

View File

@@ -0,0 +1,29 @@
package service
import (
neDataService "be.ems/src/modules/network_data/service"
neService "be.ems/src/modules/network_element/service"
)
// 实例化服务层 CDR 结构体
var NewCBS = &CBS{
neInfoService: neService.NewNeInfo,
cbcMessageService: neDataService.NewCBCMessage,
}
// CDR 消息处理
type CBS struct {
neInfoService *neService.NeInfo
cbcMessageService *neDataService.CBCMessage // CDR会话事件服务
}
type CBSEventData struct {
EventName string `json:"eventName"` // 事件名称
MessageId int64 `json:"messageId"` // 消息ID
Detail string `json:"detail"` // 详情
}
// Resolve 接收处理
func (s *CBS) Resolve(c CBSEventData) error {
return s.cbcMessageService.UpdateDetail(c.EventName, c.Detail)
}