diff --git a/features/cdr/cdrevent.go b/features/cdr/cdrevent.go index 5567be4e..7214f179 100644 --- a/features/cdr/cdrevent.go +++ b/features/cdr/cdrevent.go @@ -1,13 +1,12 @@ package cdr import ( - "encoding/json" - "io" + "fmt" "net/http" - "time" + "strings" + "be.ems/lib/core/ctx" "be.ems/lib/dborm" - "be.ems/lib/global" "be.ems/lib/log" "be.ems/lib/services" "be.ems/restagent/config" @@ -16,197 +15,14 @@ import ( ) var ( - 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" + UriCDREvent = config.DefaultUriPrefix + "/cdrManagement/v1/elementType/{elementTypeValue}/objectType/cdrEvent" + UriCDRFile = config.DefaultUriPrefix + "/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" + CustomUriCDREvent = config.UriPrefix + "/cdrManagement/v1/elementType/{elementTypeValue}/objectType/cdrEvent" + CustomUriCDRFile = config.UriPrefix + "/cdrManagement/v1/elementType/{elementTypeValue}/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"` -} - +// CDREvent CDR数据表格结构体 type CDREvent struct { NeType string `json:"neType" xorm:"ne_type"` NeName string `json:"neName" xorm:"ne_name"` @@ -215,29 +31,26 @@ type CDREvent struct { CDR map[string]any `json:"CDR" xorm:"cdr_json"` } -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)) - 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) +// PostCDREventFrom 接收CDR数据请求 +func PostCDREventFrom(w http.ResponseWriter, r *http.Request) { + log.Info("PostCDREventFrom processing... ") + neType := ctx.GetParam(r, "elementTypeValue") + var cdrEvent CDREvent + if err := ctx.ShouldBindJSON(r, &cdrEvent); err != nil { services.ResponseInternalServerError500ProcessError(w, err) return } - log.Trace("cdrEvent:", cdrEvent) - affected, err := dborm.XormInsertTableOne("cdr_event_ims", cdrEvent) + neTypeLower := strings.ToLower(cdrEvent.NeType) + if neType == "" || neType != neTypeLower { + services.ResponseInternalServerError500ProcessError(w, fmt.Errorf("inconsistent network element types")) + return + } + + tableName := fmt.Sprintf("cdr_event_%s", neTypeLower) + affected, err := dborm.XormInsertTableOne(tableName, cdrEvent) if err != nil && affected <= 0 { - log.Error("Failed to insert cdr_event_ims:", err) + log.Error("Failed to insert "+tableName, err) services.ResponseInternalServerError500ProcessError(w, err) return } @@ -246,49 +59,17 @@ func PostCDREventFromIMS(w http.ResponseWriter, r *http.Request) { neInfo := neService.NewNeInfoImpl.SelectNeInfoByRmuid(cdrEvent.RmUID) if neInfo.RmUID == cdrEvent.RmUID { // 推送到ws订阅组 - if v, ok := cdrEvent.CDR["recordType"]; ok { - if v == "MOC" || v == "MTSM" { + switch neType { + case "IMS": + if v, ok := cdrEvent.CDR["recordType"]; ok && (v == "MOC" || v == "MTSM") { wsService.NewWSSendImpl.ByGroupID(wsService.GROUP_IMS_CDR+neInfo.NeId, cdrEvent) } + case "SMF": + wsService.NewWSSendImpl.ByGroupID(wsService.GROUP_SMF_CDR+neInfo.NeId, cdrEvent) + case "SMSC": + wsService.NewWSSendImpl.ByGroupID(wsService.GROUP_SMSC_CDR+neInfo.NeId, cdrEvent) } } 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 - } - - // 发送到匹配的网元 - neInfo := neService.NewNeInfoImpl.SelectNeInfoByRmuid(cdrEvent.RmUID) - if neInfo.RmUID == cdrEvent.RmUID { - // 推送到ws订阅组 - wsService.NewWSSendImpl.ByGroupID(wsService.GROUP_SMF_CDR+neInfo.NeId, cdrEvent) - } - - services.ResponseStatusOK204NoContent(w) -} diff --git a/lib/routes/routes.go b/lib/routes/routes.go index 537a9384..9565f8ec 100644 --- a/lib/routes/routes.go +++ b/lib/routes/routes.go @@ -279,17 +279,12 @@ func init() { Register("GET", ue.UriNSSFSubscriptions, ue.GetSubscriptionsFromNSSF, nil) Register("GET", ue.CustomUriNSSFSubscriptions, ue.GetSubscriptionsFromNSSF, 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) + // cdr event + Register("POST", cdr.UriCDREvent, cdr.PostCDREventFrom, nil) + Register("POST", cdr.CustomUriCDREvent, cdr.PostCDREventFrom, nil) // UE event 上报的UE事件 Register("POST", event.UriUEEvent, event.PostUEEvent, nil) - // UE event AMF上报的UE事件, 无前缀给到Gin处理 //Register("POST", event.UriUEEvent, event.PostUEEventFromAMF, nil) diff --git a/src/modules/ws/service/ws_send.impl.go b/src/modules/ws/service/ws_send.impl.go index ef836d75..88035523 100644 --- a/src/modules/ws/service/ws_send.impl.go +++ b/src/modules/ws/service/ws_send.impl.go @@ -20,6 +20,8 @@ const ( GROUP_IMS_CDR = "1005_" // 组号-SMF_CDR会话事件 1006_neId GROUP_SMF_CDR = "1006_" + // 组号-SMSC_CDR会话事件 1007_neId + GROUP_SMSC_CDR = "1007_" // 组号-AMF_UE会话事件 GROUP_AMF_UE = "1010" // 组号-MME_UE会话事件 1011_neId