feat: 添加oam模块

This commit is contained in:
TsMask
2025-09-25 15:20:49 +08:00
parent 1f0e0cfce2
commit 960a31645b
39 changed files with 4810 additions and 124 deletions

31
go.mod
View File

@@ -4,6 +4,7 @@ go 1.24
require (
github.com/dlclark/regexp2 v1.11.4
github.com/expr-lang/expr v1.17.6
github.com/gin-gonic/gin v1.10.1
github.com/go-ldap/ldap/v3 v3.4.11
github.com/go-resty/resty/v2 v2.14.0
@@ -24,13 +25,14 @@ require (
github.com/prometheus-community/pro-bing v0.7.0
github.com/redis/go-redis/v9 v9.12.0
github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v4 v4.24.12
github.com/shirou/gopsutil/v4 v4.25.6
github.com/slayercat/GoSNMPServer v0.5.2
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.19.0
github.com/spf13/pflag v1.0.6
github.com/spf13/viper v1.20.1
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.4
github.com/tsmask/go-oam v1.0.9
github.com/wneessen/go-mail v0.6.2
github.com/xuri/excelize/v2 v2.9.0
github.com/xuri/xgen v0.0.0-20240722131518-d0691b701898
@@ -56,10 +58,11 @@ require (
github.com/chenjiandongx/ginprom v0.0.0-20210617023641-6c809602c38a
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/creack/pty v1.1.24 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/ebitengine/purego v0.8.1 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
@@ -72,11 +75,11 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.0
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
@@ -91,11 +94,9 @@ require (
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/metaleap/go-util v0.0.0-20180330192724-a09253046f73 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
@@ -103,7 +104,7 @@ require (
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.21.1 // indirect
github.com/orcaman/concurrent-map/v2 v2.0.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_golang v1.20.0
@@ -112,14 +113,13 @@ require (
github.com/prometheus/procfs v0.15.1 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.4 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect; indirect // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/shirou/gopsutil/v3 v3.23.11 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.4.2 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect; indirect // indirect
github.com/spf13/afero v1.11.0 // indirect; indirect // indirect
github.com/spf13/cast v1.7.0 // indirect; indirect // indirect
github.com/spf13/afero v1.12.0 // indirect; indirect // indirect
github.com/spf13/cast v1.7.1 // indirect; indirect // indirect
github.com/subosito/gotenv v1.6.0 // indirect; indirect // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect; indirect // indirect
github.com/tebeka/strftime v0.1.5 // indirect
@@ -135,12 +135,11 @@ require (
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect
golang.org/x/image v0.19.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/tools v0.28.0 // indirect
golang.org/x/tools/cmd/guru v0.1.1-deprecated // indirect
google.golang.org/protobuf v1.34.2 // indirect
google.golang.org/protobuf v1.36.1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
xorm.io/builder v0.3.13 // indirect
)

76
go.sum
View File

@@ -29,26 +29,29 @@ github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJ
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/expr-lang/expr v1.17.6 h1:1h6i8ONk9cexhDmowO/A64VPxHScu7qfSl2k8OlINec=
github.com/expr-lang/expr v1.17.6/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
@@ -88,6 +91,8 @@ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
@@ -110,8 +115,9 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -127,8 +133,6 @@ github.com/gosnmp/gosnmp v1.38.0 h1:I5ZOMR8kb0DXAFg/88ACurnuwGwYkXWq3eLpJPHMEYc=
github.com/gosnmp/gosnmp v1.38.0/go.mod h1:FE+PEZvKrFz9afP9ii1W3cprXuVZ17ypCcyyfYuu5LY=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
@@ -185,8 +189,6 @@ github.com/linxGnu/gosmpp v0.3.0/go.mod h1:Ba6SULQql3IbF2A5Mtj3DqMKoFbx1pEz/8xyi
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI=
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/matoous/go-nanoid/v2 v2.1.0 h1:P64+dmq21hhWdtvZfEAofnvJULaRR1Yib0+PnU669bE=
@@ -199,8 +201,6 @@ github.com/metaleap/go-util v0.0.0-20180330192724-a09253046f73 h1:4vKVhAdype/dej
github.com/metaleap/go-util v0.0.0-20180330192724-a09253046f73/go.mod h1:l71/5fppWP5A6nqhcxz6wQAYok6pr/vM2+KHIy50/LY=
github.com/metaleap/go-xsd v0.0.0-20180330193350-61f7638f502f h1:eeJGcYszuvOpmuJxeq57LaOO8mJurfjpOHJJMfQSD0s=
github.com/metaleap/go-xsd v0.0.0-20180330193350-61f7638f502f/go.mod h1:WK3zEKtwVd/v+NM3lh1ZE6MdDfHsdOFFOD5Ezi4Hutg=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -231,15 +231,14 @@ github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/
github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
@@ -268,14 +267,12 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ=
github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shirou/gopsutil/v4 v4.24.12 h1:qvePBOk20e0IKA1QXrIIU+jmk+zEiYVVx06WjBRlZo4=
github.com/shirou/gopsutil/v4 v4.24.12/go.mod h1:DCtMPAad2XceTeIAbGyVfycbYQNBGk2P8cvDi7/VN9o=
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -287,19 +284,18 @@ github.com/slayercat/GoSNMPServer v0.5.2 h1:IK2d3kz6JoiYHbAZT5H7hrQQRzAD7rxF0iJZ
github.com/slayercat/GoSNMPServer v0.5.2/go.mod h1:6taMSIwudR+7pKRO6dz2U+xoNccZds8eiMVlEN66fXY=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -308,7 +304,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
@@ -329,6 +324,8 @@ github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYN
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
github.com/tsmask/go-oam v1.0.9 h1:6g3rpMwSu0x8mzvq4QdMZgbABvMo3ek2D0x18Ecm7uU=
github.com/tsmask/go-oam v1.0.9/go.mod h1:HqFtN0LA9BiR1HWyHO++DY0fyYTl2sKVnRrZzuWy9n4=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
@@ -417,8 +414,8 @@ golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -478,8 +475,9 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -503,8 +501,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -513,8 +511,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -28,7 +28,7 @@ import (
var NewIMS = &IMSController{
neInfoService: neService.NewNeInfo,
cdrEventService: neDataService.NewCDREventIMS,
kpiReportService: neDataService.NewPerfKPI,
kpiReportService: neDataService.NewKpiReport,
}
// 网元IMS
@@ -37,7 +37,7 @@ var NewIMS = &IMSController{
type IMSController struct {
neInfoService *neService.NeInfo // 网元信息服务
cdrEventService *neDataService.CDREventIMS // CDR会话事件服务
kpiReportService *neDataService.PerfKPI // 统计信息服务
kpiReportService *neDataService.KpiReport // 统计信息服务
}
// CDR会话列表

View File

@@ -12,15 +12,15 @@ import (
// 实例化控制层 UPFController 结构体
var NewUPF = &UPFController{
neInfoService: neService.NewNeInfo,
perfKPIService: neDataService.NewPerfKPI,
perfKPIService: neDataService.NewKpiReport,
}
// 网元UPF
//
// PATH /upf
type UPFController struct {
neInfoService *neService.NeInfo // 网元信息服务
perfKPIService *neDataService.PerfKPI // 统计信息服务
neInfoService *neService.NeInfo // 网元信息服务
perfKPIService *neDataService.KpiReport // 统计信息服务
}
// 总流量数 N3上行 N6下行

View File

@@ -0,0 +1,46 @@
package model
import "time"
// AlarmEvent 告警_事件记录表
type AlarmEvent struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"` // 网元类型
NeId string `json:"neId" gorm:"column:ne_id"` // 网元ID
AlarmSeq string `json:"alarmSeq" gorm:"column:alarm_seq"` // 告警序号 同网元类型连续递增
AlarmId string `json:"alarmId" gorm:"column:alarm_id"` // 告警ID
AlarmTitle string `json:"alarmTitle" gorm:"column:alarm_title"` // 告警标题
AlarmCode string `json:"alarmCode" gorm:"column:alarm_code"` // 告警状态码
EventTime time.Time `json:"eventTime" gorm:"column:event_time"` // 事件产生时间
ObjectUid string `json:"objectUid" gorm:"column:object_uid"` // 对象ID
ObjectName string `json:"objectName" gorm:"column:object_name"` // 对象名称
ObjectType string `json:"objectType" gorm:"column:object_type"` // 对象类型
LocationInfo string `json:"locationInfo" gorm:"column:location_info"` // 告警定位信息
AlarmStatus string `json:"alarmStatus" gorm:"column:alarm_status"` // 告警状态
SpecificProblem string `json:"specificProblem" gorm:"column:specific_problem"` // 告警问题原因
SpecificProblemId string `json:"specificProblemId" gorm:"column:specific_problem_id"` // 告警问题原因ID
AddInfo string `json:"addInfo" gorm:"column:add_info"` // 告警辅助信息
ClearType string `json:"clearType" gorm:"column:clear_type"` // 清除状态
ClearTime time.Time `json:"clearTime" gorm:"column:clear_time"` // 清除时间
ClearUser string `json:"clearUser" gorm:"column:clear_user"` // 清除用户
Timestamp time.Time `json:"timestamp" gorm:"column:timestamp"` // 创建时间
}
// TableName 表名称
func (*AlarmEvent) TableName() string {
return "alarm_event"
}
// AlarmEventQuery 告警事件数据查询参数结构体
type AlarmEventQuery struct {
NeType string `json:"neType" form:"neType"` // 网元类型
NeID string `json:"neId" form:"neId"` // 网元ID
AlarmCode string `json:"alarmCode" form:"alarmCode"`
AlarmStatus string `json:"alarmStatus" form:"alarmStatus" binding:"omitempty,oneof=Clear Active"` // 告警状态
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查event_time
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=event_time id"` // 排序字段,填写结果字段
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"`
}

View File

@@ -0,0 +1,39 @@
package model
import "time"
// AlarmForwardLog 告警_转发日志记录
type AlarmForwardLog struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeId string `json:"neId" gorm:"column:ne_id"`
AlarmSeq string `json:"alarmSeq" gorm:"column:alarm_seq"` // 告警序号 连续递增
AlarmId string `json:"alarmId" gorm:"column:alarm_id"` // 告警ID
AlarmCode string `json:"alarmCode" gorm:"column:alarm_code"` // 告警状态码
AlarmTitle string `json:"alarmTitle" gorm:"column:alarm_title"` // 告警标题
AlarmStatus string `json:"alarmStatus" gorm:"column:alarm_status"` // 告警状态
AlarmType string `json:"alarmType" gorm:"column:alarm_type"` // 告警类型
OrigSeverity string `json:"origSeverity" gorm:"column:orig_severity"` // 严重程度
EventTime time.Time `json:"eventTime" gorm:"column:event_time"` // 事件产生时间
CreatedAt time.Time `json:"createdAt" gorm:"column:created_at"` // 创建时间
Type string `json:"type" gorm:"column:type"` // 转发方式 SMS/EMAIL/SMSC
Target string `json:"target" gorm:"column:target"` // 发送目标用户
Result string `json:"result" gorm:"column:result"` // 发送结果
}
// TableName 表名称
func (*AlarmForwardLog) TableName() string {
return "alarm_forward_log"
}
// AlarmForwardLogQuery 告警转发日志数据查询参数结构体
type AlarmForwardLogQuery struct {
NeType string `json:"neType" form:"neType"` // 网元类型
NeID string `json:"neId" form:"neId"` // 网元ID
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=event_time id"` // 排序字段,填写结果字段
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"`
}

View File

@@ -0,0 +1,38 @@
package model
import "time"
// AlarmLog 告警_日志记录
type AlarmLog struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeId string `json:"neId" gorm:"column:ne_id"`
AlarmSeq string `json:"alarmSeq" gorm:"column:alarm_seq"` // 告警序号 连续递增
AlarmId string `json:"alarmId" gorm:"column:alarm_id"` // 告警ID
AlarmCode string `json:"alarmCode" gorm:"column:alarm_code"` // 告警状态码
AlarmTitle string `json:"alarmTitle" gorm:"column:alarm_title"` // 告警标题
AlarmStatus string `json:"alarmStatus" gorm:"column:alarm_status"` // 告警状态
AlarmType string `json:"alarmType" gorm:"column:alarm_type"` // 告警类型
OrigSeverity string `json:"origSeverity" gorm:"column:orig_severity"` // 严重程度
EventTime time.Time `json:"eventTime" gorm:"column:event_time"` // 事件产生时间
CreatedAt time.Time `json:"createdAt" gorm:"column:created_at"` // 创建时间
}
// TableName 表名称
func (*AlarmLog) TableName() string {
return "alarm_log"
}
// AlarmLogQuery 告警日志数据查询参数结构体
type AlarmLogQuery struct {
NeType string `json:"neType" form:"neType"` // 网元类型
NeID string `json:"neId" form:"neId"` // 网元ID
AlarmStatus string `json:"alarmStatus" form:"alarmStatus" binding:"omitempty,oneof=Clear Active"` // 告警状态
OrigSeverity string `json:"origSeverity" form:"origSeverity"` // 告警类型
BeginTime int64 `json:"beginTime" form:"beginTime"` // 开始时间 查event_time
EndTime int64 `json:"endTime" form:"endTime"`
SortField string `json:"sortField" form:"sortField" binding:"omitempty,oneof=event_time id"` // 排序字段,填写结果字段
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"`
}

View File

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

View File

@@ -0,0 +1,17 @@
package model
// CDREvent CDR会话对象 cdr_event
type CDREvent struct {
ID int64 `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"` // 接收到的timestamp秒级存储毫秒时间戳
CdrJson string `json:"cdrJSON" gorm:"column:cdr_json"` // data JSON String
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 记录创建存储毫秒
}
// TableName 表名称
func (*CDREvent) TableName() string {
return "cdr_event"
}

View File

@@ -1,18 +1,18 @@
package model
// GoldKPITitle 黄金指标标题信息对象 kpi_title
type GoldKPITitle struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
KPIID string `json:"kpiId" gorm:"column:kpi_id"`
// KpiTitle 指标标题信息对象 kpi_title
type KpiTitle struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"` // 网元类型
KpiId string `json:"kpiId" gorm:"column:kpi_id"` // KPI标识
TitleJson string `json:"titleJson" gorm:"column:title_json"`
CnTitle string `json:"cnTitle" gorm:"column:cn_title"`
EnTitle string `json:"enTitle" gorm:"column:en_title"`
StatusFlag string `json:"statusFlag" gorm:"column:status_flag"`
CnTitle string `json:"cnTitle" gorm:"column:cn_title"` // 中文名
EnTitle string `json:"enTitle" gorm:"column:en_title"` // 英文名
StatusFlag string `json:"statusFlag" gorm:"column:status_flag"` // 状态标识 1:启用 0:禁用
}
// TableName 表名称
func (*GoldKPITitle) TableName() string {
func (*KpiTitle) TableName() string {
return "kpi_title"
}
@@ -36,8 +36,8 @@ func (*KpiReport) TableName() string {
return "kpi_report"
}
// GoldKPIQuery 黄金指标查询参数结构体
type GoldKPIQuery struct {
// KPIQuery 指标查询参数结构体
type KPIQuery struct {
NeType string `form:"neType" binding:"required"`
//NeID string `form:"neId" binding:"required"`
NeID string `form:"neId"`

View File

@@ -0,0 +1,19 @@
package model
// UEEvent UE会话对象 ue_event
type UEEvent struct {
ID int64 `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"` // 接收到时间
EventType string `json:"eventType" gorm:"column:event_type"` // 事件类型
EventJSONStr string `json:"eventJSON" gorm:"column:event_json"` // data JSON String
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 记录创建存储毫秒
TenantID string `json:"tenantID" gorm:"column:tenant_id"` // 租户ID
}
// TableName 表名称
func (*UEEvent) TableName() string {
return "ue_event"
}

View File

@@ -388,5 +388,5 @@ func Setup(router *gin.Engine) {
// InitLoad 初始参数
func InitLoad() {
// 启动时加载UPF上下行流量
go service.NewPerfKPI.UPFTodayFlowLoad(30)
go service.NewKpiReport.UPFTodayFlowLoad(30)
}

View File

@@ -0,0 +1,169 @@
package repository
import (
"time"
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 AlarmEvent 结构体
var NewAlarmEvent = &AlarmEvent{}
// AlarmEvent 告警 数据层处理
type AlarmEvent struct{}
// SelectByPage 分页查询集合
func (r AlarmEvent) SelectByPage(query model.AlarmEventQuery) ([]model.AlarmEvent, int64) {
tx := db.DB("").Model(&model.AlarmEvent{})
// 查询条件拼接
if query.NeType != "" {
tx = tx.Where("ne_type = ?", query.NeType)
}
if query.NeID != "" {
tx = tx.Where("ne_id = ?", query.NeID)
}
if query.AlarmCode != "" {
tx = tx.Where("alarm_code = ?", query.AlarmCode)
}
if query.AlarmStatus != "" {
tx = tx.Where("alarm_status = ?", query.AlarmStatus)
}
if query.BeginTime != 0 {
tx = tx.Where("event_time >= ?", query.BeginTime)
}
if query.EndTime != 0 {
tx = tx.Where("event_time <= ?", query.EndTime)
}
// 查询结果
var total int64 = 0
rows := []model.AlarmEvent{}
// 查询数量为0直接返回
if err := tx.Count(&total).Error; err != nil || total <= 0 {
return rows, total
}
// 排序
if query.SortField != "" {
sortField := query.SortField
if query.SortOrder == "desc" {
sortField = sortField + " desc"
}
tx = tx.Order(sortField)
}
// 查询数据分页
pageNum, pageSize := db.PageNumSize(query.PageNum, query.PageSize)
tx = tx.Limit(pageSize).Offset(pageSize * pageNum)
err := tx.Find(&rows).Error
if err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows, total
}
return rows, total
}
// Select 查询集合
func (r AlarmEvent) Select(param model.AlarmEvent) []model.AlarmEvent {
tx := db.DB("").Model(&model.AlarmEvent{})
// 查询条件拼接
if param.NeType != "" {
tx = tx.Where("ne_type = ?", param.NeType)
}
if param.NeId != "" {
tx = tx.Where("ne_id = ?", param.NeId)
}
if param.AlarmId != "" {
tx = tx.Where("alarm_id = ?", param.AlarmId)
}
if param.AlarmCode != "" {
tx = tx.Where("alarm_code = ?", param.AlarmCode)
}
if param.AlarmStatus != "" {
tx = tx.Where("alarm_status = ?", param.AlarmStatus)
}
// 查询数据
rows := []model.AlarmEvent{}
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// SelectByIds 通过ID查询
func (r AlarmEvent) SelectByIds(ids []int64) []model.AlarmEvent {
rows := []model.AlarmEvent{}
if len(ids) <= 0 {
return rows
}
tx := db.DB("").Model(&model.AlarmEvent{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// Insert 新增信息 返回新增数据ID
func (r AlarmEvent) Insert(param model.AlarmEvent) int64 {
if param.Timestamp.IsZero() {
param.Timestamp = time.Now()
}
// 执行插入
if err := db.DB("").Create(&param).Error; err != nil {
logger.Errorf("insert err => %v", err.Error())
return 0
}
return param.ID
}
// Update 修改信息 返回受影响的行数
func (r AlarmEvent) Update(param model.AlarmEvent) int64 {
if param.ID <= 0 {
return 0
}
tx := db.DB("").Model(&model.AlarmEvent{})
// 构建查询条件
tx = tx.Where("id = ?", param.ID)
tx = tx.Omit("id", "timestamp")
// 执行更新
if err := tx.Updates(param).Error; err != nil {
logger.Errorf("update err => %v", err.Error())
return 0
}
return tx.RowsAffected
}
// DeleteByIds 批量删除信息
func (r AlarmEvent) DeleteByIds(ids []int64) int64 {
if len(ids) <= 0 {
return 0
}
tx := db.DB("").Where("id in ?", ids)
if err := tx.Delete(&model.AlarmEvent{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}
// SelectAlarmEventSeqLast 查询网元告警最后一条序号
func (r AlarmEvent) SelectAlarmEventSeqLast(neType, neId string) int64 {
tx := db.DB("").Model(&model.AlarmEvent{})
tx = tx.Where("ne_type=? and ne_id=?", neType, neId)
tx = tx.Select("alarm_seq").Order("alarm_seq DESC")
// 查询数据
var AlarmEventSeq int64 = 0
if err := tx.Limit(1).Find(&AlarmEventSeq).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return AlarmEventSeq
}
return AlarmEventSeq
}

View File

@@ -0,0 +1,104 @@
package repository
import (
"time"
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 AlarmForwardLog 结构体
var NewAlarmForwardLog = &AlarmForwardLog{}
// AlarmForwardLog 告警转发记录表 数据层处理
type AlarmForwardLog struct{}
// SelectByPage 分页查询集合
func (r AlarmForwardLog) SelectByPage(query model.AlarmForwardLogQuery) ([]model.AlarmForwardLog, int64) {
tx := db.DB("").Model(&model.AlarmForwardLog{})
// 查询条件拼接
if query.NeType != "" {
tx = tx.Where("ne_type = ?", query.NeType)
}
if query.NeID != "" {
tx = tx.Where("ne_id = ?", query.NeID)
}
if query.BeginTime != 0 {
tx = tx.Where("created_at >= ?", query.BeginTime)
}
if query.EndTime != 0 {
tx = tx.Where("created_at <= ?", query.EndTime)
}
// 查询结果
var total int64 = 0
rows := []model.AlarmForwardLog{}
// 查询数量为0直接返回
if err := tx.Count(&total).Error; err != nil || total <= 0 {
return rows, total
}
// 排序
if query.SortField != "" {
sortField := query.SortField
if query.SortOrder == "desc" {
sortField = sortField + " desc"
}
tx = tx.Order(sortField)
}
// 查询数据分页
pageNum, pageSize := db.PageNumSize(query.PageNum, query.PageSize)
tx = tx.Limit(pageSize).Offset(pageSize * pageNum)
err := tx.Find(&rows).Error
if err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows, total
}
return rows, total
}
// SelectByIds 通过ID查询
func (r AlarmForwardLog) SelectByIds(ids []int64) []model.AlarmForwardLog {
rows := []model.AlarmForwardLog{}
if len(ids) <= 0 {
return rows
}
tx := db.DB("").Model(&model.AlarmForwardLog{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// DeleteByIds 批量删除信息
func (r AlarmForwardLog) DeleteByIds(ids []int64) int64 {
if len(ids) <= 0 {
return 0
}
tx := db.DB("").Where("id in ?", ids)
if err := tx.Delete(&model.AlarmForwardLog{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}
// Insert 新增信息
func (r AlarmForwardLog) Insert(param model.AlarmForwardLog) int64 {
if param.CreatedAt.IsZero() {
param.CreatedAt = time.Now()
}
// 执行插入
if err := db.DB("").Create(&param).Error; err != nil {
logger.Errorf("insert err => %v", err.Error())
return 0
}
return param.ID
}

View File

@@ -0,0 +1,107 @@
package repository
import (
"time"
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 AlarmLog 结构体
var NewAlarmLog = &AlarmLog{}
// AlarmLog 基站状态记录表 数据层处理
type AlarmLog struct{}
// SelectByPage 分页查询集合
func (r AlarmLog) SelectByPage(query model.AlarmLogQuery) ([]model.AlarmLog, int64) {
tx := db.DB("").Model(&model.AlarmLog{})
// 查询条件拼接
if query.NeType != "" {
tx = tx.Where("ne_type = ?", query.NeType)
}
if query.NeID != "" {
tx = tx.Where("ne_id = ?", query.NeID)
}
if query.AlarmStatus != "" {
tx = tx.Where("alarm_status = ?", query.AlarmStatus)
}
if query.BeginTime != 0 {
tx = tx.Where("created_at >= ?", query.BeginTime)
}
if query.EndTime != 0 {
tx = tx.Where("created_at <= ?", query.EndTime)
}
// 查询结果
var total int64 = 0
rows := []model.AlarmLog{}
// 查询数量为0直接返回
if err := tx.Count(&total).Error; err != nil || total <= 0 {
return rows, total
}
// 排序
if query.SortField != "" {
sortField := query.SortField
if query.SortOrder == "desc" {
sortField = sortField + " desc"
}
tx = tx.Order(sortField)
}
// 查询数据分页
pageNum, pageSize := db.PageNumSize(query.PageNum, query.PageSize)
tx = tx.Limit(pageSize).Offset(pageSize * pageNum)
err := tx.Find(&rows).Error
if err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows, total
}
return rows, total
}
// SelectByIds 通过ID查询
func (r AlarmLog) SelectByIds(ids []int64) []model.AlarmLog {
rows := []model.AlarmLog{}
if len(ids) <= 0 {
return rows
}
tx := db.DB("").Model(&model.AlarmLog{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// DeleteByIds 批量删除信息
func (r AlarmLog) DeleteByIds(ids []int64) int64 {
if len(ids) <= 0 {
return 0
}
tx := db.DB("").Where("id in ?", ids)
if err := tx.Delete(&model.AlarmLog{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}
// Insert 新增信息
func (r AlarmLog) Insert(param model.AlarmLog) int64 {
if param.CreatedAt.IsZero() {
param.CreatedAt = time.Now()
}
// 执行插入
if err := db.DB("").Create(&param).Error; err != nil {
logger.Errorf("insert err => %v", err.Error())
return 0
}
return param.ID
}

View File

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

View File

@@ -0,0 +1,205 @@
package repository
import (
"fmt"
"sort"
"strings"
"time"
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 CDREvent 结构体
var NewCDREvent = &CDREvent{}
// CDREvent CDR会话事件 数据层处理
type CDREvent struct{}
// SelectByPage 分页查询集合
func (r CDREvent) SelectByPage(neType string, query map[string]string) ([]model.CDREvent, int64) {
// 表名
tableName := fmt.Sprintf("cdr_event_%s", strings.ToLower(neType))
tx := db.DB("").Table(tableName).Model(&model.CDREvent{})
// 查询条件拼接
if v, ok := query["rmUID"]; ok && v != "" {
tx = tx.Where("rm_uid = ?", v)
}
if v, ok := query["beginTime"]; ok && v != "" {
if len(v) == 10 {
v = v + "000"
}
tx = tx.Where("created_at >= ?", v)
}
if v, ok := query["endTime"]; ok && v != "" {
if len(v) == 10 {
v = v + "000"
}
tx = tx.Where("created_at <= ?", v)
}
// 各个网元特殊查询条件
switch neType {
case "SMSC":
if v, ok := query["callerParty"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.callerParty') like ?", fmt.Sprintf("%%%s%%", v))
}
if v, ok := query["calledParty"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.calledParty') like ?", fmt.Sprintf("%%%s%%", v))
}
if v, ok := query["recordType"]; ok && v != "" {
recordTypes := strings.Split(v, ",")
var querytrArr []string
for _, recordType := range recordTypes {
querytrArr = append(querytrArr, fmt.Sprintf("JSON_EXTRACT(cdr_json, '$.recordType') = '%s'", recordType))
}
tx = tx.Where(fmt.Sprintf("( %s )", strings.Join(querytrArr, " OR ")))
}
case "SMF":
if v, ok := query["recordType"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.recordType') = ?", v)
}
if v, ok := query["subscriberID"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.subscriberIdentifier.subscriptionIDData') like ?", fmt.Sprintf("%%%s%%", v))
}
if v, ok := query["dnn"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.pDUSessionChargingInformation.dNNID') = ?", v)
}
case "SGWC":
if v, ok := query["imsi"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.servedIMSI') like ?", fmt.Sprintf("%%%s%%", v))
}
if v, ok := query["msisdn"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.servedMSISDN') like ?", fmt.Sprintf("%%%s%%", v))
}
case "IMS":
if v, ok := query["callerParty"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.callerParty') like ?", fmt.Sprintf("%%%s%%", v))
}
if v, ok := query["calledParty"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(cdr_json, '$.calledParty') like ?", fmt.Sprintf("%%%s%%", v))
}
}
var total int64 = 0
rows := []model.CDREvent{}
// 查询数量 长度为0直接返回
if err := tx.Count(&total).Error; err != nil || total <= 0 {
return rows, total
}
// 分页
if query["pageNum"] != "" && query["pageSize"] != "" {
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
tx = tx.Offset(int(pageNum * pageSize)).Limit(int(pageSize))
}
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query err => %v", err)
}
// 排序
if v, ok := query["sortField"]; ok && v != "" {
sortField := v
sortOrder := "asc"
if o, ok := query["sortOrder"]; ok && o != "" {
if o == "desc" {
sortOrder = "desc"
} else {
sortOrder = "asc"
}
}
sort.SliceStable(rows, func(i, j int) bool {
// 支持的排序字段映射
fieldGetters := map[string]func(*model.CDREvent) any{
"id": func(row *model.CDREvent) any { return row.ID },
"timestamp": func(row *model.CDREvent) any { return row.Timestamp },
// 可添加更多支持的字段
}
// 获取字段 getter 函数
getter, ok := fieldGetters[sortField]
if !ok {
// 非法字段使用默认排序id升序
return rows[i].ID < rows[j].ID
}
// 获取比较值
valI, valJ := getter(&rows[i]), getter(&rows[j])
// 根据字段类型进行比较
switch v := valI.(type) {
case int64:
if sortOrder == "desc" {
return v > valJ.(int64)
}
return v < valJ.(int64)
case string:
if sortOrder == "desc" {
return v > valJ.(string)
}
return v < valJ.(string)
default:
// 不支持的字段类型,使用默认排序
return rows[i].ID < rows[j].ID
}
})
}
return rows, total
}
// SelectByIds 通过ID查询
func (r CDREvent) SelectByIds(neType string, ids []int64) []model.CDREvent {
rows := []model.CDREvent{}
if len(ids) <= 0 {
return rows
}
// 表名
tableName := fmt.Sprintf("cdr_event_%s", strings.ToLower(neType))
tx := db.DB("").Table(tableName).Model(&model.CDREvent{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// DeleteByIds 批量删除信息
func (r CDREvent) DeleteByIds(neType string, ids []int64) int64 {
if len(ids) <= 0 {
return 0
}
// 表名
tableName := fmt.Sprintf("cdr_event_%s", strings.ToLower(neType))
tx := db.DB("").Table(tableName).Where("id in ?", ids)
if err := tx.Delete(&model.CDREvent{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}
// Insert 新增信息 返回新增数据ID
func (r CDREvent) Insert(param model.CDREvent) int64 {
if param.NeType == "" {
return 0
}
if param.CreatedAt == 0 {
param.CreatedAt = time.Now().UnixMilli()
}
// 表名
tableName := fmt.Sprintf("cdr_event_%s", strings.ToLower(param.NeType))
// 执行插入
if err := db.DB("").Table(tableName).Create(&param).Error; err != nil {
logger.Errorf("insert err => %v", err.Error())
return 0
}
return param.ID
}

View File

@@ -4,21 +4,23 @@ import (
"fmt"
"sort"
"strings"
"time"
"be.ems/lib/log"
"be.ems/src/framework/database/db"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 PerfKPI 结构体
var NewPerfKPI = &PerfKPI{}
var NewKpiReport = &KpiReport{}
// PerfKPI 性能统计 数据层处理
type PerfKPI struct{}
// KpiReport 性能统计 数据层处理
type KpiReport struct{}
// SelectGoldKPI 通过网元指标数据信息
func (r *PerfKPI) SelectGoldKPI(query model.GoldKPIQuery, kpiIds []string) []map[string]any {
func (r *KpiReport) SelectGoldKPI(query model.KPIQuery, kpiIds []string) []map[string]any {
// 查询条件拼接
var conditions []string
var params []any
@@ -93,7 +95,7 @@ func (r *PerfKPI) SelectGoldKPI(query model.GoldKPIQuery, kpiIds []string) []map
}
// SelectGoldKPI 通过网元指标数据信息
func (r PerfKPI) SelectKPI(query model.GoldKPIQuery) []model.KpiReport {
func (r *KpiReport) SelectKPI(query model.KPIQuery) []model.KpiReport {
rows := []model.KpiReport{}
if query.NeType == "" {
return rows
@@ -164,8 +166,8 @@ func (r PerfKPI) SelectKPI(query model.GoldKPIQuery) []model.KpiReport {
}
// SelectGoldKPITitle 网元对应的指标名称
func (r *PerfKPI) SelectGoldKPITitle(neType string) []model.GoldKPITitle {
result := []model.GoldKPITitle{}
func (r *KpiReport) SelectGoldKPITitle(neType string) []model.KpiTitle {
result := []model.KpiTitle{}
tx := datasource.DefaultDB().Table("kpi_title").Where("ne_type = ? and status_flag = '1'", neType).Find(&result)
if err := tx.Error; err != nil {
logger.Errorf("Find err => %v", err)
@@ -173,9 +175,27 @@ func (r *PerfKPI) SelectGoldKPITitle(neType string) []model.GoldKPITitle {
return result
}
// Insert 新增信息 返回新增数据ID
func (r KpiReport) Insert(param model.KpiReport) int64 {
if param.NeType == "" {
return 0
}
if param.CreatedAt == 0 {
param.CreatedAt = time.Now().UnixMilli()
}
// 表名
tableName := fmt.Sprintf("kpi_report_%s", strings.ToLower(param.NeType))
// 执行插入
if err := db.DB("").Table(tableName).Create(&param).Error; err != nil {
logger.Errorf("insert err => %v", err.Error())
return 0
}
return param.ID
}
// SelectByPageTitle 分页查询集合
func (r PerfKPI) TitleSelectByPage(query map[string]string) ([]model.GoldKPITitle, int64) {
tx := datasource.DB("").Model(&model.GoldKPITitle{})
func (r KpiReport) TitleSelectByPage(query map[string]string) ([]model.KpiTitle, int64) {
tx := datasource.DB("").Model(&model.KpiTitle{})
// 查询条件拼接
if v, ok := query["neType"]; ok && v != "" {
tx = tx.Where("ne_type = ?", v)
@@ -189,7 +209,7 @@ func (r PerfKPI) TitleSelectByPage(query map[string]string) ([]model.GoldKPITitl
// 查询结果
var total int64 = 0
rows := []model.GoldKPITitle{}
rows := []model.KpiTitle{}
// 查询数量 长度为0直接返回
if err := tx.Count(&total).Error; err != nil || total <= 0 {
@@ -222,23 +242,23 @@ func (r PerfKPI) TitleSelectByPage(query map[string]string) ([]model.GoldKPITitl
}
// TitleSelect 网元对应的指标名称
func (r PerfKPI) TitleSelect(param model.GoldKPITitle) []model.GoldKPITitle {
tx := datasource.DB("").Model(&model.GoldKPITitle{})
func (r KpiReport) TitleSelect(param model.KpiTitle) []model.KpiTitle {
tx := datasource.DB("").Model(&model.KpiTitle{})
// 构建查询条件
if param.ID != "" {
if param.ID != 0 {
tx = tx.Where("id =?", param.ID)
}
if param.NeType != "" {
tx = tx.Where("ne_type =?", param.NeType)
}
if param.KPIID != "" {
tx = tx.Where("kpi_id =?", param.KPIID)
if param.KpiId != "" {
tx = tx.Where("kpi_id =?", param.KpiId)
}
if param.StatusFlag != "" {
tx = tx.Where("status_flag = ?", param.StatusFlag)
}
// 查询数据
rows := []model.GoldKPITitle{}
rows := []model.KpiTitle{}
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
@@ -247,11 +267,11 @@ func (r PerfKPI) TitleSelect(param model.GoldKPITitle) []model.GoldKPITitle {
}
// TitleUpdate 修改信息
func (r PerfKPI) TitleUpdate(param model.GoldKPITitle) int64 {
if param.ID == "" {
func (r KpiReport) TitleUpdate(param model.KpiTitle) int64 {
if param.ID == 0 {
return 0
}
tx := datasource.DB("").Model(&model.GoldKPITitle{})
tx := datasource.DB("").Model(&model.KpiTitle{})
// 构建查询条件
tx = tx.Where("id = ?", param.ID)
tx = tx.Omit("id", "kpi_id", "title_json")
@@ -264,7 +284,7 @@ func (r PerfKPI) TitleUpdate(param model.GoldKPITitle) int64 {
}
// SelectUPFTotalFlow 查询UPF总流量 N3上行 N6下行
func (r *PerfKPI) SelectUPFTotalFlow(neType, rmUID, startDate, endDate string) map[string]any {
func (r *KpiReport) SelectUPFTotalFlow(neType, rmUID, startDate, endDate string) map[string]any {
// 查询条件拼接
var conditions []string
var params []any
@@ -303,7 +323,7 @@ func (r *PerfKPI) SelectUPFTotalFlow(neType, rmUID, startDate, endDate string) m
}
// SelectIMSBusyHour 查询IMS忙时流量 SCSCF.06呼叫尝试次数 SCSCF.09呼叫成功次数
func (r *PerfKPI) SelectIMSBusyHour(rmUID string, startDate, endDate int64) []map[string]any {
func (r *KpiReport) SelectIMSBusyHour(rmUID string, startDate, endDate int64) []map[string]any {
// 查询条件拼接
var conditions []string
var params []any

View File

@@ -16,7 +16,8 @@ var NewSysTenant = &SysTenant{}
type SysTenant struct{}
// Query 查询租户信息 ID 名称
// radioKey 5G_2 4G_3
// radioKey 5G_2/4G_3
// imsi 123456789012345
func (r SysTenant) Query(query map[string]string) (int64, string) {
// 查询条件拼接
var conditions []string
@@ -25,6 +26,10 @@ func (r SysTenant) Query(query map[string]string) (int64, string) {
conditions = append(conditions, "st2.tenancy_type = 'RADIO' AND st2.tenancy_key = ?")
params = append(params, v)
}
if v, ok := query["imsi"]; ok && v != "" {
conditions = append(conditions, "st2.tenancy_type = 'IMSI' AND st2.tenancy_key like ?")
params = append(params, fmt.Sprintf("%%%s%%", v))
}
// 构建查询条件语句
whereSql := ""

View File

@@ -0,0 +1,170 @@
package repository
import (
"fmt"
"sort"
"strings"
"time"
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 UEEvent 结构体
var NewUEEvent = &UEEvent{}
// UEEvent UE会话事件 数据层处理
type UEEvent struct{}
// SelectByPage 分页查询集合
func (r UEEvent) SelectByPage(neType string, query map[string]string) ([]model.UEEvent, int64) {
// 表名
tableName := fmt.Sprintf("ue_event_%s", strings.ToLower(neType))
tx := db.DB("").Table(tableName).Model(&model.UEEvent{})
// 查询条件拼接
if v, ok := query["rmUID"]; ok && v != "" {
tx = tx.Where("rm_uid = ?", v)
}
if v, ok := query["beginTime"]; ok && v != "" {
if len(v) == 10 {
v = v + "000"
}
tx = tx.Where("created_at >= ?", v)
}
if v, ok := query["endTime"]; ok && v != "" {
if len(v) == 10 {
v = v + "000"
}
tx = tx.Where("created_at <= ?", v)
}
if v, ok := query["eventType"]; ok && v != "" {
eventTypes := strings.Split(v, ",")
tx = tx.Where("event_type in ?", eventTypes)
}
if v, ok := query["imsi"]; ok && v != "" {
tx = tx.Where("JSON_EXTRACT(event_json, '$.imsi') like ?", fmt.Sprintf("%%%s%%", v))
}
// 查询结果
var total int64 = 0
rows := []model.UEEvent{}
// 查询数量 长度为0直接返回
if err := tx.Count(&total).Error; err != nil || total <= 0 {
return rows, total
}
// 分页
if query["pageNum"] != "" && query["pageSize"] != "" {
pageNum, pageSize := db.PageNumSize(query["pageNum"], query["pageSize"])
tx = tx.Offset(int(pageNum * pageSize)).Limit(int(pageSize))
}
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query err => %v", err)
}
// 排序
if v, ok := query["sortField"]; ok && v != "" {
sortField := v
sortOrder := "asc"
if o, ok := query["sortOrder"]; ok && o != "" {
if o == "desc" {
sortOrder = "desc"
} else {
sortOrder = "asc"
}
}
sort.SliceStable(rows, func(i, j int) bool {
// 支持的排序字段映射
fieldGetters := map[string]func(*model.UEEvent) any{
"id": func(row *model.UEEvent) any { return row.ID },
"timestamp": func(row *model.UEEvent) any { return row.CreatedAt },
"createdAt": func(row *model.UEEvent) any { return row.CreatedAt },
// 可添加更多支持的字段
}
// 获取字段 getter 函数
getter, ok := fieldGetters[sortField]
if !ok {
// 非法字段使用默认排序id升序
return rows[i].ID < rows[j].ID
}
// 获取比较值
valI, valJ := getter(&rows[i]), getter(&rows[j])
// 根据字段类型进行比较
switch v := valI.(type) {
case int64:
if sortOrder == "desc" {
return v > valJ.(int64)
}
return v < valJ.(int64)
case string:
if sortOrder == "desc" {
return v > valJ.(string)
}
return v < valJ.(string)
default:
// 不支持的字段类型,使用默认排序
return rows[i].ID < rows[j].ID
}
})
}
return rows, total
}
// SelectByIds 通过ID查询
func (r UEEvent) SelectByIds(neType string, ids []int64) []model.UEEvent {
rows := []model.UEEvent{}
if len(ids) <= 0 {
return rows
}
// 表名
tableName := fmt.Sprintf("ue_event_%s", strings.ToLower(neType))
tx := db.DB("").Table(tableName).Model(&model.UEEvent{})
// 构建查询条件
tx = tx.Where("id in ?", ids)
// 查询数据
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return rows
}
return rows
}
// DeleteByIds 批量删除信息
func (r UEEvent) DeleteByIds(neType string, ids []int64) int64 {
if len(ids) <= 0 {
return 0
}
// 表名
tableName := fmt.Sprintf("ue_event_%s", strings.ToLower(neType))
tx := db.DB("").Table(tableName).Where("id in ?", ids)
if err := tx.Delete(&model.UEEvent{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
}
return tx.RowsAffected
}
// Insert 新增信息 返回新增数据ID
func (r UEEvent) Insert(param model.UEEvent) int64 {
if param.NeType == "" {
return 0
}
if param.CreatedAt == 0 {
param.CreatedAt = time.Now().UnixMilli()
}
// 表名
tableName := fmt.Sprintf("ue_event_%s", strings.ToLower(param.NeType))
// 执行插入
if err := db.DB("").Table(tableName).Create(&param).Error; err != nil {
logger.Errorf("insert err => %v", err.Error())
return 0
}
return param.ID
}

View File

@@ -0,0 +1,85 @@
package service
import (
"fmt"
"time"
"github.com/tsmask/go-oam"
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
)
// 实例化数据层 AlarmEvent 结构体
var NewAlarmEvent = &AlarmEvent{
alarmEventRepository: repository.NewAlarmEvent,
}
// AlarmEvent 告警 服务层处理
type AlarmEvent struct {
alarmEventRepository *repository.AlarmEvent // 告警数据信息
}
// FindByPage 根据条件分页查询
func (r AlarmEvent) FindByPage(query model.AlarmEventQuery) ([]model.AlarmEvent, int64) {
return r.alarmEventRepository.SelectByPage(query)
}
// Find 查询
func (r AlarmEvent) Find(param model.AlarmEvent) []model.AlarmEvent {
return r.alarmEventRepository.Select(param)
}
// Insert 新增信息
func (s AlarmEvent) Insert(param model.AlarmEvent) int64 {
return s.alarmEventRepository.Insert(param)
}
// Update 修改信息
func (s AlarmEvent) Update(param model.AlarmEvent) int64 {
return s.alarmEventRepository.Update(param)
}
// DeleteByIds 批量删除信息
func (r AlarmEvent) DeleteByIds(ids []int64) (int64, error) {
// 检查是否存在
data := r.alarmEventRepository.SelectByIds(ids)
if len(data) <= 0 {
return 0, fmt.Errorf("no data")
}
if len(data) == len(ids) {
rows := r.alarmEventRepository.DeleteByIds(ids)
return rows, nil
}
// 删除信息失败!
return 0, fmt.Errorf("delete fail")
}
// FindAlarmEventSeqLast 查询网元告警最后一条序号
func (s AlarmEvent) FindAlarmEventSeqLast(neType, neId string) int64 {
return s.alarmEventRepository.SelectAlarmEventSeqLast(neType, neId)
}
// ClearByIds 批量清除告警信息
func (r AlarmEvent) ClearByIds(ids []int64, clearUser, clearType string) (int64, error) {
// 检查是否存在
arr := r.alarmEventRepository.SelectByIds(ids)
if len(arr) <= 0 {
return 0, fmt.Errorf("no data")
}
if len(arr) == len(ids) {
var rows int64 = 0
for _, v := range arr {
v.AlarmStatus = oam.ALARM_STATUS_CLEAR
// 告警清除
v.ClearType = clearType
v.ClearUser = clearUser
v.ClearTime = time.Now()
rows += r.alarmEventRepository.Update(v)
}
return rows, nil
}
return 0, fmt.Errorf("clear fail")
}

View File

@@ -0,0 +1,26 @@
package service
import (
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
)
// 实例化数据层 AlarmForwardLog 结构体
var NewAlarmForwardLog = &AlarmForwardLog{
alarmForwardLogRepository: repository.NewAlarmForwardLog,
}
// AlarmForwardLog 告警转发记录表 服务层处理
type AlarmForwardLog struct {
alarmForwardLogRepository *repository.AlarmForwardLog // 告警转发记录信息
}
// FindByPage 根据条件分页查询
func (r AlarmForwardLog) FindByPage(query model.AlarmForwardLogQuery) ([]model.AlarmForwardLog, int64) {
return r.alarmForwardLogRepository.SelectByPage(query)
}
// Insert 插入数据
func (r AlarmForwardLog) Insert(item model.AlarmForwardLog) int64 {
return r.alarmForwardLogRepository.Insert(item)
}

View File

@@ -0,0 +1,26 @@
package service
import (
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
)
// 实例化数据层 AlarmLog 结构体
var NewAlarmLog = &AlarmLog{
alarmLogRepository: repository.NewAlarmLog,
}
// AlarmLog 基站状态记录表 服务层处理
type AlarmLog struct {
alarmLogRepository *repository.AlarmLog // 基站状态记录信息
}
// FindByPage 根据条件分页查询
func (r AlarmLog) FindByPage(query model.AlarmLogQuery) ([]model.AlarmLog, int64) {
return r.alarmLogRepository.SelectByPage(query)
}
// Insert 插入数据
func (r AlarmLog) Insert(item model.AlarmLog) int64 {
return r.alarmLogRepository.Insert(item)
}

View File

@@ -8,6 +8,7 @@ import (
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
"github.com/tsmask/go-oam"
)
// 实例化数据层 Alarm 结构体
@@ -61,6 +62,58 @@ func (s Alarm) FindAlarmSeqLast(neType, neId string) int64 {
return s.alarmRepository.SelectAlarmSeqLast(neType, neId)
}
// ClearByIds 批量清除告警信息
func (r Alarm) ClearByIds(ids []string, clearUser, clearType string) (int64, error) {
// 检查是否存在
arr := r.alarmRepository.SelectByIds(ids)
if len(arr) <= 0 {
return 0, fmt.Errorf("no data")
}
if len(arr) == len(ids) {
var rows int64 = 0
for _, v := range arr {
// 状态检查AlarmCode变更告警ID
alarmCode := parse.Number(v.AlarmCode)
if alarmCode == constants.ALARM_STATE_CHECK || alarmCode == constants.ALARM_CMD_CHECK || alarmCode == constants.ALARM_LICENSE_CHECK {
v.AlarmId = fmt.Sprintf("%s%d", v.AlarmCode, v.EventTime.UnixMilli())
}
v.AlarmStatus = oam.ALARM_STATUS_CLEAR
// 告警清除
clearTime := time.Now()
v.ClearType = clearType
v.ClearUser = clearUser
v.ClearTime = &clearTime
rows += r.alarmRepository.Update(v)
}
return rows, nil
}
return 0, fmt.Errorf("clear fail")
}
// AckByIds 批量确认清除告警信息
func (r Alarm) AckByIds(ids []string, ackUser, ackState string) (int64, error) {
// 检查是否存在
arr := r.alarmRepository.SelectByIds(ids)
if len(arr) <= 0 {
return 0, fmt.Errorf("no data")
}
if len(arr) == len(ids) {
var rows int64 = 0
for _, v := range arr {
// 确认清除
v.AckState = ackState
ackTime := time.Now()
v.AckTime = &ackTime
v.AckUser = ackUser
rows += r.alarmRepository.Update(v)
}
return rows, nil
}
return 0, fmt.Errorf("ack fail")
}
// AlarmClearByIds 批量清除告警信息
func (r Alarm) AlarmClearByIds(ids []string, clearUser string) (int64, error) {
// 检查是否存在

View File

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

View File

@@ -0,0 +1,645 @@
package service
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"be.ems/src/framework/i18n"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
sysService "be.ems/src/modules/system/service"
)
// 实例化数据层 CDREvent 结构体
var NewCDREvent = &CDREvent{
cdrEventRepository: repository.NewCDREvent,
}
// CDREvent CDR会话事件 服务层处理
type CDREvent struct {
cdrEventRepository *repository.CDREvent // CDR会话事件数据信息
}
// FindByPage 根据条件分页查询
func (r CDREvent) FindByPage(neType string, query map[string]string) ([]model.CDREvent, int64) {
return r.cdrEventRepository.SelectByPage(neType, query)
}
// DeleteByIds 批量删除信息
func (r CDREvent) DeleteByIds(neType string, ids []int64) (int64, error) {
// 检查是否存在
rows := r.cdrEventRepository.SelectByIds(neType, ids)
if len(rows) <= 0 {
return 0, fmt.Errorf("not data")
}
if len(rows) == len(ids) {
rows := r.cdrEventRepository.DeleteByIds(neType, ids)
return rows, nil
}
// 删除信息失败!
return 0, fmt.Errorf("delete fail")
}
// Insert 新增信息
func (s CDREvent) Insert(param model.CDREvent) int64 {
return s.cdrEventRepository.Insert(param)
}
// ExportSMSC 导出数据到 xlsx 文件
func (r CDREvent) ExportSMSC(rows []model.CDREvent, fileName, language string) (string, error) {
// 第一行表头标题
headerCells := map[string]string{
"A1": "ID",
"B1": "NE Name",
"C1": "Record Behavior",
"D1": "Service Type",
"E1": "Caller",
"F1": "Called",
"G1": "Result",
"H1": "Time",
}
// 读取字典数据 CDR 原因码
dictCDRCauseCode := sysService.NewSysDictData.FindByType("cdr_cause_code")
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 解析 JSON 字符串为 map
var cdrJSON map[string]interface{}
err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON)
if err != nil {
logger.Warnf("CDRExport Error parsing JSON: %s", err.Error())
continue
}
// 记录类型
recordType := ""
if v, ok := cdrJSON["recordType"]; ok && v != nil {
recordType = v.(string)
}
// 服务类型
serviceType := ""
if v, ok := cdrJSON["serviceType"]; ok && v != nil {
serviceType = v.(string)
}
// 被叫
called := ""
if v, ok := cdrJSON["calledParty"]; ok && v != nil {
called = v.(string)
}
// 主叫
caller := ""
if v, ok := cdrJSON["callerParty"]; ok && v != nil {
caller = v.(string)
}
// 呼叫结果 0失败1成功
callResult := "Fail"
if v, ok := cdrJSON["result"]; ok && v != nil {
resultVal := parse.Number(v)
if resultVal == 1 {
callResult = "Success"
}
}
// 结果原因
if v, ok := cdrJSON["cause"]; ok && v != nil && callResult == "Fail" {
cause := fmt.Sprint(v)
for _, v := range dictCDRCauseCode {
if cause == v.DictValue {
callResult = fmt.Sprintf("%s, %s", callResult, i18n.TKey(language, v.DictLabel))
break
}
}
}
// 取时间
timeStr := ""
if v, ok := cdrJSON["updateTime"]; ok && v != nil {
if releaseTime := parse.Number(v); releaseTime > 0 {
timeStr = date.ParseDateToStr(releaseTime, date.YYYY_MM_DDTHH_MM_SSZ)
} else {
timeStr = v.(string)
}
}
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: row.NeName,
"C" + idx: recordType,
"D" + idx: serviceType,
"E" + idx: caller,
"F" + idx: called,
"G" + idx: callResult,
"H" + idx: timeStr,
})
}
// 导出数据表格
return file.WriteSheet(headerCells, dataCells, fileName, "")
}
// ExportSMF 导出数据到 xlsx 文件
func (r CDREvent) ExportSMF(rows []model.CDREvent, fileName string) (string, error) {
// 第一行表头标题
headerCells := map[string]string{
"A1": "ID",
"B1": "Charging ID",
"C1": "NE Name",
"D1": "Resource Unique ID",
"E1": "Subscriber ID Data",
"F1": "Subscriber ID Type",
"G1": "Data Volume Uplink",
"H1": "Data Volume Downlink",
"I1": "Data Total Volume",
"J1": "Duration",
"K1": "Invocation Time",
"L1": "User Identifier",
"M1": "SSC Mode",
"N1": "DNN ID",
"O1": "PDU Type",
"P1": "RAT Type",
"Q1": "PDU IPv4 Address",
"R1": "Network Function IPv4",
"S1": "PDU IPv6 Address Swith Prefix",
"T1": "Record Network Function ID",
"U1": "Record Type",
"V1": "Record Opening Time",
}
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 解析 JSON 字符串为 map
var cdrJSON map[string]interface{}
err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON)
if err != nil {
logger.Warnf("CDRExport Error parsing JSON: %s", err.Error())
continue
}
// 计费ID
chargingID := ""
if v, ok := cdrJSON["chargingID"]; ok && v != nil {
chargingID = fmt.Sprint(parse.Number(v))
}
// 订阅 ID 类型
subscriptionIDType := "-"
// 订阅 ID 数据
subscriptionIDData := "-"
if v, ok := cdrJSON["subscriberIdentifier"]; ok && v != nil {
if sub, subOk := v.(map[string]any); subOk && sub != nil {
subscriptionIDType = sub["subscriptionIDType"].(string)
subscriptionIDData = sub["subscriptionIDData"].(string)
}
}
// 网络功能 IPv4 地址
networkFunctionIPv4Address := ""
if v, ok := cdrJSON["nFunctionConsumerInformation"]; ok && v != nil {
if conInfo, conInfoOk := v.(map[string]any); conInfoOk && conInfo != nil {
networkFunctionIPv4Address = conInfo["networkFunctionIPv4Address"].(string)
}
}
// 数据量上行链路
var dataVolumeUplink int64 = 0
// 数据量下行链路
var dataVolumeDownlink int64 = 0
// 数据总量
var dataTotalVolume int64 = 0
if v, ok := cdrJSON["listOfMultipleUnitUsage"]; ok && v != nil {
usageList := v.([]any)
if len(usageList) > 0 {
for _, used := range usageList {
usedUnit := used.(map[string]any)
usedUnitList := usedUnit["usedUnitContainer"].([]any)
if len(usedUnitList) > 0 {
for _, data := range usedUnitList {
udata := data.(map[string]any)
if dup, dupOk := udata["dataVolumeUplink"]; dupOk {
dataVolumeUplink += parse.Number(dup)
}
if ddown, ddownOk := udata["dataVolumeDownlink"]; ddownOk {
dataVolumeDownlink += parse.Number(ddown)
}
if dt, dtOk := udata["dataTotalVolume"]; dtOk {
dataTotalVolume += parse.Number(dt)
}
}
}
}
}
}
// 时长
duration := "-"
if v, ok := cdrJSON["duration"]; ok && v != nil {
duration = fmt.Sprint(parse.Number(v))
}
// 调用时间
invocationTimestamp := ""
if v, ok := cdrJSON["invocationTimestamp"]; ok && v != nil {
invocationTimestamp = v.(string)
}
// 记录打开时间
User_Identifier := ""
SSC_Mode := ""
RAT_Type := ""
DNN_ID := ""
PDU_Type := ""
PDU_IPv4 := ""
PDU_IPv6 := ""
if v, ok := cdrJSON["pDUSessionChargingInformation"]; ok && v != nil {
pduInfo := v.(map[string]any)
if v, ok := pduInfo["userIdentifier"]; ok && v != nil {
User_Identifier = v.(string)
}
if v, ok := pduInfo["sSCMode"]; ok && v != nil {
SSC_Mode = v.(string)
}
if v, ok := pduInfo["rATType"]; ok && v != nil {
RAT_Type = v.(string)
}
if v, ok := pduInfo["dNNID"]; ok && v != nil {
DNN_ID = v.(string)
}
if v, ok := pduInfo["pDUType"]; ok && v != nil {
PDU_Type = v.(string)
}
if v, ok := pduInfo["pDUAddress"]; ok && v != nil {
pDUAddress := v.(map[string]any)
if addr, ok := pDUAddress["pDUIPv4Address"]; ok && addr != nil {
PDU_IPv4 = addr.(string)
}
if addr, ok := pDUAddress["pDUIPv6AddresswithPrefix"]; ok && addr != nil {
PDU_IPv6 = addr.(string)
}
}
}
// 记录网络参数ID
recordNFID := ""
if v, ok := cdrJSON["recordingNetworkFunctionID"]; ok && v != nil {
recordNFID = v.(string)
}
//记录开始时间
recordOpeningTime := ""
if v, ok := cdrJSON["recordOpeningTime"]; ok && v != nil {
recordOpeningTime = v.(string)
}
//记录类型
recordType := ""
if v, ok := cdrJSON["recordType"]; ok && v != nil {
recordType = v.(string)
}
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: chargingID,
"C" + idx: row.NeName,
"D" + idx: row.RmUid,
"E" + idx: subscriptionIDData,
"F" + idx: subscriptionIDType,
"G" + idx: dataVolumeUplink,
"H" + idx: dataVolumeDownlink,
"I" + idx: dataTotalVolume,
"J" + idx: duration,
"K" + idx: invocationTimestamp,
"L" + idx: User_Identifier,
"M" + idx: SSC_Mode,
"N" + idx: DNN_ID,
"O" + idx: PDU_Type,
"P" + idx: RAT_Type,
"Q" + idx: PDU_IPv4,
"R" + idx: networkFunctionIPv4Address,
"S" + idx: PDU_IPv6,
"T" + idx: recordNFID,
"U" + idx: recordType,
"V" + idx: recordOpeningTime,
})
}
// 导出数据表格
return file.WriteSheet(headerCells, dataCells, fileName, "")
}
// ExportSGWC 导出数据到 xlsx 文件
func (r CDREvent) ExportSGWC(rows []model.CDREvent, fileName string) (string, error) {
// 第一行表头标题
headerCells := map[string]string{
"A1": "ID",
"B1": "NE Name",
"C1": "Resource Unique ID",
"D1": "Charging ID",
"E1": "IMSI",
"F1": "MSISDN",
"G1": "GPRS Uplink",
"H1": "GPRS Downlink",
"I1": "Duration",
"J1": "Invocation Time",
"K1": "PGW Address",
"L1": "SGW Address",
"M1": "RAT Type",
"N1": "PDPPDN Type",
"O1": "PDPPDN Address",
"P1": "Node Address",
"Q1": "Node Type",
"R1": "Record Access Point Name NI",
"S1": "Record Cause For Rec Closing",
"T1": "Record Sequence Number",
"U1": "Local Record Sequence Number",
"V1": "Record Type",
"W1": "Record Opening Time",
}
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 解析 JSON 字符串为 map
var cdrJSON map[string]interface{}
err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON)
if err != nil {
logger.Warnf("CDRExport Error parsing JSON: %s", err.Error())
continue
}
// 计费ID
chargingID := ""
if v, ok := cdrJSON["chargingID"]; ok && v != nil {
chargingID = fmt.Sprint(parse.Number(v))
}
// IMSI
servedIMSI := ""
if v, ok := cdrJSON["servedIMSI"]; ok && v != nil {
servedIMSI = fmt.Sprint(v)
}
// MSISDN
servedMSISDN := ""
if v, ok := cdrJSON["servedMSISDN"]; ok && v != nil {
servedMSISDN = fmt.Sprint(v)
}
// pGWAddressUsed
pGWAddressUsed := ""
if v, ok := cdrJSON["pGWAddressUsed"]; ok && v != nil {
pGWAddressUsed = fmt.Sprint(v)
headerCells["K1"] = "PGW Address"
}
if v, ok := cdrJSON["GGSNAddress"]; ok && v != nil {
pGWAddressUsed = fmt.Sprint(v)
headerCells["K1"] = "GGSN Address"
}
// sGWAddress
sGWAddress := ""
if v, ok := cdrJSON["sGWAddress"]; ok && v != nil {
sGWAddress = fmt.Sprint(v)
headerCells["L1"] = "SGW Address"
}
if v, ok := cdrJSON["SGSNAddress"]; ok && v != nil {
sGWAddress = fmt.Sprint(v)
headerCells["L1"] = "SGSN Address"
}
// recordType
recordType := ""
if v, ok := cdrJSON["recordType"]; ok && v != nil {
recordType = fmt.Sprint(v)
}
// rATType
rATType := ""
if v, ok := cdrJSON["rATType"]; ok && v != nil {
rATType = fmt.Sprint(v)
}
// pdpPDNType
pdpPDNType := ""
if v, ok := cdrJSON["pdpPDNType"]; ok && v != nil {
pdpPDNType = fmt.Sprint(v)
}
// servedPDPPDNAddress
servedPDPPDNAddress := ""
if v, ok := cdrJSON["servedPDPPDNAddress"]; ok && v != nil {
servedPDPPDNAddress = fmt.Sprint(v)
}
// servedPDPPDNAddress
servingNodeAddress := []string{}
if v, ok := cdrJSON["servingNodeAddress"]; ok && v != nil {
for _, v := range v.([]any) {
servingNodeAddress = append(servingNodeAddress, fmt.Sprint(v))
}
}
// servingNodeType
servingNodeType := []string{}
if v, ok := cdrJSON["servingNodeType"]; ok && v != nil {
for _, v := range v.([]any) {
if v, ok := v.(map[string]any)["servingNodeType"]; ok && v != nil {
servingNodeType = append(servingNodeType, fmt.Sprint(v))
}
}
}
// accessPointNameNI
accessPointNameNI := ""
if v, ok := cdrJSON["accessPointNameNI"]; ok && v != nil {
accessPointNameNI = fmt.Sprint(v)
}
// causeForRecClosing
causeForRecClosing := ""
if v, ok := cdrJSON["causeForRecClosing"]; ok && v != nil {
causeForRecClosing = fmt.Sprint(v)
}
// recordSequenceNumber
recordSequenceNumber := ""
if v, ok := cdrJSON["recordSequenceNumber"]; ok && v != nil {
recordSequenceNumber = fmt.Sprint(v)
}
// localRecordSequenceNumber
localRecordSequenceNumber := ""
if v, ok := cdrJSON["localRecordSequenceNumber"]; ok && v != nil {
localRecordSequenceNumber = fmt.Sprint(v)
}
// 数据量上行链路
var dataVolumeGPRSUplink int64 = 0
// 数据量下行链路
var dataVolumeGPRSDownlink int64 = 0
if v, ok := cdrJSON["listOfTrafficVolumes"]; ok && v != nil {
usageList := v.([]any)
if len(usageList) > 0 {
for _, used := range usageList {
usedUnit := used.(map[string]any)
if dup, dupOk := usedUnit["dataVolumeGPRSUplink"]; dupOk {
dataVolumeGPRSUplink = parse.Number(dup)
}
if ddown, ddownOk := usedUnit["dataVolumeGPRSDownlink"]; ddownOk {
dataVolumeGPRSDownlink = parse.Number(ddown)
}
}
}
}
// 时长
duration := "-"
if v, ok := cdrJSON["duration"]; ok && v != nil {
duration = fmt.Sprint(parse.Number(v))
}
// 调用时间
invocationTimestamp := ""
if v, ok := cdrJSON["recordOpeningTime"]; ok && v != nil {
invocationTimestamp = v.(string)
}
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: row.NeName,
"C" + idx: row.RmUid,
"D" + idx: chargingID,
"E" + idx: servedIMSI,
"F" + idx: servedMSISDN,
"G" + idx: dataVolumeGPRSUplink,
"H" + idx: dataVolumeGPRSDownlink,
"I" + idx: duration,
"J" + idx: invocationTimestamp,
"K" + idx: pGWAddressUsed,
"L" + idx: sGWAddress,
"M" + idx: rATType,
"N" + idx: pdpPDNType,
"O" + idx: servedPDPPDNAddress,
"P" + idx: strings.Join(servingNodeAddress, ","),
"Q" + idx: strings.Join(servingNodeType, ","),
"R" + idx: accessPointNameNI,
"S" + idx: causeForRecClosing,
"T" + idx: recordSequenceNumber,
"U" + idx: localRecordSequenceNumber,
"V" + idx: recordType,
"W" + idx: invocationTimestamp,
})
}
// 导出数据表格
return file.WriteSheet(headerCells, dataCells, fileName, "")
}
// ExportIMS 导出数据到 xlsx 文件
func (r CDREvent) ExportIMS(rows []model.CDREvent, fileName, language string) (string, error) {
// 第一行表头标题
headerCells := map[string]string{
"A1": "ID",
"B1": "NE Name",
"C1": "Record Behavior",
"D1": "Type",
"E1": "Caller",
"F1": "Called",
"G1": "Duration",
"H1": "Result Code",
"I1": "Result Cause",
"J1": "Call Start Time",
"K1": "Hangup Time",
}
// 读取字典数据 CDR SIP响应代码类别类型
dictCDRSipCode := sysService.NewSysDictData.FindByType("cdr_sip_code")
// 读取字典数据 CDR SIP响应代码类别类型原因
dictCDRSipCodeCause := sysService.NewSysDictData.FindByType("cdr_sip_code_cause")
// 读取字典数据 CDR 呼叫类型
dictCDRCallType := sysService.NewSysDictData.FindByType("cdr_call_type")
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 解析 JSON 字符串为 map
var cdrJSON map[string]any
err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON)
if err != nil {
logger.Warnf("CDRExport Error parsing JSON: %s", err.Error())
continue
}
// 记录类型
recordType := ""
if v, ok := cdrJSON["recordType"]; ok && v != nil {
recordType = v.(string)
}
// 呼叫类型
callType := "sms"
callTypeLable := "SMS"
if v, ok := cdrJSON["callType"]; ok && v != nil {
callType = v.(string)
for _, v := range dictCDRCallType {
if callType == v.DictValue {
callTypeLable = i18n.TKey(language, v.DictLabel)
break
}
}
}
// 被叫
called := ""
if v, ok := cdrJSON["calledParty"]; ok && v != nil {
called = v.(string)
}
// 主叫
caller := ""
if v, ok := cdrJSON["callerParty"]; ok && v != nil {
caller = v.(string)
}
// 时长
duration := "-"
if v, ok := cdrJSON["callDuration"]; ok && v != nil && callType != "sms" {
duration = fmt.Sprintf("%ds", parse.Number(v))
}
// 呼叫结果 非短信都有code作为结果 sms短信都ok
callResult := "Other"
callCause := "Call failure for other reason"
if callType == "sms" {
callResult = "Success"
callCause = "Normal Send"
} else {
if v, ok := cdrJSON["cause"]; ok && v != nil {
cause := fmt.Sprint(v)
for _, v := range dictCDRSipCode {
if cause == v.DictValue {
callResult = i18n.TKey(language, v.DictLabel)
break
}
}
for _, v := range dictCDRSipCodeCause {
if cause == v.DictValue {
callCause = i18n.TKey(language, v.DictLabel)
break
}
}
}
}
// 呼叫时间
seizureTimeStr := ""
if v, ok := cdrJSON["seizureTime"]; ok && v != nil {
if seizureTime := parse.Number(v); seizureTime > 0 {
seizureTimeStr = date.ParseDateToStr(seizureTime, date.YYYY_MM_DDTHH_MM_SSZ)
} else {
seizureTimeStr = v.(string)
}
}
// 挂断时间
releaseTimeStr := ""
if v, ok := cdrJSON["releaseTime"]; ok && v != nil {
if releaseTime := parse.Number(v); releaseTime > 0 {
releaseTimeStr = date.ParseDateToStr(releaseTime, date.YYYY_MM_DDTHH_MM_SSZ)
} else {
releaseTimeStr = v.(string)
}
}
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: row.NeName,
"C" + idx: recordType,
"D" + idx: callTypeLable,
"E" + idx: caller,
"F" + idx: called,
"G" + idx: duration,
"H" + idx: callResult,
"I" + idx: callCause,
"J" + idx: seizureTimeStr,
"K" + idx: releaseTimeStr,
})
}
// 导出数据表格
return file.WriteSheet(headerCells, dataCells, fileName, "")
}

View File

@@ -14,26 +14,26 @@ import (
neModel "be.ems/src/modules/network_element/model"
)
// 实例化数据层 PerfKPI 结构体
var NewPerfKPI = &PerfKPI{
perfKPIRepository: repository.NewPerfKPI,
// 实例化数据层 KpiReport 结构体
var NewKpiReport = &KpiReport{
kpiReportRepository: repository.NewKpiReport,
}
// PerfKPI 性能统计 服务层处理
type PerfKPI struct {
perfKPIRepository *repository.PerfKPI // 性能统计数据信息
// KpiReport 性能统计 服务层处理
type KpiReport struct {
kpiReportRepository *repository.KpiReport // 性能统计数据信息
}
// SelectGoldKPI 通过网元指标数据信息
func (r *PerfKPI) SelectGoldKPI(query model.GoldKPIQuery) []map[string]any {
func (r *KpiReport) SelectGoldKPI(query model.KPIQuery) []map[string]any {
// 获取数据指标id
var kpiIds []string
kpiTitles := r.perfKPIRepository.SelectGoldKPITitle(query.NeType)
for _, kpiId := range kpiTitles {
kpiIds = append(kpiIds, kpiId.KPIID)
kpiTitles := r.kpiReportRepository.SelectGoldKPITitle(query.NeType)
for _, v := range kpiTitles {
kpiIds = append(kpiIds, v.KpiId)
}
data := r.perfKPIRepository.SelectGoldKPI(query, kpiIds)
data := r.kpiReportRepository.SelectGoldKPI(query, kpiIds)
if data == nil {
return []map[string]any{}
}
@@ -41,29 +41,39 @@ func (r *PerfKPI) SelectGoldKPI(query model.GoldKPIQuery) []map[string]any {
}
// SelectGoldKPITitle 网元对应的指标名称
func (r *PerfKPI) SelectGoldKPITitle(neType string) []model.GoldKPITitle {
return r.perfKPIRepository.SelectGoldKPITitle(neType)
func (r *KpiReport) SelectGoldKPITitle(neType string) []model.KpiTitle {
return r.kpiReportRepository.SelectGoldKPITitle(neType)
}
// FindTitle 网元对应的指标名称
func (r KpiReport) FindTitle(neType string) []model.KpiTitle {
return r.kpiReportRepository.SelectGoldKPITitle(neType)
}
// Insert 新增信息
func (s KpiReport) Insert(param model.KpiReport) int64 {
return s.kpiReportRepository.Insert(param)
}
// TitleSelectByPage 分页查询指标名称
func (r *PerfKPI) TitleSelectByPage(query map[string]string) ([]model.GoldKPITitle, int64) {
return r.perfKPIRepository.TitleSelectByPage(query)
func (r *KpiReport) TitleSelectByPage(query map[string]string) ([]model.KpiTitle, int64) {
return r.kpiReportRepository.TitleSelectByPage(query)
}
// TitleFind 查询信息
func (r PerfKPI) TitleFind(param model.GoldKPITitle) []model.GoldKPITitle {
return r.perfKPIRepository.TitleSelect(param)
func (r KpiReport) TitleFind(param model.KpiTitle) []model.KpiTitle {
return r.kpiReportRepository.TitleSelect(param)
}
// TitleUpdate 修改指标状态
func (r *PerfKPI) TitleUpdate(param model.GoldKPITitle) int64 {
return r.perfKPIRepository.TitleUpdate(param)
func (r *KpiReport) TitleUpdate(param model.KpiTitle) int64 {
return r.kpiReportRepository.TitleUpdate(param)
}
// FindData 通过网元指标数据信息
func (s PerfKPI) FindData(query model.GoldKPIQuery) []map[string]any {
func (s KpiReport) FindData(query model.KPIQuery) []map[string]any {
// 原始数据
rows := s.perfKPIRepository.SelectKPI(query)
rows := s.kpiReportRepository.SelectKPI(query)
if len(rows) <= 0 {
return []map[string]any{}
}
@@ -169,7 +179,7 @@ func (s PerfKPI) FindData(query model.GoldKPIQuery) []map[string]any {
// UPFTodayFlowFind 查询UPF总流量 N3上行 N6下行
// day 统计天数
func (r PerfKPI) UPFTodayFlowFind(rmUID string, day int) (int64, int64) {
func (r KpiReport) UPFTodayFlowFind(rmUID string, day int) (int64, int64) {
// 获取当前日期
now := time.Now()
var upTotal, downTotal int64
@@ -197,7 +207,7 @@ func (r PerfKPI) UPFTodayFlowFind(rmUID string, day int) (int64, int64) {
}
// UPFTodayFlow UPF流量今日统计
func (r PerfKPI) UPFTodayFlowUpdate(rmUID string, upValue, downValue int64) error {
func (r KpiReport) UPFTodayFlowUpdate(rmUID string, upValue, downValue int64) error {
// 按日期存储统计数据
dateKey := time.Now().Format("2006-01-02")
key := fmt.Sprintf("%sUPF_FLOW:%s:%s", cachekey.NE_DATA_KEY, rmUID, dateKey)
@@ -214,7 +224,7 @@ func (r PerfKPI) UPFTodayFlowUpdate(rmUID string, upValue, downValue int64) erro
// UPFTodayFlowLoad UPF上下行数据到redis
// day 统计天数
func (r PerfKPI) UPFTodayFlowLoad(day int) {
func (r KpiReport) UPFTodayFlowLoad(day int) {
cacheKeys, _ := redis.GetKeys("", cachekey.NE_KEY+"UPF:*")
if len(cacheKeys) == 0 {
return
@@ -237,7 +247,7 @@ func (r PerfKPI) UPFTodayFlowLoad(day int) {
endTime := beginTime + 24*60*60*1000 - 1
// 查询历史数据
// down * 8 / 1000 / 1000 单位M
info := r.perfKPIRepository.SelectUPFTotalFlow("UPF", v.RmUID, fmt.Sprint(beginTime), fmt.Sprint(endTime))
info := r.kpiReportRepository.SelectUPFTotalFlow("UPF", v.RmUID, fmt.Sprint(beginTime), fmt.Sprint(endTime))
if v, ok := info["up"]; ok && v == nil {
info["up"] = 0
}
@@ -267,7 +277,7 @@ func (r PerfKPI) UPFTodayFlowLoad(day int) {
}
// IMSBusyHour IMS忙时流量统计
func (r PerfKPI) IMSBusyHour(rmUID string, timestamp int64) []map[string]any {
func (r KpiReport) IMSBusyHour(rmUID string, timestamp int64) []map[string]any {
t := time.UnixMilli(timestamp)
beginTime := t
endTime := t
@@ -284,7 +294,7 @@ func (r PerfKPI) IMSBusyHour(rmUID string, timestamp int64) []map[string]any {
endTime = beginTime.Add(time.Hour - time.Millisecond)
}
// 转换为毫秒级时间戳
return r.perfKPIRepository.SelectIMSBusyHour(rmUID, beginTime.UnixMilli(), endTime.UnixMilli())
return r.kpiReportRepository.SelectIMSBusyHour(rmUID, beginTime.UnixMilli(), endTime.UnixMilli())
}
// 定义结构体用于存储话务量值和对应的时间
@@ -294,12 +304,12 @@ type TrafficData struct {
}
// IMSBusyWeek IMS忙时流量统计 周
func (r PerfKPI) IMSBusyWeek(rmUID string, weekStart, weekEnd int64) map[string]any {
func (r KpiReport) IMSBusyWeek(rmUID string, weekStart, weekEnd int64) map[string]any {
weekStartTime := time.UnixMilli(weekStart)
weekEndTime := time.UnixMilli(weekEnd)
// 1. 获取一周内每小时的呼叫数据
data := r.perfKPIRepository.SelectIMSBusyHour(rmUID, weekStartTime.UnixMilli(), weekEndTime.UnixMilli())
data := r.kpiReportRepository.SelectIMSBusyHour(rmUID, weekStartTime.UnixMilli(), weekEndTime.UnixMilli())
if len(data) == 0 {
return map[string]any{

View File

@@ -0,0 +1,229 @@
package service
import (
"encoding/json"
"fmt"
"strconv"
"be.ems/src/framework/i18n"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
sysService "be.ems/src/modules/system/service"
"github.com/tsmask/go-oam"
)
// 实例化数据层 UEEvent 结构体
var NewUEEvent = &UEEvent{
ueEventRepository: repository.NewUEEvent,
}
// UEEvent UE会话事件 服务层处理
type UEEvent struct {
ueEventRepository *repository.UEEvent // UE会话事件数据信息
}
// FindByPage 根据条件分页查询
func (r UEEvent) FindByPage(neType string, query map[string]string) ([]model.UEEvent, int64) {
return r.ueEventRepository.SelectByPage(neType, query)
}
// DeleteByIds 批量删除信息
func (r UEEvent) DeleteByIds(neType string, ids []int64) (int64, error) {
// 检查是否存在
rows := r.ueEventRepository.SelectByIds(neType, ids)
if len(rows) <= 0 {
return 0, fmt.Errorf("no data")
}
if len(rows) == len(ids) {
rows := r.ueEventRepository.DeleteByIds(neType, ids)
return rows, nil
}
// 删除信息失败!
return 0, fmt.Errorf("delete fail")
}
// Insert 新增信息
func (r UEEvent) Insert(param model.UEEvent) int64 {
return r.ueEventRepository.Insert(param)
}
// ExportAMF 导出数据到 xlsx 文件
func (r UEEvent) ExportAMF(rows []model.UEEvent, fileName, language string) (string, error) {
// 第一行表头标题
headerCells := map[string]string{
"A1": "ID",
"B1": "IMSI",
"C1": "Event Type",
"D1": "Result",
"E1": "Time",
}
// 读取字典数据 UE 事件类型
dictUEEventType := sysService.NewSysDictData.SelectDictDataByType("ue_event_type")
// 读取字典数据 UE 事件认证代码类型
dictUEAauthCode := sysService.NewSysDictData.FindByType("ue_auth_code")
// 读取字典数据 UE 事件CM状态
dictUEEventCmState := sysService.NewSysDictData.FindByType("ue_event_cm_state")
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 解析 JSON 字符串为 map
var eventJSON map[string]interface{}
err := json.Unmarshal([]byte(row.EventJSONStr), &eventJSON)
if err != nil {
logger.Warnf("UEExport Error parsing JSON: %s", err.Error())
continue
}
// 取IMSI
imsi := ""
if v, ok := eventJSON["imsi"]; ok && v != nil {
imsi = v.(string)
}
// 取类型
eventType := ""
for _, v := range dictUEEventType {
if row.EventType == v.DictValue {
eventType = i18n.TKey(language, v.DictLabel)
break
}
}
// 取结果
eventResult := ""
// 取时间
timeStr := ""
if row.EventType == oam.UENB_TYPE_AUTH {
if v, ok := eventJSON["authTime"]; ok && v != nil {
timeStr = v.(string)
}
if v, ok := eventJSON["authCode"]; ok && v != nil {
eventResult = v.(string)
for _, v := range dictUEAauthCode {
if eventResult == v.DictValue {
eventResult = i18n.TKey(language, v.DictLabel)
break
}
}
}
}
if row.EventType == oam.UENB_TYPE_DETACH {
if v, ok := eventJSON["detachTime"]; ok && v != nil {
timeStr = v.(string)
}
eventResult = "Success"
}
if row.EventType == oam.UENB_TYPE_CM {
if v, ok := eventJSON["changeTime"]; ok && v != nil {
timeStr = v.(string)
}
if v, ok := eventJSON["status"]; ok && v != nil {
eventResult = fmt.Sprint(v)
for _, v := range dictUEEventCmState {
if eventResult == v.DictValue {
eventResult = i18n.TKey(language, v.DictLabel)
break
}
}
}
}
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: imsi,
"C" + idx: eventType,
"D" + idx: eventResult,
"E" + idx: timeStr,
})
}
// 导出数据表格
return file.WriteSheet(headerCells, dataCells, fileName, "")
}
// ExportMME 导出数据到 xlsx 文件
func (r UEEvent) ExportMME(rows []model.UEEvent, fileName, language string) (string, error) {
// 第一行表头标题
headerCells := map[string]string{
"A1": "ID",
"B1": "IMSI",
"C1": "Event Type",
"D1": "Result",
"E1": "Time",
}
// 读取字典数据 UE 事件类型
dictUEEventType := sysService.NewSysDictData.FindByType("ue_event_type")
// 读取字典数据 UE 事件认证代码类型
dictUEAauthCode := sysService.NewSysDictData.FindByType("ue_auth_code")
// 读取字典数据 UE 事件CM状态
dictUEEventCmState := sysService.NewSysDictData.FindByType("ue_event_cm_state")
// 从第二行开始的数据
dataCells := make([]map[string]any, 0)
for i, row := range rows {
idx := strconv.Itoa(i + 2)
// 解析 JSON 字符串为 map
var eventJSON map[string]interface{}
err := json.Unmarshal([]byte(row.EventJSONStr), &eventJSON)
if err != nil {
logger.Warnf("UEExport Error parsing JSON: %s", err.Error())
continue
}
// 取IMSI
imsi := ""
if v, ok := eventJSON["imsi"]; ok && v != nil {
imsi = v.(string)
}
// 取类型
eventType := row.EventType
for _, v := range dictUEEventType {
if row.EventType == v.DictValue {
eventType = i18n.TKey(language, v.DictLabel)
break
}
}
// 取结果
eventResult := ""
if v, ok := eventJSON["result"]; ok && v != nil {
eventResult = v.(string)
if row.EventType == oam.UENB_TYPE_AUTH {
for _, v := range dictUEAauthCode {
if eventResult == v.DictValue {
eventResult = i18n.TKey(language, v.DictLabel)
break
}
}
}
if row.EventType == oam.UENB_TYPE_CM {
for _, v := range dictUEEventCmState {
if eventResult == v.DictValue {
eventResult = i18n.TKey(language, v.DictLabel)
break
}
}
}
}
// 取时间
timeStr := ""
if v, ok := eventJSON["timestamp"]; ok && v != nil {
rowTime := parse.Number(v)
timeStr = date.ParseDateToStr(rowTime, date.YYYY_MM_DDTHH_MM_SSZ)
}
dataCells = append(dataCells, map[string]any{
"A" + idx: row.ID,
"B" + idx: imsi,
"C" + idx: eventType,
"D" + idx: eventResult,
"E" + idx: timeStr,
})
}
// 导出数据表格
return file.WriteSheet(headerCells, dataCells, fileName, "")
}

View File

@@ -0,0 +1,11 @@
package notification
import (
"be.ems/src/framework/logger"
"github.com/gin-gonic/gin"
)
// 模块路由注册
func Setup(router *gin.Engine) {
logger.Infof("开始加载 ====> notification 模块路由")
}

View File

@@ -0,0 +1,126 @@
package service
import (
"bytes"
"crypto/tls"
"fmt"
ht "html/template"
"strings"
"time"
"github.com/wneessen/go-mail"
"be.ems/src/framework/config"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/parse"
neDataModel "be.ems/src/modules/network_data/model"
)
// EmailAlarm 发告警邮件
func EmailAlarm(a neDataModel.Alarm, neIP string) error {
emailList := fmt.Sprint(config.Get("alarm.alarmEmailForward.emailList"))
if len(emailList) == 0 {
return fmt.Errorf("email list is empty")
}
// 模板
htmlBodyTemplate := `
<strong>Alarm information</strong>
<p style="text-indent: 2.5em;">Sequence: {{.AlarmSeq}}</p>
<p style="text-indent: 2.5em;">NE Name: {{.NeName}}</p>
<p style="text-indent: 4.5em;">NE IP: {{.NeIp}}</p>
<p style="text-indent: 5em;">Title: {{.AlarmTitle}}</p>
<p style="text-indent: 3.5em;">Severity: {{.OrigSeverity}}</p>
<p style="text-indent: 2.5em;">Event Time: {{.AlarmTime}}</p>
<p style="text-indent: 2em;">Alarm Status: {{.AlarmStatus}}</p>
<i>Automatic sent by OMC, please do not reply!</i>
`
htmlTpl, err := ht.New("htmltpl").Parse(htmlBodyTemplate)
if err != nil {
return fmt.Errorf("EmailAlarm Parse alarmId:%s fail %s", a.AlarmId, err.Error())
}
// 参数值
data := map[string]any{
"AlarmSeq": a.AlarmSeq,
"NeName": a.NeName,
"NeIp": neIP,
"AlarmTitle": a.AlarmTitle,
"OrigSeverity": a.OrigSeverity,
"AlarmTime": date.ParseDateToStr(a.EventTime, time.RFC3339),
"AlarmStatus": a.AlarmStatus,
}
buffer := bytes.NewBuffer(nil)
if err := htmlTpl.Execute(buffer, data); err != nil {
return fmt.Errorf("EmailAlarm Execute alarmId:%s fail %s", a.AlarmId, err.Error())
}
htmlStr := buffer.String()
// 发送邮件
err = EmailSendHTML(htmlStr, strings.Split(emailList, ","))
if err != nil {
return fmt.Errorf("EmailAlarm alarmId:%s fail %s", a.AlarmId, err.Error())
}
return err
}
// EmailSendHTML 发送HTML邮件
func EmailSendHTML(htmlStr string, toEmailArr []string) error {
// QQ 邮箱:
// SMTP 服务器地址smtp.qq.comSSL协议端口465/994 | 非SSL协议端口25
// 163 邮箱:
// SMTP 服务器地址smtp.163.com端口25
// host := "mail.agrandtech.com"
// port := 25
// userName := "smtpext@agrandtech.com"
// password := "1000smtp@omc!"
emailConf := config.Get("alarm.alarmEmailForward").(map[string]any)
enable := parse.Boolean(emailConf["enable"])
if !enable {
return fmt.Errorf("email notification not enable")
}
title := fmt.Sprint(emailConf["title"])
smtp := fmt.Sprint(emailConf["smtp"])
port := parse.Number(emailConf["port"])
user := fmt.Sprint(emailConf["user"])
password := fmt.Sprint(emailConf["password"])
message := mail.NewMsg()
// 发件人
if err := message.From(user); err != nil {
return fmt.Errorf("failed to set From address: %s", err)
}
// 收件人
hasTo := false
for _, v := range toEmailArr {
if err := message.AddTo(v); err != nil {
logger.Errorf("failed to set To address: %v %s", v, err)
continue
}
hasTo = true
}
if !hasTo {
return fmt.Errorf("failed to set To address not has")
}
// 邮件主题
message.Subject(title)
// 邮件HTML内容
message.SetBodyString(mail.TypeTextHTML, htmlStr)
// 连接到邮件SMTP服务器
client, err := mail.NewClient(smtp,
mail.WithSMTPAuth(mail.SMTPAuthAutoDiscover),
mail.WithUsername(user),
mail.WithPort(int(port)),
mail.WithPassword(password),
mail.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}),
)
if err != nil {
return fmt.Errorf("failed to create mail client: %s", err)
}
// 发送
if err := client.DialAndSend(message); err != nil {
return fmt.Errorf("failed to send mail: %s", err)
}
return nil
}

View File

@@ -0,0 +1,164 @@
package service
import (
"bytes"
"fmt"
"strings"
tt "text/template"
"time"
"github.com/linxGnu/gosmpp"
"github.com/linxGnu/gosmpp/data"
"github.com/linxGnu/gosmpp/pdu"
"be.ems/src/framework/config"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/parse"
neDataModel "be.ems/src/modules/network_data/model"
)
var smscSession *gosmpp.Session
// connSM 连接smsc
func connSM() *gosmpp.Session {
if smscSession != nil {
return smscSession
}
smscAddr := fmt.Sprint(config.Get("alarm.alarmSMSForward.smscAddr"))
systemType := fmt.Sprint(config.Get("alarm.alarmSMSForward.systemType"))
systemID := fmt.Sprint(config.Get("alarm.alarmSMSForward.systemID"))
password := fmt.Sprint(config.Get("alarm.alarmSMSForward.password"))
// 建立连接
session, err := gosmpp.NewSession(
gosmpp.TXConnector(gosmpp.NonTLSDialer, gosmpp.Auth{
SMSC: smscAddr,
SystemID: systemID,
Password: password,
SystemType: systemType,
}),
gosmpp.Settings{
ReadTimeout: 2 * time.Second,
OnSubmitError: func(_ pdu.PDU, err error) {
logger.Errorf("failed to smpp submit error: %s", err.Error())
},
OnRebindingError: func(err error) {
logger.Errorf("failed to smpp rebinding error: %s", err.Error())
},
}, -1)
if err != nil {
logger.Errorf("failed to create smpp new session error: %s", err.Error())
return nil
}
// defer session.Close()
smscSession = session
return smscSession
}
// newSubmitSM 构建短信提交
func newSubmitSM(destSM string, message string) (*pdu.SubmitSM, error) {
dataCoding := parse.Number(config.Get("alarm.alarmSMSForward.dataCoding"))
enc := data.FromDataCoding(byte(dataCoding))
srcSM := fmt.Sprint(config.Get("alarm.alarmSMSForward.serviceNumber"))
// 源地址
srcAddr := pdu.NewAddress()
srcAddr.SetTon(5)
srcAddr.SetNpi(0)
err := srcAddr.SetAddress(srcSM)
if err != nil {
return nil, err
}
destAddr := pdu.NewAddress()
destAddr.SetTon(1)
destAddr.SetNpi(1)
err = destAddr.SetAddress(destSM)
if err != nil {
return nil, err
}
// build up submitSM
submitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)
submitSM.SourceAddr = srcAddr
submitSM.DestAddr = destAddr
if err = submitSM.Message.SetMessageWithEncoding(message, enc); err != nil {
return nil, err
}
submitSM.ProtocolID = 0
submitSM.RegisteredDelivery = 1
submitSM.ReplaceIfPresentFlag = 0
submitSM.EsmClass = 0
return submitSM, nil
}
// SMSCAlarm 发告警短信
func SMSCAlarm(a neDataModel.Alarm, neIP string) error {
mobileList := fmt.Sprint(config.Get("alarm.alarmSMSForward.mobileList"))
if len(mobileList) == 0 {
return fmt.Errorf("smsc list is empty")
}
// 模板
textBodyTemplate := `Alarm Notification
Sequence: {{.AlarmSeq}},
NE Name: {{.NeName}}
NE IP: {{.NeIp}}
Title: {{.AlarmTitle}}
Severity: {{.OrigSeverity}}
Event Time: {{.AlarmTime}}
Alarm Status: {{.AlarmStatus}}
Automatic sent by OMC, please do not reply!`
textTpl, err := tt.New("texttpl").Parse(textBodyTemplate)
if err != nil {
return fmt.Errorf("SMSCAlarm alarmId:%s fail %s", a.AlarmId, err.Error())
}
// 参数值
data := map[string]any{
"AlarmSeq": a.AlarmSeq,
"NeName": a.NeName,
"NeIp": neIP,
"AlarmTitle": a.AlarmTitle,
"OrigSeverity": a.OrigSeverity,
"AlarmTime": date.ParseDateToStr(a.EventTime, time.RFC3339),
"AlarmStatus": a.AlarmStatus,
}
buffer := bytes.NewBuffer(nil)
if err := textTpl.Execute(buffer, data); err != nil {
return fmt.Errorf("EmailAlarm Execute alarmId:%s fail %s", a.AlarmId, err.Error())
}
textStr := buffer.String()
// 发送短信
err = SMSCSendText(textStr, strings.Split(mobileList, ","))
if err != nil {
return fmt.Errorf("EmailAlarm alarmId:%s fail %s", a.AlarmId, err.Error())
}
return err
}
// SMSCSendText 发送文本短信
func SMSCSendText(textStr string, toMobileArr []string) error {
enable := parse.Boolean(config.Get("alarm.alarmSMSForward.enable"))
if !enable {
return fmt.Errorf("smsc notification not enable")
}
sm := connSM()
if sm == nil {
return fmt.Errorf("smpp new session create failed")
}
errArr := []string{}
for _, v := range toMobileArr {
submitSM, err := newSubmitSM(v, textStr)
if err != nil {
errArr = append(errArr, err.Error())
continue
}
if err = sm.Transceiver().Submit(submitSM); err != nil {
errArr = append(errArr, err.Error())
continue
}
}
if len(errArr) > 0 {
return fmt.Errorf("%s", strings.Join(errArr, ","))
}
return nil
}

View File

@@ -0,0 +1,721 @@
package controller
import (
"encoding/json"
"fmt"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/tsmask/go-oam"
goOamState "github.com/tsmask/go-oam/modules/state/service"
"be.ems/src/framework/logger"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/parse"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neModel "be.ems/src/modules/network_element/model"
neService "be.ems/src/modules/network_element/service"
oamService "be.ems/src/modules/oam/service"
)
// NewAPIRest 实例化控制层
var NewAPIRest = &APIRestController{}
// APIRestController 北向定义 控制层处理
//
// PATH /api/rest
type APIRestController struct{}
// ResolveAlarm 接收告警
//
// POST /faultManagement/v1/elementType/:elementTypeValue/objectType/alarms
func (s APIRestController) ResolveAlarm(c *gin.Context) {
var body []struct {
AlarmSeq int `json:"alarmSeq"`
AlarmId string `json:"alarmId"`
NeId string `json:"neId"` // 收到实际是rmUID
AlarmCode int `json:"alarmCode"`
AlarmTitle string `json:"alarmTitle"`
EventTime string `json:"eventTime"`
AlarmType string `json:"alarmType"`
OrigSeverity string `json:"origSeverity"`
PerceivedSeverity string `json:"perceivedSeverity"`
PVFlag string `json:"pvFlag"`
NeName string `json:"neName"`
NeType string `json:"neType"`
ObjectUid string `json:"objectUid"`
ObjectName string `json:"objectName"`
ObjectType string `json:"objectType"`
LocationInfo string `json:"locationInfo"`
Province string `json:"province"`
AlarmStatus int `json:"alarmStatus"`
SpecificProblem string `json:"specificProblem"`
SpecificProblemID string `json:"specificProblemID"`
AddInfo string `json:"addInfo"`
}
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
elementTypeValue := c.Param("elementTypeValue")
// alarmTypeValue 映射值
alarmTypeValue := func(str string) string {
arr := []string{
oam.ALARM_TYPE_COMMUNICATION_ALARM,
oam.ALARM_TYPE_EQUIPMENT_ALARM,
oam.ALARM_TYPE_PROCESSING_FAILURE,
oam.ALARM_TYPE_ENVIRONMENTAL_ALARM,
oam.ALARM_TYPE_QUALITY_OF_SERVICE_ALARM,
}
for k, v := range arr {
if v == str {
return v
}
if fmt.Sprint(k+1) == str {
return v
}
}
return str
}
// origSeverityValue 映射值
origSeverityValue := func(str string) string {
arr := []string{
oam.ALARM_SEVERITY_CRITICAL,
oam.ALARM_SEVERITY_MAJOR,
oam.ALARM_SEVERITY_MINOR,
oam.ALARM_SEVERITY_WARNING,
oam.ALARM_SEVERITY_EVENT,
}
for k, v := range arr {
if v == str {
return v
}
if fmt.Sprint(k+1) == str {
return v
}
}
return str
}
// alarmStatusValue 映射值
alarmStatusValue := func(value int) string {
arr := []string{
oam.ALARM_STATUS_CLEAR,
oam.ALARM_STATUS_ACTIVE,
}
for k, v := range arr {
if k == value {
return v
}
}
return oam.ALARM_STATUS_ACTIVE
}
alarmArr := make([]oam.Alarm, 0)
for _, v := range body {
if !strings.EqualFold(v.NeType, elementTypeValue) {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "elementType is inconsistent with neType"))
return
}
// 产生时间
eventTime := date.ParseStrToDate(v.EventTime, time.RFC3339)
// 创建告警
alarm := oam.Alarm{
NeUid: v.NeId, // 网元唯一标识
AlarmTime: eventTime.UnixMilli(), // 事件产生时间
AlarmId: v.AlarmId, // 告警ID 唯一,清除时对应
AlarmCode: v.AlarmCode, // 告警状态码
AlarmType: alarmTypeValue(v.AlarmType), // 告警类型
AlarmTitle: v.AlarmTitle, // 告警标题
PerceivedSeverity: origSeverityValue(v.OrigSeverity), // 告警级别
AlarmStatus: alarmStatusValue(v.AlarmStatus), // 告警状态
SpecificProblem: v.SpecificProblem, // 告警问题原因
SpecificProblemID: v.SpecificProblemID, // 告警问题原因ID
AddInfo: v.AddInfo, // 告警辅助信息
LocationInfo: v.LocationInfo, // 告警定位信息
}
alarmArr = append(alarmArr, alarm)
}
errArr := make([]string, 0)
for _, alarm := range alarmArr {
if err := oamService.NewAlarm.Resolve(alarm); err != nil {
errArr = append(errArr, err.Error())
}
}
if len(errArr) > 0 {
c.JSON(200, resp.ErrData(errArr))
return
}
c.JSON(200, resp.Ok(nil))
}
// ResolveCDR 接收话单
//
// POST /cdrManagement/v1/elementType/:elementTypeValue/objectType/cdrEvent
func (s APIRestController) ResolveCDR(c *gin.Context) {
var body struct {
NeType string `json:"neType" `
NeName string `json:"neName" `
RmUID string `json:"rmUID" `
Timestamp int `json:"timestamp" `
CDR map[string]any `json:"CDR" `
}
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
elementTypeValue := c.Param("elementTypeValue")
if !strings.EqualFold(body.NeType, elementTypeValue) {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "elementType is inconsistent with neType"))
return
}
recordTime := time.Now()
if body.Timestamp > 1e12 {
recordTime = time.UnixMilli(int64(body.Timestamp))
} else if body.Timestamp > 1e9 {
recordTime = time.Unix(int64(body.Timestamp), 0)
}
// 创建CDR
cdr := oam.CDR{
NeUid: body.RmUID, // 网元唯一标识
RecordTime: recordTime.UnixMilli(), // 记录时间 时间戳毫秒Push时自动填充
Data: body.CDR, // 话单信息
}
if err := oamService.NewCDR.Resolve(cdr); err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// ResolveKPI 接收KPI
//
// POST /performanceManagement/v1/elementType/:elementTypeValue/objectType/kpiReport/:index
func (s APIRestController) ResolveKPI(c *gin.Context) {
var body struct {
Timestamp string `json:"TimeStamp" binding:"required"`
Task struct {
Period struct {
StartTime string `json:"StartTime"`
EndTime string `json:"EndTime"`
} `json:"Period" binding:"required"`
NE struct {
NEName string `json:"NEName"`
RmUID string `json:"rmUID"`
NeType string `json:"NeType"`
KPIs []struct {
KPIID string `json:"KPIID"`
Value int64 `json:"Value"`
Err string `json:"Err"`
} `json:"KPIs" binding:"required"`
} `json:"NE" binding:"required"`
} `json:"Task" binding:"required"`
}
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
elementTypeValue := c.Param("elementTypeValue")
if !strings.EqualFold(body.Task.NE.NeType, elementTypeValue) {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "elementType is inconsistent with neType"))
return
}
// index := c.Param("index")
timestamp := body.Timestamp
taskPeriod := body.Task.Period
taskNeKPIs := body.Task.NE.KPIs
// 时间数据处理
receiverTime := date.ParseStrToDate(timestamp, date.YYYY_MM_DDTHH_MM_SSZ)
startTime := date.ParseStrToDate(taskPeriod.StartTime, date.YYYY_MM_DDTHH_MM_SSZ)
endTime := date.ParseStrToDate(taskPeriod.EndTime, date.YYYY_MM_DDTHH_MM_SSZ)
granularity := parse.Number(endTime.Sub(startTime).Seconds())
// kpi data数据
KpiValues := make(map[string]float64, 0)
for _, v := range taskNeKPIs {
KpiValues[v.KPIID] = float64(v.Value)
}
// 创建KPI
kpi := oam.KPI{
NeUid: body.Task.NE.RmUID, // 网元唯一标识
RecordTime: receiverTime.UnixMilli(), // 记录时间 时间戳毫秒Push时自动填充
Granularity: granularity, // 时间间隔 5/10/.../60/300 (秒)
Data: KpiValues, // 指标信息
}
if err := oamService.NewKPI.Resolve(kpi); err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// ResolveNBState 接收基站状态变更
//
// POST /ueManagement/v1/elementType/:elementTypeValue/objectType/nbState
func (s APIRestController) ResolveNBState(c *gin.Context) {
var body struct {
NeType string `json:"neType" `
NeName string `json:"neName" `
RmUID string `json:"rmUID"`
StateList []struct {
Address string `json:"address" `
Name string `json:"name" `
Position string `json:"position" `
NbName string `json:"nbName" `
State string `json:"state" ` // "OFF" or "ON"
OffTime string `json:"offTime" ` //if State=OFF, will set it
OnTime string `json:"onTime" ` //if State=ON , will set it
}
}
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
elementTypeValue := c.Param("elementTypeValue")
if !strings.EqualFold(body.NeType, elementTypeValue) {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "elementType is inconsistent with neType"))
return
}
if len(body.StateList) == 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "no stateList"))
return
}
nbStateArr := make([]oam.NBState, 0)
for _, v := range body.StateList {
if v.Address == "" || v.State == "" {
continue
}
stateTime := date.ParseStrToDate(v.OffTime, time.RFC3339)
stateStr := oam.NB_STATE_OFF
if v.State == "ON" {
stateTime = date.ParseStrToDate(v.OnTime, time.RFC3339)
stateStr = oam.NB_STATE_ON
}
// 创建NbState
nbState := oam.NBState{
NeUid: body.RmUID, // 网元唯一标识
RecordTime: time.Now().UnixMilli(), // 记录时间 时间戳毫秒Push时自动填充
Address: v.Address, // 基站地址
DeviceName: v.NbName, // 基站设备名称
State: stateStr, // 基站状态 ON/OFF
StateTime: stateTime.UnixMilli(), // 基站状态时间 时间戳毫秒
Name: v.Name, // 基站名称 网元标记
Position: v.Position, // 基站位置 网元标记
}
nbStateArr = append(nbStateArr, nbState)
}
errArr := make([]string, 0)
for _, nbState := range nbStateArr {
if err := oamService.NewNBState.Resolve(nbState); err != nil {
errArr = append(errArr, err.Error())
}
}
if len(errArr) > 0 {
c.JSON(200, resp.ErrData(errArr))
return
}
c.JSON(200, resp.Ok(nil))
}
// ResolveUENB 接收终端接入基站
//
// POST /logManagement/v1/elementType/:elementTypeValue/objectType/ueEvent
func (s APIRestController) ResolveUENB(c *gin.Context) {
var body struct {
NeType string `json:"neType" `
NeName string `json:"neName" `
RmUID string `json:"rmUID" `
Timestamp int64 `json:"timestamp" `
EventType string `json:"eventType" `
EventJson map[string]any `json:"eventJSON" `
}
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
elementTypeValue := c.Param("elementTypeValue")
if !strings.EqualFold(body.NeType, elementTypeValue) {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "elementType is inconsistent with neType"))
return
}
// 记录时间
recordTime := time.Now()
if body.Timestamp > 1e12 {
recordTime = time.UnixMilli(int64(body.Timestamp))
} else if body.Timestamp > 1e9 {
recordTime = time.Unix(int64(body.Timestamp), 0)
}
// 创建UENB
uenb := oam.UENB{
NeUid: body.RmUID, // 网元唯一标识
RecordTime: recordTime.UnixMilli(), // 记录时间
NBId: "0", // 基站ID
CellId: "0", // 小区ID
TAC: "", // TAC
IMSI: "", // IMSI
Result: oam.UENB_RESULT_AUTH_SUCCESS, // 结果值
Type: oam.UENB_TYPE_DETACH, // 终端接入基站类型
}
// 基站ID
if v, ok := body.EventJson["eNBID"]; ok && v != nil {
uenb.NBId = fmt.Sprint(v)
}
if v, ok := body.EventJson["gNBID"]; ok && v != nil {
uenb.NBId = fmt.Sprint(v)
}
// 小区ID
if v, ok := body.EventJson["cellID"]; ok && v != nil {
uenb.CellId = fmt.Sprint(v)
}
// TAC
if v, ok := body.EventJson["tacID"]; ok && v != nil {
uenb.TAC = fmt.Sprint(v)
}
// IMSI
if v, ok := body.EventJson["imsi"]; ok && v != nil {
uenb.IMSI = fmt.Sprint(v)
}
// 结果值
if v, ok := body.EventJson["result"]; ok && v != nil {
uenb.Result = fmt.Sprint(v)
}
// 终端接入基站类型
if v, ok := body.EventJson["type"]; ok && v != nil {
switch v := fmt.Sprint(v); v {
case "detach":
uenb.Type = oam.UENB_TYPE_DETACH
case "auth-result":
uenb.Type = oam.UENB_TYPE_AUTH
case "cm-state":
uenb.Type = oam.UENB_TYPE_CM
}
}
if err := oamService.NewUENB.Resolve(uenb); err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// ResolveUENBByAMF 接收终端接入基站-AMF
//
// POST /upload-ue/v1/:eventType
func (s APIRestController) ResolveUENBByAMF(c *gin.Context) {
var body map[string]any
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
// 创建UENB
uenb := oam.UENB{
NeUid: "4400HXAMF001", // 网元唯一标识
RecordTime: 0, // 记录时间
NBId: "0", // 基站ID
CellId: "0", // 小区ID
TAC: "", // TAC
IMSI: "", // IMSI
Result: oam.UENB_RESULT_AUTH_SUCCESS, // 结果值
Type: oam.UENB_TYPE_DETACH, // 终端接入基站类型
}
// 从eventJson中获取rmUID
if v, ok := body["rmUID"]; ok {
uenb.NeUid = fmt.Sprint(v)
}
// 统一格式
eventType := c.Param("eventType")
switch eventType {
case "auth-result":
// {"authCode":"200","authMessage":"成功","authTime":"2024-12-07 16:48:37","cellID":"3","gNBID":"1","imsi":"460002082100000","onlineNumber":1,"tacID":"81"}
if v, ok := body["imsi"]; ok {
uenb.IMSI = fmt.Sprint(v)
}
if v, ok := body["cellID"]; ok {
uenb.CellId = fmt.Sprint(v)
}
if v, ok := body["gNBID"]; ok {
uenb.NBId = fmt.Sprint(v)
}
if v, ok := body["tacID"]; ok {
uenb.TAC = fmt.Sprint(v)
}
if v, ok := body["authCode"]; ok {
uenb.Result = fmt.Sprint(v)
}
if v, ok := body["authTime"]; ok {
authTime := date.ParseStrToDate(fmt.Sprint(v), date.YYYY_MM_DD_HH_MM_SS)
uenb.RecordTime = authTime.UnixMilli()
}
uenb.Type = oam.UENB_TYPE_AUTH
case "detach":
// {"detachResult":0,"detachTime":"2024-12-07 18:00:47","imsi":"460002082100000"}
if v, ok := body["imsi"]; ok {
uenb.IMSI = fmt.Sprint(v)
}
if v, ok := body["detachResult"]; ok {
if v == "0" {
uenb.Result = oam.UENB_RESULT_AUTH_SUCCESS
} else {
uenb.Result = fmt.Sprint(v)
}
}
if v, ok := body["detachTime"]; ok {
detachTime := date.ParseStrToDate(fmt.Sprint(v), date.YYYY_MM_DD_HH_MM_SS)
uenb.RecordTime = detachTime.UnixMilli()
}
uenb.Type = oam.UENB_TYPE_DETACH
case "cm-state":
// {"changeTime":"2024-12-07 17:07:52","imsi":"460002082100000","onlineNumber":1,"status":2}
if v, ok := body["imsi"]; ok {
uenb.IMSI = fmt.Sprint(v)
}
if v, ok := body["status"]; ok {
uenb.Result = fmt.Sprint(v)
}
if v, ok := body["changeTime"]; ok {
changeTime := date.ParseStrToDate(fmt.Sprint(v), date.YYYY_MM_DD_HH_MM_SS)
uenb.RecordTime = changeTime.UnixMilli()
}
uenb.Type = oam.UENB_TYPE_CM
}
if err := oamService.NewUENB.Resolve(uenb); err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
c.JSON(200, resp.Ok(nil))
}
// ResolveAlarmHistory 拉取告警历史
//
// GET /faultManagement/v1/elementType/:elementTypeValue/objectType/alarms
func (s APIRestController) ResolveAlarmHistory(c *gin.Context) {
elementTypeValue := c.Param("elementTypeValue")
// Get alarms from OMC return 204
if strings.ToLower(elementTypeValue) == "omc" {
c.JSON(200, resp.OkMsg("omc alarms no content"))
return
}
// alarmTypeValue 映射值
alarmTypeValue := func(str string) string {
arr := []string{
oam.ALARM_TYPE_COMMUNICATION_ALARM,
oam.ALARM_TYPE_EQUIPMENT_ALARM,
oam.ALARM_TYPE_PROCESSING_FAILURE,
oam.ALARM_TYPE_ENVIRONMENTAL_ALARM,
oam.ALARM_TYPE_QUALITY_OF_SERVICE_ALARM,
}
for k, v := range arr {
if v == str {
return v
}
if fmt.Sprint(k+1) == str {
return v
}
}
return str
}
// origSeverityValue 映射值
origSeverityValue := func(str string) string {
arr := []string{
oam.ALARM_SEVERITY_CRITICAL,
oam.ALARM_SEVERITY_MAJOR,
oam.ALARM_SEVERITY_MINOR,
oam.ALARM_SEVERITY_WARNING,
oam.ALARM_SEVERITY_EVENT,
}
for k, v := range arr {
if v == str {
return v
}
if fmt.Sprint(k+1) == str {
return v
}
}
return str
}
// alarmStatusValue 映射值
alarmStatusValue := func(value int) string {
arr := []string{
oam.ALARM_STATUS_CLEAR,
oam.ALARM_STATUS_ACTIVE,
}
for k, v := range arr {
if k == value {
return v
}
}
return oam.ALARM_STATUS_ACTIVE
}
alarmArr := make([]oam.Alarm, 0)
type body struct {
AlarmSeq int `json:"alarmSeq"`
AlarmId string `json:"alarmId"`
NeId string `json:"neId"` // 收到实际是rmUID
AlarmCode int `json:"alarmCode"`
AlarmTitle string `json:"alarmTitle"`
EventTime string `json:"eventTime"`
AlarmType string `json:"alarmType"`
OrigSeverity string `json:"origSeverity"`
PerceivedSeverity string `json:"perceivedSeverity"`
PVFlag string `json:"pvFlag"`
NeName string `json:"neName"`
NeType string `json:"neType"`
ObjectUid string `json:"objectUid"`
ObjectName string `json:"objectName"`
ObjectType string `json:"objectType"`
LocationInfo string `json:"locationInfo"`
Province string `json:"province"`
AlarmStatus int `json:"alarmStatus"`
SpecificProblem string `json:"specificProblem"`
SpecificProblemID string `json:"specificProblemID"`
AddInfo string `json:"addInfo"`
}
parseItem := func(v body) oam.Alarm {
// 产生时间
eventTime := date.ParseStrToDate(v.EventTime, time.RFC3339)
// 创建告警
alarm := oam.Alarm{
NeUid: v.NeId, // 网元唯一标识
AlarmTime: eventTime.UnixMilli(), // 事件产生时间
AlarmId: v.AlarmId, // 告警ID 唯一,清除时对应
AlarmCode: v.AlarmCode, // 告警状态码
AlarmType: alarmTypeValue(v.AlarmType), // 告警类型
AlarmTitle: v.AlarmTitle, // 告警标题
PerceivedSeverity: origSeverityValue(v.OrigSeverity), // 告警级别
AlarmStatus: alarmStatusValue(v.AlarmStatus), // 告警状态
SpecificProblem: v.SpecificProblem, // 告警问题原因
SpecificProblemID: v.SpecificProblemID, // 告警问题原因ID
AddInfo: v.AddInfo, // 告警辅助信息
LocationInfo: v.LocationInfo, // 告警定位信息
}
return alarm
}
var neInfos []neModel.NeInfo
if elementTypeValue == "all" {
neInfos = neService.NewNeInfo.Find(neModel.NeInfo{}, false, false)
} else {
neInfos = neService.NewNeInfo.FindByNeType(strings.ToUpper(elementTypeValue))
}
for _, neInfo := range neInfos {
data, err := neFetchlink.AlarmHistory(neInfo)
if err != nil {
logger.Errorf("failed to fetch alarm history:%s", err.Error())
continue
}
if len(data) == 0 {
logger.Warnf("not found sync alarms %s", neInfo.RmUID)
continue
}
bodyArr := make([]body, 0)
// 将 []map[string]any 序列化为 JSON 字符串
jsonData, err := json.Marshal(data)
if err != nil {
logger.Errorf("marshal error: %s", err.Error())
continue
}
// 反序列化到结构体
err = json.Unmarshal(jsonData, &bodyArr)
if err != nil {
logger.Errorf("Error unmarshal error: %s", err.Error())
continue
}
for _, v := range bodyArr {
alarmArr = append(alarmArr, parseItem(v))
}
}
errArr := make([]string, 0)
for _, alarm := range alarmArr {
if err := oamService.NewAlarm.Resolve(alarm); err != nil {
errArr = append(errArr, err.Error())
}
}
if len(errArr) > 0 {
c.JSON(200, resp.OkData(errArr))
return
}
c.JSON(200, resp.Ok(nil))
}
// QuerySystemState 查询系统状态
//
// GET /systemManagement/v1/elementType/:elementTypeValue/objectType/systemState
func (s APIRestController) QuerySystemState(c *gin.Context) {
elementTypeValue := c.Param("elementTypeValue")
if strings.ToLower(elementTypeValue) != "omc" {
c.JSON(200, resp.ErrMsg("elementType only omc"))
return
}
info := goOamState.NewState.Info()
info.SerialNum = "-"
info.ExpiryDate = "-"
info.Capability = 50
info.Version = "config.Version"
c.JSON(200, info)
}
// NeConfigOMC 网元配置对端网管信息
//
// PUT /systemManagement/v1/elementType/:elementTypeValue/objectType/config/omcNeConfig
func (s APIRestController) NeConfigOMC(c *gin.Context) {
c.JSON(204, nil)
}
// @Description CBSManagement CB消息
type CBSState struct {
NeName string `json:"neName"` // 网元名称
RmUID string `json:"rmUID"` // 网元唯一标识
EventData []oamService.CBSEventData `json:"eventData"` // 事件数据
}
func (s APIRestController) ResolveCBSState(c *gin.Context) {
var state CBSState
if err := c.ShouldBindBodyWithJSON(&state); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
for _, eventData := range state.EventData {
if err := oamService.NewCBS.Resolve(eventData); err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
}
c.JSON(200, resp.Ok(nil))
}

42
src/modules/oam/oam.go Normal file
View File

@@ -0,0 +1,42 @@
package oam
import (
"github.com/gin-gonic/gin"
"github.com/tsmask/go-oam"
"be.ems/src/framework/logger"
"be.ems/src/modules/oam/service"
)
// Setup 模块路由注册
func Setup(router *gin.Engine) {
logger.Infof("开始加载 ====> oam 模块路由")
// 网管接收端收告警
oam.AlarmReceiveRoute(router, service.NewAlarm.Resolve)
// 网管接收端收终端接入基站
oam.UENBReceiveRoute(router, service.NewUENB.Resolve)
// 网管接收端收基站状态
oam.NBStateReceiveRoute(router, service.NewNBState.Resolve)
// 网管接收端收话单
oam.CDRReceiveRoute(router, service.NewCDR.Resolve)
// 网管接收端收KPI
oam.KPIReceiveRoute(router, service.NewKPI.Resolve)
// APIRest 北向定义
// aprRest := controller.NewAPIRest
// aprRestGroup := router.Group("/api/rest")
// {
// aprRestGroup.GET("/faultManagement/v1/elementType/:elementTypeValue/objectType/alarms", aprRest.ResolveAlarmHistory)
// aprRestGroup.POST("/faultManagement/v1/elementType/:elementTypeValue/objectType/alarms", aprRest.ResolveAlarm)
// aprRestGroup.POST("/cdrManagement/v1/elementType/:elementTypeValue/objectType/cdrEvent", aprRest.ResolveCDR)
// aprRestGroup.POST("/performanceManagement/v1/elementType/:elementTypeValue/objectType/kpiReport/:index", aprRest.ResolveKPI)
// aprRestGroup.POST("/ueManagement/v1/elementType/:elementTypeValue/objectType/nbState", aprRest.ResolveNBState)
// aprRestGroup.POST("/ueManagement/v1/elementType/:elementTypeValue/objectType/cbsState", aprRest.ResolveCBSState)
// aprRestGroup.POST("/logManagement/v1/elementType/:elementTypeValue/objectType/ueEvent", aprRest.ResolveUENB)
// router.POST("/upload-ue/v1/:eventType", aprRest.ResolveUENBByAMF) // AMF特殊上报
// aprRestGroup.GET("/systemManagement/v1/elementType/:elementTypeValue/objectType/systemState", aprRest.QuerySystemState)
// aprRestGroup.PUT("/systemManagement/v1/elementType/:elementTypeValue/objectType/config/omcNeConfig", aprRest.NeConfigOMC)
// }
}

View File

@@ -0,0 +1,301 @@
package service
import (
"fmt"
"time"
"be.ems/src/framework/config"
"be.ems/src/framework/constants"
"be.ems/src/framework/utils/parse"
"github.com/tsmask/go-oam"
neDataModel "be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neService "be.ems/src/modules/network_element/service"
notificationService "be.ems/src/modules/notification/service"
traceService "be.ems/src/modules/trace/service"
wsService "be.ems/src/modules/ws/service"
)
// 实例化服务层 Alarm 结构体
var NewAlarm = &Alarm{
neInfoService: neService.NewNeInfo,
wsService: wsService.NewWSSend,
alarmService: neDataService.NewAlarm,
alarmEventService: neDataService.NewAlarmEvent,
alarmLogService: neDataService.NewAlarmLog,
alarmForwardLogService: neDataService.NewAlarmForwardLog,
}
// Alarm 消息处理
type Alarm struct {
neInfoService *neService.NeInfo
wsService *wsService.WSSend
alarmService *neDataService.Alarm
alarmEventService *neDataService.AlarmEvent
alarmLogService *neDataService.AlarmLog
alarmForwardLogService *neDataService.AlarmForwardLog
}
// Resolve 接收处理
func (s *Alarm) Resolve(a oam.Alarm) error {
// 是否存在网元
neInfo := s.neInfoService.FindByRmuid(a.NeUid)
if neInfo.NeType == "" || neInfo.RmUID != a.NeUid {
return fmt.Errorf("resolve alarm network element does not exist %s", a.NeUid)
}
// seq 告警序号
lastSeq := neDataService.NewAlarm.FindAlarmSeqLast(neInfo.NeType, neInfo.NeId)
alarmTime := time.UnixMilli(a.AlarmTime)
// 告警信息
alarm := neDataModel.Alarm{
NeType: neInfo.NeType,
NeId: neInfo.NeId,
NeName: neInfo.NeName,
Province: neInfo.Province,
PvFlag: neInfo.PvFlag,
AlarmSeq: fmt.Sprintf("%d", lastSeq+1),
AlarmId: a.AlarmId,
AlarmTitle: a.AlarmTitle,
AlarmCode: fmt.Sprintf("%d", a.AlarmCode),
EventTime: alarmTime,
AlarmType: a.AlarmType,
OrigSeverity: a.PerceivedSeverity,
PerceivedSeverity: a.PerceivedSeverity,
ObjectUid: neInfo.RmUID,
ObjectName: neInfo.NeName,
ObjectType: neInfo.NeType,
LocationInfo: a.LocationInfo,
AlarmStatus: a.AlarmStatus,
SpecificProblem: a.SpecificProblem,
SpecificProblemId: a.SpecificProblemID,
AddInfo: a.AddInfo,
}
// 进行清除
if a.AlarmStatus == oam.ALARM_STATUS_CLEAR {
if a.PerceivedSeverity == oam.ALARM_SEVERITY_EVENT {
if err := s.clearEvent(alarm); err != nil {
return err
}
} else {
if err := s.clear(alarm); err != nil {
return err
}
}
}
// 进行新增
if a.AlarmStatus == oam.ALARM_STATUS_ACTIVE {
if a.PerceivedSeverity == oam.ALARM_SEVERITY_EVENT {
if err := s.addEvent(alarm); err != nil {
return err
}
} else {
if err := s.add(alarm); err != nil {
return err
}
}
}
// 记录日志
if err := s.saveLog(alarm); err != nil {
return err
}
// 推送
s.wsService.ByGroupID(fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId), alarm)
// 通知
go s.notify(neInfo.IP, alarm)
return nil
}
// saveLog 记录日志
func (s *Alarm) saveLog(alarm neDataModel.Alarm) error {
alarmLog := neDataModel.AlarmLog{
NeType: alarm.NeType,
NeId: alarm.NeId,
AlarmSeq: alarm.AlarmSeq,
AlarmId: alarm.AlarmId,
AlarmTitle: alarm.AlarmTitle,
AlarmCode: alarm.AlarmCode,
AlarmStatus: alarm.AlarmStatus,
AlarmType: alarm.AlarmType,
OrigSeverity: alarm.PerceivedSeverity,
EventTime: alarm.EventTime,
}
insertId := s.alarmLogService.Insert(alarmLog)
if insertId <= 0 {
return fmt.Errorf("save alarm log fail")
}
return nil
}
// add 新增告警
func (s *Alarm) add(alarm neDataModel.Alarm) error {
// 检查网元告警ID是否唯一
alarmIdArr := s.alarmService.Find(neDataModel.Alarm{
NeType: alarm.NeType,
NeId: alarm.NeId,
AlarmId: alarm.AlarmId,
})
if len(alarmIdArr) > 0 {
return fmt.Errorf("already exists alarmId:%s", alarm.AlarmId)
}
insertId := s.alarmService.Insert(alarm)
if insertId != "" {
alarm.ID = insertId
return nil
}
return fmt.Errorf("add alarm fail")
}
// clear 清除告警
func (s *Alarm) clear(alarm neDataModel.Alarm) error {
// 检查网元告警ID是否唯一
alarmIdArr := s.alarmService.Find(neDataModel.Alarm{
NeType: alarm.NeType,
NeId: alarm.NeId,
AlarmId: alarm.AlarmId,
})
if len(alarmIdArr) != 1 {
return fmt.Errorf("not exists alarmId:%s", alarm.AlarmId)
}
// 告警清除
rows, _ := s.alarmService.ClearByIds([]string{alarmIdArr[0].ID}, alarm.ObjectUid, constants.ALARM_CLEAR_TYPE_AUTO_CLEAR)
if rows > 0 {
return nil
}
return fmt.Errorf("clear fail alarmId:%s", alarm.AlarmId)
}
// addEvent 新增告警事件
func (s *Alarm) addEvent(alarm neDataModel.Alarm) error {
// 检查网元告警ID是否唯一
alarmIdArr := s.alarmEventService.Find(neDataModel.AlarmEvent{
NeType: alarm.NeType,
NeId: alarm.NeId,
AlarmId: alarm.AlarmId,
})
if len(alarmIdArr) > 0 {
return fmt.Errorf("event already exists alarmId:%s", alarm.AlarmId)
}
// seq 告警序号
lastSeq := s.alarmEventService.FindAlarmEventSeqLast(alarm.NeType, alarm.NeId)
alarmEvent := neDataModel.AlarmEvent{
NeType: alarm.NeType,
NeId: alarm.NeId,
AlarmSeq: fmt.Sprintf("%d", lastSeq+1),
AlarmId: alarm.AlarmId,
AlarmTitle: alarm.AlarmTitle,
AlarmCode: alarm.AlarmCode,
EventTime: alarm.EventTime,
ObjectUid: alarm.ObjectUid,
ObjectName: alarm.ObjectName,
ObjectType: alarm.ObjectType,
LocationInfo: alarm.LocationInfo,
AlarmStatus: alarm.AlarmStatus,
SpecificProblem: alarm.SpecificProblem,
SpecificProblemId: alarm.SpecificProblemId,
AddInfo: alarm.AddInfo,
}
insertId := s.alarmEventService.Insert(alarmEvent)
if insertId > 0 {
alarmEvent.ID = insertId
// 网元重启后,清除活动告警
if alarm.AlarmCode == fmt.Sprintf("%d", constants.ALARM_EVENT_REBOOT) {
rows := s.alarmService.Find(neDataModel.Alarm{
NeType: alarm.NeType,
NeId: alarm.NeId,
AlarmStatus: oam.ALARM_STATUS_ACTIVE,
})
ids := make([]string, 0)
for _, v := range rows {
ids = append(ids, v.ID)
}
s.alarmService.ClearByIds(ids, alarm.ObjectUid, constants.ALARM_CLEAR_TYPE_AUTO_CLEAR)
}
// 网元重启后,有跟踪任务的需要重新补发启动任务
if alarm.AlarmCode == fmt.Sprintf("%d", constants.ALARM_EVENT_REBOOT) {
traceService.NewTraceTask.RunUnstopped(alarm.NeType, alarm.NeId)
}
return nil
}
return fmt.Errorf("event add fail")
}
// clearEvent 清除告警事件
func (s *Alarm) clearEvent(alarm neDataModel.Alarm) error {
alarmEventService := neDataService.NewAlarmEvent
// 检查网元告警ID是否唯一
alarmIdArr := alarmEventService.Find(neDataModel.AlarmEvent{
NeType: alarm.NeType,
NeId: alarm.NeId,
AlarmId: alarm.AlarmId,
})
if len(alarmIdArr) != 1 {
return fmt.Errorf("event not exists alarmId:%s", alarm.AlarmId)
}
// 告警清除
rows, _ := s.alarmEventService.ClearByIds([]int64{alarmIdArr[0].ID}, alarm.ObjectUid, constants.ALARM_CLEAR_TYPE_AUTO_CLEAR)
if rows > 0 {
return nil
}
return fmt.Errorf("event clear fail alarmId:%s", alarm.AlarmId)
}
// notify 通知
func (s *Alarm) notify(neIp string, alarm neDataModel.Alarm) {
// 邮箱
emailEnable := parse.Boolean(config.Get("notification.email.enable"))
if emailEnable {
emailList := fmt.Sprint(config.Get("notification.email.emailList"))
emailResult := "Sent Successfully!"
emailErr := notificationService.EmailAlarm(alarm, neIp)
if emailErr != nil {
emailResult = emailErr.Error()
}
s.notifyLog(alarm, "EMAIL", emailList, emailResult)
}
// 短信
smscEnable := parse.Boolean(config.Get("notification.smsc.enable"))
if smscEnable {
mobileList := fmt.Sprint(config.Get("notification.smsc.mobileList"))
smscResult := "Sent Successfully!"
smscErr := notificationService.SMSCAlarm(alarm, neIp)
if smscErr != nil {
smscResult = smscErr.Error()
}
s.notifyLog(alarm, "SMSC", mobileList, smscResult)
}
}
// notifyLog 通知日志
func (s *Alarm) notifyLog(alarm neDataModel.Alarm, forwardBy, toUser, result string) error {
alarmForwardLog := neDataModel.AlarmForwardLog{
NeType: alarm.NeType,
NeId: alarm.NeId,
AlarmSeq: alarm.AlarmSeq,
AlarmId: alarm.AlarmId,
AlarmTitle: alarm.AlarmTitle,
AlarmCode: alarm.AlarmCode,
AlarmStatus: alarm.AlarmStatus,
AlarmType: alarm.AlarmType,
OrigSeverity: alarm.OrigSeverity,
EventTime: alarm.EventTime,
Type: forwardBy,
Target: toUser,
Result: result,
}
// 记录日志
insertId := s.alarmForwardLogService.Insert(alarmForwardLog)
if insertId <= 0 {
return fmt.Errorf("notify alarm log fail")
}
return nil
}

View File

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

View File

@@ -0,0 +1,71 @@
package service
import (
"encoding/json"
"fmt"
"github.com/tsmask/go-oam"
neDataModel "be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neService "be.ems/src/modules/network_element/service"
wsService "be.ems/src/modules/ws/service"
)
// 实例化服务层 CDR 结构体
var NewCDR = &CDR{
neInfoService: neService.NewNeInfo,
wsService: wsService.NewWSSend,
cdrEventService: neDataService.NewCDREvent,
}
// CDR 消息处理
type CDR struct {
neInfoService *neService.NeInfo
wsService *wsService.WSSend
cdrEventService *neDataService.CDREvent // CDR会话事件服务
}
// Resolve 接收处理
func (s *CDR) Resolve(c oam.CDR) error {
if c.Data == nil {
return fmt.Errorf("cdr data is nil")
}
// 是否存在网元
neInfo := s.neInfoService.FindByRmuid(c.NeUid)
if neInfo.NeType == "" || neInfo.RmUID != c.NeUid {
return fmt.Errorf("resolve cdr network element does not exist %s", c.NeUid)
}
cdrByte, _ := json.Marshal(c.Data)
cdrEvent := neDataModel.CDREvent{
NeType: neInfo.NeType,
NeName: neInfo.NeName,
RmUid: neInfo.RmUID,
Timestamp: c.RecordTime,
CdrJson: string(cdrByte),
CreatedAt: c.RecordTime,
}
insertId := s.cdrEventService.Insert(cdrEvent)
if insertId <= 0 {
return fmt.Errorf("add cdr data fail")
}
cdrEvent.ID = insertId
// 推送到ws订阅组
switch neInfo.NeType {
case "IMS":
dataMap := c.Data.(map[string]any)
v, ok := dataMap["recordType"]
if ok && (v == "MOC" || v == "MTSM") {
s.wsService.ByGroupID(fmt.Sprintf("%s_%s", wsService.GROUP_IMS_CDR, neInfo.NeId), cdrEvent)
}
case "SMF":
s.wsService.ByGroupID(fmt.Sprintf("%s_%s", wsService.GROUP_SMF_CDR, neInfo.NeId), cdrEvent)
case "SMSC":
s.wsService.ByGroupID(fmt.Sprintf("%s_%s", wsService.GROUP_SMSC_CDR, neInfo.NeId), cdrEvent)
case "SGWC":
s.wsService.ByGroupID(fmt.Sprintf("%s_%s", wsService.GROUP_SGWC_CDR, neInfo.NeId), cdrEvent)
}
return nil
}

View File

@@ -0,0 +1,241 @@
package service
import (
"encoding/json"
"fmt"
"math"
"time"
"be.ems/src/framework/utils/date"
"be.ems/src/framework/utils/expr"
"be.ems/src/framework/utils/parse"
"github.com/tsmask/go-oam"
neDataModel "be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neModel "be.ems/src/modules/network_element/model"
neService "be.ems/src/modules/network_element/service"
wsService "be.ems/src/modules/ws/service"
)
// 实例化服务层 KPI 结构体
var NewKPI = &KPI{
neInfoService: neService.NewNeInfo,
wsService: wsService.NewWSSend,
kpiReportService: neDataService.NewKpiReport,
kpiCReportService: neDataService.NewKpiCReport,
}
// KPI 消息处理
type KPI struct {
neInfoService *neService.NeInfo
wsService *wsService.WSSend
kpiReportService *neDataService.KpiReport
kpiCReportService *neDataService.KpiCReport
}
// Resolve 接收处理
func (s *KPI) Resolve(k oam.KPI) error {
if len(k.Data) == 0 {
return fmt.Errorf("kpi data is nil")
}
// 是否存在网元
neInfo := s.neInfoService.FindByRmuid(k.NeUid)
if neInfo.NeType == "" || neInfo.RmUID != k.NeUid {
return fmt.Errorf("resolve kpi network element does not exist %s", k.NeUid)
}
// 时间片
curTime := time.Now()
curSeconds := curTime.Hour()*3600 + curTime.Minute()*60 + curTime.Second()
index := int64(curSeconds) / k.Granularity
if err := s.saveKPIData(neInfo, k, index); err != nil {
return err
}
if err := s.saveKPIDataC(neInfo, k, index); err != nil {
return err
}
return nil
}
// saveKPIData 存储KPI数据并推送到ws订阅组
func (s KPI) saveKPIData(neInfo neModel.NeInfo, k oam.KPI, index int64) error {
// 时间数据处理
recordTime := time.Now()
if k.RecordTime > 1e12 {
recordTime = time.UnixMilli(k.RecordTime)
} else if k.RecordTime > 1e9 {
recordTime = time.Unix(k.RecordTime, 0)
}
recordDate := date.ParseDateToStr(recordTime, "2006-01-02")
recordEndTime := date.ParseDateToStr(recordTime, "15:04:05")
startTime := recordTime.Add(-time.Duration(k.Granularity) * time.Second)
recordStartTime := date.ParseDateToStr(startTime, "15:04:05")
// kpi data数据json
kpiTitles := s.kpiReportService.FindTitle(neInfo.NeType)
KpiValues := make([]map[string]any, 0)
for _, kt := range kpiTitles {
item := map[string]any{
"kpiId": kt.KpiId,
"value": 0,
"err": "",
}
// 匹配指标记录
for k, v := range k.Data {
if k == kt.KpiId {
item["value"] = v
}
}
KpiValues = append(KpiValues, item)
}
KpiValuesByte, err := json.Marshal(KpiValues)
if err != nil {
return err
}
// KPI 信息
kpiData := neDataModel.KpiReport{
NeType: neInfo.NeType,
NeName: neInfo.NeName,
RmUid: neInfo.RmUID,
Date: recordDate,
StartTime: recordStartTime,
EndTime: recordEndTime,
Index: index,
Granularity: k.Granularity,
KpiValues: string(KpiValuesByte),
CreatedAt: k.RecordTime,
}
insertId := s.kpiReportService.Insert(kpiData)
if insertId <= 0 {
return fmt.Errorf("add kpi data fail")
}
kpiData.ID = insertId
// 指标事件对象
data := map[string]any{
"neType": kpiData.NeType,
"neName": kpiData.NeName,
"rmUID": kpiData.RmUid,
"startIndex": kpiData.Index,
"timeGroup": kpiData.CreatedAt,
// kip_id ...
}
for _, v := range KpiValues {
data[fmt.Sprint(v["kpiId"])] = v["value"]
}
// 推送到ws订阅组
s.wsService.ByGroupID(fmt.Sprintf("%s_%s_%s", wsService.GROUP_KPI, neInfo.NeType, neInfo.NeId), data)
// 更新UPF总流量
if neInfo.NeType == "UPF" {
upValue := parse.Number(data["UPF.03"])
downValue := parse.Number(data["UPF.06"])
s.kpiReportService.UPFTodayFlowUpdate(neInfo.RmUID, upValue, downValue)
}
return nil
}
// saveKPIDataC 存储自定义KPI数据并推送到ws订阅组
func (s KPI) saveKPIDataC(neInfo neModel.NeInfo, k oam.KPI, index int64) error {
// 时间数据处理
recordTime := time.Now()
if k.RecordTime > 1e12 {
recordTime = time.UnixMilli(k.RecordTime)
} else if k.RecordTime > 1e9 {
recordTime = time.Unix(k.RecordTime, 0)
}
recordDate := date.ParseDateToStr(recordTime, "2006-01-02")
recordEndTime := date.ParseDateToStr(recordTime, "15:04:05")
startTime := recordTime.Add(-time.Duration(k.Granularity) * time.Second)
recordStartTime := date.ParseDateToStr(startTime, "15:04:05")
// kpi data数据json
kpiCTitles := s.kpiCReportService.FindTitle(neInfo.NeType)
KpiValues := make([]map[string]any, 0)
// 自定义指标的表达式环境变量
KpiExprEnv := make(map[string]any, 0)
for k, v := range k.Data {
KpiExprEnv[k] = v
}
// 自定义指标的计算
for _, v := range kpiCTitles {
item := map[string]any{
"kpiId": v.KpiId,
"value": 0,
"err": "",
}
// 匹配指标记录
if envValue, envOk := KpiExprEnv[v.KpiId]; envOk {
item["value"] = envValue
}
// 计算结果
exprStr, exprEnv := expr.ParseExprEnv(v.Expression, KpiExprEnv)
result, err := expr.Eval(exprStr, exprEnv)
if err != nil {
item["value"] = 0
item["err"] = err.Error()
} else {
if v.Unit == "%" {
resultV, ok := result.(float64)
if !ok || math.IsNaN(resultV) {
resultV = 0
}
if resultV > 100 {
result = 100
}
if resultV <= 0 {
result = 0
}
}
item["value"] = result
}
KpiValues = append(KpiValues, item)
}
KpiValuesByte, err := json.Marshal(KpiValues)
if err != nil {
return err
}
// KPI 信息
kpiCData := neDataModel.KpiCReport{
NeType: neInfo.NeType,
NeName: neInfo.NeName,
RmUid: neInfo.RmUID,
Date: recordDate,
StartTime: recordStartTime,
EndTime: recordEndTime,
Index: index,
Granularity: k.Granularity,
KpiValues: string(KpiValuesByte),
CreatedAt: k.RecordTime,
}
insertId := s.kpiCReportService.Insert(kpiCData)
if insertId <= 0 {
return fmt.Errorf("add kpic data fail")
}
kpiCData.ID = insertId
// 指标事件对象
data := map[string]any{
"neType": kpiCData.NeType,
"neName": kpiCData.NeName,
"rmUID": kpiCData.RmUid,
"startIndex": kpiCData.Index,
"timeGroup": kpiCData.CreatedAt,
// kip_id ...
}
for _, v := range KpiValues {
data[fmt.Sprint(v["kpiId"])] = v["value"]
}
// 推送到ws订阅组
s.wsService.ByGroupID(fmt.Sprintf("%s_%s_%s", wsService.GROUP_KPI_C, neInfo.NeType, neInfo.NeId), data)
return nil
}

View File

@@ -0,0 +1,65 @@
package service
import (
"fmt"
"time"
"be.ems/src/framework/utils/date"
"github.com/tsmask/go-oam"
neDataModel "be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neService "be.ems/src/modules/network_element/service"
wsService "be.ems/src/modules/ws/service"
)
// 实例化服务层 NBState 结构体
var NewNBState = &NBState{
neInfoService: neService.NewNeInfo,
wsService: wsService.NewWSSend,
nbStateService: neDataService.NewNBState,
}
// NBState 消息处理
type NBState struct {
neInfoService *neService.NeInfo
wsService *wsService.WSSend
nbStateService *neDataService.NBState
}
// Resolve 接收处理
func (s *NBState) Resolve(n oam.NBState) error {
// 是否存在网元
neInfo := s.neInfoService.FindByRmuid(n.NeUid)
if neInfo.NeType == "" || neInfo.RmUID != n.NeUid {
return fmt.Errorf("resolve nb_state network element does not exist %s", n.NeUid)
}
nbState := neDataModel.NBState{
NeType: neInfo.NeType,
NeId: neInfo.NeId,
RmUid: neInfo.RmUID,
Address: n.Address,
Name: n.Name,
Position: n.Position,
NbName: n.DeviceName,
State: n.State,
Time: date.ParseDateToStr(n.StateTime, time.RFC3339),
}
insertId := s.nbStateService.Insert(nbState)
if insertId <= 0 {
return fmt.Errorf("add nb_state data fail")
}
nbState.ID = insertId
// 推送到ws订阅组
switch neInfo.NeType {
case "AMF":
s.wsService.ByGroupID(wsService.GROUP_AMF_NB, nbState)
s.wsService.ByGroupID(fmt.Sprintf("%s_%s", wsService.GROUP_AMF_NB, neInfo.NeId), nbState)
case "MME":
s.wsService.ByGroupID(wsService.GROUP_MME_NB, nbState)
s.wsService.ByGroupID(fmt.Sprintf("%s_%s", wsService.GROUP_MME_NB, neInfo.NeId), nbState)
}
return nil
}

View File

@@ -0,0 +1,69 @@
package service
import (
"encoding/json"
"fmt"
"github.com/tsmask/go-oam"
neDataModel "be.ems/src/modules/network_data/model"
neDataRepository "be.ems/src/modules/network_data/repository"
neDataService "be.ems/src/modules/network_data/service"
neService "be.ems/src/modules/network_element/service"
wsService "be.ems/src/modules/ws/service"
)
// 实例化服务层 UENB 结构体
var NewUENB = &UENB{
neInfoService: neService.NewNeInfo,
wsService: wsService.NewWSSend,
ueEventService: neDataService.NewUEEvent,
}
// UENB 消息处理
type UENB struct {
neInfoService *neService.NeInfo
wsService *wsService.WSSend
ueEventService *neDataService.UEEvent // UE会话事件服务
}
// Resolve 接收处理
func (s *UENB) Resolve(u oam.UENB) error {
// 是否存在网元
neInfo := s.neInfoService.FindByRmuid(u.NeUid)
if neInfo.NeType == "" || neInfo.RmUID != u.NeUid {
return fmt.Errorf("resolve ue_nb network element does not exist %s", u.NeUid)
}
// 查询租户ID
tenantID, _ := neDataRepository.NewSysTenant.Query(map[string]string{
"imsi": u.IMSI,
})
uenbByte, _ := json.Marshal(u)
uenbEvent := neDataModel.UEEvent{
NeType: neInfo.NeType,
NeName: neInfo.NeName,
RmUID: neInfo.RmUID,
Timestamp: u.RecordTime,
EventType: u.Type,
EventJSONStr: string(uenbByte),
TenantID: fmt.Sprintf("%d", tenantID),
}
insertId := s.ueEventService.Insert(uenbEvent)
if insertId <= 0 {
return fmt.Errorf("add ue_nb data fail")
}
uenbEvent.ID = insertId
// 推送到ws订阅组
switch neInfo.NeType {
case "AMF":
s.wsService.ByGroupID(wsService.GROUP_AMF_UE, uenbEvent)
s.wsService.ByGroupID(fmt.Sprintf("%s_%s", wsService.GROUP_AMF_UE, neInfo.NeId), uenbEvent)
case "MME":
s.wsService.ByGroupID(wsService.GROUP_MME_UE, uenbEvent)
s.wsService.ByGroupID(fmt.Sprintf("%s_%s", wsService.GROUP_MME_UE, neInfo.NeId), uenbEvent)
}
return nil
}