fix: UDM批量操作加载数据根据前缀重载

This commit is contained in:
TsMask
2025-10-11 09:49:49 +08:00
parent 4da212739e
commit 13b2ce3e64
10 changed files with 109 additions and 134 deletions

View File

@@ -124,7 +124,7 @@ func (s *Controller) Add(c *gin.Context) {
var body IMSUser var body IMSUser
err := c.ShouldBindBodyWith(&body, binding.JSON) err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || (len(body.IMSI) < IMSI_MAX_LENGTH && body.Tag == TAG_VoLTE) { if err != nil || (len(body.IMSI) < IMSI_MAX_LENGTH && body.Tag == 1) {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return return
} }
@@ -171,7 +171,7 @@ func (s *Controller) Adds(c *gin.Context) {
var body IMSUser var body IMSUser
err := c.ShouldBindBodyWith(&body, binding.JSON) err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || (len(body.IMSI) < IMSI_MAX_LENGTH && body.Tag == TAG_VoLTE) { if err != nil || (len(body.IMSI) < IMSI_MAX_LENGTH && body.Tag == 1) {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return return
} }
@@ -215,7 +215,7 @@ func (s *Controller) Edit(c *gin.Context) {
var body IMSUser var body IMSUser
err := c.ShouldBindBodyWith(&body, binding.JSON) err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || (len(body.IMSI) < IMSI_MAX_LENGTH && body.Tag == TAG_VoLTE) { if err != nil || (len(body.IMSI) < IMSI_MAX_LENGTH && body.Tag == 1) {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return return
} }
@@ -412,7 +412,14 @@ func (s *Controller) Export(c *gin.Context) {
data := [][]string{} data := [][]string{}
data = append(data, []string{"IMSI", "MSISDN", "Tag", "VNI"}) data = append(data, []string{"IMSI", "MSISDN", "Tag", "VNI"})
for _, v := range rows { for _, v := range rows {
data = append(data, []string{v.IMSI, v.MSISDN, v.Tag, v.VNI}) tag := ""
if v.Tag == 0 {
tag = "VoIP"
}
if v.Tag == 1 {
tag = "VoLTE"
}
data = append(data, []string{v.IMSI, v.MSISDN, tag, v.VNI})
} }
// 输出到文件 // 输出到文件
if err := file.WriterFileCSV(data, filePath); err != nil { if err := file.WriterFileCSV(data, filePath); err != nil {
@@ -425,7 +432,14 @@ func (s *Controller) Export(c *gin.Context) {
// 转换数据 // 转换数据
data := [][]string{} data := [][]string{}
for _, v := range rows { for _, v := range rows {
data = append(data, []string{v.IMSI, v.MSISDN, v.Tag, v.VNI}) tag := ""
if v.Tag == 0 {
tag = "VoIP"
}
if v.Tag == 1 {
tag = "VoLTE"
}
data = append(data, []string{v.IMSI, v.MSISDN, tag, v.VNI})
} }
// 输出到文件 // 输出到文件
if err := file.WriterFileTXTLine(data, ",", filePath); err != nil { if err := file.WriterFileTXTLine(data, ",", filePath); err != nil {

View File

@@ -58,12 +58,12 @@ func ParseCallTag(s string) CallTag {
// @Description IMS用户信息 // @Description IMS用户信息
type IMSUser struct { type IMSUser struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"` // 主键 ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"` // 主键
NeId string `json:"neId" gorm:"column:ne_id"` // UDM网元标识 NeId string `json:"neId" gorm:"column:ne_id"` // UDM网元标识
IMSI string `json:"imsi" gorm:"column:imsi"` // SIM卡/USIM卡ID IMSI string `json:"imsi" gorm:"column:imsi"` // SIM卡/USIM卡ID
MSISDN string `json:"msisdn" gorm:"column:msisdn"` // 用户电话号码 MSISDN string `json:"msisdn" gorm:"column:msisdn"` // 用户电话号码
Tag CallTag `json:"tag" gorm:"column:tag"` // tag: 0=VoIP, 1=VoLTE Tag int `json:"tag" gorm:"column:tag"` // tag: 0=VoIP, 1=VoLTE
VNI string `json:"vni" gorm:"column:vni"` // VNI VNI string `json:"vni" gorm:"column:vni"` // VNI
TenantID string `json:"tenantID" gorm:"column:tenant_id"` TenantID string `json:"tenantID" gorm:"column:tenant_id"`
TenantName string `json:"tenantName" gorm:"-"` TenantName string `json:"tenantName" gorm:"-"`

View File

@@ -2,10 +2,10 @@ package ims_user
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
"be.ems/src/framework/database/redis" "be.ems/src/framework/database/redis"
"be.ems/src/framework/utils/parse"
neService "be.ems/src/modules/network_element/service" neService "be.ems/src/modules/network_element/service"
) )
@@ -20,8 +20,8 @@ type Service struct {
} }
// dataByRedis UDM签约用户 db:0 中 volte:* // dataByRedis UDM签约用户 db:0 中 volte:*
func (r *Service) dataByRedis(imsi, neId string) []VoLTEUser { func (r *Service) dataByRedis(imsi, neId string) []IMSUser {
arr := []VoLTEUser{} arr := []IMSUser{}
key := fmt.Sprintf("volte:%s", imsi) key := fmt.Sprintf("volte:%s", imsi)
source := fmt.Sprintf("UDM_%s", neId) source := fmt.Sprintf("UDM_%s", neId)
@@ -71,12 +71,12 @@ func (r *Service) dataByRedis(imsi, neId string) []VoLTEUser {
if len(impiParts) > 1 { if len(impiParts) > 1 {
vni = impiParts[1] // 输出: ims.mnc001.mcc110.3gppnetwork.org vni = impiParts[1] // 输出: ims.mnc001.mcc110.3gppnetwork.org
} }
a := VoLTEUser{ a := IMSUser{
NeId: neId, NeId: neId,
IMSI: imsi, // volte:360000100000130:8612300000130 IMSI: imsi, // volte:360000100000130:8612300000130
MSISDN: msisdn, // 8612300000130 MSISDN: msisdn, // 8612300000130
Tag: m["tag"], // volte = tag Tag: int(parse.Number(m["tag"])), // volte = tag
VNI: vni, // ims.mnc001.mcc110.3gppnetwork.org VNI: vni, // ims.mnc001.mcc110.3gppnetwork.org
} }
arr = append(arr, a) arr = append(arr, a)
} }
@@ -92,7 +92,7 @@ func (r *Service) ResetData(neId string) int64 {
} }
// ParseInfo 解析单个用户imsi签约信息 data从命令MML得到的结果 // ParseInfo 解析单个用户imsi签约信息 data从命令MML得到的结果
func (r *Service) ParseInfo(imsi, neId string, data map[string]string) VoLTEUser { func (r *Service) ParseInfo(imsi, neId string, data map[string]string) IMSUser {
u := r.volteRepository.SelectByIMSIAndNeID(imsi, neId) u := r.volteRepository.SelectByIMSIAndNeID(imsi, neId)
msisdn := data["msisdn"] msisdn := data["msisdn"]
@@ -104,7 +104,7 @@ func (r *Service) ParseInfo(imsi, neId string, data map[string]string) VoLTEUser
u.NeId = neId u.NeId = neId
u.IMSI = imsi u.IMSI = imsi
u.MSISDN = msisdn u.MSISDN = msisdn
u.Tag = data["volte_tag"] u.Tag = int(parse.Number(data["volte_tag"]))
u.VNI = data["VNI"] u.VNI = data["VNI"]
return u return u
} }
@@ -115,13 +115,13 @@ func (r *Service) SelectPage(query map[string]any) map[string]any {
} }
// SelectList 查询数据库 // SelectList 查询数据库
func (r *Service) SelectList(u VoLTEUser) []VoLTEUser { func (r *Service) SelectList(u IMSUser) []IMSUser {
return r.volteRepository.SelectList(u) return r.volteRepository.SelectList(u)
} }
// Insert 从数据中读取后删除imsi再存入数据库 // Insert 从数据中读取后删除imsi再存入数据库
// imsi长度15ki长度32opc长度0或者32 // imsi长度15ki长度32opc长度0或者32
func (r *Service) Insert(neId string, u VoLTEUser) int64 { func (r *Service) Insert(neId string, u IMSUser) int64 {
uArr := r.dataByRedis(u.IMSI, neId) uArr := r.dataByRedis(u.IMSI, neId)
if len(uArr) > 0 { if len(uArr) > 0 {
r.volteRepository.Delete(u.IMSI, neId) r.volteRepository.Delete(u.IMSI, neId)
@@ -176,33 +176,26 @@ func (r *Service) Delete(neId, imsi string) int64 {
// LoadData 重新加载从imsi开始num的数据 // LoadData 重新加载从imsi开始num的数据
func (r *Service) LoadData(neId, imsiOrMsisdn, num string) { func (r *Service) LoadData(neId, imsiOrMsisdn, num string) {
startIMSIOrMsisdn, _ := strconv.ParseInt(imsiOrMsisdn, 10, 64) // 直接删除前缀的记录
subNum, _ := strconv.ParseInt(num, 10, 64) index := len(imsiOrMsisdn) - len(num) - 1
var i int64 prefix := imsiOrMsisdn[:index]
for i = 0; i < subNum; i++ { r.volteRepository.DeletePrefixByIMSI(neId, prefix)
keyIMSI := fmt.Sprintf("%015d", startIMSIOrMsisdn+i) // 加载数据
if !strings.HasPrefix(imsiOrMsisdn, "0") { arr := r.dataByRedis(neId, prefix+"*")
keyIMSI = fmt.Sprintf("%d", startIMSIOrMsisdn+i) if len(arr) > 0 {
}
// 删除原数据
r.volteRepository.Delete(keyIMSI, neId)
arr := r.dataByRedis(keyIMSI, neId)
if len(arr) < 1 {
continue
}
r.volteRepository.Inserts(arr) r.volteRepository.Inserts(arr)
} }
} }
// ParseCommandParams 解析数据组成命令参数 msisdn=xx,xx=xx,... // ParseCommandParams 解析数据组成命令参数 msisdn=xx,xx=xx,...
func (r *Service) ParseCommandParams(item VoLTEUser) string { func (r *Service) ParseCommandParams(item IMSUser) string {
var conditions []string var conditions []string
if item.MSISDN != "" { if item.MSISDN != "" {
conditions = append(conditions, fmt.Sprintf("msisdn=%s", item.MSISDN)) conditions = append(conditions, fmt.Sprintf("msisdn=%s", item.MSISDN))
} }
if item.Tag != "" { if item.Tag < 2 {
conditions = append(conditions, fmt.Sprintf("volte=%s", item.Tag)) conditions = append(conditions, fmt.Sprintf("volte=%d", item.Tag))
} }
if item.VNI != "" { if item.VNI != "" {
conditions = append(conditions, fmt.Sprintf("vni=%s", item.VNI)) conditions = append(conditions, fmt.Sprintf("vni=%s", item.VNI))

View File

@@ -2,7 +2,6 @@ package service
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
"time" "time"
@@ -186,20 +185,13 @@ func (r *IMSUserService) Delete(neId, imsi string) int64 {
// LoadData 重新加载从imsi开始num的数据 // LoadData 重新加载从imsi开始num的数据
func (r *IMSUserService) LoadData(neId, imsiOrMsisdn, num string) { func (r *IMSUserService) LoadData(neId, imsiOrMsisdn, num string) {
startIMSIOrMsisdn, _ := strconv.ParseInt(imsiOrMsisdn, 10, 64) // 直接删除前缀的记录
subNum, _ := strconv.ParseInt(num, 10, 64) index := len(imsiOrMsisdn) - len(num) - 1
var i int64 prefix := imsiOrMsisdn[:index]
for i = 0; i < subNum; i++ { r.imsUserRepository.DeletePrefixByIMSI(prefix, neId)
keyIMSI := fmt.Sprintf("%015d", startIMSIOrMsisdn+i) // 加载数据
if !strings.HasPrefix(imsiOrMsisdn, "0") { arr := r.dataByRedis(prefix+"*", neId)
keyIMSI = fmt.Sprintf("%d", startIMSIOrMsisdn+i) if len(arr) > 0 {
}
// 删除原数据
r.imsUserRepository.Delete(keyIMSI, neId)
arr := r.dataByRedis(keyIMSI+":*", neId)
if len(arr) < 1 {
continue
}
r.imsUserRepository.Inserts(arr) r.imsUserRepository.Inserts(arr)
} }
} }

View File

@@ -2,7 +2,6 @@ package service
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
"time" "time"
@@ -163,18 +162,14 @@ func (r *VoIPAuthService) Delete(neId, userName string) int64 {
} }
// LoadData 重新加载从userName开始num的数据 // LoadData 重新加载从userName开始num的数据
func (r *VoIPAuthService) LoadData(neId, userName, num string) { func (r *VoIPAuthService) LoadData(neId, username, num string) {
startUserName, _ := strconv.ParseInt(userName, 10, 64) // 直接删除前缀的记录
subNum, _ := strconv.ParseInt(num, 10, 64) index := len(username) - len(num) - 1
var i int64 prefix := username[:index]
for i = 0; i < subNum; i++ { r.voipAuthRepository.DeletePrefixByUserName(prefix, neId)
keyUserName := fmt.Sprintf("%d", startUserName+i) // 加载数据
// 删除原数据 arr := r.dataByRedis(prefix+"*", neId)
r.voipAuthRepository.Delete(keyUserName, neId) if len(arr) > 0 {
arr := r.dataByRedis(keyUserName, neId)
if len(arr) < 1 {
continue
}
r.voipAuthRepository.Inserts(arr) r.voipAuthRepository.Inserts(arr)
} }
} }

View File

@@ -446,7 +446,7 @@ func (s *UDMSubController) Removes(c *gin.Context) {
// 命令ok时 // 命令ok时
if strings.Contains(data, "ok") { if strings.Contains(data, "ok") {
s.udmSubService.LoadData(neId, imsi, num, "-(Deleted)-") s.udmSubService.LoadData(neId, imsi, num, "")
} }
c.JSON(200, result.OkData(data)) c.JSON(200, result.OkData(data))
} }

View File

@@ -2,7 +2,6 @@ package service
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
"time" "time"
@@ -181,18 +180,13 @@ func (r *UDMAuthUser) Delete(imsi, neId string) int64 {
// LoadData 重新加载从imsi开始num的数据 // LoadData 重新加载从imsi开始num的数据
func (r *UDMAuthUser) LoadData(neId, imsi, num string) { func (r *UDMAuthUser) LoadData(neId, imsi, num string) {
startIMSI, _ := strconv.ParseInt(imsi, 10, 64) // 直接删除前缀的记录
subNum, _ := strconv.ParseInt(num, 10, 64) idx := len(imsi) - len(num) - 1
var i int64 prefix := imsi[:idx]
for i = 0; i < subNum; i++ { r.udmAuthRepository.DeletePrefixByIMSI(neId, prefix)
keyIMSI := fmt.Sprintf("%015d", startIMSI+i) // keys ausf:4600001000004*
// 删除原数据 arr := r.dataByRedis(prefix+"*", neId)
r.udmAuthRepository.Delete(keyIMSI, neId) if len(arr) > 0 {
// 加载数据
arr := r.dataByRedis(keyIMSI, neId)
if len(arr) < 1 {
continue
}
r.udmAuthRepository.Inserts(arr) r.udmAuthRepository.Inserts(arr)
} }
} }

View File

@@ -272,38 +272,40 @@ func (r *UDMSubUser) Delete(neId, imsi string) int64 {
} }
// LoadData 重新加载从imsi开始num的数据 // LoadData 重新加载从imsi开始num的数据
// remark不为空则新增到拓展信息删除标记为-(Deleted)-
func (r *UDMSubUser) LoadData(neId, imsi, num, remark string) { func (r *UDMSubUser) LoadData(neId, imsi, num, remark string) {
startIMSI, _ := strconv.ParseInt(imsi, 10, 64)
subNum, _ := strconv.ParseInt(num, 10, 64) subNum, _ := strconv.ParseInt(num, 10, 64)
var i int64 // 直接删除前缀的记录
for i = 0; i < subNum; i++ { index := len(imsi) - len(num) - 1
keyIMSI := fmt.Sprintf("%015d", startIMSI+i) prefix := imsi[:index]
// 删除原数据 r.udmSubRepository.DeletePrefixByIMSI(prefix, neId)
r.udmSubRepository.Delete(keyIMSI, neId) // r.udmUserInfoRepository.Delete(prefix, "%")
if remark == "-(Deleted)-" { // 加载数据
r.udmUserInfoRepository.Delete(keyIMSI, "%") arr := r.dataByRedis(prefix+"*", neId)
} if len(arr) > 0 {
// 加载数据,删除标记为-(Deleted)-加载为空不插入
arr := r.dataByRedis(keyIMSI, neId)
if len(arr) < 1 {
continue
}
r.udmSubRepository.Inserts(arr)
// 拓展信息 // 拓展信息
if remark != "" { if remark != "" {
startIMSI, _ := strconv.ParseInt(imsi, 10, 64)
endIMSI := startIMSI + subNum
uarr := make([]model.UDMUserInfo, 0, len(arr)) uarr := make([]model.UDMUserInfo, 0, len(arr))
for _, v := range arr { for _, v := range arr {
uarr = append(uarr, model.UDMUserInfo{ itemIMSI, err := strconv.ParseInt(v.IMSI, 10, 64)
IMSI: v.IMSI, if err != nil {
MSISDN: v.MSISDN, continue
NeId: v.NeId, }
Remark: remark, // 只处理在范围内的IMSI
}) if itemIMSI >= startIMSI && itemIMSI < endIMSI {
uarr = append(uarr, model.UDMUserInfo{
NeId: v.NeId,
IMSI: v.IMSI,
MSISDN: v.MSISDN,
Remark: remark,
})
r.udmUserInfoRepository.Delete(neId, v.IMSI)
}
} }
r.udmUserInfoRepository.Delete(keyIMSI, neId)
r.udmUserInfoRepository.Inserts(uarr) r.udmUserInfoRepository.Inserts(uarr)
} }
r.udmSubRepository.Inserts(arr)
} }
} }

View File

@@ -2,7 +2,6 @@ package service
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
"time" "time"
@@ -170,18 +169,13 @@ func (r UDMVOIPUser) Delete(username, neId string) int64 {
// LoadData 重新加载从username开始num的数据 // LoadData 重新加载从username开始num的数据
func (r UDMVOIPUser) LoadData(neId, username, num string) { func (r UDMVOIPUser) LoadData(neId, username, num string) {
startUserName, _ := strconv.ParseInt(username, 10, 64) // 直接删除前缀的记录
subNum, _ := strconv.ParseInt(num, 10, 64) index := len(username) - len(num) - 1
var i int64 prefix := username[:index]
for i = 0; i < subNum; i++ { r.udmVOIPRepository.DeletePrefixByUserName(neId, prefix)
keyUserName := fmt.Sprintf("%d", startUserName+i) // 加载数据
// 删除原数据 arr := r.dataByRedis(neId, prefix+"*")
r.udmVOIPRepository.Delete(keyUserName, neId) if len(arr) > 0 {
// 加载数据
arr := r.dataByRedis(keyUserName, neId)
if len(arr) < 1 {
continue
}
r.udmVOIPRepository.Inserts(arr) r.udmVOIPRepository.Inserts(arr)
} }
} }

View File

@@ -2,7 +2,6 @@ package service
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
"time" "time"
@@ -192,21 +191,13 @@ func (r UDMVolteIMSUser) Delete(imsi, neId string) int64 {
// LoadData 重新加载从imsi开始num的数据 // LoadData 重新加载从imsi开始num的数据
func (r UDMVolteIMSUser) LoadData(neId, imsiOrMsisdn, num string) { func (r UDMVolteIMSUser) LoadData(neId, imsiOrMsisdn, num string) {
startIMSIOrMsisdn, _ := strconv.ParseInt(imsiOrMsisdn, 10, 64) // 直接删除前缀的记录
subNum, _ := strconv.ParseInt(num, 10, 64) index := len(imsiOrMsisdn) - len(num) - 1
var i int64 prefix := imsiOrMsisdn[:index]
for i = 0; i < subNum; i++ { r.udmVolteIMSRepository.DeletePrefixByIMSI(neId, prefix)
keyIMSI := fmt.Sprintf("%015d", startIMSIOrMsisdn+i) // 加载数据
if !strings.HasPrefix(imsiOrMsisdn, "0") { arr := r.dataByRedis(neId, prefix+"*")
keyIMSI = fmt.Sprintf("%d", startIMSIOrMsisdn+i) if len(arr) > 0 {
}
// 删除原数据
r.udmVolteIMSRepository.Delete(keyIMSI, neId)
// 加载数据
arr := r.dataByRedis(keyIMSI+":*", neId)
if len(arr) < 1 {
continue
}
r.udmVolteIMSRepository.Inserts(arr) r.udmVolteIMSRepository.Inserts(arr)
} }
} }