diff --git a/features/cdr/cdrevent.go b/features/cdr/cdrevent.go index 90437bd..2f0efec 100644 --- a/features/cdr/cdrevent.go +++ b/features/cdr/cdrevent.go @@ -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) +} diff --git a/features/cm/license.go b/features/cm/license.go index 30b2e1f..ffee447 100644 --- a/features/cm/license.go +++ b/features/cm/license.go @@ -8,11 +8,13 @@ import ( "os/exec" "strings" + "be.ems/lib/core/utils/ctx" "be.ems/lib/dborm" "be.ems/lib/log" "be.ems/lib/services" "be.ems/restagent/config" - + neModel "be.ems/src/modules/network_element/model" + neService "be.ems/src/modules/network_element/service" "github.com/gorilla/mux" ) @@ -503,22 +505,54 @@ func UploadLicenseFileData(w http.ResponseWriter, r *http.Request) { } } */ - neLicense := dborm.NeLicense{ - NeType: neTypeUpper, - NeID: neId, - Status: "ACTIVE", - Path: licensePath, - FileName: licenseFileName, - Comment: comment, + + // neLicense := dborm.NeLicense{ + // NeType: neTypeUpper, + // NeID: neId, + // Status: "ACTIVE", + // Path: licensePath, + // FileName: licenseFileName, + // Comment: comment, + // } + + // log.Debug("neLicense:", neLicense) + // _, err = dborm.XormInsertTableOne("ne_license", neLicense) + // if err != nil { + // log.Error("Faile to XormInsertTableOne:", err) + // services.ResponseInternalServerError500ProcessError(w, err) + // } + + neLicense := neModel.NeLicense{ + NeType: neTypeUpper, + NeId: neId, + Status: "0", + LicensePath: neFilePath, + Remark: comment, } log.Debug("neLicense:", neLicense) - _, err = dborm.XormInsertTableOne("ne_license", neLicense) - if err != nil { - log.Error("Faile to XormInsertTableOne:", err) - services.ResponseInternalServerError500ProcessError(w, err) - return + + // 检查是否存在授权记录 + neLicense2 := neService.NewNeLicenseImpl.SelectByNeTypeAndNeID(neTypeUpper, neId) + if neLicense2.NeId != neId { + // 读取授权码 + code, _ := neService.NewNeLicenseImpl.ReadLicenseInfo(neLicense) + neLicense.ActivationRequestCode = code + + neLicense.CreateBy = ctx.LoginUserToUserName(r) + insertId := neService.NewNeLicenseImpl.Insert(neLicense) + if insertId != "" { + services.ResponseStatusOK204NoContent(w) + return + } + } else { + neLicense2.UpdateBy = ctx.LoginUserToUserName(r) + upRows := neService.NewNeLicenseImpl.Update(neLicense2) + if upRows > 0 { + services.ResponseStatusOK204NoContent(w) + return + } } - services.ResponseStatusOK204NoContent(w) + services.ResponseInternalServerError500ProcessError(w, err) } diff --git a/features/cm/software.go b/features/cm/software.go index 2ab11dc..2085b5c 100644 --- a/features/cm/software.go +++ b/features/cm/software.go @@ -20,9 +20,9 @@ import ( ) const ( - SoftwareStatusUploaded = "Uploaded" - SoftwareStatusInactive = "Inactive" - SoftwareStatusActive = "Active" + SoftwareStatusUploaded = "3" + SoftwareStatusInactive = "3" + SoftwareStatusActive = "1" DigestsSignOkString = "digests signatures OK" SoftwareVerifiedOk = "Verified OK" ) @@ -419,7 +419,7 @@ func DownloadSoftwareFile(w http.ResponseWriter, r *http.Request) { return } - fileName := (*neSoftware)[0]["file_name"] + fileName := (*neSoftware)[0]["name"] path := (*neSoftware)[0]["path"] md5Sum := (*neSoftware)[0]["md5_sum"] @@ -473,7 +473,7 @@ func DeleteSoftwareFile(w http.ResponseWriter, r *http.Request) { return } - fileName := (*neSoftware)[0]["file_name"] + fileName := (*neSoftware)[0]["name"] path := (*neSoftware)[0]["path"] filePath := fmt.Sprintf("%s/%s", path, fileName) err = os.Remove(filePath) @@ -560,7 +560,7 @@ func DistributeSoftwareToNF(w http.ResponseWriter, r *http.Request) { return } - fileName := (*neSoftware)[0]["file_name"] + fileName := (*neSoftware)[0]["name"] path := (*neSoftware)[0]["path"] srcFile := fmt.Sprintf("%s/%s", path, fileName) @@ -611,7 +611,7 @@ func DistributeSoftwareToNF(w http.ResponseWriter, r *http.Request) { Version: (*neSoftware)[0]["version"], FilePath: fmt.Sprintf("%s/software/%s/%s", config.GetYamlConfig().NE.OmcDir, neTypeLower, fileName), PreVersion: (*neVersion)[0]["version"], - PreFile: (*neVersion)[0]["file_path"], + PreFile: (*neVersion)[0]["path"], Status: SoftwareStatusInactive, } @@ -695,21 +695,29 @@ func ActiveSoftwareToNF(w http.ResponseWriter, r *http.Request) { log.Trace("neVersion:", neVersion) if !config.GetYamlConfig().OMC.TestMode { - filePath := (*neVersion)[0]["file_path"] + filePath := (*neVersion)[0]["path"] sshHost := fmt.Sprintf("%s@%s", config.GetYamlConfig().NE.User, neInfo.Ip) - srcFile := fmt.Sprintf("%s/actpkg.sh", config.GetYamlConfig().OMC.BinDir) + runCmd := fmt.Sprintf("sudo rm -f %s/actpkg.sh", config.GetYamlConfig().NE.ScpDir) + err = RunSSHCmd(sshHost, runCmd) + if err != nil { + log.Errorf("Failed to run cmd: %s", runCmd) + services.ResponseInternalServerError500ProcessError(w, err) + return + } + + srcFile := fmt.Sprintf("%s/actpkg.sh", config.GetYamlConfig().OMC.BinDir) scpDir := fmt.Sprintf("%s@%s:%s", config.GetYamlConfig().NE.User, neInfo.Ip, config.GetYamlConfig().NE.ScpDir) cmd := exec.Command("scp", "-r", srcFile, scpDir) _, err := cmd.CombinedOutput() if err != nil { - log.Errorf("Faile to scp NF: neType=%s, neId=%s, ip=%s", neType, neId, neInfo.Ip) + log.Errorf("Failed to scp NF: neType=%s, neId=%s, ip=%s", neType, neId, neInfo.Ip) services.ResponseInternalServerError500ProcessError(w, err) return } - runCmd := fmt.Sprintf("sudo %s/actpkg.sh '%s' %s", + runCmd = fmt.Sprintf("sudo %s/actpkg.sh '%s' %s", config.GetYamlConfig().NE.ScpDir, filePath, neTypeUpper) if neTypeLower == "omc" { idNeVersion, _ := strconv.Atoi((*neVersion)[0]["id"]) @@ -897,6 +905,14 @@ func RollBackSoftwareToNF(w http.ResponseWriter, r *http.Request) { if !config.GetYamlConfig().OMC.TestMode { sshHost := fmt.Sprintf("%s@%s", config.GetYamlConfig().NE.User, neInfo.Ip) + runCmd := fmt.Sprintf("sudo rm -f %s/rbkpkg.sh", config.GetYamlConfig().NE.ScpDir) + err = RunSSHCmd(sshHost, runCmd) + if err != nil { + log.Errorf("Failed to run cmd: %s", runCmd) + services.ResponseInternalServerError500ProcessError(w, err) + return + } + srcFile := fmt.Sprintf("%s/rbkpkg.sh", config.GetYamlConfig().OMC.BinDir) scpDir := fmt.Sprintf("%s@%s:%s", config.GetYamlConfig().NE.User, @@ -909,7 +925,7 @@ func RollBackSoftwareToNF(w http.ResponseWriter, r *http.Request) { return } - runCmd := fmt.Sprintf("sudo %s/rbkpkg.sh '%s' %s", + runCmd = fmt.Sprintf("sudo %s/rbkpkg.sh '%s' %s", config.GetYamlConfig().NE.ScpDir, filePath, neTypeUpper) if neTypeLower == "omc" { idNeVersion, _ := strconv.Atoi((*neVersion)[0]["id"]) @@ -919,7 +935,7 @@ func RollBackSoftwareToNF(w http.ResponseWriter, r *http.Request) { PreVersion: "-", PreFile: "-", NewVersion: (*neVersion)[0]["version"], - NewFile: (*neVersion)[0]["file_path"], + NewFile: (*neVersion)[0]["path"], Status: SoftwareStatusActive, } @@ -1021,7 +1037,7 @@ func RollBackSoftwareToNF(w http.ResponseWriter, r *http.Request) { PreVersion: "-", PreFile: "-", NewVersion: (*neVersion)[0]["version"], - NewFile: (*neVersion)[0]["file_path"], + NewFile: (*neVersion)[0]["path"], Status: SoftwareStatusActive, } diff --git a/features/file/file.go b/features/file/file.go index 7abfe87..7162b3f 100644 --- a/features/file/file.go +++ b/features/file/file.go @@ -72,13 +72,13 @@ func UploadFile(w http.ResponseWriter, r *http.Request) { fileName = fileNamePath } else { path = config.GetYamlConfig().OMC.FrontUpload - fileName, err := services.HandleUploadFile(r, path, fileName) + fileNamePath, err := services.HandleUploadFile(r, path, fileName) if err != nil { log.Error("Faile to HandleUploadFile:", err) services.ResponseInternalServerError500ProcessError(w, err) return } - fileName = fileName + fileName = fileNamePath } log.Debugf("upload file=%s to path=%s", fileName, path) diff --git a/features/fm/alarm.go b/features/fm/alarm.go index 716721b..f1e05cb 100644 --- a/features/fm/alarm.go +++ b/features/fm/alarm.go @@ -435,11 +435,20 @@ func PostAlarmFromNF(w http.ResponseWriter, r *http.Request) { SetAlarmAckInfo(valueJson, &alarmData) } log.Debug("alarmData:", alarmData) - affected, err := session.Insert(alarmData) - if err != nil && affected <= 0 { - log.Error("Failed to insert alarm data:", err) - services.ResponseInternalServerError500DatabaseOperationFailed(w) - continue + if alarmData.OrigSeverity == "Event" && config.GetYamlConfig().Alarm.SplitEventAlarm { + affected, err := xEngine.Table("alarm_event").InsertOne(alarmData) + if err != nil && affected <= 0 { + log.Error("Failed to insert alarm_event:", err) + services.ResponseInternalServerError500ProcessError(w, err) + continue + } + } else { + affected, err := session.Insert(alarmData) + if err != nil && affected <= 0 { + log.Error("Failed to insert alarm data:", err) + services.ResponseInternalServerError500DatabaseOperationFailed(w) + continue + } } alarmLog := new(AlarmLog) alarmLog.NeType = alarmData.NeType @@ -451,7 +460,7 @@ func PostAlarmFromNF(w http.ResponseWriter, r *http.Request) { alarmLog.EventTime = eventTime log.Trace("alarmLog:", alarmLog) - affected, err = session.Insert(alarmLog) + affected, err := session.Insert(alarmLog) if err != nil && affected <= 0 { log.Error("Failed to insert alarm_log:", err) } @@ -470,6 +479,28 @@ func PostAlarmFromNF(w http.ResponseWriter, r *http.Request) { services.ResponseStatusOK200Null(w) } +type AlarmEvent struct { + AlarmSeq int `json:"alarmSeq"` + AlarmId string `json:"alarmId" xorm:"alarm_id"` + NeId string `json:"neId"` + AlarmCode int `json:"alarmCode"` + AlarmTitle string `json:"alarmTitle"` + EventTime string `json:"eventTime"` + AlarmType string `json:"alarmType"` + OrigSeverity string `json:"origSeverity"` + PVFlag string `json:"pvFlag" xorm:"pv_flag"` + NeName string `json:"neName"` + NeType string `json:"neType"` + ObjectUid string `json:"objectUid" xorm:"object_uid"` + ObjectName string `json:"objectName" xorm:"object_name"` + ObjectType string `json:"objectType" xorm:"object_type"` + LocationInfo string `json:"locationInfo"` + Province string `json:"province"` + SpecificProblem string `json:"specificProblem"` + SpecificProblemID string `json:"specificProblemID" xorm:"specific_problem_id"` + AddInfo string `json:"addInfo"` +} + // process alarm get from NFs func GetAlarmFromNF(w http.ResponseWriter, r *http.Request) { log.Debug("GetAlarmFromNF processing... ") diff --git a/features/maintenance/maintenance.go b/features/maintenance/maintenance.go deleted file mode 100644 index 2ee014a..0000000 --- a/features/maintenance/maintenance.go +++ /dev/null @@ -1,313 +0,0 @@ -package maintenance - -import ( - "encoding/json" - "fmt" - "net/http" - "os" - "os/exec" - "path" - "runtime" - "time" - - "be.ems/lib/core/utils/ctx" - "be.ems/lib/dborm" - "be.ems/lib/log" - "be.ems/lib/services" - "be.ems/restagent/config" - "github.com/shirou/gopsutil/cpu" - "github.com/shirou/gopsutil/disk" - "github.com/shirou/gopsutil/mem" -) - -// (1) OMC能够对相关的文件系统资源、内存、CPU资源、数据存储空间、数据库空间等系统指标进行监控管理; -// 对于虚拟化部署OMC系统,能够对虚机内存、虚机CPU、虚拟存储空间、文件系统资源、数据库空间等指标进行监控,提供界面截图 ; - -// (2) 系统监控指标的采样时间和阈值可由用户设定,超过阈值将产生不同级别的告警,提供界面截图 ; - -// (3) OMC能够方便的查询数据库连接情况;并可手工干预数据库的连接,能方便的终结非法的数据库连接 ; - -// (4) 用户能方便的查询系统进程、应用进程等的进程名、进程类型、开始时间、运行主机等信息,提供界面截图 -// (5) 用户能方便的对系统进程、应用进程等做中断或者启动操作,提供界面截图 - -// (6) 对于文件系统资源、内存、CPU资源、数据存储空间、数据库空间等系统指标数据,要求OMC能够保存至少3个月,提供界面截图 ; -// (7) 用户可以按照需求自定义报表模板并生成OMC系统维护数据报表,提供界面截图 ; - -// (8) OMC具备自身告警管理功能,对于传统OMC系统,如:服务器单电源告警,存储硬盘告警、OMC系统软件故障等; -// 对于虚拟化OMC系统,如虚机告警、虚拟硬盘告警等,提供界面截图 。 - -var ( - // parameter config management - Uri = config.UriPrefix + "/maintenance/{apiVersion}/zz" - - // (1) OMC能够对相关的文件系统资源、内存、CPU资源、数据存储空间、数据库空间等系统指标进行监控管理; - UriPref = config.UriPrefix + "/maintenance/{apiVersion}/pref" - - // (6) 对于文件系统资源、内存、CPU资源、数据存储空间、数据库空间等系统指标数据,要求OMC能够保存至少3个月,提供界面截图 ; - UriPrefLog = config.UriPrefix + "/maintenance/{apiVersion}/prefLog" - - // (2) 系统监控指标的采样时间和阈值可由用户设定,超过阈值将产生不同级别的告警,提供界面截图 ; - UriConfig = config.UriPrefix + "/maintenance/{apiVersion}/config" - - // (3) OMC能够方便的查询数据库连接情况;并可手工干预数据库的连接,能方便的终结非法的数据库连接 - UriSqlClient = config.UriPrefix + "/maintenance/{apiVersion}/sqlClient" - - // (4) 用户能方便的查询系统进程、应用进程等的进程名、进程类型、开始时间、运行主机等信息,提供界面截图 - // (5) 用户能方便的对系统进程、应用进程等做中断或者启动操作,提供界面截图 - UriTop = config.UriPrefix + "/maintenance/{apiVersion}/top" -) - -func init() { - // 定時收集 TODO - prefLogSave("") -} - -func List(w http.ResponseWriter, r *http.Request) { - fmt.Println("zz List") - services.ResponseStatusOK200Null(w) -} - -// 性能指標 -func prefInfo(dirName string) map[string]any { - data := make(map[string]any) - - // 显示文件資源目录 - dirPath := "D://" - if runtime.GOOS == "linux" { - dirPath = "/home" - } - // 訪問下級 - if dirName != "" { - dirPath = path.Join(dirPath, dirName) - } - dir_list, e := os.ReadDir(dirPath) - if e != nil { - log.Error(e) - } - list := make([]map[string]any, 0) - for _, v := range dir_list { - o, err := v.Info() - if err != nil { - continue - } - list = append(list, map[string]any{ - "name": o.Name(), - "size": o.Size(), - "mode": o.Mode().String(), - "modTime": o.ModTime().Format("2006-01-02 15:04:05"), - "isDir": o.IsDir(), - }) - } - data["dirList"] = list - - // 文件資源使用率 - u, _ := disk.Usage(dirPath) - usedGB := int(u.Used) / (1024 * 1024 * 1024 * 1) - data["dirUse"] = fmt.Sprintf("%d", usedGB) - - // CPU使用率 - percent, err := cpu.Percent(time.Second, false) - if err != nil { - log.Error(err) - } - data["cpuUse"] = fmt.Sprintf("%.2f", percent[0]) - - // 内存使用率 - memInfo, err := mem.VirtualMemory() - if err != nil { - log.Error(err) - } - data["memUse"] = memInfo.UsedPercent - - // 獲取數據庫占用空間 - if dborm.DbClient.XEngine != nil { - conf := config.GetYamlConfig() - result, err := dborm.DbClient.XEngine.QueryString(`SELECT - CONCAT(TRUNCATE(SUM(data_length)/1024/1024,2),'MB') AS data_size, - CONCAT(TRUNCATE(SUM(max_data_length)/1024/1024,2),'MB') AS max_data_size, - CONCAT(TRUNCATE(SUM(data_free)/1024/1024,2),'MB') AS data_free, - CONCAT(TRUNCATE(SUM(index_length)/1024/1024,2),'MB') AS index_size - FROM information_schema.tables WHERE TABLE_SCHEMA = ?; - `, conf.Database.Name) - if err == nil { - data["dbInfo"] = result[0] - } else { - data["dbInfo"] = map[string]string{} - } - } - return data -} - -// 性能指標存入數據庫 -func prefLogSave(dirName string) { - if dborm.DbClient.XEngine != nil { - data := prefInfo(dirName) - - dirListByte, err := json.Marshal(data["dirList"]) - if err != nil { - log.Error(err) - } - dbInfoByte, err := json.Marshal(data["dbInfo"]) - if err != nil { - log.Error(err) - } - rse, err := dborm.DbClient.XEngine.Exec(`INSERT INTO sys_perf_data - (id, create_time, dir_used, dir_list, db_info, mem_used, cpu_used) - VALUES(NULL, NOW(), ?, ?, ?, ?, ?); - `, data["dirUse"], string(dirListByte), string(dbInfoByte), data["memUse"], data["cpuUse"]) - if err != nil { - log.Error(err) - } - fmt.Println(rse.LastInsertId()) - } -} - -// GET http://192.168.21.183:3040/api/rest/maintenance/v1/pref?dir=true -func Pref(w http.ResponseWriter, r *http.Request) { - // 知道下級文件資源目录 - dirName := r.URL.Query().Get("dirName") - data := prefInfo(dirName) - services.ResponseWithJson(w, http.StatusOK, data) -} - -// POST http://192.168.21.183:3040/api/rest/maintenance/v1/config -func Config(w http.ResponseWriter, r *http.Request) { - // json 請求參數獲取 - var bodyArgs struct { - Key string `json:"key"` - Value string `json:"value"` - } - err := ctx.ShouldBindJSON(r, &bodyArgs) - if err != nil { - log.Error("io.ReadAll is failed:", err) - services.ResponseNotFound404UriNotExist(w, r) - return - } - - // 進行值更新 - if dborm.DbClient.XEngine != nil { - result, err := dborm.DbClient.XEngine.QueryString("SELECT * FROM information_schema.processlist") - if err != nil { - fmt.Println(err) - } - fmt.Println(result) - rse, err := dborm.DbClient.XEngine.Exec("UPDATE sys_config SET value = ? where id = ?", "true", 100) - if err != nil { - fmt.Println(err) - } - fmt.Println(rse) - } - - services.ResponseStatusOK200Null(w) -} - -// http://192.168.21.183:3040/api/rest/maintenance/v1/sqlClient?type=close -// http://192.168.21.183:3040/api/rest/maintenance/v1/sqlClient?type=connet -// http://192.168.21.183:3040/api/rest/maintenance/v1/sqlClient?type=user -func SqlClient(w http.ResponseWriter, r *http.Request) { - // 关闭 - isClose := r.URL.Query().Get("type") - if isClose == "close" && dborm.DbClient.XEngine != nil { - dborm.DbClient.XEngine.Close() - } - - // 重连 - isConnet := r.URL.Query().Get("type") - if isConnet == "connet" && dborm.DbClient.XEngine == nil { - conf := config.GetYamlConfig() - err := dborm.InitDbClient(conf.Database.Type, conf.Database.User, conf.Database.Password, - conf.Database.Host, conf.Database.Port, conf.Database.Name, conf.Database.ConnParam) - if err != nil { - fmt.Println("dborm.initDbClient err:", err) - services.ResponseInternalServerError500DatabaseOperationFailed(w) - return - } - } - - // 查询实例 - isUser := r.URL.Query().Get("type") - if isUser == "user" && dborm.DbClient.XEngine != nil { - result, err := dborm.DbClient.XEngine.QueryString("SELECT * FROM information_schema.processlist") - if err != nil { - fmt.Println(err) - } - fmt.Println(result) - rse, err := dborm.DbClient.XEngine.Exec("KILL CONNECTION CONNECTION_ID()") - if err != nil { - fmt.Println(err) - } - fmt.Println(rse) - } - - // 进行连接测试 - err := dborm.DbClient.XEngine.Ping() - if err != nil { - fmt.Println(err) - } - services.ResponseStatusOK200Null(w) -} - -// GET http://192.168.21.183:3040/api/rest/maintenance/v1/top?grep= -func Top(w http.ResponseWriter, r *http.Request) { - // 過濾命令 - grep := r.URL.Query().Get("grep") - // 命令拼接 - var cmd *exec.Cmd - switch runtime.GOOS { - case "linux": - command := "ps -ef " - if grep != "" { - command += grep - } - cmd = exec.Command(command) - case "windows": - command := "wmic process list brief " - if grep != "" { - command += grep - } - cmd = exec.Command("cmd", "/C", command) - } - - out, err := cmd.CombinedOutput() - fmt.Println(string(out)) - if err != nil { - fmt.Println(err) - } - services.ResponseWithJson(w, http.StatusOK, string(out)) -} - -// PATCH http://192.168.21.183:3040/api/rest/maintenance/v1/top?ops=&name= -func TopOps(w http.ResponseWriter, r *http.Request) { - // json 請求參數獲取 - var bodyArgs struct { - Ops string `json:"ops"` - Pid string `json:"pid"` - } - err := ctx.ShouldBindJSON(r, &bodyArgs) - if err != nil { - log.Error("io.ReadAll is failed:", err) - services.ResponseNotFound404UriNotExist(w, r) - return - } - // 命令拼接 - var cmd *exec.Cmd - switch runtime.GOOS { - case "linux": - switch bodyArgs.Ops { - case "kill": - cmd = exec.Command("kill", "-9", bodyArgs.Pid) - } - case "windows": - switch bodyArgs.Ops { - case "kill": - cmd = exec.Command("cmd", "/C", "taskkill", "-PID", bodyArgs.Pid, "-F") - } - } - - out, err := cmd.CombinedOutput() - fmt.Println(string(out)) - if err != nil { - fmt.Println(err) - } - services.ResponseWithJson(w, http.StatusOK, string(out)) -} diff --git a/features/monitor/monitor/model.go b/features/monitor/monitor/model.go deleted file mode 100644 index 8997a22..0000000 --- a/features/monitor/monitor/model.go +++ /dev/null @@ -1,55 +0,0 @@ -package monitor - -import "time" - -type MonitorBase struct { - ID uint `xorm:"id" json:"id"` - CreatedAt time.Time `xorm:"created_at" json:"createdAt"` - UpdatedAt time.Time `xorm:"updated_at" json:"updatedAt"` - - Cpu float64 `xorm:"cpu" json:"cpu"` - - LoadUsage float64 `xorm:"load_usage" json:"loadUsage"` - CpuLoad1 float64 `xorm:"cpu_load1" json:"cpuLoad1"` - CpuLoad5 float64 `xorm:"cpu_load5" json:"cpuLoad5"` - CpuLoad15 float64 `xorm:"cpu_load15" json:"cpuLoad15"` - - Memory float64 `xorm:"memory" json:"memory"` - - DbSize uint `xorm:"db_size" json:"dbSize"` -} - -type MonitorIO struct { - ID uint `xorm:"id" json:"id"` - CreatedAt time.Time `xorm:"created_at" json:"createdAt"` - UpdatedAt time.Time `xorm:"updated_at" json:"updatedAt"` - - Name string `xorm:"name" json:"name"` - Read uint64 `xorm:"read" json:"read"` - Write uint64 `xorm:"write" json:"write"` - Count uint64 `xorm:"count" json:"count"` - Time uint64 `xorm:"time" json:"time"` -} - -type MonitorNetwork struct { - ID uint `xorm:"id" json:"id"` - CreatedAt time.Time `xorm:"created_at" json:"createdAt"` - UpdatedAt time.Time `xorm:"updated_at" json:"updatedAt"` - - Name string `xorm:"name" json:"name"` - Up float64 `xorm:"up" json:"up"` - Down float64 `xorm:"down" json:"down"` -} - -type MonitorSearch struct { - Param string `json:"param" validate:"required,oneof=all cpu memory load io network"` - Info string `json:"info"` - StartTime time.Time `json:"startTime"` - EndTime time.Time `json:"endTime"` -} - -type MonitorData struct { - Param string `json:"param" validate:"required,oneof=cpu memory load io network"` - Date []time.Time `json:"date"` - Value []interface{} `json:"value"` -} diff --git a/features/monitor/monitor/monitor.go b/features/monitor/monitor/monitor.go deleted file mode 100644 index 4409e7d..0000000 --- a/features/monitor/monitor/monitor.go +++ /dev/null @@ -1,162 +0,0 @@ -package monitor - -import ( - "net/http" - "sort" - "strings" - "time" - - "be.ems/lib/core/utils/ctx" - "be.ems/lib/dborm" - "be.ems/lib/services" - "be.ems/restagent/config" - "github.com/shirou/gopsutil/disk" - "github.com/shirou/gopsutil/net" -) - -// 点击【主机 - 监控】菜单,进入监控报表,直观的了解服务器的运行状态,包含【平均负载】、【CPU性能监控】、【内存使用监控】、【磁盘IO监控】、【网络IO监控】 - -// 可以查看昨天,今天,最近7天,最近30天,自定义时间的监控指标情况。 -// 默认监控是开启的,可以在【面板设置 - 监控】页面中根据需求对监控进行开启和关闭。 -// 监控数据默认保存30天,可以自行修改,也可手动清理该日志。 - -var ( - // IP地址 - UriIPAddr = config.DefaultUriPrefix + "/monitor/{apiVersion}/monitor/ipaddr" - // 可选网络 - UriNetOpt = config.DefaultUriPrefix + "/monitor/{apiVersion}/monitor/netoptions" - // 可选磁盘 - UriIoOpt = config.DefaultUriPrefix + "/monitor/{apiVersion}/monitor/iooptions" - // 加载 - UriLoad = config.DefaultUriPrefix + "/monitor/{apiVersion}/monitor/load" - - // IP地址 - UriIPAddrOAM = config.UriPrefix + "/monitor/{apiVersion}/monitor/ipaddr" - // 可选网络 - UriNetOptOAM = config.UriPrefix + "/monitor/{apiVersion}/monitor/netoptions" - // 可选磁盘 - UriIoOptOAM = config.UriPrefix + "/monitor/{apiVersion}/monitor/iooptions" - // 加载 - UriLoadOAM = config.UriPrefix + "/monitor/{apiVersion}/monitor/load" -) - -// IPAddr IP地址 -func IPAddr(w http.ResponseWriter, r *http.Request) { - ipAddrs := []map[string]string{} - interfaces, err := net.Interfaces() - if err == nil { - for _, iface := range interfaces { - addrs := map[string]string{} - for _, v := range iface.Addrs { - prefix := strings.Split(v.Addr, "/")[0] - if strings.Contains(prefix, "::") { - addrs["IPv6"] = prefix - } - if strings.Contains(prefix, ".") { - addrs["IPv4"] = prefix - } - } - ipAddrs = append(ipAddrs, addrs) - } - } - services.ResponseWithJson(w, 200, ipAddrs) -} - -// Netoptions 可选网络 -func Netoptions(w http.ResponseWriter, r *http.Request) { - netStat, _ := net.IOCounters(true) - var options []string - options = append(options, "all") - for _, net := range netStat { - options = append(options, net.Name) - } - sort.Strings(options) - services.ResponseWithJson(w, 200, options) -} - -// Iooptions 可选磁盘 -func Iooptions(w http.ResponseWriter, r *http.Request) { - diskStat, _ := disk.IOCounters() - var options []string - options = append(options, "all") - for _, net := range diskStat { - options = append(options, net.Name) - } - sort.Strings(options) - services.ResponseWithJson(w, 200, options) -} - -// LoadMonitor 载入监控 -func LoadMonitor(w http.ResponseWriter, r *http.Request) { - // json 請求參數獲取 - var bodyArgs MonitorSearch - err := ctx.ShouldBindJSON(r, &bodyArgs) - if err != nil || dborm.DbClient.XEngine == nil { - services.ResponseErrorWithJson(w, 400, err.Error()) - return - } - - loc := time.Now().Location() - bodyArgs.StartTime = bodyArgs.StartTime.In(loc) - bodyArgs.EndTime = bodyArgs.EndTime.In(loc) - - var backdatas []MonitorData - if bodyArgs.Param == "all" || bodyArgs.Param == "cpu" || bodyArgs.Param == "memory" || bodyArgs.Param == "load" { - var bases []MonitorBase - err := dborm.DbClient.XEngine.Table("monitor_base"). - Where("created_at > ? AND created_at < ?", bodyArgs.StartTime, bodyArgs.EndTime). - Desc("created_at"). - Find(&bases) - if err != nil { - services.ResponseErrorWithJson(w, 400, err.Error()) - return - } - - var itemData MonitorData - itemData.Param = "base" - for _, base := range bases { - itemData.Date = append(itemData.Date, base.CreatedAt) - itemData.Value = append(itemData.Value, base) - } - backdatas = append(backdatas, itemData) - } - if bodyArgs.Param == "all" || bodyArgs.Param == "io" { - var bases []MonitorIO - err := dborm.DbClient.XEngine.Table("monitor_io"). - Where("created_at > ? AND created_at < ?", bodyArgs.StartTime, bodyArgs.EndTime). - Desc("created_at"). - Find(&bases) - if err != nil { - services.ResponseErrorWithJson(w, 400, err.Error()) - return - } - - var itemData MonitorData - itemData.Param = "io" - for _, base := range bases { - itemData.Date = append(itemData.Date, base.CreatedAt) - itemData.Value = append(itemData.Value, base) - } - backdatas = append(backdatas, itemData) - } - if bodyArgs.Param == "all" || bodyArgs.Param == "network" { - var bases []MonitorNetwork - err := dborm.DbClient.XEngine.Table("monitor_network"). - Where("name = ? AND created_at > ? AND created_at < ?", bodyArgs.Info, bodyArgs.StartTime, bodyArgs.EndTime). - Desc("created_at"). - Find(&bases) - if err != nil { - services.ResponseErrorWithJson(w, 400, err.Error()) - return - } - - var itemData MonitorData - itemData.Param = "network" - for _, base := range bases { - itemData.Date = append(itemData.Date, base.CreatedAt) - itemData.Value = append(itemData.Value, base) - } - backdatas = append(backdatas, itemData) - } - services.ResponseWithJson(w, 200, backdatas) -} diff --git a/features/monitor/monitor/task.go b/features/monitor/monitor/task.go deleted file mode 100644 index a30521a..0000000 --- a/features/monitor/monitor/task.go +++ /dev/null @@ -1,233 +0,0 @@ -package monitor - -import ( - "encoding/json" - "fmt" - "strconv" - "time" - - "be.ems/lib/dborm" - "be.ems/lib/log" - "be.ems/restagent/config" - "github.com/robfig/cron/v3" - "github.com/shirou/gopsutil/v3/cpu" - "github.com/shirou/gopsutil/v3/disk" - "github.com/shirou/gopsutil/v3/load" - "github.com/shirou/gopsutil/v3/mem" - "github.com/shirou/gopsutil/v3/net" -) - -type MonitorService struct{} - -type IMonitorService interface { - Run() -} - -func NewIMonitorService() IMonitorService { - return &MonitorService{} -} - -func (m *MonitorService) Run() { - // monitorStatus, _ := dborm.XormGetConfig("SystemMonitor", "MonitorStatus") - // if monitorStatus["value"] == "disable" { - // return - // } - var itemModel MonitorBase - itemModel.CreatedAt = time.Now() - itemModel.UpdatedAt = time.Now() - - totalPercent, _ := cpu.Percent(3*time.Second, false) - if len(totalPercent) == 1 { - itemModel.Cpu = totalPercent[0] - } - cpuCount, _ := cpu.Counts(false) - - loadInfo, _ := load.Avg() - itemModel.CpuLoad1 = loadInfo.Load1 - itemModel.CpuLoad5 = loadInfo.Load5 - itemModel.CpuLoad15 = loadInfo.Load15 - itemModel.LoadUsage = loadInfo.Load1 / (float64(cpuCount*2) * 0.75) * 100 - - memoryInfo, _ := mem.VirtualMemory() - itemModel.Memory = memoryInfo.UsedPercent - - var dataSize int - conf := config.GetYamlConfig() - result, err := dborm.DbClient.XEngine.QueryString("SELECT SUM(data_length) AS data_size FROM information_schema.tables WHERE TABLE_SCHEMA = ?;", conf.Database.Name) - if err != nil { - dataSize = 0 - } else { - v, _ := strconv.Atoi(result[0]["data_size"]) - dataSize = v - } - itemModel.DbSize = uint(dataSize) - - _, errx := dborm.DbClient.XEngine.Table("monitor_base").Insert(itemModel) - if errx != nil { - log.Errorf("Insert basic monitoring data failed, err: %v", err) - } - - go loadDiskIO() - go loadNetIO() - - // 删除保留的记录 - // monitorStoreDays, _ := dborm.XormGetConfig("SystemMonitor", "MonitorStoreDays") - // if monitorStoreDays["value"] != "" { - // return - // } - // storeDays, err := strconv.Atoi(MonitorStoreDays.Value) - // if err != nil { - // timeForDelete := time.Now().AddDate(0, 0, -storeDays) - // DelMonitorBase(timeForDelete) - // DelMonitorIO(timeForDelete) - // DelMonitorNet(timeForDelete) - // } - -} - -func loadDiskIO() { - ioStat, _ := disk.IOCounters() - - time.Sleep(60 * time.Second) - - ioStat2, _ := disk.IOCounters() - var ioList []MonitorIO - for _, io2 := range ioStat2 { - for _, io1 := range ioStat { - if io2.Name == io1.Name { - var itemIO MonitorIO - itemIO.CreatedAt = time.Now() - itemIO.UpdatedAt = time.Now() - - itemIO.Name = io1.Name - if io2.ReadBytes != 0 && io1.ReadBytes != 0 && io2.ReadBytes > io1.ReadBytes { - itemIO.Read = uint64(float64(io2.ReadBytes-io1.ReadBytes) / 60) - } - if io2.WriteBytes != 0 && io1.WriteBytes != 0 && io2.WriteBytes > io1.WriteBytes { - itemIO.Write = uint64(float64(io2.WriteBytes-io1.WriteBytes) / 60) - } - - if io2.ReadCount != 0 && io1.ReadCount != 0 && io2.ReadCount > io1.ReadCount { - itemIO.Count = uint64(float64(io2.ReadCount-io1.ReadCount) / 60) - } - writeCount := uint64(0) - if io2.WriteCount != 0 && io1.WriteCount != 0 && io2.WriteCount > io1.WriteCount { - writeCount = uint64(float64(io2.WriteCount-io1.WriteCount) / 60) - } - if writeCount > itemIO.Count { - itemIO.Count = writeCount - } - - if io2.ReadTime != 0 && io1.ReadTime != 0 && io2.ReadTime > io1.ReadTime { - itemIO.Time = uint64(float64(io2.ReadTime-io1.ReadTime) / 60) - } - writeTime := uint64(0) - if io2.WriteTime != 0 && io1.WriteTime != 0 && io2.WriteTime > io1.WriteTime { - writeTime = uint64(float64(io2.WriteTime-io1.WriteTime) / 60) - } - if writeTime > itemIO.Time { - itemIO.Time = writeTime - } - ioList = append(ioList, itemIO) - break - } - } - } - _, err := dborm.DbClient.XEngine.Table("monitor_io").Insert(ioList) - if err != nil { - log.Errorf("Insert io monitoring data failed, err: %v", err) - } -} - -func loadNetIO() { - netStat, _ := net.IOCounters(true) - netStatAll, _ := net.IOCounters(false) - - time.Sleep(60 * time.Second) - - netStat2, _ := net.IOCounters(true) - var netList []MonitorNetwork - for _, net2 := range netStat2 { - for _, net1 := range netStat { - if net2.Name == net1.Name { - var itemNet MonitorNetwork - itemNet.CreatedAt = time.Now() - itemNet.UpdatedAt = time.Now() - - itemNet.Name = net1.Name - - if net2.BytesSent != 0 && net1.BytesSent != 0 && net2.BytesSent > net1.BytesSent { - itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / 60 - } - if net2.BytesRecv != 0 && net1.BytesRecv != 0 && net2.BytesRecv > net1.BytesRecv { - itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / 60 - } - netList = append(netList, itemNet) - break - } - } - } - netStatAll2, _ := net.IOCounters(false) - for _, net2 := range netStatAll2 { - for _, net1 := range netStatAll { - if net2.Name == net1.Name { - var itemNet MonitorNetwork - itemNet.Name = net1.Name - if net2.BytesSent != 0 && net1.BytesSent != 0 && net2.BytesSent > net1.BytesSent { - itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / 60 - } - - if net2.BytesRecv != 0 && net1.BytesRecv != 0 && net2.BytesRecv > net1.BytesRecv { - itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / 60 - } - netList = append(netList, itemNet) - break - } - } - } - - _, err := dborm.DbClient.XEngine.Table("monitor_network").Insert(netList) - if err != nil { - log.Errorf("Insert network monitoring data failed, err: %v", err) - } -} - -var c *cron.Cron -var monitorCronID int - -func init() { - c = cron.New() - c.Start() - monitorCronID = 0 -} - -// StartMonitor 开始监控任务 removeBefore删除上次任务,间隔interval分钟 -func StartMonitor(removeBefore bool, interval string) error { - if removeBefore { - c.Remove(cron.EntryID(monitorCronID)) - } - - // 读取配置 - if interval == "" { - v, err := dborm.XormGetConfig("SystemMonitor", "sampleTime") - if err != nil { - return err - } - data := make(map[string]any) - err = json.Unmarshal([]byte(v["value_json"].(string)), &data) - if err != nil { - log.Error("json StartMonitor:%s", err.Error()) - return err - } - interval = data["sampleTime"].(string) - } - - imservice := NewIMonitorService() - monitorID, err := c.AddJob(fmt.Sprintf("@every %sm", interval), imservice) - if err != nil { - return err - } - imservice.Run() - monitorCronID = int(monitorID) - return nil -} diff --git a/features/monitor/psnet/psnet.go b/features/monitor/psnet/psnet.go deleted file mode 100644 index fa0ca82..0000000 --- a/features/monitor/psnet/psnet.go +++ /dev/null @@ -1,93 +0,0 @@ -package psnet - -import ( - "fmt" - "net" - "net/http" - "time" - - "be.ems/lib/core/utils/ctx" - "be.ems/lib/log" - "be.ems/lib/services" - "be.ems/lib/wsinfo" - "be.ems/restagent/config" - "github.com/gorilla/websocket" - "github.com/shirou/gopsutil/process" -) - -var ( - // websockte通信 - UriWs = config.DefaultUriPrefix + "/monitor/{apiVersion}/psnet/ws" - // 停止进程 - UriStop = config.DefaultUriPrefix + "/monitor/{apiVersion}/psnet/stop" - - // 检查ip端口请求 - UriPing = config.DefaultUriPrefix + "/monitor/{apiVersion}/psnet/ping" -) - -// 进程管理 -var wsUpgrade = websocket.Upgrader{ - CheckOrigin: func(r *http.Request) bool { - return true - }, -} - -// ProcessWs -func ProcessWs(w http.ResponseWriter, r *http.Request) { - ws, err := wsUpgrade.Upgrade(w, r, nil) - if err != nil { - return - } - wsClient := wsinfo.NewWsClient("processClient", ws) - go wsClient.Read() - go wsClient.Write() -} - -// 停止进程 {"PID":30040} -func StopProcess(w http.ResponseWriter, r *http.Request) { - // json 請求參數獲取 - var bodyArgs struct { - PID int32 `json:"PID" validate:"required"` - } - err := ctx.ShouldBindJSON(r, &bodyArgs) - if err != nil { - log.Error("io.ReadAll is failed:", err) - services.ResponseErrorWithJson(w, 400, err.Error()) - return - } - - proc, err := process.NewProcess(bodyArgs.PID) - if err != nil { - services.ResponseErrorWithJson(w, 400, err.Error()) - return - } - if err := proc.Kill(); err != nil { - services.ResponseErrorWithJson(w, 400, err.Error()) - return - } - services.ResponseStatusOK200Null(w) -} - -// 检查ip端口请求 -func Ping(w http.ResponseWriter, r *http.Request) { - // json 請求參數獲取 - var bodyArgs struct { - Host string `json:"host" validate:"required"` - Port string `json:"port" validate:"required"` - } - err := ctx.ShouldBindJSON(r, &bodyArgs) - if err != nil { - log.Error("io.ReadAll is failed:", err) - services.ResponseErrorWithJson(w, 400, err.Error()) - return - } - - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", bodyArgs.Host, bodyArgs.Port), 3*time.Second) - if err != nil { - services.ResponseErrorWithJson(w, 400, err.Error()) - return - } - defer conn.Close() - services.ResponseStatusOK200Null(w) - -} diff --git a/features/pm/performance.go b/features/pm/performance.go index 6f17fcd..d717e79 100644 --- a/features/pm/performance.go +++ b/features/pm/performance.go @@ -65,16 +65,16 @@ type GoldKpi struct { } type KpiData struct { - ID int `json:"id" xorm:"pk 'id' '<-' autoincr"` - NEType string `json:"neType" xorm:"ne_type"` - NEName string `json:"neName" xorm:"ne_name"` - RmUid string `json:"rmUid" xorm:"rm_uid"` - Date string `json:"date" xorm:"date"` - StartTime time.Time `json:"startTime" xorm:"start_time"` - EndTime time.Time `json:"endTime" xorm:"end_time"` - Index int `json:"index" xorm:"index"` - Granularity int8 `json:"granularity" xorm:"granularity"` - KPIValues []KPIVal `json:"kpiValues" xorm:"json 'kpi_values'"` + ID int `json:"id" xorm:"pk 'id' '<-' autoincr"` + NEType string `json:"neType" xorm:"ne_type"` + NEName string `json:"neName" xorm:"ne_name"` + RmUid string `json:"rmUid" xorm:"rm_uid"` + Date string `json:"date" xorm:"date"` + StartTime string `json:"startTime" xorm:"start_time"` + EndTime string `json:"endTime" xorm:"end_time"` + Index int `json:"index" xorm:"index"` + Granularity int8 `json:"granularity" xorm:"granularity"` + KPIValues []KPIVal `json:"kpiValues" xorm:"json 'kpi_values'"` //CreatedAt int64 `json:"createdAt" xorm:"created 'created_at'"` CreatedAt int64 `json:"createdAt" xorm:"'created_at'"` } @@ -190,7 +190,7 @@ func GetDateTimeFromTimeString(fmtString string, timeString string) string { return t.Format(global.DateTime) } -// process alarm post message from NFs +// process KPI report post message from NFs func PostKPIReportFromNF(w http.ResponseWriter, r *http.Request) { log.Debug("PostKPIReportFromNF processing... ") @@ -202,6 +202,91 @@ func PostKPIReportFromNF(w http.ResponseWriter, r *http.Request) { return } + // 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 + } + //log.Trace("Request body:", string(body)) + kpiReport := new(KpiReport) + _ = json.Unmarshal(body, &kpiReport) + //log.Trace("kpiReport:", kpiReport) + + layout := time.RFC3339Nano + //kpiDate := GetDateFromTimeString(layout, kpiReport.Task.Period.StartTime) + kpiIndex, _ := strconv.Atoi(vars["index"]) + startTime := global.GetFmtTimeString(layout, kpiReport.Task.Period.StartTime, time.DateTime) + endTime := global.GetFmtTimeString(layout, kpiReport.Task.Period.EndTime, time.DateTime) + // get time granularity from startTime and endTime + seconds, _ := global.GetSecondDuration(startTime, endTime) + var granularity int8 = 60 + if seconds != 0 && seconds <= math.MaxInt8 && seconds >= math.MinInt8 { + granularity = int8(seconds) + } + + // 黄金指标事件对象 + kpiEvent := map[string]any{ + // kip_id ... + "neType": kpiReport.Task.NE.NeType, + "neName": kpiReport.Task.NE.NEName, + "startIndex": kpiIndex, + "timeGroup": startTime, + } + // insert into new kpi_report_xxx table + kpiData := new(KpiData) + kpiData.Date = startTime + kpiData.Index = kpiIndex + //stime, _ := time.ParseInLocation(time.RFC3339Nano, kpiReport.Task.Period.StartTime, time.Local) + //etime, _ := time.ParseInLocation(time.RFC3339Nano, kpiReport.Task.Period.EndTime, time.Local) + kpiData.StartTime = startTime + kpiData.EndTime = endTime + kpiData.Granularity = granularity + kpiData.NEName = kpiReport.Task.NE.NEName + kpiData.NEType = kpiReport.Task.NE.NeType + kpiData.RmUid = kpiReport.Task.NE.RmUID + kpiVal := new(KPIVal) + kpiData.CreatedAt = time.Now().UnixMilli() + for _, k := range kpiReport.Task.NE.KPIs { + kpiEvent[k.KPIID] = k.Value // kip_id + + kpiVal.KPIID = k.KPIID + kpiVal.Value = int64(k.Value) + kpiVal.Err = k.Err + kpiData.KPIValues = append(kpiData.KPIValues, *kpiVal) + } + + // insert kpi_report table, no session + tableName := "kpi_report_" + strings.ToLower(kpiReport.Task.NE.NeType) + affected, err := xEngine.Table(tableName).Insert(kpiData) + if err != nil && affected <= 0 { + log.Errorf("Failed to insert %s:%v", tableName, err) + services.ResponseInternalServerError500ProcessError(w, err) + return + } + + // 推送到ws订阅组 + wsService.NewWSSendImpl.ByGroupID(wsService.GROUP_KPI, kpiEvent) + if kpiReport.Task.NE.NeType == "UPF" { + wsService.NewWSSendImpl.ByGroupID(wsService.GROUP_KPI_UPF, kpiEvent) + } + + services.ResponseStatusOK204NoContent(w) +} + +// post kpi report from NEs, insert insto gold_kpi table, discard... +func PostGoldKPIFromNF(w http.ResponseWriter, r *http.Request) { + log.Debug("PostKPIReportFromNF processing... ") + + vars := mux.Vars(r) + apiVer := vars["apiVersion"] + if apiVer != global.ApiVersionV1 { + log.Error("Uri api version is invalid. apiVersion:", apiVer) + services.ResponseNotFound404UriNotExist(w, r) + return + } + // body, err := io.ReadAll(io.LimitReader(r.Body, global.RequestBodyMaxLen)) body, err := io.ReadAll(io.LimitReader(r.Body, global.RequestBodyMaxLen)) if err != nil { @@ -244,12 +329,10 @@ func PostKPIReportFromNF(w http.ResponseWriter, r *http.Request) { kpiData := new(KpiData) kpiData.Date = goldKpi.Date kpiData.Index = goldKpi.Index - st, _ := time.ParseInLocation(time.RFC3339Nano, kpiReport.Task.Period.StartTime, time.Local) - et, _ := time.ParseInLocation(time.RFC3339Nano, kpiReport.Task.Period.EndTime, time.Local) - // kpiData.StartTime = goldKpi.StartTime - // kpiData.EndTime = goldKpi.EndTime - kpiData.StartTime = st - kpiData.EndTime = et + //st, _ := time.ParseInLocation(time.RFC3339Nano, kpiReport.Task.Period.StartTime, time.Local) + //et, _ := time.ParseInLocation(time.RFC3339Nano, kpiReport.Task.Period.EndTime, time.Local) + kpiData.StartTime = goldKpi.StartTime + kpiData.EndTime = goldKpi.EndTime kpiData.Granularity = goldKpi.Granularity kpiData.NEName = goldKpi.NEName kpiData.NEType = goldKpi.NEType @@ -969,9 +1052,9 @@ func PostMeasurementFromNF(w http.ResponseWriter, r *http.Request) { layout := time.RFC3339 measurement.Date = GetDateFromTimeString(layout, measurement.StartTime) measurement.StartTime = GetDateTimeFromTimeString(layout, measurement.StartTime) - affected, err := session.Table("northbound_pm").Insert(measurement) + affected, err := session.Table("nbi_pm").Insert(measurement) if err != nil && affected <= 0 { - log.Error("Failed to insert northbound_pm:", err) + log.Error("Failed to insert nbi_pm:", err) services.ResponseInternalServerError500DatabaseOperationFailed(w) return } @@ -1052,9 +1135,9 @@ func GetMeasurementFromNF(w http.ResponseWriter, r *http.Request) { layout := time.RFC3339 measurement.Date = GetDateFromTimeString(layout, measurement.StartTime) measurement.StartTime = GetDateTimeFromTimeString(layout, measurement.StartTime) - affected, err := session.Table("northbound_pm").Insert(measurement) + affected, err := session.Table("nbi_pm").Insert(measurement) if err != nil && affected <= 0 { - log.Error("Failed to insert northbound_pm:", err) + log.Error("Failed to insert nbi_pm:", err) services.ResponseInternalServerError500DatabaseOperationFailed(w) return } diff --git a/features/security/account.go b/features/security/account.go index c1190fc..9440aba 100644 --- a/features/security/account.go +++ b/features/security/account.go @@ -2,30 +2,16 @@ package security import ( "encoding/json" - "fmt" - "image/color" "io" "net/http" - "strconv" "strings" - "time" - "be.ems/features/security/service" - sysConfigService "be.ems/features/sys_config/service" - "be.ems/lib/core/account" - "be.ems/lib/core/cache" - "be.ems/lib/core/constants/cachekey" - "be.ems/lib/core/utils/ctx" - "be.ems/lib/core/vo/result" "be.ems/lib/dborm" "be.ems/lib/global" "be.ems/lib/log" "be.ems/lib/oauth" "be.ems/lib/services" "be.ems/restagent/config" - srcConfig "be.ems/src/framework/config" - "be.ems/src/framework/redis" - "github.com/mojocn/base64Captcha" ) var ( @@ -34,22 +20,6 @@ var ( CustomUriOauthToken = config.UriPrefix + "/securityManagement/{apiVersion}/{elementTypeValue}/token" CustomUriOauthHandshake = config.UriPrefix + "/securityManagement/{apiVersion}/{elementTypeValue}/handshake" - - // 系统登录 - UriLogin = config.DefaultUriPrefix + "/securityManagement/{apiVersion}/login" - CustomUriLogin = config.UriPrefix + "/securityManagement/{apiVersion}/login" - - // 获取验证码 - UriCaptchaImage = config.DefaultUriPrefix + "/securityManagement/{apiVersion}/captchaImage" - CustomUriCaptchaImage = config.UriPrefix + "/securityManagement/{apiVersion}/captchaImage" - - // 登录用户信息 - UriUserInfo = config.DefaultUriPrefix + "/securityManagement/{apiVersion}/getUserInfo" - CustomUriUserInfo = config.UriPrefix + "/securityManagement/{apiVersion}/getUserInfo" - - // 登录用户路由信息 - UriRouters = config.DefaultUriPrefix + "/securityManagement/{apiVersion}/getRouters" - CustomUriRouters = config.UriPrefix + "/securityManagement/{apiVersion}/getRouters" ) func LoginFromOMC(w http.ResponseWriter, r *http.Request) { @@ -134,14 +104,8 @@ func LoginFromOMC(w http.ResponseWriter, r *http.Request) { } if user != nil { - // 缓存用户信息 - account.CacheLoginUser(user) - redis.SetByExpire("", "session_token", token, time.Second*1800) - // 角色权限集合,管理员拥有所有权限 - userId := fmt.Sprint(user.Id) - isAdmin := srcConfig.IsAdmin(userId) - roles, perms := service.NewServiceAccount.RoleAndMenuPerms(userId, isAdmin) - services.ResponseStatusOK200LoginWhitRP(w, token, user, roles, perms) + empty := []string{} + services.ResponseStatusOK200LoginWhitRP(w, token, user, empty, empty) return } services.ResponseBadRequest400IncorrectLogin(w) @@ -223,189 +187,3 @@ func HandshakeFromOMC(w http.ResponseWriter, r *http.Request) { } services.ResponseStatusOK200Null(w) } - -// 系统登录 -// -// POST /login -func LoginOMC(w http.ResponseWriter, r *http.Request) { - log.Info("LoginOMC processing... ") - var body struct { - Username string `json:"username" binding:"required"` // Username 用户名 - Password string `json:"password" binding:"required"` // Password 用户密码 - Code string `json:"code"` // Code 验证码 - UUID string `json:"uuid"` // UUID 验证码唯一标识 - } - err := ctx.ShouldBindJSON(r, &body) - if err != nil { - log.Error("Invalid Json Format") - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - // response 400-5 - if body.Username == "" || body.Password == "" { - log.Error("Wrong parameter value") - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 校验验证码 - // 从数据库配置获取验证码开关 true开启,false关闭 - captchaEnabledStr := sysConfigService.NewServiceSysConfig.SelectConfigValueByKey("sys.account.captchaEnabled") - captchaEnabled, err := strconv.ParseBool(captchaEnabledStr) - if err != nil { - captchaEnabled = false - } - if captchaEnabled { - if body.Code == "" || body.UUID == "" { - log.Error("Authentication failed, mismatch captcha") - ctx.JSON(w, 400, result.CodeMsg(400, "Verification code information error")) - return - } - verifyKey := cachekey.CAPTCHA_CODE_KEY + body.UUID - captcha, ok := cache.GetLocalTTL(verifyKey) - if captcha == nil || !ok { - log.Error("Authentication failed, captcha emtry") - ctx.JSON(w, 400, result.CodeMsg(400, "The verification code has expired")) - return - } - cache.DeleteLocalTTL(verifyKey) - if captcha.(string) != body.Code { - log.Error("Authentication failed, not match captcha") - ctx.JSON(w, 400, result.CodeMsg(400, "Verification code error")) - return - } - } - - validUser, user, err := dborm.XormCheckLoginUser(body.Username, body.Password, config.GetYamlConfig().Auth.Crypt) - if !validUser || err != nil { - // response 400-4 - log.Error("Authentication failed, mismatch user or password") - ctx.JSON(w, 400, result.CodeMsg(400, err.Error())) - return - } - - token := oauth.GenRandToken("omc") // Generate new token to session ID - sourceAddr := r.RemoteAddr[:strings.Index(r.RemoteAddr, ":")] - affected, err := dborm.XormInsertSession(body.Username, sourceAddr, token, - config.GetExpiresFromConfig(), config.GetYamlConfig().Auth.Session) - if err != nil { - log.Error("Failed to XormInsertSession:", err) - if affected == -1 { - services.ResponseForbidden403MultiLoginNotAllowed(w) - } else { - services.ResponseBadRequest400IncorrectLogin(w) - } - - return - } - - if user != nil { - // 缓存用户信息 - account.CacheLoginUser(user) - redis.SetByExpire("", "session_token", token, time.Second*1800) - ctx.JSON(w, 200, result.OkData(map[string]any{ - "accessToken": token, - })) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 获取验证码 -// -// GET /captchaImage -func CaptchaImage(w http.ResponseWriter, r *http.Request) { - configService := sysConfigService.NewServiceSysConfig - - // 从数据库配置获取验证码开关 true开启,false关闭 - captchaEnabledStr := configService.SelectConfigValueByKey("sys.account.captchaEnabled") - captchaEnabled, err := strconv.ParseBool(captchaEnabledStr) - if err != nil { - captchaEnabled = false - } - if !captchaEnabled { - ctx.JSON(w, 200, result.Ok(map[string]any{ - "captchaEnabled": captchaEnabled, - })) - return - } - - // 生成唯一标识 - verifyKey := "" - data := map[string]any{ - "captchaEnabled": captchaEnabled, - "uuid": "", - "img": "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7", - } - - // char 字符验证 - driverCaptcha := &base64Captcha.DriverString{ - //Height png height in pixel. - Height: 40, - // Width Captcha png width in pixel. - Width: 120, - //NoiseCount text noise count. - NoiseCount: 4, - //Length random string length. - Length: 4, - //Source is a unicode which is the rand string from. - Source: "023456789abcdefghjkmnprstuvwxyz", - //ShowLineOptions := OptionShowHollowLine | OptionShowSlimeLine | OptionShowSineLine . - ShowLineOptions: base64Captcha.OptionShowHollowLine, - //BgColor captcha image background color (optional) - BgColor: &color.RGBA{ - R: 250, - G: 250, - B: 250, - A: 255, // 不透明 - }, - } - // 验证码生成 - id, question, answer := driverCaptcha.GenerateIdQuestionAnswer() - // 验证码表达式解析输出 - item, err := driverCaptcha.DrawCaptcha(question) - if err != nil { - log.Infof("Generate Id Question Answer %s : %v", question, err) - } else { - data["uuid"] = id - data["img"] = item.EncodeB64string() - verifyKey = cachekey.CAPTCHA_CODE_KEY + id - cache.SetLocalTTL(verifyKey, answer, 120*time.Second) - } - - // 本地开发下返回验证码结果,方便接口调试 - // text, ok := cache.GetLocalTTL(verifyKey) - // if ok { - // data["text"] = text.(string) - // } - - ctx.JSON(w, 200, result.Ok(data)) -} - -// 登录用户信息 -func UserInfo(w http.ResponseWriter, r *http.Request) { - loginUser, err := ctx.LoginUser(r) - if err != nil { - ctx.JSON(w, 200, result.OkData(err.Error())) - } - // 角色权限集合,管理员拥有所有权限 - userId := fmt.Sprint(loginUser.UserID) - isAdmin := srcConfig.IsAdmin(userId) - roles, perms := service.NewServiceAccount.RoleAndMenuPerms(userId, isAdmin) - - ctx.JSON(w, 200, result.OkData(map[string]any{ - "user": loginUser.User, - "roles": roles, - "permissions": perms, - })) -} - -// 登录用户路由信息 -func Routers(w http.ResponseWriter, r *http.Request) { - userID := ctx.LoginUserToUserID(r) - - // 前端路由,管理员拥有所有 - isAdmin := srcConfig.IsAdmin(userID) - buildMenus := service.NewServiceAccount.RouteMenus(userID, isAdmin) - ctx.JSON(w, 200, result.OkData(buildMenus)) -} diff --git a/features/security/service/service_account.go b/features/security/service/service_account.go deleted file mode 100644 index 28aa2c8..0000000 --- a/features/security/service/service_account.go +++ /dev/null @@ -1,56 +0,0 @@ -package service - -import ( - menuService "be.ems/features/sys_menu/service" - roleService "be.ems/features/sys_role/service" - userService "be.ems/features/sys_user/service" - "be.ems/lib/core/utils/parse" - "be.ems/lib/core/vo" -) - -// 实例化服务层 ServiceAccount 结构体 -var NewServiceAccount = &ServiceAccount{ - sysUserService: userService.NewServiceSysUser, - sysRoleService: roleService.NewServiceSysRole, - sysMenuService: menuService.NewServiceSysMenu, -} - -// 账号身份操作服务 服务层处理 -type ServiceAccount struct { - // 用户信息服务 - sysUserService *userService.ServiceSysUser - // 角色服务 - sysRoleService *roleService.ServiceSysRole - // 菜单服务 - sysMenuService *menuService.ServiceSysMenu -} - -// RoleAndMenuPerms 角色和菜单数据权限 -func (s *ServiceAccount) RoleAndMenuPerms(userId string, isAdmin bool) ([]string, []string) { - if isAdmin { - return []string{"admin"}, []string{"*:*:*"} - } else { - // 角色key - roleGroup := []string{} - roles := s.sysRoleService.SelectRoleListByUserId(userId) - for _, role := range roles { - roleGroup = append(roleGroup, role.RoleKey) - } - // 菜单权限key - perms := s.sysMenuService.SelectMenuPermsByUserId(userId) - return parse.RemoveDuplicates(roleGroup), parse.RemoveDuplicates(perms) - } -} - -// RouteMenus 前端路由所需要的菜单 -func (s *ServiceAccount) RouteMenus(userId string, isAdmin bool) []vo.Router { - var buildMenus []vo.Router - if isAdmin { - menus := s.sysMenuService.SelectMenuTreeByUserId("*") - buildMenus = s.sysMenuService.BuildRouteMenus(menus, "") - } else { - menus := s.sysMenuService.SelectMenuTreeByUserId(userId) - buildMenus = s.sysMenuService.BuildRouteMenus(menus, "") - } - return buildMenus -} diff --git a/features/sys_config/api_sys_config.go b/features/sys_config/api_sys_config.go deleted file mode 100644 index b8cbb5f..0000000 --- a/features/sys_config/api_sys_config.go +++ /dev/null @@ -1,230 +0,0 @@ -package sysconfig - -import ( - "fmt" - "net/http" - "strings" - - "be.ems/features/sys_config/model" - "be.ems/features/sys_config/service" - "be.ems/lib/core/utils/ctx" - "be.ems/lib/core/utils/parse" - "be.ems/lib/core/vo/result" - "be.ems/lib/midware" - "be.ems/lib/services" - "be.ems/restagent/config" -) - -// 参数配置信息接口添加到路由 -func Routers() []services.RouterItem { - // 实例化控制层 SysConfigApi 结构体 - var apis = &SysConfigApi{ - sysConfigService: service.NewServiceSysConfig, - } - - rs := [...]services.RouterItem{ - { - Method: "GET", - Pattern: "/configs", - Handler: apis.List, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/config/{configId}", - Handler: apis.Info, - Middleware: midware.Authorize(nil), - }, - { - Method: "POST", - Pattern: "/config", - Handler: apis.Add, - Middleware: midware.Authorize(nil), - }, - { - Method: "PUT", - Pattern: "/config", - Handler: apis.Edit, - Middleware: midware.Authorize(nil), - }, - { - Method: "DELETE", - Pattern: "/config/{configIds}", - Handler: apis.Remove, - Middleware: midware.Authorize(nil), - }, - { - Method: "PUT", - Pattern: "/config/refreshCache", - Handler: apis.RefreshCache, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/config/configKey/{configKey}", - Handler: apis.ConfigKey, - Middleware: midware.Authorize(nil), - }, - // 添加更多的 Router 对象... - } - - // 生成两组前缀路由 - rsPrefix := []services.RouterItem{} - for _, v := range rs { - path := "/configManage/{apiVersion}" + v.Pattern - // 固定前缀 - v.Pattern = config.DefaultUriPrefix + path - rsPrefix = append(rsPrefix, v) - // 可配置 - v.Pattern = config.UriPrefix + path - rsPrefix = append(rsPrefix, v) - } - return rsPrefix -} - -// 参数配置信息 -// -// PATH /configManage -type SysConfigApi struct { - // 参数配置服务 - sysConfigService *service.ServiceSysConfig -} - -// 参数配置列表 -// -// GET /list -func (s *SysConfigApi) List(w http.ResponseWriter, r *http.Request) { - querys := ctx.QueryMap(r) - data := s.sysConfigService.SelectConfigPage(querys) - ctx.JSON(w, 200, result.Ok(data)) -} - -// 参数配置信息 -// -// GET /:configId -func (s *SysConfigApi) Info(w http.ResponseWriter, r *http.Request) { - configId := ctx.Param(r, "configId") - if configId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - data := s.sysConfigService.SelectConfigById(configId) - if data.ConfigID == configId { - ctx.JSON(w, 200, result.OkData(data)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 参数配置新增 -// -// POST / -func (s *SysConfigApi) Add(w http.ResponseWriter, r *http.Request) { - var body model.SysConfig - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.ConfigID != "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查属性值唯一 - uniqueConfigKey := s.sysConfigService.CheckUniqueConfigKey(body.ConfigKey, "") - if !uniqueConfigKey { - msg := fmt.Sprintf("[%s] Parameter key name already exists", body.ConfigKey) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - body.CreateBy = ctx.LoginUserToUserName(r) - insertId := s.sysConfigService.InsertConfig(body) - if insertId != "" { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 参数配置修改 -// -// PUT / -func (s *SysConfigApi) Edit(w http.ResponseWriter, r *http.Request) { - var body model.SysConfig - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.ConfigID == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查属性值唯一 - uniqueConfigKey := s.sysConfigService.CheckUniqueConfigKey(body.ConfigKey, body.ConfigID) - if !uniqueConfigKey { - msg := fmt.Sprintf("[%s] Parameter key name already exists", body.ConfigKey) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 检查是否存在 - config := s.sysConfigService.SelectConfigById(body.ConfigID) - if config.ConfigID != body.ConfigID { - ctx.JSON(w, 200, result.ErrMsg("No permission to access parameter configuration data!")) - return - } - - body.UpdateBy = ctx.LoginUserToUserName(r) - rows := s.sysConfigService.UpdateConfig(body) - if rows > 0 { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 参数配置删除 -// -// DELETE /:configIds -func (s *SysConfigApi) Remove(w http.ResponseWriter, r *http.Request) { - configIds := ctx.Param(r, "configIds") - if configIds == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - // 处理字符转id数组后去重 - ids := strings.Split(configIds, ",") - uniqueIDs := parse.RemoveDuplicates(ids) - if len(uniqueIDs) <= 0 { - ctx.JSON(w, 200, result.Err(nil)) - return - } - rows, err := s.sysConfigService.DeleteConfigByIds(uniqueIDs) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - msg := fmt.Sprintf("删除成功:%d", rows) - ctx.JSON(w, 200, result.OkMsg(msg)) -} - -// 参数配置刷新缓存 -// -// PUT /refreshCache -func (s *SysConfigApi) RefreshCache(w http.ResponseWriter, r *http.Request) { - s.sysConfigService.ResetConfigCache() - ctx.JSON(w, 200, result.Ok(nil)) -} - -// 参数配置根据参数键名 -// -// GET /configKey/:configKey -func (s *SysConfigApi) ConfigKey(w http.ResponseWriter, r *http.Request) { - configKey := ctx.Param(r, "configKey") - if configKey == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - key := s.sysConfigService.SelectConfigValueByKey(configKey) - if key != "" { - ctx.JSON(w, 200, result.OkData(key)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} diff --git a/features/sys_config/model/sys_config.go b/features/sys_config/model/sys_config.go deleted file mode 100644 index 8949c67..0000000 --- a/features/sys_config/model/sys_config.go +++ /dev/null @@ -1,25 +0,0 @@ -package model - -// 参数配置对象 sys_config -type SysConfig struct { - // 参数主键 - ConfigID string `json:"configId"` - // 参数名称 - ConfigName string `json:"configName" binding:"required"` - // 参数键名 - ConfigKey string `json:"configKey" binding:"required"` - // 参数键值 - ConfigValue string `json:"configValue" binding:"required"` - // 系统内置(Y是 N否) - ConfigType string `json:"configType"` - // 创建者 - CreateBy string `json:"createBy"` - // 创建时间 - CreateTime int64 `json:"createTime"` - // 更新者 - UpdateBy string `json:"updateBy"` - // 更新时间 - UpdateTime int64 `json:"updateTime"` - // 备注 - Remark string `json:"remark"` -} diff --git a/features/sys_config/service/repo_sys_config.go b/features/sys_config/service/repo_sys_config.go deleted file mode 100644 index c406526..0000000 --- a/features/sys_config/service/repo_sys_config.go +++ /dev/null @@ -1,336 +0,0 @@ -package service - -import ( - "fmt" - "strings" - "time" - - "be.ems/features/sys_config/model" - "be.ems/lib/core/datasource" - "be.ems/lib/core/utils/date" - "be.ems/lib/core/utils/parse" - "be.ems/lib/log" -) - -// 实例化数据层 RepoSysConfig 结构体 -var NewRepoSysConfig = &RepoSysConfig{ - selectSql: `select - config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark - from sys_config`, - - resultMap: map[string]string{ - "config_id": "ConfigID", - "config_name": "ConfigName", - "config_key": "ConfigKey", - "config_value": "ConfigValue", - "config_type": "ConfigType", - "remark": "Remark", - "create_by": "CreateBy", - "create_time": "CreateTime", - "update_by": "UpdateBy", - "update_time": "UpdateTime", - }, -} - -// RepoSysConfig 参数配置表 数据层处理 -type RepoSysConfig struct { - // 查询视图对象SQL - selectSql string - // 结果字段与实体映射 - resultMap map[string]string -} - -// convertResultRows 将结果记录转实体结果组 -func (r *RepoSysConfig) convertResultRows(rows []map[string]any) []model.SysConfig { - arr := make([]model.SysConfig, 0) - for _, row := range rows { - sysConfig := model.SysConfig{} - for key, value := range row { - if keyMapper, ok := r.resultMap[key]; ok { - datasource.SetFieldValue(&sysConfig, keyMapper, value) - } - } - arr = append(arr, sysConfig) - } - return arr -} - -// SelectDictDataPage 分页查询参数配置列表数据 -func (r *RepoSysConfig) SelectConfigPage(query map[string]any) map[string]any { - // 查询条件拼接 - var conditions []string - var params []any - if v, ok := query["configName"]; ok && v != "" { - conditions = append(conditions, "config_name like concat(?, '%')") - params = append(params, v) - } - if v, ok := query["configType"]; ok && v != "" { - conditions = append(conditions, "config_type = ?") - params = append(params, v) - } - if v, ok := query["configKey"]; ok && v != "" { - conditions = append(conditions, "config_key like concat(?, '%')") - params = append(params, v) - } - beginTime, ok := query["beginTime"] - if !ok { - beginTime, ok = query["params[beginTime]"] - } - if ok && beginTime != "" { - conditions = append(conditions, "create_time >= ?") - beginDate := date.ParseStrToDate(beginTime.(string), date.YYYY_MM_DD) - params = append(params, beginDate.UnixMilli()) - } - endTime, ok := query["endTime"] - if !ok { - endTime, ok = query["params[endTime]"] - } - if ok && endTime != "" { - conditions = append(conditions, "create_time <= ?") - endDate := date.ParseStrToDate(endTime.(string), date.YYYY_MM_DD) - params = append(params, endDate.UnixMilli()) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数量 长度为0直接返回 - totalSql := "select count(1) as 'total' from sys_config" - totalRows, err := datasource.RawDB("", totalSql+whereSql, params) - if err != nil { - log.Errorf("total err => %v", err) - } - total := parse.Number(totalRows[0]["total"]) - if total == 0 { - return map[string]any{ - "total": total, - "rows": []model.SysConfig{}, - } - } - - // 分页 - pageNum, pageSize := datasource.PageNumSize(query["pageNum"], query["pageSize"]) - pageSql := " limit ?,? " - params = append(params, pageNum*pageSize) - params = append(params, pageSize) - - // 查询数据 - querySql := r.selectSql + whereSql + pageSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - } - - // 转换实体 - rows := r.convertResultRows(results) - return map[string]any{ - "total": total, - "rows": rows, - } -} - -// SelectConfigList 查询参数配置列表 -func (r *RepoSysConfig) SelectConfigList(sysConfig model.SysConfig) []model.SysConfig { - // 查询条件拼接 - var conditions []string - var params []any - if sysConfig.ConfigName != "" { - conditions = append(conditions, "config_name like concat(?, '%')") - params = append(params, sysConfig.ConfigName) - } - if sysConfig.ConfigType != "" { - conditions = append(conditions, "config_type = ?") - params = append(params, sysConfig.ConfigType) - } - if sysConfig.ConfigKey != "" { - conditions = append(conditions, "config_key like concat(?, '%')") - params = append(params, sysConfig.ConfigKey) - } - if sysConfig.CreateTime > 0 { - conditions = append(conditions, "create_time >= ?") - params = append(params, sysConfig.CreateTime) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数据 - querySql := r.selectSql + whereSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysConfig{} - } - - // 转换实体 - return r.convertResultRows(results) -} - -// SelectConfigValueByKey 通过参数键名查询参数键值 -func (r *RepoSysConfig) SelectConfigValueByKey(configKey string) string { - querySql := "select config_value as 'str' from sys_config where config_key = ?" - results, err := datasource.RawDB("", querySql, []any{configKey}) - if err != nil { - log.Errorf("query err => %v", err) - return "" - } - if len(results) > 0 { - return fmt.Sprint(results[0]["str"]) - } - return "" -} - -// SelectConfigByIds 通过配置ID查询参数配置信息 -func (r *RepoSysConfig) SelectConfigByIds(configIds []string) []model.SysConfig { - placeholder := datasource.KeyPlaceholderByQuery(len(configIds)) - querySql := r.selectSql + " where config_id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(configIds) - results, err := datasource.RawDB("", querySql, parameters) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysConfig{} - } - // 转换实体 - return r.convertResultRows(results) -} - -// CheckUniqueConfig 校验配置参数是否唯一 -func (r *RepoSysConfig) CheckUniqueConfig(sysConfig model.SysConfig) string { - // 查询条件拼接 - var conditions []string - var params []any - if sysConfig.ConfigKey != "" { - conditions = append(conditions, "config_key = ?") - params = append(params, sysConfig.ConfigKey) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } else { - return "" - } - - // 查询数据 - querySql := "select config_id as 'str' from sys_config " + whereSql + " limit 1" - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err %v", err) - return "" - } - if len(results) > 0 { - return fmt.Sprint(results[0]["str"]) - } - return "" -} - -// InsertConfig 新增参数配置 -func (r *RepoSysConfig) InsertConfig(sysConfig model.SysConfig) string { - // 参数拼接 - params := make(map[string]any) - if sysConfig.ConfigName != "" { - params["config_name"] = sysConfig.ConfigName - } - if sysConfig.ConfigKey != "" { - params["config_key"] = sysConfig.ConfigKey - } - if sysConfig.ConfigValue != "" { - params["config_value"] = sysConfig.ConfigValue - } - if sysConfig.ConfigType != "" { - params["config_type"] = sysConfig.ConfigType - } - if sysConfig.Remark != "" { - params["remark"] = sysConfig.Remark - } - if sysConfig.CreateBy != "" { - params["create_by"] = sysConfig.CreateBy - params["create_time"] = time.Now().UnixMilli() - } - - // 构建执行语句 - keys, placeholder, values := datasource.KeyPlaceholderValueByInsert(params) - sql := "insert into sys_config (" + strings.Join(keys, ",") + ")values(" + placeholder + ")" - - // 执行插入 - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - - insertId, err := results.LastInsertId() - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - return fmt.Sprint(insertId) -} - -// UpdateConfig 修改参数配置 -func (r *RepoSysConfig) UpdateConfig(sysConfig model.SysConfig) int64 { - // 参数拼接 - params := make(map[string]any) - if sysConfig.ConfigName != "" { - params["config_name"] = sysConfig.ConfigName - } - if sysConfig.ConfigKey != "" { - params["config_key"] = sysConfig.ConfigKey - } - if sysConfig.ConfigValue != "" { - params["config_value"] = sysConfig.ConfigValue - } - if sysConfig.ConfigType != "" { - params["config_type"] = sysConfig.ConfigType - } - if sysConfig.Remark != "" { - params["remark"] = sysConfig.Remark - } - if sysConfig.UpdateBy != "" { - params["update_by"] = sysConfig.UpdateBy - params["update_time"] = time.Now().UnixMilli() - } - - // 构建执行语句 - keys, values := datasource.KeyValueByUpdate(params) - sql := "update sys_config set " + strings.Join(keys, ",") + " where config_id = ?" - - // 执行更新 - values = append(values, sysConfig.ConfigID) - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("update row : %v", err.Error()) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("update err => %v", err) - return 0 - } - return affected -} - -// DeleteConfigByIds 批量删除参数配置信息 -func (r *RepoSysConfig) DeleteConfigByIds(configIds []string) int64 { - placeholder := datasource.KeyPlaceholderByQuery(len(configIds)) - sql := "delete from sys_config where config_id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(configIds) - results, err := datasource.ExecDB("", sql, parameters) - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - return affected -} diff --git a/features/sys_config/service/service_sys_config.go b/features/sys_config/service/service_sys_config.go deleted file mode 100644 index ede49ab..0000000 --- a/features/sys_config/service/service_sys_config.go +++ /dev/null @@ -1,155 +0,0 @@ -package service - -import ( - "errors" - - "be.ems/features/sys_config/model" - "be.ems/lib/core/cache" - "be.ems/lib/core/constants/cachekey" -) - -// 实例化服务层 ServiceSysConfig 结构体 -var NewServiceSysConfig = &ServiceSysConfig{ - sysConfigRepository: NewRepoSysConfig, -} - -// ServiceSysConfig 参数配置 服务层处理 -type ServiceSysConfig struct { - // 参数配置表 - sysConfigRepository *RepoSysConfig -} - -// SelectDictDataPage 分页查询参数配置列表数据 -func (r *ServiceSysConfig) SelectConfigPage(query map[string]any) map[string]any { - return r.sysConfigRepository.SelectConfigPage(query) -} - -// SelectConfigList 查询参数配置列表 -func (r *ServiceSysConfig) SelectConfigList(sysConfig model.SysConfig) []model.SysConfig { - return r.sysConfigRepository.SelectConfigList(sysConfig) -} - -// SelectConfigValueByKey 通过参数键名查询参数键值 -func (r *ServiceSysConfig) SelectConfigValueByKey(configKey string) string { - cacheKey := r.getCacheKey(configKey) - // 从缓存中读取 - cacheValue, ok := cache.GetLocal(cacheKey) - if cacheValue != nil && ok { - return cacheValue.(string) - } - // 无缓存时读取数据放入缓存中 - configValue := r.sysConfigRepository.SelectConfigValueByKey(configKey) - if configValue != "" { - cache.SetLocal(cacheKey, configValue) - return configValue - } - return "" -} - -// SelectConfigById 通过配置ID查询参数配置信息 -func (r *ServiceSysConfig) SelectConfigById(configId string) model.SysConfig { - if configId == "" { - return model.SysConfig{} - } - configs := r.sysConfigRepository.SelectConfigByIds([]string{configId}) - if len(configs) > 0 { - return configs[0] - } - return model.SysConfig{} -} - -// CheckUniqueConfigKey 校验参数键名是否唯一 -func (r *ServiceSysConfig) CheckUniqueConfigKey(configKey, configId string) bool { - uniqueId := r.sysConfigRepository.CheckUniqueConfig(model.SysConfig{ - ConfigKey: configKey, - }) - if uniqueId == configId { - return true - } - return uniqueId == "" -} - -// InsertConfig 新增参数配置 -func (r *ServiceSysConfig) InsertConfig(sysConfig model.SysConfig) string { - configId := r.sysConfigRepository.InsertConfig(sysConfig) - if configId != "" { - r.loadingConfigCache(sysConfig.ConfigKey) - } - return configId -} - -// UpdateConfig 修改参数配置 -func (r *ServiceSysConfig) UpdateConfig(sysConfig model.SysConfig) int64 { - rows := r.sysConfigRepository.UpdateConfig(sysConfig) - if rows > 0 { - r.loadingConfigCache(sysConfig.ConfigKey) - } - return rows -} - -// DeleteConfigByIds 批量删除参数配置信息 -func (r *ServiceSysConfig) DeleteConfigByIds(configIds []string) (int64, error) { - // 检查是否存在 - configs := r.sysConfigRepository.SelectConfigByIds(configIds) - if len(configs) <= 0 { - return 0, errors.New("does not have permission to access parameter configuration data") - } - for _, config := range configs { - // 检查是否为内置参数 - if config.ConfigType == "Y" { - return 0, errors.New(config.ConfigID + " Configuration parameters are built-in parameters and their deletion is prohibited!") - } - // 清除缓存 - r.clearConfigCache(config.ConfigKey) - } - if len(configs) == len(configIds) { - rows := r.sysConfigRepository.DeleteConfigByIds(configIds) - return rows, nil - } - return 0, errors.New("failed to delete parameter configuration information") -} - -// ResetConfigCache 重置参数缓存数据 -func (r *ServiceSysConfig) ResetConfigCache() { - r.clearConfigCache("*") - r.loadingConfigCache("*") -} - -// getCacheKey 组装缓存key -func (r *ServiceSysConfig) getCacheKey(configKey string) string { - return cachekey.SYS_CONFIG_KEY + configKey -} - -// loadingConfigCache 加载参数缓存数据 -func (r *ServiceSysConfig) loadingConfigCache(configKey string) { - // 查询全部参数 - if configKey == "*" { - sysConfigs := r.SelectConfigList(model.SysConfig{}) - for _, v := range sysConfigs { - key := r.getCacheKey(v.ConfigKey) - cache.DeleteLocal(key) - cache.SetLocal(key, v.ConfigValue) - } - return - } - // 指定参数 - if configKey != "" { - cacheValue := r.sysConfigRepository.SelectConfigValueByKey(configKey) - if cacheValue != "" { - key := r.getCacheKey(configKey) - cache.DeleteLocal(key) - cache.SetLocal(key, cacheValue) - } - return - } -} - -// clearConfigCache 清空参数缓存数据 -func (r *ServiceSysConfig) clearConfigCache(configKey string) bool { - key := r.getCacheKey(configKey) - keys := cache.GetLocalKeys(key) - for _, v := range keys { - cache.DeleteLocal(v) - } - return len(keys) > 0 -} diff --git a/features/sys_dict_data/api_sys_dict_data.go b/features/sys_dict_data/api_sys_dict_data.go deleted file mode 100644 index 748ed55..0000000 --- a/features/sys_dict_data/api_sys_dict_data.go +++ /dev/null @@ -1,247 +0,0 @@ -package sysdictdata - -import ( - "fmt" - "net/http" - "strings" - - "be.ems/features/sys_dict_data/model" - sysDictDataService "be.ems/features/sys_dict_data/service" - sysDictTypeService "be.ems/features/sys_dict_type/service" - "be.ems/lib/core/utils/ctx" - "be.ems/lib/core/utils/parse" - "be.ems/lib/core/vo/result" - "be.ems/lib/midware" - "be.ems/lib/services" - "be.ems/restagent/config" -) - -// 字典类型对应的字典数据信息接口添加到路由 -func Routers() []services.RouterItem { - // 实例化控制层 SysDictDataApi 结构体 - var apis = &SysDictDataApi{ - sysDictDataService: sysDictDataService.NewServiceSysDictData, - sysDictTypeService: sysDictTypeService.NewServiceSysDictType, - } - - rs := [...]services.RouterItem{ - { - Method: "GET", - Pattern: "/dictDatas", - Handler: apis.List, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/dictData/{dictCode}", - Handler: apis.Info, - Middleware: midware.Authorize(nil), - }, - { - Method: "POST", - Pattern: "/dictData", - Handler: apis.Add, - Middleware: midware.Authorize(nil), - }, - { - Method: "PUT", - Pattern: "/dictData", - Handler: apis.Edit, - Middleware: midware.Authorize(nil), - }, - { - Method: "DELETE", - Pattern: "/dictData/{dictCodes}", - Handler: apis.Remove, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/dictData/type/{dictType}", - Handler: apis.DictType, - Middleware: midware.Authorize(nil), - }, - // 添加更多的 Router 对象... - } - - // 生成两组前缀路由 - rsPrefix := []services.RouterItem{} - for _, v := range rs { - path := "/dictDataManage/{apiVersion}" + v.Pattern - // 固定前缀 - v.Pattern = config.DefaultUriPrefix + path - rsPrefix = append(rsPrefix, v) - // 可配置 - v.Pattern = config.UriPrefix + path - rsPrefix = append(rsPrefix, v) - } - return rsPrefix -} - -// 字典类型对应的字典数据信息 -// -// PATH /dictDataManage -type SysDictDataApi struct { - // 字典数据服务 - sysDictDataService *sysDictDataService.ServiceSysDictData - // 字典类型服务 - sysDictTypeService *sysDictTypeService.ServiceSysDictType -} - -// 字典数据列表 -// -// GET /list -func (s *SysDictDataApi) List(w http.ResponseWriter, r *http.Request) { - querys := ctx.QueryMap(r) - data := s.sysDictDataService.SelectDictDataPage(querys) - ctx.JSON(w, 200, result.Ok(data)) -} - -// 字典数据详情 -// -// GET /:dictCode -func (s *SysDictDataApi) Info(w http.ResponseWriter, r *http.Request) { - dictCode := ctx.Param(r, "dictCode") - if dictCode == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - data := s.sysDictDataService.SelectDictDataByCode(dictCode) - if data.DictCode == dictCode { - ctx.JSON(w, 200, result.OkData(data)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 字典数据新增 -// -// POST / -func (s *SysDictDataApi) Add(w http.ResponseWriter, r *http.Request) { - var body model.SysDictData - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.DictCode != "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查字典类型是否存在 - sysDictType := s.sysDictTypeService.SelectDictTypeByType(body.DictType) - if sysDictType.DictType != body.DictType { - ctx.JSON(w, 200, result.ErrMsg("No permission to access dictionary type data!")) - return - } - - // 检查字典标签唯一 - uniqueDictLabel := s.sysDictDataService.CheckUniqueDictLabel(body.DictType, body.DictLabel, "") - if !uniqueDictLabel { - msg := fmt.Sprintf("[%s] The subscript signature of this dictionary type already exists", body.DictLabel) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 检查字典键值唯一 - uniqueDictValue := s.sysDictDataService.CheckUniqueDictValue(body.DictType, body.DictValue, "") - if !uniqueDictValue { - msg := fmt.Sprintf("[%s] The label value under this dictionary type already exists", body.DictValue) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - body.CreateBy = ctx.LoginUserToUserName(r) - insertId := s.sysDictDataService.InsertDictData(body) - if insertId != "" { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 字典类型修改 -// -// PUT / -func (s *SysDictDataApi) Edit(w http.ResponseWriter, r *http.Request) { - var body model.SysDictData - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.DictCode == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查字典类型是否存在 - sysDictType := s.sysDictTypeService.SelectDictTypeByType(body.DictType) - if sysDictType.DictType != body.DictType { - ctx.JSON(w, 200, result.ErrMsg("No permission to access dictionary type data!")) - return - } - - // 检查字典编码是否存在 - SysDictDataApi := s.sysDictDataService.SelectDictDataByCode(body.DictCode) - if SysDictDataApi.DictCode != body.DictCode { - ctx.JSON(w, 200, result.ErrMsg("No permission to access dictionary encoding data!")) - return - } - - // 检查字典标签唯一 - uniqueDictLabel := s.sysDictDataService.CheckUniqueDictLabel(body.DictType, body.DictLabel, body.DictCode) - if !uniqueDictLabel { - msg := fmt.Sprintf("Data modification failed for [%s], the dictionary type subscript signature already exists", body.DictLabel) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 检查字典键值唯一 - uniqueDictValue := s.sysDictDataService.CheckUniqueDictValue(body.DictType, body.DictValue, body.DictCode) - if !uniqueDictValue { - msg := fmt.Sprintf("Data modification failed for [%s], label value already exists under this dictionary type", body.DictValue) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - body.UpdateBy = ctx.LoginUserToUserName(r) - rows := s.sysDictDataService.UpdateDictData(body) - if rows > 0 { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 字典数据删除 -// -// DELETE /:dictCodes -func (s *SysDictDataApi) Remove(w http.ResponseWriter, r *http.Request) { - dictCodes := ctx.Param(r, "dictCodes") - if dictCodes == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - // 处理字符转id数组后去重 - ids := strings.Split(dictCodes, ",") - uniqueIDs := parse.RemoveDuplicates(ids) - if len(uniqueIDs) <= 0 { - ctx.JSON(w, 200, result.Err(nil)) - return - } - rows, err := s.sysDictDataService.DeleteDictDataByCodes(uniqueIDs) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - msg := fmt.Sprintf("Successfully deleted: %d", rows) - ctx.JSON(w, 200, result.OkMsg(msg)) -} - -// 字典数据列表(指定字典类型) -// -// GET /type/:dictType -func (s *SysDictDataApi) DictType(w http.ResponseWriter, r *http.Request) { - dictType := ctx.Param(r, "dictType") - if dictType == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - data := s.sysDictDataService.SelectDictDataByType(dictType) - ctx.JSON(w, 200, result.OkData(data)) -} diff --git a/features/sys_dict_data/model/sys_dict_data.go b/features/sys_dict_data/model/sys_dict_data.go deleted file mode 100644 index 7c8cd06..0000000 --- a/features/sys_dict_data/model/sys_dict_data.go +++ /dev/null @@ -1,31 +0,0 @@ -package model - -// SysDictData 字典数据对象 sys_dict_data -type SysDictData struct { - // 字典编码 - DictCode string `json:"dictCode"` - // 字典排序 - DictSort int `json:"dictSort"` - // 字典标签 - DictLabel string `json:"dictLabel" binding:"required"` - // 字典键值 - DictValue string `json:"dictValue" binding:"required"` - // 字典类型 - DictType string `json:"dictType" binding:"required"` - // 样式属性(样式扩展) - TagClass string `json:"tagClass"` - // 标签类型(预设颜色) - TagType string `json:"tagType"` - // 状态(0停用 1正常) - Status string `json:"status"` - // 创建者 - CreateBy string `json:"createBy"` - // 创建时间 - CreateTime int64 `json:"createTime"` - // 更新者 - UpdateBy string `json:"updateBy"` - // 更新时间 - UpdateTime int64 `json:"updateTime"` - // 备注 - Remark string `json:"remark"` -} diff --git a/features/sys_dict_data/repo/repo_sys_dict_data.go b/features/sys_dict_data/repo/repo_sys_dict_data.go deleted file mode 100644 index 02b0b37..0000000 --- a/features/sys_dict_data/repo/repo_sys_dict_data.go +++ /dev/null @@ -1,369 +0,0 @@ -package repo - -import ( - "fmt" - "strings" - "time" - - "be.ems/features/sys_dict_data/model" - "be.ems/lib/core/datasource" - "be.ems/lib/core/utils/parse" - "be.ems/lib/log" -) - -// 实例化数据层 RepoSysDictData 结构体 -var NewRepoSysDictData = &RepoSysDictData{ - selectSql: `select - dict_code, dict_sort, dict_label, dict_value, dict_type, tag_class, tag_type, status, create_by, create_time, remark - from sys_dict_data`, - - resultMap: map[string]string{ - "dict_code": "DictCode", - "dict_sort": "DictSort", - "dict_label": "DictLabel", - "dict_value": "DictValue", - "dict_type": "DictType", - "tag_class": "TagClass", - "tag_type": "TagType", - "status": "Status", - "remark": "Remark", - "create_by": "CreateBy", - "create_time": "CreateTime", - "update_by": "UpdateBy", - "update_time": "UpdateTime", - }, -} - -// RepoSysDictData 字典类型数据表 数据层处理 -type RepoSysDictData struct { - // 查询视图对象SQL - selectSql string - // 结果字段与实体映射 - resultMap map[string]string -} - -// convertResultRows 将结果记录转实体结果组 -func (r *RepoSysDictData) convertResultRows(rows []map[string]any) []model.SysDictData { - arr := make([]model.SysDictData, 0) - for _, row := range rows { - sysDictData := model.SysDictData{} - for key, value := range row { - if keyMapper, ok := r.resultMap[key]; ok { - datasource.SetFieldValue(&sysDictData, keyMapper, value) - } - } - arr = append(arr, sysDictData) - } - return arr -} - -// SelectDictDataPage 根据条件分页查询字典数据 -func (r *RepoSysDictData) SelectDictDataPage(query map[string]any) map[string]any { - // 查询条件拼接 - var conditions []string - var params []any - if v, ok := query["dictType"]; ok && v != "" { - conditions = append(conditions, "dict_type = ?") - params = append(params, v) - } - if v, ok := query["dictLabel"]; ok && v != "" { - conditions = append(conditions, "dict_label like concat(?, '%')") - params = append(params, v) - } - if v, ok := query["status"]; ok && v != "" { - conditions = append(conditions, "status = ?") - params = append(params, v) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数量 长度为0直接返回 - totalSql := "select count(1) as 'total' from sys_dict_data" - totalRows, err := datasource.RawDB("", totalSql+whereSql, params) - if err != nil { - log.Errorf("total err => %v", err) - } - total := parse.Number(totalRows[0]["total"]) - if total == 0 { - return map[string]any{ - "total": total, - "rows": []model.SysDictData{}, - } - } - - // 分页 - pageNum, pageSize := datasource.PageNumSize(query["pageNum"], query["pageSize"]) - pageSql := " order by dict_sort asc limit ?,? " - params = append(params, pageNum*pageSize) - params = append(params, pageSize) - - // 查询数据 - querySql := r.selectSql + whereSql + pageSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - } - - // 转换实体 - rows := r.convertResultRows(results) - return map[string]any{ - "total": total, - "rows": rows, - } -} - -// SelectDictDataList 根据条件查询字典数据 -func (r *RepoSysDictData) SelectDictDataList(sysDictData model.SysDictData) []model.SysDictData { - // 查询条件拼接 - var conditions []string - var params []any - if sysDictData.DictLabel != "" { - conditions = append(conditions, "dict_label like concat(?, '%')") - params = append(params, sysDictData.DictLabel) - } - if sysDictData.DictType != "" { - conditions = append(conditions, "dict_type = ?") - params = append(params, sysDictData.DictType) - } - if sysDictData.Status != "" { - conditions = append(conditions, "status = ?") - params = append(params, sysDictData.Status) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数据 - orderSql := " order by dict_sort asc " - querySql := r.selectSql + whereSql + orderSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysDictData{} - } - - // 转换实体 - return r.convertResultRows(results) -} - -// SelectDictDataByCodes 根据字典数据编码查询信息 -func (r *RepoSysDictData) SelectDictDataByCodes(dictCodes []string) []model.SysDictData { - placeholder := datasource.KeyPlaceholderByQuery(len(dictCodes)) - querySql := r.selectSql + " where dict_code in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(dictCodes) - results, err := datasource.RawDB("", querySql, parameters) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysDictData{} - } - // 转换实体 - return r.convertResultRows(results) -} - -// CountDictDataByType 查询字典数据 -func (r *RepoSysDictData) CountDictDataByType(dictType string) int64 { - querySql := "select count(1) as 'total' from sys_dict_data where dict_type = ?" - results, err := datasource.RawDB("", querySql, []any{dictType}) - if err != nil { - log.Errorf("query err => %v", err) - return 0 - } - if len(results) > 0 { - return parse.Number(results[0]["total"]) - } - return 0 -} - -// CheckUniqueDictData 校验字典数据是否唯一 -func (r *RepoSysDictData) CheckUniqueDictData(sysDictData model.SysDictData) string { - // 查询条件拼接 - var conditions []string - var params []any - if sysDictData.DictType != "" { - conditions = append(conditions, "dict_type = ?") - params = append(params, sysDictData.DictType) - } - if sysDictData.DictLabel != "" { - conditions = append(conditions, "dict_label = ?") - params = append(params, sysDictData.DictLabel) - } - if sysDictData.DictValue != "" { - conditions = append(conditions, "dict_value = ?") - params = append(params, sysDictData.DictValue) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } else { - return "" - } - - // 查询数据 - querySql := "select dict_code as 'str' from sys_dict_data " + whereSql + " limit 1" - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err %v", err) - return "" - } - if len(results) > 0 { - return fmt.Sprint(results[0]["str"]) - } - return "" -} - -// DeleteDictDataByCodes 批量删除字典数据信息 -func (r *RepoSysDictData) DeleteDictDataByCodes(dictCodes []string) int64 { - placeholder := datasource.KeyPlaceholderByQuery(len(dictCodes)) - sql := "delete from sys_dict_data where dict_code in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(dictCodes) - results, err := datasource.ExecDB("", sql, parameters) - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - return affected -} - -// InsertDictData 新增字典数据信息 -func (r *RepoSysDictData) InsertDictData(sysDictData model.SysDictData) string { - // 参数拼接 - params := make(map[string]any) - if sysDictData.DictSort > 0 { - params["dict_sort"] = sysDictData.DictSort - } - if sysDictData.DictLabel != "" { - params["dict_label"] = sysDictData.DictLabel - } - if sysDictData.DictValue != "" { - params["dict_value"] = sysDictData.DictValue - } - if sysDictData.DictType != "" { - params["dict_type"] = sysDictData.DictType - } - if sysDictData.TagClass != "" { - params["tag_class"] = sysDictData.TagClass - } - if sysDictData.TagType != "" { - params["tag_type"] = sysDictData.TagType - } - if sysDictData.Status != "" { - params["status"] = sysDictData.Status - } - if sysDictData.Remark != "" { - params["remark"] = sysDictData.Remark - } - if sysDictData.CreateBy != "" { - params["create_by"] = sysDictData.CreateBy - params["create_time"] = time.Now().UnixMilli() - } - - // 构建执行语句 - keys, placeholder, values := datasource.KeyPlaceholderValueByInsert(params) - sql := "insert into sys_dict_data (" + strings.Join(keys, ",") + ")values(" + placeholder + ")" - - // 执行插入 - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - insertId, err := results.LastInsertId() - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - return fmt.Sprint(insertId) -} - -// UpdateDictData 修改字典数据信息 -func (r *RepoSysDictData) UpdateDictData(sysDictData model.SysDictData) int64 { - // 参数拼接 - params := make(map[string]any) - if sysDictData.DictSort > 0 { - params["dict_sort"] = sysDictData.DictSort - } - if sysDictData.DictLabel != "" { - params["dict_label"] = sysDictData.DictLabel - } - if sysDictData.DictValue != "" { - params["dict_value"] = sysDictData.DictValue - } - if sysDictData.DictType != "" { - params["dict_type"] = sysDictData.DictType - } - if sysDictData.TagClass != "" { - params["tag_class"] = sysDictData.TagClass - } - if sysDictData.TagType != "" { - params["tag_type"] = sysDictData.TagType - } - if sysDictData.Status != "" { - params["status"] = sysDictData.Status - } - if sysDictData.Remark != "" { - params["remark"] = sysDictData.Remark - } - if sysDictData.UpdateBy != "" { - params["update_by"] = sysDictData.UpdateBy - params["update_time"] = time.Now().UnixMilli() - } - - // 构建执行语句 - keys, values := datasource.KeyValueByUpdate(params) - sql := "update sys_dict_data set " + strings.Join(keys, ",") + " where dict_code = ?" - - // 执行更新 - values = append(values, sysDictData.DictCode) - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("update row : %v", err.Error()) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("update err => %v", err) - return 0 - } - return affected -} - -// UpdateDictDataType 同步修改字典类型 -func (r *RepoSysDictData) UpdateDictDataType(oldDictType string, newDictType string) int64 { - // 参数拼接 - params := make([]any, 0) - if oldDictType == "" || newDictType == "" { - return 0 - } - params = append(params, newDictType) - params = append(params, oldDictType) - - // 构建执行语句 - sql := "update sys_dict_data set dict_type = ? where dict_type = ?" - - // 执行更新 - results, err := datasource.ExecDB("", sql, params) - if err != nil { - log.Errorf("update row : %v", err.Error()) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("update err => %v", err) - return 0 - } - return affected -} diff --git a/features/sys_dict_data/service/service_sys_dict_data.go b/features/sys_dict_data/service/service_sys_dict_data.go deleted file mode 100644 index 68a2cff..0000000 --- a/features/sys_dict_data/service/service_sys_dict_data.go +++ /dev/null @@ -1,111 +0,0 @@ -package service - -import ( - "errors" - - "be.ems/features/sys_dict_data/model" - "be.ems/features/sys_dict_data/repo" - sysDictTypeService "be.ems/features/sys_dict_type/service" -) - -// 实例化服务层 ServiceSysDictData 结构体 -var NewServiceSysDictData = &ServiceSysDictData{ - sysDictDataRepository: *repo.NewRepoSysDictData, - sysDictTypeService: *sysDictTypeService.NewServiceSysDictType, -} - -// ServiceSysDictData 字典类型数据 服务层处理 -type ServiceSysDictData struct { - // 字典数据服务 - sysDictDataRepository repo.RepoSysDictData - // 字典类型服务 - sysDictTypeService sysDictTypeService.ServiceSysDictType -} - -// SelectDictDataPage 根据条件分页查询字典数据 -func (r *ServiceSysDictData) SelectDictDataPage(query map[string]any) map[string]any { - return r.sysDictDataRepository.SelectDictDataPage(query) -} - -// SelectDictDataList 根据条件查询字典数据 -func (r *ServiceSysDictData) SelectDictDataList(sysDictData model.SysDictData) []model.SysDictData { - return r.sysDictDataRepository.SelectDictDataList(sysDictData) -} - -// SelectDictDataByCode 根据字典数据编码查询信息 -func (r *ServiceSysDictData) SelectDictDataByCode(dictCode string) model.SysDictData { - if dictCode == "" { - return model.SysDictData{} - } - dictCodes := r.sysDictDataRepository.SelectDictDataByCodes([]string{dictCode}) - if len(dictCodes) > 0 { - return dictCodes[0] - } - return model.SysDictData{} -} - -// SelectDictDataByType 根据字典类型查询信息 -func (r *ServiceSysDictData) SelectDictDataByType(dictType string) []model.SysDictData { - return r.sysDictTypeService.DictDataCache(dictType) -} - -// CheckUniqueDictLabel 校验字典标签是否唯一 -func (r *ServiceSysDictData) CheckUniqueDictLabel(dictType, dictLabel, dictCode string) bool { - uniqueId := r.sysDictDataRepository.CheckUniqueDictData(model.SysDictData{ - DictType: dictType, - DictLabel: dictLabel, - }) - if uniqueId == dictCode { - return true - } - return uniqueId == "" -} - -// CheckUniqueDictValue 校验字典键值是否唯一 -func (r *ServiceSysDictData) CheckUniqueDictValue(dictType, dictValue, dictCode string) bool { - uniqueId := r.sysDictDataRepository.CheckUniqueDictData(model.SysDictData{ - DictType: dictType, - DictValue: dictValue, - }) - if uniqueId == dictCode { - return true - } - return uniqueId == "" -} - -// DeleteDictDataByCodes 批量删除字典数据信息 -func (r *ServiceSysDictData) DeleteDictDataByCodes(dictCodes []string) (int64, error) { - // 检查是否存在 - dictDatas := r.sysDictDataRepository.SelectDictDataByCodes(dictCodes) - if len(dictDatas) <= 0 { - return 0, errors.New("does not have permission to access dictionary-encoded data") - } - if len(dictDatas) == len(dictCodes) { - for _, v := range dictDatas { - // 刷新缓存 - r.sysDictTypeService.ClearDictCache(v.DictType) - r.sysDictTypeService.LoadingDictCache(v.DictType) - } - rows := r.sysDictDataRepository.DeleteDictDataByCodes(dictCodes) - return rows, nil - } - return 0, errors.New("failed to delete dictionary data information") -} - -// InsertDictData 新增字典数据信息 -func (r *ServiceSysDictData) InsertDictData(sysDictData model.SysDictData) string { - insertId := r.sysDictDataRepository.InsertDictData(sysDictData) - if insertId != "" { - r.sysDictTypeService.LoadingDictCache(sysDictData.DictType) - } - return insertId -} - -// UpdateDictData 修改字典数据信息 -func (r *ServiceSysDictData) UpdateDictData(sysDictData model.SysDictData) int64 { - rows := r.sysDictDataRepository.UpdateDictData(sysDictData) - if rows > 0 { - r.sysDictTypeService.LoadingDictCache(sysDictData.DictType) - } - return rows -} diff --git a/features/sys_dict_type/api_sys_dict_type.go b/features/sys_dict_type/api_sys_dict_type.go deleted file mode 100644 index d378070..0000000 --- a/features/sys_dict_type/api_sys_dict_type.go +++ /dev/null @@ -1,253 +0,0 @@ -package sysdicttype - -import ( - "fmt" - "net/http" - "strings" - - "be.ems/features/sys_dict_type/model" - sysDictTypeService "be.ems/features/sys_dict_type/service" - "be.ems/lib/core/utils/ctx" - "be.ems/lib/core/utils/parse" - "be.ems/lib/core/vo/result" - "be.ems/lib/midware" - "be.ems/lib/services" - "be.ems/restagent/config" -) - -// 字典类型信息接口添加到路由 -func Routers() []services.RouterItem { - // 实例化控制层 SysDictTypeApi 结构体 - var apis = &SysDictTypeApi{ - sysDictTypeService: *sysDictTypeService.NewServiceSysDictType, - } - - rs := [...]services.RouterItem{ - { - Method: "GET", - Pattern: "/dictTypes", - Handler: apis.List, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/dictType/{dictId}", - Handler: apis.Info, - Middleware: midware.Authorize(nil), - }, - { - Method: "POST", - Pattern: "/dictType", - Handler: apis.Add, - Middleware: midware.Authorize(nil), - }, - { - Method: "PUT", - Pattern: "/dictType", - Handler: apis.Edit, - Middleware: midware.Authorize(nil), - }, - { - Method: "DELETE", - Pattern: "/dictType/{dictIds}", - Handler: apis.Remove, - Middleware: midware.Authorize(nil), - }, - { - Method: "PUT", - Pattern: "/dictType/refreshCache", - Handler: apis.RefreshCache, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/dictTypes/optionselect", - Handler: apis.DictOptionselect, - Middleware: midware.Authorize(nil), - }, - // 添加更多的 Router 对象... - } - - // 生成两组前缀路由 - rsPrefix := []services.RouterItem{} - for _, v := range rs { - path := "/dictTypegManage/{apiVersion}" + v.Pattern - // 固定前缀 - v.Pattern = config.DefaultUriPrefix + path - rsPrefix = append(rsPrefix, v) - // 可配置 - v.Pattern = config.UriPrefix + path - rsPrefix = append(rsPrefix, v) - } - return rsPrefix -} - -// 字典类型信息 -// -// PATH /dictTypegManage -type SysDictTypeApi struct { - // 字典类型服务 - sysDictTypeService sysDictTypeService.ServiceSysDictType -} - -// 字典类型列表 -// -// GET /list -func (s *SysDictTypeApi) List(w http.ResponseWriter, r *http.Request) { - querys := ctx.QueryMap(r) - data := s.sysDictTypeService.SelectDictTypePage(querys) - ctx.JSON(w, 200, result.Ok(data)) -} - -// 字典类型信息 -// -// GET /:dictId -func (s *SysDictTypeApi) Info(w http.ResponseWriter, r *http.Request) { - dictId := ctx.Param(r, "dictId") - if dictId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - data := s.sysDictTypeService.SelectDictTypeByID(dictId) - if data.DictID == dictId { - ctx.JSON(w, 200, result.OkData(data)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 字典类型新增 -// -// POST / -func (s *SysDictTypeApi) Add(w http.ResponseWriter, r *http.Request) { - var body model.SysDictType - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.DictID != "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查字典名称唯一 - uniqueDictName := s.sysDictTypeService.CheckUniqueDictName(body.DictName, "") - if !uniqueDictName { - msg := fmt.Sprintf("Failed to add dictionary '%s', dictionary name already exists", body.DictName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 检查字典类型唯一 - uniqueDictType := s.sysDictTypeService.CheckUniqueDictType(body.DictType, "") - if !uniqueDictType { - msg := fmt.Sprintf("Failed to add dictionary '%s', dictionary type already exists", body.DictType) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - body.CreateBy = ctx.LoginUserToUserName(r) - insertId := s.sysDictTypeService.InsertDictType(body) - if insertId != "" { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 字典类型修改 -// -// PUT / -func (s *SysDictTypeApi) Edit(w http.ResponseWriter, r *http.Request) { - var body model.SysDictType - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.DictID == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查数据是否存在 - dictInfo := s.sysDictTypeService.SelectDictTypeByID(body.DictID) - if dictInfo.DictID != body.DictID { - ctx.JSON(w, 200, result.ErrMsg("No permission to access dictionary type data!")) - return - } - - // 检查字典名称唯一 - uniqueDictName := s.sysDictTypeService.CheckUniqueDictName(body.DictName, body.DictID) - if !uniqueDictName { - msg := fmt.Sprintf("Dictionary modification failed for [%s], dictionary name already exists", body.DictName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 检查字典类型唯一 - uniqueDictType := s.sysDictTypeService.CheckUniqueDictType(body.DictType, body.DictID) - if !uniqueDictType { - msg := fmt.Sprintf("Dictionary modification failed for [%s], dictionary type already exists", body.DictType) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - body.UpdateBy = ctx.LoginUserToUserName(r) - rows := s.sysDictTypeService.UpdateDictType(body) - if rows > 0 { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 字典类型删除 -// -// DELETE /:dictIds -func (s *SysDictTypeApi) Remove(w http.ResponseWriter, r *http.Request) { - dictIds := ctx.Param(r, "dictIds") - if dictIds == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - // 处理字符转id数组后去重 - ids := strings.Split(dictIds, ",") - uniqueIDs := parse.RemoveDuplicates(ids) - if len(uniqueIDs) <= 0 { - ctx.JSON(w, 200, result.Err(nil)) - return - } - rows, err := s.sysDictTypeService.DeleteDictTypeByIDs(uniqueIDs) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - msg := fmt.Sprintf("Successfully deleted: %d", rows) - ctx.JSON(w, 200, result.OkMsg(msg)) -} - -// 字典类型刷新缓存 -// -// PUT /refreshCache -func (s *SysDictTypeApi) RefreshCache(w http.ResponseWriter, r *http.Request) { - s.sysDictTypeService.ResetDictCache() - ctx.JSON(w, 200, result.Ok(nil)) -} - -// 字典类型选择框列表 -// -// GET /getDictOptionselect -func (s *SysDictTypeApi) DictOptionselect(w http.ResponseWriter, r *http.Request) { - data := s.sysDictTypeService.SelectDictTypeList(model.SysDictType{ - Status: "1", - }) - - type labelValue struct { - Label string `json:"label"` - Value string `json:"value"` - } - - // 数据组 - arr := []labelValue{} - for _, v := range data { - arr = append(arr, labelValue{ - Label: v.DictName, - Value: v.DictType, - }) - } - ctx.JSON(w, 200, result.OkData(arr)) -} diff --git a/features/sys_dict_type/model/sys_dict_type.go b/features/sys_dict_type/model/sys_dict_type.go deleted file mode 100644 index 0ad0b1a..0000000 --- a/features/sys_dict_type/model/sys_dict_type.go +++ /dev/null @@ -1,23 +0,0 @@ -package model - -// SysDictType 字典类型对象 sys_dict_type -type SysDictType struct { - // 字典主键 - DictID string `json:"dictId"` - // 字典名称 - DictName string `json:"dictName" binding:"required"` - // 字典类型 - DictType string `json:"dictType" binding:"required"` - // 状态(0停用 1正常) - Status string `json:"status"` - // 创建者 - CreateBy string `json:"createBy"` - // 创建时间 - CreateTime int64 `json:"createTime"` - // 更新者 - UpdateBy string `json:"updateBy"` - // 更新时间 - UpdateTime int64 `json:"updateTime"` - // 备注 - Remark string `json:"remark"` -} diff --git a/features/sys_dict_type/repo/repo_sys_dict_type.go b/features/sys_dict_type/repo/repo_sys_dict_type.go deleted file mode 100644 index 261f648..0000000 --- a/features/sys_dict_type/repo/repo_sys_dict_type.go +++ /dev/null @@ -1,330 +0,0 @@ -package repo - -import ( - "fmt" - "strings" - "time" - - "be.ems/features/sys_dict_type/model" - "be.ems/lib/core/datasource" - "be.ems/lib/core/utils/date" - "be.ems/lib/core/utils/parse" - "be.ems/lib/log" -) - -// 实例化数据层 RepoSysDictType 结构体 -var NewRepoSysDictType = &RepoSysDictType{ - selectSql: `select - dict_id, dict_name, dict_type, status, create_by, create_time, remark - from sys_dict_type`, - - resultMap: map[string]string{ - "dict_id": "DictID", - "dict_name": "DictName", - "dict_type": "DictType", - "remark": "Remark", - "status": "Status", - "create_by": "CreateBy", - "create_time": "CreateTime", - "update_by": "UpdateBy", - "update_time": "UpdateTime", - }, -} - -// RepoSysDictType 字典类型表 数据层处理 -type RepoSysDictType struct { - // 查询视图对象SQL - selectSql string - // 结果字段与实体映射 - resultMap map[string]string -} - -// convertResultRows 将结果记录转实体结果组 -func (r *RepoSysDictType) convertResultRows(rows []map[string]any) []model.SysDictType { - arr := make([]model.SysDictType, 0) - for _, row := range rows { - sysDictType := model.SysDictType{} - for key, value := range row { - if keyMapper, ok := r.resultMap[key]; ok { - datasource.SetFieldValue(&sysDictType, keyMapper, value) - } - } - arr = append(arr, sysDictType) - } - return arr -} - -// SelectDictTypePage 根据条件分页查询字典类型 -func (r *RepoSysDictType) SelectDictTypePage(query map[string]any) map[string]any { - // 查询条件拼接 - var conditions []string - var params []any - if v, ok := query["dictName"]; ok && v != "" { - conditions = append(conditions, "dict_name like concat(?, '%')") - params = append(params, v) - } - if v, ok := query["dictType"]; ok && v != "" { - conditions = append(conditions, "dict_type like concat(?, '%')") - params = append(params, v) - } - if v, ok := query["status"]; ok && v != "" { - conditions = append(conditions, "status = ?") - params = append(params, v) - } - beginTime, ok := query["beginTime"] - if !ok { - beginTime, ok = query["params[beginTime]"] - } - if ok && beginTime != "" { - conditions = append(conditions, "create_time >= ?") - beginDate := date.ParseStrToDate(beginTime.(string), date.YYYY_MM_DD) - params = append(params, beginDate.UnixMilli()) - } - endTime, ok := query["endTime"] - if !ok { - endTime, ok = query["params[endTime]"] - } - if ok && endTime != "" { - conditions = append(conditions, "create_time <= ?") - endDate := date.ParseStrToDate(endTime.(string), date.YYYY_MM_DD) - params = append(params, endDate.UnixMilli()) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数量 长度为0直接返回 - totalSql := "select count(1) as 'total' from sys_dict_type" - totalRows, err := datasource.RawDB("", totalSql+whereSql, params) - if err != nil { - log.Errorf("total err => %v", err) - } - total := parse.Number(totalRows[0]["total"]) - if total == 0 { - return map[string]any{ - "total": total, - "rows": []model.SysDictType{}, - } - } - - // 分页 - pageNum, pageSize := datasource.PageNumSize(query["pageNum"], query["pageSize"]) - pageSql := " limit ?,? " - params = append(params, pageNum*pageSize) - params = append(params, pageSize) - - // 查询数据 - querySql := r.selectSql + whereSql + pageSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - } - - // 转换实体 - rows := r.convertResultRows(results) - return map[string]any{ - "total": total, - "rows": rows, - } -} - -// SelectDictTypeList 根据条件查询字典类型 -func (r *RepoSysDictType) SelectDictTypeList(sysDictType model.SysDictType) []model.SysDictType { - // 查询条件拼接 - var conditions []string - var params []any - if sysDictType.DictName != "" { - conditions = append(conditions, "dict_name like concat(?, '%')") - params = append(params, sysDictType.DictName) - } - if sysDictType.DictType != "" { - conditions = append(conditions, "dict_type like concat(?, '%')") - params = append(params, sysDictType.DictType) - } - if sysDictType.Status != "" { - conditions = append(conditions, "status = ?") - params = append(params, sysDictType.Status) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数据 - querySql := r.selectSql + whereSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysDictType{} - } - - // 转换实体 - return r.convertResultRows(results) -} - -// SelectDictTypeByIDs 根据字典类型ID查询信息 -func (r *RepoSysDictType) SelectDictTypeByIDs(dictIDs []string) []model.SysDictType { - placeholder := datasource.KeyPlaceholderByQuery(len(dictIDs)) - querySql := r.selectSql + " where dict_id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(dictIDs) - results, err := datasource.RawDB("", querySql, parameters) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysDictType{} - } - // 转换实体 - return r.convertResultRows(results) -} - -// SelectDictTypeByType 根据字典类型查询信息 -func (r *RepoSysDictType) SelectDictTypeByType(dictType string) model.SysDictType { - querySql := r.selectSql + " where dict_type = ?" - results, err := datasource.RawDB("", querySql, []any{dictType}) - if err != nil { - log.Errorf("query err => %v", err) - return model.SysDictType{} - } - // 转换实体 - rows := r.convertResultRows(results) - if len(rows) > 0 { - return rows[0] - } - return model.SysDictType{} -} - -// CheckUniqueDictType 校验字典是否唯一 -func (r *RepoSysDictType) CheckUniqueDictType(sysDictType model.SysDictType) string { - // 查询条件拼接 - var conditions []string - var params []any - if sysDictType.DictName != "" { - conditions = append(conditions, "dict_name = ?") - params = append(params, sysDictType.DictName) - } - if sysDictType.DictType != "" { - conditions = append(conditions, "dict_type = ?") - params = append(params, sysDictType.DictType) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } else { - return "" - } - - // 查询数据 - querySql := "select dict_id as 'str' from sys_dict_type " + whereSql + " limit 1" - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err %v", err) - return "" - } - if len(results) > 0 { - return fmt.Sprint(results[0]["str"]) - } - return "" -} - -// InsertDictType 新增字典类型信息 -func (r *RepoSysDictType) InsertDictType(sysDictType model.SysDictType) string { - // 参数拼接 - params := make(map[string]any) - if sysDictType.DictName != "" { - params["dict_name"] = sysDictType.DictName - } - if sysDictType.DictType != "" { - params["dict_type"] = sysDictType.DictType - } - if sysDictType.Status != "" { - params["status"] = sysDictType.Status - } - if sysDictType.Remark != "" { - params["remark"] = sysDictType.Remark - } - if sysDictType.CreateBy != "" { - params["create_by"] = sysDictType.CreateBy - params["create_time"] = time.Now().UnixMilli() - } - - // 构建执行语句 - keys, placeholder, values := datasource.KeyPlaceholderValueByInsert(params) - sql := "insert into sys_dict_type (" + strings.Join(keys, ",") + ")values(" + placeholder + ")" - - // 执行插入 - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - insertId, err := results.LastInsertId() - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - return fmt.Sprint(insertId) -} - -// UpdateDictType 修改字典类型信息 -func (r *RepoSysDictType) UpdateDictType(sysDictType model.SysDictType) int64 { - // 参数拼接 - params := make(map[string]any) - if sysDictType.DictName != "" { - params["dict_name"] = sysDictType.DictName - } - if sysDictType.DictType != "" { - params["dict_type"] = sysDictType.DictType - } - if sysDictType.Status != "" { - params["status"] = sysDictType.Status - } - if sysDictType.Remark != "" { - params["remark"] = sysDictType.Remark - } - if sysDictType.UpdateBy != "" { - params["update_by"] = sysDictType.UpdateBy - params["update_time"] = time.Now().UnixMilli() - } - - // 构建执行语句 - keys, values := datasource.KeyValueByUpdate(params) - sql := "update sys_dict_type set " + strings.Join(keys, ",") + " where dict_id = ?" - - // 执行更新 - values = append(values, sysDictType.DictID) - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("update row : %v", err.Error()) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("update err => %v", err) - return 0 - } - return affected -} - -// DeleteDictTypeByIDs 批量删除字典类型信息 -func (r *RepoSysDictType) DeleteDictTypeByIDs(dictIDs []string) int64 { - placeholder := datasource.KeyPlaceholderByQuery(len(dictIDs)) - sql := "delete from sys_dict_type where dict_id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(dictIDs) - results, err := datasource.ExecDB("", sql, parameters) - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - return affected -} diff --git a/features/sys_dict_type/service/service_sys_dict_type.go b/features/sys_dict_type/service/service_sys_dict_type.go deleted file mode 100644 index 66674a1..0000000 --- a/features/sys_dict_type/service/service_sys_dict_type.go +++ /dev/null @@ -1,211 +0,0 @@ -package service - -import ( - "encoding/json" - "errors" - "fmt" - - sysDictDataModel "be.ems/features/sys_dict_data/model" - sysDictDataRepo "be.ems/features/sys_dict_data/repo" - sysDictTypeModel "be.ems/features/sys_dict_type/model" - "be.ems/features/sys_dict_type/repo" - "be.ems/lib/core/cache" - "be.ems/lib/core/constants/cachekey" -) - -// 实例化服务层 ServiceSysDictType 结构体 -var NewServiceSysDictType = &ServiceSysDictType{ - sysDictTypeRepository: *repo.NewRepoSysDictType, - sysDictDataRepository: *sysDictDataRepo.NewRepoSysDictData, -} - -// ServiceSysDictType 字典类型 服务层处理 -type ServiceSysDictType struct { - // 字典类型服务 - sysDictTypeRepository repo.RepoSysDictType - // 字典数据服务 - sysDictDataRepository sysDictDataRepo.RepoSysDictData -} - -// SelectDictTypePage 根据条件分页查询字典类型 -func (r *ServiceSysDictType) SelectDictTypePage(query map[string]any) map[string]any { - return r.sysDictTypeRepository.SelectDictTypePage(query) -} - -// SelectDictTypeList 根据条件查询字典类型 -func (r *ServiceSysDictType) SelectDictTypeList(sysDictType sysDictTypeModel.SysDictType) []sysDictTypeModel.SysDictType { - return r.sysDictTypeRepository.SelectDictTypeList(sysDictType) -} - -// SelectDictTypeByID 根据字典类型ID查询信息 -func (r *ServiceSysDictType) SelectDictTypeByID(dictID string) sysDictTypeModel.SysDictType { - if dictID == "" { - return sysDictTypeModel.SysDictType{} - } - dictTypes := r.sysDictTypeRepository.SelectDictTypeByIDs([]string{dictID}) - if len(dictTypes) > 0 { - return dictTypes[0] - } - return sysDictTypeModel.SysDictType{} -} - -// SelectDictTypeByType 根据字典类型查询信息 -func (r *ServiceSysDictType) SelectDictTypeByType(dictType string) sysDictTypeModel.SysDictType { - return r.sysDictTypeRepository.SelectDictTypeByType(dictType) -} - -// CheckUniqueDictName 校验字典名称是否唯一 -func (r *ServiceSysDictType) CheckUniqueDictName(dictName, dictID string) bool { - uniqueId := r.sysDictTypeRepository.CheckUniqueDictType(sysDictTypeModel.SysDictType{ - DictName: dictName, - }) - if uniqueId == dictID { - return true - } - return uniqueId == "" -} - -// CheckUniqueDictType 校验字典类型是否唯一 -func (r *ServiceSysDictType) CheckUniqueDictType(dictType, dictID string) bool { - uniqueId := r.sysDictTypeRepository.CheckUniqueDictType(sysDictTypeModel.SysDictType{ - DictType: dictType, - }) - if uniqueId == dictID { - return true - } - return uniqueId == "" -} - -// InsertDictType 新增字典类型信息 -func (r *ServiceSysDictType) InsertDictType(sysDictType sysDictTypeModel.SysDictType) string { - insertId := r.sysDictTypeRepository.InsertDictType(sysDictType) - if insertId != "" { - r.LoadingDictCache(sysDictType.DictType) - } - return insertId -} - -// UpdateDictType 修改字典类型信息 -func (r *ServiceSysDictType) UpdateDictType(sysDictType sysDictTypeModel.SysDictType) int64 { - data := r.sysDictTypeRepository.SelectDictTypeByIDs([]string{sysDictType.DictID}) - if len(data) == 0 { - return 0 - } - // 修改字典类型key时同步更新其字典数据的类型key - oldDictType := data[0].DictType - rows := r.sysDictTypeRepository.UpdateDictType(sysDictType) - if rows > 0 && oldDictType != "" && oldDictType != sysDictType.DictType { - r.sysDictDataRepository.UpdateDictDataType(oldDictType, sysDictType.DictType) - } - // 刷新缓存 - r.ClearDictCache(oldDictType) - r.LoadingDictCache(sysDictType.DictType) - return rows -} - -// DeleteDictTypeByIDs 批量删除字典类型信息 -func (r *ServiceSysDictType) DeleteDictTypeByIDs(dictIDs []string) (int64, error) { - // 检查是否存在 - dictTypes := r.sysDictTypeRepository.SelectDictTypeByIDs(dictIDs) - if len(dictTypes) <= 0 { - return 0, errors.New("no permission to access dictionary type data") - } - for _, v := range dictTypes { - // 字典类型下级含有数据 - useCount := r.sysDictDataRepository.CountDictDataByType(v.DictType) - if useCount > 0 { - msg := fmt.Sprintf("[%s] Dictionary data exists and cannot be deleted. ", v.DictName) - return 0, errors.New(msg) - } - // 清除缓存 - r.ClearDictCache(v.DictType) - } - if len(dictTypes) == len(dictIDs) { - rows := r.sysDictTypeRepository.DeleteDictTypeByIDs(dictIDs) - return rows, nil - } - return 0, errors.New("failed to delete dictionary data information") -} - -// ResetDictCache 重置字典缓存数据 -func (r *ServiceSysDictType) ResetDictCache() { - r.ClearDictCache("*") - r.LoadingDictCache("") -} - -// getCacheKey 组装缓存key -func (r *ServiceSysDictType) getDictCache(dictType string) string { - return cachekey.SYS_DICT_KEY + dictType -} - -// LoadingDictCache 加载字典缓存数据 -func (r *ServiceSysDictType) LoadingDictCache(dictType string) { - sysDictData := sysDictDataModel.SysDictData{ - Status: "1", - } - - // 指定字典类型 - if dictType != "" { - sysDictData.DictType = dictType - // 删除缓存 - key := r.getDictCache(dictType) - cache.DeleteLocal(key) - } - - sysDictDataList := r.sysDictDataRepository.SelectDictDataList(sysDictData) - if len(sysDictDataList) == 0 { - return - } - - // 将字典数据按类型分组 - m := make(map[string][]sysDictDataModel.SysDictData, 0) - for _, v := range sysDictDataList { - key := v.DictType - if item, ok := m[key]; ok { - m[key] = append(item, v) - } else { - m[key] = []sysDictDataModel.SysDictData{v} - } - } - - // 放入缓存 - for k, v := range m { - key := r.getDictCache(k) - values, _ := json.Marshal(v) - cache.SetLocal(key, string(values)) - } -} - -// ClearDictCache 清空字典缓存数据 -func (r *ServiceSysDictType) ClearDictCache(dictType string) bool { - key := r.getDictCache(dictType) - keys := cache.GetLocalKeys(key) - for _, v := range keys { - cache.DeleteLocal(v) - } - return len(keys) > 0 -} - -// DictDataCache 获取字典数据缓存数据 -func (r *ServiceSysDictType) DictDataCache(dictType string) []sysDictDataModel.SysDictData { - data := []sysDictDataModel.SysDictData{} - key := r.getDictCache(dictType) - jsonAny, ok := cache.GetLocal(key) - if jsonAny != nil && ok { - err := json.Unmarshal([]byte(jsonAny.(string)), &data) - if err != nil { - data = []sysDictDataModel.SysDictData{} - } - } else { - data = r.sysDictDataRepository.SelectDictDataList(sysDictDataModel.SysDictData{ - Status: "1", - DictType: dictType, - }) - if len(data) > 0 { - cache.DeleteLocal(key) - values, _ := json.Marshal(data) - cache.SetLocal(key, string(values)) - } - } - return data -} diff --git a/features/sys_menu/api_sys_menu.go b/features/sys_menu/api_sys_menu.go deleted file mode 100644 index b949a15..0000000 --- a/features/sys_menu/api_sys_menu.go +++ /dev/null @@ -1,354 +0,0 @@ -package sysmenu - -import ( - "fmt" - "net/http" - - "be.ems/features/sys_menu/consts" - "be.ems/features/sys_menu/model" - "be.ems/features/sys_menu/service" - "be.ems/lib/core/utils/ctx" - "be.ems/lib/core/utils/regular" - "be.ems/lib/core/vo/result" - "be.ems/lib/midware" - "be.ems/lib/services" - "be.ems/restagent/config" - srcConfig "be.ems/src/framework/config" -) - -// 菜单接口添加到路由 -func Routers() []services.RouterItem { - // 实例化控制层 SysMenuApi 结构体 - var apis = &SysMenuApi{ - sysMenuService: service.NewServiceSysMenu, - } - - rs := [...]services.RouterItem{ - { - Method: "GET", - Pattern: "/menus", - Handler: apis.List, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/menu/{menuId}", - Handler: apis.Info, - Middleware: midware.Authorize(nil), - }, - { - Method: "POST", - Pattern: "/menu", - Handler: apis.Add, - Middleware: midware.Authorize(nil), - }, - { - Method: "PUT", - Pattern: "/menu", - Handler: apis.Edit, - Middleware: midware.Authorize(nil), - }, - { - Method: "DELETE", - Pattern: "/menu/{menuId}", - Handler: apis.Remove, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/menus/treeSelect", - Handler: apis.TreeSelect, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/menu/roleMenuTreeSelect/{roleId}", - Handler: apis.RoleMenuTreeSelect, - Middleware: midware.Authorize(nil), - }, - // 添加更多的 Router 对象... - } - - // 生成两组前缀路由 - rsPrefix := []services.RouterItem{} - for _, v := range rs { - path := "/menuManage/{apiVersion}" + v.Pattern - // 固定前缀 - v.Pattern = config.DefaultUriPrefix + path - rsPrefix = append(rsPrefix, v) - // 可配置 - v.Pattern = config.UriPrefix + path - rsPrefix = append(rsPrefix, v) - } - return rsPrefix -} - -// // 实例化控制层 SysMenuApi 结构体 -// var NewSysMenu = &SysMenuApi{ -// sysMenuService: NewServiceSysMenu, -// } - -// 菜单信息 -// -// PATH /menuManage -type SysMenuApi struct { - // 菜单服务 - sysMenuService *service.ServiceSysMenu -} - -// 菜单列表 -// -// GET /list -func (s *SysMenuApi) List(w http.ResponseWriter, r *http.Request) { - query := model.SysMenu{} - if v := ctx.GetQuery(r, "menuName"); v != "" { - query.MenuName = v - } - if v := ctx.GetQuery(r, "status"); v != "" { - query.Status = v - } - - userId := ctx.LoginUserToUserID(r) - if srcConfig.IsAdmin(userId) { - userId = "*" - } - data := s.sysMenuService.SelectMenuList(query, userId) - ctx.JSON(w, 200, result.OkData(data)) -} - -// 菜单信息 -// -// GET /:menuId -func (s *SysMenuApi) Info(w http.ResponseWriter, r *http.Request) { - menuId := ctx.Param(r, "menuId") - if menuId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - data := s.sysMenuService.SelectMenuById(menuId) - if data.MenuID == menuId { - ctx.JSON(w, 200, result.OkData(data)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 菜单新增 -// -// POST / -func (s *SysMenuApi) Add(w http.ResponseWriter, r *http.Request) { - var body model.SysMenu - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.MenuID != "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 目录和菜单检查地址唯一 - if consts.TYPE_DIR == body.MenuType || consts.TYPE_MENU == body.MenuType { - uniqueNenuPath := s.sysMenuService.CheckUniqueMenuPath(body.Path, "") - if !uniqueNenuPath { - msg := fmt.Sprintf("菜单新增【%s】失败,菜单路由地址已存在", body.MenuName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - } - - // 检查名称唯一 - uniqueNenuName := s.sysMenuService.CheckUniqueMenuName(body.MenuName, body.ParentID, "") - if !uniqueNenuName { - msg := fmt.Sprintf("菜单新增【%s】失败,菜单名称已存在", body.MenuName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 外链菜单需要符合网站http(s)开头 - if body.IsFrame == "0" && !regular.ValidHttp(body.Path) { - msg := fmt.Sprintf("菜单新增【%s】失败,非内部地址必须以http(s)://开头", body.MenuName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - body.CreateBy = ctx.LoginUserToUserName(r) - insertId := s.sysMenuService.InsertMenu(body) - if insertId != "" { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 菜单修改 -// -// PUT / -func (s *SysMenuApi) Edit(w http.ResponseWriter, r *http.Request) { - var body model.SysMenu - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.MenuID == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 上级菜单不能选自己 - if body.MenuID == body.ParentID { - msg := fmt.Sprintf("Menu modification failed for [%s], parent menu cannot select itself", body.MenuName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 检查数据是否存在 - menuInfo := s.sysMenuService.SelectMenuById(body.MenuID) - if menuInfo.MenuID != body.MenuID { - ctx.JSON(w, 200, result.ErrMsg("No permission to access menu data")) - return - } - // 父级ID不为0是要检查 - if body.ParentID != "0" { - menuParent := s.sysMenuService.SelectMenuById(body.ParentID) - if menuParent.MenuID != body.ParentID { - ctx.JSON(w, 200, result.ErrMsg("No permission to access menu data")) - return - } - // 禁用菜单时检查父菜单是否使用 - if body.Status == "1" && menuParent.Status == "0" { - ctx.JSON(w, 200, result.ErrMsg("Parent menu not enabled!")) - return - } - } - - // 目录和菜单检查地址唯一 - if consts.TYPE_DIR == body.MenuType || consts.TYPE_MENU == body.MenuType { - uniqueNenuPath := s.sysMenuService.CheckUniqueMenuPath(body.Path, body.MenuID) - if !uniqueNenuPath { - msg := fmt.Sprintf("菜单修改【%s】失败,菜单路由地址已存在", body.MenuName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - } - - // 检查名称唯一 - uniqueNenuName := s.sysMenuService.CheckUniqueMenuName(body.MenuName, body.ParentID, body.MenuID) - if !uniqueNenuName { - msg := fmt.Sprintf("菜单修改【%s】失败,菜单名称已存在", body.MenuName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 外链菜单需要符合网站http(s)开头 - if body.IsFrame == "0" && !regular.ValidHttp(body.Path) { - msg := fmt.Sprintf("菜单修改【%s】失败,非内部地址必须以http(s)://开头", body.MenuName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 禁用菜单时检查子菜单是否使用 - if body.Status == "0" { - hasStatus := s.sysMenuService.HasChildByMenuIdAndStatus(body.MenuID, "1") - if hasStatus > 0 { - msg := fmt.Sprintf("不允许禁用,存在使用子菜单数:%d", hasStatus) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - } - - body.UpdateBy = ctx.LoginUserToUserName(r) - rows := s.sysMenuService.UpdateMenu(body) - if rows > 0 { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 菜单删除 -// -// DELETE /:menuId -func (s *SysMenuApi) Remove(w http.ResponseWriter, r *http.Request) { - menuId := ctx.Param(r, "menuId") - if menuId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查数据是否存在 - menu := s.sysMenuService.SelectMenuById(menuId) - if menu.MenuID != menuId { - ctx.JSON(w, 200, result.ErrMsg("No permission to access menu data!")) - return - } - - // 检查是否存在子菜单 - hasChild := s.sysMenuService.HasChildByMenuIdAndStatus(menuId, "") - if hasChild > 0 { - msg := fmt.Sprintf("Deletion not allowed, there are sub orders: %d", hasChild) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 检查是否分配给角色 - existRole := s.sysMenuService.CheckMenuExistRole(menuId) - if existRole > 0 { - msg := fmt.Sprintf("Deletion not allowed, menu already assigned to roles: %d", existRole) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - rows := s.sysMenuService.DeleteMenuById(menuId) - if rows > 0 { - msg := fmt.Sprintf("Successfully deleted: %d", rows) - ctx.JSON(w, 200, result.OkMsg(msg)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 菜单树结构列表 -// -// GET /treeSelect -func (s *SysMenuApi) TreeSelect(w http.ResponseWriter, r *http.Request) { - query := model.SysMenu{} - if v := ctx.GetQuery(r, "menuName"); v != "" { - query.MenuName = v - } - if v := ctx.GetQuery(r, "status"); v != "" { - query.Status = v - } - - userId := ctx.LoginUserToUserID(r) - if srcConfig.IsAdmin(userId) { - userId = "*" - } - data := s.sysMenuService.SelectMenuTreeSelectByUserId(query, userId) - ctx.JSON(w, 200, result.OkData(data)) - -} - -// 菜单树结构列表(指定角色) -// -// GET /roleMenuTreeSelect/:roleId -func (s *SysMenuApi) RoleMenuTreeSelect(w http.ResponseWriter, r *http.Request) { - roleId := ctx.Param(r, "roleId") - if roleId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - query := model.SysMenu{} - if v := ctx.GetQuery(r, "menuName"); v != "" { - query.MenuName = v - } - if v := ctx.GetQuery(r, "status"); v != "" { - query.Status = v - } - - userId := ctx.LoginUserToUserID(r) - if srcConfig.IsAdmin(userId) { - userId = "*" - } - menuTreeSelect := s.sysMenuService.SelectMenuTreeSelectByUserId(query, userId) - checkedKeys := s.sysMenuService.SelectMenuListByRoleId(roleId) - ctx.JSON(w, 200, result.OkData(map[string]any{ - "menus": menuTreeSelect, - "checkedKeys": checkedKeys, - })) -} diff --git a/features/sys_menu/consts/consts_menu.go b/features/sys_menu/consts/consts_menu.go deleted file mode 100644 index 6a1dca3..0000000 --- a/features/sys_menu/consts/consts_menu.go +++ /dev/null @@ -1,24 +0,0 @@ -package consts - -// 系统菜单常量信息 - -const ( - // 组件布局类型-基础布局组件标识 - COMPONENT_LAYOUT_BASIC = "BasicLayout" - // 组件布局类型-空白布局组件标识 - COMPONENT_LAYOUT_BLANK = "BlankLayout" - // 组件布局类型-内链接布局组件标识 - COMPONENT_LAYOUT_LINK = "LinkLayout" -) - -const ( - // 菜单类型-目录 - TYPE_DIR = "D" - // 菜单类型-菜单 - TYPE_MENU = "M" - // 菜单类型-按钮 - TYPE_BUTTON = "B" -) - -// 菜单内嵌地址标识-带/前缀 -const PATH_INLINE = "/inline" diff --git a/features/sys_menu/model/sys_menu.go b/features/sys_menu/model/sys_menu.go deleted file mode 100644 index 397e30e..0000000 --- a/features/sys_menu/model/sys_menu.go +++ /dev/null @@ -1,46 +0,0 @@ -package model - -// SysMenu 菜单权限对象 sys_menu -type SysMenu struct { - // 菜单ID - MenuID string `json:"menuId"` - // 菜单名称 - MenuName string `json:"menuName" binding:"required"` - // 父菜单ID 默认0 - ParentID string `json:"parentId" binding:"required"` - // 显示顺序 - MenuSort int `json:"menuSort"` - // 路由地址 - Path string `json:"path"` - // 组件路径 - Component string `json:"component"` - // 是否内部跳转(0否 1是) - IsFrame string `json:"isFrame"` - // 是否缓存(0不缓存 1缓存) - IsCache string `json:"isCache"` - // 菜单类型(D目录 M菜单 B按钮) - MenuType string `json:"menuType" binding:"required"` - // 是否显示(0隐藏 1显示) - Visible string `json:"visible"` - // 菜单状态(0停用 1正常) - Status string `json:"status"` - // 权限标识 - Perms string `json:"perms"` - // 菜单图标(#无图标) - Icon string `json:"icon"` - // 创建者 - CreateBy string `json:"createBy"` - // 创建时间 - CreateTime int64 `json:"createTime"` - // 更新者 - UpdateBy string `json:"updateBy"` - // 更新时间 - UpdateTime int64 `json:"updateTime"` - // 备注 - Remark string `json:"remark"` - - // ====== 非数据库字段属性 ====== - - // 子菜单 - Children []SysMenu `json:"children,omitempty"` -} diff --git a/features/sys_menu/service/repo_sys_menu.go b/features/sys_menu/service/repo_sys_menu.go deleted file mode 100644 index 1b8f50a..0000000 --- a/features/sys_menu/service/repo_sys_menu.go +++ /dev/null @@ -1,475 +0,0 @@ -package service - -import ( - "fmt" - "strings" - "time" - - "be.ems/features/sys_menu/consts" - "be.ems/features/sys_menu/model" - "be.ems/lib/core/datasource" - "be.ems/lib/core/utils/parse" - "be.ems/lib/log" -) - -// 实例化数据层 RepoSysMenu 结构体 -var NewRepoSysMenu = &RepoSysMenu{ - selectSql: `select - m.menu_id, m.menu_name, m.parent_id, m.menu_sort, m.path, m.component, m.is_frame, m.is_cache, m.menu_type, m.visible, m.status, ifnull(m.perms,'') as perms, m.icon, m.create_time, m.remark - from sys_menu m`, - - selectSqlByUser: `select distinct - m.menu_id, m.menu_name, m.parent_id, m.menu_sort, m.path, m.component, m.is_frame, m.is_cache, m.menu_type, m.visible, m.status, ifnull(m.perms,'') as perms, m.icon, m.create_time, m.remark - from sys_menu m - left join sys_role_menu rm on m.menu_id = rm.menu_id - left join sys_user_role ur on rm.role_id = ur.role_id - left join sys_role ro on ur.role_id = ro.role_id`, - - resultMap: map[string]string{ - "menu_id": "MenuID", - "menu_name": "MenuName", - "parent_name": "ParentName", - "parent_id": "ParentID", - "path": "Path", - "menu_sort": "MenuSort", - "component": "Component", - "is_frame": "IsFrame", - "is_cache": "IsCache", - "menu_type": "MenuType", - "visible": "Visible", - "status": "Status", - "perms": "Perms", - "icon": "Icon", - "create_by": "CreateBy", - "create_time": "CreateTime", - "update_by": "UpdateBy", - "update_time": "UpdateTime", - "remark": "Remark", - }, -} - -// RepoSysMenu 菜单表 数据层处理 -type RepoSysMenu struct { - // 查询视图对象SQL - selectSql string - // 查询视图用户对象SQL - selectSqlByUser string - // 结果字段与实体映射 - resultMap map[string]string -} - -// convertResultRows 将结果记录转实体结果组 -func (r *RepoSysMenu) convertResultRows(rows []map[string]any) []model.SysMenu { - arr := make([]model.SysMenu, 0) - for _, row := range rows { - sysMenu := model.SysMenu{} - for key, value := range row { - if keyMapper, ok := r.resultMap[key]; ok { - datasource.SetFieldValue(&sysMenu, keyMapper, value) - } - } - arr = append(arr, sysMenu) - } - return arr -} - -// SelectMenuList 查询系统菜单列表 -func (r *RepoSysMenu) SelectMenuList(sysMenu model.SysMenu, userId string) []model.SysMenu { - // 查询条件拼接 - var conditions []string - var params []any - if sysMenu.MenuName != "" { - conditions = append(conditions, "m.menu_name like concat('%', concat(?, '%'))") - params = append(params, sysMenu.MenuName) - } - if sysMenu.Visible != "" { - conditions = append(conditions, "m.visible = ?") - params = append(params, sysMenu.Visible) - } - if sysMenu.Status != "" { - conditions = append(conditions, "m.status = ?") - params = append(params, sysMenu.Status) - } - - fromSql := r.selectSql - - // 个人菜单 - if userId != "*" { - fromSql = r.selectSqlByUser - conditions = append(conditions, "ur.user_id = ?") - params = append(params, userId) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数据 - orderSql := " order by m.parent_id, m.menu_sort" - querySql := fromSql + whereSql + orderSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysMenu{} - } - - // 转换实体 - return r.convertResultRows(results) -} - -// SelectMenuPermsByUserId 根据用户ID查询权限 -func (r *RepoSysMenu) SelectMenuPermsByUserId(userId string) []string { - querySql := `select distinct m.perms as 'str' from sys_menu m - left join sys_role_menu rm on m.menu_id = rm.menu_id - left join sys_user_role ur on rm.role_id = ur.role_id - left join sys_role r on r.role_id = ur.role_id - where m.status = '1' and m.perms != '' and r.status = '1' and ur.user_id = ? ` - - // 查询结果 - results, err := datasource.RawDB("", querySql, []any{userId}) - if err != nil { - log.Errorf("query err => %v", err) - return []string{} - } - - // 读取结果 - rows := make([]string, 0) - for _, m := range results { - rows = append(rows, fmt.Sprintf("%v", m["str"])) - } - return rows -} - -// SelectMenuTreeByUserId 根据用户ID查询菜单 -func (r *RepoSysMenu) SelectMenuTreeByUserId(userId string) []model.SysMenu { - var params []any - var querySql string - - if userId == "*" { - // 管理员全部菜单 - querySql = r.selectSql + ` where - m.menu_type in (?,?) and m.status = '1' - order by m.parent_id, m.menu_sort` - params = append(params, consts.TYPE_DIR) - params = append(params, consts.TYPE_MENU) - } else { - // 用户ID权限 - querySql = r.selectSqlByUser + ` where - m.menu_type in (?, ?) and m.status = '1' - and ur.user_id = ? and ro.status = '1' - order by m.parent_id, m.menu_sort` - params = append(params, consts.TYPE_DIR) - params = append(params, consts.TYPE_MENU) - params = append(params, userId) - } - - // 查询结果 - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysMenu{} - } - - return r.convertResultRows(results) -} - -// SelectMenuListByRoleId 根据角色ID查询菜单树信息 -func (r *RepoSysMenu) SelectMenuListByRoleId(roleId string, menuCheckStrictly bool) []string { - querySql := `select m.menu_id as 'str' from sys_menu m - left join sys_role_menu rm on m.menu_id = rm.menu_id - where rm.role_id = ? ` - var params []any - params = append(params, roleId) - // 展开 - if menuCheckStrictly { - querySql += ` and m.menu_id not in - (select m.parent_id from sys_menu m - inner join sys_role_menu rm on m.menu_id = rm.menu_id - and rm.role_id = ?) ` - params = append(params, roleId) - } - - // 查询结果 - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - return []string{} - } - - if len(results) > 0 { - ids := make([]string, 0) - for _, v := range results { - ids = append(ids, fmt.Sprintf("%v", v["str"])) - } - return ids - } - return []string{} -} - -// SelectMenuByIds 根据菜单ID查询信息 -func (r *RepoSysMenu) SelectMenuByIds(menuIds []string) []model.SysMenu { - placeholder := datasource.KeyPlaceholderByQuery(len(menuIds)) - querySql := r.selectSql + " where m.menu_id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(menuIds) - results, err := datasource.RawDB("", querySql, parameters) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysMenu{} - } - // 转换实体 - return r.convertResultRows(results) -} - -// HasChildByMenuIdAndStatus 存在菜单子节点数量与状态 -func (r *RepoSysMenu) HasChildByMenuIdAndStatus(menuId, status string) int64 { - querySql := "select count(1) as 'total' from sys_menu where parent_id = ?" - params := []any{menuId} - - // 菜单状态 - if status != "" { - querySql += " and status = ?" - params = append(params, status) - } - - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - return 0 - } - if len(results) > 0 { - return parse.Number(results[0]["total"]) - } - return 0 -} - -// InsertMenu 新增菜单信息 -func (r *RepoSysMenu) InsertMenu(sysMenu model.SysMenu) string { - // 参数拼接 - params := make(map[string]any) - if sysMenu.MenuID != "" { - params["menu_id"] = sysMenu.MenuID - } - if sysMenu.ParentID != "" { - params["parent_id"] = sysMenu.ParentID - } - if sysMenu.MenuName != "" { - params["menu_name"] = sysMenu.MenuName - } - if sysMenu.MenuSort > 0 { - params["menu_sort"] = sysMenu.MenuSort - } - if sysMenu.Path != "" { - params["path"] = sysMenu.Path - } - if sysMenu.Component != "" { - params["component"] = sysMenu.Component - } - if sysMenu.IsFrame != "" { - params["is_frame"] = sysMenu.IsFrame - } - if sysMenu.IsCache != "" { - params["is_cache"] = sysMenu.IsCache - } - if sysMenu.MenuType != "" { - params["menu_type"] = sysMenu.MenuType - } - if sysMenu.Visible != "" { - params["visible"] = sysMenu.Visible - } - if sysMenu.Status != "" { - params["status"] = sysMenu.Status - } - if sysMenu.Perms != "" { - params["perms"] = sysMenu.Perms - } - if sysMenu.Icon != "" { - params["icon"] = sysMenu.Icon - } else { - params["icon"] = "#" - } - if sysMenu.Remark != "" { - params["remark"] = sysMenu.Remark - } - if sysMenu.CreateBy != "" { - params["create_by"] = sysMenu.CreateBy - params["create_time"] = time.Now().UnixMilli() - } - - // 根据菜单类型重置参数 - if sysMenu.MenuType == consts.TYPE_BUTTON { - params["component"] = "" - params["path"] = "" - params["icon"] = "#" - params["is_cache"] = "1" - params["is_frame"] = "1" - params["visible"] = "1" - params["status"] = "1" - } - if sysMenu.MenuType == consts.TYPE_DIR { - params["component"] = "" - params["perms"] = "" - } - - // 构建执行语句 - keys, placeholder, values := datasource.KeyPlaceholderValueByInsert(params) - sql := "insert into sys_menu (" + strings.Join(keys, ",") + ")values(" + placeholder + ")" - - // 执行插入 - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - - insertId, err := results.LastInsertId() - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - return fmt.Sprint(insertId) -} - -// UpdateMenu 修改菜单信息 -func (r *RepoSysMenu) UpdateMenu(sysMenu model.SysMenu) int64 { - // 参数拼接 - params := make(map[string]any) - if sysMenu.MenuID != "" { - params["menu_id"] = sysMenu.MenuID - } - if sysMenu.ParentID != "" { - params["parent_id"] = sysMenu.ParentID - } - if sysMenu.MenuName != "" { - params["menu_name"] = sysMenu.MenuName - } - if sysMenu.MenuSort > 0 { - params["menu_sort"] = sysMenu.MenuSort - } - if sysMenu.Path != "" { - params["path"] = sysMenu.Path - } - if sysMenu.Component != "" { - params["component"] = sysMenu.Component - } - if sysMenu.IsFrame != "" { - params["is_frame"] = sysMenu.IsFrame - } - if sysMenu.IsCache != "" { - params["is_cache"] = sysMenu.IsCache - } - if sysMenu.MenuType != "" { - params["menu_type"] = sysMenu.MenuType - } - if sysMenu.Visible != "" { - params["visible"] = sysMenu.Visible - } - if sysMenu.Status != "" { - params["status"] = sysMenu.Status - } - if sysMenu.Perms != "" { - params["perms"] = sysMenu.Perms - } - if sysMenu.Icon != "" { - params["icon"] = sysMenu.Icon - } else { - params["icon"] = "#" - } - if sysMenu.Remark != "" { - params["remark"] = sysMenu.Remark - } - if sysMenu.UpdateBy != "" { - params["update_by"] = sysMenu.UpdateBy - params["update_time"] = time.Now().UnixMilli() - } - - // 根据菜单类型重置参数 - if sysMenu.MenuType == consts.TYPE_BUTTON { - params["component"] = "" - params["path"] = "" - params["icon"] = "#" - params["is_cache"] = "1" - params["is_frame"] = "1" - params["visible"] = "1" - params["status"] = "1" - } - if sysMenu.MenuType == consts.TYPE_DIR { - params["component"] = "" - params["perms"] = "" - } - - // 构建执行语句 - keys, values := datasource.KeyValueByUpdate(params) - sql := "update sys_menu set " + strings.Join(keys, ",") + " where menu_id = ?" - - // 执行更新 - values = append(values, sysMenu.MenuID) - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("update row : %v", err.Error()) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("update err => %v", err) - return 0 - } - return affected -} - -// DeleteMenuById 删除菜单管理信息 -func (r *RepoSysMenu) DeleteMenuById(menuId string) int64 { - sql := "delete from sys_menu where menu_id = ?" - results, err := datasource.ExecDB("", sql, []any{menuId}) - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - return affected -} - -// CheckUniqueMenu 校验菜单是否唯一 -func (r *RepoSysMenu) CheckUniqueMenu(sysMenu model.SysMenu) string { - // 查询条件拼接 - var conditions []string - var params []any - if sysMenu.MenuName != "" { - conditions = append(conditions, "menu_name = ?") - params = append(params, sysMenu.MenuName) - } - if sysMenu.ParentID != "" { - conditions = append(conditions, "parent_id = ?") - params = append(params, sysMenu.ParentID) - } - if sysMenu.Path != "" { - conditions = append(conditions, "path = ?") - params = append(params, sysMenu.Path) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - if whereSql == "" { - return "" - } - - // 查询数据 - querySql := "select menu_id as 'str' from sys_menu " + whereSql + " limit 1" - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err %v", err) - return "" - } - if len(results) > 0 { - return fmt.Sprint(results[0]["str"]) - } - return "" -} diff --git a/features/sys_menu/service/service_sys_menu.go b/features/sys_menu/service/service_sys_menu.go deleted file mode 100644 index 0da85e3..0000000 --- a/features/sys_menu/service/service_sys_menu.go +++ /dev/null @@ -1,408 +0,0 @@ -package service - -import ( - "encoding/base64" - "strings" - - "be.ems/features/sys_menu/consts" - "be.ems/features/sys_menu/model" - sysRoleService "be.ems/features/sys_role/service" - sysrolemenu "be.ems/features/sys_role_menu" - "be.ems/lib/core/utils/parse" - "be.ems/lib/core/utils/regular" - "be.ems/lib/core/vo" -) - -// 实例化服务层 ServiceSysMenu 结构体 -var NewServiceSysMenu = &ServiceSysMenu{ - sysMenuRepository: NewRepoSysMenu, - sysRoleMenuRepository: sysrolemenu.NewRepoSysRoleMenu, - sysRoleRepository: sysRoleService.NewRepoSysRole, -} - -// ServiceSysMenu 菜单 服务层处理 -type ServiceSysMenu struct { - // 菜单服务 - sysMenuRepository *RepoSysMenu - // 角色与菜单关联服务 - sysRoleMenuRepository *sysrolemenu.RepoSysRoleMenu - // 角色服务 - sysRoleRepository *sysRoleService.RepoSysRole -} - -// SelectMenuList 查询系统菜单列表 -func (r *ServiceSysMenu) SelectMenuList(sysMenu model.SysMenu, userId string) []model.SysMenu { - return r.sysMenuRepository.SelectMenuList(sysMenu, userId) -} - -// SelectMenuPermsByUserId 根据用户ID查询权限 -func (r *ServiceSysMenu) SelectMenuPermsByUserId(userId string) []string { - return r.sysMenuRepository.SelectMenuPermsByUserId(userId) -} - -// SelectMenuTreeByUserId 根据用户ID查询菜单 -func (r *ServiceSysMenu) SelectMenuTreeByUserId(userId string) []model.SysMenu { - sysMenus := r.sysMenuRepository.SelectMenuTreeByUserId(userId) - return r.parseDataToTree(sysMenus) -} - -// SelectMenuTreeSelectByUserId 根据用户ID查询菜单树结构信息 -func (r *ServiceSysMenu) SelectMenuTreeSelectByUserId(sysMenu model.SysMenu, userId string) []vo.TreeSelect { - sysMenus := r.sysMenuRepository.SelectMenuList(sysMenu, userId) - - // 过滤旧前端菜单 - sysMenusF := []model.SysMenu{} - for _, v := range sysMenus { - if v.Perms != "page" { - continue - } - sysMenusF = append(sysMenusF, v) - } - - menus := r.parseDataToTree(sysMenusF) - tree := make([]vo.TreeSelect, 0) - for _, menu := range menus { - tree = append(tree, sysMenuTreeSelect(menu)) - } - return tree -} - -// sysMenuTreeSelect 使用给定的 SysMenu 对象解析为 TreeSelect 对象 -func sysMenuTreeSelect(sysMenu model.SysMenu) vo.TreeSelect { - t := vo.TreeSelect{} - t.ID = sysMenu.MenuID - t.Label = sysMenu.MenuName - t.Title = sysMenu.MenuName - - if len(sysMenu.Children) > 0 { - for _, menu := range sysMenu.Children { - child := sysMenuTreeSelect(menu) - t.Children = append(t.Children, child) - } - } else { - t.Children = []vo.TreeSelect{} - } - - return t -} - -// SelectMenuListByRoleId 根据角色ID查询菜单树信息 TODO -func (r *ServiceSysMenu) SelectMenuListByRoleId(roleId string) []string { - roles := r.sysRoleRepository.SelectRoleByIds([]string{roleId}) - if len(roles) > 0 { - role := roles[0] - if role.RoleID == roleId { - return r.sysMenuRepository.SelectMenuListByRoleId( - role.RoleID, - role.MenuCheckStrictly == "1", - ) - } - } - return []string{} -} - -// SelectMenuById 根据菜单ID查询信息 -func (r *ServiceSysMenu) SelectMenuById(menuId string) model.SysMenu { - if menuId == "" { - return model.SysMenu{} - } - menus := r.sysMenuRepository.SelectMenuByIds([]string{menuId}) - if len(menus) > 0 { - return menus[0] - } - return model.SysMenu{} -} - -// HasChildByMenuIdAndStatus 存在菜单子节点数量与状态 -func (r *ServiceSysMenu) HasChildByMenuIdAndStatus(menuId, status string) int64 { - return r.sysMenuRepository.HasChildByMenuIdAndStatus(menuId, status) -} - -// CheckMenuExistRole 查询菜单是否存在角色 -func (r *ServiceSysMenu) CheckMenuExistRole(menuId string) int64 { - return r.sysRoleMenuRepository.CheckMenuExistRole(menuId) -} - -// InsertMenu 新增菜单信息 -func (r *ServiceSysMenu) InsertMenu(sysMenu model.SysMenu) string { - return r.sysMenuRepository.InsertMenu(sysMenu) -} - -// UpdateMenu 修改菜单信息 -func (r *ServiceSysMenu) UpdateMenu(sysMenu model.SysMenu) int64 { - return r.sysMenuRepository.UpdateMenu(sysMenu) -} - -// DeleteMenuById 删除菜单管理信息 -func (r *ServiceSysMenu) DeleteMenuById(menuId string) int64 { - // 删除菜单与角色关联 - r.sysRoleMenuRepository.DeleteMenuRole([]string{menuId}) - return r.sysMenuRepository.DeleteMenuById(menuId) -} - -// CheckUniqueMenuName 校验菜单名称是否唯一 -func (r *ServiceSysMenu) CheckUniqueMenuName(menuName, parentId, menuId string) bool { - uniqueId := r.sysMenuRepository.CheckUniqueMenu(model.SysMenu{ - MenuName: menuName, - ParentID: parentId, - }) - if uniqueId == menuId { - return true - } - return uniqueId == "" -} - -// CheckUniqueMenuPath 校验路由地址是否唯一(针对目录和菜单) -func (r *ServiceSysMenu) CheckUniqueMenuPath(path, menuId string) bool { - uniqueId := r.sysMenuRepository.CheckUniqueMenu(model.SysMenu{ - Path: path, - }) - if uniqueId == menuId { - return true - } - return uniqueId == "" -} - -// BuildRouteMenus 构建前端路由所需要的菜单 -func (r *ServiceSysMenu) BuildRouteMenus(sysMenus []model.SysMenu, prefix string) []vo.Router { - routers := []vo.Router{} - for _, item := range sysMenus { - router := vo.Router{} - router.Name = r.getRouteName(item) - router.Path = r.getRouterPath(item) - router.Component = r.getComponent(item) - router.Meta = r.getRouteMeta(item) - - // 子项菜单 目录类型 非路径链接 - cMenus := item.Children - if len(cMenus) > 0 && item.MenuType == consts.TYPE_DIR && !regular.ValidHttp(item.Path) { - // 获取重定向地址 - redirectPrefix, redirectPath := r.getRouteRedirect( - cMenus, - router.Path, - prefix, - ) - router.Redirect = redirectPath - // 子菜单进入递归 - router.Children = r.BuildRouteMenus(cMenus, redirectPrefix) - } else if item.ParentID == "0" && item.IsFrame == "1" && item.MenuType == consts.TYPE_MENU { - // 父菜单 内部跳转 菜单类型 - menuPath := "/" + item.MenuID - childPath := menuPath + r.getRouterPath(item) - children := vo.Router{ - Name: r.getRouteName(item), - Path: childPath, - Component: item.Component, - Meta: r.getRouteMeta(item), - } - router.Meta.HideChildInMenu = true - router.Children = append(router.Children, children) - router.Name = item.MenuID - router.Path = menuPath - router.Redirect = childPath - router.Component = consts.COMPONENT_LAYOUT_BASIC - } else if item.ParentID == "0" && item.IsFrame == "1" && regular.ValidHttp(item.Path) { - // 父菜单 内部跳转 路径链接 - menuPath := "/" + item.MenuID - childPath := menuPath + r.getRouterPath(item) - children := vo.Router{ - Name: r.getRouteName(item), - Path: childPath, - Component: consts.COMPONENT_LAYOUT_LINK, - Meta: r.getRouteMeta(item), - } - router.Meta.HideChildInMenu = true - router.Children = append(router.Children, children) - router.Name = item.MenuID - router.Path = menuPath - router.Redirect = childPath - router.Component = consts.COMPONENT_LAYOUT_BASIC - } - - routers = append(routers, router) - } - return routers -} - -// getRouteName 获取路由名称 路径英文首字母大写 -func (r *ServiceSysMenu) getRouteName(sysMenu model.SysMenu) string { - routerName := parse.FirstUpper(sysMenu.Path) - // 路径链接 - if regular.ValidHttp(sysMenu.Path) { - return routerName[:5] + "Link" + sysMenu.MenuID - } - return routerName -} - -// getRouterPath 获取路由地址 -func (r *ServiceSysMenu) getRouterPath(sysMenu model.SysMenu) string { - routerPath := sysMenu.Path - - // 显式路径 - if routerPath == "" || strings.HasPrefix(routerPath, "/") { - return routerPath - } - - // 路径链接 内部跳转 - if regular.ValidHttp(routerPath) && sysMenu.IsFrame == "1" { - routerPath = regular.Replace(routerPath, `/^http(s)?:\/\/+/`, "") - routerPath = base64.StdEncoding.EncodeToString([]byte(routerPath)) - } - - // 父菜单 内部跳转 - if sysMenu.ParentID == "0" && sysMenu.IsFrame == "1" { - routerPath = "/" + routerPath - } - - return routerPath -} - -// getComponent 获取组件信息 -func (r *ServiceSysMenu) getComponent(sysMenu model.SysMenu) string { - // 内部跳转 路径链接 - if sysMenu.IsFrame == "1" && regular.ValidHttp(sysMenu.Path) { - return consts.COMPONENT_LAYOUT_LINK - } - - // 非父菜单 目录类型 - if sysMenu.ParentID != "0" && sysMenu.MenuType == consts.TYPE_DIR { - return consts.COMPONENT_LAYOUT_BLANK - } - - // 组件路径 内部跳转 菜单类型 - if sysMenu.Component != "" && sysMenu.IsFrame == "1" && sysMenu.MenuType == consts.TYPE_MENU { - // 父菜单套外层布局 - if sysMenu.ParentID == "0" { - return consts.COMPONENT_LAYOUT_BASIC - } - return sysMenu.Component - } - - return consts.COMPONENT_LAYOUT_BASIC -} - -// getRouteMeta 获取路由元信息 -func (r *ServiceSysMenu) getRouteMeta(sysMenu model.SysMenu) vo.RouterMeta { - meta := vo.RouterMeta{} - if sysMenu.Icon == "#" { - meta.Icon = "" - } else { - meta.Icon = sysMenu.Icon - } - meta.Title = sysMenu.MenuName - meta.HideChildInMenu = false - meta.HideInMenu = sysMenu.Visible == "0" - meta.Cache = sysMenu.IsCache == "1" - meta.Target = "" - - // 路径链接 非内部跳转 - if regular.ValidHttp(sysMenu.Path) && sysMenu.IsFrame == "0" { - meta.Target = "_blank" - } - - return meta -} - -// getRouteRedirect 获取路由重定向地址(针对目录) -// -// cMenus 子菜单数组 -// routerPath 当前菜单路径 -// prefix 菜单重定向路径前缀 -func (r *ServiceSysMenu) getRouteRedirect(cMenus []model.SysMenu, routerPath string, prefix string) (string, string) { - redirectPath := "" - - // 重定向为首个显示并启用的子菜单 - var firstChild *model.SysMenu - for _, item := range cMenus { - if item.IsFrame == "1" && item.Visible == "1" { - firstChild = &item - break - } - } - - // 检查内嵌隐藏菜单是否可做重定向 - if firstChild == nil { - for _, item := range cMenus { - if item.IsFrame == "1" && item.Visible == "1" && strings.Contains(item.Path, consts.PATH_INLINE) { - firstChild = &item - break - } - } - } - - if firstChild != nil { - firstChildPath := r.getRouterPath(*firstChild) - if strings.HasPrefix(firstChildPath, "/") { - redirectPath = firstChildPath - } else { - // 拼接追加路径 - if !strings.HasPrefix(routerPath, "/") { - prefix += "/" - } - prefix = prefix + routerPath - redirectPath = prefix + "/" + firstChildPath - } - } - - return prefix, redirectPath -} - -// parseDataToTree 将数据解析为树结构,构建前端所需要下拉树结构 -func (r *ServiceSysMenu) parseDataToTree(sysMenus []model.SysMenu) []model.SysMenu { - // 节点分组 - nodesMap := make(map[string][]model.SysMenu) - // 节点id - treeIds := []string{} - // 树节点 - tree := []model.SysMenu{} - - for _, item := range sysMenus { - parentID := item.ParentID - // 分组 - mapItem, ok := nodesMap[parentID] - if !ok { - mapItem = []model.SysMenu{} - } - mapItem = append(mapItem, item) - nodesMap[parentID] = mapItem - // 记录节点ID - treeIds = append(treeIds, item.MenuID) - } - - for key, value := range nodesMap { - // 选择不是节点ID的作为树节点 - found := false - for _, id := range treeIds { - if id == key { - found = true - break - } - } - if !found { - tree = append(tree, value...) - } - } - - for i, node := range tree { - iN := r.parseDataToTreeComponet(node, &nodesMap) - tree[i] = iN - } - - return tree -} - -// parseDataToTreeComponet 递归函数处理子节点 -func (r *ServiceSysMenu) parseDataToTreeComponet(node model.SysMenu, nodesMap *map[string][]model.SysMenu) model.SysMenu { - id := node.MenuID - children, ok := (*nodesMap)[id] - if ok { - node.Children = children - } - if len(node.Children) > 0 { - for i, child := range node.Children { - icN := r.parseDataToTreeComponet(child, nodesMap) - node.Children[i] = icN - } - } - return node -} diff --git a/features/sys_role/api_sys_role.go b/features/sys_role/api_sys_role.go deleted file mode 100644 index ec43a1e..0000000 --- a/features/sys_role/api_sys_role.go +++ /dev/null @@ -1,368 +0,0 @@ -package sysrole - -import ( - "fmt" - "net/http" - "strings" - - "be.ems/features/sys_role/model" - "be.ems/features/sys_role/service" - userService "be.ems/features/sys_user/service" - "be.ems/lib/core/utils/ctx" - "be.ems/lib/core/utils/parse" - "be.ems/lib/core/vo/result" - "be.ems/lib/midware" - "be.ems/lib/services" - "be.ems/restagent/config" -) - -// 角色接口添加到路由 -func Routers() []services.RouterItem { - // 实例化控制层 SysRoleApi 结构体 - var apis = &SysRoleApi{ - sysRoleService: service.NewServiceSysRole, - sysUserService: userService.NewServiceSysUser, - } - - rs := [...]services.RouterItem{ - { - Method: "GET", - Pattern: "/roles", - Handler: apis.List, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/role/{roleId}", - Handler: apis.Info, - Middleware: midware.Authorize(nil), - }, - { - Method: "POST", - Pattern: "/role", - Handler: apis.Add, - Middleware: midware.Authorize(nil), - }, - { - Method: "PUT", - Pattern: "/role", - Handler: apis.Edit, - Middleware: midware.Authorize(nil), - }, - { - Method: "DELETE", - Pattern: "/role/{roleIds}", - Handler: apis.Remove, - Middleware: midware.Authorize(nil), - }, - { - Method: "PUT", - Pattern: "/role/changeStatus", - Handler: apis.Status, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/role/authUser/allocatedList", - Handler: apis.AuthUserAllocatedList, - Middleware: midware.Authorize(nil), - }, - { - Method: "PUT", - Pattern: "/role/authUser/checked", - Handler: apis.AuthUserChecked, - Middleware: midware.Authorize(nil), - }, - // 添加更多的 Router 对象... - } - - // 生成两组前缀路由 - rsPrefix := []services.RouterItem{} - for _, v := range rs { - path := "/roleManage/{apiVersion}" + v.Pattern - // 固定前缀 - v.Pattern = config.DefaultUriPrefix + path - rsPrefix = append(rsPrefix, v) - // 可配置 - v.Pattern = config.UriPrefix + path - rsPrefix = append(rsPrefix, v) - } - return rsPrefix -} - -// // 实例化控制层 SysRoleApi 结构体 -// var NewSysRole = &SysRoleApi{ -// sysRoleService: sysrole.NewServiceSysRole, -// sysUserService: sysuser.NewServiceSysUser, -// } - -// 角色信息 -// -// PATH /roleManage -type SysRoleApi struct { - // 角色服务 - sysRoleService *service.ServiceSysRole - // 用户服务 - sysUserService *userService.ServiceSysUser -} - -// 角色列表 -// -// GET /list -func (s *SysRoleApi) List(w http.ResponseWriter, r *http.Request) { - querys := ctx.QueryMap(r) - data := s.sysRoleService.SelectRolePage(querys) - ctx.JSON(w, 200, result.Ok(data)) -} - -// 角色信息详情 -// -// GET /:roleId -func (s *SysRoleApi) Info(w http.ResponseWriter, r *http.Request) { - roleId := ctx.Param(r, "roleId") - if roleId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - data := s.sysRoleService.SelectRoleById(roleId) - if data.RoleID == roleId { - ctx.JSON(w, 200, result.OkData(data)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 角色信息新增 -// -// POST / -func (s *SysRoleApi) Add(w http.ResponseWriter, r *http.Request) { - var body model.SysRole - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.RoleID != "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 判断角色名称是否唯一 - uniqueRoleName := s.sysRoleService.CheckUniqueRoleName(body.RoleName, "") - if !uniqueRoleName { - msg := fmt.Sprintf("[%s] Role name already exists", body.RoleName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 判断角色键值是否唯一 - uniqueRoleKey := s.sysRoleService.CheckUniqueRoleKey(body.RoleKey, "") - if !uniqueRoleKey { - msg := fmt.Sprintf("[%s] The role key value already exists", body.RoleName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - body.CreateBy = ctx.LoginUserToUserName(r) - insertId := s.sysRoleService.InsertRole(body) - if insertId != "" { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 角色信息修改 -// -// PUT / -func (s *SysRoleApi) Edit(w http.ResponseWriter, r *http.Request) { - var body model.SysRole - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.RoleID == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查是否管理员角色 - if body.RoleID == "1" { - ctx.JSON(w, 200, result.ErrMsg("Operation of administrator role is not allowed")) - return - } - - // 检查是否存在 - role := s.sysRoleService.SelectRoleById(body.RoleID) - if role.RoleID != body.RoleID { - ctx.JSON(w, 200, result.ErrMsg("No permission to access role data!")) - return - } - - // 判断角色名称是否唯一 - uniqueRoleName := s.sysRoleService.CheckUniqueRoleName(body.RoleName, body.RoleID) - if !uniqueRoleName { - msg := fmt.Sprintf("[%s] Role name already exists", body.RoleName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - // 判断角色键值是否唯一 - uniqueRoleKey := s.sysRoleService.CheckUniqueRoleKey(body.RoleKey, body.RoleID) - if !uniqueRoleKey { - msg := fmt.Sprintf("[%s] The role key value already exists", body.RoleName) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - body.UpdateBy = ctx.LoginUserToUserName(r) - rows := s.sysRoleService.UpdateRole(body) - if rows > 0 { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 角色信息删除 -// -// DELETE /:roleIds -func (s *SysRoleApi) Remove(w http.ResponseWriter, r *http.Request) { - roleIds := ctx.Param(r, "roleIds") - if roleIds == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - // 处理字符转id数组后去重 - ids := strings.Split(roleIds, ",") - uniqueIDs := parse.RemoveDuplicates(ids) - if len(uniqueIDs) <= 0 { - ctx.JSON(w, 200, result.Err(nil)) - return - } - // 检查是否管理员角色 - for _, id := range uniqueIDs { - if id == "1" { - ctx.JSON(w, 200, result.ErrMsg("Operation of administrator role is not allowed")) - return - } - } - rows, err := s.sysRoleService.DeleteRoleByIds(uniqueIDs) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - msg := fmt.Sprintf("Successfully deleted: %d", rows) - ctx.JSON(w, 200, result.OkMsg(msg)) -} - -// 角色状态变更 -// -// PUT /changeStatus -func (s *SysRoleApi) Status(w http.ResponseWriter, r *http.Request) { - var body struct { - // 角色ID - RoleID string `json:"roleId" binding:"required"` - // 状态 - Status string `json:"status" binding:"required"` - } - err := ctx.ShouldBindJSON(r, &body) - if err != nil { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查是否管理员角色 - if body.RoleID == "1" { - ctx.JSON(w, 200, result.ErrMsg("Operation of administrator role is not allowed")) - return - } - - // 检查是否存在 - role := s.sysRoleService.SelectRoleById(body.RoleID) - if role.RoleID != body.RoleID { - ctx.JSON(w, 200, result.ErrMsg("No permission to access role data!")) - return - } - - // 与旧值相等不变更 - if role.Status == body.Status { - ctx.JSON(w, 200, result.ErrMsg("Change status equals old value!")) - return - } - - // 更新状态不刷新缓存 - userName := ctx.LoginUserToUserName(r) - SysRoleApi := model.SysRole{ - RoleID: body.RoleID, - Status: body.Status, - UpdateBy: userName, - } - rows := s.sysRoleService.UpdateRole(SysRoleApi) - if rows > 0 { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 角色分配用户列表 -// -// GET /authUser/allocatedList -func (s *SysRoleApi) AuthUserAllocatedList(w http.ResponseWriter, r *http.Request) { - querys := ctx.QueryMap(r) - roleId, ok := querys["roleId"] - if !ok || roleId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查是否存在 - role := s.sysRoleService.SelectRoleById(roleId.(string)) - if role.RoleID != roleId { - ctx.JSON(w, 200, result.ErrMsg("No permission to access role data!")) - return - } - - data := s.sysUserService.SelectAllocatedPage(querys) - ctx.JSON(w, 200, result.Ok(data)) -} - -// 角色分配选择授权 -// -// PUT /authUser/checked -func (s *SysRoleApi) AuthUserChecked(w http.ResponseWriter, r *http.Request) { - var body struct { - // 角色ID - RoleID string `json:"roleId" binding:"required"` - // 用户ID组 - UserIDs string `json:"userIds" binding:"required"` - // 选择操作 添加true 取消false - Checked bool `json:"checked"` - } - err := ctx.ShouldBindJSON(r, &body) - if err != nil { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 处理字符转id数组后去重 - ids := strings.Split(body.UserIDs, ",") - uniqueIDs := parse.RemoveDuplicates(ids) - if len(uniqueIDs) <= 0 { - ctx.JSON(w, 200, result.Err(nil)) - return - } - - // 检查是否存在 - role := s.sysRoleService.SelectRoleById(body.RoleID) - if role.RoleID != body.RoleID { - ctx.JSON(w, 200, result.ErrMsg("No permission to access role data!")) - return - } - - var rows int64 - if body.Checked { - rows = s.sysRoleService.InsertAuthUsers(body.RoleID, uniqueIDs) - } else { - rows = s.sysRoleService.DeleteAuthUsers(body.RoleID, uniqueIDs) - } - if rows > 0 { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} diff --git a/features/sys_role/model/sys_role.go b/features/sys_role/model/sys_role.go deleted file mode 100644 index 921fd25..0000000 --- a/features/sys_role/model/sys_role.go +++ /dev/null @@ -1,38 +0,0 @@ -package model - -// SysRole 角色对象 sys_role -type SysRole struct { - // 角色ID - RoleID string `json:"roleId"` - // 角色名称 - RoleName string `json:"roleName" binding:"required"` - // 角色键值 - RoleKey string `json:"roleKey" binding:"required"` - // 显示顺序 - RoleSort int `json:"roleSort"` - // 菜单树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示) - MenuCheckStrictly string `json:"menuCheckStrictly"` - // 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示) - DeptCheckStrictly string `json:"deptCheckStrictly"` - // 角色状态(0停用 1正常) - Status string `json:"status"` - // 删除标志(0代表存在 1代表删除) - DelFlag string `json:"delFlag"` - // 创建者 - CreateBy string `json:"createBy"` - // 创建时间 - CreateTime int64 `json:"createTime"` - // 更新者 - UpdateBy string `json:"updateBy"` - // 更新时间 - UpdateTime int64 `json:"updateTime"` - // 备注 - Remark string `json:"remark"` - - // ====== 非数据库字段属性 ====== - - // 菜单组 - MenuIds []string `json:"menuIds,omitempty"` - // 部门组(数据权限) - DeptIds []string `json:"deptIds,omitempty"` -} diff --git a/features/sys_role/service/repo_sys_role.go b/features/sys_role/service/repo_sys_role.go deleted file mode 100644 index c4182ac..0000000 --- a/features/sys_role/service/repo_sys_role.go +++ /dev/null @@ -1,362 +0,0 @@ -package service - -import ( - "fmt" - "strings" - "time" - - "be.ems/features/sys_role/model" - "be.ems/lib/core/datasource" - "be.ems/lib/core/utils/date" - "be.ems/lib/core/utils/parse" - "be.ems/lib/log" -) - -// 实例化数据层 RepoSysRole 结构体 -var NewRepoSysRole = &RepoSysRole{ - selectSql: `select distinct - r.role_id, r.role_name, r.role_key, r.role_sort, r.menu_check_strictly, - r.dept_check_strictly, r.status, r.del_flag, r.create_time, r.remark - from sys_role r - left join sys_user_role ur on ur.role_id = r.role_id - left join user u on u.id = ur.user_id`, - - resultMap: map[string]string{ - "role_id": "RoleID", - "role_name": "RoleName", - "role_key": "RoleKey", - "role_sort": "RoleSort", - "menu_check_strictly": "MenuCheckStrictly", - "dept_check_strictly": "DeptCheckStrictly", - "status": "Status", - "del_flag": "DelFlag", - "create_by": "CreateBy", - "create_time": "CreateTime", - "update_by": "UpdateBy", - "update_time": "UpdateTime", - "remark": "Remark", - }, -} - -// RepoSysRole 角色表 数据层处理 -type RepoSysRole struct { - // 查询视图对象SQL - selectSql string - // 结果字段与实体映射 - resultMap map[string]string -} - -// convertResultRows 将结果记录转实体结果组 -func (r *RepoSysRole) convertResultRows(rows []map[string]any) []model.SysRole { - arr := make([]model.SysRole, 0) - for _, row := range rows { - sysRole := model.SysRole{} - for key, value := range row { - if keyMapper, ok := r.resultMap[key]; ok { - datasource.SetFieldValue(&sysRole, keyMapper, value) - } - } - arr = append(arr, sysRole) - } - return arr -} - -// SelectRolePage 根据条件分页查询角色数据 -func (r *RepoSysRole) SelectRolePage(query map[string]any) map[string]any { - // 查询条件拼接 - var conditions []string - var params []any - if v, ok := query["roleId"]; ok && v != "" { - conditions = append(conditions, "r.role_id = ?") - params = append(params, v) - } - if v, ok := query["roleName"]; ok && v != "" { - conditions = append(conditions, "r.role_name like concat('%', concat(?, '%'))") - params = append(params, v) - } - if v, ok := query["roleKey"]; ok && v != "" { - conditions = append(conditions, "r.role_key like concat('%', concat(?, '%'))") - params = append(params, v) - } - if v, ok := query["status"]; ok && v != "" { - conditions = append(conditions, "r.status = ?") - params = append(params, v) - } - beginTime, ok := query["beginTime"] - if !ok { - beginTime, ok = query["params[beginTime]"] - } - if ok && beginTime != "" { - conditions = append(conditions, "r.create_time >= ?") - beginDate := date.ParseStrToDate(beginTime.(string), date.YYYY_MM_DD) - params = append(params, beginDate.UnixMilli()) - } - endTime, ok := query["endTime"] - if !ok { - endTime, ok = query["params[endTime]"] - } - if ok && endTime != "" { - conditions = append(conditions, "r.create_time <= ?") - endDate := date.ParseStrToDate(endTime.(string), date.YYYY_MM_DD) - params = append(params, endDate.UnixMilli()) - } - - // 构建查询条件语句 - whereSql := " where r.del_flag = '0' " - if len(conditions) > 0 { - whereSql += " and " + strings.Join(conditions, " and ") - } - - // 查询数量 长度为0直接返回 - totalSql := `select count(distinct r.role_id) as 'total' from sys_role r - left join sys_user_role ur on ur.role_id = r.role_id - left join user u on u.id = ur.user_id` - totalRows, err := datasource.RawDB("", totalSql+whereSql, params) - if err != nil { - log.Errorf("total err => %v", err) - } - total := parse.Number(totalRows[0]["total"]) - if total == 0 { - return map[string]any{ - "total": total, - "rows": []model.SysRole{}, - } - } - - // 分页 - pageNum, pageSize := datasource.PageNumSize(query["pageNum"], query["pageSize"]) - pageSql := " order by r.role_sort asc limit ?,? " - params = append(params, pageNum*pageSize) - params = append(params, pageSize) - - // 查询数据 - querySql := r.selectSql + whereSql + pageSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - } - - // 转换实体 - rows := r.convertResultRows(results) - return map[string]any{ - "total": total, - "rows": rows, - } -} - -// SelectRoleList 根据条件查询角色数据 -func (r *RepoSysRole) SelectRoleList(sysRole model.SysRole) []model.SysRole { - // 查询条件拼接 - var conditions []string - var params []any - if sysRole.RoleID != "" { - conditions = append(conditions, "r.role_id = ?") - params = append(params, sysRole.RoleID) - } - if sysRole.RoleKey != "" { - conditions = append(conditions, "r.role_key like concat('%', concat(?, '%'))") - params = append(params, sysRole.RoleKey) - } - if sysRole.RoleName != "" { - conditions = append(conditions, "r.role_name like concat('%', concat(?, '%'))") - params = append(params, sysRole.RoleName) - } - if sysRole.Status != "" { - conditions = append(conditions, "r.status = ?") - params = append(params, sysRole.Status) - } - - // 构建查询条件语句 - whereSql := " where r.del_flag = '0' " - if len(conditions) > 0 { - whereSql += " and " + strings.Join(conditions, " and ") - } - - // 查询数据 - orderSql := " order by r.role_sort" - querySql := r.selectSql + whereSql + orderSql - rows, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysRole{} - } - return r.convertResultRows(rows) -} - -// SelectRoleListByUserId 根据用户ID获取角色选择框列表 -func (r *RepoSysRole) SelectRoleListByUserId(userId string) []model.SysRole { - querySql := r.selectSql + " where r.del_flag = '0' and ur.user_id = ?" - results, err := datasource.RawDB("", querySql, []any{userId}) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysRole{} - } - return r.convertResultRows(results) -} - -// SelectRoleByIds 通过角色ID查询角色 -func (r *RepoSysRole) SelectRoleByIds(roleIds []string) []model.SysRole { - placeholder := datasource.KeyPlaceholderByQuery(len(roleIds)) - querySql := r.selectSql + " where r.role_id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(roleIds) - results, err := datasource.RawDB("", querySql, parameters) - if err != nil { - log.Errorf("query err => %v", err) - return []model.SysRole{} - } - // 转换实体 - return r.convertResultRows(results) -} - -// UpdateRole 修改角色信息 -func (r *RepoSysRole) UpdateRole(sysRole model.SysRole) int64 { - // 参数拼接 - params := make(map[string]any) - if sysRole.RoleName != "" { - params["role_name"] = sysRole.RoleName - } - if sysRole.RoleKey != "" { - params["role_key"] = sysRole.RoleKey - } - if sysRole.RoleSort > 0 { - params["role_sort"] = sysRole.RoleSort - } - if sysRole.MenuCheckStrictly != "" { - params["menu_check_strictly"] = sysRole.MenuCheckStrictly - } - if sysRole.DeptCheckStrictly != "" { - params["dept_check_strictly"] = sysRole.DeptCheckStrictly - } - if sysRole.Status != "" { - params["status"] = sysRole.Status - } - if sysRole.Remark != "" { - params["remark"] = sysRole.Remark - } - if sysRole.UpdateBy != "" { - params["update_by"] = sysRole.UpdateBy - params["update_time"] = time.Now().UnixMilli() - } - - // 构建执行语句 - keys, values := datasource.KeyValueByUpdate(params) - sql := "update sys_role set " + strings.Join(keys, ",") + " where role_id = ?" - - // 执行更新 - values = append(values, sysRole.RoleID) - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("update row : %v", err.Error()) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("update err => %v", err) - return 0 - } - return affected -} - -// InsertRole 新增角色信息 -func (r *RepoSysRole) InsertRole(sysRole model.SysRole) string { - // 参数拼接 - params := make(map[string]any) - if sysRole.RoleID != "" { - params["role_id"] = sysRole.RoleID - } - if sysRole.RoleName != "" { - params["role_name"] = sysRole.RoleName - } - if sysRole.RoleKey != "" { - params["role_key"] = sysRole.RoleKey - } - if sysRole.RoleSort > 0 { - params["role_sort"] = sysRole.RoleSort - } - if sysRole.MenuCheckStrictly != "" { - params["menu_check_strictly"] = sysRole.MenuCheckStrictly - } - if sysRole.DeptCheckStrictly != "" { - params["dept_check_strictly"] = sysRole.DeptCheckStrictly - } - if sysRole.Status != "" { - params["status"] = sysRole.Status - } - if sysRole.Remark != "" { - params["remark"] = sysRole.Remark - } - if sysRole.CreateBy != "" { - params["create_by"] = sysRole.CreateBy - params["create_time"] = time.Now().UnixMilli() - } - - // 构建执行语句 - keys, placeholder, values := datasource.KeyPlaceholderValueByInsert(params) - sql := "insert into sys_role (" + strings.Join(keys, ",") + ")values(" + placeholder + ")" - - // 执行插入 - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - insertId, err := results.LastInsertId() - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - return fmt.Sprint(insertId) -} - -// DeleteRoleByIds 批量删除角色信息 -func (r *RepoSysRole) DeleteRoleByIds(roleIds []string) int64 { - placeholder := datasource.KeyPlaceholderByQuery(len(roleIds)) - sql := "update sys_role set del_flag = '1' where role_id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(roleIds) - results, err := datasource.ExecDB("", sql, parameters) - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - return affected -} - -// CheckUniqueRole 校验角色是否唯一 -func (r *RepoSysRole) CheckUniqueRole(sysRole model.SysRole) string { - // 查询条件拼接 - var conditions []string - var params []any - if sysRole.RoleName != "" { - conditions = append(conditions, "r.role_name = ?") - params = append(params, sysRole.RoleName) - } - if sysRole.RoleKey != "" { - conditions = append(conditions, "r.role_key = ?") - params = append(params, sysRole.RoleKey) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } else { - return "" - } - - // 查询数据 - querySql := "select role_id as 'str' from sys_role r " + whereSql + " and r.del_flag = '0' limit 1" - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err %v", err) - return "" - } - if len(results) > 0 { - return fmt.Sprint(results[0]["str"]) - } - return "" -} diff --git a/features/sys_role/service/service_sys_role.go b/features/sys_role/service/service_sys_role.go deleted file mode 100644 index 33d89be..0000000 --- a/features/sys_role/service/service_sys_role.go +++ /dev/null @@ -1,165 +0,0 @@ -package service - -import ( - "errors" - "fmt" - - "be.ems/features/sys_role/model" - sysrolemenu "be.ems/features/sys_role_menu" - sysuserrole "be.ems/features/sys_user_role" -) - -// 实例化服务层 ServiceSysRole 结构体 -var NewServiceSysRole = &ServiceSysRole{ - sysRoleRepository: NewRepoSysRole, - sysUserRoleRepository: sysuserrole.NewRepoSysUserRole, - sysRoleMenuRepository: sysrolemenu.NewRepoSysRoleMenu, -} - -// ServiceSysRole 角色 服务层处理 -type ServiceSysRole struct { - // 角色服务 - sysRoleRepository *RepoSysRole - // 用户与角色关联服务 - sysUserRoleRepository *sysuserrole.RepoSysUserRole - // 角色与菜单关联服务 - sysRoleMenuRepository *sysrolemenu.RepoSysRoleMenu -} - -// SelectRolePage 根据条件分页查询角色数据 -func (r *ServiceSysRole) SelectRolePage(query map[string]any) map[string]any { - return r.sysRoleRepository.SelectRolePage(query) -} - -// SelectRoleList 根据条件查询角色数据 -func (r *ServiceSysRole) SelectRoleList(sysRole model.SysRole) []model.SysRole { - return r.sysRoleRepository.SelectRoleList(sysRole) -} - -// SelectRoleListByUserId 根据用户ID获取角色选择框列表 -func (r *ServiceSysRole) SelectRoleListByUserId(userId string) []model.SysRole { - return r.sysRoleRepository.SelectRoleListByUserId(userId) -} - -// SelectRoleById 通过角色ID查询角色 -func (r *ServiceSysRole) SelectRoleById(roleId string) model.SysRole { - if roleId == "" { - return model.SysRole{} - } - posts := r.sysRoleRepository.SelectRoleByIds([]string{roleId}) - if len(posts) > 0 { - return posts[0] - } - return model.SysRole{} -} - -// UpdateRole 修改角色信息 -func (r *ServiceSysRole) UpdateRole(sysRole model.SysRole) int64 { - rows := r.sysRoleRepository.UpdateRole(sysRole) - if rows > 0 { - // 删除角色与菜单关联 - r.sysRoleMenuRepository.DeleteRoleMenu([]string{sysRole.RoleID}) - if len(sysRole.MenuIds) > 0 { - r.insertRoleMenu(sysRole.RoleID, sysRole.MenuIds) - } - } - return rows -} - -// InsertRole 新增角色信息 -func (r *ServiceSysRole) InsertRole(sysRole model.SysRole) string { - insertId := r.sysRoleRepository.InsertRole(sysRole) - if insertId != "" && len(sysRole.MenuIds) > 0 { - r.insertRoleMenu(insertId, sysRole.MenuIds) - } - return insertId -} - -// insertRoleMenu 新增角色菜单信息 -func (r *ServiceSysRole) insertRoleMenu(roleId string, menuIds []string) int64 { - if roleId == "" || len(menuIds) <= 0 { - return 0 - } - - sysRoleMenus := []sysrolemenu.SysRoleMenu{} - for _, menuId := range menuIds { - if menuId == "" { - continue - } - sysRoleMenus = append(sysRoleMenus, sysrolemenu.NewSysRoleMenu(roleId, menuId)) - } - - return r.sysRoleMenuRepository.BatchRoleMenu(sysRoleMenus) -} - -// DeleteRoleByIds 批量删除角色信息 -func (r *ServiceSysRole) DeleteRoleByIds(roleIds []string) (int64, error) { - // 检查是否存在 - roles := r.sysRoleRepository.SelectRoleByIds(roleIds) - if len(roles) <= 0 { - return 0, errors.New("no permission to access role data") - } - for _, role := range roles { - // 检查是否为已删除 - if role.DelFlag == "1" { - return 0, errors.New(role.RoleID + " The character information has been deleted") - } - // 检查分配用户 - userCount := r.sysUserRoleRepository.CountUserRoleByRoleId(role.RoleID) - if userCount > 0 { - msg := fmt.Sprintf("[%s] has been assigned to a user and cannot be deleted", role.RoleName) - return 0, errors.New(msg) - } - } - if len(roles) == len(roleIds) { - // 删除角色与菜单关联 - r.sysRoleMenuRepository.DeleteRoleMenu(roleIds) - rows := r.sysRoleRepository.DeleteRoleByIds(roleIds) - return rows, nil - } - return 0, errors.New("failed to delete role information") -} - -// CheckUniqueRoleName 校验角色名称是否唯一 -func (r *ServiceSysRole) CheckUniqueRoleName(roleName, roleId string) bool { - uniqueId := r.sysRoleRepository.CheckUniqueRole(model.SysRole{ - RoleName: roleName, - }) - if uniqueId == roleId { - return true - } - return uniqueId == "" -} - -// CheckUniqueRoleKey 校验角色权限是否唯一 -func (r *ServiceSysRole) CheckUniqueRoleKey(roleKey, roleId string) bool { - uniqueId := r.sysRoleRepository.CheckUniqueRole(model.SysRole{ - RoleKey: roleKey, - }) - if uniqueId == roleId { - return true - } - return uniqueId == "" -} - -// DeleteAuthUsers 批量取消授权用户角色 -func (r *ServiceSysRole) DeleteAuthUsers(roleId string, userIds []string) int64 { - return r.sysUserRoleRepository.DeleteUserRoleByRoleId(roleId, userIds) -} - -// InsertAuthUsers 批量新增授权用户角色 -func (r *ServiceSysRole) InsertAuthUsers(roleId string, userIds []string) int64 { - if roleId == "" || len(userIds) <= 0 { - return 0 - } - - sysUserRoles := []sysuserrole.SysUserRole{} - for _, userId := range userIds { - if userId == "" { - continue - } - sysUserRoles = append(sysUserRoles, sysuserrole.NewSysUserRole(userId, roleId)) - } - - return r.sysUserRoleRepository.BatchUserRole(sysUserRoles) -} diff --git a/features/sys_role_menu/model_sys_role_menu.go b/features/sys_role_menu/model_sys_role_menu.go deleted file mode 100644 index 755e66d..0000000 --- a/features/sys_role_menu/model_sys_role_menu.go +++ /dev/null @@ -1,15 +0,0 @@ -package sysrolemenu - -// SysRoleMenu 角色和菜单关联对象 sys_role_menu -type SysRoleMenu struct { - RoleID string `json:"roleId"` // 角色ID - MenuID string `json:"menuId"` // 菜单ID -} - -// NewSysRoleMenu 创建角色和菜单关联对象的构造函数 -func NewSysRoleMenu(roleID string, menuID string) SysRoleMenu { - return SysRoleMenu{ - RoleID: roleID, - MenuID: menuID, - } -} diff --git a/features/sys_role_menu/repo_sys_role_menu.go b/features/sys_role_menu/repo_sys_role_menu.go deleted file mode 100644 index fb11cf5..0000000 --- a/features/sys_role_menu/repo_sys_role_menu.go +++ /dev/null @@ -1,86 +0,0 @@ -package sysrolemenu - -import ( - "fmt" - "strings" - - "be.ems/lib/core/datasource" - "be.ems/lib/core/utils/parse" - "be.ems/lib/log" -) - -// 实例化数据层 RepoSysRoleMenu 结构体 -var NewRepoSysRoleMenu = &RepoSysRoleMenu{} - -// RepoSysRoleMenu 角色与菜单关联表 数据层处理 -type RepoSysRoleMenu struct{} - -// CheckMenuExistRole 查询菜单分配给角色使用数量 -func (r *RepoSysRoleMenu) CheckMenuExistRole(menuId string) int64 { - querySql := "select count(1) as 'total' from sys_role_menu where menu_id = ?" - results, err := datasource.RawDB("", querySql, []any{menuId}) - if err != nil { - log.Errorf("query err => %v", err) - return 0 - } - if len(results) > 0 { - return parse.Number(results[0]["total"]) - } - return 0 -} - -// DeleteRoleMenu 批量删除角色和菜单关联 -func (r *RepoSysRoleMenu) DeleteRoleMenu(roleIds []string) int64 { - placeholder := datasource.KeyPlaceholderByQuery(len(roleIds)) - sql := "delete from sys_role_menu where role_id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(roleIds) - results, err := datasource.ExecDB("", sql, parameters) - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - return affected -} - -// DeleteMenuRole 批量删除菜单和角色关联 -func (r *RepoSysRoleMenu) DeleteMenuRole(menuIds []string) int64 { - placeholder := datasource.KeyPlaceholderByQuery(len(menuIds)) - sql := "delete from sys_role_menu where menu_id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(menuIds) - results, err := datasource.ExecDB("", sql, parameters) - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - return affected -} - -// BatchRoleMenu 批量新增角色菜单信息 -func (r *RepoSysRoleMenu) BatchRoleMenu(sysRoleMenus []SysRoleMenu) int64 { - keyValues := make([]string, 0) - for _, item := range sysRoleMenus { - keyValues = append(keyValues, fmt.Sprintf("(%s,%s)", item.RoleID, item.MenuID)) - } - sql := "insert into sys_role_menu(role_id, menu_id) values " + strings.Join(keyValues, ",") - results, err := datasource.ExecDB("", sql, nil) - if err != nil { - log.Errorf("insert err => %v", err) - return 0 - } - insertId, err := results.LastInsertId() - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return 0 - } - return insertId -} diff --git a/features/sys_user/api_sys_user.go b/features/sys_user/api_sys_user.go deleted file mode 100644 index 69b748c..0000000 --- a/features/sys_user/api_sys_user.go +++ /dev/null @@ -1,323 +0,0 @@ -package sysuser - -import ( - "fmt" - "net/http" - "strings" - - sysRoleModel "be.ems/features/sys_role/model" - sysRoleService "be.ems/features/sys_role/service" - sysUserModel "be.ems/features/sys_user/model" - "be.ems/features/sys_user/service" - "be.ems/lib/core/utils/ctx" - "be.ems/lib/core/utils/parse" - "be.ems/lib/core/vo/result" - "be.ems/lib/midware" - "be.ems/lib/services" - "be.ems/restagent/config" - srcConfig "be.ems/src/framework/config" -) - -// 用户接口添加到路由 -func Routers() []services.RouterItem { - // 实例化控制层 SysUserApi 结构体 - var apis = &SysUserApi{ - sysUserService: service.NewServiceSysUser, - sysRoleService: sysRoleService.NewServiceSysRole, - } - - rs := [...]services.RouterItem{ - { - Method: "GET", - Pattern: "/users", - Handler: apis.List, - Middleware: midware.Authorize(nil), - }, - { - Method: "GET", - Pattern: "/user/{userId}", - Handler: apis.Info, - Middleware: midware.Authorize(nil), - }, - { - Method: "POST", - Pattern: "/user", - Handler: apis.Add, - Middleware: midware.Authorize(nil), - }, - { - Method: "PUT", - Pattern: "/user", - Handler: apis.Edit, - Middleware: midware.Authorize(nil), - }, - { - Method: "DELETE", - Pattern: "/user/{userIds}", - Handler: apis.Remove, - Middleware: midware.Authorize(nil), - }, - // 添加更多的 Router 对象... - } - - // 生成两组前缀路由 - rsPrefix := []services.RouterItem{} - for _, v := range rs { - path := "/userManage/{apiVersion}" + v.Pattern - // 固定前缀 - v.Pattern = config.DefaultUriPrefix + path - rsPrefix = append(rsPrefix, v) - // 可配置 - v.Pattern = config.UriPrefix + path - rsPrefix = append(rsPrefix, v) - } - return rsPrefix -} - -// // 实例化控制层 SysUserApi 结构体 -// var NewSysUser = &SysUserApi{ -// sysUserService: service.NewSysUserImpl, -// sysRoleService: service.NewSysRoleImpl, -// sysPostService: service.NewSysPostImpl, -// sysDictDataService: service.NewSysDictDataImpl, -// } - -// 用户信息 -// -// PATH /system/user -type SysUserApi struct { - // 用户服务 - sysUserService *service.ServiceSysUser - // 角色服务 - sysRoleService *sysRoleService.ServiceSysRole -} - -// 用户信息列表 -// -// GET /list -func (s *SysUserApi) List(w http.ResponseWriter, r *http.Request) { - querys := ctx.QueryMap(r) - data := s.sysUserService.SelectUserPage(querys) - ctx.JSON(w, 200, result.Ok(data)) -} - -// 用户信息详情 -// -// GET /:userId -func (s *SysUserApi) Info(w http.ResponseWriter, r *http.Request) { - userId := ctx.Param(r, "userId") - if userId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - // 查询系统角色列表 - roles := s.sysRoleService.SelectRoleList(sysRoleModel.SysRole{}) - - // 不是系统指定管理员需要排除其角色 - if !srcConfig.IsAdmin(userId) { - rolesFilter := make([]sysRoleModel.SysRole, 0) - for _, r := range roles { - if r.RoleID != "1" { - rolesFilter = append(rolesFilter, r) - } - } - roles = rolesFilter - } - - // 新增用户时,用户ID为0 - if userId == "0" { - ctx.JSON(w, 200, result.OkData(map[string]any{ - "user": map[string]any{}, - "roleIds": []string{}, - "roles": roles, - })) - return - } - - // 检查用户是否存在 - user := s.sysUserService.SelectUserById(userId) - if user.Id != userId { - ctx.JSON(w, 200, result.ErrMsg("No permission to access user data!")) - return - } - - // 角色ID组 - roleIds := make([]string, 0) - for _, r := range user.Roles { - roleIds = append(roleIds, r.RoleID) - } - - ctx.JSON(w, 200, result.OkData(map[string]any{ - "user": user, - "roleIds": roleIds, - "roles": roles, - })) -} - -// 用户信息新增 -// -// POST / -func (s *SysUserApi) Add(w http.ResponseWriter, r *http.Request) { - var body sysUserModel.SysUser - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.Id != "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查用户登录账号是否唯一 - uniqueUserName := s.sysUserService.CheckUniqueUserName(body.AccountId, "") - if !uniqueUserName { - msg := fmt.Sprintf("[%s] Login account already exists", body.AccountId) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - insertId := s.sysUserService.InsertUser(body) - if insertId != "" { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 用户信息修改 -// -// POST / -func (s *SysUserApi) Edit(w http.ResponseWriter, r *http.Request) { - var body sysUserModel.SysUser - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.Id == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查是否管理员用户 - // if srcConfig.IsAdmin(body.Id) { - // ctx.JSON(w, 200, result.ErrMsg("不允许操作管理员用户")) - // return - // } - - user := s.sysUserService.SelectUserById(body.Id) - if user.Id != body.Id { - ctx.JSON(w, 200, result.ErrMsg("No permission to access user data!")) - return - } - - // 检查用户登录账号是否唯一 - uniqueUserName := s.sysUserService.CheckUniqueUserName(body.AccountId, body.Id) - if !uniqueUserName { - msg := fmt.Sprintf("[%s] Login account already exists", body.AccountId) - ctx.JSON(w, 200, result.ErrMsg(msg)) - return - } - - body.AccountId = "" // 忽略修改登录用户名称 - // body.Password = "" // 忽略修改密码 - rows := s.sysUserService.UpdateUserAndRolePost(body) - if rows > 0 { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 用户信息删除 -// -// DELETE /:userIds -func (s *SysUserApi) Remove(w http.ResponseWriter, r *http.Request) { - userIds := ctx.Param(r, "userIds") - if userIds == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - // 处理字符转id数组后去重 - ids := strings.Split(userIds, ",") - uniqueIDs := parse.RemoveDuplicates(ids) - if len(uniqueIDs) <= 0 { - ctx.JSON(w, 200, result.Err(nil)) - return - } - rows, err := s.sysUserService.DeleteUserByIds(uniqueIDs) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - msg := fmt.Sprintf("Successfully deleted: %d", rows) - ctx.JSON(w, 200, result.OkMsg(msg)) -} - -// 用户重置密码 -// -// PUT /resetPwd -func (s *SysUserApi) ResetPwd(w http.ResponseWriter, r *http.Request) { - var body struct { - UserID string `json:"userId" binding:"required"` - Password string `json:"password" binding:"required"` - } - if err := ctx.ShouldBindJSON(r, &body); err != nil { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查是否管理员用户 - if srcConfig.IsAdmin(body.UserID) { - ctx.JSON(w, 200, result.ErrMsg("No permission to access user data!")) - return - } - - user := s.sysUserService.SelectUserById(body.UserID) - if user.Id != body.UserID { - ctx.JSON(w, 200, result.ErrMsg("No permission to access user data!")) - return - } - - SysUserApi := sysUserModel.SysUser{ - Id: body.UserID, - Password: body.Password, - } - rows := s.sysUserService.UpdateUser(SysUserApi) - if rows > 0 { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} - -// 用户状态修改 -// -// PUT /changeStatus -func (s *SysUserApi) Status(w http.ResponseWriter, r *http.Request) { - var body struct { - UserID string `json:"userId" binding:"required"` - Status string `json:"status" binding:"required"` - } - if err := ctx.ShouldBindJSON(r, &body); err != nil { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 检查是否存在 - user := s.sysUserService.SelectUserById(body.UserID) - if user.Id != body.UserID { - ctx.JSON(w, 200, result.ErrMsg("No permission to access user data!")) - return - } - - // 与旧值相等不变更 - if user.Status == body.Status { - ctx.JSON(w, 200, result.ErrMsg("Change status equals old value!")) - return - } - - SysUserApi := sysUserModel.SysUser{ - Id: body.UserID, - Status: body.Status, - } - rows := s.sysUserService.UpdateUser(SysUserApi) - if rows > 0 { - ctx.JSON(w, 200, result.Ok(nil)) - return - } - ctx.JSON(w, 200, result.Err(nil)) -} diff --git a/features/sys_user/model/sys_user.go b/features/sys_user/model/sys_user.go deleted file mode 100644 index 1f5f108..0000000 --- a/features/sys_user/model/sys_user.go +++ /dev/null @@ -1,42 +0,0 @@ -package model - -import "be.ems/features/sys_role/model" - -type SysUser struct { - Id string `json:"id" xorm:"pk 'id' autoincr"` - AccountId string `json:"accountId" xorm:"account_id"` - Name string `json:"name" xorm:"name"` - Sn string `json:"sn"` - Gender string `json:"gender"` - Description string `json:"description"` - TelephoneNumber string `json:"telephoneNumber" xorm:"telephone_number"` - Mobile string `json:"mobile"` - Email string `json:"email" xorm:"email"` - StartTime string `json:"startTime" xorm:"start_time"` - EndTime string `json:"endTime" xorm:"end_time"` - IdCardNumber string `json:"idCardNumber"` - EmployeeNumber string `json:"employeeNumber"` - Organize string `json:"organize"` - EmployeeType string `json:"employeeType"` - SupporterCorpName string `json:"supporterCorpName"` - RealName string `json:"realName" xorm:"real_name"` - Password string `json:"password" xorm:"-"` - PasswordSha512 string `json:"-" xorm:"-"` - ChangePasswordFlag int `json:"changePasswordFlag"` - PasswordExpiration string `json:"passwordExpiration" xorm:"password_expiration"` - Status string `json:"status"` - UserExpiration string `json:"userExpiration" xorm:"user_expiration"` - GroupName string `json:"groupName" xorm:"group_name"` - Profile string `json:"-" xorm:"profile"` - Phone string `json:"phone" xorm:"phone"` - CreateTime string `json:"createTime" xorm:"create_time"` - UpdateTime string `json:"updateTime" xorm:"update_time"` - Unit string `json:"unit" xorm:"unit"` - - // 角色对象组 - Roles []model.SysRole `json:"roles"` - // 角色ID - 参数提交绑定 - RoleID string `json:"roleId,omitempty"` - // 角色组 - 参数提交绑定 - RoleIDs []string `json:"roleIds,omitempty"` -} diff --git a/features/sys_user/service/repo_sys_user.go b/features/sys_user/service/repo_sys_user.go deleted file mode 100644 index de55949..0000000 --- a/features/sys_user/service/repo_sys_user.go +++ /dev/null @@ -1,578 +0,0 @@ -package service - -import ( - "fmt" - "strings" - "time" - - sysRoleModel "be.ems/features/sys_role/model" - sysUserModel "be.ems/features/sys_user/model" - "be.ems/lib/core/datasource" - "be.ems/lib/core/utils/date" - "be.ems/lib/core/utils/parse" - "be.ems/lib/log" - "be.ems/src/framework/utils/crypto" -) - -// 实例化数据层 RepoSysUser 结构体 -var NewRepoSysUser = &RepoSysUser{ - selectSql: `select u.id, - u.account_id, u.name, u.sn, u.gender, u.description, u.telephone_number, u.mobile, u.email, - u.start_time, u.end_time, u.id_card_number, u.employee_number, - u.organize, u.employee_type, u.supporter_corp_name, u.real_name, u.password, u.password_sha512, - u.change_password_flag,u.password_expiration, u.status, u.user_expiration, u.group_name, - u.profile, u.phone, u.create_time, u.update_time, u.unit, - r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status - from user u - left join sys_user_role ur on u.id = ur.user_id - left join sys_role r on r.role_id = ur.role_id`, - - sysUserMap: map[string]string{ - "id": "Id", - "account_id": "AccountId", - "name": "Name", - "sn": "Sn", - "gender": "Gender", - "description": "Description", - "telephone_number": "TelephoneNumber", - "mobile": "Mobile", - "email": "Email", - "start_time": "StartTime", - "end_time": "EndTime", - "id_card_number": "IdCardNumber", - "employee_number": "EmployeeNumber", - "organize": "Organize", - "employee_type": "EmployeeType", - "supporter_corp_name": "SupporterCorpName", - "real_name": "RealName", - "password": "Password", - "password_sha512": "PasswordSha512", - "change_password_flag": "ChangePasswordFlag", - "password_expiration": "PasswordExpiration", - "status": "Status", - "user_expiration": "UserExpiration", - "group_name": "GroupName", - "profile": "Profile", - "phone": "Phone", - "create_time": "CreateTime", - "update_time": "UpdateTime", - "unit": "Unit", - }, - - sysRoleMap: map[string]string{ - "role_id": "RoleID", - "role_name": "RoleName", - "role_key": "RoleKey", - "role_sort": "RoleSort", - "data_scope": "DataScope", - "role_status": "Status", - }, -} - -// RepoSysUser 用户表 数据层处理 -type RepoSysUser struct { - // 查询视图对象SQL - selectSql string - // 用户信息实体映射 - sysUserMap map[string]string - // 用户角色实体映射 一对多 - sysRoleMap map[string]string -} - -// convertResultRows 将结果记录转实体结果组 -func (r *RepoSysUser) convertResultRows(rows []map[string]any) []sysUserModel.SysUser { - arr := make([]sysUserModel.SysUser, 0) - - for _, row := range rows { - sysUser := sysUserModel.SysUser{} - sysRole := sysRoleModel.SysRole{} - sysUser.Roles = []sysRoleModel.SysRole{} - - for key, value := range row { - if keyMapper, ok := r.sysUserMap[key]; ok { - datasource.SetFieldValue(&sysUser, keyMapper, value) - } - if keyMapper, ok := r.sysRoleMap[key]; ok { - datasource.SetFieldValue(&sysRole, keyMapper, value) - } - } - - if sysRole.RoleKey != "" { - sysUser.Roles = append(sysUser.Roles, sysRole) - } - - one := true - for i, a := range arr { - if a.Id == sysUser.Id { - arrUser := &arr[i] - arrUser.Roles = append(arrUser.Roles, sysUser.Roles...) - one = false - break - } - } - if one { - arr = append(arr, sysUser) - } - } - - return arr -} - -// SelectUserPage 根据条件分页查询用户列表 -func (r *RepoSysUser) SelectUserPage(query map[string]any) map[string]any { - selectUserSql := `select u.id, - u.account_id, u.name, u.sn, u.gender, u.description, u.telephone_number, u.mobile, u.email, - u.start_time, u.end_time, u.id_card_number, u.employee_number, - u.organize, u.employee_type, u.supporter_corp_name, u.real_name, - u.change_password_flag,u.password_expiration, u.status, u.user_expiration, u.group_name, - u.profile, u.phone, u.create_time, u.update_time, u.unit - from user u` - selectUserTotalSql := `select count(distinct u.id) as 'total' from user u` - - // 查询条件拼接 - var conditions []string - var params []any - if v, ok := query["accountId"]; ok && v != "" { - conditions = append(conditions, "u.account_id = ?") - params = append(params, v) - } - if v, ok := query["name"]; ok && v != "" { - conditions = append(conditions, "u.name concat('%', concat(?, '%'))") - params = append(params, v) - } - if v, ok := query["status"]; ok && v != "" { - conditions = append(conditions, "u.status = ?") - params = append(params, v) - } - if v, ok := query["phonenumber"]; ok && v != "" { - conditions = append(conditions, "u.phonenumber like concat('%', concat(?, '%'))") - params = append(params, v) - } - beginTime, ok := query["beginTime"] - if !ok { - beginTime, ok = query["params[beginTime]"] - } - if ok && beginTime != "" { - conditions = append(conditions, "u.login_date >= ?") - beginDate := date.ParseStrToDate(beginTime.(string), date.YYYY_MM_DD) - params = append(params, beginDate.UnixMilli()) - } - endTime, ok := query["endTime"] - if !ok { - endTime, ok = query["params[endTime]"] - } - if ok && endTime != "" { - conditions = append(conditions, "u.login_date <= ?") - endDate := date.ParseStrToDate(endTime.(string), date.YYYY_MM_DD) - params = append(params, endDate.UnixMilli()) - } - if v, ok := query["deptId"]; ok && v != "" { - conditions = append(conditions, "(u.dept_id = ? or u.dept_id in ( select t.dept_id from sys_dept t where find_in_set(?, ancestors) ))") - params = append(params, v) - params = append(params, v) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数量 长度为0直接返回 - totalSql := selectUserTotalSql + whereSql - totalRows, err := datasource.RawDB("", totalSql, params) - if err != nil { - log.Errorf("total err => %v", err) - } - total := parse.Number(totalRows[0]["total"]) - if total == 0 { - return map[string]any{ - "total": total, - "rows": []sysUserModel.SysUser{}, - } - } - - // 分页 - pageNum, pageSize := datasource.PageNumSize(query["pageNum"], query["pageSize"]) - pageSql := " limit ?,? " - params = append(params, pageNum*pageSize) - params = append(params, pageSize) - - // 查询数据 - querySql := selectUserSql + whereSql + pageSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - } - - // 转换实体 - rows := r.convertResultRows(results) - return map[string]any{ - "total": total, - "rows": rows, - } -} - -// SelectAllocatedPage 根据条件分页查询分配用户角色列表 -func (r *RepoSysUser) SelectAllocatedPage(query map[string]any) map[string]any { - // 查询条件拼接 - var conditions []string - var params []any - if v, ok := query["name"]; ok && v != "" { - conditions = append(conditions, "u.name like concat('%', concat(?, '%'))") - params = append(params, v) - } - if v, ok := query["phone"]; ok && v != "" { - conditions = append(conditions, "u.phone like concat('%', concat(?, '%'))") - params = append(params, v) - } - if v, ok := query["status"]; ok && v != "" { - conditions = append(conditions, "u.status = ?") - params = append(params, v) - } - // 分配角色用户 - if allocated, ok := query["allocated"]; ok && allocated != "" { - if roleId, ok := query["roleId"]; ok && roleId != "" { - if parse.Boolean(allocated) { - conditions = append(conditions, "r.role_id = ?") - params = append(params, roleId) - } else { - conditions = append(conditions, `(r.role_id != ? or r.role_id IS NULL) - and u.id not in ( - select u.id from user u - inner join sys_user_role ur on u.id = ur.user_id - and ur.role_id = ? - )`) - params = append(params, roleId) - params = append(params, roleId) - } - - } - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数量 长度为0直接返回 - totalSql := `select count(distinct u.id) as 'total' from user u - left join sys_user_role ur on u.id = ur.user_id - left join sys_role r on r.role_id = ur.role_id` - totalRows, err := datasource.RawDB("", totalSql+whereSql, params) - if err != nil { - log.Errorf("total err => %v", err) - } - total := parse.Number(totalRows[0]["total"]) - if total == 0 { - return map[string]any{ - "total": total, - "rows": []sysUserModel.SysUser{}, - } - } - - // 分页 - pageNum, pageSize := datasource.PageNumSize(query["pageNum"], query["pageSize"]) - pageSql := " limit ?,? " - params = append(params, pageNum*pageSize) - params = append(params, pageSize) - - // 查询数据 - querySql := `select distinct - u.id, u.account_id, u.name, u.gender, u.email, - u.phone, u.status, u.create_time, u.real_name - from user u - left join sys_user_role ur on u.id = ur.user_id - left join sys_role r on r.role_id = ur.role_id` - querySql = querySql + whereSql + pageSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - } - - // 转换实体 - rows := r.convertResultRows(results) - return map[string]any{ - "total": total, - "rows": rows, - } -} - -// SelectUserList 根据条件查询用户列表 -func (r *RepoSysUser) SelectUserList(sysUser sysUserModel.SysUser, dataScopeSQL string) []sysUserModel.SysUser { - selectUserSql := `select - u.id, u.account_id, u.name, u.real_name, u.email, u.gender, u.phone, u.create_time, u.status, u.description - from user u` - - // 查询条件拼接 - var conditions []string - var params []any - if sysUser.AccountId != "" { - conditions = append(conditions, "u.account_id = ?") - params = append(params, sysUser.AccountId) - } - if sysUser.Name != "" { - conditions = append(conditions, "u.name like concat('%', concat(?, '%'))") - params = append(params, sysUser.Name) - } - if sysUser.Status != "" { - conditions = append(conditions, "u.status = ?") - params = append(params, sysUser.Status) - } - if sysUser.Phone != "" { - conditions = append(conditions, "u.phone like concat('%', concat(?, '%'))") - params = append(params, sysUser.Phone) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数据 - querySql := selectUserSql + whereSql + dataScopeSQL - rows, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - return []sysUserModel.SysUser{} - } - return r.convertResultRows(rows) -} - -// SelectUserByIds 通过用户ID查询用户 -func (r *RepoSysUser) SelectUserByIds(userIds []string) []sysUserModel.SysUser { - placeholder := datasource.KeyPlaceholderByQuery(len(userIds)) - querySql := r.selectSql + " where u.id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(userIds) - results, err := datasource.RawDB("", querySql, parameters) - if err != nil { - log.Errorf("query err => %v", err) - return []sysUserModel.SysUser{} - } - // 转换实体 - return r.convertResultRows(results) -} - -// SelectUserByUserName 通过用户登录账号查询用户 -func (r *RepoSysUser) SelectUserByUserName(userName string) sysUserModel.SysUser { - querySql := r.selectSql + " where u.name = ?" - results, err := datasource.RawDB("", querySql, []any{userName}) - if err != nil { - log.Errorf("query err => %v", err) - return sysUserModel.SysUser{} - } - // 转换实体 - rows := r.convertResultRows(results) - if len(rows) > 0 { - return rows[0] - } - return sysUserModel.SysUser{} -} - -// InsertUser 新增用户信息 -func (r *RepoSysUser) InsertUser(sysUser sysUserModel.SysUser) string { - // 参数拼接 - params := make(map[string]any) - if sysUser.AccountId != "" { - params["account_id"] = sysUser.AccountId - } - if sysUser.Name != "" { - params["name"] = sysUser.Name - } - if sysUser.Sn != "" { - params["sn"] = sysUser.Sn - } else { - params["sn"] = "" - } - if sysUser.RealName != "" { - params["real_name"] = sysUser.RealName - } else { - params["real_name"] = "" - } - if sysUser.Gender != "" { - params["gender"] = sysUser.Gender - } - if sysUser.Email != "" { - params["email"] = sysUser.Email - } else { - params["email"] = "" - } - if sysUser.Phone != "" { - params["phone"] = sysUser.Phone - } else { - params["phone"] = "" - } - if sysUser.Unit != "" { - params["unit"] = sysUser.Unit - } else { - params["unit"] = "" - } - - if sysUser.Organize != "" { - params["organize"] = sysUser.Organize - } - if sysUser.Password != "" { - password := crypto.BcryptHash(sysUser.Password) - params["password"] = password - } - if sysUser.Status != "" { - params["status"] = sysUser.Status - } - if sysUser.PasswordExpiration != "" { - params["password_expiration"] = sysUser.PasswordExpiration - } - if sysUser.UserExpiration != "" { - params["user_expiration"] = sysUser.UserExpiration - } - if sysUser.GroupName != "" { - params["group_name"] = sysUser.GroupName - } - params["create_time"] = time.Now() - - // 构建执行语句 - keys, placeholder, values := datasource.KeyPlaceholderValueByInsert(params) - sql := "insert into user (" + strings.Join(keys, ",") + ")values(" + placeholder + ")" - - // 执行插入 - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - insertId, err := results.LastInsertId() - if err != nil { - log.Errorf("insert row : %v", err.Error()) - return "" - } - return fmt.Sprint(insertId) -} - -// UpdateUser 修改用户信息 -func (r *RepoSysUser) UpdateUser(sysUser sysUserModel.SysUser) int64 { - // 参数拼接 - params := make(map[string]any) - if sysUser.Name != "" { - params["name"] = sysUser.Name - } - if sysUser.Sn != "" { - params["sn"] = sysUser.Sn - } else { - params["sn"] = "" - } - if sysUser.RealName != "" { - params["real_name"] = sysUser.RealName - } else { - params["real_name"] = "" - } - if sysUser.Gender != "" { - params["gender"] = sysUser.Gender - } - if sysUser.Email != "" { - params["email"] = sysUser.Email - } else { - params["email"] = "" - } - if sysUser.Phone != "" { - params["phone"] = sysUser.Phone - } else { - params["phone"] = "" - } - if sysUser.Unit != "" { - params["unit"] = sysUser.Unit - } else { - params["unit"] = "" - } - - if sysUser.Organize != "" { - params["organize"] = sysUser.Organize - } - if sysUser.Password != "" { - password := crypto.BcryptHash(sysUser.Password) - params["password"] = password - } - if sysUser.Status != "" { - params["status"] = sysUser.Status - } - if sysUser.PasswordExpiration != "" { - params["password_expiration"] = sysUser.PasswordExpiration - } - if sysUser.UserExpiration != "" { - params["user_expiration"] = sysUser.UserExpiration - } - if sysUser.GroupName != "" { - params["group_name"] = sysUser.GroupName - } - params["update_time"] = time.Now() - - // 构建执行语句 - keys, values := datasource.KeyValueByUpdate(params) - sql := "update user set " + strings.Join(keys, ",") + " where id = ?" - - // 执行更新 - values = append(values, sysUser.Id) - results, err := datasource.ExecDB("", sql, values) - if err != nil { - log.Errorf("update row : %v", err.Error()) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("update err => %v", err) - return 0 - } - return affected -} - -// DeleteUserByIds 批量删除用户信息 -func (r *RepoSysUser) DeleteUserByIds(userIds []string) int64 { - placeholder := datasource.KeyPlaceholderByQuery(len(userIds)) - sql := "delete from user where id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(userIds) - results, err := datasource.ExecDB("", sql, parameters) - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - return affected -} - -// CheckUniqueUser 校验用户信息是否唯一 -func (r *RepoSysUser) CheckUniqueUser(sysUser sysUserModel.SysUser) string { - // 查询条件拼接 - var conditions []string - var params []any - if sysUser.Name != "" { - conditions = append(conditions, "name = ?") - params = append(params, sysUser.Name) - } - if sysUser.AccountId != "" { - conditions = append(conditions, "account_id = ?") - params = append(params, sysUser.AccountId) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } else { - return "" - } - - // 查询数据 - querySql := "select id as 'str' from user " + whereSql + " limit 1" - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err %v", err) - } - if len(results) > 0 { - return fmt.Sprint(results[0]["str"]) - } - return "" -} diff --git a/features/sys_user/service/service_sys_user.go b/features/sys_user/service/service_sys_user.go deleted file mode 100644 index e5021fc..0000000 --- a/features/sys_user/service/service_sys_user.go +++ /dev/null @@ -1,150 +0,0 @@ -package service - -import ( - "errors" - "fmt" - - sysUserModel "be.ems/features/sys_user/model" - sysuserrole "be.ems/features/sys_user_role" -) - -// 实例化服务层 ServiceSysUser 结构体 -var NewServiceSysUser = &ServiceSysUser{ - sysUserRepository: NewRepoSysUser, - sysUserRoleRepository: sysuserrole.NewRepoSysUserRole, -} - -// ServiceSysUser 用户 服务层处理 -type ServiceSysUser struct { - // 用户服务 - sysUserRepository *RepoSysUser - // 用户与角色服务 - sysUserRoleRepository *sysuserrole.RepoSysUserRole -} - -// SelectUserPage 根据条件分页查询用户列表 -func (r *ServiceSysUser) SelectUserPage(query map[string]any) map[string]any { - return r.sysUserRepository.SelectUserPage(query) -} - -// SelectUserList 根据条件查询用户列表 -func (r *ServiceSysUser) SelectUserList(sysUser sysUserModel.SysUser) []sysUserModel.SysUser { - return []sysUserModel.SysUser{} -} - -// SelectAllocatedPage 根据条件分页查询分配用户角色列表 -func (r *ServiceSysUser) SelectAllocatedPage(query map[string]any) map[string]any { - return r.sysUserRepository.SelectAllocatedPage(query) -} - -// SelectUserByUserName 通过用户名查询用户 -func (r *ServiceSysUser) SelectUserByUserName(userName string) sysUserModel.SysUser { - return r.sysUserRepository.SelectUserByUserName(userName) -} - -// SelectUserById 通过用户ID查询用户 -func (r *ServiceSysUser) SelectUserById(userId string) sysUserModel.SysUser { - if userId == "" { - return sysUserModel.SysUser{} - } - users := r.sysUserRepository.SelectUserByIds([]string{userId}) - if len(users) > 0 { - return users[0] - } - return sysUserModel.SysUser{} -} - -// InsertUser 新增用户信息 -func (r *ServiceSysUser) InsertUser(sysUser sysUserModel.SysUser) string { - // 新增用户信息 - insertId := r.sysUserRepository.InsertUser(sysUser) - if insertId != "" { - // 新增用户角色信息 - r.insertUserRole(insertId, sysUser.RoleIDs) - } - return insertId -} - -// insertUserRole 新增用户角色信息 -func (r *ServiceSysUser) insertUserRole(userId string, roleIds []string) int64 { - if userId == "" || len(roleIds) <= 0 { - return 0 - } - - sysUserRoles := []sysuserrole.SysUserRole{} - for _, roleId := range roleIds { - // 管理员角色禁止操作,只能通过配置指定用户ID分配 - if roleId == "" || roleId == "1" { - continue - } - sysUserRoles = append(sysUserRoles, sysuserrole.NewSysUserRole(userId, roleId)) - } - - return r.sysUserRoleRepository.BatchUserRole(sysUserRoles) -} - -// UpdateUser 修改用户信息 -func (r *ServiceSysUser) UpdateUser(sysUser sysUserModel.SysUser) int64 { - return r.sysUserRepository.UpdateUser(sysUser) -} - -// UpdateUserAndRolePost 修改用户信息同时更新角色和岗位 -func (r *ServiceSysUser) UpdateUserAndRolePost(sysUser sysUserModel.SysUser) int64 { - userId := fmt.Sprint(sysUser.Id) - // 删除用户与角色关联 - r.sysUserRoleRepository.DeleteUserRole([]string{userId}) - // 新增用户角色信息 - r.insertUserRole(userId, sysUser.RoleIDs) - return r.sysUserRepository.UpdateUser(sysUser) -} - -// DeleteUserByIds 批量删除用户信息 -func (r *ServiceSysUser) DeleteUserByIds(userIds []string) (int64, error) { - // 检查是否存在 - users := r.sysUserRepository.SelectUserByIds(userIds) - if len(users) <= 0 { - return 0, errors.New("no permission to access user data") - } - if len(users) == len(userIds) { - // 删除用户与角色关联 - r.sysUserRoleRepository.DeleteUserRole(userIds) - // ... 注意其他userId进行关联的表 - // 删除用户 - rows := r.sysUserRepository.DeleteUserByIds(userIds) - return rows, nil - } - return 0, errors.New("failed to delete user information") -} - -// CheckUniqueUserName 校验用户名称是否唯一 -func (r *ServiceSysUser) CheckUniqueUserName(accountId, userId string) bool { - uniqueId := r.sysUserRepository.CheckUniqueUser(sysUserModel.SysUser{ - AccountId: accountId, - }) - if uniqueId == userId { - return true - } - return uniqueId == "" -} - -// CheckUniquePhone 校验手机号码是否唯一 -func (r *ServiceSysUser) CheckUniquePhone(phonenumber, userId string) bool { - uniqueId := r.sysUserRepository.CheckUniqueUser(sysUserModel.SysUser{ - Phone: phonenumber, - }) - if uniqueId == userId { - return true - } - return uniqueId == "" -} - -// CheckUniqueEmail 校验email是否唯一 -func (r *ServiceSysUser) CheckUniqueEmail(email, userId string) bool { - uniqueId := r.sysUserRepository.CheckUniqueUser(sysUserModel.SysUser{ - Email: email, - }) - if uniqueId == userId { - return true - } - return uniqueId == "" -} diff --git a/features/sys_user_role/model_sys_user_role.go b/features/sys_user_role/model_sys_user_role.go deleted file mode 100644 index bc493eb..0000000 --- a/features/sys_user_role/model_sys_user_role.go +++ /dev/null @@ -1,15 +0,0 @@ -package sysuserrole - -// SysUserRole 用户和角色关联对象 sys_user_role -type SysUserRole struct { - UserID string `json:"userId"` // 用户ID - RoleID string `json:"roleId"` // 角色ID -} - -// NewSysUserRole 创建用户和角色关联对象的构造函数 -func NewSysUserRole(userID string, roleID string) SysUserRole { - return SysUserRole{ - UserID: userID, - RoleID: roleID, - } -} diff --git a/features/sys_user_role/repo_sys_user_role.go b/features/sys_user_role/repo_sys_user_role.go deleted file mode 100644 index 66a6cec..0000000 --- a/features/sys_user_role/repo_sys_user_role.go +++ /dev/null @@ -1,87 +0,0 @@ -package sysuserrole - -import ( - "fmt" - "strings" - - "be.ems/lib/core/datasource" - "be.ems/lib/core/utils/parse" - "be.ems/lib/log" -) - -// 实例化数据层 RepoSysUserRole 结构体 -var NewRepoSysUserRole = &RepoSysUserRole{} - -// RepoSysUserRole 用户与角色关联表 数据层处理 -type RepoSysUserRole struct{} - -// CountUserRoleByRoleId 通过角色ID查询角色使用数量 -func (r *RepoSysUserRole) CountUserRoleByRoleId(roleId string) int64 { - querySql := "select count(1) as total from sys_user_role where role_id = ?" - results, err := datasource.RawDB("", querySql, []any{roleId}) - if err != nil { - log.Errorf("query err => %v", err) - return 0 - } - if len(results) > 0 { - return parse.Number(results[0]["total"]) - } - return 0 -} - -// BatchUserRole 批量新增用户角色信息 -func (r *RepoSysUserRole) BatchUserRole(sysUserRoles []SysUserRole) int64 { - keyValues := make([]string, 0) - for _, item := range sysUserRoles { - keyValues = append(keyValues, fmt.Sprintf("(%s,%s)", item.UserID, item.RoleID)) - } - sql := "insert into sys_user_role(user_id, role_id) values " + strings.Join(keyValues, ",") - results, err := datasource.ExecDB("", sql, nil) - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - return affected -} - -// DeleteUserRole 批量删除用户和角色关联 -func (r *RepoSysUserRole) DeleteUserRole(userIds []string) int64 { - placeholder := datasource.KeyPlaceholderByQuery(len(userIds)) - sql := "delete from sys_user_role where user_id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(userIds) - results, err := datasource.ExecDB("", sql, parameters) - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - return affected -} - -// DeleteUserRoleByRoleId 批量取消授权用户角色 -func (r *RepoSysUserRole) DeleteUserRoleByRoleId(roleId string, userIds []string) int64 { - placeholder := datasource.KeyPlaceholderByQuery(len(userIds)) - sql := "delete from sys_user_role where role_id= ? and user_id in (" + placeholder + ")" - parameters := datasource.ConvertIdsSlice(userIds) - parameters = append([]any{roleId}, parameters...) - results, err := datasource.ExecDB("", sql, parameters) - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - affected, err := results.RowsAffected() - if err != nil { - log.Errorf("delete err => %v", err) - return 0 - } - return affected -} diff --git a/features/trace/tcpdump.go b/features/trace/tcpdump.go deleted file mode 100644 index e2f8765..0000000 --- a/features/trace/tcpdump.go +++ /dev/null @@ -1,339 +0,0 @@ -package trace - -import ( - "fmt" - "net" - "net/http" - "strings" - "time" - - "be.ems/lib/core/conf" - "be.ems/lib/core/utils/ctx" - "be.ems/lib/core/vo/result" - "be.ems/lib/dborm" - "be.ems/lib/log" - "be.ems/restagent/config" - "be.ems/src/framework/utils/cmd" - "be.ems/src/framework/utils/ssh" -) - -var ( - UriTcpdumpTask = config.DefaultUriPrefix + "/traceManagement/{apiVersion}/tcpdumpNeTask" - CustomUriTcpdumpTask = config.UriPrefix + "/traceManagement/{apiVersion}/tcpdumpNeTask" // decode message api - - UriTcpdumpPcapDownload = config.DefaultUriPrefix + "/traceManagement/{apiVersion}/tcpdumpPcapDownload" - CustomUriTcpdumpPcapDownload = config.UriPrefix + "/traceManagement/{apiVersion}/tcpdumpPcapDownload" // decode message api - - UriTcpdumpNeUPFTask = config.DefaultUriPrefix + "/traceManagement/{apiVersion}/tcpdumpNeUPFTask" - CustomUriTcpdumpNeUPFTask = config.UriPrefix + "/traceManagement/{apiVersion}/tcpdumpNeUPFTask" // decode message api -) - -// NeInfo 网元信息 -func NeInfo(neType, neId string) (*dborm.NeInfo, error) { - neInfo, err := dborm.XormGetNeInfo(neType, neId) - if err != nil { - log.Error("dborm.XormGetNeInfo is failed:", err) - return nil, err - } - if neInfo == nil || neInfo.Ip == "" { - return nil, fmt.Errorf("not ne_info or not IP") - } - return neInfo, nil -} - -// TcpdumpNeTask 网元发送执行 pcap -func TcpdumpNeTask(w http.ResponseWriter, r *http.Request) { - var body struct { - NeType string `json:"neType"` // 网元类型 - NeId string `json:"neId"` // 网元ID - Timeout int `json:"timeout"` // 超时时间 - Cmd string `json:"cmd"` // 命令 - } - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.NeType == "" || body.NeId == "" || body.Timeout < 5 || body.Cmd == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfo(body.NeType, body.NeId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - filePcapName := fmt.Sprintf("tmp_%s_%s_%d.pcap", body.NeType, body.NeId, time.Now().UnixMilli()) - fileLogName := fmt.Sprintf("tmp_%s_%s_%d.log", body.NeType, body.NeId, time.Now().UnixMilli()) - writeLog := fmt.Sprintf(" >> %s 2>&1 \ncat %s", fileLogName, fileLogName) // 执行信息写入日志文件,放置弹出code 127 - cmdStr := fmt.Sprintf("cd /tmp \nsudo timeout %d tcpdump -i any %s -s0 -w %s", body.Timeout, body.Cmd, filePcapName) - usernameNe := conf.Get("ne.user").(string) // 网元统一用户 - sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.Ip) - msg, err := cmd.ExecWithCheck("ssh", sshHost, cmdStr+writeLog) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - ctx.JSON(w, 200, result.OkData(map[string]any{ - "cmd": cmdStr, - "msg": msg, - "fileName": filePcapName, - })) -} - -// TcpdumpPcapDownload 网元抓包pcap文件下载 -func TcpdumpPcapDownload(w http.ResponseWriter, r *http.Request) { - var body struct { - NeType string `json:"neType"` // 网元类型 - NeId string `json:"neId"` // 网元ID - FileName string `json:"fileName"` // 文件名 - } - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.NeType == "" || body.NeId == "" || body.FileName == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfo(body.NeType, body.NeId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - nePath := fmt.Sprintf("/tmp/%s", body.FileName) - localPath := fmt.Sprintf("%s/tcpdump/pcap/%s", conf.Get("ne.omcdir"), body.FileName) - err = ssh.FileSCPNeToLocal(neInfo.Ip, nePath, localPath) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - ctx.FileAttachment(w, r, localPath, body.FileName) -} - -// TcpdumpNeUPFTask 网元UPF发送执行 pcap -func TcpdumpNeUPFTask(w http.ResponseWriter, r *http.Request) { - var body struct { - NeType string `json:"neType"` // 网元类型 - NeId string `json:"neId"` // 网元ID - Cmd string `json:"cmd"` // 命令 - RunType string `json:"runType"` // 执行开始start还是停止stop - } - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.NeType != "UPF" || body.NeId == "" || body.Cmd == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfo(body.NeType, body.NeId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 开始telnet - if body.RunType == "start_telnet" { - // 创建TCP连接 - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", neInfo.Ip, 5002)) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - defer conn.Close() - - filePcapName := fmt.Sprintf("tmp_%s_%s.pcap", body.NeType, body.NeId) - cmdStr := fmt.Sprintf("pcap dispatch trace on max 100000 file %s", filePcapName) - - fmt.Fprintln(conn, cmdStr) - - // 读取内容 - time.Sleep(time.Duration(200) * time.Millisecond) - buf := make([]byte, 1024*8) - n, err := conn.Read(buf) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - } else { - str := string(buf[0:n]) - s := strings.Index(str, "pcap dispatch trace:") - if s != -1 { - e := strings.Index(str, "\r\nupfd1#") - str = str[s:e] - } else { - str = fmt.Sprintf("Executed, please stop before proceeding %d", n) - } - ctx.JSON(w, 200, result.OkData(map[string]any{ - "cmd": cmdStr, - "msg": str, - "fileName": filePcapName, - })) - } - conn.Close() - return - } - // 停止telnet - if body.RunType == "stop_telnet" { - // 创建TCP连接 - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", neInfo.Ip, 5002)) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - defer conn.Close() - - filePcapName := fmt.Sprintf("tmp_%s_%s.pcap", body.NeType, body.NeId) - cmdStr := "pcap dispatch trace off" - - fmt.Fprintln(conn, cmdStr) - - // 读取内容 - time.Sleep(time.Duration(200) * time.Millisecond) - buf := make([]byte, 1024*8) - n, err := conn.Read(buf) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - } else { - str := string(buf[0:n]) - s := strings.Index(str, "pcap dispatch trace:") - if s == -1 { - s = strings.Index(str, "Write ") - } - if s != -1 { - e := strings.Index(str, "\r\nupfd1#") - str = str[s:e] - } else { - str = "No stoppable found" - } - - ctx.JSON(w, 200, result.OkData(map[string]any{ - "cmd": cmdStr, - "msg": str, - "fileName": filePcapName, - })) - } - conn.Close() - return - } - - // 开始 -脚本 - if body.RunType == "start2" { - fileLogName := fmt.Sprintf("tmp_%s_%s.log", body.NeType, body.NeId) - filePcapName := fmt.Sprintf("tmp_%s_%s.pcap", body.NeType, body.NeId) - // 复制文件到网元上 - err := ssh.FileSCPLocalToNe(neInfo.Ip, "C:\\AMP\\Probject\\ems_backend\\restagent\\backup\\upf_pcap", "/tmp") - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - writeLog := fmt.Sprintf(" >> %s 2>&1 \ncat %s", fileLogName, fileLogName) // 执行信息写入日志文件,放置弹出code 127 - cmdStr := fmt.Sprintf("cd /tmp \nchmod +x upf_pcap\n./upf_pcap '192.168.4.139' 'root' 'Admin123@pl' 'pcap dispatch trace on max 100000 file %s' %s ", fileLogName, writeLog) - - usernameNe := conf.Get("ne.user").(string) // 网元统一用户 - sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.Ip) - msg, err := cmd.ExecWithCheck("ssh", sshHost, cmdStr) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - } else { - ctx.JSON(w, 200, result.OkData(map[string]any{ - "cmd": cmdStr, - "msg": msg, - "fileName": filePcapName, - })) - } - return - } - // 停止 -脚本 - if body.RunType == "stop2" { - fileLogName := fmt.Sprintf("tmp_%s_%s.log", body.NeType, body.NeId) - filePcapName := fmt.Sprintf("tmp_%s_%s.pcap", body.NeType, body.NeId) - // cmdStr := "cd /tmp \nexpect /tmp/cat.sh " - err := ssh.FileSCPLocalToNe(neInfo.Ip, "C:\\AMP\\Probject\\ems_backend\\restagent\\backup\\upf_pcap", "/tmp") - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - writeLog := fmt.Sprintf(" >> %s 2>&1 \ncat %s", fileLogName, fileLogName) // 执行信息写入日志文件,放置弹出code 127 - cmdStr := fmt.Sprintf("cd /tmp \nchmod +x upf_pcap\n./upf_pcap '192.168.4.139' 'root' 'Admin123@pl' 'pcap dispatch trace off' %s ", writeLog) - - usernameNe := conf.Get("ne.user").(string) // 网元统一用户 - sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.Ip) - msg, err := cmd.ExecWithCheck("ssh", sshHost, cmdStr) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - } else { - ctx.JSON(w, 200, result.OkData(map[string]any{ - "cmd": cmdStr, - "msg": msg, - "fileName": filePcapName, - })) - } - return - } - - // 开始-脚本字符串 - if body.RunType == "start_str" { - fileLogName := fmt.Sprintf("tmp_%s_%s.log", body.NeType, body.NeId) - filePcapName := fmt.Sprintf("tmp_%s_%s.pcap", body.NeType, body.NeId) - scriptStr := "set capcmd [lindex $argv 0]\nspawn telnet localhost 5002\nexpect \"upfd1# \"\nsend \"$capcmd\\n\"\nexpect \"upfd1# \"\nsend \"quit\\n\"\nexpect \"eof\"" - writeLog := fmt.Sprintf(" > %s 2>&1 \ncat %s", fileLogName, fileLogName) // 执行信息写入日志文件输出,避免弹出code 127 - - capCmdStr := fmt.Sprintf("%s file %s", body.Cmd, filePcapName) - - cmdStr := fmt.Sprintf("cd /tmp\n\necho '%s' > cap.sh\n\nchmod +x cap.sh\n\nexpect ./cap.sh '%s'%s", scriptStr, capCmdStr, writeLog) - usernameNe := conf.Get("ne.user").(string) // 网元统一用户 - sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.Ip) - msg, err := cmd.ExecWithCheck("ssh", sshHost, cmdStr) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - } else { - s := strings.Index(msg, "pcap dispatch trace:") - if s != -1 { - e := strings.Index(msg, "\r\nupfd1#") - msg = msg[s:e] - } else { - msg = "Executed, please stop before proceeding" - } - ctx.JSON(w, 200, result.OkData(map[string]any{ - "cmd": capCmdStr, - "msg": msg, - "fileName": filePcapName, - })) - } - return - } - // 停止-脚本字符串 - if body.RunType == "stop_str" { - fileLogName := fmt.Sprintf("tmp_%s_%s.log", body.NeType, body.NeId) - filePcapName := fmt.Sprintf("tmp_%s_%s.pcap", body.NeType, body.NeId) - scriptStr := "set capcmd [lindex $argv 0]\nspawn telnet localhost 5002\nexpect \"upfd1# \"\nsend \"$capcmd\\n\"\nexpect \"upfd1# \"\nsend \"quit\\n\"\nexpect \"eof\"" - writeLog := fmt.Sprintf(" > %s 2>&1 \ncat %s", fileLogName, fileLogName) // 执行信息写入日志文件输出,避免弹出code 127 - - capCmdStr := body.Cmd - - cmdStr := fmt.Sprintf("cd /tmp\n\necho '%s' > cap.sh\n\nchmod +x cap.sh\n\nexpect ./cap.sh '%s'%s", scriptStr, capCmdStr, writeLog) - - usernameNe := conf.Get("ne.user").(string) // 网元统一用户 - sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.Ip) - msg, err := cmd.ExecWithCheck("ssh", sshHost, cmdStr) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - } else { - s := strings.Index(msg, "pcap dispatch trace:") - if s == -1 { - s = strings.Index(msg, "Write ") - } - if s != -1 { - e := strings.Index(msg, "\r\nupfd1#") - msg = msg[s:e] - } else { - msg = "No stoppable found" - } - ctx.JSON(w, 200, result.OkData(map[string]any{ - "cmd": capCmdStr, - "msg": msg, - "fileName": filePcapName, - })) - } - return - } - - ctx.JSON(w, 200, result.ErrMsg("runType is start or stop")) -} diff --git a/features/udm_user/api_udm_user.go b/features/udm_user/api_udm_user.go deleted file mode 100644 index 40918e4..0000000 --- a/features/udm_user/api_udm_user.go +++ /dev/null @@ -1,1211 +0,0 @@ -package udmuser - -import ( - "fmt" - "net/http" - "strconv" - "strings" - "time" - - "be.ems/features/udm_user/model" - "be.ems/features/udm_user/service" - "be.ems/lib/core/conf" - mmlclient "be.ems/lib/core/mml_client" - "be.ems/lib/core/utils/ctx" - "be.ems/lib/core/vo/result" - "be.ems/lib/dborm" - "be.ems/lib/log" - "be.ems/lib/midware" - "be.ems/lib/services" - "be.ems/restagent/config" - "be.ems/src/framework/middleware/collectlogs" - "be.ems/src/framework/utils/file" - "be.ems/src/framework/utils/ssh" -) - -// UDM 用户信息接口添加到路由 -func Routers() []services.RouterItem { - // 实例化控制层 SysDictTypeApi 结构体 - var apis = &UdmUserApi{ - authUser: *service.NewServiceUdmAuthUser, - subUser: *service.NewServiceUdmSubUser, - } - - rs := [...]services.RouterItem{ - // UDM鉴权用户 - { - Method: "GET", - Pattern: "/auths", - Handler: apis.UdmAuthUserList, - Middleware: midware.Authorize(nil), - }, - { - Method: "POST", - Pattern: "/authSave/{neId}", - Handler: apis.UdmAuthUserSave, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Authentication User", collectlogs.BUSINESS_TYPE_INSERT)), - }, - { - Method: "GET", - Pattern: "/auth/{neId}/{imsi}", - Handler: apis.UdmAuthUserInfo, - Middleware: midware.Authorize(nil), - }, - { - Method: "POST", - Pattern: "/auth/{neId}", - Handler: apis.UdmAuthUserAdd, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Authentication User", collectlogs.BUSINESS_TYPE_INSERT)), - }, - { - Method: "POST", - Pattern: "/auth/{neId}/{num}", - Handler: apis.UdmAuthUserAdds, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Authentication User", collectlogs.BUSINESS_TYPE_INSERT)), - }, - { - Method: "PUT", - Pattern: "/auth/{neId}", - Handler: apis.UdmAuthUserEdit, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Authentication User", collectlogs.BUSINESS_TYPE_UPDATE)), - }, - { - Method: "DELETE", - Pattern: "/auth/{neId}/{imsi}", - Handler: apis.UdmAuthUserRemove, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Authentication User", collectlogs.BUSINESS_TYPE_DELETE)), - }, - { - Method: "DELETE", - Pattern: "/auth/{neId}/{imsi}/{num}", - Handler: apis.UdmAuthUserRemoves, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Authentication User", collectlogs.BUSINESS_TYPE_DELETE)), - }, - { - Method: "POST", - Pattern: "/authExport", - Handler: apis.UdmAuthUserExport, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Authentication User", collectlogs.BUSINESS_TYPE_EXPORT)), - }, - { - Method: "POST", - Pattern: "/authImport/{neId}", - Handler: apis.UdmAuthUserImport, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Authentication User", collectlogs.BUSINESS_TYPE_IMPORT)), - }, - // UDM签约用户 - { - Method: "GET", - Pattern: "/subs", - Handler: apis.UdmSubUserList, - Middleware: midware.Authorize(nil), - }, - { - Method: "POST", - Pattern: "/subSave/{neId}", - Handler: apis.UdmSubUserSave, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Contracted User", collectlogs.BUSINESS_TYPE_INSERT)), - }, - { - Method: "GET", - Pattern: "/subInfo/{neId}/{imsi}", - Handler: apis.UdmSubUserInfo, - Middleware: midware.Authorize(nil), - }, - { - Method: "POST", - Pattern: "/sub/{neId}", - Handler: apis.UdmSubUserAdd, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Contracted User", collectlogs.BUSINESS_TYPE_INSERT)), - }, - { - Method: "POST", - Pattern: "/sub/{neId}/{num}", - Handler: apis.UdmSubUserAdds, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Contracted User", collectlogs.BUSINESS_TYPE_INSERT)), - }, - { - Method: "PUT", - Pattern: "/sub/{neId}", - Handler: apis.UdmSubUserEdit, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Contracted User", collectlogs.BUSINESS_TYPE_UPDATE)), - }, - { - Method: "PUT", - Pattern: "/sub4gIP/{neId}", - Handler: apis.UdmSubUser4GIP, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Contracted User", collectlogs.BUSINESS_TYPE_UPDATE)), - }, - { - Method: "PUT", - Pattern: "/subSmData/{neId}", - Handler: apis.UdmSubUserSmData, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Contracted User", collectlogs.BUSINESS_TYPE_UPDATE)), - }, - { - Method: "DELETE", - Pattern: "/sub/{neId}/{imsi}", - Handler: apis.UdmSubUserRemove, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Contracted User", collectlogs.BUSINESS_TYPE_DELETE)), - }, - { - Method: "DELETE", - Pattern: "/sub/{neId}/{imsi}/{num}", - Handler: apis.UdmSubUserRemoves, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Contracted User", collectlogs.BUSINESS_TYPE_DELETE)), - }, - { - Method: "POST", - Pattern: "/subExport", - Handler: apis.UdmSubUserExport, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Contracted User", collectlogs.BUSINESS_TYPE_EXPORT)), - }, - { - Method: "POST", - Pattern: "/subImport/{neId}", - Handler: apis.UdmSubUserImport, - Middleware: midware.LogOperate(collectlogs.OptionNew("UDM Contracted User", collectlogs.BUSINESS_TYPE_IMPORT)), - }, - // 添加更多的 Router 对象... - } - - // 生成两组前缀路由 - rsPrefix := []services.RouterItem{} - for _, v := range rs { - path := "/udmUserManage/{apiVersion}" + v.Pattern - // 固定前缀 - v.Pattern = config.DefaultUriPrefix + path - rsPrefix = append(rsPrefix, v) - // 可配置 - v.Pattern = config.UriPrefix + path - rsPrefix = append(rsPrefix, v) - } - return rsPrefix -} - -// NeInfoByUDM 网元信息 -func NeInfoByUDM(neId string) (*dborm.NeInfo, error) { - neInfo, err := dborm.XormGetNeInfo("UDM", neId) - if err != nil { - log.Error("dborm.XormGetNeInfo is failed:", err) - return nil, err - } - if neInfo == nil || neInfo.Ip == "" { - return nil, fmt.Errorf("not ne_info or not IP") - } - return neInfo, nil -} - -// UDM 用户 -// -// PATH /udmUserManage -type UdmUserApi struct { - authUser service.ServiceUdmAuthUser - subUser service.ServiceUdmSubUser -} - -// UDM鉴权用户 -// -// GET /auths -func (s *UdmUserApi) UdmAuthUserList(w http.ResponseWriter, r *http.Request) { - querys := ctx.QueryMap(r) - querys["neId"] = "" - data := s.authUser.Page(querys) - ctx.JSON(w, 200, result.Ok(data)) -} - -// UDM鉴权用户-获取全部保存数据库 -// -// POST /authSave/{neId} -func (s *UdmUserApi) UdmAuthUserSave(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - if neId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neId = "" - data := s.authUser.Save(neId) - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM鉴权用户-信息 -// -// GET /authInfo/{neId}/{imsi} -func (s *UdmUserApi) UdmAuthUserInfo(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - imsi := ctx.Param(r, "imsi") - if neId == "" || imsi == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("dsp authdat:imsi=%s", imsi) - - // 发送MML - data, err := mmlclient.MMLSendMsgToMap(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 查询数据库是否存在并存入 - neId = "" - var userInfo model.UdmAuthUser - list := s.authUser.List(model.UdmAuthUser{NeID: neId, Imsi: imsi}) - if len(list) > 0 { - userInfo = list[0] - // 返回查询的用户信息 - userInfo.Amf = data["amf"] - userInfo.AlgoIndex = data["algo"] - userInfo.Opc = data["opc"] - userInfo.Ki = data["ki"] - } else { - userInfo := model.UdmAuthUser{ - Imsi: imsi, - Amf: data["amf"], - AlgoIndex: data["algo"], - Opc: data["opc"], - Ki: data["ki"], - } - s.authUser.Insert(neId, userInfo) - } - ctx.JSON(w, 200, result.OkData(userInfo)) - -} - -// UDM鉴权用户-增加 -// -// POST /{neId} -func (s *UdmUserApi) UdmAuthUserAdd(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - if neId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - var body model.UdmAuthUser - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.Imsi == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("add authdat:imsi=%s,ki=%s,amf=%s,algo=%s,opc=%s", body.Imsi, body.Ki, body.Amf, body.AlgoIndex, body.Opc) - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.authUser.Insert(neId, body) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM鉴权用户-批量添加 -// -// POST /{neId}/{num} -func (s *UdmUserApi) UdmAuthUserAdds(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - num := ctx.Param(r, "num") - if neId == "" || num == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - var body model.UdmAuthUser - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.Imsi == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("bad authdat:start_imsi=%s,sub_num=%s,ki=%s,amf=%s,algo=%s,opc=%s", body.Imsi, num, body.Ki, body.Amf, body.AlgoIndex, body.Opc) - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.authUser.Inserts(neId, body, num) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM鉴权用户-修改 -// -// PUT /{neId} -func (s *UdmUserApi) UdmAuthUserEdit(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - if neId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - var body model.UdmAuthUser - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.Imsi == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("mod authdata:imsi=%s", body.Imsi) - // 修改的参数名称 - if body.Ki != "" { - msg += fmt.Sprintf(",ki=%s", body.Ki) - } - if body.Amf != "" { - msg += fmt.Sprintf(",amf=%s", body.Amf) - } - if body.AlgoIndex != "" { - msg += fmt.Sprintf(",algo=%s", body.AlgoIndex) - } - if body.Opc != "" { - msg += fmt.Sprintf(",opc=%s", body.Opc) - } - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.authUser.Update(neId, body) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM鉴权用户-删除 -// -// DELETE /{neId}/{imsi} -func (s *UdmUserApi) UdmAuthUserRemove(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - imsi := ctx.Param(r, "imsi") - if neId == "" || imsi == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("del authdat:imsi=%s", imsi) - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.authUser.Delete(neId, imsi) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM鉴权用户-批量删除 -// -// DELETE /{neId}/{imsi}/{num} -func (s *UdmUserApi) UdmAuthUserRemoves(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - imsi := ctx.Param(r, "imsi") - num := ctx.Param(r, "num") - if neId == "" || imsi == "" || num == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("bde authdat:start_imsi=%s,sub_num=%s", imsi, num) - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.authUser.Deletes(neId, imsi, num) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM鉴权用户-导出 -// -// POST /authExport -func (s *UdmUserApi) UdmAuthUserExport(w http.ResponseWriter, r *http.Request) { - var body struct { - NeId string `json:"neId"` - Type string `json:"type"` - } - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.NeId == "" || body.Type == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - if !(body.Type == "csv" || body.Type == "txt") { - ctx.JSON(w, 200, result.ErrMsg("Export file types support CSV and txt")) - return - } - - neId := "" - list := s.authUser.List(model.UdmAuthUser{NeID: neId}) - // 文件名 - fileName := fmt.Sprintf("OMC_AUTH_USER_EXPORT_%s_%d.%s", neId, time.Now().UnixMilli(), body.Type) - filePath := fmt.Sprintf("%s/upload/mml/%s", conf.Get("ne.omcdir"), fileName) - - if body.Type == "csv" { - // 转换数据 - data := [][]string{} - data = append(data, []string{"imsi", "ki", "algo", "amf", "opc"}) - for _, v := range list { - data = append(data, []string{v.Imsi, v.Ki, v.AlgoIndex, v.Amf, v.Opc}) - } - // 输出到文件 - err := file.WriterFileCSV(data, filePath) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - } - - if body.Type == "txt" { - // 转换数据 - data := [][]string{} - for _, v := range list { - data = append(data, []string{v.Imsi, v.Ki, v.AlgoIndex, v.Amf, v.Opc}) - } - // 输出到文件 - err = file.WriterFileTXT(data, ",", filePath) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - } - - ctx.FileAttachment(w, r, filePath, fileName) -} - -// UDM鉴权用户-导入 -// -// POST /authImport/{neId} -func (s *UdmUserApi) UdmAuthUserImport(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - if neId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 获取文件名 - _, fileHeader, err := r.FormFile("file") - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - if !(strings.HasSuffix(fileHeader.Filename, ".csv") || strings.HasSuffix(fileHeader.Filename, ".txt")) { - ctx.JSON(w, 200, result.ErrMsg("Please upload files in. csv and. txt formats with content fields: imsi,ki,algo,amf,opc")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 文件名 - fileName := fmt.Sprintf("OMC_AUTH_USER_IMPORT_%s_%d_%s", neId, time.Now().UnixMilli(), fileHeader.Filename) - localPath := fmt.Sprintf("%s/upload/mml/%s", conf.Get("ne.omcdir"), fileName) - nePath := conf.Get("mml.upload").(string) - // 输出保存文件 - err = ctx.SaveUploadedFile(r, localPath) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 复制到远程 - err = ssh.FileSCPLocalToNe(neInfo.Ip, localPath, nePath) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("import authdat:path=%s", fmt.Sprintf("%s/%s", nePath, fileName)) - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - if strings.HasSuffix(fileHeader.Filename, ".csv") { - data := file.ReadFileCSV(localPath) - neId = "" - go s.authUser.InsertCSV(neId, data) - } - if strings.HasSuffix(fileHeader.Filename, ".txt") { - data := file.ReadFileTXT(",", localPath) - neId = "" - go s.authUser.InsertTxt(neId, data) - } - } - ctx.JSON(w, 200, result.OkMsg(data)) -} - -// UDM签约用户 -// -// GET /subs -func (s *UdmUserApi) UdmSubUserList(w http.ResponseWriter, r *http.Request) { - querys := ctx.QueryMap(r) - querys["neId"] = "" - data := s.subUser.Page(querys) - ctx.JSON(w, 200, result.Ok(data)) -} - -// UDM签约用户-获取全部保存数据库 -// -// POST /subSave/{neId} -func (s *UdmUserApi) UdmSubUserSave(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - if neId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neId = "" - data := s.subUser.Save(neId) - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM签约用户-信息 -// -// GET /subInfo/{neId}/{imsi} -func (s *UdmUserApi) UdmSubUserInfo(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - imsi := ctx.Param(r, "imsi") - if neId == "" || imsi == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("dsp udmuser:imsi=%s", imsi) - - // 发送MML - data, err := mmlclient.MMLSendMsgToMap(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 解析返回的数据 - cnType, _ := strconv.ParseInt(data["CNType"][:4], 0, 64) - rat, _ := strconv.ParseInt(data["RAT"][:4], 0, 64) - msisdn := data["MSISDN"] - imsMsisdnLen := strings.Index(msisdn, ",") - if imsMsisdnLen != -1 { - msisdn = msisdn[:imsMsisdnLen] - } - userInfo := model.UdmSubUser{ - Imsi: imsi, - Msisdn: msisdn, - Ambr: data["AMBR"], - Arfb: data["AreaForbidden"], - Cn: fmt.Sprint(cnType), - SmData: data["SM-Data(snssai+dnn[1..n])"], - Sar: data["ServiceAreaRestriction"], - Nssai: data["NSSAI"], - SmfSel: data["Smf-Selection"], - Rat: fmt.Sprint(rat), - } - // 1,64,24,65,def_eps,1,2,010200000000,- - if v, ok := data["EPS-Data"]; ok { - userInfo.EpsDat = v - arr := strings.Split(v, ",") - userInfo.EpsFlag = arr[0] - userInfo.EpsOdb = arr[1] - userInfo.HplmnOdb = arr[2] - userInfo.Ard = arr[3] - userInfo.Epstpl = arr[4] - userInfo.ContextId = arr[5] - userInfo.ApnContext = arr[7] - userInfo.StaticIp = arr[8] - } - - // 查询数据库是否存在并存入更新 - neId = "" - list := s.subUser.List(model.UdmSubUser{NeID: neId, Imsi: imsi}) - if len(list) > 0 { - listItme := list[0] - userInfo.ID = listItme.ID - s.subUser.Update(neId, userInfo) - } else { - s.subUser.Insert(neId, userInfo) - } - ctx.JSON(w, 200, result.OkData(userInfo)) -} - -// UDM签约用户-增加 -// -// POST /{neId} -func (s *UdmUserApi) UdmSubUserAdd(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - if neId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - var body model.UdmSubUser - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.Imsi == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("add udmuser:imsi=%s,msisdn=%s,ambr=%s,nssai=%s,arfb=%s,sar=%s,rat=%s,cn=%s,smf_sel=%s,sm_data=%s,eps_flag=%s,eps_odb=%s,hplmn_odb=%s,ard=%s,epstpl=%s,context_id=%s,apn_context=%s", - body.Imsi, body.Msisdn, body.Ambr, body.Nssai, body.Arfb, body.Sar, body.Rat, body.Cn, body.SmfSel, body.SmData, body.EpsFlag, body.EpsOdb, body.HplmnOdb, body.Ard, body.Epstpl, body.ContextId, body.ApnContext) - // static_ip指给4G UE分配的静态IP,没有可不带此字段名,批量添加IP会自动递增 - if body.StaticIp != "" { - msg += fmt.Sprintf(",static_ip=%s", body.StaticIp) - } - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.subUser.Insert(neId, body) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM签约用户-批量添加 -// -// POST /{neId}/{num} -func (s *UdmUserApi) UdmSubUserAdds(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - num := ctx.Param(r, "num") - if neId == "" || num == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - var body model.UdmSubUser - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.Imsi == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("bad udmuser:start_imsi=%s,start_msisdn=%s,sub_num=%s,ambr=%s,nssai=%s,arfb=%s,sar=%s,rat=%s,cn=%s,smf_sel=%s,sm_data=%s,eps_flag=%s,eps_odb=%s,hplmn_odb=%s,ard=%s,epstpl=%s,context_id=%s,apn_context=%s", - body.Imsi, body.Msisdn, num, body.Ambr, body.Nssai, body.Arfb, body.Sar, body.Rat, body.Cn, body.SmfSel, body.SmData, body.EpsFlag, body.EpsOdb, body.HplmnOdb, body.Ard, body.Epstpl, body.ContextId, body.ApnContext) - // static_ip指给4G UE分配的静态IP,没有可不带此字段名,批量添加IP会自动递增 - if body.StaticIp != "" { - msg += fmt.Sprintf(",static_ip=%s", body.StaticIp) - } - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.subUser.Inserts(neId, body, num) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM签约用户-批量添加4G用户 -// -// POST /sub4G/{neId} -func (s *UdmUserApi) UdmSubUserAdd4G(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - if neId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - var body model.UdmSubUser - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.Imsi == "" || body.SubNum == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("bmd udmuser:start_imsi=%s,sub_num=%s,eps_flag=%s", body.Imsi, body.SubNum, body.EpsFlag) - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.subUser.Insert4G(neId, body) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM签约用户-修改 -// -// PUT /{neId} -func (s *UdmUserApi) UdmSubUserEdit(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - if neId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - var body model.UdmSubUser - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.Imsi == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("mod udmuser:imsi=%s", body.Imsi) - // 修改的参数名称 - if body.Msisdn != "" { - msg += fmt.Sprintf(",msisdn=%s", body.Msisdn) - } - if body.Ambr != "" { - msg += fmt.Sprintf(",ambr=%s", body.Ambr) - } - if body.Nssai != "" { - msg += fmt.Sprintf(",nssai=%s", body.Nssai) - } - if body.Arfb != "" { - msg += fmt.Sprintf(",arfb=%s", body.Arfb) - } - if body.Sar != "" { - msg += fmt.Sprintf(",sar=%s", body.Sar) - } - if body.Rat != "" { - msg += fmt.Sprintf(",rat=%s", body.Rat) - } - if body.Cn != "" { - msg += fmt.Sprintf(",cn=%s", body.Cn) - } - if body.SmfSel != "" { - msg += fmt.Sprintf(",smf_sel=%s", body.SmfSel) - } - if body.SmData != "" { - msg += fmt.Sprintf(",sm_data=%s", body.SmData) - } - if body.EpsDat != "" { - msg += fmt.Sprintf(",eps_dat=%s", body.EpsDat) - } - if body.EpsFlag != "" { - msg += fmt.Sprintf(",eps_flag=%s", body.EpsFlag) - } - if body.EpsOdb != "" { - msg += fmt.Sprintf(",eps_odb=%s", body.EpsOdb) - } - if body.HplmnOdb != "" { - msg += fmt.Sprintf(",hplmn_odb=%s", body.HplmnOdb) - } - if body.Epstpl != "" { - msg += fmt.Sprintf(",epstpl=%s", body.Epstpl) - } - if body.Ard != "" { - msg += fmt.Sprintf(",ard=%s", body.Ard) - } - if body.ContextId != "" { - msg += fmt.Sprintf(",context_id=%s", body.ContextId) - } - if body.ApnContext != "" { - msg += fmt.Sprintf(",apn_context=%s", body.ApnContext) - } - if body.StaticIp != "" { - msg += fmt.Sprintf(",static_ip=%s", body.StaticIp) - } - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.subUser.Update(neId, body) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM签约用户-批量修改4G IP -// -// PUT /sub4gIP/{neId} -func (s *UdmUserApi) UdmSubUser4GIP(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - if neId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - var body model.UdmSubUser - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.Imsi == "" || body.SubNum == "" || body.StaticIp == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("bmd udmuser:start_imsi=%s,sub_num=%s,static_ip=%s", body.Imsi, body.SubNum, body.StaticIp) - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.subUser.Update4GIP(neId, body) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM签约用户-批量修改sm-data -// -// PUT /subSmData/{neId} -func (s *UdmUserApi) UdmSubUserSmData(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - if neId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - var body model.UdmSubUser - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.Imsi == "" || body.SubNum == "" || body.SmData == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 3-000003&internet-10.10.1.1&ims-10.11.1.1 - msg := fmt.Sprintf("bmd udmuser:start_imsi=%s,sub_num=%s,sm_data=%s", body.Imsi, body.SubNum, body.SmData) - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.subUser.UpdateSmData(neId, body) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM签约用户-删除 -// -// DELETE /{neId}/{imsi} -func (s *UdmUserApi) UdmSubUserRemove(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - imsi := ctx.Param(r, "imsi") - if neId == "" || imsi == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("del udmuser:imsi=%s", imsi) - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.subUser.Delete(neId, imsi) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM签约用户-批量删除 -// -// DELETE /{neId}/{imsi}/{num} -func (s *UdmUserApi) UdmSubUserRemoves(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - imsi := ctx.Param(r, "imsi") - num := ctx.Param(r, "num") - if neId == "" || imsi == "" || num == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("bde udmuser:start_imsi=%s,sub_num=%s", imsi, num) - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 命令ok时 - if strings.Contains(data, "ok") { - neId = "" - s.subUser.Deletes(neId, imsi, num) - } - ctx.JSON(w, 200, result.OkData(data)) -} - -// UDM签约用户-导出 -// -// POST /subExport -func (s *UdmUserApi) UdmSubUserExport(w http.ResponseWriter, r *http.Request) { - var body struct { - NeId string `json:"neId"` - Type string `json:"type"` - } - err := ctx.ShouldBindJSON(r, &body) - if err != nil || body.NeId == "" || body.Type == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - if !(body.Type == "csv" || body.Type == "txt") { - ctx.JSON(w, 200, result.ErrMsg("Export file type support csv、txt")) - return - } - - neId := "" - list := s.subUser.List(model.UdmSubUser{NeID: neId}) - // 文件名 - fileName := fmt.Sprintf("OMC_AUTH_USER_EXPORT_%s_%d.%s", neId, time.Now().UnixMilli(), body.Type) - filePath := fmt.Sprintf("%s/upload/mml/%s", conf.Get("ne.omcdir"), fileName) - - if body.Type == "csv" { - // 转换数据 - data := [][]string{} - data = append(data, []string{"imsi", "msisdn", "ambr", "nssai", "arfb", "sar", "rat", "cn", "smf_sel", "sm_dat", "eps_dat"}) - for _, v := range list { - epsDat := fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s", v.EpsFlag, v.EpsOdb, v.HplmnOdb, v.Ard, v.Epstpl, v.ContextId, v.ApnContext, v.StaticIp) - data = append(data, []string{v.Imsi, v.Msisdn, v.Ambr, v.Nssai, v.Arfb, v.Sar, v.Rat, v.Cn, v.SmfSel, v.SmData, epsDat}) - } - // 输出到文件 - err = file.WriterFileCSV(data, filePath) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - } - - if body.Type == "txt" { - // 转换数据 - data := [][]string{} - for _, v := range list { - epsDat := fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s", v.EpsFlag, v.EpsOdb, v.HplmnOdb, v.Ard, v.Epstpl, v.ContextId, v.ApnContext, v.StaticIp) - data = append(data, []string{v.Imsi, v.Msisdn, v.Ambr, v.Nssai, v.Arfb, v.Sar, v.Rat, v.Cn, v.SmfSel, v.SmData, epsDat}) - } - // 输出到文件 - err = file.WriterFileTXT(data, ",", filePath) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - } - - ctx.FileAttachment(w, r, filePath, fileName) -} - -// UDM签约用户-导入 -// -// POST /subImport/{neId} -func (s *UdmUserApi) UdmSubUserImport(w http.ResponseWriter, r *http.Request) { - neId := ctx.Param(r, "neId") - if neId == "" { - ctx.JSON(w, 400, result.CodeMsg(400, "parameter error")) - return - } - - // 获取文件名 - _, fileHeader, err := r.FormFile("file") - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - if !(strings.HasSuffix(fileHeader.Filename, ".csv") || strings.HasSuffix(fileHeader.Filename, ".txt")) { - ctx.JSON(w, 200, result.ErrMsg("Please upload files in. csv and. txt formats with content fields: imsi,msisdn,ambr,nssai,arfb,sar,rat,cn,smf_sel,sm_dat,eps_dat")) - return - } - - neInfo, err := NeInfoByUDM(neId) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 文件名 - fileName := fmt.Sprintf("OMC_SUB_USER_IMPORT_%s_%d_%s", neId, time.Now().UnixMilli(), fileHeader.Filename) - localPath := fmt.Sprintf("%s/upload/mml/%s", conf.Get("ne.omcdir"), fileName) - nePath := conf.Get("mml.upload").(string) - // 输出保存文件 - err = ctx.SaveUploadedFile(r, localPath) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - // 复制到远程 - err = ssh.FileSCPLocalToNe(neInfo.Ip, localPath, nePath) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - - msg := fmt.Sprintf("import udmuser:path=%s", fmt.Sprintf("%s/%s", nePath, fileName)) - - // 发送MML - data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) - if err != nil { - ctx.JSON(w, 200, result.ErrMsg(err.Error())) - return - } - // 命令ok时 - if strings.Contains(data, "ok") { - if strings.HasSuffix(fileHeader.Filename, ".csv") { - data := file.ReadFileCSV(localPath) - neId = "" - go s.subUser.InsertCSV(neId, data) - } - if strings.HasSuffix(fileHeader.Filename, ".txt") { - data := file.ReadFileTXT(",", localPath) - neId = "" - go s.subUser.InsertTxt(neId, data) - } - } - ctx.JSON(w, 200, result.OkMsg(data)) -} diff --git a/features/udm_user/model/udm_auth_user.go b/features/udm_user/model/udm_auth_user.go deleted file mode 100644 index 520d346..0000000 --- a/features/udm_user/model/udm_auth_user.go +++ /dev/null @@ -1,13 +0,0 @@ -package model - -// UdmAuthUser UDM鉴权用户 -type UdmAuthUser struct { - ID string `json:"id" xorm:"pk 'id' autoincr"` - Imsi string `json:"imsi" xorm:"imsi"` // SIM卡号 - Amf string `json:"amf" xorm:"amf"` // ANF - Status string `json:"status" xorm:"status"` // 状态 - Ki string `json:"ki" xorm:"ki"` // ki - AlgoIndex string `json:"algoIndex" xorm:"algo_index"` // - Opc string `json:"opc" xorm:"opc"` - NeID string `json:"neId" xorm:"ne_id"` // UDM网元标识-子系统 -} diff --git a/features/udm_user/model/udm_sub_user.go b/features/udm_user/model/udm_sub_user.go deleted file mode 100644 index 466fce1..0000000 --- a/features/udm_user/model/udm_sub_user.go +++ /dev/null @@ -1,29 +0,0 @@ -package model - -// UdmSubUser UDM签约用户 -type UdmSubUser struct { - ID string `json:"id" xorm:"pk 'id' autoincr"` - Msisdn string `json:"msisdn" xorm:"msisdn"` // 相当手机号 - Imsi string `json:"imsi" xorm:"imsi"` // SIM卡号 - Ambr string `json:"ambr" xorm:"ambr"` - Nssai string `json:"nssai" xorm:"nssai"` - Rat string `json:"rat" xorm:"rat"` - Arfb string `json:"arfb" xorm:"arfb"` - Sar string `json:"sar" xorm:"sar"` - Cn string `json:"cn" xorm:"cn"` - SmData string `json:"smData" xorm:"sm_data"` - SmfSel string `json:"smfSel" xorm:"smf_sel"` - EpsDat string `json:"epsDat" xorm:"eps_dat"` - NeID string `json:"neId" xorm:"ne_id"` // UDM网元标识-子系统 - - EpsFlag string `json:"epsFlag" xorm:"eps_flag"` - EpsOdb string `json:"epsOdb" xorm:"eps_odb"` - HplmnOdb string `json:"hplmnOdb" xorm:"hplmn_odb"` - Ard string `json:"ard" xorm:"ard"` - Epstpl string `json:"epstpl" xorm:"epstpl"` - ContextId string `json:"contextId" xorm:"context_id"` - ApnContext string `json:"apnContext" xorm:"apn_context"` - StaticIp string `json:"staticIp" xorm:"static_ip"` - - SubNum string `json:"subNum,omitempty" xorm:"-"` // 批量数 -} diff --git a/features/udm_user/repo/repo_udm_auth_user.go b/features/udm_user/repo/repo_udm_auth_user.go deleted file mode 100644 index 9c38877..0000000 --- a/features/udm_user/repo/repo_udm_auth_user.go +++ /dev/null @@ -1,276 +0,0 @@ -package repo - -import ( - "strconv" - "strings" - - "be.ems/features/udm_user/model" - "be.ems/lib/core/datasource" - "be.ems/lib/core/utils/parse" - "be.ems/lib/log" -) - -// 实例化数据层 RepoUdmAuthUser 结构体 -var NewRepoUdmAuthUser = &RepoUdmAuthUser{ - selectSql: `select - id, imsi, amf, status, ki, algo_index, opc, ne_id - from u_auth_user`, - - resultMap: map[string]string{ - "id": "ID", - "imsi": "Imsi", - "amf": "Amf", - "status": "Status", - "ki": "Ki", - "algo_index": "AlgoIndex", - "opc": "Opc", - "ne_id": "NeID", - }, -} - -// RepoUdmAuthUser UDM鉴权用户 数据层处理 -type RepoUdmAuthUser struct { - // 查询视图对象SQL - selectSql string - // 结果字段与实体映射 - resultMap map[string]string -} - -// convertResultRows 将结果记录转实体结果组 -func (r *RepoUdmAuthUser) convertResultRows(rows []map[string]any) []model.UdmAuthUser { - arr := make([]model.UdmAuthUser, 0) - for _, row := range rows { - UdmUser := model.UdmAuthUser{} - for key, value := range row { - if keyMapper, ok := r.resultMap[key]; ok { - datasource.SetFieldValue(&UdmUser, keyMapper, value) - } - } - arr = append(arr, UdmUser) - } - return arr -} - -// SelectPage 根据条件分页查询 -func (r *RepoUdmAuthUser) SelectPage(query map[string]any) map[string]any { - // 查询条件拼接 - var conditions []string - var params []any - if v, ok := query["imsi"]; ok && v != "" { - conditions = append(conditions, "imsi like concat(concat('%', ?), '%')") - params = append(params, v) - } - if v, ok := query["neId"]; ok && v != "" { - conditions = append(conditions, "ne_id = ?") - params = append(params, v) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - result := map[string]any{ - "total": 0, - "rows": []model.UdmAuthUser{}, - } - - // 查询数量 长度为0直接返回 - totalSql := "select count(1) as 'total' from u_auth_user" - totalRows, err := datasource.RawDB("", totalSql+whereSql, params) - if err != nil { - log.Errorf("total err => %v", err) - return result - } - total := parse.Number(totalRows[0]["total"]) - if total == 0 { - return result - } else { - result["total"] = total - } - - // 分页 - pageNum, pageSize := datasource.PageNumSize(query["pageNum"], query["pageSize"]) - pageSql := " limit ?,? " - params = append(params, pageNum*pageSize) - params = append(params, pageSize) - - // 排序 - sortSql := "" - if v, ok := query["sortField"]; ok && v != "" { - if v == "imsi" { - sortSql += " order by imsi " - } - if o, ok := query["sortOrder"]; ok && o != nil && v != "" { - if o == "desc" { - sortSql += " desc " - } else { - sortSql += " asc " - } - } - } - - // 查询数据 - querySql := r.selectSql + whereSql + sortSql + pageSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - } - - // 转换实体 - result["rows"] = r.convertResultRows(results) - return result -} - -// SelectList 根据实体查询 -func (r *RepoUdmAuthUser) SelectList(auth model.UdmAuthUser) []model.UdmAuthUser { - // 查询条件拼接 - var conditions []string - var params []any - if auth.Imsi != "" { - conditions = append(conditions, "imsi = ?") - params = append(params, auth.Imsi) - } - if auth.NeID != "" { - conditions = append(conditions, "ne_id = ?") - params = append(params, auth.NeID) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数据 - querySql := r.selectSql + whereSql + " order by imsi asc " - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - } - - // 转换实体 - return r.convertResultRows(results) -} - -// ClearAndInsert 清空ne_id后新增实体 -func (r *RepoUdmAuthUser) ClearAndInsert(neID string, authArr []model.UdmAuthUser) int64 { - var num int64 = 0 - - // 清空指定ne_id - _, err := datasource.ExecDB("", "TRUNCATE TABLE u_auth_user", nil) - // _, err := datasource.ExecDB("", "DELETE FROM u_auth_user WHERE ne_id = ?", []any{neID}) - if err != nil { - log.Errorf("TRUNCATE err => %v", err) - } - - n := len(authArr) - batchSize := 2000 - for i := 0; i < n; i += batchSize { - end := i + batchSize - if end > n { - end = n - } - batch := authArr[i:end] - - // 调用 InsertMulti 函数将批量数据插入数据库 - results, err := datasource.DefaultDB().Table("u_auth_user").InsertMulti(batch) - if err != nil { - log.Errorf("InsertMulti err => %v", err) - continue - } - num += results - } - return num -} - -// Insert 新增实体 -func (r *RepoUdmAuthUser) Insert(authUser model.UdmAuthUser) int64 { - results, err := datasource.DefaultDB().Table("u_auth_user").Insert(authUser) - if err != nil { - log.Errorf("Insert err => %v", err) - return results - } - return results -} - -// Insert 批量添加 -func (r *RepoUdmAuthUser) Inserts(authUsers []model.UdmAuthUser) int64 { - var num int64 - n := len(authUsers) - batchSize := 2000 - for i := 0; i < n; i += batchSize { - end := i + batchSize - if end > n { - end = n - } - batch := authUsers[i:end] - - // 调用 InsertMulti 函数将批量数据插入数据库 - results, err := datasource.DefaultDB().Table("u_auth_user").InsertMulti(batch) - if err != nil { - log.Errorf("Insert err => %v", err) - continue - } - num += results - } - return num -} - -// Update 修改更新 -func (r *RepoUdmAuthUser) Update(neID string, authUser model.UdmAuthUser) int64 { - // 查询先 - var user model.UdmAuthUser - has, err := datasource.DefaultDB().Table("u_auth_user").Where("imsi = ? and ne_id = ?", authUser.Imsi, neID).Get(&user) - if !has || err != nil { - return 0 - } - - if authUser.Ki != "" && authUser.Ki != user.Ki { - user.Ki = authUser.Ki - } - if authUser.Amf != "" && authUser.Amf != user.Amf { - user.Amf = authUser.Amf - } - if authUser.AlgoIndex != "" && authUser.AlgoIndex != user.AlgoIndex { - user.AlgoIndex = authUser.AlgoIndex - } - if authUser.Opc != "" && authUser.Opc != user.Opc { - user.Opc = authUser.Opc - } - - results, err := datasource.DefaultDB().Table("u_auth_user").Where("imsi = ? and ne_id = ?", user.Imsi, user.NeID).Update(user) - if err != nil { - return 0 - } - return results -} - -// Delete 删除实体 -func (r *RepoUdmAuthUser) Delete(neID, imsi string) int64 { - results, err := datasource.DefaultDB().Table("u_auth_user").Where("imsi = ? and ne_id = ?", imsi, neID).Delete() - if err != nil { - return results - } - return results -} - -// Delete 删除范围实体 -func (r *RepoUdmAuthUser) Deletes(neID, imsi, num string) int64 { - imsiV, err := strconv.Atoi(imsi) - if err != nil { - return 0 - } - - numV, err := strconv.Atoi(num) - if err != nil { - return 0 - } - - results, err := datasource.DefaultDB().Table("u_auth_user").Where("imsi >= ? and imsi < ? and ne_id = ?", imsiV, imsiV+numV, neID).Delete() - if err != nil { - return results - } - return results -} diff --git a/features/udm_user/repo/repo_udm_sub_user.go b/features/udm_user/repo/repo_udm_sub_user.go deleted file mode 100644 index 03c90a5..0000000 --- a/features/udm_user/repo/repo_udm_sub_user.go +++ /dev/null @@ -1,450 +0,0 @@ -package repo - -import ( - "fmt" - "strconv" - "strings" - - "be.ems/features/udm_user/model" - "be.ems/lib/core/datasource" - "be.ems/lib/core/utils/parse" - "be.ems/lib/log" -) - -// 实例化数据层 RepoUdmSubUser 结构体 -var NewRepoUdmSubUser = &RepoUdmSubUser{ - selectSql: `select - id, msisdn, imsi, ambr, nssai, rat, arfb, sar, cn, sm_data, smf_sel, eps_dat, ne_id, eps_flag, eps_odb, hplmn_odb, ard, epstpl, context_id, apn_context, static_ip - from u_sub_user`, - - resultMap: map[string]string{ - "id": "ID", - "msisdn": "Msisdn", - "imsi": "Imsi", - "ambr": "Ambr", - "nssai": "Nssai", - "rat": "Rat", - "arfb": "Arfb", - "sar": "Sar", - "cn": "Cn", - "sm_data": "SmData", - "smf_sel": "SmfSel", - "eps_dat": "EpsDat", - "ne_id": "NeID", - "eps_flag": "EpsFlag", - "eps_odb": "EpsOdb", - "hplmn_odb": "HplmnOdb", - "ard": "Ard", - "epstpl": "Epstpl", - "context_id": "ContextId", - "apn_context": "ApnContext", - "static_ip": "StaticIp", - }, -} - -// RepoUdmSubUser UDM签约用户 数据层处理 -type RepoUdmSubUser struct { - // 查询视图对象SQL - selectSql string - // 结果字段与实体映射 - resultMap map[string]string -} - -// convertResultRows 将结果记录转实体结果组 -func (r *RepoUdmSubUser) convertResultRows(rows []map[string]any) []model.UdmSubUser { - arr := make([]model.UdmSubUser, 0) - for _, row := range rows { - UdmUser := model.UdmSubUser{} - for key, value := range row { - if keyMapper, ok := r.resultMap[key]; ok { - datasource.SetFieldValue(&UdmUser, keyMapper, value) - } - } - arr = append(arr, UdmUser) - } - return arr -} - -// SelectPage 根据条件分页查询字典类型 -func (r *RepoUdmSubUser) SelectPage(query map[string]any) map[string]any { - // 查询条件拼接 - var conditions []string - var params []any - if v, ok := query["msisdn"]; ok && v != "" { - conditions = append(conditions, "msisdn like concat(concat('%', ?), '%')") - params = append(params, v) - } - if v, ok := query["imsi"]; ok && v != "" { - conditions = append(conditions, "imsi like concat(concat('%', ?), '%')") - params = append(params, v) - } - if v, ok := query["neId"]; ok && v != "" { - conditions = append(conditions, "ne_id = ?") - params = append(params, v) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - result := map[string]any{ - "total": 0, - "rows": []model.UdmAuthUser{}, - } - - // 查询数量 长度为0直接返回 - totalSql := "select count(1) as 'total' from u_sub_user" - totalRows, err := datasource.RawDB("", totalSql+whereSql, params) - if err != nil { - log.Errorf("total err => %v", err) - return result - } - total := parse.Number(totalRows[0]["total"]) - if total == 0 { - return result - } else { - result["total"] = total - } - - // 分页 - pageNum, pageSize := datasource.PageNumSize(query["pageNum"], query["pageSize"]) - pageSql := " limit ?,? " - params = append(params, pageNum*pageSize) - params = append(params, pageSize) - - // 排序 - sortSql := "" - if v, ok := query["sortField"]; ok && v != "" { - if v == "imsi" { - sortSql += " order by imsi " - } - if v == "msisdn" { - sortSql += " order by msisdn " - } - if o, ok := query["sortOrder"]; ok && o != nil && v != "" { - if o == "desc" { - sortSql += " desc " - } else { - sortSql += " asc " - } - } - } - - // 查询数据 - querySql := r.selectSql + whereSql + sortSql + pageSql - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - return result - } - - // 转换实体 - result["rows"] = r.convertResultRows(results) - return result -} - -// SelectList 根据实体查询 -func (r *RepoUdmSubUser) SelectList(auth model.UdmSubUser) []model.UdmSubUser { - // 查询条件拼接 - var conditions []string - var params []any - if auth.Imsi != "" { - conditions = append(conditions, "imsi = ?") - params = append(params, auth.Imsi) - } - if auth.NeID != "" { - conditions = append(conditions, "ne_id = ?") - params = append(params, auth.NeID) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } - - // 查询数据 - querySql := r.selectSql + whereSql + " order by imsi asc " - results, err := datasource.RawDB("", querySql, params) - if err != nil { - log.Errorf("query err => %v", err) - } - - // 转换实体 - return r.convertResultRows(results) -} - -// ClearAndInsert 清空ne_id后新增实体 -func (r *RepoUdmSubUser) ClearAndInsert(neID string, subArr []model.UdmSubUser) int64 { - var num int64 = 0 - - // 清空指定ne_id - _, err := datasource.ExecDB("", "TRUNCATE TABLE u_sub_user", nil) - // _, err := datasource.ExecDB("", "DELETE FROM u_sub_user WHERE ne_id = ?", []any{neID}) - if err != nil { - log.Errorf("TRUNCATE err => %v", err) - } - - n := len(subArr) - batchSize := 2000 - for i := 0; i < n; i += batchSize { - end := i + batchSize - if end > n { - end = n - } - batch := subArr[i:end] - - // 调用 InsertMulti 函数将批量数据插入数据库 - results, err := datasource.DefaultDB().Table("u_sub_user").InsertMulti(batch) - if err != nil { - log.Errorf("InsertMulti err => %v", err) - continue - } - num += results - } - - // for _, u := range subArr { - // u.NeID = neID - // results, err := datasource.DefaultDB().Table("u_sub_user").Insert(u) - // if err != nil { - // return num - // } - // num += results - // } - return num -} - -// Insert 新增实体 -func (r *RepoUdmSubUser) Insert(subUser model.UdmSubUser) int64 { - results, err := datasource.DefaultDB().Table("u_sub_user").Insert(subUser) - if err != nil { - log.Errorf("Insert err => %v", err) - return results - } - return results -} - -// Insert 批量添加 -func (r *RepoUdmSubUser) Inserts(subUser []model.UdmSubUser) int64 { - var num int64 - n := len(subUser) - batchSize := 2000 - for i := 0; i < n; i += batchSize { - end := i + batchSize - if end > n { - end = n - } - batch := subUser[i:end] - - // 调用 InsertMulti 函数将批量数据插入数据库 - results, err := datasource.DefaultDB().Table("u_sub_user").InsertMulti(batch) - if err != nil { - log.Errorf("Insert err => %v", err) - continue - } - num += results - } - return num -} - -// Insert4G 批量添加4G用户 -func (r *RepoUdmSubUser) Insert4G(neID string, subUser model.UdmSubUser) int64 { - var insertNum int64 - - imsiV, err := strconv.Atoi(subUser.Imsi) - if err != nil { - return 0 - } - numV, err := strconv.Atoi(subUser.SubNum) - if err != nil { - return 0 - } - - subUser.NeID = neID - for i := 0; i < numV; i++ { - subUser.Imsi = fmt.Sprint(imsiV + i) - - results, err := datasource.DefaultDB().Table("u_sub_user").Insert(subUser) - if err == nil { - log.Errorf("Insert err => %v", err) - insertNum += results - } - } - - return insertNum -} - -// Update 修改更新 -func (r *RepoUdmSubUser) Update(neID string, authUser model.UdmSubUser) int64 { - // 查询先 - var user model.UdmSubUser - has, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi = ? and ne_id = ?", authUser.Imsi, neID).Get(&user) - if !has || err != nil { - return 0 - } - - if authUser.Msisdn != "" && authUser.Msisdn != user.Msisdn { - user.Msisdn = authUser.Msisdn - } - if authUser.Ambr != "" && authUser.Ambr != user.Ambr { - user.Ambr = authUser.Ambr - } - if authUser.Arfb != "" && authUser.Arfb != user.Arfb { - user.Arfb = authUser.Arfb - } - if authUser.Sar != "" && authUser.Sar != user.Sar { - user.Sar = authUser.Sar - } - if authUser.Rat != "" && authUser.Rat != user.Rat { - user.Rat = authUser.Rat - } - if authUser.Cn != "" && authUser.Cn != user.Cn { - user.Cn = authUser.Cn - } - if authUser.SmfSel != "" && authUser.SmfSel != user.SmfSel { - user.SmfSel = authUser.SmfSel - } - if authUser.SmData != "" && authUser.SmData != user.SmData { - user.SmData = authUser.SmData - } - if authUser.EpsDat != "" && authUser.EpsDat != user.EpsDat { - user.EpsDat = authUser.EpsDat - } - if authUser.EpsFlag != "" && authUser.EpsFlag != user.EpsFlag { - user.EpsFlag = authUser.EpsFlag - } - if authUser.EpsOdb != "" && authUser.EpsDat != user.EpsDat { - user.EpsOdb = authUser.EpsOdb - } - if authUser.HplmnOdb != "" && authUser.HplmnOdb != user.HplmnOdb { - user.HplmnOdb = authUser.HplmnOdb - } - if authUser.Epstpl != "" && authUser.Epstpl != user.Epstpl { - user.Epstpl = authUser.Epstpl - } - if authUser.Ard != "" && authUser.Ard != user.Ard { - user.Ard = authUser.Ard - } - if authUser.ContextId != "" && authUser.ContextId != user.ContextId { - user.ContextId = authUser.ContextId - } - if authUser.ApnContext != "" && authUser.ApnContext != user.ApnContext { - user.ApnContext = authUser.ApnContext - } - if authUser.StaticIp != "" && authUser.StaticIp != user.StaticIp { - user.StaticIp = authUser.StaticIp - } - - results, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi = ? and ne_id = ?", user.Imsi, user.NeID).Update(user) - if err != nil { - log.Errorf("Update err => %v", err) - return 0 - } - return results -} - -// Update4GIP 批量修改4G IP -func (r *RepoUdmSubUser) Update4GIP(neID string, subUser model.UdmSubUser) int64 { - var insertNum int64 - - imsiV, err := strconv.Atoi(subUser.Imsi) - if err != nil || subUser.StaticIp == "" { - return insertNum - } - numV, err := strconv.Atoi(subUser.SubNum) - if err != nil { - return insertNum - } - - for i := 0; i < numV; i++ { - subUser.Imsi = fmt.Sprint(imsiV + i) - - // 查询先 - var user model.UdmSubUser - has, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi = ? and ne_id = ?", subUser.Imsi, neID).Get(&user) - if has && err == nil { - // IP会自动递增 - parts := strings.Split(subUser.StaticIp, ".") - lastPart := parts[3] - lastNum, _ := strconv.Atoi(lastPart) - lastNum += i - newLastPart := strconv.Itoa(lastNum) - parts[3] = newLastPart - newIP := strings.Join(parts, ".") - user.StaticIp = newIP - // 更新 - results, err := datasource.DefaultDB().Table("u_sub_user").Update(user) - if err == nil { - log.Errorf("Update err => %v", err) - insertNum += results - } - } - } - return insertNum -} - -// UpdateSmData 批量修改sm-data -func (r *RepoUdmSubUser) UpdateSmData(neID string, subUser model.UdmSubUser) int64 { - var insertNum int64 - - imsiV, err := strconv.Atoi(subUser.Imsi) - if err != nil || subUser.StaticIp == "" { - return insertNum - } - numV, err := strconv.Atoi(subUser.SubNum) - if err != nil { - return insertNum - } - - for i := 0; i < numV; i++ { - subUser.Imsi = fmt.Sprint(imsiV + i) - - // 查询先 - var user model.UdmSubUser - has, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi = ? and ne_id = ?", subUser.Imsi, neID).Get(&user) - if has && err == nil { - // IP会自动递增,需提前规划好DNN对应的IP;如dnn不需要绑定IP则不带此字段名 - // parts := strings.Split(subUser.SmData, "&") - user.SmData = subUser.SmData - // 更新 - results, err := datasource.DefaultDB().Table("u_sub_user").Update(user) - if err == nil { - log.Errorf("Update err => %v", err) - insertNum += results - } - } - } - return insertNum -} - -// Delete 删除实体 -func (r *RepoUdmSubUser) Delete(neID, imsi string) int64 { - results, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi = ? and ne_id = ?", imsi, neID).Delete() - if err != nil { - log.Errorf("Delete err => %v", err) - return results - } - return results -} - -// Delete 删除范围实体 -func (r *RepoUdmSubUser) Deletes(neID, imsi, num string) int64 { - imsiV, err := strconv.Atoi(imsi) - if err != nil { - return 0 - } - - numV, err := strconv.Atoi(num) - if err != nil { - return 0 - } - - results, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi >= ? and imsi < ? and ne_id = ?", imsiV, imsiV+numV, neID).Delete() - if err != nil { - log.Errorf("Delete err => %v", err) - return results - } - return results -} diff --git a/features/udm_user/service/service_redis_data.go b/features/udm_user/service/service_redis_data.go deleted file mode 100644 index 8687fbe..0000000 --- a/features/udm_user/service/service_redis_data.go +++ /dev/null @@ -1,119 +0,0 @@ -package service - -import ( - "strings" - - "be.ems/features/udm_user/model" - "be.ems/src/framework/redis" -) - -// phoneImsiList 获取所有imsi -// func phoneImsiList() map[string]string { -// phoneAndImsiArr := make(map[string]string, 0) -// phoneKeys, err := redis.GetKeys("udmuser", "1*********") -// if err != nil { -// return phoneAndImsiArr -// } -// for _, phone := range phoneKeys { -// imsi, err := redis.Get("udmuser", phone) -// if err != nil { -// continue -// } -// phoneAndImsiArr[phone] = imsi -// } -// return phoneAndImsiArr -// } - -// redisUdmAuthUserList UDM鉴权用户 -func redisUdmAuthUserList() []model.UdmAuthUser { - user := []model.UdmAuthUser{} - ausfArr, err := redis.GetKeys("udmuser", "ausf:*") - if err != nil { - return user - } - for _, key := range ausfArr { - m, err := redis.GetHash("udmuser", key) - if err != nil { - continue - } - - // 跳过-号数据 - imsi := key[5:] - if strings.Contains(imsi, "-") { - continue - } - - status := "0" - if _, ok := m["auth_success"]; ok { - status = "1" - } - amf := "" - if v, ok := m["amf"]; ok { - amf = strings.Replace(v, "\r\n", "", 1) - } - a := model.UdmAuthUser{ - Imsi: imsi, - Amf: amf, - Status: status, - Ki: m["ki"], - AlgoIndex: m["algo"], - Opc: m["opc"], - } - user = append(user, a) - } - return user -} - -// redisUdmSubUserList UDM签约用户 -func redisUdmSubUserList() []model.UdmSubUser { - user := []model.UdmSubUser{} - udmsdArr, err := redis.GetKeys("udmuser", "udm-sd:*") - if err != nil { - return user - } - for _, key := range udmsdArr { - m, err := redis.GetHash("udmuser", key) - if err != nil { - continue - } - - a := model.UdmSubUser{ - Imsi: key[7:], - Msisdn: m["gpsi"], // 46003550072 strings.TrimPrefix(m["gpsi"], "86"), - SmfSel: m["smf-sel"], - SmData: m["sm-dat"], // 1-000001&cmnet&ims&3gnet - } - - // def_ambr,def_nssai,0,def_arfb,def_sar,3,1,12000,1,1000,0,1,- - if v, ok := m["am-dat"]; ok { - arr := strings.Split(v, ",") - a.Ambr = arr[0] - a.Nssai = arr[1] - a.Rat = arr[2] - a.Arfb = arr[3] - a.Sar = arr[4] - a.Cn = arr[5] - } - // 1,64,24,65,def_eps,1,2,010200000000,- - if v, ok := m["eps-dat"]; ok { - arr := strings.Split(v, ",") - // 跳过非常规数据 - if len(arr) > 9 { - continue - } - a.EpsDat = v - a.EpsFlag = arr[0] - a.EpsOdb = arr[1] - a.HplmnOdb = arr[2] - a.Ard = arr[3] - a.Epstpl = arr[4] - a.ContextId = arr[5] - a.ApnContext = arr[7] - // [6] 是不要的,导入和导出不用 - a.StaticIp = arr[8] - } - - user = append(user, a) - } - return user -} diff --git a/features/udm_user/service/service_udm_auth_user.go b/features/udm_user/service/service_udm_auth_user.go deleted file mode 100644 index 5023f4c..0000000 --- a/features/udm_user/service/service_udm_auth_user.go +++ /dev/null @@ -1,151 +0,0 @@ -package service - -import ( - "fmt" - "strconv" - - "be.ems/features/udm_user/model" - "be.ems/features/udm_user/repo" -) - -// 实例化服务层 ServiceUdmAuthUser 结构体 -var NewServiceUdmAuthUser = &ServiceUdmAuthUser{ - repoAuthUser: *repo.NewRepoUdmAuthUser, -} - -// ServiceUdmAuthUser UDM鉴权用户 服务层处理 -type ServiceUdmAuthUser struct { - repoAuthUser repo.RepoUdmAuthUser -} - -// Save UDM鉴权用户-获取全部保存数据库 -func (r *ServiceUdmAuthUser) Save(neID string) int64 { - var num int64 = 0 - authArr := redisUdmAuthUserList() - // 有数据才清空 - if len(authArr) == 0 { - return num - } - go r.repoAuthUser.ClearAndInsert(neID, authArr) - return int64(len(authArr)) -} - -// Page UDM签约用户-分页查询数据库 -func (r *ServiceUdmAuthUser) Page(query map[string]any) map[string]any { - return r.repoAuthUser.SelectPage(query) -} - -// List UDM签约用户-查询数据库 -func (r *ServiceUdmAuthUser) List(authUser model.UdmAuthUser) []model.UdmAuthUser { - return r.repoAuthUser.SelectList(authUser) -} - -// Insert UDM鉴权用户-新增单个 -// imsi长度15,ki长度32,opc长度0或者32 -func (r *ServiceUdmAuthUser) Insert(neID string, authUser model.UdmAuthUser) int64 { - authUser.NeID = neID - authUser.Status = "0" - return r.repoAuthUser.Insert(authUser) -} - -// Insert UDM鉴权用户-批量添加 -func (r *ServiceUdmAuthUser) Inserts(neID string, authUser model.UdmAuthUser, num string) int64 { - var arr []model.UdmAuthUser - - imsiVlen := len(authUser.Imsi) - imsiV, err := strconv.Atoi(authUser.Imsi) - if err != nil { - return 0 - } - - numV, err := strconv.Atoi(num) - if err != nil { - return 0 - } - - authUser.NeID = neID - authUser.Status = "0" - for i := 0; i < numV; i++ { - imsi := fmt.Sprint(imsiV + i) - if len(imsi) < imsiVlen { - imsi = fmt.Sprintf("%0*s", imsiVlen, imsi) - } - authUser.Imsi = imsi - arr = append(arr, authUser) - } - - return r.repoAuthUser.Inserts(arr) -} - -// InsertCSV UDM鉴权用户-批量添加 -func (r *ServiceUdmAuthUser) InsertCSV(neID string, data []map[string]string) int64 { - var arr []model.UdmAuthUser - for _, v := range data { - var authUser model.UdmAuthUser - authUser.NeID = neID - authUser.Status = "0" - if s, ok := v["imsi"]; ok { - authUser.Imsi = s - } - if s, ok := v["ki"]; ok { - authUser.Ki = s - } - if s, ok := v["algo"]; ok { - authUser.AlgoIndex = s - } - if s, ok := v["amf"]; ok { - authUser.Amf = s - } - if s, ok := v["opc"]; ok { - authUser.Opc = s - } - - if authUser.Imsi == "" { - continue - } - arr = append(arr, authUser) - } - return r.repoAuthUser.Inserts(arr) -} - -// InsertTxt UDM鉴权用户-批量添加 -func (r *ServiceUdmAuthUser) InsertTxt(neID string, data [][]string) int64 { - var arr []model.UdmAuthUser - for _, v := range data { - if len(v) < 4 { - continue - } - var authUser model.UdmAuthUser - authUser.NeID = neID - authUser.Status = "0" - authUser.Imsi = v[0] - authUser.Ki = v[1] - authUser.AlgoIndex = v[2] - authUser.Amf = v[3] - if len(v) == 5 { - authUser.Opc = v[4] - } - - if authUser.Imsi == "" { - continue - } - arr = append(arr, authUser) - } - - return r.repoAuthUser.Inserts(arr) -} - -// Insert UDM鉴权用户-修改更新 -func (r *ServiceUdmAuthUser) Update(neID string, authUser model.UdmAuthUser) int64 { - return r.repoAuthUser.Update(neID, authUser) -} - -// Insert UDM鉴权用户-删除单个 -func (r *ServiceUdmAuthUser) Delete(neID, imsi string) int64 { - return r.repoAuthUser.Delete(neID, imsi) -} - -// Insert UDM鉴权用户-删除范围 -func (r *ServiceUdmAuthUser) Deletes(neID, imsi, num string) int64 { - return r.repoAuthUser.Deletes(neID, imsi, num) -} diff --git a/features/udm_user/service/service_udm_sub_user.go b/features/udm_user/service/service_udm_sub_user.go deleted file mode 100644 index 6a2d218..0000000 --- a/features/udm_user/service/service_udm_sub_user.go +++ /dev/null @@ -1,231 +0,0 @@ -package service - -import ( - "fmt" - "strconv" - "strings" - - "be.ems/features/udm_user/model" - "be.ems/features/udm_user/repo" -) - -// 实例化服务层 ServiceUdmSubUser 结构体 -var NewServiceUdmSubUser = &ServiceUdmSubUser{ - repoSunUser: *repo.NewRepoUdmSubUser, -} - -// ServiceUdmSubUser UDM签约用户 服务层处理 -type ServiceUdmSubUser struct { - repoSunUser repo.RepoUdmSubUser -} - -// Save UDM签约用户-获取全部保存数据库 -func (r *ServiceUdmSubUser) Save(neID string) int64 { - var num int64 = 0 - subArr := redisUdmSubUserList() - // 有数据才清空 - if len(subArr) == 0 { - return num - } - go r.repoSunUser.ClearAndInsert(neID, subArr) - return int64(len(subArr)) -} - -// Page UDM签约用户-分页查询数据库 -func (r *ServiceUdmSubUser) Page(query map[string]any) map[string]any { - return r.repoSunUser.SelectPage(query) -} - -// List UDM签约用户-查询数据库 -func (r *ServiceUdmSubUser) List(subUser model.UdmSubUser) []model.UdmSubUser { - return r.repoSunUser.SelectList(subUser) -} - -// Insert UDM签约用户-新增单个 -// imsi长度15,ki长度32,opc长度0或者32 -func (r *ServiceUdmSubUser) Insert(neID string, subUser model.UdmSubUser) int64 { - subUser.NeID = neID - return r.repoSunUser.Insert(subUser) -} - -// Insert UDM签约用户-批量添加 -func (r *ServiceUdmSubUser) Inserts(neID string, subUser model.UdmSubUser, num string) int64 { - var arr []model.UdmSubUser - - imsiVlen := len(subUser.Imsi) - imsiV, err := strconv.Atoi(subUser.Imsi) - if err != nil { - return 0 - } - msisdnVlen := len(subUser.Msisdn) - msisdnV, err := strconv.Atoi(subUser.Msisdn) - if err != nil { - return 0 - } - - numV, err := strconv.Atoi(num) - if err != nil { - return 0 - } - - subUser.NeID = neID - for i := 0; i < numV; i++ { - msisdn := fmt.Sprint(msisdnV + i) - if len(msisdn) < msisdnVlen { - msisdn = fmt.Sprintf("%0*s", msisdnVlen, msisdn) - } - subUser.Msisdn = msisdn - - imsi := fmt.Sprint(imsiV + i) - if len(imsi) < imsiVlen { - imsi = fmt.Sprintf("%0*s", imsiVlen, imsi) - } - subUser.Imsi = imsi - - // IP会自动递增 - if len(subUser.StaticIp) > 5 { - parts := strings.Split(subUser.StaticIp, ".") - lastPart := parts[3] - lastNum, _ := strconv.Atoi(lastPart) - lastNum += i - newLastPart := strconv.Itoa(lastNum) - parts[3] = newLastPart - newIP := strings.Join(parts, ".") - subUser.StaticIp = newIP - } - - subUser.EpsDat = fmt.Sprintf("%s,%s,%s,%s,%s,%s,2,%s,%s", subUser.EpsFlag, subUser.EpsOdb, subUser.HplmnOdb, subUser.Ard, subUser.Epstpl, subUser.ContextId, subUser.ApnContext, subUser.StaticIp) - - arr = append(arr, subUser) - } - - return r.repoSunUser.Inserts(arr) -} - -// InsertCSV UDM签约用户-批量添加 -func (r *ServiceUdmSubUser) InsertCSV(neID string, data []map[string]string) int64 { - var arr []model.UdmSubUser - for _, v := range data { - var subUser model.UdmSubUser - subUser.NeID = neID - if s, ok := v["imsi"]; ok { - subUser.Imsi = s - } - if s, ok := v["msisdn"]; ok { - subUser.Msisdn = s - } - if s, ok := v["ambr"]; ok { - subUser.Ambr = s - } - if s, ok := v["nssai"]; ok { - subUser.Nssai = s - } - if s, ok := v["arfb"]; ok { - subUser.Arfb = s - } - if s, ok := v["sar"]; ok { - subUser.Sar = s - } - if s, ok := v["rat"]; ok { - subUser.Rat = s - } - if s, ok := v["cn"]; ok { - subUser.Cn = s - } - if s, ok := v["smf_sel"]; ok { - subUser.SmfSel = s - } - if s, ok := v["sm_dat"]; ok { - subUser.SmData = s - } - if s, ok := v["eps_dat"]; ok { - subUser.EpsDat = s - arr := strings.Split(s, ",") - if len(arr) == 8 { - subUser.EpsFlag = arr[0] - subUser.EpsOdb = arr[1] - subUser.HplmnOdb = arr[2] - subUser.Ard = arr[3] - subUser.Epstpl = arr[4] - subUser.ContextId = arr[5] - subUser.ApnContext = arr[6] - subUser.StaticIp = arr[7] - } - } - - if subUser.Imsi == "" { - continue - } - arr = append(arr, subUser) - } - return r.repoSunUser.Inserts(arr) -} - -// InsertTxt UDM签约用户-批量添加 -func (r *ServiceUdmSubUser) InsertTxt(neID string, data [][]string) int64 { - var arr []model.UdmSubUser - for _, v := range data { - if len(v) < 10 { - continue - } - var subUser model.UdmSubUser - subUser.NeID = neID - subUser.Imsi = v[0] - subUser.Msisdn = v[1] - subUser.Ambr = v[2] - subUser.Nssai = v[3] - subUser.Arfb = v[4] - subUser.Sar = v[5] - subUser.Rat = v[6] - subUser.Cn = v[7] - subUser.SmfSel = v[8] - subUser.SmData = v[9] - if len(v) > 17 { - subUser.EpsFlag = v[10] - subUser.EpsOdb = v[11] - subUser.HplmnOdb = v[12] - subUser.Ard = v[13] - subUser.Epstpl = v[14] - subUser.ContextId = v[15] - subUser.ApnContext = v[16] - subUser.StaticIp = v[17] - subUser.EpsDat = strings.Join(v[10:], ",") - } - - if subUser.Imsi == "" { - continue - } - arr = append(arr, subUser) - } - return r.repoSunUser.Inserts(arr) -} - -// Insert UDM签约用户-批量添加4G用户 -func (r *ServiceUdmSubUser) Insert4G(neID string, subUser model.UdmSubUser) int64 { - return r.repoSunUser.Insert4G(neID, subUser) -} - -// Insert UDM签约用户-修改更新 -func (r *ServiceUdmSubUser) Update(neID string, subUser model.UdmSubUser) int64 { - return r.repoSunUser.Update(neID, subUser) -} - -// Update4GIP UDM签约用户-批量修改4G IP -func (r *ServiceUdmSubUser) Update4GIP(neID string, subUser model.UdmSubUser) int64 { - return r.repoSunUser.Update4GIP(neID, subUser) -} - -// Update4GIP UDM签约用户-批量修改sm-data -func (r *ServiceUdmSubUser) UpdateSmData(neID string, subUser model.UdmSubUser) int64 { - return r.repoSunUser.UpdateSmData(neID, subUser) -} - -// Insert UDM签约用户-删除单个 -func (r *ServiceUdmSubUser) Delete(neID, imsi string) int64 { - return r.repoSunUser.Delete(neID, imsi) -} - -// Insert UDM签约用户-删除范围 -func (r *ServiceUdmSubUser) Deletes(neID, imsi, num string) int64 { - return r.repoSunUser.Deletes(neID, imsi, num) -} diff --git a/lib/core/account/account.go b/lib/core/account/account.go deleted file mode 100644 index 81d5853..0000000 --- a/lib/core/account/account.go +++ /dev/null @@ -1,54 +0,0 @@ -package account - -import ( - "fmt" - "strconv" - "time" - - sysMenuService "be.ems/features/sys_menu/service" - sysRoleService "be.ems/features/sys_role/service" - "be.ems/lib/core/cache" - "be.ems/lib/core/vo" - "be.ems/lib/dborm" - srcConfig "be.ems/src/framework/config" -) - -// 登录缓存用户信息 -func CacheLoginUser(user *dborm.User) { - // 过期时间 - expiresStr, err := dborm.XormGetConfigValue("Security", "sessionExpires") - if err != nil { - expiresStr = "18000" - } - expiresValue, _ := strconv.Atoi(expiresStr) - expireTime := time.Duration(expiresValue) * time.Second - - nowTime := time.Now().UnixMilli() - - // 登录用户 - loginUser := vo.LoginUser{ - UserID: fmt.Sprint(user.Id), - UserName: user.Name, - ExpireTime: nowTime + expireTime.Milliseconds(), - LoginTime: nowTime, - User: *user, - } - - // 是否管理员 - if srcConfig.IsAdmin(loginUser.UserID) { - loginUser.Permissions = []string{"*:*:*"} - } else { - // 获取权限标识 - loginUser.Permissions = sysMenuService.NewRepoSysMenu.SelectMenuPermsByUserId(loginUser.UserID) - // 获取角色信息 - loginUser.User.Roles = sysRoleService.NewRepoSysRole.SelectRoleListByUserId(loginUser.UserID) - } - - // 缓存时间 - cache.SetLocalTTL(user.AccountId, loginUser, time.Duration(expireTime)) -} - -// 清除缓存用户信息 -func ClearLoginUser(accountId string) { - cache.DeleteLocalTTL(accountId) -} diff --git a/lib/dborm/dborm.go b/lib/dborm/dborm.go index 7455aea..aa00829 100644 --- a/lib/dborm/dborm.go +++ b/lib/dborm/dborm.go @@ -10,9 +10,9 @@ import ( "strings" - "be.ems/features/sys_role/model" "be.ems/lib/log" "be.ems/lib/oauth" + "be.ems/src/modules/system/model" _ "github.com/go-sql-driver/mysql" "xorm.io/xorm" @@ -1402,11 +1402,11 @@ type MeasureThreshold struct { type NeSoftware struct { Id int `json:"id" xorm:"pk 'id' autoincr"` NeType string `json:"neType" xorm:"ne_type"` - FileName string `json:"neName" xorm:"file_name"` - Path string `json:"path"` - Version string `json:"version"` - Md5Sum string `json:"md5Sum" xorm:"md5_sum"` - Comment string `json:"comment"` + FileName string `json:"fileName" xorm:"name"` + Path string `json:"path" xorm:"path"` + Version string `json:"version" ` + Md5Sum string `json:"md5Sum" xorm:"-"` + Comment string `json:"comment" xorm:"description"` // Status string `json:"status"` UpdateTime string `json:"createTime" xorm:"-" ` } @@ -1415,18 +1415,18 @@ type NeVersion struct { Id int `json:"id" xorm:"pk 'id' autoincr"` NeType string `json:"neType" xorm:"ne_type"` NeId string `json:"neId" xorm:"ne_id"` - Version string `json:"version"` - FilePath string `json:"filePath" xorm:"file_path"` + Version string `json:"version" ` + FilePath string `json:"filePath" xorm:"path"` NewVersion string `json:"newVersion" xorm:"new_version"` - NewFile string `json:"newFile" xorm:"new_file"` + NewFile string `json:"newFile" xorm:"new_path"` PreVersion string `json:"preVersion" xorm:"pre_version"` - PreFile string `json:"preFile" xorm:"pre_file"` - Status string `json:"status"` + PreFile string `json:"preFile" xorm:"pre_path"` + Status string `json:"status" xorm:"status"` UpdateTime string `json:"createTime" xorm:"-" ` } func XormGetDataBySQL(sql string) (*[]map[string]string, error) { - //log.Debug("XormGetDataBySQL processing... ") + log.Debug("XormGetDataBySQL processing... ") rows := make([]map[string]string, 0) rows, err := DbClient.XEngine.QueryString(sql) @@ -1708,25 +1708,26 @@ func IsPermissionAllowed(token, method, module, dbname, tbname, pack string) (bo type NeLicense struct { NeType string `json:"neType" xorm:"ne_type"` NeID string `json:"neID" xorm:"ne_id"` - SerialNo string `json:"serialNo" xorm:"serial_no"` - Capcity int `json:"capcity" xorm:"capcity"` - Used int `json:"used" xorm:"used"` - FeatureEnabled string `json:"featureEnabled" xorm:"feature_enabled"` - ExpirationDate string `json:"expirationDate" xorm:"expiration_date"` + SerialNo string `json:"serialNo" xorm:"serial_num"` + Capcity int `json:"capcity" xorm:"-"` + Used int `json:"used" xorm:"-"` + FeatureEnabled string `json:"featureEnabled" xorm:"-"` + ExpirationDate string `json:"expirationDate" xorm:"expiry_date"` Status string `json:"status" xorm:"status"` - Path string `json:"path" xorm:"path"` - FileName string `json:"file_name" xorm:"file_name"` - Comment string `json:"comment" xorm:"comment"` + Path string `json:"path" xorm:"license_path"` + FileName string `json:"file_name" xorm:"-"` + Comment string `json:"comment" xorm:"remark"` CreatedAt string `json:"createdAt" xorm:"-"` UpdatedAt string `json:"updatedAt" xorm:"-"` DeletedAt string `json:"deletedAt" xorm:"-"` } func XormAdjustmentNeLicense(neType, neID string, value int) (int64, error) { + return 1, nil //neLicense := NeLicense{NeType: neType, NeID: neID, Capability: value} // session.LogoutTime.Valid = true // session.LogoutTime.Time = time.Now() - res, err := xEngine.Exec("update ne_license set capcity=capcity+? where IFNULL(ne_type, '')=? and IFNULL(ne_id, '')=?", value, neType, neID) + // res, err := xEngine.Exec("update ne_license set capcity=capcity+? where IFNULL(ne_type, '')=? and IFNULL(ne_id, '')=?", value, neType, neID) // defer xSession.Close() //affected, err := xSession.Table("ne_license").Where("ne_type=? and ne_id=?", neType, neID).Update(&neLicense) @@ -1734,25 +1735,26 @@ func XormAdjustmentNeLicense(neType, neID string, value int) (int64, error) { // //affected, err := xSession.Table("ne_license").SQL("ne_tye=? and ne_id=?", neType, neID).Update(session) // err := xSession.SQL("update ne_license set capability=capability+? where ne_type=? and ne_id=?", value, neType, neID) //xSession.Commit() - affected, err := res.RowsAffected() - return affected, err + // affected, err := res.RowsAffected() + // return affected, err } func XormUpdateNeLicense(neType, neID string, capcity int) (int64, error) { - var err error - var res sql.Result - if neType != "" && neID != "" { - res, err = xEngine.Exec("update ne_license set capcity=? where ne_type=? and ne_id=?", capcity, neType, neID) - } else if neType != "" && neID == "" { - res, err = xEngine.Exec("update ne_license set capcity=? where ne_type=?", capcity, neType) - } else if neType == "" && neID != "" { - res, err = xEngine.Exec("update ne_license set capcity=? where ne_id=?", capcity, neID) - } else { - res, err = xEngine.Exec("update ne_license set capcity=?", capcity) - } + return 1, nil + // var err error + // var res sql.Result + // if neType != "" && neID != "" { + // res, err = xEngine.Exec("update ne_license set capcity=? where ne_type=? and ne_id=?", capcity, neType, neID) + // } else if neType != "" && neID == "" { + // res, err = xEngine.Exec("update ne_license set capcity=? where ne_type=?", capcity, neType) + // } else if neType == "" && neID != "" { + // res, err = xEngine.Exec("update ne_license set capcity=? where ne_id=?", capcity, neID) + // } else { + // res, err = xEngine.Exec("update ne_license set capcity=?", capcity) + // } - affected, err := res.RowsAffected() - return affected, err + // affected, err := res.RowsAffected() + // return affected, err } type NorthboundCm struct { @@ -1777,21 +1779,21 @@ func XormGetNorthboundCm(neType string, cmResults *[]NorthboundCm) error { log.Info("XormGetNorthboundCm processing... ") cmResult := new(NorthboundCm) - rows, err := xEngine.Table("northbound_cm"). + rows, err := xEngine.Table("nbi_cm"). Distinct("object_type"). Where("`ne_type` = ?", neType). Desc("timestamp"). Cols("*"). Rows(cmResult) if err != nil { - log.Error("Failed to get table northbound_cm:", err) + log.Error("Failed to get table nbi_cm:", err) return err } defer rows.Close() for rows.Next() { err := rows.Scan(cmResult) if err != nil { - log.Error("Failed to get table northbound_cm:", err) + log.Error("Failed to get table nbi_cm:", err) return err } *cmResults = append(*cmResults, *cmResult) @@ -1803,14 +1805,14 @@ func XormGetNorthboundCmLatestObject(neType, neID, objectType string) (*Northbou log.Info("XormGetNorthboundCmLatestObject processing... ") cmResult := new(NorthboundCm) - _, err := xEngine.Table("northbound_cm"). + _, err := xEngine.Table("nbi_cm"). Where("`ne_type`=? and `ne_id`=? and `object_type`=?", neType, neID, objectType). Desc("timestamp"). Cols("*"). Limit(1). Get(cmResult) if err != nil { - log.Error("Failed to get table northbound_cm:", err) + log.Error("Failed to get table nbi_cm:", err) return nil, err } diff --git a/lib/global/kits.go b/lib/global/kits.go index 593f18c..22091f0 100644 --- a/lib/global/kits.go +++ b/lib/global/kits.go @@ -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 +} diff --git a/lib/midware/arrow_ip_addr.go b/lib/midware/arrow_ip_addr.go index 98c2946..c9c7a32 100644 --- a/lib/midware/arrow_ip_addr.go +++ b/lib/midware/arrow_ip_addr.go @@ -10,6 +10,7 @@ import ( "be.ems/lib/services" ) +// 已禁用 // 登录策略限制登录时间和访问ip范围 func ArrowIPAddr(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/lib/midware/authorize.go b/lib/midware/authorize.go index 152709b..b56e9c3 100644 --- a/lib/midware/authorize.go +++ b/lib/midware/authorize.go @@ -14,6 +14,7 @@ import ( tokenUtils "be.ems/src/framework/utils/token" ) +// 已禁用由Gin部分接管 // Authorize 用户身份授权认证校验 // // 只需含有其中角色 "hasRoles": {"xxx"}, diff --git a/lib/midware/cors.go b/lib/midware/cors.go index b0a73ad..a924369 100644 --- a/lib/midware/cors.go +++ b/lib/midware/cors.go @@ -5,6 +5,7 @@ import ( "strings" ) +// 已禁用由Gin部分接管 // Cors 跨域 func Cors(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/lib/midware/operate_log.go b/lib/midware/operate_log.go index 6b00eb0..d8bc396 100644 --- a/lib/midware/operate_log.go +++ b/lib/midware/operate_log.go @@ -11,8 +11,8 @@ import ( "strings" "time" + "be.ems/lib/core/utils/ctx" "be.ems/lib/core/utils/parse" - "be.ems/lib/dborm" "be.ems/src/framework/constants/common" "be.ems/src/framework/middleware/collectlogs" "be.ems/src/framework/utils/ip2region" @@ -47,13 +47,7 @@ func LogOperate(options collectlogs.Options) func(http.Handler) http.Handler { funcName = funcName[lastDotIndex+1:] // 用户名 - username := "-" - accessToken := r.Header.Get("AccessToken") - if accessToken != "" { - // 验证令牌 == 这里直接查数据库session - se, _ := dborm.XormUpdateSessionShakeTime(accessToken) - username = se.AccountId - } + username := ctx.LoginUserToUserName(r) // 解析ip地址 ip := strings.Split(r.RemoteAddr, ":")[0] diff --git a/lib/routes/routes.go b/lib/routes/routes.go index bf95fe7..baddb75 100644 --- a/lib/routes/routes.go +++ b/lib/routes/routes.go @@ -13,21 +13,12 @@ import ( "be.ems/features/fm" "be.ems/features/lm" "be.ems/features/mml" - "be.ems/features/monitor/monitor" - "be.ems/features/monitor/psnet" "be.ems/features/nbi" "be.ems/features/pm" "be.ems/features/security" "be.ems/features/sm" "be.ems/features/state" - sysconfig "be.ems/features/sys_config" - sysdictdata "be.ems/features/sys_dict_data" - sysdicttype "be.ems/features/sys_dict_type" - sysmenu "be.ems/features/sys_menu" - sysrole "be.ems/features/sys_role" - sysuser "be.ems/features/sys_user" "be.ems/features/trace" - udmuser "be.ems/features/udm_user" "be.ems/features/ue" "be.ems/lib/midware" "be.ems/lib/services" @@ -233,16 +224,6 @@ func init() { Register("PUT", trace.CustomUriTraceTask, trace.PutTraceTaskToNF, nil) Register("DELETE", trace.CustomUriTraceTask, trace.DeleteTraceTaskToNF, nil) - // 网元发送执行 pcap抓包任务 - Register("POST", trace.UriTcpdumpTask, trace.TcpdumpNeTask, midware.LogOperate(collectlogs.OptionNew("Packet Capturing Task", collectlogs.BUSINESS_TYPE_INSERT))) - Register("POST", trace.CustomUriTcpdumpTask, trace.TcpdumpNeTask, midware.LogOperate(collectlogs.OptionNew("Packet Capturing Task", collectlogs.BUSINESS_TYPE_INSERT))) - // 网元发送执行 抓包下载pcap文件 - Register("POST", trace.UriTcpdumpPcapDownload, trace.TcpdumpPcapDownload, midware.LogOperate(collectlogs.OptionNew("Packet Capturing Task", collectlogs.BUSINESS_TYPE_EXPORT))) - Register("POST", trace.CustomUriTcpdumpPcapDownload, trace.TcpdumpPcapDownload, midware.LogOperate(collectlogs.OptionNew("Packet Capturing Task", collectlogs.BUSINESS_TYPE_EXPORT))) - // 网元发送执行UPF pcap抓包 - Register("POST", trace.UriTcpdumpNeUPFTask, trace.TcpdumpNeUPFTask, midware.LogOperate(collectlogs.OptionNew("Packet Capturing Task", collectlogs.BUSINESS_TYPE_INSERT))) - Register("POST", trace.CustomUriTcpdumpNeUPFTask, trace.TcpdumpNeUPFTask, midware.LogOperate(collectlogs.OptionNew("Packet Capturing Task", collectlogs.BUSINESS_TYPE_INSERT))) - // file management Register("POST", file.UriFile, file.UploadFile, midware.LogOperate(collectlogs.OptionNew("File", collectlogs.BUSINESS_TYPE_INSERT))) Register("GET", file.UriFile, file.DownloadFile, midware.LogOperate(collectlogs.OptionNew("File", collectlogs.BUSINESS_TYPE_OTHER))) @@ -300,23 +281,17 @@ 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) - // UE event + // smf cdr event + Register("POST", cdr.UriSMFCDREvent, cdr.PostCDREventFromSMF, nil) + Register("POST", cdr.CustomUriSMFCDREvent, cdr.PostCDREventFromSMF, nil) + + // UE event AMF上报的UE事件, 无前缀给到Gin处理 //Register("POST", event.UriUEEvent, event.PostUEEventFromAMF, nil) - // 进程网络 - Register("GET", psnet.UriWs, psnet.ProcessWs, nil) - Register("POST", psnet.UriStop, psnet.StopProcess, nil) - Register("POST", psnet.UriPing, psnet.Ping, nil) - - // 主机CPU内存监控 - Register("POST", monitor.UriLoad, monitor.LoadMonitor, nil) - Register("GET", monitor.UriNetOpt, monitor.Netoptions, nil) - Register("GET", monitor.UriIPAddr, monitor.IPAddr, nil) - Register("GET", monitor.UriIoOpt, monitor.Iooptions, nil) - // 文件资源 Register("GET", file.UriDiskList, file.DiskList, nil) Register("POST", file.UriListFiles, file.ListFiles, nil) @@ -337,56 +312,6 @@ func init() { Register("POST", lm.ExtBackupDataUri, lm.ExtDatabaseBackupData, nil) Register("POST", lm.CustomExtBackupDataUri, lm.ExtDatabaseBackupData, nil) - // 系统登录 - Register("POST", security.UriLogin, security.LoginOMC, nil) - Register("POST", security.CustomUriLogin, security.LoginOMC, nil) - - // 获取验证码 - Register("GET", security.UriCaptchaImage, security.CaptchaImage, nil) - Register("GET", security.CustomUriCaptchaImage, security.CaptchaImage, nil) - - // 登录用户信息 - Register("GET", security.UriUserInfo, security.UserInfo, midware.Authorize(nil)) - Register("GET", security.CustomUriUserInfo, security.UserInfo, midware.Authorize(nil)) - - // 登录用户路由信息 - Register("GET", security.UriRouters, security.Routers, midware.Authorize(nil)) - Register("GET", security.CustomUriRouters, security.Routers, midware.Authorize(nil)) - - // 参数配置信息接口添加到路由 - for _, v := range sysconfig.Routers() { - Register(v.Method, v.Pattern, v.Handler, v.Middleware) - } - - // 字典类型信息接口添加到路由 - for _, v := range sysdicttype.Routers() { - Register(v.Method, v.Pattern, v.Handler, v.Middleware) - } - - // 字典类型对应的字典数据信息接口添加到路由 - for _, v := range sysdictdata.Routers() { - Register(v.Method, v.Pattern, v.Handler, v.Middleware) - } - - // 菜单接口添加到路由 - for _, v := range sysmenu.Routers() { - Register(v.Method, v.Pattern, v.Handler, v.Middleware) - } - - // 角色接口添加到路由 - for _, v := range sysrole.Routers() { - Register(v.Method, v.Pattern, v.Handler, v.Middleware) - } - - // 用户接口添加到路由 - for _, v := range sysuser.Routers() { - Register(v.Method, v.Pattern, v.Handler, v.Middleware) - } - - // UDM 用户信息接口添加到路由 - for _, v := range udmuser.Routers() { - Register(v.Method, v.Pattern, v.Handler, v.Middleware) - } } // To resolv rest POST/PUT/DELETE/PATCH cross domain diff --git a/restagent/config/config.go b/restagent/config/config.go index 1dd3ce9..181cbef 100644 --- a/restagent/config/config.go +++ b/restagent/config/config.go @@ -20,6 +20,11 @@ type YamlConfig struct { Count int `yaml:"count"` } `yaml:"logger"` + Pprof struct { + Enabled bool `yaml:"enabled"` + Addr string `yaml:"addr"` + } `yaml:"pprof"` + // Rest []struct { // IPv4 string `yaml:"ipv4"` // IPv6 string `yaml:"ipv6"` @@ -76,30 +81,7 @@ type YamlConfig struct { CmdTimeout int `yaml:"cmdTimeout"` } `yaml:"omc"` - Alarm struct { - ForwardAlarm bool `yaml:"forwardAlarm"` - SMProxy string `yaml:"smProxy"` - Email struct { - Smtp string `yaml:"smtp"` - Port uint16 `yaml:"port"` - User string `yaml:"user"` - Password string `yaml:"password"` - TlsSkipVerify bool `yaml:"tlsSkipVerify"` - } `yaml:"email"` - SMS struct { - ApiURL string `yaml:"apiURL"` - AccessKeyID string `yaml:"AccessKeyID"` - AccessKeySecret string `yaml:"accessKeySecret"` - SignName string `yaml:"signName"` - TemplateCode string `yaml:"templateCode"` - } `yaml:"sms"` - SMSC struct { - Addr string `yaml:"addr"` - SystemID string `yaml:"systemID"` - Password string `yaml:"password"` - SystemType string `yaml:"systemType"` - } `yaml:"smsc"` - } `yaml:"alarm"` + Alarm AlarmConfig `yaml:"alarm"` MML MMLParam `yaml:"mml"` @@ -164,6 +146,32 @@ type DbConfig struct { Backup string `yaml:"backup"` } +type AlarmConfig struct { + SplitEventAlarm bool `yaml:"splitEventAlarm"` + ForwardAlarm bool `yaml:"forwardAlarm"` + SMProxy string `yaml:"smProxy"` + Email struct { + Smtp string `yaml:"smtp"` + Port uint16 `yaml:"port"` + User string `yaml:"user"` + Password string `yaml:"password"` + TlsSkipVerify bool `yaml:"tlsSkipVerify"` + } `yaml:"email"` + SMS struct { + ApiURL string `yaml:"apiURL"` + AccessKeyID string `yaml:"AccessKeyID"` + AccessKeySecret string `yaml:"accessKeySecret"` + SignName string `yaml:"signName"` + TemplateCode string `yaml:"templateCode"` + } `yaml:"sms"` + SMSC struct { + Addr string `yaml:"addr"` + SystemID string `yaml:"systemID"` + Password string `yaml:"password"` + SystemType string `yaml:"systemType"` + } `yaml:"smsc"` +} + type MMLParam struct { Port int `yaml:"port"` Port2 int `yaml:"port2"` @@ -175,7 +183,6 @@ type MMLParam struct { User string `yaml:"user"` Password string `ymal:"password"` MmlHome string `yaml:"mmlHome"` - Upload string `yaml:"upload"` } type TestDatas struct { @@ -223,6 +230,9 @@ func NewYamlConfig() YamlConfig { SizeCol: 128, BufferSize: 65535, }, + Alarm: AlarmConfig{ + SplitEventAlarm: true, + }, } } diff --git a/restagent/etc/restconf-t.yaml b/restagent/etc/restconf-t.yaml index ff7f7b6..d3323c6 100644 --- a/restagent/etc/restconf-t.yaml +++ b/restagent/etc/restconf-t.yaml @@ -3,20 +3,40 @@ # duration: rotation time with xx hours, example: 1/12/24 hours # count: rotation count of log, default is 30 rotation logger: - file: d:/local.git/be.ems/restagent/log/restagent-t.log + file: d:/local.git/be.ems/restagent/log/restagent.log level: trace duration: 24 count: 2 # rest agent listen ipv4/v6 and port, support multiple routines # ip: 0.0.0.0 or ::0, support IPv4/v6 +# clientAuthType: 0:NoClientCert (default), 1:RequestClientCert, 2:RequireAnyClientCert, +# 3:VerifyClientCertIfGiven, 4:RequireAndVerifyClientCerts rest: - ipv4: 0.0.0.0 ipv6: port: 33030 - ipv4: 0.0.0.0 ipv6: - port: 36060 + port: 33443 + scheme: https + clientAuthType: 0 + caFile: ./etc/certs/omc-ca.crt + certFile: ./etc/certs/omc-server.crt + keyFile: ./etc/certs/omc-server.key + +webServer: + enabled: false + rootDir: d:/local.git/fe.ems.vue3/dist + listen: + - addr: :80 + schema: http + - addr: :443 + scheme: https + clientAuthType: 0 + caFile: ./etc/certs/omc-ca.crt + certFile: ./etc/certs/omc-server.crt + keyFile: ./etc/certs/omc-server.key database: type: mysql @@ -25,6 +45,7 @@ database: host: 127.0.0.1 port: 33066 name: omc_db + connParam: charset=utf8mb4&collation=utf8mb4_general_ci&parseTime=True&interpolateParams=True backup: d:/local.git/be.ems/restagent/database # Redis 缓存数据,数据源声明全小写 @@ -33,25 +54,34 @@ redis: # OMC系统使用库 default: port: 6379 # Redis port - host: "192.168.2.166" # Redis host - password: "" + host: "192.168.2.219" # Redis host + password: "123456" db: 10 # Redis db_num # UDM网元用户库 udmuser: port: 6379 # Redis port - host: "192.168.2.166" - password: "" + host: "192.168.2.219" + password: "123456" db: 0 # Redis db_num # 多个数据源时可以用这个指定默认的数据源 defaultDataSourceName: "default" - + +# sleep: time delay for after write buffer (millisecond) +# deadLine: timeout for io read and write (second) mml: port: 4100 + port2: 5002 sleep: 200 + deadLine: 10 + sizeRow: 100 + sizeCol: 128 + bufferSize: 65535 user: admin password: admin mmlHome: ./mmlhome + upload: /home/manager +# NE config ne: user: root etcdir: /usr/local/etc @@ -59,6 +89,13 @@ ne: omcdir: /usr/local/omc scpdir: /tmp licensedir: /usr/local/etc/{neType}/license + # backup etc list of IMS,no space + etcListIMS: '{*.yaml,mmtel,vars.cfg}' + etcListDefault: '{*.yaml,*.conf,*.cfg}' + # true/false to overwrite config file when dpkg ne software + dpkgOverwrite: false + # dpkg timeout (second) + dpkgTimeout: 180 # chk2ne: true/false, if put OmcNeConfig parameters to NE omc: @@ -71,42 +108,55 @@ omc: vendor: "" dn: 4600 chk2ne: false - sn: 13750650 + sn: "-" checksign: false + rootDir: ./ + binDir: ./bin backup: ./backup upload: ./upload frontUpload: d:/local.git/fe.ems/upload frontTraceDir: d:/local.git/fe.ems/trace software: ./software license: ./license - gtpUri: gtp:192.168.2.119:2152 + gtpUri: gtp:192.168.2.219:2152 checkContentType: false testMode: false rbacMode: true runDir: + cmdTimeout: 120 # Alarm module setting # Forward interface: -# email/sms +# email/sms +# smProxy: sms(Short Message Service)/smsc(SMS Centre) alarm: forwardAlarm: true email: - smtp: smtp@xxx.com.cn + smtp: mail.agrandtech.com port: 25 - user: smtpuser - password: smtpuser@omc + user: smtpext@agrandtech.com + password: "1000smtp@omc!" + # TLS skip verify: true/false + tlsSkipVerify: true + smProxy: smsc sms: - apiURL: http://smsc.xxx.com.cn/ + apiURL: http://smsc.xxx.com/ accessKeyID: xxxx accessKeySecret: xxxx signName: xxx SMSC templateCode: 1000 - + smsc: + addr: "192.168.13.114:2775" + systemID: "omc" + password: "omc123" + systemType: "UTRAN" + #User authorized information # crypt: mysql/md5/bcrypt # token: true/false to check accessToken # expires for session, unit: second # Support single/multiple session of user +# auth: crypt: bcrypt token: true @@ -129,6 +179,18 @@ params: subidmaxnum: 20 urimaxlen: 2100000 rmuidregexp: "[0-9]{4}[A-Z]{2}[A-Z]{2}[0-9A-Z]{1}[0-9A-Z]{3}[0-9A-Z]{1,16}" + testConfig: enabled: true - file: ./etc/testconfig.yaml \ No newline at end of file + file: ./etc/testconfig.yaml + +# 静态文件配置, 相对项目根路径或填绝对路径 +staticFile: + # 默认资源,dir目录需要预先创建 + default: + prefix: "/static" + dir: "./static" + # 文件上传资源目录映射,与项目目录同级 + upload: + prefix: "/upload" + dir: "./upload" diff --git a/restagent/etc/restconf.yaml b/restagent/etc/restconf.yaml index f2fa6a2..9fb9aea 100644 --- a/restagent/etc/restconf.yaml +++ b/restagent/etc/restconf.yaml @@ -2,11 +2,16 @@ # level: /trace/debug/info/warn/error/fatal, default: debug # duration: rotation time with xx hours, example: 1/12/24 hours # count: rotation count of log, default is 30 rotation +# pprof: false(default)/true to disable/enable pprof logger: file: d:/local.git/be.ems/restagent/log/restagent.log level: trace duration: 24 - count: 2 + count: 2 + +pprof: + enabled: true + addr: :36060 # rest agent listen ipv4/v6 and port, support multiple routines # ip: 0.0.0.0 or ::0, support IPv4/v6 @@ -16,9 +21,17 @@ rest: - ipv4: 0.0.0.0 ipv6: port: 33040 + - ipv4: 0.0.0.0 + ipv6: + port: 34443 + scheme: https + clientAuthType: 0 + caFile: ./etc/certs/omc-ca.crt + certFile: ./etc/certs/omc-server.crt + keyFile: ./etc/certs/omc-server.key webServer: - enabled: false + enabled: true rootDir: d:/local.git/fe.ems.vue3/dist listen: - addr: :80 @@ -33,10 +46,10 @@ webServer: database: type: mysql user: root - password: "root@1234" - host: "192.168.5.59" - port: 3306 - name: "omc_db" + password: 1000omc@kp! + host: 127.0.0.1 + port: 33066 + name: omc_db connParam: charset=utf8mb4&collation=utf8mb4_general_ci&parseTime=True&interpolateParams=True backup: d:/local.git/be.ems/restagent/database @@ -46,14 +59,14 @@ redis: # OMC系统使用库 default: port: 6379 # Redis port - host: "192.168.5.59" # Redis host - password: "redis@1234" + host: "192.168.2.219" # Redis host + password: "123456" db: 10 # Redis db_num # UDM网元用户库 udmuser: port: 6379 # Redis port - host: "172.60.5.140" - password: "helloearth" + host: "192.168.2.219" + password: "123456" db: 0 # Redis db_num # 多个数据源时可以用这个指定默认的数据源 defaultDataSourceName: "default" @@ -71,11 +84,10 @@ mml: user: admin password: admin mmlHome: ./mmlhome - upload: /home/agtuser # NE config ne: - user: agtuser + user: root etcdir: /usr/local/etc bindir: /usr/local/bin omcdir: /usr/local/omc @@ -119,9 +131,10 @@ omc: # Alarm module setting # Forward interface: -# email/sms +# email/sms +# smProxy: sms(Short Message Service)/smsc(SMS Centre) alarm: - forwardAlarm: false + forwardAlarm: true email: smtp: mail.agrandtech.com port: 25 @@ -129,8 +142,9 @@ alarm: password: "1000smtp@omc!" # TLS skip verify: true/false tlsSkipVerify: true + smProxy: smsc sms: - apiURL: http://smsc.xxx.com.cn/ + apiURL: http://smsc.xxx.com/ accessKeyID: xxxx accessKeySecret: xxxx signName: xxx SMSC @@ -175,12 +189,12 @@ testConfig: file: ./etc/testconfig.yaml # 静态文件配置, 相对项目根路径或填绝对路径 -# staticFile: -# # 默认资源,dir目录需要预先创建 -# default: -# prefix: "/static" -# dir: "./static" -# # 文件上传资源目录映射,与项目目录同级 -# upload: -# prefix: "/upload" -# dir: "./upload" +staticFile: + # 默认资源,dir目录需要预先创建 + default: + prefix: "/static" + dir: "./static" + # 文件上传资源目录映射,与项目目录同级 + upload: + prefix: "/upload" + dir: "./upload" diff --git a/restagent/makefile b/restagent/makefile index 8fec79f..2f41274 100644 --- a/restagent/makefile +++ b/restagent/makefile @@ -1,7 +1,7 @@ # Makefile for rest agent project PROJECT = OMC -VERSION = 2.2404.6 +VERSION = 2.2405.4 PLATFORM = amd64 ARMPLATFORM = aarch64 BUILDDIR = ../../build diff --git a/restagent/restagent.go b/restagent/restagent.go index 04aac95..ec8afd3 100644 --- a/restagent/restagent.go +++ b/restagent/restagent.go @@ -9,6 +9,8 @@ import ( "strconv" "strings" + _ "net/http/pprof" + "be.ems/features/dbrest" "be.ems/features/event" "be.ems/features/fm" @@ -185,12 +187,20 @@ func HttpListenWebServer(addr string) { } func main() { + // src 配置中心初始加载 src.ConfigurationInit() app := src.AppEngine() conf := config.GetYamlConfig() + if conf.Pprof.Enabled { + // 启用pprof HTTP服务 + go func() { + fmt.Println(http.ListenAndServe(conf.Pprof.Addr, nil)) + }() + } + log.InitLogger(conf.Logger.File, conf.Logger.Duration, conf.Logger.Count, "omc:restagent", config.GetLogLevel()) fmt.Printf("OMC restagent version: %s\n", global.Version) log.Infof("========================= OMC restagent startup =========================") @@ -241,9 +251,6 @@ func main() { // AMF上报的UE事件, 无前缀,暂时特殊处理 app.POST(event.UriUEEvent, event.PostUEEventFromAMF) - // 开启监控采集 - // monitor.StartMonitor(false, "") - var listenLocalhost bool = false for _, rest := range conf.Rest { // ipv4 goroutines diff --git a/src/app.go b/src/app.go index b6b7329..75dd363 100644 --- a/src/app.go +++ b/src/app.go @@ -8,6 +8,7 @@ import ( "be.ems/src/framework/errorcatch" "be.ems/src/framework/middleware" "be.ems/src/framework/middleware/security" + "be.ems/src/framework/utils/machine" "be.ems/src/modules/chart" "be.ems/src/modules/common" "be.ems/src/modules/crontask" @@ -18,7 +19,9 @@ import ( "be.ems/src/modules/trace" "be.ems/src/modules/ws" + "github.com/chenjiandongx/ginprom" "github.com/gin-gonic/gin" + "github.com/prometheus/client_golang/prometheus/promhttp" ) //go:embed assets/* @@ -28,6 +31,13 @@ var assetsDir embed.FS func AppEngine() *gin.Engine { app := initAppEngine() + // TODO 不建议在主分支中加入 + // 性能分析监控 + if promEnabled := config.Get("pprof.enabled"); promEnabled != nil && promEnabled.(bool) { + app.Use(ginprom.PromMiddleware(nil)) + app.GET("/metrics", ginprom.PromHandler(promhttp.Handler())) + } + // 初始全局默认 initDefeat(app) @@ -37,6 +47,9 @@ func AppEngine() *gin.Engine { // 设置程序内全局资源访问 config.SetAssetsDirFS(assetsDir) + // 首次安装启动记录 + machine.Launch() + // 读取服务配置 app.ForwardedByClientIP = config.Get("server.proxy").(bool) return app @@ -108,7 +121,7 @@ func initDefeat(app *gin.Engine) { app.NoRoute(func(c *gin.Context) { c.JSON(404, gin.H{ "code": 404, - "msg": fmt.Sprintf("%s Not Found", c.Request.RequestURI), + "msg": fmt.Sprintf("Not Found %s", c.Request.RequestURI), }) }) } diff --git a/src/framework/config/config/config.default.yaml b/src/framework/config/config/config.default.yaml index ce4c945..c5a30c6 100644 --- a/src/framework/config/config/config.default.yaml +++ b/src/framework/config/config/config.default.yaml @@ -1,7 +1,7 @@ # 项目信息 framework: name: "CN EMS" - version: "2.2404.6" + version: "2.2405.4" # 应用服务配置 server: @@ -15,7 +15,7 @@ logger: fileDir: "/var/log" fileName: "omc.log" level: 2 # 日志记录的等级 0:silent<1:info<2:warn<3:error - maxDay: 180 # 日志会保留 180 天 + maxDay: 7 # 日志会保留 180 天 maxSize: 10 # 调整按 10MB 大小的切割 # 静态文件配置, 相对项目根路径或填绝对路径 diff --git a/src/framework/constants/common/common.go b/src/framework/constants/common/common.go index 3786104..08e0bcb 100644 --- a/src/framework/constants/common/common.go +++ b/src/framework/constants/common/common.go @@ -19,3 +19,6 @@ const STATUS_NO = "0" // 上下文信息-登录用户 const CTX_LOGIN_USER = "loginuser" + +// 启动-引导系统初始 +const LAUNCH_BOOTLOADER = "bootloader" diff --git a/src/framework/logger/writer.go b/src/framework/logger/writer.go index 3209686..d535f64 100644 --- a/src/framework/logger/writer.go +++ b/src/framework/logger/writer.go @@ -34,7 +34,7 @@ const ( // NewLogger 实例日志器对象 func NewLogger(env, fileDir, fileName string, level, maxDay, maxSize int) (*Logger, error) { logFilePath := filepath.Join(fileDir, fileName) - if err := os.MkdirAll(filepath.Dir(logFilePath), 0750); err != nil { + if err := os.MkdirAll(filepath.Dir(logFilePath), 0775); err != nil { return nil, fmt.Errorf("failed to mkdir logger dir: %v", err) } fileHandle, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) diff --git a/src/framework/utils/crypto/aes.go b/src/framework/utils/crypto/aes.go index 4db36ac..290eb71 100644 --- a/src/framework/utils/crypto/aes.go +++ b/src/framework/utils/crypto/aes.go @@ -10,21 +10,17 @@ import ( "io" ) -// aesKey 字符串AES加解密密钥 -const aesKey = "AGT66VfY4SMaiT97" - // StringEncryptByAES 字符串AES加密 func StringEncryptByAES(text string) (string, error) { if len(text) == 0 { return "", nil } - pass := []byte(text) - xpass, err := aesEncryptWithSalt([]byte(aesKey), pass) - if err == nil { - pass64 := base64.StdEncoding.EncodeToString(xpass) - return pass64, err + xpass, err := aesEncryptWithSalt([]byte(text)) + if err != nil { + return "", err } - return "", err + pass64 := base64.StdEncoding.EncodeToString(xpass) + return pass64, nil } // StringDecryptByAES 字符串AES解密 @@ -36,53 +32,70 @@ func StringDecryptByAES(text string) (string, error) { if err != nil { return "", err } - var tpass []byte - tpass, err = aesDecryptWithSalt([]byte(aesKey), bytesPass) - if err == nil { - result := string(tpass[:]) - return result, err + + tpass, err := aesDecryptWithSalt(bytesPass) + if err != nil { + return "", err } - return "", err + return string(tpass), nil } -// aesEncryptWithSalt AES加密 -func aesEncryptWithSalt(key, plaintext []byte) ([]byte, error) { - blockSize := aes.BlockSize - padding := blockSize - len(plaintext)%blockSize - padtext := bytes.Repeat([]byte{byte(padding)}, padding) - plaintext = append(plaintext, padtext...) +// aesKey 字符串AES加解密密钥 +const aesKey = "AGT66VfY4SMaiT97a7df0aef1704d5c5" - block, err := aes.NewCipher(key) +// const aesKey = "AGT66VfY4SMaiT97" +// aesEncryptWithSalt AES加密 +func aesEncryptWithSalt(plaintext []byte) ([]byte, error) { + block, err := aes.NewCipher([]byte(aesKey)) if err != nil { return nil, err } + blockSize := aes.BlockSize + + padding := blockSize - (len(plaintext) % blockSize) + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + plaintext = append(plaintext, padtext...) + ciphertext := make([]byte, blockSize+len(plaintext)) - iv := ciphertext[0:blockSize] + iv := ciphertext[:blockSize] if _, err := io.ReadFull(rand.Reader, iv); err != nil { return nil, err } - cbc := cipher.NewCBCEncrypter(block, iv) - cbc.CryptBlocks(ciphertext[blockSize:], plaintext) + + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(ciphertext[blockSize:], plaintext) + return ciphertext, nil } // aesDecryptWithSalt AES解密 -func aesDecryptWithSalt(key, ciphertext []byte) ([]byte, error) { +func aesDecryptWithSalt(ciphertext []byte) ([]byte, error) { blockSize := aes.BlockSize - var block cipher.Block - block, err := aes.NewCipher(key) + if len(ciphertext) < blockSize { + return nil, fmt.Errorf("ciphertext too short") + } + + iv := ciphertext[:blockSize] + ciphertext = ciphertext[blockSize:] + + block, err := aes.NewCipher([]byte(aesKey)) if err != nil { return nil, err } - if len(ciphertext) < blockSize { - return nil, fmt.Errorf("iciphertext too short") + + if len(ciphertext)%blockSize != 0 { + return nil, fmt.Errorf("ciphertext is not a multiple of the block size") } - iv := ciphertext[:blockSize] - ciphertext = ciphertext[blockSize:] - cbc := cipher.NewCBCDecrypter(block, iv) - cbc.CryptBlocks(ciphertext, ciphertext) - length := len(ciphertext) - unpadding := int(ciphertext[len(ciphertext)-1]) - ciphertext = ciphertext[:(length - unpadding)] + + mode := cipher.NewCBCDecrypter(block, iv) + mode.CryptBlocks(ciphertext, ciphertext) + + // 去除填充 + padding := int(ciphertext[len(ciphertext)-1]) + if padding > blockSize || padding == 0 { + return nil, fmt.Errorf("invalid padding") + } + ciphertext = ciphertext[:len(ciphertext)-padding] + return ciphertext, nil } diff --git a/src/framework/utils/file/csv.go b/src/framework/utils/file/csv.go index 6591ff8..401f2ec 100644 --- a/src/framework/utils/file/csv.go +++ b/src/framework/utils/file/csv.go @@ -20,7 +20,7 @@ func WriterFileCSV(data [][]string, filePath string) error { dirPath := filepath.Dir(filePath) // 确保文件夹路径存在 - err := os.MkdirAll(dirPath, os.ModePerm) + err := os.MkdirAll(dirPath, 0775) if err != nil { logger.Errorf("MkdirAll dir %v", err) } diff --git a/src/framework/utils/file/excel.go b/src/framework/utils/file/excel.go index 7441680..69b7aa5 100644 --- a/src/framework/utils/file/excel.go +++ b/src/framework/utils/file/excel.go @@ -142,7 +142,7 @@ func WriteSheet(headerCells map[string]string, dataCells []map[string]any, fileN saveFilePath := filepath.Join(dir, filePath, fileName) // 创建文件目录 - if err := os.MkdirAll(filepath.Dir(saveFilePath), 0750); err != nil { + if err := os.MkdirAll(filepath.Dir(saveFilePath), 0775); err != nil { return "", fmt.Errorf("failed to create save file %v", err) } diff --git a/src/framework/utils/file/file.go b/src/framework/utils/file/file.go index d0a602a..d1f90f0 100644 --- a/src/framework/utils/file/file.go +++ b/src/framework/utils/file/file.go @@ -177,13 +177,13 @@ func ReadUploadFileStream(filePath, headerRange string) (map[string]any, error) "range": "", "chunkSize": 0, "fileSize": 0, - "data": nil, + "data": []byte{}, } // 文件大小 fileSize := getFileSize(fileAsbPath) if fileSize <= 0 { - return result, nil + return result, fmt.Errorf("file does not exist") } result["fileSize"] = fileSize @@ -312,12 +312,12 @@ func CopyUploadFile(filePath, dst string) error { } defer src.Close() - if err := os.MkdirAll(filepath.Dir(dst), 0750); err != nil { + if err := os.MkdirAll(filepath.Dir(dst), 0775); err != nil { return err } // 如果目标文件已经存在,先将目标文件重命名 - if _, err := os.Stat(dst); err == nil { + if info, err := os.Stat(dst); err == nil && !info.IsDir() { ext := filepath.Ext(dst) name := dst[0 : len(dst)-len(ext)] newName := fmt.Sprintf("%s-%s%s", name, time.Now().Format("20060102_150405"), ext) diff --git a/src/framework/utils/file/json.go b/src/framework/utils/file/json.go index 88217c9..4421c2b 100644 --- a/src/framework/utils/file/json.go +++ b/src/framework/utils/file/json.go @@ -16,7 +16,7 @@ func WriterFileJSON(data any, filePath string) error { dirPath := filepath.Dir(filePath) // 确保文件夹路径存在 - err := os.MkdirAll(dirPath, os.ModePerm) + err := os.MkdirAll(dirPath, 0775) if err != nil { logger.Errorf("CreateFile MkdirAll %v", err) } @@ -46,7 +46,7 @@ func WriterFileJSONLine(data []any, filePath string) error { dirPath := filepath.Dir(filePath) // 确保文件夹路径存在 - err := os.MkdirAll(dirPath, os.ModePerm) + err := os.MkdirAll(dirPath, 0775) if err != nil { logger.Errorf("CreateFile MkdirAll %v", err) } diff --git a/src/framework/utils/file/txt.go b/src/framework/utils/file/txt.go index d509fd8..e468a19 100644 --- a/src/framework/utils/file/txt.go +++ b/src/framework/utils/file/txt.go @@ -22,7 +22,7 @@ func WriterFileTXT(data [][]string, sep string, filePath string) error { dirPath := filepath.Dir(filePath) // 确保文件夹路径存在 - err := os.MkdirAll(dirPath, os.ModePerm) + err := os.MkdirAll(dirPath, 0775) if err != nil { logger.Errorf("CreateFile MkdirAll %v", err) } diff --git a/src/framework/utils/file/utils.go b/src/framework/utils/file/utils.go index fbb05fe..eccf54c 100644 --- a/src/framework/utils/file/utils.go +++ b/src/framework/utils/file/utils.go @@ -26,7 +26,7 @@ func transferToNewFile(file *multipart.FileHeader, dst string) error { } defer src.Close() - if err = os.MkdirAll(filepath.Dir(dst), 0750); err != nil { + if err = os.MkdirAll(filepath.Dir(dst), 0775); err != nil { return err } @@ -66,7 +66,7 @@ func mergeToNewFile(dirPath string, writePath string, fileName string) error { // 写入到新路径文件 newFilePath := filepath.Join(writePath, fileName) - if err = os.MkdirAll(filepath.Dir(newFilePath), 0750); err != nil { + if err = os.MkdirAll(filepath.Dir(newFilePath), 0775); err != nil { return err } diff --git a/src/framework/utils/machine/launch.go b/src/framework/utils/machine/launch.go new file mode 100644 index 0000000..ab680b4 --- /dev/null +++ b/src/framework/utils/machine/launch.go @@ -0,0 +1,178 @@ +package machine + +import ( + "encoding/json" + "fmt" + "hash/fnv" + "os" + "runtime" + "time" + + "be.ems/src/framework/constants/common" + "be.ems/src/framework/logger" + "be.ems/src/framework/utils/cmd" + "be.ems/src/framework/utils/crypto" + "be.ems/src/framework/utils/parse" +) + +// 机器的唯一标识符 +var Code string + +// 初始信息 +var LaunchInfo map[string]any + +// codeGenerate 生成机器的唯一标识符 +func codeGenerate() string { + var machineID string + + // 获取主机名 + hostname, err := os.Hostname() + if err != nil { + panic(err) + } + machineID += hostname + + // 获取 CPU 信息 + numCPU := runtime.NumCPU() + machineID += fmt.Sprintf("%d", numCPU) + + // 获取操作系统信息 + osInfo := runtime.GOOS + machineID += osInfo + + // 使用哈希函数生成机器码 + h := fnv.New32a() + h.Write([]byte(machineID)) + machineCode := h.Sum32() + + return fmt.Sprintf("%x", machineCode) +} + +// 网管本地路径 +func filePath() string { + filePath := "/usr/local/etc/omc/machine.ini" + if runtime.GOOS == "windows" { + filePath = fmt.Sprintf("C:%s", filePath) + } + return filePath +} + +// codeFileRead 读取机器保留的信息 +func codeFileRead() (map[string]any, error) { + var mapData map[string]any + // 读取文件内容 + bytes, err := os.ReadFile(filePath()) + if err != nil { + logger.Warnf("CodeFileRead ReadFile => %s", err.Error()) + return mapData, fmt.Errorf("not file") + } + content := string(bytes) + // 解密 + contentDe, err := crypto.StringDecryptByAES(content) + if err != nil { + logger.Errorf("CodeFileRead decrypt: %v", err.Error()) + return mapData, fmt.Errorf("decrypt fail") + } + // 序列化Map + mapData, err = parse.ConvertConfigToMap("json", string(contentDe)) + if err != nil { + logger.Warnf("NeConfPara5GRead ConvertConfigToMap => %s", err.Error()) + return mapData, fmt.Errorf("content error") + } + return mapData, nil +} + +// codeFileWrite 写入机器保留的信息 +func codeFileWrite(data map[string]any) error { + jsonByte, _ := json.Marshal(data) + // 加密 + contentEn, err := crypto.StringEncryptByAES(string(jsonByte)) + if err != nil { + logger.Errorf("insert encrypt: %v", err.Error()) + return fmt.Errorf("encrypt fail") + } + return parse.ConvertConfigToFile("txt", filePath(), contentEn) +} + +// Launch 记录首次安装启动初始信息 +func Launch() { + Code = codeGenerate() + // 检查文件是否存在 + if _, err := os.Stat(filePath()); err != nil { + LaunchInfo = map[string]any{ + "code": Code, // 机器码 + "useTime": time.Now().UnixMilli(), // 首次使用时间 + + common.LAUNCH_BOOTLOADER: true, // 启动引导 + common.LAUNCH_BOOTLOADER + "Time": 0, // 引导完成时间 + } + codeFileWrite(LaunchInfo) + } else { + // 读取记录文件 + data, err := codeFileRead() + if err != nil { + // 文件异常就重新生成 + os.Remove(filePath()) + Launch() + return + } + LaunchInfo = data + } +} + +// SetLaunchInfo 新增额外的初始信息 +func SetLaunchInfo(info map[string]any) error { + if info == nil { + return fmt.Errorf("not info") + } + + // 固定值禁止变更 + constKeys := []string{"code", "useTime"} + for k, v := range info { + constKey := false + for _, ck := range constKeys { + if ck == k { + constKey = true + break + } + } + if constKey { + continue + } else { + LaunchInfo[k] = v + } + } + return codeFileWrite(LaunchInfo) +} + +// Bootloader 启动引导标记 +func Bootloader(flag bool) error { + return SetLaunchInfo(map[string]any{ + common.LAUNCH_BOOTLOADER: flag, // 启动引导 true开 false关 + common.LAUNCH_BOOTLOADER + "Time": time.Now().UnixMilli(), // 引导完成时间 + }) +} + +// Reset 引导数据重置 +func Reset() error { + // 重置数据库 + if runtime.GOOS == "windows" { + // return fmt.Errorf("not support window") + } else { + // 重置数据库 + if _, err := cmd.Execf("sudo /usr/local/omc/bin/setomc.sh -m install"); err != nil { + return err + } + // 重启服务 + if _, err := cmd.Execf("nohup sh -c \"sleep 1s && %s\" > /dev/null 2>&1 &", "sudo systemctl restart restagent"); err != nil { + return err + } + } + + // 重置引导标记 + if err := Bootloader(true); err != nil { + return err + } + + return nil +} diff --git a/src/framework/utils/parse/parse.go b/src/framework/utils/parse/parse.go index ff8145c..8eb27dc 100644 --- a/src/framework/utils/parse/parse.go +++ b/src/framework/utils/parse/parse.go @@ -1,8 +1,11 @@ package parse import ( + "encoding/json" "fmt" "image/color" + "os" + "path/filepath" "reflect" "regexp" "strconv" @@ -10,6 +13,7 @@ import ( "time" "github.com/robfig/cron/v3" + "gopkg.in/yaml.v3" ) // Number 解析数值型 @@ -51,6 +55,8 @@ func Boolean(str any) bool { case float32, float64: num := reflect.ValueOf(str).Float() return num != 0 + case bool: + return str default: return false } @@ -165,3 +171,83 @@ func Color(colorStr string) *color.RGBA { A: 255, // 不透明 } } + +// ConvertIPMask 转换IP网络地址掩码 24 -> 255.255.255.0 +func ConvertIPMask(bits int64) string { + if bits < 0 || bits > 32 { + return "Invalid Mask Bits" + } + + // 构建一个32位的uint32类型掩码,指定前bits位为1,其余为0 + mask := uint32((1<>24), + fmt.Sprintf("%d", (mask>>16)&255), + fmt.Sprintf("%d", (mask>>8)&255), + fmt.Sprintf("%d", mask&255), + } + + // 将分组用点号连接起来形成掩码字符串 + return strings.Join(groups, ".") +} + +// ConvertConfigToMap 将配置内容转换为Map结构数据 +// +// configType 类型支持:txt json yaml yml +func ConvertConfigToMap(configType, content string) (map[string]any, error) { + // 类型支持:viper.SupportedExts + // config := viper.New() + // config.SetConfigType(configType) + // err := config.ReadConfig(bytes.NewBuffer([]byte(content))) + // return config.AllSettings(), err + + var configMap map[string]interface{} + var err error + if configType == "" || configType == "txt" { + configMap = map[string]interface{}{ + "txt": content, + } + } + if configType == "yaml" || configType == "yml" { + err = yaml.Unmarshal([]byte(content), &configMap) + } + if configType == "json" { + err = json.Unmarshal([]byte(content), &configMap) + } + return configMap, err +} + +// ConvertConfigToFile 将数据写入到指定文件内 +// +// configType 类型支持:txt json yaml yml +func ConvertConfigToFile(configType, filePath string, data any) error { + // viper.SupportedExts + // config := viper.New() + // config.SetConfigType(configType) + // for key, value := range mapData { + // config.Set(key, value) + // } + // return config.WriteConfigAs(filePath) + + var dataByte []byte + var err error + if configType == "" || configType == "txt" { + dataByte = []byte(data.(string)) + } + if configType == "yaml" || configType == "yml" { + dataByte, err = yaml.Marshal(data) + } + if configType == "json" { + dataByte, err = json.Marshal(data) + } + if err != nil { + return err + } + + if err := os.MkdirAll(filepath.Dir(filePath), 0775); err != nil { + return err + } + return os.WriteFile(filePath, dataByte, 0644) +} diff --git a/src/framework/utils/repo/repo.go b/src/framework/utils/repo/repo.go index f783c7f..5bd59fe 100644 --- a/src/framework/utils/repo/repo.go +++ b/src/framework/utils/repo/repo.go @@ -20,7 +20,7 @@ func PageNumSize(pageNum, pageSize any) (int64, int64) { // 显示记录数 size := parse.Number(pageSize) - if size < 0 { + if size < 1 { size = 10 } return num - 1, size diff --git a/src/framework/utils/ssh/scp.go b/src/framework/utils/ssh/scp.go index 8151b3a..33beb53 100644 --- a/src/framework/utils/ssh/scp.go +++ b/src/framework/utils/ssh/scp.go @@ -28,7 +28,7 @@ func FileSCPLocalToNe(neIp, localPath, nePath string) error { // 网元NE 远程文件复制到本地文件 func FileSCPNeToLocal(neIp, nePath, localPath string) error { // 确保文件夹路径存在 - if err := os.MkdirAll(filepath.Dir(localPath), 0750); err != nil { + if err := os.MkdirAll(filepath.Dir(localPath), 0775); err != nil { logger.Errorf("FileSCPNeToLocal MkdirAll err %v", err) return err } diff --git a/src/framework/utils/ssh/ssh.go b/src/framework/utils/ssh/ssh.go index 1542361..eaf5c3d 100644 --- a/src/framework/utils/ssh/ssh.go +++ b/src/framework/utils/ssh/ssh.go @@ -6,12 +6,14 @@ import ( "io" "os" "os/user" + "path/filepath" "strings" "sync" "time" "be.ems/src/framework/logger" "be.ems/src/framework/utils/cmd" + gosftp "github.com/pkg/sftp" gossh "golang.org/x/crypto/ssh" ) @@ -133,8 +135,12 @@ func (c *ConnSSH) SendToAuthorizedKeys() error { return err } authorizedKeysEntry := fmt.Sprintln(strings.TrimSpace(publicKey)) - cmdStr := "echo '" + authorizedKeysEntry + "' >> ~/.ssh/authorized_keys" - _, err = c.RunCMD(cmdStr) + cmdStrArr := []string{ + fmt.Sprintf("sudo mkdir -p /home/%s/.ssh && sudo chown %s:%s /home/%s/.ssh && sudo chmod 700 /home/%s/.ssh", c.User, c.User, c.User, c.User, c.User), + fmt.Sprintf("sudo touch /home/%s/.ssh/authorized_keys && sudo chown %s:%s /home/%s/.ssh/authorized_keys && sudo chmod 600 /home/%s/.ssh/authorized_keys", c.User, c.User, c.User, c.User, c.User), + fmt.Sprintf("echo '%s' | sudo tee -a /home/%s/.ssh/authorized_keys", authorizedKeysEntry, c.User), + } + _, err = c.RunCMD(strings.Join(cmdStrArr, " && ")) if err != nil { logger.Errorf("SendAuthorizedKeys echo err %s", err.Error()) return err @@ -156,9 +162,8 @@ func (c *ConnSSH) CurrentUserRsaKey(publicKey bool) (string, error) { // 是否存在私钥并创建 keyPath := fmt.Sprintf("%s/.ssh/id_rsa", usr.HomeDir) if _, err := os.Stat(keyPath); err != nil { - _, err2 := cmd.ExecWithCheck("ssh-keygen", "-t", "rsa", "-P", "", "-f", keyPath) - if err2 != nil { - logger.Errorf("CurrentUserPrivateKey ssh-keygen [%s] rsa => %s", usr.Username, err2.Error()) + if _, err := cmd.ExecWithCheck("ssh-keygen", "-t", "rsa", "-P", "", "-f", keyPath); err != nil { + logger.Errorf("CurrentUserPrivateKey ssh-keygen [%s] rsa => %s", usr.Username, err.Error()) } } @@ -239,12 +244,11 @@ func (s *SSHClientSession) Write(cmd string) (int, error) { return s.Stdin.Write([]byte(cmd)) } -// Read 读取结果 等待一会才有结果 +// Read 读取结果 func (s *SSHClientSession) Read() []byte { if s.Stdout == nil { return []byte{} } - // time.Sleep(300 * time.Millisecond) bs := s.Stdout.Bytes() if len(bs) > 0 { s.Stdout.Reset() @@ -253,15 +257,6 @@ func (s *SSHClientSession) Read() []byte { return []byte{} } -// CombinedOutput 发送命令带结果返回 -func (s *SSHClientSession) CombinedOutput(cmd string) (string, error) { - n, err := s.Write(cmd) - if n == 0 || err != nil { - return "", err - } - return string(s.Read()), nil -} - // singleWriter SSH客户端会话消息 type singleWriter struct { b bytes.Buffer @@ -283,3 +278,208 @@ func (w *singleWriter) Reset() { defer w.mu.Unlock() w.b.Reset() } + +// NewClientSFTP 创建SSH客户端SFTP对象 +func (c *ConnSSH) NewClientSFTP() (*SSHClientSFTP, error) { + sftpClient, err := gosftp.NewClient(c.Client) + if err != nil { + logger.Errorf("NewClientSFTP failed to create sftp: => %s", err.Error()) + return nil, err + } + + return &SSHClientSFTP{ + Client: sftpClient, + }, nil +} + +// SSHClientSFTP SSH客户端SFTP对象 +type SSHClientSFTP struct { + Client *gosftp.Client +} + +// Close 关闭会话 +func (s *SSHClientSFTP) Close() { + if s.Client != nil { + s.Client.Close() + } +} + +// CopyDirRemoteToLocal 复制目录-远程到本地 +func (s *SSHClientSFTP) CopyDirRemoteToLocal(remoteDir, localDir string) error { + // 列出远程目录中的文件和子目录 + remoteFiles, err := s.Client.ReadDir(remoteDir) + if err != nil { + logger.Errorf("CopyDirRemoteToLocal failed to reading remote directory %s: => %s", remoteDir, err.Error()) + return err + } + + // 创建本地目录 + err = os.MkdirAll(localDir, 0775) + if err != nil { + logger.Errorf("CopyDirRemoteToLocal failed to creating local directory %s: => %s", localDir, err.Error()) + return err + } + + // 遍历远程文件和子目录并复制到本地 + for _, remoteFile := range remoteFiles { + remotePath := filepath.Join(remoteDir, remoteFile.Name()) + localPath := filepath.Join(localDir, remoteFile.Name()) + + if remoteFile.IsDir() { + // 如果是子目录,则递归复制子目录 + err = s.CopyDirRemoteToLocal(remotePath, localPath) + if err != nil { + logger.Errorf("CopyDirRemoteToLocal failed to copying remote directory %s: => %s", remotePath, err.Error()) + continue + } + } else { + // 如果是文件,则复制文件内容 + remoteFile, err := s.Client.Open(remotePath) + if err != nil { + logger.Errorf("CopyDirRemoteToLocal failed to opening remote file %s: => %s", remotePath, err.Error()) + continue + } + defer remoteFile.Close() + + localFile, err := os.Create(localPath) + if err != nil { + logger.Errorf("CopyDirRemoteToLocal failed to creating local file %s: => %s", localPath, err.Error()) + continue + } + defer localFile.Close() + + _, err = io.Copy(localFile, remoteFile) + if err != nil { + logger.Errorf("CopyDirRemoteToLocal failed to copying file contents from %s to %s: => %s", remotePath, localPath, err.Error()) + continue + } + } + } + return nil +} + +// CopyDirRemoteToLocal 复制目录-本地到远程 +func (s *SSHClientSFTP) CopyDirLocalToRemote(localDir, remoteDir string) error { + // 创建远程目录 + err := s.Client.MkdirAll(remoteDir) + if err != nil { + logger.Errorf("CopyDirLocalToRemote failed to creating remote directory %s: => %s", remoteDir, err.Error()) + return err + } + + // 遍历本地目录中的文件和子目录并复制到远程 + err = filepath.Walk(localDir, func(localPath string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // 生成远程路径 + remotePath := filepath.Join(remoteDir, localPath[len(localDir):]) + + if info.IsDir() { + // 如果是子目录,则创建远程目录 + err := s.Client.MkdirAll(remotePath) + if err != nil { + logger.Errorf("CopyDirLocalToRemote failed to creating remote directory %s: => %s", remotePath, err.Error()) + return nil + } + } else { + // 如果是文件,则复制文件内容 + localFile, err := os.Open(localPath) + if err != nil { + logger.Errorf("CopyDirLocalToRemote failed to opening local file %s: => %s", localPath, err.Error()) + return nil + } + defer localFile.Close() + + remoteFile, err := s.Client.Create(remotePath) + if err != nil { + logger.Errorf("CopyDirLocalToRemote failed to creating remote file %s: => %s", remotePath, err.Error()) + return nil + } + defer remoteFile.Close() + + _, err = io.Copy(remoteFile, localFile) + if err != nil { + logger.Errorf("CopyDirLocalToRemote failed to copying file contents from %s to %s: => %s", localPath, remotePath, err.Error()) + return nil + } + } + + return nil + }) + if err != nil { + logger.Errorf("CopyDirLocalToRemote failed to walking local directory: => %s", err.Error()) + return err + } + return nil +} + +// CopyDirRemoteToLocal 复制文件-远程到本地 +func (s *SSHClientSFTP) CopyFileRemoteToLocal(remotePath, localPath string) error { + // 打开远程文件 + remoteFile, err := s.Client.Open(remotePath) + if err != nil { + logger.Errorf("CopyFileRemoteToLocal failed to opening remote file: => %s", err.Error()) + return err + } + defer remoteFile.Close() + + if err := os.MkdirAll(filepath.Dir(localPath), 0775); err != nil { + return err + } + + // 如果目标文件已经存在,先将目标文件重命名 + // if info, err := os.Stat(localPath); err == nil && !info.IsDir() { + // ext := filepath.Ext(localPath) + // name := localPath[0 : len(localPath)-len(ext)] + // newName := fmt.Sprintf("%s-%s%s", name, time.Now().Format("20060102_150405"), ext) + // err := os.Rename(localPath, newName) + // if err != nil { + // return err + // } + // } + + // 创建本地文件 + localFile, err := os.Create(localPath) + if err != nil { + logger.Errorf("CopyFileRemoteToLocal failed to creating local file: => %s", err.Error()) + return err + } + defer localFile.Close() + + // 复制文件内容 + _, err = io.Copy(localFile, remoteFile) + if err != nil { + logger.Errorf("CopyFileRemoteToLocal failed to copying contents: => %s", err.Error()) + return err + } + return nil +} + +// CopyDirRemoteToLocal 复制文件-本地到远程 +func (s *SSHClientSFTP) CopyFileLocalToRemote(localPath, remotePath string) error { + // 打开本地文件 + localFile, err := os.Open(localPath) + if err != nil { + logger.Errorf("CopyFileLocalToRemote failed to opening local file: => %s", err.Error()) + return err + } + defer localFile.Close() + + // 创建远程文件 + remoteFile, err := s.Client.Create(remotePath) + if err != nil { + logger.Errorf("CopyFileLocalToRemote failed to creating remote file: => %s", err.Error()) + return err + } + defer remoteFile.Close() + + // 复制文件内容 + _, err = io.Copy(remoteFile, localFile) + if err != nil { + logger.Errorf("CopyFileLocalToRemote failed to copying contents: => %s", err.Error()) + return err + } + return nil +} diff --git a/src/framework/utils/telnet/telnet.go b/src/framework/utils/telnet/telnet.go index 651ede9..078fd47 100644 --- a/src/framework/utils/telnet/telnet.go +++ b/src/framework/utils/telnet/telnet.go @@ -50,7 +50,13 @@ func (c *ConnTelnet) NewClient() (*ConnTelnet, error) { // fmt.Fprintln(client, c.User) // fmt.Fprintln(client, c.Password) + // 需要确保接收方理解并正确处理发送窗口大小设置命令 + client.Write([]byte{255, 251, 31}) // 发送窗口大小选项 + client.Write([]byte{255, 250, 31, 0, 128, 0, 120, 255, 240}) // 发送窗口行和列的大小 c.Client = &client + + // 排空连接登录的信息 + c.RunCMD("") return c, nil } @@ -70,32 +76,14 @@ func (c *ConnTelnet) RunCMD(cmd string) (string, error) { var buf bytes.Buffer tmp := make([]byte, 1024) - // 排空连接登录的信息 - for { - // 设置读取超时时间为100毫秒 - conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) - n, err := conn.Read(tmp) - if err != nil { - // 判断是否是超时错误 - if netErr, ok := err.(net.Error); ok && netErr.Timeout() { - break - } - break - } - if n == 0 { - break - } - buf.Write(tmp[:n]) - } - buf.Reset() - // 写入命令 - _, err := conn.Write([]byte(cmd)) - if err != nil { - return "", err + if cmd != "" { + if _, err := conn.Write([]byte(cmd)); err != nil { + return "", err + } } - // 读取本次响应命令消息 + // 读取命令消息 for { // 设置读取超时时间为1000毫秒 conn.SetReadDeadline(time.Now().Add(1000 * time.Millisecond)) @@ -119,35 +107,12 @@ func (c *ConnTelnet) RunCMD(cmd string) (string, error) { } // NewClient 创建Telnet客户端会话对象 -func (c *ConnTelnet) NewClientSession() (*TelnetClientSession, error) { +func (c *ConnTelnet) NewClientSession(cols, rows uint8) (*TelnetClientSession, error) { if c.Client == nil { return nil, fmt.Errorf("telnet client not connected") } - conn := *c.Client - - var buf bytes.Buffer - tmp := make([]byte, 1024) - // 排空连接登录的信息 - for { - // 设置读取超时时间为5毫秒 - conn.SetReadDeadline(time.Now().Add(5 * time.Millisecond)) - n, err := conn.Read(tmp) - if err != nil { - // 判断是否是超时错误 - if netErr, ok := err.(net.Error); ok && netErr.Timeout() { - break - } - break - } - if n == 0 { - break - } - buf.Write(tmp[:n]) - } - buf.Reset() - return &TelnetClientSession{ - Client: conn, + Client: *c.Client, }, nil } diff --git a/src/framework/utils/token/token.go b/src/framework/utils/token/token.go index 2a4fb06..b07879d 100644 --- a/src/framework/utils/token/token.go +++ b/src/framework/utils/token/token.go @@ -11,6 +11,7 @@ import ( "be.ems/src/framework/logger" redisCahe "be.ems/src/framework/redis" "be.ems/src/framework/utils/generate" + "be.ems/src/framework/utils/machine" "be.ems/src/framework/vo" jwt "github.com/golang-jwt/jwt/v5" @@ -74,7 +75,7 @@ func Create(loginUser *vo.LoginUser, ilobArgs ...string) string { // 生成令牌设置密钥 secret := config.Get("jwt.secret").(string) - tokenStr, err := jwtToken.SignedString([]byte(secret)) + tokenStr, err := jwtToken.SignedString([]byte(machine.Code + "@" + secret)) if err != nil { logger.Infof("jwt sign err : %v", err) return "" @@ -118,7 +119,7 @@ func Verify(tokenString string) (jwt.MapClaims, error) { // 判断加密算法是预期的加密算法 if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok { secret := config.Get("jwt.secret").(string) - return []byte(secret), nil + return []byte(machine.Code + "@" + secret), nil } return nil, jwt.ErrSignatureInvalid }) diff --git a/src/modules/common/common.go b/src/modules/common/common.go index b25d8e0..bb6c68b 100644 --- a/src/modules/common/common.go +++ b/src/modules/common/common.go @@ -25,6 +25,14 @@ func Setup(router *gin.Engine) { // 系统可暴露的配置信息 indexGroup.GET("/sys-conf", controller.NewCommont.SysConfig) + // 系统引导初始化 + guideGroup := router.Group("/bootloader") + { + guideGroup.POST("", controller.NewBootloader.Start) + guideGroup.PUT("", middleware.PreAuthorize(nil), controller.NewBootloader.Done) + guideGroup.DELETE("", middleware.PreAuthorize(nil), controller.NewBootloader.Reset) + guideGroup.PUT("/account", middleware.PreAuthorize(nil), controller.NewBootloader.Account) + } // 验证码操作处理 indexGroup.GET("/captchaImage", diff --git a/src/modules/common/controller/bootloader.go b/src/modules/common/controller/bootloader.go new file mode 100644 index 0000000..daa12da --- /dev/null +++ b/src/modules/common/controller/bootloader.go @@ -0,0 +1,181 @@ +package controller + +import ( + adminConstants "be.ems/src/framework/constants/admin" + "be.ems/src/framework/constants/common" + tokenConstants "be.ems/src/framework/constants/token" + "be.ems/src/framework/i18n" + "be.ems/src/framework/utils/ctx" + "be.ems/src/framework/utils/machine" + "be.ems/src/framework/utils/regular" + tokenUtils "be.ems/src/framework/utils/token" + "be.ems/src/framework/vo" + "be.ems/src/framework/vo/result" + commonService "be.ems/src/modules/common/service" + systemService "be.ems/src/modules/system/service" + "github.com/gin-gonic/gin" +) + +// 实例化控制层 BootloaderController 结构体 +var NewBootloader = &BootloaderController{ + accountService: commonService.NewAccountImpl, + sysUserService: systemService.NewSysUserImpl, +} + +// 系统引导初始化 +// +// PATH /bootloader +type BootloaderController struct { + // 账号身份操作服务 + accountService commonService.IAccount + // 用户信息服务 + sysUserService systemService.ISysUser +} + +// 首次引导开始 +// +// POST / +func (s *BootloaderController) Start(c *gin.Context) { + // 是否完成引导 + launchInfo := machine.LaunchInfo + if launchInfo == nil { + c.JSON(200, result.Err(nil)) + return + } + if v, ok := launchInfo[common.LAUNCH_BOOTLOADER]; ok && !v.(bool) { + c.JSON(200, result.ErrMsg("bootloader done")) + return + } + + // 查询用户登录账号 + sysUser := s.sysUserService.SelectUserById("1") + if sysUser.UserID != "1" { + c.JSON(200, result.ErrMsg("not found user data")) + return + } + + // 登录用户信息 + loginUser := vo.LoginUser{ + UserID: sysUser.UserID, + DeptID: sysUser.DeptID, + User: sysUser, + Permissions: []string{adminConstants.PERMISSION}, + } + + // 当前请求信息 + ipaddr, location := ctx.IPAddrLocation(c) + os, browser := ctx.UaOsBrowser(c) + + // 生成令牌,创建系统访问记录 + tokenStr := tokenUtils.Create(&loginUser, ipaddr, location, os, browser) + if tokenStr == "" { + c.JSON(200, result.Err(nil)) + return + } else { + s.accountService.UpdateLoginDateAndIP(&loginUser) + } + + c.JSON(200, result.OkData(map[string]any{ + tokenConstants.RESPONSE_FIELD: tokenStr, + })) +} + +// 首次引导完成 +// +// PUT / +func (s *BootloaderController) Done(c *gin.Context) { + // 是否完成引导 + launchInfo := machine.LaunchInfo + if launchInfo == nil { + c.JSON(200, result.Err(nil)) + return + } + if v, ok := launchInfo[common.LAUNCH_BOOTLOADER]; ok && !v.(bool) { + c.JSON(200, result.ErrMsg("bootloader done")) + return + } + + // 标记引导完成 + if err := machine.Bootloader(false); err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + + // 清除授权信息 + tokenUtils.Remove(ctx.Authorization(c)) + c.JSON(200, result.Ok(nil)) +} + +// 引导系统数据重置 +// +// DELETE / +func (s *BootloaderController) Reset(c *gin.Context) { + // 是否完成引导 + launchInfo := machine.LaunchInfo + if launchInfo == nil { + c.JSON(200, result.Err(nil)) + return + } + if v, ok := launchInfo[common.LAUNCH_BOOTLOADER]; ok && v.(bool) { + c.JSON(200, result.ErrMsg("bootloader not done")) + return + } + + if err := machine.Reset(); err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + + // 清除授权信息 + tokenUtils.Remove(ctx.Authorization(c)) + c.JSON(200, result.Ok(nil)) +} + +// 账号变更 +// +// PUT /account +func (s *BootloaderController) Account(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body struct { + UserName string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` + } + if err := c.ShouldBindJSON(&body); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + if !regular.ValidPassword(body.Password) { + // 登录密码至少包含大小写字母、数字、特殊符号,且不少于6位 + c.JSON(200, result.ErrMsg(i18n.TKey(language, "user.errPasswd"))) + return + } + + // 是否完成引导 + launchInfo := machine.LaunchInfo + if launchInfo == nil { + c.JSON(200, result.Err(nil)) + return + } + if v, ok := launchInfo[common.LAUNCH_BOOTLOADER]; ok && !v.(bool) { + c.JSON(200, result.ErrMsg("bootloader done")) + return + } + + // 查询用户登录账号 + sysUser := s.sysUserService.SelectUserById("2") + if sysUser.UserID != "2" { + c.JSON(200, result.ErrMsg("not found user data")) + return + } + sysUser.UserName = body.UserName + sysUser.NickName = body.UserName + sysUser.Password = body.Password + sysUser.UpdateBy = ctx.LoginUserToUserName(c) + rows := s.sysUserService.UpdateUser(sysUser) + if rows > 0 { + c.JSON(200, result.Ok(nil)) + return + } + c.JSON(200, result.Err(nil)) +} diff --git a/src/modules/common/controller/file.go b/src/modules/common/controller/file.go index 3506243..6dc67b7 100644 --- a/src/modules/common/controller/file.go +++ b/src/modules/common/controller/file.go @@ -43,13 +43,6 @@ func (s *FileController) Download(c *gin.Context) { return } routerPath := string(decodedBytes) - // 地址文件名截取 - fileName := routerPath[strings.LastIndex(routerPath, "/")+1:] - - // 响应头 - c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+url.QueryEscape(fileName)+`"`) - c.Writer.Header().Set("Accept-Ranges", "bytes") - c.Writer.Header().Set("Content-Type", "application/octet-stream") // 断点续传 headerRange := c.GetHeader("Range") @@ -58,6 +51,12 @@ func (s *FileController) Download(c *gin.Context) { c.JSON(200, result.ErrMsg(err.Error())) return } + + // 响应头 + c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+url.QueryEscape(filepath.Base(routerPath))+`"`) + c.Writer.Header().Set("Accept-Ranges", "bytes") + c.Writer.Header().Set("Content-Type", "application/octet-stream") + if headerRange != "" { c.Writer.Header().Set("Content-Range", fmt.Sprint(resultMap["range"])) c.Writer.Header().Set("Content-Length", fmt.Sprint(resultMap["chunkSize"])) @@ -65,7 +64,6 @@ func (s *FileController) Download(c *gin.Context) { } else { c.Writer.Header().Set("Content-Length", fmt.Sprint(resultMap["fileSize"])) c.Status(200) - } c.Writer.Write(resultMap["data"].([]byte)) } @@ -222,7 +220,7 @@ func (s *CommontController) TransferStaticFile(c *gin.Context) { delPrefix := strings.Replace(body.StaticPath, static["prefix"].(string), "", 1) staticPath := strings.Replace(delPrefix, "{language}", lang, 1) - newFile := fmt.Sprintf("%s%s", dir, staticPath) + newFile := filepath.ToSlash(fmt.Sprintf("%s%s", dir, staticPath)) err = file.CopyUploadFile(body.UploadPath, newFile) if err != nil { diff --git a/src/modules/common/service/commont.impl.go b/src/modules/common/service/commont.impl.go index 6273ab3..10ad3f8 100644 --- a/src/modules/common/service/commont.impl.go +++ b/src/modules/common/service/commont.impl.go @@ -5,6 +5,8 @@ import ( "be.ems/lib/global" "be.ems/src/framework/config" + "be.ems/src/framework/constants/common" + "be.ems/src/framework/utils/machine" systemService "be.ems/src/modules/system/service" ) @@ -29,6 +31,17 @@ func (s *CommontImpl) SystemConfigInfo() map[string]string { infoMap["version"] = global.Version infoMap["buildTime"] = global.BuildTime infoMap["goVer"] = global.GoVer + // 系统首次使用标记 + launchInfo := machine.LaunchInfo + if launchInfo != nil { + if v, ok := launchInfo[common.LAUNCH_BOOTLOADER]; ok { + infoMap[common.LAUNCH_BOOTLOADER] = fmt.Sprint(v) + } else { + infoMap[common.LAUNCH_BOOTLOADER] = "true" + } + } else { + infoMap[common.LAUNCH_BOOTLOADER] = "true" + } // 序列号 infoMap["serialNum"] = fmt.Sprint(config.Get("omc.sn")) // 获取LOGO类型 diff --git a/src/modules/network_data/controller/smf.go b/src/modules/network_data/controller/smf.go new file mode 100644 index 0000000..ee3ddb9 --- /dev/null +++ b/src/modules/network_data/controller/smf.go @@ -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)) +} diff --git a/src/modules/network_data/model/cdr_event.go b/src/modules/network_data/model/cdr_event.go index a4721d3..ae49041 100644 --- a/src/modules/network_data/model/cdr_event.go +++ b/src/modules/network_data/model/cdr_event.go @@ -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,14 +15,16 @@ type CDREvent struct { // CDREventQuery CDR会话对象查询参数结构体 type CDREventQuery struct { - NeType string `json:"neType" form:"neType" binding:"required"` // 网元类型, 暂时支持IMS - NeID string `json:"neId" form:"neId" binding:"required"` - RmUID string `json:"rmUID" form:"rmUID"` - RecordType string `json:"recordType" form:"recordType"` // 记录行为 MOC MTC MOSM MTSM - 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"` + 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 + CallerParty string `json:"callerParty" form:"callerParty"` // 主叫号码 + CalledParty string `json:"calledParty" form:"calledParty"` // 被叫号码 + 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"` } diff --git a/src/modules/network_data/model/cdr_event_smf.go b/src/modules/network_data/model/cdr_event_smf.go new file mode 100644 index 0000000..0234f91 --- /dev/null +++ b/src/modules/network_data/model/cdr_event_smf.go @@ -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"` +} diff --git a/src/modules/network_data/model/ue_event.go b/src/modules/network_data/model/ue_event.go index d39dd63..4045465 100644 --- a/src/modules/network_data/model/ue_event.go +++ b/src/modules/network_data/model/ue_event.go @@ -20,6 +20,7 @@ type UEEventQuery struct { NeID string `json:"neId" form:"neId" binding:"required"` RmUID string `json:"rmUID" form:"rmUID"` EventType string `json:"eventType" form:"eventType"` // 事件类型 auth-result detach cm-state + IMSI string `json:"imsi" form:"imsi"` // imsi StartTime string `json:"startTime" form:"startTime"` EndTime string `json:"endTime" form:"endTime"` SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=timestamp"` // 排序字段,填写结果字段 diff --git a/src/modules/network_data/network_data.go b/src/modules/network_data/network_data.go index 9c7bae7..8d56e69 100644 --- a/src/modules/network_data/network_data.go +++ b/src/modules/network_data/network_data.go @@ -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") { diff --git a/src/modules/network_data/repository/cdr_event.go b/src/modules/network_data/repository/cdr_event.go index cb3241c..bd5c562 100644 --- a/src/modules/network_data/repository/cdr_event.go +++ b/src/modules/network_data/repository/cdr_event.go @@ -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 +} diff --git a/src/modules/network_data/repository/cdr_event.impl.go b/src/modules/network_data/repository/cdr_event.impl.go index ac75e43..c9903b8 100644 --- a/src/modules/network_data/repository/cdr_event.impl.go +++ b/src/modules/network_data/repository/cdr_event.impl.go @@ -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) @@ -63,10 +93,6 @@ func (r *CDREventImpl) SelectPage(querys model.CDREventQuery) map[string]any { conditions = append(conditions, "rm_uid = ?") params = append(params, querys.RmUID) } - 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) @@ -77,6 +103,14 @@ func (r *CDREventImpl) SelectPage(querys model.CDREventQuery) map[string]any { endDate := date.ParseStrToDate(querys.EndTime, date.YYYY_MM_DD_HH_MM_SS) params = append(params, endDate.Unix()) } + if querys.CallerParty != "" { + conditions = append(conditions, "JSON_EXTRACT(cdr_json, '$.callerParty') = ?") + params = append(params, querys.CallerParty) + } + if querys.CalledParty != "" { + conditions = append(conditions, "JSON_EXTRACT(cdr_json, '$.calledParty') = ?") + params = append(params, querys.CalledParty) + } if querys.RecordType != "" { recordTypes := strings.Split(querys.RecordType, ",") placeholder := repo.KeyPlaceholderByQuery(len(recordTypes)) @@ -98,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) @@ -160,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 { diff --git a/src/modules/network_data/repository/perf_kpi.go b/src/modules/network_data/repository/perf_kpi.go index 1a00957..065d784 100644 --- a/src/modules/network_data/repository/perf_kpi.go +++ b/src/modules/network_data/repository/perf_kpi.go @@ -7,9 +7,15 @@ type IPerfKPI interface { // SelectGoldKPI 通过网元指标数据信息 SelectGoldKPI(query model.GoldKPIQuery, kpiIds []string) []map[string]any + // select from new kpi report table, exp. kpi_report_upf + SelectKpiReport(query model.GoldKPIQuery, kpiIds []string) []map[string]any + // SelectGoldKPITitle 网元对应的指标名称 SelectGoldKPITitle(neType string) []model.GoldKPITitle // SelectUPFTotalFlow 查询UPF总流量 N3上行 N6下行 SelectUPFTotalFlow(neType, rmUID, startDate, endDate string) map[string]any + + // select upf throughput from new kpi_report + SelectUPFThroughput(neType, rmUID, startDate, endDate string) map[string]any } diff --git a/src/modules/network_data/repository/perf_kpi.impl.go b/src/modules/network_data/repository/perf_kpi.impl.go index 8975ae6..fab63d0 100644 --- a/src/modules/network_data/repository/perf_kpi.impl.go +++ b/src/modules/network_data/repository/perf_kpi.impl.go @@ -86,6 +86,79 @@ func (r *PerfKPIImpl) SelectGoldKPI(query model.GoldKPIQuery, kpiIds []string) [ return results } +func (r *PerfKPIImpl) SelectKpiReport(query model.GoldKPIQuery, kpiIds []string) []map[string]any { + // 查询条件拼接 + var conditions []string + var params []any + var tableName string = "kpi_report_" + if query.RmUID != "" { + conditions = append(conditions, "gk.rm_uid = ?") + params = append(params, query.RmUID) + } + if query.NeType != "" { + conditions = append(conditions, "gk.ne_type = ?") + params = append(params, query.NeType) + tableName += strings.ToLower(query.NeType) + } + var dateTimeStr string = "CONCAT(gk.`date`, \" \", gk.start_time)" + if query.StartTime != "" { + conditions = append(conditions, dateTimeStr+" >= ?") + params = append(params, query.StartTime) + } + if query.EndTime != "" { + conditions = append(conditions, dateTimeStr+" <= ?") + params = append(params, query.EndTime) + } + // 构建查询条件语句 + whereSql := "" + if len(conditions) > 0 { + whereSql += " where " + strings.Join(conditions, " and ") + } + + // 查询字段列 + timeFormat := "DATE_FORMAT(" + dateTimeStr + ", '%Y-%m-%d %H:%i:')" + secondGroup := fmt.Sprintf("LPAD(FLOOR(SECOND(gk.start_time) / %d) * %d, 2, '0')", query.Interval, query.Interval) + groupByField := fmt.Sprintf("CONCAT( %s, %s ) AS timeGroup", timeFormat, secondGroup) + if query.Interval > 60 { + minute := query.Interval / 60 + timeFormat = "DATE_FORMAT(" + dateTimeStr + ", '%Y-%m-%d %H:')" + minuteGroup := fmt.Sprintf("LPAD(FLOOR(MINUTE(gk.start_time) / %d) * %d, 2, '0')", minute, minute) + groupByField = fmt.Sprintf("CONCAT( %s, %s ) AS timeGroup", timeFormat, minuteGroup) + } + var fields = []string{ + groupByField, + "min(CASE WHEN gk.index != '' THEN gk.index ELSE 0 END) AS startIndex", + "min(CASE WHEN gk.ne_type != '' THEN gk.ne_type ELSE 0 END) AS neType", + "min(CASE WHEN gk.ne_name != '' THEN gk.ne_name ELSE 0 END) AS neName", + } + for i, kid := range kpiIds { + // 特殊字段,只取最后一次收到的非0值 + if kid == "AMF.01" || kid == "UDM.01" || kid == "UDM.02" || kid == "UDM.03" || kid == "SMF.01" { + str := fmt.Sprintf("IFNULL(SUBSTRING_INDEX(GROUP_CONCAT( CASE WHEN JSON_EXTRACT(gk.kpi_values, '$[%d].kpi_id') = '%s' THEN JSON_EXTRACT(gk.kpi_values, '$[%d].value') END ), ',', 1), 0) AS '%s'", i, kid, i, kid) + fields = append(fields, str) + } else { + str := fmt.Sprintf("sum(CASE WHEN JSON_EXTRACT(gk.kpi_values, '$[%d].kpi_id') = '%s' THEN JSON_EXTRACT(gk.kpi_values, '$[%d].value') ELSE 0 END) AS '%s'", i, kid, i, kid) + fields = append(fields, str) + } + } + fieldsSql := strings.Join(fields, ",") + + // 查询数据 + if query.SortField == "" { + query.SortField = "timeGroup" + } + if query.SortOrder == "" { + query.SortOrder = "desc" + } + orderSql := fmt.Sprintf(" order by %s %s", query.SortField, query.SortOrder) + querySql := fmt.Sprintf("SELECT %s FROM %s gk %s GROUP BY timeGroup %s", fieldsSql, tableName, whereSql, orderSql) + results, err := datasource.RawDB("", querySql, params) + if err != nil { + logger.Errorf("query err => %v", err) + } + return results +} + // SelectGoldKPITitle 网元对应的指标名称 func (r *PerfKPIImpl) SelectGoldKPITitle(neType string) []model.GoldKPITitle { result := []model.GoldKPITitle{} @@ -131,3 +204,39 @@ func (r *PerfKPIImpl) SelectUPFTotalFlow(neType, rmUID, startDate, endDate strin } return results[0] } + +// SelectUPFTotalFlow 查询UPF总流量 N3上行 N6下行 +func (r *PerfKPIImpl) SelectUPFThroughput(neType, rmUID, startDate, endDate string) map[string]any { + // 查询条件拼接 + var conditions []string + var params []any + if neType != "" { + conditions = append(conditions, "gk.ne_type = ?") + params = append(params, neType) + } + if rmUID != "" { + conditions = append(conditions, "gk.rm_uid = ?") + params = append(params, rmUID) + } + if startDate != "" { + conditions = append(conditions, "gk.date >= ?") + params = append(params, startDate) + } + if endDate != "" { + conditions = append(conditions, "gk.date <= ?") + params = append(params, endDate) + } + // 构建查询条件语句 + whereSql := "" + if len(conditions) > 0 { + whereSql += " where " + strings.Join(conditions, " and ") + } + + // 查询数据 + querySql := fmt.Sprintf("SELECT sum( CASE WHEN JSON_EXTRACT(gk.kpi_values, '$[2].kpi_id') = 'UPF.03' THEN JSON_EXTRACT(gk.kpi_values, '$[2].value') ELSE 0 END ) AS 'up', sum( CASE WHEN JSON_EXTRACT(gk.kpi_values, '$[5].kpi_id') = 'UPF.06' THEN JSON_EXTRACT(gk.kpi_values, '$[5].value') ELSE 0 END ) AS 'down' FROM kpi_report_upf gk %s", whereSql) + results, err := datasource.RawDB("", querySql, params) + if err != nil { + logger.Errorf("query err => %v", err) + } + return results[0] +} diff --git a/src/modules/network_data/repository/ue_event.impl.go b/src/modules/network_data/repository/ue_event.impl.go index 01ab647..8046ffc 100644 --- a/src/modules/network_data/repository/ue_event.impl.go +++ b/src/modules/network_data/repository/ue_event.impl.go @@ -74,6 +74,10 @@ func (r *UEEventImpl) SelectPage(querys model.UEEventQuery) map[string]any { endDate := date.ParseStrToDate(querys.EndTime, date.YYYY_MM_DD_HH_MM_SS) params = append(params, endDate.Unix()) } + if querys.IMSI != "" { + conditions = append(conditions, "JSON_EXTRACT(event_json, '$.imsi') = ?") + params = append(params, querys.IMSI) + } if querys.EventType != "" { eventTypes := strings.Split(querys.EventType, ",") placeholder := repo.KeyPlaceholderByQuery(len(eventTypes)) diff --git a/src/modules/network_data/service/cdr_event.go b/src/modules/network_data/service/cdr_event.go index 27e9917..b05234c 100644 --- a/src/modules/network_data/service/cdr_event.go +++ b/src/modules/network_data/service/cdr_event.go @@ -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) +} diff --git a/src/modules/network_data/service/cdr_event.impl.go b/src/modules/network_data/service/cdr_event.impl.go index 89ac737..fef4a13 100644 --- a/src/modules/network_data/service/cdr_event.impl.go +++ b/src/modules/network_data/service/cdr_event.impl.go @@ -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") +} diff --git a/src/modules/network_data/service/perf_kpi.impl.go b/src/modules/network_data/service/perf_kpi.impl.go index 297657d..1376c17 100644 --- a/src/modules/network_data/service/perf_kpi.impl.go +++ b/src/modules/network_data/service/perf_kpi.impl.go @@ -31,7 +31,8 @@ func (r *PerfKPIImpl) SelectGoldKPI(query model.GoldKPIQuery) []map[string]any { kpiIds = append(kpiIds, kpiId.KPIID) } - data := r.perfKPIRepository.SelectGoldKPI(query, kpiIds) + //data := r.perfKPIRepository.SelectGoldKPI(query, kpiIds) + data := r.perfKPIRepository.SelectKpiReport(query, kpiIds) if data == nil { return []map[string]any{} } @@ -66,7 +67,8 @@ func (r *PerfKPIImpl) SelectUPFTotalFlow(neType, rmUID string, day int) map[stri } } - info = r.perfKPIRepository.SelectUPFTotalFlow(neType, rmUID, startDate, endDate) + //info = r.perfKPIRepository.SelectUPFTotalFlow(neType, rmUID, startDate, endDate) + info = r.perfKPIRepository.SelectUPFThroughput(neType, rmUID, startDate, endDate) // 保存到缓存 infoJSON, _ := json.Marshal(info) diff --git a/src/modules/network_element/controller/ne_action.go b/src/modules/network_element/controller/ne_action.go index 783e3c0..f38e68a 100644 --- a/src/modules/network_element/controller/ne_action.go +++ b/src/modules/network_element/controller/ne_action.go @@ -155,3 +155,52 @@ func (s *NeActionController) Files(c *gin.Context) { "rows": splitRows, })) } + +// 网元服务操作 +// +// PUT /service +func (s *NeActionController) Service(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body struct { + NeType string `json:"neType" binding:"required"` + NeID string `json:"neId" binding:"required"` + Action string `json:"action" binding:"required,oneof=start restart stop reboot poweroff"` // 操作行为 + } + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 查询网元获取IP + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(body.NeType, body.NeID) + if neInfo.NeId != body.NeID || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + neTypeLower := strings.ToLower(neInfo.NeType) + cmdStr := fmt.Sprintf("sudo service %s %s", neTypeLower, body.Action) + if neTypeLower == "omc" { + cmdStr = fmt.Sprintf("nohup sh -c \"sudo systemctl stop restagent && sleep 5s && sudo systemctl %s restagent\" > /dev/null 2>&1 &", body.Action) + } else if neTypeLower == "ims" { + if body.Action == "restart" { + cmdStr = "sudo ims-stop || true && sudo ims-start" + } else { + cmdStr = fmt.Sprintf("sudo ims-%s", body.Action) + } + } + + if body.Action == "reboot" { + cmdStr = "sudo shutdown -r now" + } + if body.Action == "poweroff" { + cmdStr = "sudo shutdown -h now" + } + + _, err := s.neInfoService.NeRunCMD(body.NeType, body.NeID, cmdStr) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + c.JSON(200, result.Ok(nil)) +} diff --git a/src/modules/network_element/controller/ne_host.go b/src/modules/network_element/controller/ne_host.go index 7baca4b..9336733 100644 --- a/src/modules/network_element/controller/ne_host.go +++ b/src/modules/network_element/controller/ne_host.go @@ -167,8 +167,7 @@ func (s *NeHostController) Remove(c *gin.Context) { func (s *NeHostController) Test(c *gin.Context) { language := ctx.AcceptLanguage(c) var body model.NeHost - err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil { + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } @@ -176,8 +175,13 @@ func (s *NeHostController) Test(c *gin.Context) { if body.HostType == "ssh" { var connSSH ssh.ConnSSH body.CopyTo(&connSSH) - - client, err := connSSH.NewClient() + var client *ssh.ConnSSH + var err error + if body.AuthMode == "2" { + client, err = connSSH.NewClientByLocalPrivate() + } else { + client, err = connSSH.NewClient() + } if err != nil { // 连接主机失败,请检查连接参数后重试 c.JSON(200, result.ErrMsg(i18n.TKey(language, "neHost.errByHostInfo"))) @@ -199,7 +203,12 @@ func (s *NeHostController) Test(c *gin.Context) { return } defer client.Close() - c.JSON(200, result.Ok(nil)) + if strings.HasSuffix(client.LastResult, ">") || strings.HasSuffix(client.LastResult, "> ") || strings.HasSuffix(client.LastResult, "# ") { + c.JSON(200, result.Ok(nil)) + } else { + // 连接主机失败,请检查连接参数后重试 + c.JSON(200, result.ErrMsg(i18n.TKey(language, "neHost.errByHostInfo"))) + } return } } @@ -213,8 +222,7 @@ func (s *NeHostController) Cmd(c *gin.Context) { HostID string `json:"hostId" binding:"required"` // 主机ID Cmd string `json:"cmd" binding:"required"` // 执行命令 } - err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil { + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } @@ -230,8 +238,13 @@ func (s *NeHostController) Cmd(c *gin.Context) { if neHost.HostType == "ssh" { var connSSH ssh.ConnSSH neHost.CopyTo(&connSSH) - - client, err := connSSH.NewClient() + var client *ssh.ConnSSH + var err error + if neHost.AuthMode == "2" { + client, err = connSSH.NewClientByLocalPrivate() + } else { + client, err = connSSH.NewClient() + } if err != nil { // 连接主机失败,请检查连接参数后重试 c.JSON(200, result.ErrMsg(i18n.TKey(language, "neHost.errByHostInfo"))) @@ -278,17 +291,21 @@ func (s *NeHostController) Cmd(c *gin.Context) { func (s *NeHostController) CheckBySSH(c *gin.Context) { language := ctx.AcceptLanguage(c) var body model.NeHost - err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil { + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } var connSSH ssh.ConnSSH body.CopyTo(&connSSH) - // 创建链接SSH客户端 - client, err := connSSH.NewClient() + var client *ssh.ConnSSH + var err error + if body.AuthMode == "2" { + client, err = connSSH.NewClientByLocalPrivate() + } else { + client, err = connSSH.NewClient() + } if err != nil { // 连接主机失败,请检查连接参数后重试 c.JSON(200, result.ErrMsg(i18n.TKey(language, "neHost.errByHostInfo"))) @@ -339,18 +356,22 @@ func (s *NeHostController) CheckBySSH(c *gin.Context) { } // 本地免密创建链接直连 - lcoalConnSSH := ssh.ConnSSH{ - User: body.User, - Addr: body.Addr, - Port: body.Port, - } - lcoalClient, err := lcoalConnSSH.NewClientByLocalPrivate() - if err == nil { + if body.AuthMode == "2" { data["sshLink"] = true } else { - data["sshLink"] = false + lcoalConnSSH := ssh.ConnSSH{ + User: body.User, + Addr: body.Addr, + Port: body.Port, + } + lcoalClient, err := lcoalConnSSH.NewClientByLocalPrivate() + if err == nil { + data["sshLink"] = true + defer lcoalClient.Close() + } else { + data["sshLink"] = false + } } - defer lcoalClient.Close() c.JSON(200, result.OkData(data)) } @@ -361,8 +382,7 @@ func (s *NeHostController) CheckBySSH(c *gin.Context) { func (s *NeHostController) AuthorizedBySSH(c *gin.Context) { language := ctx.AcceptLanguage(c) var body model.NeHost - err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil { + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil || body.AuthMode == "2" { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } @@ -377,8 +397,8 @@ func (s *NeHostController) AuthorizedBySSH(c *gin.Context) { lcoalClient, err := lcoalConnSSH.NewClientByLocalPrivate() if err == nil { sshLink = true + defer lcoalClient.Close() } - defer lcoalClient.Close() if sshLink { // 连接主机成功,无需重复免密授权认证 c.JSON(200, result.OkMsg(i18n.TKey(language, "neHost.okBySSHLink"))) diff --git a/src/modules/network_element/controller/ne_info.go b/src/modules/network_element/controller/ne_info.go index 55c10c3..177a19b 100644 --- a/src/modules/network_element/controller/ne_info.go +++ b/src/modules/network_element/controller/ne_info.go @@ -17,15 +17,21 @@ import ( // 实例化控制层 NeInfoController 结构体 var NewNeInfo = &NeInfoController{ - neInfoService: neService.NewNeInfoImpl, + neInfoService: neService.NewNeInfoImpl, + neLicenseService: neService.NewNeLicenseImpl, + neVersionService: neService.NewNeVersionImpl, } // 网元信息请求 // -// PATH / +// PATH /info type NeInfoController struct { // 网元信息服务 neInfoService neService.INeInfo + // 网元授权激活信息服务 + neLicenseService neService.INeLicense + // 网元版本信息服务 + neVersionService neService.INeVersion } // neStateCacheMap 网元状态缓存最后一次成功的信息 @@ -114,7 +120,8 @@ func (s *NeInfoController) ListAll(c *gin.Context) { var querys struct { NeType string `form:"neType"` NeId string `form:"neId"` - BandStatus string `form:"bandStatus"` + BandStatus bool `form:"bandStatus"` + BandHost bool `form:"bandHost"` } if err := c.ShouldBindQuery(&querys); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) @@ -129,8 +136,7 @@ func (s *NeInfoController) ListAll(c *gin.Context) { if querys.NeId != "" { ne.NeId = querys.NeId } - bandStatus := parse.Boolean(querys.BandStatus) - neList := s.neInfoService.SelectList(ne, bandStatus) + neList := s.neInfoService.SelectList(ne, querys.BandStatus, querys.BandHost) if len(neList) == 0 { c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) return @@ -138,53 +144,74 @@ func (s *NeInfoController) ListAll(c *gin.Context) { c.JSON(200, result.OkData(neList)) } -// 网元端配置文件读取 +// 网元端Para5G配置文件读取 // -// GET /configFile -func (s *NeInfoController) ConfigFileRead(c *gin.Context) { +// GET /para5GFile +func (s *NeInfoController) Para5GFileRead(c *gin.Context) { + data, err := s.neInfoService.NeConfPara5GRead() + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + c.JSON(200, result.OkData(data)) +} + +// 网元端Para5G配置文件写入 +// +// PUT /para5GFile +func (s *NeInfoController) Para5GFileWrite(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body struct { + Content map[string]any `json:"content" binding:"required"` // 内容 + SyncNE []string `json:"syncNe"` // 同步到网元 + } + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + err := s.neInfoService.NeConfPara5GWirte(body.Content, body.SyncNE) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + c.JSON(200, result.Ok(nil)) +} + +// 网元端OAM配置文件读取 +// +// GET /oamFile +func (s *NeInfoController) OAMFileRead(c *gin.Context) { language := ctx.AcceptLanguage(c) var querys struct { - NeType string `form:"neType" binding:"required"` - NeID string `form:"neId" binding:"required"` - FilePath string `form:"filePath"` // 不带文件路径时进行复制覆盖本地网元配置目录 + NeType string `form:"neType" binding:"required"` + NeID string `form:"neId" binding:"required"` } if err := c.ShouldBindQuery(&querys); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } - // 查询网元获取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"))) + data, err := s.neInfoService.NeConfOAMRead(querys.NeType, querys.NeID) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) return } - - data := s.neInfoService.NeConfigFileRead(neInfo, querys.FilePath) - if querys.FilePath == "" { - c.JSON(200, result.OkData(data)) - return - } - if len(data) > 0 { - c.JSON(200, result.OkData(data[0])) - return - } - c.JSON(200, result.ErrMsg("no data")) + c.JSON(200, result.OkData(data)) } -// 网元端配置文件写入 +// 网元端OAM配置文件写入 // -// PUT /configFile -func (s *NeInfoController) ConfigFileWrite(c *gin.Context) { +// PUT /oamFile +func (s *NeInfoController) OAMFileWrite(c *gin.Context) { language := ctx.AcceptLanguage(c) var body struct { - NeType string `json:"neType" binding:"required"` - NeID string `json:"neId" binding:"required"` - FilePath string `json:"filePath" binding:"required"` - Content string `json:"content" binding:"required"` - Sync bool `json:"sync"` + NeType string `json:"neType" binding:"required"` + NeID string `json:"neId" binding:"required"` + Content map[string]any `json:"content" binding:"required"` // 内容 + Sync bool `json:"sync"` // 同步到网元 } - if err := c.ShouldBindJSON(&body); err != nil { + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } @@ -196,7 +223,7 @@ func (s *NeInfoController) ConfigFileWrite(c *gin.Context) { return } - err := s.neInfoService.NeConfigFileWirte(neInfo, body.FilePath, body.Content, body.Sync) + err := s.neInfoService.NeConfOAMSync(neInfo, body.Content, body.Sync) if err != nil { c.JSON(200, result.ErrMsg(err.Error())) return @@ -260,22 +287,53 @@ func (s *NeInfoController) Add(c *gin.Context) { } // 获取网元状态是否正常 - _, err = neService.NeState(body) + body.ServerState, err = neService.NeState(body) if err != nil { - body.Status = "1" + body.Status = "0" } else { // 下发网管配置信息给网元 _, err = neService.NeConfigOMC(body) if err == nil { - body.Status = "0" + body.Status = "1" } else { - body.Status = "3" + body.Status = "2" } } + loginUserName := ctx.LoginUserToUserName(c) + // 新增Version信息 + neVersion := model.NeVersion{ + NeType: body.NeType, + NeId: body.NeId, + CreateBy: loginUserName, + } + // 新增License信息 + neLicense := model.NeLicense{ + NeType: body.NeType, + NeId: body.NeId, + CreateBy: loginUserName, + } + + // 已有网元可获取的信息 + if body.ServerState != nil { + if v, ok := body.ServerState["version"]; ok && v != nil { + neVersion.Version = v.(string) + } + if v, ok := body.ServerState["sn"]; ok && v != nil { + neLicense.SerialNum = v.(string) + } + if v, ok := body.ServerState["expire"]; ok && v != nil { + neLicense.ExpiryDate = v.(string) + neLicense.Status = "1" + } + } + + s.neVersionService.Insert(neVersion) + s.neLicenseService.Insert(neLicense) + body.CreateBy = loginUserName insertId := s.neInfoService.Insert(body) if insertId != "" { - c.JSON(200, result.Ok(nil)) + c.JSON(200, result.OkData(insertId)) return } c.JSON(200, result.Err(nil)) @@ -294,8 +352,8 @@ func (s *NeInfoController) Edit(c *gin.Context) { } // 检查属性值唯一 - uniqueHostCmd := s.neInfoService.CheckUniqueNeTypeAndNeId(body.NeType, body.NeId, body.ID) - if !uniqueHostCmd { + uniqueInfo := s.neInfoService.CheckUniqueNeTypeAndNeId(body.NeType, body.NeId, body.ID) + if !uniqueInfo { // 网元信息操作【%s】失败,同类型下标识已存在 msg := i18n.TTemplate(language, "neInfo.errKeyExists", map[string]any{"key": body.NeId}) c.JSON(200, result.ErrMsg(msg)) @@ -309,21 +367,64 @@ func (s *NeInfoController) Edit(c *gin.Context) { c.JSON(200, result.ErrMsg(i18n.TKey(language, "neInfo.noData"))) return } + // 赋予主机ID + if neInfo.HostIDs != "" && len(body.Hosts) > 0 { + hostIDs := strings.Split(neInfo.HostIDs, ",") + for index, id := range hostIDs { + body.Hosts[index].HostID = id + } + } // 获取网元状态是否正常 - _, err = neService.NeState(body) + body.ServerState, err = neService.NeState(body) if err != nil { - body.Status = "1" + body.Status = "0" } else { // 下发网管配置信息给网元 _, err = neService.NeConfigOMC(body) if err == nil { - body.Status = "0" + body.Status = "1" } else { - body.Status = "3" + body.Status = "2" } } + loginUserName := ctx.LoginUserToUserName(c) + neLicense := s.neLicenseService.SelectByNeTypeAndNeID(neInfo.NeType, neInfo.NeId) + neVersion := s.neVersionService.SelectByNeTypeAndNeID(neInfo.NeType, neInfo.NeId) + + // 已有网元可获取的信息 + if body.ServerState != nil { + if v, ok := body.ServerState["version"]; ok && v != nil { + neVersion.Version = v.(string) + neVersion.UpdateBy = loginUserName + } + if v, ok := body.ServerState["sn"]; ok && v != nil { + neLicense.SerialNum = v.(string) + } + if v, ok := body.ServerState["expire"]; ok && v != nil { + neLicense.ExpiryDate = v.(string) + neLicense.Status = "1" + neLicense.UpdateBy = loginUserName + } + } + + if neVersion.ID != "" { + if neVersion.NeType != body.NeType || neVersion.NeId != body.NeId { + neVersion.NeType = body.NeType + neVersion.NeId = body.NeId + } + s.neVersionService.Update(neVersion) + } + if neLicense.ID != "" { + if neLicense.NeType != body.NeType || neLicense.NeId != body.NeId { + neLicense.NeType = body.NeType + neLicense.NeId = body.NeId + } + s.neLicenseService.Update(neLicense) + } + + body.UpdateBy = loginUserName rows := s.neInfoService.Update(body) if rows > 0 { c.JSON(200, result.Ok(nil)) diff --git a/src/modules/network_element/controller/ne_license.go b/src/modules/network_element/controller/ne_license.go index 7022df8..d503b8d 100644 --- a/src/modules/network_element/controller/ne_license.go +++ b/src/modules/network_element/controller/ne_license.go @@ -2,11 +2,9 @@ package controller import ( "fmt" - "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_element/model" neService "be.ems/src/modules/network_element/service" @@ -37,6 +35,14 @@ func (s *NeLicenseController) List(c *gin.Context) { querys := ctx.QueryMap(c) data := s.neLicenseService.SelectPage(querys) + // 过滤屏蔽授权文件 + rows := data["rows"].([]model.NeLicense) + arr := &rows + for i := range *arr { + (*arr)[i].ActivationRequestCode = "-" + (*arr)[i].LicensePath = "-" + } + c.JSON(200, result.Ok(data)) } @@ -61,109 +67,28 @@ func (s *NeLicenseController) Info(c *gin.Context) { c.JSON(200, result.OkData(neLicense)) } -// 网元授权激活信息新增 +// 网元neType和neID查询 // -// POST / -func (s *NeLicenseController) Add(c *gin.Context) { +// GET /byTypeAndID +func (s *NeLicenseController) NeTypeAndID(c *gin.Context) { language := ctx.AcceptLanguage(c) - var body model.NeLicense - err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil || body.ID != "" { + var querys struct { + NeType string `form:"neType" binding:"required"` + NeId string `form:"neId" binding:"required"` + } + if err := c.ShouldBindQuery(&querys); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } - // 查询网元获取IP - neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(body.NeType, body.NeId) - if neInfo.NeId != body.NeId || neInfo.IP == "" { - c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) - return - } - - // 检查属性值唯一 - uniqueInfo := s.neLicenseService.CheckUniqueTypeAndID(neInfo.NeType, neInfo.NeId, "") - if !uniqueInfo { - // 网元授权激活操作【%s】失败,网元类型信息已存在 - msg := i18n.TTemplate(language, "neLicense.errKeyExists", map[string]any{"name": neInfo.NeType}) - c.JSON(200, result.ErrMsg(msg)) - return - } - - // 读取授权码 - code, _ := s.neLicenseService.ReadLicenseInfo(neInfo) - body.ActivationRequestCode = code - - body.CreateBy = ctx.LoginUserToUserName(c) - insertId := s.neLicenseService.Insert(body) - if insertId != "" { - c.JSON(200, result.Ok(nil)) - return - } - c.JSON(200, result.Err(nil)) -} - -// 网元授权激活信息修改 -// -// PUT / -func (s *NeLicenseController) Edit(c *gin.Context) { - language := ctx.AcceptLanguage(c) - var body model.NeLicense - err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil || body.ID == "" { - c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) - return - } - - // 检查属性值唯一 - uniqueInfo := s.neLicenseService.CheckUniqueTypeAndID(body.NeType, body.NeId, body.ID) - if !uniqueInfo { - // 网元授权激活操作【%s】失败,网元类型信息已存在 - msg := i18n.TTemplate(language, "neLicense.errKeyExists", map[string]any{"name": body.NeType}) - c.JSON(200, result.ErrMsg(msg)) - return - } - - // 检查是否存在 - neLicense := s.neLicenseService.SelectById(body.ID) - if neLicense.ID != body.ID { + neLicense := s.neLicenseService.SelectByNeTypeAndNeID(querys.NeType, querys.NeId) + if neLicense.NeId != querys.NeId { // 没有可访问网元授权激活数据! c.JSON(200, result.ErrMsg(i18n.TKey(language, "neLicense.noData"))) return } - body.UpdateBy = ctx.LoginUserToUserName(c) - rows := s.neLicenseService.Update(body) - if rows > 0 { - c.JSON(200, result.Ok(nil)) - return - } - c.JSON(200, result.Err(nil)) -} - -// 网元授权激活信息删除 -// -// DELETE /:licenseIds -func (s *NeLicenseController) Remove(c *gin.Context) { - language := ctx.AcceptLanguage(c) - licenseIds := c.Param("licenseIds") - if licenseIds == "" { - c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) - return - } - // 处理字符转id数组后去重 - ids := strings.Split(licenseIds, ",") - uniqueIDs := parse.RemoveDuplicates(ids) - if len(uniqueIDs) <= 0 { - c.JSON(200, result.Err(nil)) - return - } - rows, err := s.neLicenseService.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)) + c.JSON(200, result.OkData(neLicense)) } // 网元授权激活授权申请码 @@ -180,15 +105,8 @@ func (s *NeLicenseController) Code(c *gin.Context) { 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 - } - // 检查是否存在授权记录 - neLicense := s.neLicenseService.SelectByNeTypeAndNeID(neInfo.NeType, neInfo.NeId) + neLicense := s.neLicenseService.SelectByNeTypeAndNeID(querys.NeType, querys.NeId) if neLicense.NeId != querys.NeId { // 没有可访问网元授权激活数据! c.JSON(200, result.ErrMsg(i18n.TKey(language, "neLicense.noData"))) @@ -196,7 +114,7 @@ func (s *NeLicenseController) Code(c *gin.Context) { } // 更新授权码 - code, licensePath := s.neLicenseService.ReadLicenseInfo(neInfo) + code, licensePath := s.neLicenseService.ReadLicenseInfo(neLicense) neLicense.ActivationRequestCode = code if licensePath != "" { neLicense.LicensePath = licensePath @@ -218,7 +136,7 @@ func (s *NeLicenseController) Change(c *gin.Context) { language := ctx.AcceptLanguage(c) var body model.NeLicense err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil || body.HostId == "" { + if err != nil || body.LicensePath == "" { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } @@ -226,23 +144,30 @@ func (s *NeLicenseController) Change(c *gin.Context) { // 检查是否存在授权记录 neLicense := s.neLicenseService.SelectByNeTypeAndNeID(body.NeType, body.NeId) if neLicense.NeId != body.NeId { - body.Status = "0" - body.CreateBy = ctx.LoginUserToUserName(c) - body.ID = s.neLicenseService.Insert(body) - } else { - neLicense.LicensePath = body.LicensePath - neLicense.Status = "0" - neLicense.UpdateBy = ctx.LoginUserToUserName(c) - s.neLicenseService.Update(neLicense) - } - - // 进行上传替换 - err = s.neLicenseService.UploadToNeHost(body) - if err != nil { - c.JSON(200, result.ErrMsg(err.Error())) + // 没有可访问网元授权激活数据! + c.JSON(200, result.ErrMsg(i18n.TKey(language, "neLicense.noData"))) return } - c.JSON(200, result.Ok(nil)) + + // 更新授权记录 + if body.Remark != "" { + neLicense.Remark = body.Remark + } + neLicense.LicensePath = body.LicensePath + neLicense.Status = "0" + neLicense.UpdateBy = ctx.LoginUserToUserName(c) + upRows := s.neLicenseService.Update(neLicense) + if upRows > 0 { + // 进行上传替换 + err = s.neLicenseService.UploadLicense(body) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + c.JSON(200, result.Ok(nil)) + return + } + c.JSON(200, result.Err(nil)) } // 网元授权激活状态 @@ -259,43 +184,41 @@ func (s *NeLicenseController) State(c *gin.Context) { 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 - } - // 检查是否存在授权记录 - neLicense := s.neLicenseService.SelectByNeTypeAndNeID(neInfo.NeType, neInfo.NeId) + neLicense := s.neLicenseService.SelectByNeTypeAndNeID(querys.NeType, querys.NeId) if neLicense.NeId != querys.NeId { // 没有可访问网元授权激活数据! c.JSON(200, result.ErrMsg(i18n.TKey(language, "neLicense.noData"))) return } - // 查询网元状态 - neState, err := neService.NeState(neInfo) - if err != nil { - c.JSON(200, result.ErrMsg("network element service anomaly")) + // 查询网元获取IP获取网元状态 + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neLicense.NeType, neLicense.NeId) + if neInfo.NeId != neLicense.NeId || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) return } + if neState, err := neService.NeState(neInfo); err == nil { + neLicense.Status = "1" + neLicense.SerialNum = fmt.Sprint(neState["sn"]) + neLicense.ExpiryDate = fmt.Sprint(neState["expire"]) + code, licensePath := s.neLicenseService.ReadLicenseInfo(neLicense) + neLicense.ActivationRequestCode = code + neLicense.LicensePath = licensePath + } else { + neLicense.Status = "0" + } // 更新授权信息 - neLicense.SerialNum = fmt.Sprint(neState["sn"]) - neLicense.ExpiryDate = fmt.Sprint(neState["expire"]) - code, licensePath := s.neLicenseService.ReadLicenseInfo(neInfo) - neLicense.ActivationRequestCode = code - neLicense.LicensePath = licensePath - neLicense.Status = "1" neLicense.UpdateBy = ctx.LoginUserToUserName(c) - rows := s.neLicenseService.Update(neLicense) - if rows > 0 { + s.neLicenseService.Update(neLicense) + + if neLicense.Status == "1" { c.JSON(200, result.OkData(map[string]string{ "sn": neLicense.SerialNum, "expire": neLicense.ExpiryDate, })) return } - c.JSON(200, result.Err(nil)) + c.JSON(200, result.ErrMsg(fmt.Sprintf("%s service status exception", neLicense.NeType))) } diff --git a/src/modules/network_element/controller/ne_software.go b/src/modules/network_element/controller/ne_software.go index 03d1614..d5658d7 100644 --- a/src/modules/network_element/controller/ne_software.go +++ b/src/modules/network_element/controller/ne_software.go @@ -69,15 +69,26 @@ func (s *NeSoftwareController) Add(c *gin.Context) { return } - // 检查属性值唯一 - uniqueSoftware := s.neSoftwareService.CheckUniqueTypeAndNameAndVersion(body.NeType, body.Name, body.Version, "") - if !uniqueSoftware { - // 网元软件包操作【%s】失败,网元类型与文件名版本已存在 - msg := i18n.TTemplate(language, "neSoftware.errKeyExists", map[string]any{"name": body.Name}) - c.JSON(200, result.ErrMsg(msg)) - return + // 找到已存在的删除后重新添加 + neSoftwares := s.neSoftwareService.SelectList(model.NeSoftware{ + NeType: body.NeType, + Name: body.Name, + Version: body.Version, + }) + if len(neSoftwares) > 0 { + neSoftware := neSoftwares[0] + s.neSoftwareService.DeleteByIds([]string{neSoftware.ID}) } + // 检查属性值唯一 + // uniqueSoftware := s.neSoftwareService.CheckUniqueTypeAndNameAndVersion(body.NeType, body.Name, body.Version, "") + // if !uniqueSoftware { + // // 网元软件包操作【%s】失败,网元类型与文件名版本已存在 + // msg := i18n.TTemplate(language, "neSoftware.errKeyExists", map[string]any{"name": body.Name}) + // c.JSON(200, result.ErrMsg(msg)) + // return + // } + body.CreateBy = ctx.LoginUserToUserName(c) insertId := s.neSoftwareService.Insert(body) if insertId != "" { @@ -151,40 +162,32 @@ func (s *NeSoftwareController) Remove(c *gin.Context) { c.JSON(200, result.OkMsg(msg)) } -// 网元软件包安装检查 +// 网元软件包设为网元新版本 // -// POST /checkInstall -func (s *NeSoftwareController) CheckInstall(c *gin.Context) { +// POST /newNeVersion +func (s *NeSoftwareController) NewNeVersion(c *gin.Context) { language := ctx.AcceptLanguage(c) var body model.NeSoftware err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil || body.HostId == "" { + if err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } - // 检查是否存在软件包记录 + // 找到已存在的软件包信息 neSoftwares := s.neSoftwareService.SelectList(model.NeSoftware{ NeType: body.NeType, Name: body.Name, Version: body.Version, }) - if len(neSoftwares) <= 0 { - body.CreateBy = ctx.LoginUserToUserName(c) - body.ID = s.neSoftwareService.Insert(body) - } else { + if len(neSoftwares) > 0 { neSoftware := neSoftwares[0] - neSoftware.Path = body.Path - neSoftware.Description = body.Description - neSoftware.UpdateBy = ctx.LoginUserToUserName(c) - s.neSoftwareService.Update(neSoftware) - } - - // 进行安装检查 - cmdStrArr, err := s.neSoftwareService.UploadToNeHost(body) - if err != nil { - c.JSON(200, result.ErrMsg(err.Error())) + s.neSoftwareService.UpdateVersions(neSoftware, model.NeVersion{ + NeType: neSoftware.NeType, + UpdateBy: ctx.LoginUserToUserName(c), + }) + c.JSON(200, result.Ok(nil)) return } - c.JSON(200, result.OkData(cmdStrArr)) + c.JSON(200, result.Err(nil)) } diff --git a/src/modules/network_element/controller/ne_version.go b/src/modules/network_element/controller/ne_version.go index 5fab991..94bc2d8 100644 --- a/src/modules/network_element/controller/ne_version.go +++ b/src/modules/network_element/controller/ne_version.go @@ -1,13 +1,9 @@ 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_element/model" neService "be.ems/src/modules/network_element/service" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" @@ -57,96 +53,34 @@ func (s *NeVersionController) Info(c *gin.Context) { c.JSON(200, result.OkData(neVersion)) } -// 网元版本信息新增 +// 网元版本操作 // -// POST / -func (s *NeVersionController) Add(c *gin.Context) { +// POST /operate +func (s *NeVersionController) Operate(c *gin.Context) { language := ctx.AcceptLanguage(c) - var body model.NeVersion - err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil || body.ID != "" { + var body struct { + Action string `json:"action" binding:"required,oneof=install upgrade rollback"` // 操作行为 + NeType string `json:"neType" gorm:"ne_type" binding:"required"` // 网元类型 + NeId string `json:"neId" gorm:"ne_id" binding:"required"` // 网元ID + Preinput map[string]string `json:"preinput" ` // 预先输入参数 + } + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } - // 检查属性值唯一 - uniqueInfo := s.neVersionService.CheckUniqueTypeAndID(body.NeType, body.NeId, "") - if !uniqueInfo { - // 网元版本操作【%s】失败,网元类型信息已存在 - msg := i18n.TTemplate(language, "neVersion.errKeyExists", map[string]any{"name": body.NeType}) - c.JSON(200, result.ErrMsg(msg)) - return - } - - body.CreateBy = ctx.LoginUserToUserName(c) - insertId := s.neVersionService.Insert(body) - if insertId != "" { - c.JSON(200, result.Ok(nil)) - return - } - c.JSON(200, result.Err(nil)) -} - -// 网元版本信息修改 -// -// PUT / -func (s *NeVersionController) Edit(c *gin.Context) { - language := ctx.AcceptLanguage(c) - var body model.NeVersion - err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil || body.ID == "" { - c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) - return - } - - // 检查属性值唯一 - uniqueInfo := s.neVersionService.CheckUniqueTypeAndID(body.NeType, body.NeId, body.ID) - if !uniqueInfo { - // 网元版本操作【%s】失败,网元类型信息已存在 - msg := i18n.TTemplate(language, "neVersion.errKeyExists", map[string]any{"name": body.NeType}) - c.JSON(200, result.ErrMsg(msg)) - return - } - - // 检查是否存在 - neVersion := s.neVersionService.SelectById(body.ID) - if neVersion.ID != body.ID { + neVersion := s.neVersionService.SelectByNeTypeAndNeID(body.NeType, body.NeId) + if neVersion.NeId != body.NeId { // 没有可访问网元版本数据! c.JSON(200, result.ErrMsg(i18n.TKey(language, "neVersion.noData"))) return } - body.UpdateBy = ctx.LoginUserToUserName(c) - rows := s.neVersionService.Update(body) - if rows > 0 { - c.JSON(200, result.Ok(nil)) - return - } - c.JSON(200, result.Err(nil)) -} - -// 网元版本信息删除 -// -// DELETE /:versionIds -func (s *NeVersionController) Remove(c *gin.Context) { - language := ctx.AcceptLanguage(c) - versionIds := c.Param("versionIds") - if versionIds == "" { - c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) - return - } - // 处理字符转id数组后去重 - ids := strings.Split(versionIds, ",") - uniqueIDs := parse.RemoveDuplicates(ids) - if len(uniqueIDs) <= 0 { - c.JSON(200, result.Err(nil)) - return - } - rows, err := s.neVersionService.DeleteByIds(uniqueIDs) + // 进行相关命令操作 + output, err := s.neVersionService.Operate(body.Action, neVersion, body.Preinput) 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)) + c.JSON(200, result.OkData(output)) } diff --git a/src/modules/network_element/model/ne_host.go b/src/modules/network_element/model/ne_host.go index a479635..ba7362b 100644 --- a/src/modules/network_element/model/ne_host.go +++ b/src/modules/network_element/model/ne_host.go @@ -11,7 +11,7 @@ type NeHost struct { Addr string `json:"addr" gorm:"column:addr" binding:"required"` // 主机地址 Port int64 `json:"port" gorm:"column:port" binding:"required,number,max=65535,min=1"` // SSH端口 User string `json:"user" gorm:"column:user" binding:"required"` // 主机用户名 - AuthMode string `json:"authMode" gorm:"column:auth_mode" binding:"oneof=0 1"` // 认证模式(0密码 1主机私钥) + AuthMode string `json:"authMode" gorm:"column:auth_mode" binding:"oneof=0 1 2"` // 认证模式(0密码 1主机私钥 2已免密) Password string `json:"password" gorm:"column:password"` // 认证密码 PrivateKey string `json:"privateKey" gorm:"column:private_key"` // 认证私钥 PassPhrase string `json:"passPhrase" gorm:"column:pass_phrase"` // 认证私钥密码 diff --git a/src/modules/network_element/model/ne_info.go b/src/modules/network_element/model/ne_info.go index c08e71a..373df59 100644 --- a/src/modules/network_element/model/ne_info.go +++ b/src/modules/network_element/model/ne_info.go @@ -2,21 +2,25 @@ package model // NeInfo 网元信息对象 ne_info type NeInfo struct { - ID string `json:"id"` - NeType string `json:"neType" binding:"required"` - NeId string `json:"neId" binding:"required"` - RmUID string `json:"rmUid"` - NeName string `json:"neName"` - IP string `json:"ip" binding:"required"` - Port int64 `json:"port" binding:"required,number,max=65535,min=1"` - PvFlag string `json:"pvFlag" binding:"oneof=PNF VNF"` // enum('PNF','VNF') - Province string `json:"province"` - VendorName string `json:"vendorName"` - Dn string `json:"dn"` - NeAddress string `json:"neAddress"` - Status string `json:"status"` // 0: 在线 1: 下线 2: 备用 3: 待下发配置 - UpdateTime string `json:"updateTime"` - HostIDs string `json:"hostIds"` // 网元主机ID组 数据格式(ssh,telnet,telnet) + ID string `json:"id" gorm:"id"` + NeType string `json:"neType" gorm:"ne_type" binding:"required"` + NeId string `json:"neId" gorm:"ne_id" binding:"required"` + RmUID string `json:"rmUid" gorm:"rm_uid"` + NeName string `json:"neName" gorm:"ne_name"` + IP string `json:"ip" gorm:"ip" binding:"required"` + Port int64 `json:"port" gorm:"port" binding:"required,number,max=65535,min=1"` + PvFlag string `json:"pvFlag" gorm:"pv_flag" binding:"oneof=PNF VNF"` // ''PNF'',''VNF'' + Province string `json:"province" gorm:"province"` // 省份地域 + VendorName string `json:"vendorName" gorm:"vendor_name"` + Dn string `json:"dn" gorm:"dn"` + NeAddress string `json:"neAddress" gorm:"ne_address"` // MAC地址 + HostIDs string `json:"hostIds" gorm:"host_ids"` // 网元主机ID组 数据格式(ssh,telnet,telnet) + Status string `json:"status" gorm:"status"` // 0离线 1在线 2配置待下发 + Remark string `json:"remark" gorm:"remark"` // 备注 + CreateBy string `json:"createBy" gorm:"create_by"` // 创建者 + CreateTime int64 `json:"createTime" gorm:"create_time"` // 创建时间 + UpdateBy string `json:"updateBy" gorm:"update_by"` // 更新者 + UpdateTime int64 `json:"updateTime" gorm:"update_time"` // 更新时间 // ====== 非数据库字段属性 ====== diff --git a/src/modules/network_element/model/ne_license.go b/src/modules/network_element/model/ne_license.go index 2f202bc..0a08802 100644 --- a/src/modules/network_element/model/ne_license.go +++ b/src/modules/network_element/model/ne_license.go @@ -9,7 +9,7 @@ type NeLicense struct { LicensePath string `json:"licensePath" gorm:"license_path"` // 激活授权文件 SerialNum string `json:"serialNum" gorm:"serial_num"` // 序列号 ExpiryDate string `json:"expiryDate" gorm:"expiry_date"` // 许可证到期日期 - Status string `json:"status" gorm:"status"` // 状态 ''ACTIVE'',''INACTIVE'',''PENDING'' + Status string `json:"status" gorm:"status"` // 状态 0无效 1有效 Remark string `json:"remark" gorm:"remark"` // 备注 CreateBy string `json:"createBy" gorm:"create_by"` // 创建者 CreateTime int64 `json:"createTime" gorm:"create_time"` // 创建时间 @@ -18,8 +18,7 @@ type NeLicense struct { // ====== 非数据库字段属性 ====== - Reload bool `json:"reload,omitempty" gorm:"-"` // 刷新重启网元 - HostId string `json:"hostId,omitempty" gorm:"-"` // 已记录的主机ID + Reload bool `json:"reload,omitempty" gorm:"-"` // 刷新重启网元 } // TableName 表名称 diff --git a/src/modules/network_element/model/ne_software.go b/src/modules/network_element/model/ne_software.go index d016616..3eaf956 100644 --- a/src/modules/network_element/model/ne_software.go +++ b/src/modules/network_element/model/ne_software.go @@ -15,7 +15,7 @@ type NeSoftware struct { // ====== 非数据库字段属性 ====== - HostId string `json:"hostId,omitempty" gorm:"-"` // 已记录的主机ID + NeId string `json:"neId,omitempty" gorm:"-"` // 网元ID } // TableName 表名称 diff --git a/src/modules/network_element/model/ne_version.go b/src/modules/network_element/model/ne_version.go index 50f21cc..ddde0af 100644 --- a/src/modules/network_element/model/ne_version.go +++ b/src/modules/network_element/model/ne_version.go @@ -5,13 +5,16 @@ type NeVersion struct { ID string `json:"id" gorm:"id"` NeType string `json:"neType" gorm:"ne_type" binding:"required"` // 网元类型 NeId string `json:"neId" gorm:"ne_id" binding:"required"` // 网元ID + Name string `json:"name" gorm:"name"` // 当前包名 Version string `json:"version" gorm:"version" binding:"required"` // 当前版本 Path string `json:"path" gorm:"path" binding:"required"` // 当前软件包 + PreName string `json:"preName" gorm:"pre_name"` // 上一版本包名 PreVersion string `json:"preVersion" gorm:"pre_version"` // 上一版本 PrePath string `json:"prePath" gorm:"pre_path"` // 上一版本软件包 - NewVersion string `json:"newVersion" gorm:"new_version"` // 下一版本 - NewPath string `json:"newPath" gorm:"new_path"` // 下一版本软件包 - Status string `json:"status" gorm:"status"` // 当前状态 (Uploaded下一版本上传 Inactive下一版本待激活 Active当前已激活) + NewName string `json:"newName" gorm:"new_name"` // 新版本包名 + NewVersion string `json:"newVersion" gorm:"new_version"` // 新版本 + NewPath string `json:"newPath" gorm:"new_path"` // 新版本软件包 + Status string `json:"status" gorm:"status"` // 当前状态 1当前版本 2上一版本 3有新版本 CreateBy string `json:"createBy" gorm:"column:create_by"` // 创建者 CreateTime int64 `json:"createTime" gorm:"column:create_time"` // 创建时间 UpdateBy string `json:"updateBy" gorm:"column:update_by"` // 更新者 diff --git a/src/modules/network_element/network_element.go b/src/modules/network_element/network_element.go index e877615..e44c612 100644 --- a/src/modules/network_element/network_element.go +++ b/src/modules/network_element/network_element.go @@ -36,6 +36,11 @@ func Setup(router *gin.Engine) { collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neAction", collectlogs.BUSINESS_TYPE_IMPORT)), controller.NewNeAction.PushFile, ) + neActionGroup.PUT("/service", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neAction", collectlogs.BUSINESS_TYPE_OTHER)), + controller.NewNeAction.Service, + ) } // 网元信息 @@ -53,13 +58,23 @@ func Setup(router *gin.Engine) { middleware.PreAuthorize(nil), controller.NewNeInfo.ListAll, ) - neInfoGroup.GET("/configFile", + neInfoGroup.GET("/para5GFile", middleware.PreAuthorize(nil), - controller.NewNeInfo.ConfigFileRead, + controller.NewNeInfo.Para5GFileRead, ) - neInfoGroup.PUT("/configFile", + neInfoGroup.PUT("/para5GFile", middleware.PreAuthorize(nil), - controller.NewNeInfo.ConfigFileWrite, + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neInfo", collectlogs.BUSINESS_TYPE_OTHER)), + controller.NewNeInfo.Para5GFileWrite, + ) + neInfoGroup.GET("/oamFile", + middleware.PreAuthorize(nil), + controller.NewNeInfo.OAMFileRead, + ) + neInfoGroup.PUT("/oamFile", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neInfo", collectlogs.BUSINESS_TYPE_OTHER)), + controller.NewNeInfo.OAMFileWrite, ) neInfoGroup.GET("/list", middleware.PreAuthorize(nil), @@ -173,20 +188,10 @@ func Setup(router *gin.Engine) { middleware.PreAuthorize(nil), controller.NewNeVersion.Info, ) - neVersionGroup.POST("", + neVersionGroup.POST("/operate", middleware.PreAuthorize(nil), - collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neVersion", collectlogs.BUSINESS_TYPE_INSERT)), - controller.NewNeVersion.Add, - ) - neVersionGroup.PUT("", - middleware.PreAuthorize(nil), - collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neVersion", collectlogs.BUSINESS_TYPE_UPDATE)), - controller.NewNeVersion.Edit, - ) - neVersionGroup.DELETE("/:versionIds", - middleware.PreAuthorize(nil), - collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neVersion", collectlogs.BUSINESS_TYPE_DELETE)), - controller.NewNeVersion.Remove, + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neVersion", collectlogs.BUSINESS_TYPE_OTHER)), + controller.NewNeVersion.Operate, ) } @@ -216,10 +221,10 @@ func Setup(router *gin.Engine) { collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neSoftware", collectlogs.BUSINESS_TYPE_DELETE)), controller.NewNeSoftware.Remove, ) - neSoftwareGroup.POST("/checkInstall", + neSoftwareGroup.POST("/newNeVersion", middleware.PreAuthorize(nil), collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neSoftware", collectlogs.BUSINESS_TYPE_OTHER)), - controller.NewNeSoftware.CheckInstall, + controller.NewNeSoftware.NewNeVersion, ) } @@ -234,20 +239,9 @@ func Setup(router *gin.Engine) { middleware.PreAuthorize(nil), controller.NewNeLicense.Info, ) - neLicenseGroup.POST("", + neLicenseGroup.GET("/byTypeAndID", middleware.PreAuthorize(nil), - collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neLicense", collectlogs.BUSINESS_TYPE_INSERT)), - controller.NewNeLicense.Add, - ) - neLicenseGroup.PUT("", - middleware.PreAuthorize(nil), - collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neLicense", collectlogs.BUSINESS_TYPE_UPDATE)), - controller.NewNeLicense.Edit, - ) - neLicenseGroup.DELETE("/:licenseIds", - middleware.PreAuthorize(nil), - collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neLicense", collectlogs.BUSINESS_TYPE_DELETE)), - controller.NewNeLicense.Remove, + controller.NewNeLicense.NeTypeAndID, ) neLicenseGroup.GET("/code", middleware.PreAuthorize(nil), @@ -378,4 +372,8 @@ func InitLoad() { // 启动时,清除缓存-网元类型 service.NewNeInfoImpl.ClearNeCacheByNeType("*") service.NewNeInfoImpl.SelectNeInfoByRmuid("") + // 启动时,网元公共参数数据记录到全局变量 + if para5GMap, err := service.NewNeInfoImpl.NeConfPara5GRead(); para5GMap != nil && err == nil { + service.NewNeInfoImpl.NeConfPara5GWirte(para5GMap, nil) + } } diff --git a/src/modules/network_element/repository/ne_host.impl.go b/src/modules/network_element/repository/ne_host.impl.go index 156b570..fdc9f03 100644 --- a/src/modules/network_element/repository/ne_host.impl.go +++ b/src/modules/network_element/repository/ne_host.impl.go @@ -175,21 +175,21 @@ func (r *NeHostImpl) SelectByIds(hostIds []string) []model.NeHost { for i := range *arr { passwordDe, err := crypto.StringDecryptByAES((*arr)[i].Password) if err != nil { - logger.Errorf("selectById %s StringDecryptByAES : %v", (*arr)[i].HostID, err.Error()) + logger.Errorf("selectById %s decrypt: %v", (*arr)[i].HostID, err.Error()) (*arr)[i].Password = "" } else { (*arr)[i].Password = passwordDe } privateKeyDe, err := crypto.StringDecryptByAES((*arr)[i].PrivateKey) if err != nil { - logger.Errorf("selectById %s StringDecryptByAES : %v", (*arr)[i].HostID, err.Error()) + logger.Errorf("selectById %s decrypt: %v", (*arr)[i].HostID, err.Error()) (*arr)[i].PrivateKey = "" } else { (*arr)[i].PrivateKey = privateKeyDe } passPhraseDe, err := crypto.StringDecryptByAES((*arr)[i].PassPhrase) if err != nil { - logger.Errorf("selectById %s StringDecryptByAES : %v", (*arr)[i].HostID, err.Error()) + logger.Errorf("selectById %s decrypt: %v", (*arr)[i].HostID, err.Error()) (*arr)[i].PassPhrase = "" } else { (*arr)[i].PassPhrase = passPhraseDe @@ -265,7 +265,7 @@ func (r *NeHostImpl) Insert(neHost model.NeHost) string { if neHost.Password != "" { passwordEn, err := crypto.StringEncryptByAES(neHost.Password) if err != nil { - logger.Errorf("insert StringEncryptByAES : %v", err.Error()) + logger.Errorf("insert encrypt: %v", err.Error()) return "" } params["password"] = passwordEn @@ -273,7 +273,7 @@ func (r *NeHostImpl) Insert(neHost model.NeHost) string { if neHost.PrivateKey != "" { privateKeyEn, err := crypto.StringEncryptByAES(neHost.PrivateKey) if err != nil { - logger.Errorf("insert StringEncryptByAES : %v", err.Error()) + logger.Errorf("insert encrypt: %v", err.Error()) return "" } params["private_key"] = privateKeyEn @@ -281,7 +281,7 @@ func (r *NeHostImpl) Insert(neHost model.NeHost) string { if neHost.PassPhrase != "" { passPhraseEn, err := crypto.StringEncryptByAES(neHost.PassPhrase) if err != nil { - logger.Errorf("insert StringEncryptByAES : %v", err.Error()) + logger.Errorf("insert encrypt: %v", err.Error()) return "" } params["pass_phrase"] = passPhraseEn @@ -294,6 +294,20 @@ func (r *NeHostImpl) Insert(neHost model.NeHost) string { params["create_time"] = time.Now().UnixMilli() } + // 根据认证模式清除不必要的信息 + if neHost.AuthMode == "0" { + params["private_key"] = "" + params["pass_phrase"] = "" + } + if neHost.AuthMode == "1" { + params["password"] = "" + } + if neHost.AuthMode == "2" { + params["password"] = "" + params["private_key"] = "" + params["pass_phrase"] = "" + } + // 构建执行语句 keys, placeholder, values := repo.KeyPlaceholderValueByInsert(params) sql := "insert into ne_host (" + strings.Join(keys, ",") + ")values(" + placeholder + ")" @@ -349,7 +363,7 @@ func (r *NeHostImpl) Update(neHost model.NeHost) int64 { if neHost.Password != "" { passwordEn, err := crypto.StringEncryptByAES(neHost.Password) if err != nil { - logger.Errorf("update StringEncryptByAES : %v", err.Error()) + logger.Errorf("update encrypt: %v", err.Error()) return 0 } params["password"] = passwordEn @@ -357,7 +371,7 @@ func (r *NeHostImpl) Update(neHost model.NeHost) int64 { if neHost.PrivateKey != "" { privateKeyEn, err := crypto.StringEncryptByAES(neHost.PrivateKey) if err != nil { - logger.Errorf("update StringEncryptByAES : %v", err.Error()) + logger.Errorf("update encrypt: %v", err.Error()) return 0 } params["private_key"] = privateKeyEn @@ -365,7 +379,7 @@ func (r *NeHostImpl) Update(neHost model.NeHost) int64 { if neHost.PassPhrase != "" { passPhraseEn, err := crypto.StringEncryptByAES(neHost.PassPhrase) if err != nil { - logger.Errorf("update StringEncryptByAES : %v", err.Error()) + logger.Errorf("update encrypt: %v", err.Error()) return 0 } params["pass_phrase"] = passPhraseEn @@ -376,6 +390,20 @@ func (r *NeHostImpl) Update(neHost model.NeHost) int64 { params["update_time"] = time.Now().UnixMilli() } + // 根据认证模式清除不必要的信息 + if neHost.AuthMode == "0" { + params["private_key"] = "" + params["pass_phrase"] = "" + } + if neHost.AuthMode == "1" { + params["password"] = "" + } + if neHost.AuthMode == "2" { + params["password"] = "" + params["private_key"] = "" + params["pass_phrase"] = "" + } + // 构建执行语句 keys, values := repo.KeyValueByUpdate(params) sql := "update ne_host set " + strings.Join(keys, ",") + " where host_id = ?" diff --git a/src/modules/network_element/repository/ne_info.impl.go b/src/modules/network_element/repository/ne_info.impl.go index 85fdea5..631a7e6 100644 --- a/src/modules/network_element/repository/ne_info.impl.go +++ b/src/modules/network_element/repository/ne_info.impl.go @@ -16,25 +16,26 @@ import ( // neListSort 网元列表预设排序 var neListSort = []string{ "OMC", - "MME", + "IMS", "AMF", "AUSF", "UDM", "SMF", "PCF", - "UPF", - "NRF", "NSSF", - "IMS", - "N3IWF", - "NEF", + "NRF", + "UPF", "LMF", + "NEF", + "MME", + "N3IWF", "MOCNGW", + "SMSC", } // 实例化数据层 NeInfoImpl 结构体 var NewNeInfoImpl = &NeInfoImpl{ - selectSql: `select id, ne_type, ne_id, rm_uid, ne_name, ip, port, pv_flag, province, vendor_name, dn, ne_address, status, update_time, host_ids from ne_info`, + selectSql: `select id, ne_type, ne_id, rm_uid, ne_name, ip, port, pv_flag, province, vendor_name, dn, ne_address, host_ids, status, remark, create_by, create_time, update_by, update_time from ne_info`, resultMap: map[string]string{ "id": "ID", @@ -49,9 +50,13 @@ var NewNeInfoImpl = &NeInfoImpl{ "vendor_name": "VendorName", "dn": "Dn", "ne_address": "NeAddress", - "status": "Status", - "update_time": "UpdateTime", "host_ids": "HostIDs", + "status": "Status", + "remark": "Remark", + "create_by": "CreateBy", + "create_time": "CreateTime", + "update_by": "UpdateBy", + "update_time": "UpdateTime", }, } @@ -298,11 +303,19 @@ func (r *NeInfoImpl) Insert(neInfo model.NeInfo) string { if neInfo.NeAddress != "" { params["ne_address"] = neInfo.NeAddress } - params["status"] = neInfo.Status - params["update_time"] = time.Now() if neInfo.HostIDs != "" { params["host_ids"] = neInfo.HostIDs } + if neInfo.Status != "" { + params["status"] = neInfo.Status + } + if neInfo.Remark != "" { + params["remark"] = neInfo.Remark + } + if neInfo.CreateBy != "" { + params["create_by"] = neInfo.CreateBy + params["create_time"] = time.Now().UnixMilli() + } // 构建执行语句 keys, placeholder, values := repo.KeyPlaceholderValueByInsert(params) @@ -360,11 +373,17 @@ func (r *NeInfoImpl) Update(neInfo model.NeInfo) int64 { params["vendor_name"] = neInfo.VendorName params["dn"] = neInfo.Dn params["ne_address"] = neInfo.NeAddress - params["status"] = neInfo.Status - params["update_time"] = time.Now() if neInfo.HostIDs != "" { params["host_ids"] = neInfo.HostIDs } + params["remark"] = neInfo.Remark + if neInfo.Status != "" { + params["status"] = neInfo.Status + } + if neInfo.UpdateBy != "" { + params["update_by"] = neInfo.UpdateBy + params["update_time"] = time.Now().UnixMilli() + } // 构建执行语句 keys, values := repo.KeyValueByUpdate(params) diff --git a/src/modules/network_element/repository/ne_license.go b/src/modules/network_element/repository/ne_license.go index c185d67..83cc42a 100644 --- a/src/modules/network_element/repository/ne_license.go +++ b/src/modules/network_element/repository/ne_license.go @@ -21,7 +21,4 @@ type INeLicense interface { // DeleteByIds 批量删除信息 DeleteByIds(ids []string) int64 - - // CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 - CheckUniqueTypeAndID(neLicense model.NeLicense) string } diff --git a/src/modules/network_element/repository/ne_license.impl.go b/src/modules/network_element/repository/ne_license.impl.go index fcbb1f0..0daee94 100644 --- a/src/modules/network_element/repository/ne_license.impl.go +++ b/src/modules/network_element/repository/ne_license.impl.go @@ -1,7 +1,6 @@ package repository import ( - "fmt" "strings" "time" @@ -177,41 +176,6 @@ func (r *NeLicenseImpl) SelectByIds(cmdIds []string) []model.NeLicense { return r.convertResultRows(results) } -// CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 -func (r *NeLicenseImpl) CheckUniqueTypeAndID(neLicense model.NeLicense) string { - // 查询条件拼接 - var conditions []string - var params []any - if neLicense.NeType != "" { - conditions = append(conditions, "ne_type = ?") - params = append(params, neLicense.NeType) - } - if neLicense.NeId != "" { - conditions = append(conditions, "ne_id = ?") - params = append(params, neLicense.NeId) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } else { - return "" - } - - // 查询数据 - querySql := "select id as 'str' from ne_license " + whereSql + " limit 1" - results, err := datasource.RawDB("", querySql, params) - if err != nil { - logger.Errorf("query err %v", err) - return "" - } - if len(results) > 0 { - return fmt.Sprint(results[0]["str"]) - } - return "" -} - // Insert 新增信息 func (r *NeLicenseImpl) Insert(neLicense model.NeLicense) string { // 参数拼接 diff --git a/src/modules/network_element/repository/ne_software.impl.go b/src/modules/network_element/repository/ne_software.impl.go index 133dd42..5aa8cda 100644 --- a/src/modules/network_element/repository/ne_software.impl.go +++ b/src/modules/network_element/repository/ne_software.impl.go @@ -61,8 +61,17 @@ func (r *NeSoftwareImpl) SelectPage(query map[string]any) map[string]any { var conditions []string var params []any if v, ok := query["neType"]; ok && v != "" { - conditions = append(conditions, "ne_type = ?") - params = append(params, strings.Trim(v.(string), " ")) + softwareType := v.(string) + if strings.Contains(softwareType, ",") { + softwareTypeArr := strings.Split(softwareType, ",") + placeholder := repo.KeyPlaceholderByQuery(len(softwareTypeArr)) + conditions = append(conditions, "ne_type in ("+placeholder+")") + parameters := repo.ConvertIdsSlice(softwareTypeArr) + params = append(params, parameters...) + } else { + conditions = append(conditions, "ne_type = ?") + params = append(params, strings.Trim(softwareType, " ")) + } } if v, ok := query["name"]; ok && v != "" { conditions = append(conditions, "name like concat(?, '%')") diff --git a/src/modules/network_element/repository/ne_version.go b/src/modules/network_element/repository/ne_version.go index c680553..6da08cc 100644 --- a/src/modules/network_element/repository/ne_version.go +++ b/src/modules/network_element/repository/ne_version.go @@ -21,7 +21,4 @@ type INeVersion interface { // DeleteByIds 批量删除信息 DeleteByIds(ids []string) int64 - - // CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 - CheckUniqueTypeAndID(neVersion model.NeVersion) string } diff --git a/src/modules/network_element/repository/ne_version.impl.go b/src/modules/network_element/repository/ne_version.impl.go index 7eebfec..ad2c144 100644 --- a/src/modules/network_element/repository/ne_version.impl.go +++ b/src/modules/network_element/repository/ne_version.impl.go @@ -1,7 +1,6 @@ package repository import ( - "fmt" "strings" "time" @@ -15,17 +14,20 @@ import ( // 实例化数据层 NewNeVersion 结构体 var NewNeVersionImpl = &NeVersionImpl{ selectSql: `select - id, ne_type, ne_id, version, path, pre_version, pre_path, new_version, new_path, status, create_by, create_time, update_by, update_time + id, ne_type, ne_id, name, version, path, pre_name, pre_version, pre_path, new_name, new_version, new_path, status, create_by, create_time, update_by, update_time from ne_version`, resultMap: map[string]string{ "id": "ID", "ne_type": "NeType", "ne_id": "NeId", + "name": "name", "version": "Version", "path": "Path", + "pre_name": "preName", "pre_version": "PreVersion", "pre_path": "PrePath", + "new_name": "NewName", "new_version": "NewVersion", "new_path": "NewPath", "status": "Status", @@ -76,8 +78,8 @@ func (r *NeVersionImpl) SelectPage(query map[string]any) map[string]any { conditions = append(conditions, "version like concat(?, '%')") params = append(params, strings.Trim(v.(string), " ")) } - if v, ok := query["filePath"]; ok && v != "" { - conditions = append(conditions, "file_path like concat(?, '%')") + if v, ok := query["path"]; ok && v != "" { + conditions = append(conditions, "path like concat(?, '%')") params = append(params, strings.Trim(v.(string), " ")) } @@ -108,7 +110,7 @@ func (r *NeVersionImpl) SelectPage(query map[string]any) map[string]any { // 分页 pageNum, pageSize := repo.PageNumSize(query["pageNum"], query["pageSize"]) - pageSql := " limit ?,? " + pageSql := " order by update_time desc limit ?,? " params = append(params, pageNum*pageSize) params = append(params, pageSize) @@ -146,6 +148,10 @@ func (r *NeVersionImpl) SelectList(neVersion model.NeVersion) []model.NeVersion conditions = append(conditions, "path like concat(?, '%')") params = append(params, neVersion.Path) } + if neVersion.Status != "" { + conditions = append(conditions, "status = ?") + params = append(params, neVersion.Status) + } // 构建查询条件语句 whereSql := "" @@ -178,41 +184,6 @@ func (r *NeVersionImpl) SelectByIds(cmdIds []string) []model.NeVersion { return r.convertResultRows(results) } -// CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 -func (r *NeVersionImpl) CheckUniqueTypeAndID(neVersion model.NeVersion) string { - // 查询条件拼接 - var conditions []string - var params []any - if neVersion.NeType != "" { - conditions = append(conditions, "ne_type = ?") - params = append(params, neVersion.NeType) - } - if neVersion.NeId != "" { - conditions = append(conditions, "ne_id = ?") - params = append(params, neVersion.NeId) - } - - // 构建查询条件语句 - whereSql := "" - if len(conditions) > 0 { - whereSql += " where " + strings.Join(conditions, " and ") - } else { - return "" - } - - // 查询数据 - querySql := "select id as 'str' from ne_version " + whereSql + " limit 1" - results, err := datasource.RawDB("", querySql, params) - if err != nil { - logger.Errorf("query err %v", err) - return "" - } - if len(results) > 0 { - return fmt.Sprint(results[0]["str"]) - } - return "" -} - // Insert 新增信息 func (r *NeVersionImpl) Insert(neVersion model.NeVersion) string { // 参数拼接 @@ -223,18 +194,27 @@ func (r *NeVersionImpl) Insert(neVersion model.NeVersion) string { if neVersion.NeId != "" { params["ne_id"] = neVersion.NeId } + if neVersion.Name != "" { + params["name"] = neVersion.Name + } if neVersion.Version != "" { params["version"] = neVersion.Version } if neVersion.Path != "" { params["path"] = neVersion.Path } + if neVersion.PreName != "" { + params["pre_name"] = neVersion.PreName + } if neVersion.PreVersion != "" { params["pre_version"] = neVersion.PreVersion } if neVersion.PrePath != "" { params["pre_path"] = neVersion.PrePath } + if neVersion.NewName != "" { + params["new_name"] = neVersion.NewName + } if neVersion.NewVersion != "" { params["new_version"] = neVersion.NewVersion } @@ -286,18 +266,27 @@ func (r *NeVersionImpl) Update(neVersion model.NeVersion) int64 { if neVersion.NeId != "" { params["ne_id"] = neVersion.NeId } + if neVersion.Name != "" { + params["name"] = neVersion.Name + } if neVersion.Version != "" { params["version"] = neVersion.Version } if neVersion.Path != "" { params["path"] = neVersion.Path } + if neVersion.PreName != "" { + params["pre_name"] = neVersion.PreName + } if neVersion.PreVersion != "" { params["pre_version"] = neVersion.PreVersion } if neVersion.PrePath != "" { params["pre_path"] = neVersion.PrePath } + if neVersion.NewName != "" { + params["new_name"] = neVersion.NewName + } if neVersion.NewVersion != "" { params["new_version"] = neVersion.NewVersion } diff --git a/src/modules/network_element/service/ne_direct_link.go b/src/modules/network_element/service/ne_direct_link.go index 7a4fa12..7d5afaf 100644 --- a/src/modules/network_element/service/ne_direct_link.go +++ b/src/modules/network_element/service/ne_direct_link.go @@ -15,7 +15,7 @@ import ( func NeState(neInfo model.NeInfo) (map[string]any, error) { // 网元状态 neUrl := fmt.Sprintf("http://%s:%d/api/rest/systemManagement/v1/elementType/%s/objectType/systemState", neInfo.IP, neInfo.Port, strings.ToLower(neInfo.NeType)) - resBytes, err := fetch.Get(neUrl, nil, 250) + resBytes, err := fetch.Get(neUrl, nil, 1000) if err != nil { logger.Warnf("NeState %s", err.Error()) return nil, err @@ -62,8 +62,8 @@ func NeConfigOMC(neInfo model.NeInfo) (map[string]any, error) { var resData map[string]any if err != nil { status := err.Error() - logger.Warnf("NeConfigOMC %s", status) - if strings.HasPrefix(status, "204") { + logger.Warnf("NeConfigOMC %s Put \"%s\"", status, neUrl) + if strings.HasPrefix(status, "201") || strings.HasPrefix(status, "204") { return resData, nil } return nil, err diff --git a/src/modules/network_element/service/ne_info.go b/src/modules/network_element/service/ne_info.go index a667c01..5945fb3 100644 --- a/src/modules/network_element/service/ne_info.go +++ b/src/modules/network_element/service/ne_info.go @@ -1,6 +1,9 @@ package service -import "be.ems/src/modules/network_element/model" +import ( + "be.ems/src/framework/utils/ssh" + "be.ems/src/modules/network_element/model" +) // 网元信息 服务层接口 type INeInfo interface { @@ -24,7 +27,8 @@ type INeInfo interface { // SelectList 查询列表 // // bandStatus 带状态信息 - SelectList(ne model.NeInfo, bandStatus bool) []model.NeInfo + // bandHost 带主机信息 + SelectList(ne model.NeInfo, bandStatus bool, bandHost bool) []model.NeInfo // SelectByIds 通过ID查询 // @@ -41,14 +45,23 @@ type INeInfo interface { DeleteByIds(infoIds []string) (int64, error) // CheckUniqueNeTypeAndNeId 校验同类型下标识是否唯一 - CheckUniqueNeTypeAndNeId(neType, neId, infoId string) bool + CheckUniqueNeTypeAndNeId(neType, neId, id string) bool + + // NeRunSSHclient 网元主机的SSH客户端-为创建相关连接 + NeRunSSHclient(neType, neId string) (*ssh.ConnSSH, error) // NeRunCMD 向网元发送cmd命令 NeRunCMD(neType, neId, cmd string) (string, error) - // NeConfigFileRead 网元配置文件读取 网元配置yaml文件复制到本地后通过filePath读取 - NeConfigFileRead(neInfo model.NeInfo, filePath string) []string + // neConfOAMRead 网元OAM配置文件读取 + NeConfOAMRead(neType, neId string) (map[string]any, error) - // NeConfigFileWirte 网元配置文件写入 content内容 sync同步到网元端 - NeConfigFileWirte(neInfo model.NeInfo, filePath, content string, sync bool) error + // NeConfOAMSync 网元OAM配置文件生成并同步 + NeConfOAMSync(neInfo model.NeInfo, content map[string]any, sync bool) error + + // NeConfPara5GRead 网元公共配置文件读取 + NeConfPara5GRead() (map[string]any, error) + + // NeConfPara5GWirte 网元公共配置文件写入 content内容 syncNE同步到网元端NeType@NeId + NeConfPara5GWirte(content map[string]any, syncNE []string) error } diff --git a/src/modules/network_element/service/ne_info.impl.go b/src/modules/network_element/service/ne_info.impl.go index 666617e..de5b6a8 100644 --- a/src/modules/network_element/service/ne_info.impl.go +++ b/src/modules/network_element/service/ne_info.impl.go @@ -4,12 +4,14 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "runtime" "strings" "be.ems/src/framework/constants/cachekey" "be.ems/src/framework/logger" "be.ems/src/framework/redis" + "be.ems/src/framework/utils/parse" "be.ems/src/framework/utils/ssh" "be.ems/src/modules/network_element/model" "be.ems/src/modules/network_element/repository" @@ -18,15 +20,14 @@ import ( // 实例化服务层 NeInfoImpl 结构体 var NewNeInfoImpl = &NeInfoImpl{ neInfoRepository: repository.NewNeInfoImpl, - neHostRepository: repository.NewNeHostImpl, + Para5GData: map[string]string{}, } // 网元信息 服务层处理 type NeInfoImpl struct { // 网元信息数据信息 neInfoRepository repository.INeInfo - // 网元主机连接表 - neHostRepository repository.INeHost + Para5GData map[string]string } // SelectNeInfoByNeTypeAndNeID 通过ne_type和ne_id查询网元信息 @@ -94,7 +95,7 @@ func (r *NeInfoImpl) SelectNeInfoByRmuid(rmUid string) model.NeInfo { } } } else { - neInfos := r.SelectList(neInfo, false) + neInfos := r.SelectList(neInfo, false, false) for _, v := range neInfos { key := fmt.Sprintf("%s%s:%s", cachekey.NE_KEY, strings.ToUpper(v.NeType), v.NeId) redis.Del("", key) @@ -117,37 +118,7 @@ func (r *NeInfoImpl) SelectPage(query map[string]any, bandStatus bool) map[strin // 网元直连读取网元服务状态 if bandStatus { rows := data["rows"].([]model.NeInfo) - arr := &rows - for i := range *arr { - v := (*arr)[i] - result, err := NeState(v) - if err != nil { - (*arr)[i].ServerState = map[string]any{ - "online": false, - } - // 网元状态设置为离线 - if v.Status != "1" { - v.Status = "1" - (*arr)[i].Status = v.Status - r.neInfoRepository.Update(v) - } - continue - } - result["online"] = true - (*arr)[i].ServerState = result - // 网元状态设置为在线 - if v.Status != "0" { - // 下发网管配置信息给网元 - _, err = NeConfigOMC(v) - if err != nil { - v.Status = "3" - } else { - v.Status = "0" - } - (*arr)[i].Status = v.Status - r.neInfoRepository.Update(v) - } - } + r.bandNeStatus(&rows) } return data @@ -156,50 +127,70 @@ func (r *NeInfoImpl) SelectPage(query map[string]any, bandStatus bool) map[strin // SelectList 查询列表 // // bandStatus 带状态信息 -func (r *NeInfoImpl) SelectList(ne model.NeInfo, bandStatus bool) []model.NeInfo { +// bandHost 带主机信息 +func (r *NeInfoImpl) SelectList(ne model.NeInfo, bandStatus bool, bandHost bool) []model.NeInfo { list := r.neInfoRepository.SelectList(ne) // 网元直连读取网元服务状态 if bandStatus { - neList := &list - for i := range *neList { - v := (*neList)[i] - result, err := NeState(v) - if err != nil { - (*neList)[i].ServerState = map[string]any{ - "online": false, - } - // 网元状态设置为离线 - if v.Status != "1" { - v.Status = "1" - (*neList)[i].Status = v.Status - r.neInfoRepository.Update(v) - } - continue - } - result["online"] = true - (*neList)[i].ServerState = result - // 网元状态设置为在线 - if v.Status != "0" { - // 下发网管配置信息给网元 - _, err = NeConfigOMC(v) - if err != nil { - v.Status = "3" - } else { - v.Status = "0" - } - (*neList)[i].Status = v.Status - r.neInfoRepository.Update(v) - } - } + r.bandNeStatus(&list) + } + + // 网元主机信息 + if bandHost { + r.bandNeHosts(&list) } return list } +// bandNeStatus 网元列表项数据带网元服务状态 +func (r *NeInfoImpl) bandNeStatus(arr *[]model.NeInfo) { + for i := range *arr { + v := (*arr)[i] + result, err := NeState(v) + if err != nil { + (*arr)[i].ServerState = map[string]any{ + "online": false, + } + // 网元状态设置为离线 + if v.Status != "0" { + v.Status = "0" + (*arr)[i].Status = v.Status + r.neInfoRepository.Update(v) + } + continue + } + result["online"] = true + (*arr)[i].ServerState = result + // 网元状态设置为在线 + if v.Status != "1" { + // 下发网管配置信息给网元 + _, err = NeConfigOMC(v) + if err == nil { + v.Status = "1" + } else { + v.Status = "2" + } + (*arr)[i].Status = v.Status + r.neInfoRepository.Update(v) + } + } +} + +// bandNeHosts 网元列表项数据带网元主机信息 +func (r *NeInfoImpl) bandNeHosts(arr *[]model.NeInfo) { + for i := range *arr { + v := (*arr)[i] + if v.HostIDs != "" { + (*arr)[i].Hosts = NewNeHostImpl.neHostRepository.SelectByIds(strings.Split(v.HostIDs, ",")) + } + } +} + // SelectByIds 通过ID查询 // -// bandStatus 带主机信息 +// bandHost 带主机信息 func (r *NeInfoImpl) SelectById(infoId string, bandHost bool) model.NeInfo { if infoId == "" { return model.NeInfo{} @@ -209,7 +200,7 @@ func (r *NeInfoImpl) SelectById(infoId string, bandHost bool) model.NeInfo { neInfo := neInfos[0] // 带主机信息 if neInfo.HostIDs != "" && bandHost { - neInfo.Hosts = r.neHostRepository.SelectByIds(strings.Split(neInfo.HostIDs, ",")) + neInfo.Hosts = NewNeHostImpl.neHostRepository.SelectByIds(strings.Split(neInfo.HostIDs, ",")) } return neInfo } @@ -224,7 +215,7 @@ func (r *NeInfoImpl) Insert(neInfo model.NeInfo) string { for _, host := range neInfo.Hosts { host.Title = fmt.Sprintf("%s_%s_%d", strings.ToUpper(neInfo.NeType), neInfo.NeId, host.Port) host.GroupID = "1" - hostId := r.neHostRepository.Insert(host) + hostId := NewNeHostImpl.Insert(host) if hostId != "" { hostIDs = append(hostIDs, hostId) } @@ -248,7 +239,7 @@ func (r *NeInfoImpl) Update(neInfo model.NeInfo) int64 { if host.HostID != "" { host.Title = fmt.Sprintf("%s_%s_%d", strings.ToUpper(neInfo.NeType), neInfo.NeId, host.Port) host.GroupID = "1" - r.neHostRepository.Update(host) + NewNeHostImpl.Update(host) } } } @@ -273,12 +264,20 @@ func (r *NeInfoImpl) DeleteByIds(infoIds []string) (int64, error) { for _, v := range infos { // 主机信息删除 if v.HostIDs != "" { - hostIds := strings.Split(v.HostIDs, ",") - r.neHostRepository.DeleteByIds(hostIds) + NewNeHostImpl.DeleteByIds(strings.Split(v.HostIDs, ",")) + } + // 删除License + neLicense := NewNeLicenseImpl.SelectByNeTypeAndNeID(v.NeType, v.NeId) + if neLicense.NeId == v.NeId { + NewNeLicenseImpl.DeleteByIds([]string{neLicense.ID}) + } + // 删除Version + neVersion := NewNeVersionImpl.SelectByNeTypeAndNeID(v.NeType, v.NeId) + if neVersion.NeId == v.NeId { + NewNeVersionImpl.DeleteByIds([]string{neVersion.ID}) } // 缓存信息删除 - key := fmt.Sprintf("%s%s:%s", cachekey.NE_KEY, v.NeType, v.NeId) - redis.Del("", key) + redis.Del("", fmt.Sprintf("%s%s:%s", cachekey.NE_KEY, v.NeType, v.NeId)) } rows := r.neInfoRepository.DeleteByIds(infoIds) return rows, nil @@ -288,149 +287,456 @@ func (r *NeInfoImpl) DeleteByIds(infoIds []string) (int64, error) { } // CheckUniqueNeTypeAndNeId 校验同类型下标识是否唯一 -func (r *NeInfoImpl) CheckUniqueNeTypeAndNeId(neType, neId, infoId string) bool { +func (r *NeInfoImpl) CheckUniqueNeTypeAndNeId(neType, neId, id string) bool { uniqueId := r.neInfoRepository.CheckUniqueNeTypeAndNeId(model.NeInfo{ NeType: neType, NeId: neId, }) - if uniqueId == infoId { + if uniqueId == id { return true } return uniqueId == "" } -// NeRunCMD 向网元发送cmd命令 -func (r *NeInfoImpl) NeRunCMD(neType, neId, cmd string) (string, error) { +// NeRunSSHclient 网元主机的SSH客户端-为创建相关连接 +func (r *NeInfoImpl) NeRunSSHclient(neType, neId string) (*ssh.ConnSSH, error) { neInfo := r.SelectNeInfoByNeTypeAndNeID(neType, neId) if neInfo.NeId != neId { - logger.Errorf("NeRunCMD NeType:%s NeID:%s not found", neType, neId) - return "", fmt.Errorf("neinfo not found") + logger.Errorf("NeRunSSHclient NeType:%s NeID:%s not found", neType, neId) + return nil, fmt.Errorf("neinfo not found") } - // 带主机信息 - if neInfo.HostIDs != "" { - neInfo.Hosts = r.neHostRepository.SelectByIds(strings.Split(neInfo.HostIDs, ",")) - if len(neInfo.Hosts) <= 0 { - logger.Errorf("NeRunCMD Hosts %s not found", neInfo.HostIDs) - return "", fmt.Errorf("neinfo host not found") - } + // 取主机信息 + if neInfo.HostIDs == "" { + logger.Errorf("NeRunSSHclient NeType:%s NeID:%s hostId not found", neType, neId) + return nil, fmt.Errorf("neinfo hostId not found") + } + neInfo.Hosts = NewNeHostImpl.neHostRepository.SelectByIds(strings.Split(neInfo.HostIDs, ",")) + if len(neInfo.Hosts) <= 0 { + logger.Errorf("NeRunSSHclient Hosts %s not found", neInfo.HostIDs) + return nil, fmt.Errorf("neinfo host not found") } - neHost := neInfo.Hosts[0] if neHost.HostType != "ssh" { - logger.Errorf("NeRunCMD Hosts first HostType %s not ssh", neHost.HostType) - return "", fmt.Errorf("neinfo host type not ssh") + logger.Errorf("NeRunSSHclient Hosts first HostType %s not ssh", neHost.HostType) + return nil, fmt.Errorf("neinfo host type not ssh") } + var connSSH ssh.ConnSSH neHost.CopyTo(&connSSH) - client, err := connSSH.NewClient() - if err != nil { - logger.Errorf("NeRunCMD NewClient err => %s", err.Error()) - return "", fmt.Errorf("neinfo ssh client new err") + var client *ssh.ConnSSH + var err error + if neHost.AuthMode == "2" { + client, err = connSSH.NewClientByLocalPrivate() + } else { + client, err = connSSH.NewClient() } - defer client.Close() + if err != nil { + logger.Errorf("NeRunSSHclient NewClient err => %s", err.Error()) + return nil, fmt.Errorf("neinfo ssh client new err") + } + return client, nil +} + +// NeRunCMD 向网元发送cmd命令 +func (r *NeInfoImpl) NeRunCMD(neType, neId, cmd string) (string, error) { + sshClient, err := r.NeRunSSHclient(neType, neId) + if err != nil { + return "", err + } + defer sshClient.Close() // 执行命令 - output, err := client.RunCMD(cmd) + output, err := sshClient.RunCMD(cmd) if err != nil { logger.Errorf("NeRunCMD RunCMD %s err => %s", output, err.Error()) return "", fmt.Errorf("neinfo ssh run cmd err") } - return output, nil } -// NeConfigFileRead 网元配置文件读取 网元配置yaml文件复制到本地后通过filePath读取 -func (r *NeInfoImpl) NeConfigFileRead(neInfo model.NeInfo, filePath string) []string { - files := []string{} - neTypeLower := strings.ToLower(neInfo.NeType) - - // 网管本地路径 - omcPath := "/usr/local/etc/omc/ne_config" - if runtime.GOOS == "windows" { - omcPath = fmt.Sprintf("C:%s", omcPath) +// neConfOAMData 网元OAM配置文件默认格式数据 +func (r *NeInfoImpl) neConfOAMData() map[string]any { + return map[string]any{ + "httpManageCfg": map[string]any{ + "ipType": "ipv4", + // 必改 + "ipv4": "172.60.5.2", + "ipv6": "", + "port": 33030, + "scheme": "http", + }, + "oamConfig": map[string]any{ + "enable": true, + "ipType": "ipv4", + "ipv4": "172.60.5.1", // 必改 + "ipv6": "", + "port": 33030, + "scheme": "http", + "neConfig": map[string]any{ // 必改 + "neId": "001", + "rmUid": "4400HX1XXX001", + "neName": "XXX_001", + "dn": "-", + "vendorName": "GD", + "province": "-", + "pvFlag": "PNF", + }, + }, + "snmpConfig": map[string]any{ + "enable": false, + "ipType": "ipv4", + "ipv4": "172.60.5.2", // 必改 + "ipv6": "", + "port": 4957, + }, + "kpiConfig": map[string]any{ + "enable": true, + "timer": 60, // 必改 + }, + // "pubConfigPath": "/usr/local/etc/conf/para5G.yaml", // 网元只会读一次后续会置空,建议不放 } - omcPath = fmt.Sprintf("%s/%s/%s", omcPath, neTypeLower, neInfo.NeId) - - // 读取文件内容 - if filePath != "" { - bytes, err := os.ReadFile(fmt.Sprintf("%s/%s", omcPath, filePath)) - if err != nil { - logger.Warnf("NeConfigFile ReadFile => %s", err.Error()) - return files - } - files = append(files, string(bytes)) - return files - } - - // 删除原有配置文件 - // err := os.RemoveAll(omcPath) - // if err != nil { - // logger.Warnf("NeConfigFile Remove => %s", err.Error()) - // return files - // } - - // 网元端配置路径 - nePath := "/usr/local/etc" - nePath = fmt.Sprintf("%s/%s", nePath, neTypeLower) - - // 各个网元与网元间约定配置文件 - err := ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/oam_manager.yaml", omcPath+"/oam_manager.yaml") - if err == nil { - files = append(files, "oam_manager.yaml") - } - - // 根据情况复制网元特殊配置 - switch neTypeLower { - case "ausf": - err = ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/ausfcfg.yaml", omcPath+"/ausfcfg.yaml") - if err == nil { - files = append(files, "ausfcfg.yaml") - } - case "smf": - ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/smf_conf.yaml", omcPath+"/smf_conf.yaml") - if err == nil { - files = append(files, "smf_conf.yaml") - } - ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/smf_policy.yaml", omcPath+"/smf_policy.yaml") - if err == nil { - files = append(files, "smf_policy.yaml") - } - case "ims": - } - - return files } -// NeConfigFileWirte 网元配置文件写入 content内容 sync同步到网元端 -func (r *NeInfoImpl) NeConfigFileWirte(neInfo model.NeInfo, filePath, content string, sync bool) error { - neTypeLower := strings.ToLower(neInfo.NeType) - +// neConfOAMRead 网元OAM配置文件读取 +func (r *NeInfoImpl) NeConfOAMRead(neType, neId string) (map[string]any, error) { + neTypeLower := strings.ToLower(neType) // 网管本地路径 omcPath := "/usr/local/etc/omc/ne_config" if runtime.GOOS == "windows" { omcPath = fmt.Sprintf("C:%s", omcPath) } - localFilePath := fmt.Sprintf("%s/%s/%s/%s", omcPath, neTypeLower, neInfo.NeId, filePath) + localFilePath := fmt.Sprintf("%s/%s/%s/%s", omcPath, neTypeLower, neId, "oam_manager.yaml") - err := os.WriteFile(localFilePath, []byte(content), 0644) + // 读取文件内容 + bytes, err := os.ReadFile(localFilePath) if err != nil { - logger.Warnf("NeConfigFile WriteFile => %s", err.Error()) + // logger.Warnf("NeConfOAMRead ReadFile => %s", err.Error()) + // return nil, fmt.Errorf("read file error") + // 无保留文件时返回默认文件数据 + oamData := r.neConfOAMData() + r.neConfOAMWirte(neType, neId, oamData, false) + return oamData, nil + } + content := string(bytes) + + // 序列化Map + mapData, err := parse.ConvertConfigToMap("yaml", content) + if err != nil { + logger.Warnf("NeConfOAMRead ConvertConfigToMap => %s", err.Error()) + return nil, fmt.Errorf("content convert type error") + } + return mapData, nil +} + +// neConfOAMWirte 网元OAM配置文件写入 content内容 sync同步到网元端 +func (r *NeInfoImpl) neConfOAMWirte(neType, neId string, content any, sync bool) error { + neTypeLower := strings.ToLower(neType) + fileName := "oam_manager.yaml" + // 网管本地路径 + omcPath := "/usr/local/etc/omc/ne_config" + if runtime.GOOS == "windows" { + omcPath = fmt.Sprintf("C:%s", omcPath) + } + localFilePath := fmt.Sprintf("%s/%s/%s/%s", omcPath, neTypeLower, neId, fileName) + + // 写入文件 + if err := parse.ConvertConfigToFile("yaml", localFilePath, content); err != nil { return fmt.Errorf("please check if the file exists or write permissions") } // 同步到网元端 if sync { - // 网元端配置路径 - neFilePath := fmt.Sprintf("/usr/local/etc/%s/%s", neTypeLower, filePath) - // 修改网元文件权限 - r.NeRunCMD(neInfo.NeType, neInfo.NeId, fmt.Sprintf("sudo chmod o+w %s", neFilePath)) - // 复制到网元进行覆盖 - err = ssh.FileSCPLocalToNe(neInfo.IP, localFilePath, neFilePath) + // 网元主机的SSH客户端 + sshClient, err := r.NeRunSSHclient(neType, neId) if err != nil { - logger.Warnf("NeConfigFile SyncFile => %s", err.Error()) + return err + } + defer sshClient.Close() + // 网元主机的SSH客户端进行文件传输 + sftpClient, err := sshClient.NewClientSFTP() + if err != nil { + return err + } + defer sftpClient.Close() + + // 网元端配置路径 + neFilePath := fmt.Sprintf("/usr/local/etc/%s/%s", neTypeLower, fileName) + neFileDir := filepath.ToSlash(filepath.Dir(neFilePath)) + // 修改网元文件权限 + sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 775 %s && sudo touch %s && sudo chmod o+w %s", neFileDir, neFileDir, neFilePath, neFilePath)) + // 复制到网元进行覆盖 + if err = sftpClient.CopyFileLocalToRemote(localFilePath, neFilePath); err != nil { return fmt.Errorf("please check if scp remote copy is allowed") } } return nil } + +// NeConfOAMSync 网元OAM配置文件生成并同步 +func (r *NeInfoImpl) NeConfOAMSync(neInfo model.NeInfo, content map[string]any, sync bool) error { + oamData, err := r.NeConfOAMRead(neInfo.NeType, neInfo.NeId) + if oamData == nil || err != nil { + return fmt.Errorf("error read OAM file info") + } + // 网元HTTP服务 + if v, ok := oamData["httpManageCfg"]; ok { + item := v.(map[string]any) + item["port"] = neInfo.Port + if strings.Contains(neInfo.IP, ":") { + item["ipType"] = "ipv6" + item["ipv6"] = neInfo.IP + } + if strings.Contains(neInfo.IP, ".") { + item["ipType"] = "ipv4" + item["ipv4"] = neInfo.IP + } + + oamData["httpManageCfg"] = item + } + // 对网管HTTP配置 + if v, ok := oamData["oamConfig"]; ok { + item := v.(map[string]any) + item["neConfig"] = map[string]string{ + "neId": neInfo.NeId, + "rmUid": neInfo.RmUID, + "neName": neInfo.NeName, + "dn": neInfo.Dn, + "vendorName": neInfo.VendorName, + "province": neInfo.Province, + "pvFlag": neInfo.PvFlag, + } + + if omcIP, ok := r.Para5GData["OMC_IP"]; ok && omcIP != "" { + if strings.Contains(omcIP, ":") { + item["ipType"] = "ipv6" + item["ipv6"] = omcIP + } + if strings.Contains(omcIP, ".") { + item["ipType"] = "ipv4" + item["ipv4"] = omcIP + } + } + + if oamEnable, ok := content["oamEnable"]; ok && oamEnable != nil { + item["enable"] = parse.Boolean(oamEnable) + } + if oamPort, ok := content["oamPort"]; ok && oamPort != nil { + item["port"] = parse.Number(oamPort) + } + oamData["oamConfig"] = item + } + // 对网管SNMP配置 + if v, ok := oamData["snmpConfig"]; ok { + item := v.(map[string]any) + if strings.Contains(neInfo.IP, ":") { + item["ipType"] = "ipv6" + item["ipv6"] = neInfo.IP + } + if strings.Contains(neInfo.IP, ".") { + item["ipType"] = "ipv4" + item["ipv4"] = neInfo.IP + } + + if snmpEnable, ok := content["snmpEnable"]; ok && snmpEnable != nil { + item["enable"] = parse.Boolean(snmpEnable) + } + if snmpPort, ok := content["snmpPort"]; ok && snmpPort != nil { + item["port"] = parse.Number(snmpPort) + } + oamData["snmpConfig"] = item + } + // 对网管KPI上报配置 + if v, ok := oamData["kpiConfig"]; ok { + item := v.(map[string]any) + if neInfo.NeType == "UPF" { + item["timer"] = 5 + } + + if kpiEnable, ok := content["kpiEnable"]; ok && kpiEnable != nil { + item["enable"] = parse.Boolean(kpiEnable) + } + if kpiTimer, ok := content["kpiTimer"]; ok && kpiTimer != nil { + item["timer"] = parse.Number(kpiTimer) + } + oamData["kpiConfig"] = item + } + if err := NewNeInfoImpl.neConfOAMWirte(neInfo.NeType, neInfo.NeId, oamData, sync); err != nil { + return fmt.Errorf("error wirte OAM file info") + } + return nil +} + +// NeConfPara5GRead 网元公共配置文件读取 +func (r *NeInfoImpl) NeConfPara5GRead() (map[string]any, error) { + // 网管本地路径 + omcFilePath := "/usr/local/etc/omc/para5G.yaml" + if runtime.GOOS == "windows" { + omcFilePath = fmt.Sprintf("C:%s", omcFilePath) + } + // 读取文件内容 + bytes, err := os.ReadFile(omcFilePath) + if err != nil { + logger.Warnf("NeConfPara5GRead ReadFile => %s", err.Error()) + return nil, fmt.Errorf("read file error") + } + content := string(bytes) + + // 序列化Map + mapData, err := parse.ConvertConfigToMap("yaml", content) + if err != nil { + logger.Warnf("NeConfPara5GRead ConvertConfigToMap => %s", err.Error()) + return nil, fmt.Errorf("content convert type error") + } + return mapData, nil +} + +// NeConfPara5GWirte 网元公共配置文件写入 content内容 syncNE同步到网元端NeType@NeId +func (r *NeInfoImpl) NeConfPara5GWirte(content map[string]any, syncNE []string) error { + // 网管本地路径 + omcFilePath := "/usr/local/etc/omc/para5G.yaml" + if runtime.GOOS == "windows" { + omcFilePath = fmt.Sprintf("C:%s", omcFilePath) + } + + if err := parse.ConvertConfigToFile("yaml", omcFilePath, content); err != nil { + return fmt.Errorf("please check if the file exists or write permissions") + } + + // 同步到网元端 + if len(syncNE) > 0 { + errMsg := []string{} + for _, neTI := range syncNE { + ti := strings.SplitN(neTI, "@", 2) + // 网元主机的SSH客户端 + sshClient, err := r.NeRunSSHclient(ti[0], ti[1]) + if err != nil { + errMsg = append(errMsg, fmt.Sprintf("%s : %s", ti, err.Error())) + continue + } + defer sshClient.Close() + // 网元主机的SSH客户端进行文件传输 + sftpClient, err := sshClient.NewClientSFTP() + if err != nil { + errMsg = append(errMsg, fmt.Sprintf("%s : %s", ti, err.Error())) + continue + } + defer sftpClient.Close() + + // 网元端配置路径 + neFilePath := "/usr/local/etc/conf/para5G.yaml" + neFileDir := filepath.ToSlash(filepath.Dir(neFilePath)) + // 修改网元文件权限 + sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 775 %s && sudo touch %s && sudo chmod o+rw %s", neFileDir, neFileDir, neFilePath, neFilePath)) + // 复制到网元进行覆盖 + if err = sftpClient.CopyFileLocalToRemote(omcFilePath, neFilePath); err != nil { + errMsg = append(errMsg, fmt.Sprintf("%s : please check if scp remote copy is allowed", ti)) + continue + } + } + if len(errMsg) > 0 { + return fmt.Errorf(strings.Join(errMsg, "\r\n")) + } + } + + // 转换一份数据到全局 + r.Para5GData = r.neConfPara5GDataConvert(content) + return nil +} + +// NeConfPara5GConvert 网元公共配置数据转化 content网元公共配置文件读取内容 +func (r *NeInfoImpl) neConfPara5GDataConvert(content map[string]any) map[string]string { + basic := content["basic"].(map[string]any) + external := content["external"].(map[string]any) + sbi := content["sbi"].(map[string]any) + + mcc := "460" + mnc := "01" + mncDomain := "001" + if plmnId, plmnIdOk := basic["plmnId"].(map[string]any); plmnIdOk { + mcc = plmnId["mcc"].(string) + mnc = plmnId["mnc"].(string) + // If a user input two digit MNC, add a leading zero + if len(mnc) == 2 { + mncDomain = fmt.Sprintf("0%s", mnc) + } else { + mncDomain = mnc + } + } + + sst := "1" + sd := "000001" + if plmnId, plmnIdOk := basic["snssai"].(map[string]any); plmnIdOk { + sst = plmnId["sst"].(string) + sd = plmnId["sd"].(string) + } + + n3IPAmdMask := external["upfn3_ip"].(string) + n3Arr := strings.Split(n3IPAmdMask, "/") + n3IP := n3Arr[0] + n3Mask := parse.ConvertIPMask(parse.Number(n3Arr[1])) + + n6IPAmdMask := external["upfn6_ip"].(string) + n6Arr := strings.Split(n6IPAmdMask, "/") + n6IP := n6Arr[0] + n6Mask := parse.ConvertIPMask(parse.Number(n6Arr[1])) + + ueIPAmdMask := external["ue_pool"].(string) + ueArr := strings.Split(ueIPAmdMask, "/") + ueIP := ueArr[0] + ueCicr := ueArr[1] + ueMask := parse.ConvertIPMask(parse.Number(ueArr[1])) + + return map[string]string{ + // basic + "TAC": basic["tac"].(string), + "MCC": mcc, + "MNC": mnc, + "MNC_DOMAIN": mncDomain, + "SST": sst, + "SD": sd, + "DNN_DATA": basic["dnn_data"].(string), + "DNN_IMS": basic["dnn_ims"].(string), + + // external + "N2_IP": external["amfn2_ip"].(string), + "UE_POOL": external["ue_pool"].(string), + "UE_IP": ueIP, + "UE_MASK": ueMask, + "UE_CIDR": ueCicr, + "UPF_TYPE": external["upf_type"].(string), // StandardUPF LightUPF + "N3_IP": n3IP, + "N3_MASK": n3Mask, + "N3_GW": external["upfn3_gw"].(string), + "N3_PCI": external["upfn3_pci"].(string), + "N3_MAC": external["upfn3_mac"].(string), + "N6_IP": n6IP, + "N6_MASK": n6Mask, + "N6_GW": external["upfn6_gw"].(string), + "N6_PCI": external["upfn6_pci"].(string), + "N6_MAC": external["upfn6_mac"].(string), + + "SIP_IP": external["ims_sip_ip"].(string), + + "S1_MMEIP": external["mmes1_ip"].(string), + "S11_MMEIP": external["mmes11_ip"].(string), + "S10_MMEIP": external["mmes10_ip"].(string), + + // sbi + "OMC_IP": sbi["omc_ip"].(string), + "IMS_IP": sbi["ims_ip"].(string), + "AMF_IP": sbi["amf_ip"].(string), + "AUSF_IP": sbi["ausf_ip"].(string), + "UDM_IP": sbi["udm_ip"].(string), + "ADB_IP": sbi["adb_ip"].(string), + "SMF_IP": sbi["smf_ip"].(string), + "PCF_IP": sbi["pcf_ip"].(string), + "NSSF_IP": sbi["nssf_ip"].(string), + "NRF_IP": sbi["nrf_ip"].(string), + "UPF_IP": sbi["upf_ip"].(string), + "LMF_IP": sbi["lmf_ip"].(string), + "NEF_IP": sbi["nef_ip"].(string), + "MME_IP": sbi["mme_ip"].(string), + "N3IWF_IP": sbi["n3iwf_ip"].(string), + } +} diff --git a/src/modules/network_element/service/ne_license.go b/src/modules/network_element/service/ne_license.go index c5ea23a..ace554d 100644 --- a/src/modules/network_element/service/ne_license.go +++ b/src/modules/network_element/service/ne_license.go @@ -22,16 +22,13 @@ type INeLicense interface { // DeleteByIds 批量删除信息 DeleteByIds(ids []string) (int64, error) - // CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 - CheckUniqueTypeAndID(neType, neId, id string) bool - // SelectByNeTypeAndNeID 通过ne_type和ne_id查询信息 SelectByNeTypeAndNeID(neType, neId string) model.NeLicense // ReadLicenseInfo 读取授权文件信息 - // 激活申请码, 激活文件 - ReadLicenseInfo(neInfo model.NeInfo) (string, string) + // 返回激活申请码, 激活文件 + ReadLicenseInfo(neLicense model.NeLicense) (string, string) - // UploadToNeHost 授权文件上传到网元主机 - UploadToNeHost(neLicense model.NeLicense) error + // UploadLicense 授权文件上传到网元主机 + UploadLicense(neLicense model.NeLicense) error } diff --git a/src/modules/network_element/service/ne_license.impl.go b/src/modules/network_element/service/ne_license.impl.go index d41b191..d6f684e 100644 --- a/src/modules/network_element/service/ne_license.impl.go +++ b/src/modules/network_element/service/ne_license.impl.go @@ -3,12 +3,12 @@ package service import ( "fmt" "os" + "path/filepath" "runtime" "strings" "time" "be.ems/src/framework/utils/file" - "be.ems/src/framework/utils/ssh" "be.ems/src/modules/network_element/model" "be.ems/src/modules/network_element/repository" ) @@ -84,18 +84,6 @@ func (r *NeLicenseImpl) SelectByTypeAndID(neType, neId string) model.NeLicense { return model.NeLicense{} } -// CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 -func (r *NeLicenseImpl) CheckUniqueTypeAndID(neType, neId, id string) bool { - uniqueId := r.neLicenseRepository.CheckUniqueTypeAndID(model.NeLicense{ - NeType: neType, - NeId: neId, - }) - if uniqueId == id { - return true - } - return uniqueId == "" -} - // SelectByNeTypeAndNeID 通过ne_type和ne_id查询信息 func (r *NeLicenseImpl) SelectByNeTypeAndNeID(neType, neId string) model.NeLicense { neLicenses := r.neLicenseRepository.SelectList(model.NeLicense{ @@ -109,75 +97,95 @@ func (r *NeLicenseImpl) SelectByNeTypeAndNeID(neType, neId string) model.NeLicen } // ReadLicenseInfo 读取授权文件信息 -// 激活申请码, 激活文件 -func (r *NeLicenseImpl) ReadLicenseInfo(neInfo model.NeInfo) (string, string) { - neTypeLower := strings.ToLower(neInfo.NeType) +// 返回激活申请码, 激活文件 +func (r *NeLicenseImpl) ReadLicenseInfo(neLicense model.NeLicense) (string, string) { + neTypeLower := strings.ToLower(neLicense.NeType) // 网管本地路径 omcPath := "/usr/local/etc/omc/ne_license" if runtime.GOOS == "windows" { omcPath = fmt.Sprintf("C:%s", omcPath) } - omcPath = fmt.Sprintf("%s/%s/%s", omcPath, neTypeLower, neInfo.NeId) + omcPath = fmt.Sprintf("%s/%s/%s", omcPath, neTypeLower, neLicense.NeId) // 网元端授权文件路径 nePath := fmt.Sprintf("/usr/local/etc/%s/license", neTypeLower) - // 复制授权申请码到本地 - err := ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/Activation_request_code.txt", omcPath+"/Activation_request_code.txt") + // 网元主机的SSH客户端 + sshClient, err := NewNeInfoImpl.NeRunSSHclient(neLicense.NeType, neLicense.NeId) if err != nil { return "", "" } + defer sshClient.Close() + // 网元主机的SSH客户端进行文件传输 + sftpClient, err := sshClient.NewClientSFTP() + if err != nil { + return "", "" + } + defer sftpClient.Close() + + // 复制授权申请码到本地 + if err = sftpClient.CopyFileRemoteToLocal(nePath+"/Activation_request_code.txt", omcPath+"/Activation_request_code.txt"); err != nil { + return "", "" + } // 读取文件内容 bytes, err := os.ReadFile(omcPath + "/Activation_request_code.txt") if err != nil { return "", "" } - // 激活文件 + // 复制激活文件到本地 licensePath := "" - if err = ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/system.ini", omcPath+"/system.ini"); err == nil { + if err = sftpClient.CopyFileRemoteToLocal(nePath+"/system.ini", omcPath+"/system.ini"); err == nil { licensePath = omcPath + "/system.ini" } return strings.TrimSpace(string(bytes)), licensePath } -// UploadToNeHost 授权文件上传到网元主机 -func (r *NeLicenseImpl) UploadToNeHost(neLicense model.NeLicense) error { - +// UploadLicense 授权文件上传到网元主机 +func (r *NeLicenseImpl) UploadLicense(neLicense model.NeLicense) error { // 检查文件是否存在 omcLicensePath := file.ParseUploadFilePath(neLicense.LicensePath) if _, err := os.Stat(omcLicensePath); err != nil { return fmt.Errorf("file read failure") } - // 检查网元主机 - neHostInfo := NewNeHostImpl.SelectById(neLicense.HostId) - if neHostInfo.HostType != "ssh" || neHostInfo.HostID != neLicense.HostId { - return fmt.Errorf("no found host info") + // 网元主机的SSH客户端 + sshClient, err := NewNeInfoImpl.NeRunSSHclient(neLicense.NeType, neLicense.NeId) + if err != nil { + return err } + defer sshClient.Close() + // 网元主机的SSH客户端进行文件传输 + sftpClient, err := sshClient.NewClientSFTP() + if err != nil { + return err + } + defer sftpClient.Close() // 网元端授权文件路径 neTypeLower := strings.ToLower(neLicense.NeType) - neLicensePath := fmt.Sprintf("/usr/local/etc/%s/license", neTypeLower) - // 修改文件夹权限 - NewNeInfoImpl.NeRunCMD(neLicense.NeType, neLicense.NeId, fmt.Sprintf("sudo chmod o+w %s/", neLicensePath)) + neLicensePath := fmt.Sprintf("/usr/local/etc/%s/license/system.ini", neTypeLower) + neLicenseDir := filepath.ToSlash(filepath.Dir(neLicensePath)) + // 修改网元文件权限 + sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 775 %s && sudo touch %s && sudo chmod o+rw %s", neLicenseDir, neLicenseDir, neLicensePath, neLicensePath)) + // 尝试备份授权文件 neLicensePathBack := fmt.Sprintf("%s/system_%s.ini", neLicensePath, time.Now().Format("20060102_150405")) - NewNeInfoImpl.NeRunCMD(neLicense.NeType, neLicense.NeId, fmt.Sprintf("sudo cp -rf %s/system.ini %s", neLicensePath, neLicensePathBack)) + sshClient.RunCMD(fmt.Sprintf("sudo cp -rf %s/system.ini %s", neLicensePath, neLicensePathBack)) + // 上传授权文件去覆盖 - NewNeInfoImpl.NeRunCMD(neLicense.NeType, neLicense.NeId, fmt.Sprintf("sudo chmod o+w %s/system.ini", neLicensePath)) - if err := ssh.FileSCPLocalToNe(neHostInfo.Addr, omcLicensePath, neLicensePath+"/system.ini"); err != nil { - return fmt.Errorf("error uploading license") + if err := sftpClient.CopyFileLocalToRemote(omcLicensePath, neLicensePath); err != nil { + return err } // 重启服务 if neLicense.Reload { cmdStr := fmt.Sprintf("sudo service %s restart", neTypeLower) if neTypeLower == "ims" { - cmdStr = "sudo ims-stop && sudo ims-start" + cmdStr = "sudo ims-stop || true && sudo ims-start" } else if neTypeLower == "omc" { cmdStr = "sudo /usr/local/omc/bin/omcsvc.sh restart" } - NewNeInfoImpl.NeRunCMD(neLicense.NeType, neLicense.NeId, cmdStr) + sshClient.RunCMD(cmdStr) } return nil } diff --git a/src/modules/network_element/service/ne_software.go b/src/modules/network_element/service/ne_software.go index 5f5e10f..37e9ad4 100644 --- a/src/modules/network_element/service/ne_software.go +++ b/src/modules/network_element/service/ne_software.go @@ -25,7 +25,6 @@ type INeSoftware interface { // CheckUniqueTypeAndNameAndVersion 校验网元类型和文件名版本是否唯一 CheckUniqueTypeAndNameAndVersion(neType, name, version, id string) bool - // UploadToNeHost 安装包上传到网元主机 - // 返回执行命令步骤 - UploadToNeHost(neSoftware model.NeSoftware) ([]string, error) + // UpdateVersions 更新软件包对应网元的新版本 + UpdateVersions(neSoftware model.NeSoftware, neVersion model.NeVersion) int64 } diff --git a/src/modules/network_element/service/ne_software.impl.go b/src/modules/network_element/service/ne_software.impl.go index 77783a3..8ac5209 100644 --- a/src/modules/network_element/service/ne_software.impl.go +++ b/src/modules/network_element/service/ne_software.impl.go @@ -3,11 +3,8 @@ package service import ( "fmt" "os" - "path/filepath" - "strings" "be.ems/src/framework/utils/file" - "be.ems/src/framework/utils/ssh" "be.ems/src/modules/network_element/model" "be.ems/src/modules/network_element/repository" ) @@ -47,26 +44,69 @@ func (r *NeSoftwareImpl) SelectById(id string) model.NeSoftware { // Insert 新增信息 func (r *NeSoftwareImpl) Insert(neSoftware model.NeSoftware) string { - return r.neSoftwareRepository.Insert(neSoftware) + inserId := r.neSoftwareRepository.Insert(neSoftware) + if inserId != "" { + // 更新同类型的新包版本 + neVersions := NewNeVersionImpl.SelectList(model.NeVersion{NeType: neSoftware.NeType}) + if len(neVersions) > 0 { + for _, neVersion := range neVersions { + neVersion.NewName = neSoftware.Name + neVersion.NewVersion = neSoftware.Version + neVersion.NewPath = neSoftware.Path + neVersion.Status = "3" + neVersion.UpdateBy = neSoftware.CreateBy + NewNeVersionImpl.Update(neVersion) + } + } + } + return inserId } // Update 修改信息 func (r *NeSoftwareImpl) Update(neSoftware model.NeSoftware) int64 { - return r.neSoftwareRepository.Update(neSoftware) + rows := r.neSoftwareRepository.Update(neSoftware) + if rows > 0 { + // 更新同类型的新包版本 + neVersions := NewNeVersionImpl.SelectList(model.NeVersion{ + NeType: neSoftware.NeType, + Status: "3", + }) + if len(neVersions) > 0 { + for _, neVersion := range neVersions { + neVersion.NewName = neSoftware.Name + neVersion.NewVersion = neSoftware.Version + neVersion.NewPath = neSoftware.Path + neVersion.Status = "3" + neVersion.UpdateBy = neSoftware.UpdateBy + NewNeVersionImpl.Update(neVersion) + } + } + } + return rows } // DeleteByIds 批量删除信息 func (r *NeSoftwareImpl) DeleteByIds(ids []string) (int64, error) { // 检查是否存在 - rowIds := r.neSoftwareRepository.SelectByIds(ids) - if len(rowIds) <= 0 { + rows := r.neSoftwareRepository.SelectByIds(ids) + if len(rows) <= 0 { return 0, fmt.Errorf("neSoftware.noData") } - if len(rowIds) == len(ids) { + if len(rows) == len(ids) { + // 遍历软件包列表进行文件删除 + for _, row := range rows { + // 检查文件是否存在 + filePath := file.ParseUploadFilePath(row.Path) + if _, err := os.Stat(filePath); err != nil { + continue + } + os.Remove(filePath) + } rows := r.neSoftwareRepository.DeleteByIds(ids) return rows, nil } + // 删除信息失败! return 0, fmt.Errorf("delete fail") } @@ -84,54 +124,20 @@ func (r *NeSoftwareImpl) CheckUniqueTypeAndNameAndVersion(neType, name, version, return uniqueId == "" } -// UploadToNeHost 安装包上传到网元主机 -// 返回执行命令步骤 -func (r *NeSoftwareImpl) UploadToNeHost(neSoftware model.NeSoftware) ([]string, error) { - cmdStrArr := []string{} - // 检查文件是否存在 - filePath := file.ParseUploadFilePath(neSoftware.Path) - if _, err := os.Stat(filePath); err != nil { - return cmdStrArr, fmt.Errorf("file read failure") +// UpdateVersions 更新软件包对应网元的新版本 +func (r *NeSoftwareImpl) UpdateVersions(neSoftware model.NeSoftware, neVersion model.NeVersion) int64 { + var rows int64 = 0 + // 更新同类型的新包版本 + neVersions := NewNeVersionImpl.SelectList(neVersion) + if len(neVersions) > 0 { + for _, v := range neVersions { + v.NewName = neSoftware.Name + v.NewVersion = neSoftware.Version + v.NewPath = neSoftware.Path + v.Status = "3" + v.UpdateBy = neVersion.UpdateBy + rows += NewNeVersionImpl.Update(v) + } } - fileName := filepath.Base(neSoftware.Path) - if strings.Contains(fileName, "*") { - fileName = strings.ReplaceAll(fileName, "*", "_") - } - nePath := "/tmp" - neFilePath := fmt.Sprintf("%s/%s", nePath, fileName) - - // 检查网元主机 - neHostInfo := NewNeHostImpl.SelectById(neSoftware.HostId) - if neHostInfo.HostType != "ssh" || neHostInfo.HostID != neSoftware.HostId { - return cmdStrArr, fmt.Errorf("no found host info") - } - - // 上传软件包到 /tmp - if err := ssh.FileSCPLocalToNe(neHostInfo.Addr, filePath, neFilePath); err != nil { - return cmdStrArr, fmt.Errorf("error uploading package") - } - - // 安装软件包 - cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo dpkg -i %s", neFilePath)) - - if neSoftware.NeType == "IMS" { - // 公网 PLMN地址 - cmdStrArr = append(cmdStrArr, "sudo /usr/local/etc/ims/default/tools/modipplmn.sh {PUBIP} {MCC} {MNC}") - // 内网 服务地址 - cmdStrArr = append(cmdStrArr, "sudo /usr/local/etc/ims/default/tools/modintraip.sh {PRIIP}") - // 10s后停止服务 - cmdStrArr = append(cmdStrArr, "sudo ims-start") - cmdStrArr = append(cmdStrArr, `nohup sh -c "sleep 10s && sudo ims-stop" > /dev/null 2>&1 &`) - } else { - // 10s后停止服务 - cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo service %s restart", strings.ToLower(neSoftware.NeType))) - cmdStrArr = append(cmdStrArr, fmt.Sprintf(`nohup sh -c "sleep 10s && sudo service %s stop" > /dev/null 2>&1 &`, strings.ToLower(neSoftware.NeType))) - } - - // 删除软件包 - cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo rm %s", neFilePath)) - - // 结束 - cmdStrArr = append(cmdStrArr, fmt.Sprintf("echo '%s software install successful!'", neSoftware.NeType)) - return cmdStrArr, nil + return rows } diff --git a/src/modules/network_element/service/ne_version.go b/src/modules/network_element/service/ne_version.go index d0ebc05..f92a81b 100644 --- a/src/modules/network_element/service/ne_version.go +++ b/src/modules/network_element/service/ne_version.go @@ -22,9 +22,11 @@ type INeVersion interface { // DeleteByIds 批量删除信息 DeleteByIds(ids []string) (int64, error) - // SelectByTypeAndID 通过网元类型和网元ID查询 - SelectByTypeAndID(neType, neId string) model.NeVersion + // SelectByNeTypeAndNeID 通过网元类型和网元ID查询 + SelectByNeTypeAndNeID(neType, neId string) model.NeVersion - // CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 - CheckUniqueTypeAndID(neType, neId, id string) bool + // Operate 操作版本上传到网元主机执行命令 + // + // action 安装行为:install upgrade rollback + Operate(action string, neVersion model.NeVersion, preinput map[string]string) (string, error) } diff --git a/src/modules/network_element/service/ne_version.impl.go b/src/modules/network_element/service/ne_version.impl.go index d3f4f08..473f109 100644 --- a/src/modules/network_element/service/ne_version.impl.go +++ b/src/modules/network_element/service/ne_version.impl.go @@ -2,7 +2,13 @@ package service import ( "fmt" + "os" + "path/filepath" + "strings" + "time" + "be.ems/src/framework/utils/file" + "be.ems/src/framework/utils/ssh" "be.ems/src/modules/network_element/model" "be.ems/src/modules/network_element/repository" ) @@ -66,8 +72,8 @@ func (r *NeVersionImpl) DeleteByIds(ids []string) (int64, error) { return 0, fmt.Errorf("delete fail") } -// SelectByTypeAndID 通过网元类型和网元ID查询 -func (r *NeVersionImpl) SelectByTypeAndID(neType, neId string) model.NeVersion { +// SelectByNeTypeAndNeID 通过网元类型和网元ID查询 +func (r *NeVersionImpl) SelectByNeTypeAndNeID(neType, neId string) model.NeVersion { neVersions := r.neVersionRepository.SelectList(model.NeVersion{ NeType: neType, NeId: neId, @@ -78,14 +84,539 @@ func (r *NeVersionImpl) SelectByTypeAndID(neType, neId string) model.NeVersion { return model.NeVersion{} } -// CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 -func (r *NeVersionImpl) CheckUniqueTypeAndID(neType, neId, id string) bool { - uniqueId := r.neVersionRepository.CheckUniqueTypeAndID(model.NeVersion{ - NeType: neType, - NeId: neId, - }) - if uniqueId == id { - return true +// Operate 操作版本上传到网元主机执行命令 +// +// action 安装行为:install upgrade rollback +func (r *NeVersionImpl) Operate(action string, neVersion model.NeVersion, preinput map[string]string) (string, error) { + // 网元主机的SSH客户端 + sshClient, err := NewNeInfoImpl.NeRunSSHclient(neVersion.NeType, neVersion.NeId) + if err != nil { + return "", err } - return uniqueId == "" + defer sshClient.Close() + + // ========= 文件传输阶段 ========= + softwarePath := neVersion.Path + if action == "install" || action == "upgrade" { + softwarePath = neVersion.NewPath + } + if action == "rollback" { + softwarePath = neVersion.PrePath + } + neFilePaths, err := r.operateFile(sshClient, softwarePath) + if err != nil { + return "", err + } + + // ========= 安装时设置 ========= + if action == "install" { + // 网元公共配置文件 + para5GMap, err := NewNeInfoImpl.NeConfPara5GRead() + if para5GMap == nil || err != nil { + return "", fmt.Errorf("error read para5G file info") + } + if err := NewNeInfoImpl.NeConfPara5GWirte(para5GMap, []string{fmt.Sprintf("%s@%s", neVersion.NeType, neVersion.NeId)}); err != nil { + return "", fmt.Errorf("error wirte para5G file info") + } + } + + // ========= 命令生成阶段 ========= + okFlagStr, cmdStrArr, err := r.operateCommand(action, neVersion.NeType, neFilePaths) + if err != nil { + return "", err + } + // 操作自己omc时 + if neVersion.NeType == "OMC" { + return sshClient.RunCMD(fmt.Sprintf("nohup sh -c \"sleep 3s && %s\" > /dev/null 2>&1 & \n", strings.Join(cmdStrArr, " && "))) + } + + // ========= 执行阶段 ========= + commandLine, err := r.operateRun(sshClient, preinput, cmdStrArr, neVersion.NeType, okFlagStr) + if err != nil { + return "", err + } + + // ========= 完成阶段 ========= + if strings.LastIndex(commandLine, okFlagStr) > 5 { + if err := r.operateDome(action, neVersion); err != nil { + return "", err + } + } + return commandLine, nil +} + +// operateDome 操作版本-文件传输阶段 +func (r *NeVersionImpl) operateFile(sshClient *ssh.ConnSSH, softwarePath string) ([]string, error) { + // 网元主机的SSH客户端进行文件传输 + sftpClient, err := sshClient.NewClientSFTP() + if err != nil { + return nil, err + } + defer sftpClient.Close() + + nePath := "/tmp" + copyFileToNeMap := map[string]string{} + + // 统一处理多个文件和单个文件的情况 + var softwarePaths []string + if strings.Contains(softwarePath, ",") { + softwarePaths = strings.Split(softwarePath, ",") + } else { + softwarePaths = []string{softwarePath} + } + + for _, path := range softwarePaths { + // 检查文件是否存在 + localFilePath := file.ParseUploadFilePath(path) + if _, err := os.Stat(localFilePath); err != nil { + return nil, fmt.Errorf("file read failure") + } + + fileName := filepath.Base(path) + neFilePath := fmt.Sprintf("%s/%s", nePath, fileName) + copyFileToNeMap[localFilePath] = neFilePath + } + + // 上传软件包到 /tmp + neFilePaths := []string{} + for k, v := range copyFileToNeMap { + if err = sftpClient.CopyFileLocalToRemote(k, v); err != nil { + return nil, fmt.Errorf("error uploading package") + } + neFilePaths = append(neFilePaths, v) + } + return neFilePaths, nil +} + +// operateDome 操作版本-命令生成阶段 +func (r *NeVersionImpl) operateCommand(action, neType string, neFilePaths []string) (string, []string, error) { + neTypeLower := strings.ToLower(neType) + // 命令终止结束标记 + okFlagStr := fmt.Sprintf("%s version %s successful!", neTypeLower, action) + // 安装软件包 + pkgCmdStr := fmt.Sprintf("sudo dpkg -i %s", strings.Join(neFilePaths, " ")) + fileExt := filepath.Ext(strings.ToLower(neFilePaths[0])) + if strings.HasSuffix(fileExt, "rpm") { + pkgCmdStr = fmt.Sprintf("sudo rpm -Uvh %s", strings.Join(neFilePaths, " ")) + } + + // 组合命令输入 + cmdStrArr := []string{} + if neType == "OMC" { + cmdStrArr = append(cmdStrArr, pkgCmdStr) + if action == "install" { + cmdStrArr = append(cmdStrArr, "sudo /usr/local/omc/bin/setomc.sh -m install") // 初始化数据库 + } else { + cmdStrArr = append(cmdStrArr, "sudo /usr/local/omc/bin/omcsvc.sh stop") + cmdStrArr = append(cmdStrArr, "sudo /usr/local/omc/bin/setomc.sh -m upgrade") // 升级数据库 + } + cmdStrArr = append(cmdStrArr, "sudo /usr/local/omc/bin/omcsvc.sh restart") + } else if neType == "IMS" { + if action == "install" { + para5GData := NewNeInfoImpl.Para5GData + cmdStrArr = append(cmdStrArr, pkgCmdStr+" \n") + + // 公网 PLMN地址 + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo /usr/local/etc/ims/default/tools/modipplmn.sh %s %s %s \n", para5GData["SIP_IP"], para5GData["MCC"], para5GData["MNC"])) + // 内网 服务地址 + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo /usr/local/etc/ims/default/tools/modintraip.sh %s \n", para5GData["IMS_IP"])) + // IWF连接PCF服务 + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.160/%s/g\" /usr/local/etc/iwf/iwf_conf.yaml \n", para5GData["PCF_IP"])) + // 设置 HOST + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s ims' /etc/hosts || echo '%s ims' | sudo tee -a /etc/hosts \n", para5GData["IMS_IP"], para5GData["IMS_IP"])) + mnc_mcc := fmt.Sprintf("mnc%s.mcc%s", para5GData["MNC_DOMAIN"], para5GData["MCC"]) + hssHost := fmt.Sprintf("%s hss.ims.%s.3gppnetwork.org hss", para5GData["UDM_IP"], mnc_mcc) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s' /etc/hosts || echo '%s' | sudo tee -a /etc/hosts \n", hssHost, hssHost)) + pcrfHost := fmt.Sprintf("%s pcrf.epc.%s.3gppnetwork.org pcrf", para5GData["IMS_IP"], mnc_mcc) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s' /etc/hosts || echo '%s' | sudo tee -a /etc/hosts \n", pcrfHost, pcrfHost)) + imsOrgHost := fmt.Sprintf("ims.%s.3gppnetwork.org", mnc_mcc) + imsHost := fmt.Sprintf("%s %s ims", para5GData["SIP_IP"], imsOrgHost) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s' /etc/hosts || echo '%s' | sudo tee -a /etc/hosts \n", imsHost, imsHost)) + pcscfHost := fmt.Sprintf("%s pcscf.%s pcscf", para5GData["SIP_IP"], imsOrgHost) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s' /etc/hosts || echo '%s' | sudo tee -a /etc/hosts \n", pcscfHost, pcscfHost)) + icscfHost := fmt.Sprintf("%s icscf.%s icscf", para5GData["SIP_IP"], imsOrgHost) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s' /etc/hosts || echo '%s' | sudo tee -a /etc/hosts \n", icscfHost, icscfHost)) + scscfHost := fmt.Sprintf("%s scscf.%s scscf", para5GData["SIP_IP"], imsOrgHost) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s' /etc/hosts || echo '%s' | sudo tee -a /etc/hosts \n", scscfHost, scscfHost)) + mmtelHost := fmt.Sprintf("%s mmtel.%s mmtel", para5GData["SIP_IP"], imsOrgHost) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s' /etc/hosts || echo '%s' | sudo tee -a /etc/hosts \n", mmtelHost, mmtelHost)) + mrfcHost := fmt.Sprintf("%s mrfc.%s mrfc", para5GData["SIP_IP"], imsOrgHost) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s' /etc/hosts || echo '%s' | sudo tee -a /etc/hosts \n", mrfcHost, mrfcHost)) + smsHost := fmt.Sprintf("%s smsc.%s smsc", para5GData["SIP_IP"], imsOrgHost) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s' /etc/hosts || echo '%s' | sudo tee -a /etc/hosts \n", smsHost, smsHost)) + // adb + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/adb/default/adb.conf /usr/local/etc/adb/adb.conf \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/bind 127.0.0.1/bind %s/g\" /usr/local/etc/adb/adb.conf \n", para5GData["ADB_IP"])) + + cmdStrArr = append(cmdStrArr, "sudo ims-stop || true && sudo ims-start \n") + // 30s后停止服务 + // cmdStrArr = append(cmdStrArr, "nohup sh -c \"sleep 30s && sudo ims-stop\" > /dev/null 2>&1 & \n") + } else { + cmdStrArr = append(cmdStrArr, "sudo ims-stop \n") + cmdStrArr = append(cmdStrArr, pkgCmdStr+" \n") + cmdStrArr = append(cmdStrArr, "sudo ims-start \n") + } + } else { + if action == "install" { + para5GData := NewNeInfoImpl.Para5GData + cmdStrArr = append(cmdStrArr, pkgCmdStr+" \n") + + // AMF配置修改 + if neTypeLower == "amf" { + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/amf/default/amfcfg.yaml /usr/local/etc/amf/amfcfg.yaml \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.120/%s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["AMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.8.120/%s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["N2_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mcc: 001/mcc: %s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mnc: 01/mnc: %s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["MNC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/sst: 1/sst: %s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["SST"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/sd: 000001/sd: %s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["SD"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/- 4388/- %s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["TAC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.130/%s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["AUSF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.140/%s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["UDM_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.150/%s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["SMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.160/%s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["PCF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.180/%s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["NRF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.200/%s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["LMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.210/%s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["NEF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/- internet/- %s/g\" /usr/local/etc/amf/amfcfg.yaml \n", para5GData["DNN_DATA"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s n2' /etc/hosts || echo '%s n2' | sudo tee -a /etc/hosts \n", para5GData["N2_IP"], para5GData["N2_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s amf' /etc/hosts || echo '%s amf' | sudo tee -a /etc/hosts \n", para5GData["AMF_IP"], para5GData["AMF_IP"])) + } + // AUSF配置修改 + if neTypeLower == "ausf" { + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/ausf/default/ausfcfg.yaml /usr/local/etc/ausf/ausfcfg.yaml \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.130/%s/g\" /usr/local/etc/ausf/ausfcfg.yaml \n", para5GData["AUSF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.140/%s/g\" /usr/local/etc/ausf/ausfcfg.yaml \n", para5GData["UDM_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.180/%s/g\" /usr/local/etc/ausf/ausfcfg.yaml \n", para5GData["NRF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mcc: 001/mcc: %s/g\" /usr/local/etc/ausf/ausfcfg.yaml \n", para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mnc: 01/mnc: %s/g\" /usr/local/etc/ausf/ausfcfg.yaml \n", para5GData["MNC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s ausf' /etc/hosts || echo '%s ausf' | sudo tee -a /etc/hosts \n", para5GData["AUSF_IP"], para5GData["AUSF_IP"])) + } + // UDM配置修改 + if neTypeLower == "udm" { + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/udm/default/udmcfg.yaml /usr/local/etc/udm/udmcfg.yaml \n") + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/udm/default/nssai.yaml /usr/local/etc/udm/nssai.yaml \n") + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/udm/default/snssai.yaml /usr/local/etc/udm/snssai.yaml \n") + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/udm/default/as.yaml /usr/local/etc/udm/as.yaml \n") + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/udm/default/dnn.yaml /usr/local/etc/udm/dnn.yaml \n") + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/udm/default/scscfSet.yaml /usr/local/etc/udm/scscfSet.yaml \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.180/%s/g\" /usr/local/etc/udm/udmcfg.yaml \n", para5GData["NRF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.140/%s/g\" /usr/local/etc/udm/udmcfg.yaml \n", para5GData["UDM_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.130/%s/g\" /usr/local/etc/udm/udmcfg.yaml \n", para5GData["AUSF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.120/%s/g\" /usr/local/etc/udm/udmcfg.yaml \n", para5GData["AMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mcc: 001/mcc: %s/g\" /usr/local/etc/udm/udmcfg.yaml \n", para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mnc: 01/mnc: %s/g\" /usr/local/etc/udm/udmcfg.yaml \n", para5GData["MNC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mnc001.mcc001/mnc%s.mcc%s/g\" /usr/local/etc/udm/udmcfg.yaml \n", para5GData["MNC_DOMAIN"], para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mnc001.mcc001/mnc%s.mcc%s/g\" /usr/local/etc/udm/as.yaml \n", para5GData["MNC_DOMAIN"], para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mnc001.mcc001/mnc%s.mcc%s/g\" /usr/local/etc/udm/scscfSet.yaml \n", para5GData["MNC_DOMAIN"], para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/sst: 1/sst: %s/g\" /usr/local/etc/udm/nssai.yaml \n", para5GData["SST"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/sst: 1/sst: %s/g\" /usr/local/etc/udm/snssai.yaml \n", para5GData["SST"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/sd: 000001/sd: %s/g\" /usr/local/etc/udm/nssai.yaml \n", para5GData["SD"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/sd: 000001/sd: %s/g\" /usr/local/etc/udm/snssai.yaml \n", para5GData["SD"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/dnn: internet/dnn: %s/g\" /usr/local/etc/udm/snssai.yaml \n", para5GData["DNN_DATA"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/dnn: ims/dnn: %s/g\" /usr/local/etc/udm/snssai.yaml \n", para5GData["DNN_IMS"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/serviceSelection: 'internet'/serviceSelection: '%s'/g\" /usr/local/etc/udm/epsApn.yaml \n", para5GData["DNN_DATA"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/serviceSelection: 'ims'/serviceSelection: '%s'/g\" /usr/local/etc/udm/epsApn.yaml \n", para5GData["DNN_IMS"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/dnn: internet/dnn: %s/g\" /usr/local/etc/udm/dnn.yaml \n", para5GData["DNN_DATA"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/dnn: ims/dnn: %s/g\" /usr/local/etc/udm/dnn.yaml \n", para5GData["DNN_IMS"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.18.81/%s/g\" /usr/local/etc/udm/as.yaml \n", para5GData["SIP_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s udm' /etc/hosts || echo '%s udm' | sudo tee -a /etc/hosts \n", para5GData["UDM_IP"], para5GData["UDM_IP"])) + // adb + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/adb/default/adb.conf /usr/local/etc/adb/adb.conf \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/bind 127.0.0.1/bind %s/g\" /usr/local/etc/adb/adb.conf \n", para5GData["ADB_IP"])) + } + // SMF配置修改 + if neTypeLower == "smf" { + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/smf/default/smf_conf.yaml /usr/local/etc/smf/smf_conf.yaml \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.8.110/%s/g\" /usr/local/etc/smf/smf_conf.yaml \n", para5GData["SIP_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.120/%s/g\" /usr/local/etc/smf/smf_conf.yaml \n", para5GData["AMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.140/%s/g\" /usr/local/etc/smf/smf_conf.yaml \n", para5GData["UDM_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.150/%s/g\" /usr/local/etc/smf/smf_conf.yaml \n", para5GData["SMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.160/%s/g\" /usr/local/etc/smf/smf_conf.yaml \n", para5GData["PCF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.180/%s/g\" /usr/local/etc/smf/smf_conf.yaml \n", para5GData["NRF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.190/%s/g\" /usr/local/etc/smf/smf_conf.yaml \n", para5GData["UPF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s|10.2.1.0/24|%s|g\" /usr/local/etc/smf/smf_conf.yaml \n", para5GData["UE_POOL"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/internet/%s/g\" /usr/local/etc/smf/smf_conf.yaml \n", para5GData["DNN_DATA"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s smf' /etc/hosts || echo '%s smf' | sudo tee -a /etc/hosts \n", para5GData["SMF_IP"], para5GData["SMF_IP"])) + } + // PCF配置修改 + if neTypeLower == "pcf" { + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/pcf/default/pcfcfg.yaml /usr/local/etc/pcf/pcfcfg.yaml \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.120/%s/g\" /usr/local/etc/pcf/pcfcfg.yaml \n", para5GData["AMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.140/%s/g\" /usr/local/etc/pcf/pcfcfg.yaml \n", para5GData["UDM_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.160/%s/g\" /usr/local/etc/pcf/pcfcfg.yaml \n", para5GData["PCF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.180/%s/g\" /usr/local/etc/pcf/pcfcfg.yaml \n", para5GData["NRF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.210/%s/g\" /usr/local/etc/pcf/pcfcfg.yaml \n", para5GData["NEF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mcc: 001/mcc: %s/g\" /usr/local/etc/pcf/pcfcfg.yaml \n", para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mnc: 01/mnc: %s/g\" /usr/local/etc/pcf/pcfcfg.yaml \n", para5GData["MNC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mnc001.mcc001/mnc%s.mcc%s/g\" /usr/local/etc/pcf/pcfcfg.yaml \n", para5GData["MNC_DOMAIN"], para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s pcf' /etc/hosts || echo '%s pcf' | sudo tee -a /etc/hosts \n", para5GData["PCF_IP"], para5GData["PCF_IP"])) + } + + // NSSF配置修改 + if neTypeLower == "nssf" { + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/nssf/default/nssfcfg.yaml /usr/local/etc/nssf/nssfcfg.yaml \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.170/%s/g\" /usr/local/etc/nssf/nssfcfg.yaml \n", para5GData["NSSF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.180/%s/g\" /usr/local/etc/nssf/nssfcfg.yaml \n", para5GData["NRF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mcc: 001/mcc: %s/g\" /usr/local/etc/nssf/nssfcfg.yaml \n", para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mnc: 01/mnc: %s/g\" /usr/local/etc/nssf/nssfcfg.yaml \n", para5GData["MNC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s nssf' /etc/hosts || echo '%s nssf' | sudo tee -a /etc/hosts \n", para5GData["NSSF_IP"], para5GData["NSSF_IP"])) + } + // NRF配置修改 + if neTypeLower == "nrf" { + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/nrf/default/nrfcfg.yaml /usr/local/etc/nrf/nrfcfg.yaml \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.180/%s/g\" /usr/local/etc/nrf/nrfcfg.yaml \n", para5GData["NRF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mcc: 001/mcc: %s/g\" /usr/local/etc/nrf/nrfcfg.yaml \n", para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mnc: 01/mnc: %s/g\" /usr/local/etc/nrf/nrfcfg.yaml \n", para5GData["MNC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s nrf' /etc/hosts || echo '%s nrf' | sudo tee -a /etc/hosts \n", para5GData["NRF_IP"], para5GData["NRF_IP"])) + } + + // UPF配置修改 + if neTypeLower == "upf" { + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/upf/default/upfcfg.yaml /usr/local/etc/upf/upfcfg.yaml \n") + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/upf/default/upfForwarder_1.yaml /usr/local/etc/upf/upfForwarder_1.yaml \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.190/%s/g\" /usr/local/etc/upf/upfcfg.yaml \n", para5GData["UPF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/localhost/%s/g\" /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UPF_IP"])) + // UE + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/ueIpv4: 10.2.1.0/s/ueIpv4: 10.2.1.0/ueIpv4: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UE_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/ueIpv4Mask: 255.255.255.0/s/ueIpv4Mask: 255.255.255.0/ueIpv4Mask: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UE_MASK"])) + // N3 + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.8.190/%s/g\" /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N3\"/,/ipv4Mask: 255.255.240.0/s/ipv4Mask: 255.255.240.0/ipv4Mask: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_MASK"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N3\"/,/gatewayIpv4: 192.168.1.254/s/gatewayIpv4: 192.168.1.254/gatewayIpv4: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_GW"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N3\"/,/interfacePCI: \"0000:00:00.0\"/s/interfacePCI: \"0000:00:00.0\"/interfacePCI: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_PCI"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N3\"/,/macAddr: \"00:00:00:00:00:00\"/s/macAddr: \"00:00:00:00:00:00\"/macAddr: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_MAC"])) + // 标准版 N6 + if para5GData["UPF_TYPE"] == "StandardUPF" { + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.8.191/%s/g\" /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/ipv4Mask: 255.255.240.0/s/ipv4Mask: 255.255.240.0/ipv4Mask: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_MASK"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/gatewayIpv4: 192.168.1.254/s/gatewayIpv4: 192.168.1.254/gatewayIpv4: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_GW"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/interfacePCI: \"0000:00:00.0\"/s/interfacePCI: \"0000:00:00.0\"/interfacePCI: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_PCI"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/macAddr: \"00:00:00:00:00:00\"/s/macAddr: \"00:00:00:00:00:00\"/macAddr: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_MAC"])) + // 路由 + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo ip route add '%s/%s' via '%s' \n", para5GData["UE_IP"], para5GData["UE_CIDR"], para5GData["N6_IP"])) + } + // 轻量版 + if para5GData["UPF_TYPE"] == "LightUPF" { + cmdStrArr = append(cmdStrArr, "sudo sed -i \"s/192.168.8.191/0.0.0.0/g\" /usr/local/etc/upf/upfForwarder_1.yaml \n") + cmdStrArr = append(cmdStrArr, "sudo sed -i \"s/type: upfd/type: tun/g\" /usr/local/etc/upf/upfForwarder_1.yaml \n") + cmdStrArr = append(cmdStrArr, "sudo sed -i 's/driverType: vmxnet3/driverType: \"\"/g' /usr/local/etc/upf/upfForwarder_1.yaml \n") + } + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s upf' /etc/hosts || echo '%s upf' | sudo tee -a /etc/hosts \n", para5GData["UPF_IP"], para5GData["UPF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s upfn3' /etc/hosts || echo '%s upfn3' | sudo tee -a /etc/hosts \n", para5GData["N3_IP"], para5GData["N3_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s upfn6' /etc/hosts || echo '%s upfn6' | sudo tee -a /etc/hosts \n", para5GData["N6_IP"], para5GData["N6_IP"])) + } + + // LMF配置修改 - 已不再维护,导致激活License失败 + // NEF配置修改 - SNMP无需License + if neTypeLower == "nef" { + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/nef/default/nef_conf.yaml /usr/local/etc/nef/nef_conf.yaml \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.110/%s/g\" /usr/local/etc/nef/nef_conf.yaml \n", para5GData["IMS_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.120/%s/g\" /usr/local/etc/nef/nef_conf.yaml \n", para5GData["AMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.130/%s/g\" /usr/local/etc/nef/nef_conf.yaml \n", para5GData["AUSF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.140/%s/g\" /usr/local/etc/nef/nef_conf.yaml \n", para5GData["UDM_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.150/%s/g\" /usr/local/etc/nef/nef_conf.yaml \n", para5GData["SMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.160/%s/g\" /usr/local/etc/nef/nef_conf.yaml \n", para5GData["PCF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.170/%s/g\" /usr/local/etc/nef/nef_conf.yaml \n", para5GData["NSSF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.180/%s/g\" /usr/local/etc/nef/nef_conf.yaml \n", para5GData["NRF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.190/%s/g\" /usr/local/etc/nef/nef_conf.yaml \n", para5GData["UPF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.210/%s/g\" /usr/local/etc/nef/nef_conf.yaml \n", para5GData["NEF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s nef' /etc/hosts || echo '%s nef' | sudo tee -a /etc/hosts \n", para5GData["NEF_IP"], para5GData["NEF_IP"])) + } + + // MME配置修改 - 4G + if neTypeLower == "mme" { + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/mme/default/mme.conf /usr/local/etc/mme/mme.conf \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.120/%s/g\" /usr/local/etc/mme/mme.conf \n", para5GData["AMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.150/%s/g\" /usr/local/etc/mme/mme.conf \n", para5GData["SMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s|192.168.8.220/20|%s|g\" /usr/local/etc/mme/mme.conf \n", para5GData["S1_MMEIP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s|172.16.5.220/24|%s|g\" /usr/local/etc/mme/mme.conf \n", para5GData["S11_MMEIP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s|172.16.5.221/24|%s|g\" /usr/local/etc/mme/mme.conf \n", para5GData["S10_MMEIP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/mnc001.mcc001/mnc%s.mcc%s/g\" /usr/local/etc/mme/mme.conf \n", para5GData["MNC_DOMAIN"], para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/\"00101\"/\"%s%s\"/g\" /usr/local/etc/mme/mme.conf \n", para5GData["MCC"], para5GData["MNC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/MCC=\"001\" ; MNC=\"01\";/MCC=\"%s\" ; MNC=\"%s\";/g\" /usr/local/etc/mme/mme.conf \n", para5GData["MCC"], para5GData["MNC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/TAC = \"1\";/TAC = \"%s\";/g\" /usr/local/etc/mme/mme.conf \n", para5GData["TAC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/TAC = 1;/TAC = %s;/g\" /usr/local/etc/mme/mme.conf \n", para5GData["TAC"])) + // SMF开启 + cmdStrArr = append(cmdStrArr, "sudo sed -i \"/^ *gxcfg:/,/^ *[^ ]/{s/enable: false/enable: true/;b};\" /usr/local/etc/smf/smf_conf.yaml \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s mme' /etc/hosts || echo '%s mme' | sudo tee -a /etc/hosts \n", para5GData["S11_MMEIP"], para5GData["S11_MMEIP"])) + } + // N3IWF配置修改 + if neTypeLower == "n3iwf" { + cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/n3iwf/default/n3iwfcfg.yaml /usr/local/etc/n3iwf/n3iwfcfg.yaml \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/MCC: 001/MCC: %s/g\" /usr/local/etc/n3iwf/n3iwfcfg.yaml \n", para5GData["MCC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/MNC: 01/MNC: %s/g\" /usr/local/etc/n3iwf/n3iwfcfg.yaml \n", para5GData["MNC"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.12.161/%s/g\" /usr/local/etc/n3iwf/n3iwfcfg.yaml \n", para5GData["N3IWF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.12.160/%s/g\" /usr/local/etc/n3iwf/n3iwfcfg.yaml \n", para5GData["N3IWF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.8.27/%s/g\" /usr/local/etc/n3iwf/n3iwfcfg.yaml \n", para5GData["UDM_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.1.239/%s/g\" /usr/local/etc/n3iwf/n3iwfcfg.yaml \n", para5GData["SMF_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.8.22/%s/g\" /usr/local/etc/n3iwf/n3iwfcfg.yaml \n", para5GData["N2_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.1.161/%s/g\" /usr/local/etc/n3iwf/n3iwfcfg.yaml \n", para5GData["N3_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.1.160/%s/g\" /usr/local/etc/n3iwf/n3iwfcfg.yaml \n", para5GData["N6_IP"])) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s n3iwf' /etc/hosts || echo '%s n3iwf' | sudo tee -a /etc/hosts \n", para5GData["N3IWF_IP"], para5GData["N3IWF_IP"])) + } + + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo service %s restart \n", neTypeLower)) + // 30s后停止服务 + // cmdStrArr = append(cmdStrArr, fmt.Sprintf("nohup sh -c \"sleep 30s && sudo service %s stop\" > /dev/null 2>&1 & \n", neTypeLower)) + } else { + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo service %s stop \n", neTypeLower)) + cmdStrArr = append(cmdStrArr, pkgCmdStr+" \n") + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo service %s restart \n", neTypeLower)) + } + } + + // 删除软件包 + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo rm %s \n", strings.Join(neFilePaths, " "))) + // 结束 + cmdStrArr = append(cmdStrArr, fmt.Sprintf("echo '%s' \n", okFlagStr)) + + return okFlagStr, cmdStrArr, nil +} + +// operateDome 操作版本-执行阶段 +func (r *NeVersionImpl) operateRun(sshClient *ssh.ConnSSH, preinput map[string]string, cmdStrArr []string, neType string, okFlagStr string) (string, error) { + // ssh连接会话 + clientSession, err := sshClient.NewClientSession(127, 42) + if err != nil { + return "", fmt.Errorf("neinfo ssh client session new err") + } + defer clientSession.Close() + + firstRead := true // 首次命令进行记录日志信息 + commandLineText := "" // 日志信息 + done := make(chan bool) // 完成信号 + // 超时退出 120s + timeoutTicker := time.NewTicker(120 * time.Second) + defer timeoutTicker.Stop() + // 实时读取SSH消息直接输出 + msTicker := time.NewTicker(100 * time.Millisecond) + defer msTicker.Stop() + go func() { + for { + select { + case <-timeoutTicker.C: + done <- true + return + case <-msTicker.C: + outputByte := clientSession.Read() + if len(outputByte) > 0 { + outputStr := string(outputByte) + // 非首次进行记录命令 + if !firstRead { + commandLineText += outputStr + } + + // IMS预输入 + if neType == "IMS" { + // IMS包 P/I/S-CSCF Config 配置覆盖 + if strings.Contains(outputStr, "(P/I/S-CSCF Config)? ") { + if pisCSCF, ok := preinput["pisCSCF"]; ok && pisCSCF != "" { + clientSession.Write(fmt.Sprintf("%s \n", pisCSCF)) + } else { + clientSession.Write("y \n") + } + continue + } + // MF包 etc下目录覆盖 + if strings.Contains(outputStr, "/usr/local/etc/mf directory? (Yes/No, default: No)") { + if pisCSCF, ok := preinput["updateMFetc"]; ok && pisCSCF != "" { + clientSession.Write(fmt.Sprintf("%s \n", pisCSCF)) + } else { + clientSession.Write("No \n") + } + continue + } + // MF包 share下目录覆盖 + if strings.Contains(outputStr, "/usr/local/share/mf directory? (Yes/No, default: No)") { + if pisCSCF, ok := preinput["updateMFshare"]; ok && pisCSCF != "" { + clientSession.Write(fmt.Sprintf("%s \n", pisCSCF)) + } else { + clientSession.Write("No \n") + } + continue + } + } + + // 命令终止符后继续执行命令 + if len(cmdStrArr) > 0 && strings.LastIndex(outputStr, "~$ ") > 2 { + if firstRead { + firstRead = false + } + shiftElement := cmdStrArr[0] // 获取第一个元素 + cmdStrArr = cmdStrArr[1:] // 将第一个元素从切片中移除 + clientSession.Write(shiftElement) + continue + } + // 最后输出的退出标记 + if strings.LastIndex(outputStr, okFlagStr) > 5 { + done <- true + break + } + } + } + } + }() + // 等待写入协程完成 + <-done + + return commandLineText, nil +} + +// operateDome 操作版本-完成阶段 +func (r *NeVersionImpl) operateDome(action string, neVersion model.NeVersion) error { + if action == "install" { + // 网元信息 + neInfo := NewNeInfoImpl.SelectNeInfoByNeTypeAndNeID(neVersion.NeType, neVersion.NeId) + if neInfo.NeId != neVersion.NeId { + return fmt.Errorf("error found neinfo") + } + // ========= 网元OAM配置文件 start ========== + if err := NewNeInfoImpl.NeConfOAMSync(neInfo, nil, true); err != nil { + return fmt.Errorf("error wirte OAM file info") + } + // ========= 网元OAM配置文件 end =========== + } + + // 更新Version + verInfo := r.SelectByNeTypeAndNeID(neVersion.NeType, neVersion.NeId) + if verInfo.NeId == neVersion.NeId { + curName := verInfo.Name + curVersion := verInfo.Version + curPath := verInfo.Path + if action == "install" { + verInfo.Name = neVersion.NewName + verInfo.Version = neVersion.NewVersion + verInfo.Path = neVersion.NewPath + verInfo.PreName = "-" + verInfo.PreVersion = "-" + verInfo.PrePath = "-" + verInfo.NewName = "-" + verInfo.NewVersion = "-" + verInfo.NewPath = "-" + } + if action == "upgrade" { + verInfo.Name = neVersion.NewName + verInfo.Version = neVersion.NewVersion + verInfo.Path = neVersion.NewPath + verInfo.PreName = curName + verInfo.PreVersion = curVersion + verInfo.PrePath = curPath + verInfo.NewName = "-" + verInfo.NewVersion = "-" + verInfo.NewPath = "-" + } + if action == "rollback" { + verInfo.Name = neVersion.PreName + verInfo.Version = neVersion.PreVersion + verInfo.Path = neVersion.PrePath + verInfo.PreName = curName + verInfo.PreVersion = curVersion + verInfo.PrePath = curPath + } + + verInfo.Status = "1" + NewNeVersionImpl.Update(verInfo) + } + return nil } diff --git a/src/modules/ws/controller/ws.go b/src/modules/ws/controller/ws.go index 50fd01f..04257dd 100644 --- a/src/modules/ws/controller/ws.go +++ b/src/modules/ws/controller/ws.go @@ -144,8 +144,14 @@ func (s *WSController) SSH(c *gin.Context) { // 创建链接SSH客户端 var connSSH ssh.ConnSSH neHost.CopyTo(&connSSH) - client, err := connSSH.NewClient() - if err != nil { + var client *ssh.ConnSSH + var clientErr error + if neHost.AuthMode == "2" { + client, clientErr = connSSH.NewClientByLocalPrivate() + } else { + client, clientErr = connSSH.NewClient() + } + if clientErr != nil { // 连接主机失败,请检查连接参数后重试 c.JSON(200, result.ErrMsg(i18n.TKey(language, "neHost.errByHostInfo"))) return @@ -250,8 +256,19 @@ func (s *WSController) Telnet(c *gin.Context) { } defer client.Close() + // 终端单行字符数 + cols, err := strconv.Atoi(c.Query("cols")) + if err != nil || cols > 254 { + cols = 80 + } + // 终端显示行数 + rows, err := strconv.Atoi(c.Query("rows")) + if err != nil || cols > rows { + rows = 40 + } + // 创建Telnet客户端会话 - clientSession, err := client.NewClientSession() + clientSession, err := client.NewClientSession(uint8(cols), uint8(rows)) if err != nil { // 连接主机失败,请检查连接参数后重试 c.JSON(200, result.ErrMsg(i18n.TKey(language, "neHost.errByHostInfo"))) diff --git a/src/modules/ws/processor/cdr_connect.go b/src/modules/ws/processor/cdr_connect.go index cc97f79..bd05465 100644 --- a/src/modules/ws/processor/cdr_connect.go +++ b/src/modules/ws/processor/cdr_connect.go @@ -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 +}