add: SMF CDR
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"be.ems/lib/dborm"
|
||||
"be.ems/lib/global"
|
||||
@@ -14,13 +15,197 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
UriCDREvent = config.DefaultUriPrefix + "/cdrManagement/v1/elementType/{elementTypeValue}/objectType/cdrEvent"
|
||||
UriCDRFile = config.DefaultUriPrefix + "/cdrManagement/v1/elementType/{elementTypeValue}/objectType/cdrFile"
|
||||
UriIMSCDREvent = config.DefaultUriPrefix + "/cdrManagement/v1/elementType/ims/objectType/cdrEvent"
|
||||
UriIMSCDRFile = config.DefaultUriPrefix + "/cdrManagement/v1/elementType/ims/objectType/cdrFile"
|
||||
UriSMFCDREvent = config.DefaultUriPrefix + "/cdrManagement/v1/elementType/smf/objectType/cdrEvent"
|
||||
UriSMFCDRFile = config.DefaultUriPrefix + "/cdrManagement/v1/elementType/smf/objectType/cdrFile"
|
||||
|
||||
CustomUriCDREvent = config.UriPrefix + "/cdrManagement/v1/elementType/{elementTypeValue}/objectType/cdrEvent"
|
||||
CustomUriCDRFile = config.UriPrefix + "/cdrManagement/v1/elementType/{elementTypeValue}/objectType/cdrFile"
|
||||
CustomUriIMSCDREvent = config.UriPrefix + "/cdrManagement/v1/elementType/ims/objectType/cdrEvent"
|
||||
CustomUriIMSCDRFile = config.UriPrefix + "/cdrManagement/v1/elementType/ims/objectType/cdrFile"
|
||||
CustomUriSMFCDREvent = config.UriPrefix + "/cdrManagement/v1/elementType/ims/objectType/cdrEvent"
|
||||
CustomUriSMFCDRFile = config.UriPrefix + "/cdrManagement/v1/elementType/ims/objectType/cdrFile"
|
||||
)
|
||||
|
||||
// SMF CDR
|
||||
type CdrSubscriptionID struct {
|
||||
SubscriptionIDType string `json:"subscriptionIDType"`
|
||||
SubscriptionIDData string `json:"subscriptionIDData"`
|
||||
}
|
||||
|
||||
type CdrNetWorkFuctionInfomation struct {
|
||||
NetworkFunctionality string `json:"networkFunctionality"`
|
||||
NetworkFunctionName string `json:"networkFunctionName,omitempty"`
|
||||
NetworkFunctionIPv4Address string `json:"networkFunctionIPv4Address,omitempty"`
|
||||
NetworkFunctionIPv6Address string `json:"networkFunctionIPv6Address,omitempty"`
|
||||
}
|
||||
|
||||
type SMFTrigger string
|
||||
|
||||
const (
|
||||
TimeThresholdReached SMFTrigger = "timeThresholdReached"
|
||||
VolumeThresholdReached SMFTrigger = "volumeThresholdReached"
|
||||
UnitThresholdReached SMFTrigger = "unitThresholdReached"
|
||||
TimeQuotaExhausted SMFTrigger = "timeQuotaExhausted"
|
||||
VolumeQuotaExhausted SMFTrigger = "volumeQuotaExhausted"
|
||||
UnitQuotaExhausted SMFTrigger = "unitQuotaExhausted"
|
||||
ExpiryOfQuotaValidityTime SMFTrigger = "expiryOfQuotaValidityTime"
|
||||
ExpiryOfQuotaHoldingTime SMFTrigger = "expiryOfQuotaHoldingTime"
|
||||
EndOfPDUSession SMFTrigger = "endOfPDUSession"
|
||||
)
|
||||
|
||||
type CdrSMFTrigger struct {
|
||||
SMFTrigger SMFTrigger `json:"sMFTrigger"`
|
||||
}
|
||||
|
||||
type CdrQuotaManagementIndicator string
|
||||
|
||||
// List of QuotaManagementIndicator
|
||||
const (
|
||||
Cdr_QMI_ONLINE_CHARGING CdrQuotaManagementIndicator = "ONLINE_CHARGING"
|
||||
Cdr_QMI_OFFLINE_CHARGING CdrQuotaManagementIndicator = "OFFLINE_CHARGING"
|
||||
Cdr_QMI_QUOTA_MANAGEMENT_SUSPENDED CdrQuotaManagementIndicator = "QUOTA_MANAGEMENT_SUSPENDED"
|
||||
)
|
||||
|
||||
type CdrUsedUnitContainer struct {
|
||||
ServiceIdentifier *int32 `json:"serviceIdentifier,omitempty"`
|
||||
QuotaManagementIndicatorExt CdrQuotaManagementIndicator `json:"quotaManagementIndicatorExt,omitempty"`
|
||||
Triggers []SMFTrigger `json:"triggers,omitempty"`
|
||||
TriggerTimestamp *time.Time `json:"triggerTimestamp,omitempty"`
|
||||
Time *uint32 `json:"time,omitempty"`
|
||||
DataTotalVolume *uint64 `json:"dataTotalVolume,omitempty"`
|
||||
DataVolumeUplink *uint64 `json:"dataVolumeUplink,omitempty"`
|
||||
DataVolumeDownlink *uint64 `json:"dataVolumeDownlink,omitempty"`
|
||||
ServiceSpecificUnits *uint64 `json:"serviceSpecificUnits,omitempty"`
|
||||
EventTimeStamp *time.Time `json:"eventTimeStamp,omitempty"`
|
||||
LocalSequenceNumber int32 `json:"localSequenceNumber"`
|
||||
//PDUContainerInformation *PduContainerInformation `json:"pDUContainerInformation,omitempty"`
|
||||
//NSPAContainerInformation *NspaContainerInformation `json:"nSPAContainerInformation,omitempty"`
|
||||
}
|
||||
|
||||
type CdrMultipleUnitUsage struct {
|
||||
RatingGroup uint32 `json:"ratingGroup" yaml:"ratingGroup" bson:"ratingGroup" mapstructure:"RatingGroup"`
|
||||
UsedUnitContainer []CdrUsedUnitContainer `json:"usedUnitContainer,omitempty" yaml:"usedUnitContainer" bson:"usedUnitContainer" mapstructure:"UsedUnitContainer"`
|
||||
//UPFID string `json:"uPFID,omitempty" yaml:"uPFID" bson:"uPFID" mapstructure:"UPFID"`
|
||||
}
|
||||
|
||||
type CdrSubscriberEquipmentNumber struct {
|
||||
SubscriberEquipmentNumberType string `json:"subscriberEquipmentNumberType"`
|
||||
SubscriberEquipmentNumberData string `json:"subscriberEquipmentNumberData"`
|
||||
}
|
||||
|
||||
type CdrNetworkSliceInstanceID struct {
|
||||
SST int32 `json:"sST"`
|
||||
|
||||
SD string `json:"sD,omitempty"`
|
||||
}
|
||||
|
||||
type CdrPduAddress struct {
|
||||
PDUIPv4Address string `json:"pDUIPv4Address,omitempty"`
|
||||
PDUIPv6AddresswithPrefix string `json:"pDUIPv6AddresswithPrefix,omitempty"`
|
||||
IPV4dynamicAddressFlag bool `json:"iPV4dynamicAddressFlag,omitempty"`
|
||||
IPV6dynamicPrefixFlag bool `json:"iPv6dynamicPrefixFlag,omitempty"`
|
||||
}
|
||||
|
||||
type CdrArp struct {
|
||||
PriorityLevel int32 `json:"priorityLevel"`
|
||||
PreemptionCapability string `json:"preemptionCapability"`
|
||||
PreemptionVulnerability string `json:"preemptionVulnerability"`
|
||||
}
|
||||
|
||||
type CdrAuthorizedQosInformation struct {
|
||||
FiveQi int `json:"fiveQi"`
|
||||
ARP *CdrArp `json:"aRP,omitempty"`
|
||||
PriorityLevel *int32 `json:"priorityLevel,omitempty"`
|
||||
AverWindow *int32 `json:"averWindow,omitempty"`
|
||||
MaxDataBurstVol *int32 `json:"maxDataBurstVol,omitempty"`
|
||||
}
|
||||
|
||||
type CdrSubscribedDefaultQos struct {
|
||||
FiveQi int32 `json:"fiveQi,omitempty"`
|
||||
ARP CdrArp `json:"aRP,omitempty"`
|
||||
PriorityLevel *int32 `json:"priorityLevel,omitempty"`
|
||||
}
|
||||
|
||||
type CdrSessionAmbr struct {
|
||||
Uplink string `json:"uplink"`
|
||||
|
||||
Downlink string `json:"downlink"`
|
||||
}
|
||||
|
||||
type CdrPDUSessionChargingInformation struct {
|
||||
PDUSessionChargingID int32 `json:"pDUSessionChargingID"`
|
||||
UserIdentifier string `json:"userIdentifier,omitempty"` // isdn
|
||||
UserEquipmentInfo *CdrSubscriberEquipmentNumber `json:"userEquipmentInfo,omitempty"` // imei/imeisv
|
||||
//UserLocationInfomation *UserLocation `json:"userLocationinfo,omitempty"`
|
||||
UserRoamerInOut string `json:"userRoamerInOut,omitempty"`
|
||||
PDUSessionId int32 `json:"pDUSessionId"`
|
||||
NetworkSliceInstanceID *CdrNetworkSliceInstanceID `json:"networkSliceInstanceID,omitempty"`
|
||||
//PDUType PduSessionType `json:"pDUType,omitempty"`
|
||||
SSCMode string `json:"sSCMode,omitempty"`
|
||||
DNNID string `json:"dNNID"`
|
||||
SUPIPLMNIdentifier string `json:"sUPIPLMNIdentifier,omitempty"`
|
||||
//ServingNetworkFunctionID *ServingNetworkFunctionId `json:"servingNetworkFunctionID,omitempty"`
|
||||
//RATType RatType `json:"rATType,omitempty"`
|
||||
DataNetworkNameIdentifier string `json:"dataNetworkNameIdentifier,omitempty"`
|
||||
PDUAddress CdrPduAddress `json:"pDUAddress,omitempty"`
|
||||
AuthorizedQoSInformation *CdrAuthorizedQosInformation `json:"authorizedQoSInformation,omitempty"`
|
||||
UETimeZone string `json:"uETimeZone,omitempty"`
|
||||
PDUSessionstartTime *time.Time `json:"pDUSessionstartTime,omitempty"`
|
||||
PDUSessionstopTime *time.Time `json:"pDUSessionstopTime,omitempty"`
|
||||
Diagnostics *int `json:"diagnostics,omitempty"`
|
||||
ChargingCharacteristics string `json:"chargingCharacteristics,omitempty"`
|
||||
ChChSelectionMode string `json:"chChSelectionMode,omitempty"`
|
||||
ThreeGPPPSDataOffStatus string `json:"threeGPPPSDataOffStatus,omitempty"`
|
||||
//RANSecondaryRATUsageReport *RanSecondaryRatUsageReport `json:"rANSecondaryRATUsageReport,omitempty"`
|
||||
SubscribedQoSInformation *CdrSubscribedDefaultQos `json:"subscribedQoSInformation,omitempty"`
|
||||
AuthorizedSessionAMBR *CdrSessionAmbr `json:"authorizedSessionAMBR,omitempty"`
|
||||
SubscribedSessionAMBR *CdrSessionAmbr `json:"subscribedSessionAMBR,omitempty"`
|
||||
ServingCNPLMNID string `json:"servingCNPLMNID,omitempty"`
|
||||
DnnSelectionMode string `json:"dnnSelectionMode,omitempty"`
|
||||
HomeProvidedChargingID int32 `json:"homeProvidedChargingID,omitempty"`
|
||||
|
||||
//MAPDUNon3GPPUserLocationInfo *UserLocation `json:"mAPDUNon3GPPUserLocationInfo,omitempty" yaml:"mAPDUNon3GPPUserLocationInfo" bson:"mAPDUNon3GPPUserLocationInfo" mapstructure:"MAPDUNon3GPPUserLocationInfo"`
|
||||
//PresenceReportingAreaInformation map[string]PresenceInfo `json:"presenceReportingAreaInformation,omitempty" yaml:"presenceReportingAreaInformation" bson:"presenceReportingAreaInformation" mapstructure:"PresenceReportingAreaInformation"`
|
||||
}
|
||||
|
||||
type CauseForRecordClosing string
|
||||
|
||||
const (
|
||||
NormalRelease CauseForRecordClosing = "normalRelease"
|
||||
PartialRecord CauseForRecordClosing = "partialRecord"
|
||||
AbnormalRelease CauseForRecordClosing = "abnormalRelease"
|
||||
CAMELInitCallRelease CauseForRecordClosing = "cAMELInitCallRelease"
|
||||
VolumeLimit CauseForRecordClosing = "volumeLimit"
|
||||
TimeLimit CauseForRecordClosing = "timeLimit"
|
||||
ServingNodeChange CauseForRecordClosing = "servingNodeChange"
|
||||
MaxChangeCond CauseForRecordClosing = "maxChangeCond"
|
||||
ManagementIntervention CauseForRecordClosing = "managementIntervention"
|
||||
IntraSGSNIntersystemChange CauseForRecordClosing = "intraSGSNIntersystemChange"
|
||||
RATChange CauseForRecordClosing = "rATChange"
|
||||
MSTimeZoneChange CauseForRecordClosing = "mSTimeZoneChange"
|
||||
SGSNPLMNIDChange CauseForRecordClosing = "sGSNPLMNIDChange "
|
||||
SGWChange CauseForRecordClosing = "sGWChange"
|
||||
APNAMBRChange CauseForRecordClosing = "aPNAMBRChange"
|
||||
)
|
||||
|
||||
type ChargingRecord struct {
|
||||
RecordType string `json:"recordType"`
|
||||
ChargingID int `json:"chargingID"`
|
||||
RecordingNetworkFunctionID string `json:"recordingNetworkFunctionID"` // UUID
|
||||
SubscriberIdentifier CdrSubscriptionID `json:"subscriberIdentifier,omitempty"`
|
||||
NFunctionConsumerInformation CdrNetWorkFuctionInfomation `json:"nFunctionConsumerInformation"`
|
||||
Triggers []CdrSMFTrigger `json:"triggers,omitempty"`
|
||||
ListOfMultipleUnitUsage []CdrMultipleUnitUsage `json:"listOfMultipleUnitUsage,omitempty"`
|
||||
RecordOpeningTime string `json:"recordOpeningTime"`
|
||||
Duration int `json:"duration"`
|
||||
RecordSequenceNumber int `json:"recordSequenceNumber,omitempty"`
|
||||
CauseForRecClosing CauseForRecordClosing `json:"causeForRecClosing"`
|
||||
Diagnostics *int `json:"diagnostics,omitempty"`
|
||||
LocalRecordSequenceNumber int `json:"localRecordSequenceNumber,omitempty"`
|
||||
PDUSessionChargingInformation CdrPDUSessionChargingInformation `json:"pDUSessionChargingInformation,omitempty"`
|
||||
InvocationTimestamp string `json:"invocationTimestamp,omitempty"`
|
||||
}
|
||||
|
||||
type CDREvent struct {
|
||||
NeType string `json:"neType" xorm:"ne_type"`
|
||||
NeName string `json:"neName" xorm:"ne_name"`
|
||||
@@ -29,8 +214,8 @@ type CDREvent struct {
|
||||
CDR map[string]any `json:"CDR" xorm:"cdr_json"`
|
||||
}
|
||||
|
||||
func PostCDREventFromNF(w http.ResponseWriter, r *http.Request) {
|
||||
log.Info("PostCDREventFromNF processing... ")
|
||||
func PostCDREventFromIMS(w http.ResponseWriter, r *http.Request) {
|
||||
log.Info("PostCDREventFromIMS processing... ")
|
||||
|
||||
// body, err := io.ReadAll(io.LimitReader(r.Body, global.RequestBodyMaxLen))
|
||||
body, err := io.ReadAll(io.LimitReader(r.Body, global.RequestBodyMaxLen))
|
||||
@@ -49,9 +234,9 @@ func PostCDREventFromNF(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
log.Trace("cdrEvent:", cdrEvent)
|
||||
|
||||
affected, err := dborm.XormInsertTableOne("cdr_event", cdrEvent)
|
||||
affected, err := dborm.XormInsertTableOne("cdr_event_ims", cdrEvent)
|
||||
if err != nil && affected <= 0 {
|
||||
log.Error("Failed to insert cdr_event:", err)
|
||||
log.Error("Failed to insert cdr_event_ims:", err)
|
||||
services.ResponseInternalServerError500ProcessError(w, err)
|
||||
return
|
||||
}
|
||||
@@ -65,3 +250,40 @@ func PostCDREventFromNF(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
services.ResponseStatusOK204NoContent(w)
|
||||
}
|
||||
|
||||
func PostCDREventFromSMF(w http.ResponseWriter, r *http.Request) {
|
||||
log.Info("PostCDREventFromSMF processing... ")
|
||||
|
||||
// body, err := io.ReadAll(io.LimitReader(r.Body, global.RequestBodyMaxLen))
|
||||
body, err := io.ReadAll(io.LimitReader(r.Body, global.RequestBodyMaxLen))
|
||||
if err != nil {
|
||||
log.Error("Faile to io.ReadAll: ", err)
|
||||
services.ResponseNotFound404UriNotExist(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
cdrEvent := new(CDREvent)
|
||||
err = json.Unmarshal(body, &cdrEvent)
|
||||
if cdrEvent.NeType == "" || err != nil {
|
||||
log.Error("Failed to Unmarshal cdrEvent:", err)
|
||||
services.ResponseInternalServerError500ProcessError(w, err)
|
||||
return
|
||||
}
|
||||
log.Trace("cdrEvent:", cdrEvent)
|
||||
|
||||
affected, err := dborm.XormInsertTableOne("cdr_event_smf", cdrEvent)
|
||||
if err != nil && affected <= 0 {
|
||||
log.Error("Failed to insert cdr_event_smf:", err)
|
||||
services.ResponseInternalServerError500ProcessError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 推送到ws订阅组
|
||||
// if v, ok := cdrEvent.CDR["recordType"]; ok {
|
||||
// if v == "MOC" || v == "MTSM" {
|
||||
// wsService.NewWSSendImpl.ByGroupID(wsService.GROUP_IMS_CDR, cdrEvent)
|
||||
// }
|
||||
// }
|
||||
|
||||
services.ResponseStatusOK204NoContent(w)
|
||||
}
|
||||
|
||||
@@ -696,3 +696,33 @@ func IsRpmOrDebPackage(filePath string) int {
|
||||
|
||||
return fileType
|
||||
}
|
||||
|
||||
func RecurseStructToMap(obj any) map[string]any {
|
||||
out := make(map[string]any)
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
// 递归函数,用于处理嵌套结构体
|
||||
var recurse func(reflect.Value) any
|
||||
recurse = func(value reflect.Value) any {
|
||||
if value.Kind() == reflect.Struct {
|
||||
nestedOut := make(map[string]any)
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
nestedOut[value.Type().Field(i).Name] = recurse(value.Field(i))
|
||||
}
|
||||
return nestedOut
|
||||
} else if value.Kind() == reflect.Ptr {
|
||||
return recurse(value.Elem())
|
||||
}
|
||||
return value.Interface()
|
||||
}
|
||||
|
||||
t := v.Type()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
f := v.Field(i)
|
||||
out[t.Field(i).Name] = recurse(f)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -300,8 +300,13 @@ func init() {
|
||||
Register("GET", ue.UriNSSFSubscriptions, ue.GetSubscriptionsFromNSSF, nil)
|
||||
Register("GET", ue.CustomUriNSSFSubscriptions, ue.GetSubscriptionsFromNSSF, nil)
|
||||
|
||||
Register("POST", cdr.UriCDREvent, cdr.PostCDREventFromNF, nil)
|
||||
Register("POST", cdr.CustomUriCDREvent, cdr.PostCDREventFromNF, nil)
|
||||
// ims cdr event
|
||||
Register("POST", cdr.UriIMSCDREvent, cdr.PostCDREventFromIMS, nil)
|
||||
Register("POST", cdr.CustomUriIMSCDREvent, cdr.PostCDREventFromIMS, nil)
|
||||
|
||||
// smf cdr event
|
||||
Register("POST", cdr.UriSMFCDREvent, cdr.PostCDREventFromSMF, nil)
|
||||
Register("POST", cdr.CustomUriSMFCDREvent, cdr.PostCDREventFromSMF, nil)
|
||||
|
||||
// UE event
|
||||
//Register("POST", event.UriUEEvent, event.PostUEEventFromAMF, nil)
|
||||
|
||||
80
src/modules/network_data/controller/smf.go
Normal file
80
src/modules/network_data/controller/smf.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/utils/ctx"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
"be.ems/src/framework/vo/result"
|
||||
"be.ems/src/modules/network_data/model"
|
||||
neDataService "be.ems/src/modules/network_data/service"
|
||||
neService "be.ems/src/modules/network_element/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 实例化控制层 IMSController 结构体
|
||||
var NewSMFController = &SMFController{
|
||||
neInfoService: neService.NewNeInfoImpl,
|
||||
cdrEventService: neDataService.NewSMFCDREventImpl,
|
||||
}
|
||||
|
||||
// 网元IMS
|
||||
//
|
||||
// PATH /ims
|
||||
type SMFController struct {
|
||||
// 网元信息服务
|
||||
neInfoService neService.INeInfo
|
||||
// SMF CDR会话事件服务
|
||||
cdrEventService neDataService.SMFCDREvent
|
||||
}
|
||||
|
||||
// CDR会话列表
|
||||
//
|
||||
// GET /cdr/list
|
||||
func (s *SMFController) CDRList(c *gin.Context) {
|
||||
language := ctx.AcceptLanguage(c)
|
||||
var querys model.SMFCDREventQuery
|
||||
if err := c.ShouldBindQuery(&querys); err != nil {
|
||||
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
|
||||
return
|
||||
}
|
||||
|
||||
// 查询网元获取IP
|
||||
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(querys.NeType, querys.NeID)
|
||||
if neInfo.NeId != querys.NeID || neInfo.IP == "" {
|
||||
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
|
||||
return
|
||||
}
|
||||
querys.RmUID = neInfo.RmUID
|
||||
|
||||
// 查询数据
|
||||
data := s.cdrEventService.SelectPage(querys)
|
||||
c.JSON(200, result.Ok(data))
|
||||
}
|
||||
|
||||
// CDR会话删除
|
||||
//
|
||||
// DELETE /cdr/:cdrIds
|
||||
func (s *SMFController) CDRRemove(c *gin.Context) {
|
||||
language := ctx.AcceptLanguage(c)
|
||||
cdrIds := c.Param("cdrIds")
|
||||
if cdrIds == "" {
|
||||
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
|
||||
return
|
||||
}
|
||||
// 处理字符转id数组后去重
|
||||
ids := strings.Split(cdrIds, ",")
|
||||
uniqueIDs := parse.RemoveDuplicates(ids)
|
||||
if len(uniqueIDs) <= 0 {
|
||||
c.JSON(200, result.Err(nil))
|
||||
return
|
||||
}
|
||||
rows, err := s.cdrEventService.DeleteByIds(uniqueIDs)
|
||||
if err != nil {
|
||||
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
|
||||
return
|
||||
}
|
||||
msg := i18n.TTemplate(language, "app.common.deleteSuccess", map[string]any{"num": rows})
|
||||
c.JSON(200, result.OkMsg(msg))
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package model
|
||||
|
||||
import "time"
|
||||
|
||||
// CDREvent CDR会话对象 cdr_event
|
||||
// CDREvent CDR会话对象 cdr_event_ims/cdr_event_smf
|
||||
type CDREvent struct {
|
||||
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
|
||||
NeType string `json:"neType" gorm:"column:ne_type"`
|
||||
@@ -15,7 +15,7 @@ type CDREvent struct {
|
||||
|
||||
// CDREventQuery CDR会话对象查询参数结构体
|
||||
type CDREventQuery struct {
|
||||
NeType string `json:"neType" form:"neType" binding:"required"` // 网元类型, 暂时支持IMS
|
||||
NeType string `json:"neType" form:"neType" binding:"required"` // 网元类型
|
||||
NeID string `json:"neId" form:"neId" binding:"required"`
|
||||
RmUID string `json:"rmUID" form:"rmUID"`
|
||||
RecordType string `json:"recordType" form:"recordType"` // 记录行为 MOC MTC MOSM MTSM
|
||||
|
||||
35
src/modules/network_data/model/cdr_event_smf.go
Normal file
35
src/modules/network_data/model/cdr_event_smf.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// CDREvent CDR会话对象 cdr_event_smf
|
||||
type CDREventSMF struct {
|
||||
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
|
||||
NeType string `json:"neType" gorm:"column:ne_type"`
|
||||
NeName string `json:"neName" gorm:"column:ne_name"`
|
||||
RmUID string `json:"rmUID" gorm:"column:rm_uid"`
|
||||
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"`
|
||||
RecordType string `json:"recordType" gorm:"column:record_type"`
|
||||
ChargingID string `json:"chargingID" gorm:"column:charging_id"`
|
||||
SubscriberID string `json:"subscriberID" gorm:"column:subscriber_id"`
|
||||
Duration string `json:"duration" gorm:"column:duration"`
|
||||
DataVolumeUplink string `json:"dataVolumeUplink" gorm:"column:data_volume_uplink"`
|
||||
DataVolumeDownlink string `json:"dataVolumeDownlink" gorm:"column:data_volume_downlink"`
|
||||
DataTotalVolume string `json:"dataTotalVolume" gorm:"column:data_total_volume"`
|
||||
PDUAddress string `json:"pduAddress" gorm:"column:pdu_address"`
|
||||
CreatedAt time.Time `json:"createdAt" gorm:"column:created_at;default:CURRENT_TIMESTAMP"`
|
||||
}
|
||||
|
||||
type SMFCDREventQuery struct {
|
||||
NeType string `json:"neType" form:"neType" binding:"required"` // SMF
|
||||
NeID string `json:"neId" form:"neId" binding:"required"`
|
||||
RmUID string `json:"rmUID" form:"rmUID"`
|
||||
RecordType string `json:"recordType" form:"recordType"`
|
||||
SubscriberID string `json:"subscriberID" form:"subscriberID"`
|
||||
StartTime string `json:"startTime" form:"startTime"`
|
||||
EndTime string `json:"endTime" form:"endTime"`
|
||||
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=timestamp"` // 排序字段,填写结果字段
|
||||
SortOrder string `json:"sortOrder" form:"sortOrder" binding:"omitempty,oneof=asc desc"` // 排序升降序,asc desc
|
||||
PageNum int64 `json:"pageNum" form:"pageNum" binding:"required"`
|
||||
PageSize int64 `json:"pageSize" form:"pageSize" binding:"required"`
|
||||
}
|
||||
@@ -57,6 +57,22 @@ func Setup(router *gin.Engine) {
|
||||
)
|
||||
}
|
||||
|
||||
// 网元SMF
|
||||
smfGroup := neDataGroup.Group("/smf")
|
||||
{
|
||||
// CDR会话事件列表
|
||||
smfGroup.GET("/cdr/list",
|
||||
middleware.PreAuthorize(nil),
|
||||
controller.NewSMFController.CDRList,
|
||||
)
|
||||
// CDR会话删除
|
||||
smfGroup.DELETE("/cdr/:cdrIds",
|
||||
middleware.PreAuthorize(nil),
|
||||
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.smfCDR", collectlogs.BUSINESS_TYPE_DELETE)),
|
||||
controller.NewSMFController.CDRRemove,
|
||||
)
|
||||
}
|
||||
|
||||
// 网元AMF
|
||||
amfGroup := neDataGroup.Group("/amf")
|
||||
{
|
||||
|
||||
@@ -13,3 +13,15 @@ type ICDREvent interface {
|
||||
// DeleteByIds 批量删除信息
|
||||
DeleteByIds(cdrIds []string) int64
|
||||
}
|
||||
|
||||
// SMF CDR Event
|
||||
type SMFCDREvent interface {
|
||||
// SelectPage 根据条件分页查询
|
||||
SelectPage(querys model.SMFCDREventQuery) map[string]any
|
||||
|
||||
// SelectByIds 通过ID查询
|
||||
SelectByIds(cdrIds []string) []model.CDREventSMF
|
||||
|
||||
// DeleteByIds 批量删除信息
|
||||
DeleteByIds(cdrIds []string) int64
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
// 实例化数据层 CDREventImpl 结构体
|
||||
var NewCDREventImpl = &CDREventImpl{
|
||||
selectSql: `select id, ne_type, ne_name, rm_uid, timestamp, cdr_json, created_at from cdr_event`,
|
||||
selectSql: `select id, ne_type, ne_name, rm_uid, timestamp, cdr_json, created_at from cdr_event_ims`,
|
||||
|
||||
resultMap: map[string]string{
|
||||
"id": "ID",
|
||||
@@ -35,6 +35,36 @@ type CDREventImpl struct {
|
||||
resultMap map[string]string
|
||||
}
|
||||
|
||||
// Instance of SMF CDREventImpl
|
||||
var NewSMFCDREventImpl = &SMFCDREventImpl{
|
||||
selectSql: `select id, ne_type, ne_name, rm_uid, timestamp, JSON_EXTRACT(cdr_json, '$.recordType') AS record_type, JSON_EXTRACT(cdr_json, '$.chargingID') AS charging_id, JSON_EXTRACT(cdr_json, '$.subscriberIdentifier.subscriptionIDData') AS subscriber_id, JSON_EXTRACT(cdr_json, '$.duration') AS duration, JSON_EXTRACT(cdr_json, '$.listOfMultipleUnitUsage[*].usedUnitContainer[*].dataVolumeUplink') AS data_volume_uplink, JSON_EXTRACT(cdr_json, '$.listOfMultipleUnitUsage[*].usedUnitContainer[*].dataVolumeDownlink') AS data_volume_downlink, JSON_EXTRACT(cdr_json, '$.listOfMultipleUnitUsage[*].usedUnitContainer[*].dataTotalVolume') AS data_total_volume, JSON_EXTRACT(cdr_json, '$.pDUSessionChargingInformation.pDUAddress') AS pdu_address, created_at from cdr_event_smf`,
|
||||
|
||||
resultMap: map[string]string{
|
||||
"id": "ID",
|
||||
"ne_type": "NeType",
|
||||
"ne_name": "NeName",
|
||||
"rm_uid": "RmUID",
|
||||
"timestamp": "Timestamp",
|
||||
"record_type": "RecordType",
|
||||
"charging_id": "ChargingID",
|
||||
"subscriber_id": "SubscriberID",
|
||||
"duration": "Duration",
|
||||
"data_volume_uplink": "DataVolumeUplink",
|
||||
"data_volume_downlink": "DataVolumeDownlink",
|
||||
"data_total_volume": "DataTotalVolume",
|
||||
"pdu_address": "PDUAddress",
|
||||
"created_at": "CreatedAt",
|
||||
},
|
||||
}
|
||||
|
||||
// CDREventImpl CDR会话事件 数据层处理
|
||||
type SMFCDREventImpl struct {
|
||||
// 查询视图对象SQL
|
||||
selectSql string
|
||||
// 结果字段与实体映射
|
||||
resultMap map[string]string
|
||||
}
|
||||
|
||||
// convertResultRows 将结果记录转实体结果组
|
||||
func (r *CDREventImpl) convertResultRows(rows []map[string]any) []model.CDREvent {
|
||||
arr := make([]model.CDREvent, 0)
|
||||
@@ -102,7 +132,7 @@ func (r *CDREventImpl) SelectPage(querys model.CDREventQuery) map[string]any {
|
||||
}
|
||||
|
||||
// 查询数量 长度为0直接返回
|
||||
totalSql := "select count(1) as 'total' from cdr_event"
|
||||
totalSql := "select count(1) as 'total' from cdr_event_ims"
|
||||
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
|
||||
if err != nil {
|
||||
logger.Errorf("total err => %v", err)
|
||||
@@ -164,7 +194,146 @@ func (r *CDREventImpl) SelectByIds(cdrIds []string) []model.CDREvent {
|
||||
// DeleteByIds 批量删除信息
|
||||
func (r *CDREventImpl) DeleteByIds(cdrIds []string) int64 {
|
||||
placeholder := repo.KeyPlaceholderByQuery(len(cdrIds))
|
||||
sql := "delete from cdr_event where id in (" + placeholder + ")"
|
||||
sql := "delete from cdr_event_ims where id in (" + placeholder + ")"
|
||||
parameters := repo.ConvertIdsSlice(cdrIds)
|
||||
results, err := datasource.ExecDB("", sql, parameters)
|
||||
if err != nil {
|
||||
logger.Errorf("delete err => %v", err)
|
||||
return 0
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// convertResultRows 将结果记录转实体结果组
|
||||
func (r *SMFCDREventImpl) convertResultRows(rows []map[string]any) []model.CDREventSMF {
|
||||
arr := make([]model.CDREventSMF, 0)
|
||||
for _, row := range rows {
|
||||
item := model.CDREventSMF{}
|
||||
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 *SMFCDREventImpl) SelectPage(querys model.SMFCDREventQuery) map[string]any {
|
||||
// 查询条件拼接
|
||||
var conditions []string
|
||||
var params []any
|
||||
if querys.NeType != "" {
|
||||
conditions = append(conditions, "ne_type = ?")
|
||||
params = append(params, querys.NeType)
|
||||
}
|
||||
if querys.RmUID != "" {
|
||||
conditions = append(conditions, "rm_uid = ?")
|
||||
params = append(params, querys.RmUID)
|
||||
}
|
||||
if querys.StartTime != "" {
|
||||
conditions = append(conditions, "timestamp >= ?")
|
||||
beginDate := date.ParseStrToDate(querys.StartTime, date.YYYY_MM_DD_HH_MM_SS)
|
||||
params = append(params, beginDate.Unix())
|
||||
}
|
||||
if querys.EndTime != "" {
|
||||
conditions = append(conditions, "timestamp <= ?")
|
||||
endDate := date.ParseStrToDate(querys.EndTime, date.YYYY_MM_DD_HH_MM_SS)
|
||||
params = append(params, endDate.Unix())
|
||||
}
|
||||
if querys.RecordType != "" {
|
||||
conditions = append(conditions, "JSON_EXTRACT(cdr_json, '$.recordType') = ?")
|
||||
params = append(params, querys.RecordType)
|
||||
}
|
||||
if querys.SubscriberID != "" {
|
||||
conditions = append(conditions, "JSON_EXTRACT(cdr_json, '$.subscriberIdentifier.subscriptionIDData') = ?")
|
||||
params = append(params, querys.SubscriberID)
|
||||
}
|
||||
// if querys.RecordType != "" {
|
||||
// recordTypes := strings.Split(querys.RecordType, ",")
|
||||
// placeholder := repo.KeyPlaceholderByQuery(len(recordTypes))
|
||||
// conditions = append(conditions, fmt.Sprintf("JSON_EXTRACT(cdr_json, '$.recordType') in (%s)", placeholder))
|
||||
// for _, recordType := range recordTypes {
|
||||
// params = append(params, recordType)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 构建查询条件语句
|
||||
whereSql := ""
|
||||
if len(conditions) > 0 {
|
||||
whereSql += " where " + strings.Join(conditions, " and ")
|
||||
}
|
||||
|
||||
result := map[string]any{
|
||||
"total": 0,
|
||||
"rows": []model.CDREventSMF{},
|
||||
}
|
||||
|
||||
// 查询数量 长度为0直接返回
|
||||
totalSql := "select count(1) as 'total' from cdr_event_smf"
|
||||
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
|
||||
if err != nil {
|
||||
logger.Errorf("total err => %v", err)
|
||||
return result
|
||||
}
|
||||
total := parse.Number(totalRows[0]["total"])
|
||||
if total == 0 {
|
||||
return result
|
||||
} else {
|
||||
result["total"] = total
|
||||
}
|
||||
|
||||
// 分页
|
||||
pageNum, pageSize := repo.PageNumSize(querys.PageNum, querys.PageSize)
|
||||
pageSql := " limit ?,? "
|
||||
params = append(params, pageNum*pageSize)
|
||||
params = append(params, pageSize)
|
||||
|
||||
// 排序
|
||||
orderSql := ""
|
||||
if querys.SortField != "" {
|
||||
sortSql := querys.SortField
|
||||
if querys.SortOrder != "" {
|
||||
if querys.SortOrder == "desc" {
|
||||
sortSql += " desc "
|
||||
} else {
|
||||
sortSql += " asc "
|
||||
}
|
||||
}
|
||||
orderSql = fmt.Sprintf(" order by id desc, %s ", sortSql)
|
||||
}
|
||||
|
||||
// 查询数据
|
||||
querySql := r.selectSql + whereSql + orderSql + pageSql
|
||||
results, err := datasource.RawDB("", querySql, params)
|
||||
if err != nil {
|
||||
logger.Errorf("query err => %v", err)
|
||||
}
|
||||
|
||||
// 转换实体
|
||||
result["rows"] = r.convertResultRows(results)
|
||||
return result
|
||||
}
|
||||
|
||||
// SelectByIds 通过ID查询
|
||||
func (r *SMFCDREventImpl) SelectByIds(cdrIds []string) []model.CDREventSMF {
|
||||
placeholder := repo.KeyPlaceholderByQuery(len(cdrIds))
|
||||
querySql := r.selectSql + " where id in (" + placeholder + ")"
|
||||
parameters := repo.ConvertIdsSlice(cdrIds)
|
||||
results, err := datasource.RawDB("", querySql, parameters)
|
||||
if err != nil {
|
||||
logger.Errorf("query err => %v", err)
|
||||
return []model.CDREventSMF{}
|
||||
}
|
||||
// 转换实体
|
||||
return r.convertResultRows(results)
|
||||
}
|
||||
|
||||
// DeleteByIds 批量删除信息
|
||||
func (r *SMFCDREventImpl) DeleteByIds(cdrIds []string) int64 {
|
||||
placeholder := repo.KeyPlaceholderByQuery(len(cdrIds))
|
||||
sql := "delete from cdr_event_smf where id in (" + placeholder + ")"
|
||||
parameters := repo.ConvertIdsSlice(cdrIds)
|
||||
results, err := datasource.ExecDB("", sql, parameters)
|
||||
if err != nil {
|
||||
|
||||
@@ -10,3 +10,12 @@ type ICDREvent interface {
|
||||
// DeleteByIds 批量删除信息
|
||||
DeleteByIds(cdrIds []string) (int64, error)
|
||||
}
|
||||
|
||||
// CDR会话事件 服务层接口
|
||||
type SMFCDREvent interface {
|
||||
// SelectPage 根据条件分页查询
|
||||
SelectPage(querys model.SMFCDREventQuery) map[string]any
|
||||
|
||||
// DeleteByIds 批量删除信息
|
||||
DeleteByIds(cdrIds []string) (int64, error)
|
||||
}
|
||||
|
||||
@@ -12,12 +12,21 @@ var NewCDREventImpl = &CDREventImpl{
|
||||
cdrEventRepository: repository.NewCDREventImpl,
|
||||
}
|
||||
|
||||
var NewSMFCDREventImpl = &SMFCDREventImpl{
|
||||
cdrEventRepository: repository.NewSMFCDREventImpl,
|
||||
}
|
||||
|
||||
// CDREventImpl CDR会话事件 服务层处理
|
||||
type CDREventImpl struct {
|
||||
// CDR会话事件数据信息
|
||||
cdrEventRepository repository.ICDREvent
|
||||
}
|
||||
|
||||
type SMFCDREventImpl struct {
|
||||
// CDR会话事件数据信息
|
||||
cdrEventRepository repository.SMFCDREvent
|
||||
}
|
||||
|
||||
// SelectPage 根据条件分页查询
|
||||
func (r *CDREventImpl) SelectPage(querys model.CDREventQuery) map[string]any {
|
||||
return r.cdrEventRepository.SelectPage(querys)
|
||||
@@ -38,3 +47,23 @@ func (r *CDREventImpl) DeleteByIds(cdrIds []string) (int64, error) {
|
||||
// 删除信息失败!
|
||||
return 0, fmt.Errorf("delete fail")
|
||||
}
|
||||
|
||||
func (r *SMFCDREventImpl) SelectPage(querys model.SMFCDREventQuery) map[string]any {
|
||||
return r.cdrEventRepository.SelectPage(querys)
|
||||
}
|
||||
|
||||
// DeleteByIds 批量删除信息
|
||||
func (r *SMFCDREventImpl) DeleteByIds(cdrIds []string) (int64, error) {
|
||||
// 检查是否存在
|
||||
ids := r.cdrEventRepository.SelectByIds(cdrIds)
|
||||
if len(ids) <= 0 {
|
||||
return 0, fmt.Errorf("not data")
|
||||
}
|
||||
|
||||
if len(ids) == len(cdrIds) {
|
||||
rows := r.cdrEventRepository.DeleteByIds(cdrIds)
|
||||
return rows, nil
|
||||
}
|
||||
// 删除信息失败!
|
||||
return 0, fmt.Errorf("delete fail")
|
||||
}
|
||||
|
||||
@@ -27,3 +27,21 @@ func GetCDRConnect(requestID string, data any) ([]byte, error) {
|
||||
}))
|
||||
return resultByte, err
|
||||
}
|
||||
|
||||
// GetCDRConnect 获取CDR会话事件-SMF
|
||||
func GetSMFCDRConnect(requestID string, data any) ([]byte, error) {
|
||||
msgByte, _ := json.Marshal(data)
|
||||
var query neDataModel.SMFCDREventQuery
|
||||
err := json.Unmarshal(msgByte, &query)
|
||||
if err != nil {
|
||||
logger.Warnf("ws processor GetCDRConnect err: %s", err.Error())
|
||||
return nil, fmt.Errorf("query data structure error")
|
||||
}
|
||||
|
||||
dataMap := neDataService.NewSMFCDREventImpl.SelectPage(query)
|
||||
resultByte, err := json.Marshal(result.Ok(map[string]any{
|
||||
"requestId": requestID,
|
||||
"data": dataMap,
|
||||
}))
|
||||
return resultByte, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user