feat: support ims user, voip auth data and backup UE data

This commit is contained in:
zhangsz
2025-04-09 14:16:35 +08:00
parent ba6eea0365
commit 7e5a73ffa7
36 changed files with 3569 additions and 62 deletions

View File

@@ -0,0 +1,486 @@
package ims_user
import (
"fmt"
"path/filepath"
"strings"
"time"
"be.ems/src/framework/constants/uploadsubpath"
"be.ems/src/framework/i18n"
"be.ems/src/framework/telnet"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/vo/result"
neService "be.ems/src/modules/network_element/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
// 实例化控制层 Controller 结构体
var NewController = &Controller{
volteUserService: NewVoLTEService,
neInfoService: neService.NewNeInfo,
}
// IMS用户信息 控制层处理
//
// @Description IMS用户信息 控制层处理
type Controller struct {
volteUserService *Service // IMS User信息服务
neInfoService *neService.NeInfo // 网元信息服务
}
func (s *Controller) ResetData(c *gin.Context) {
language := ctx.AcceptLanguage(c)
neId := c.Param("neId")
if neId == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
data := s.volteUserService.ResetData(neId)
c.JSON(200, result.OkData(data))
}
func (s *Controller) List(c *gin.Context) {
querys := ctx.QueryMap(c)
// querys["userName"] = ctx.LoginUserToUserName(c)
// 判断租户角色
loginUser, _ := ctx.LoginUser(c)
if len(loginUser.User.Roles) > 0 {
for _, v := range loginUser.User.Roles {
if v.RoleKey == "tenant" {
querys["tenantName"] = loginUser.User.UserName
break
}
}
}
data := s.volteUserService.SelectPage(querys)
c.JSON(200, result.Ok(data))
}
func (s *Controller) Info(c *gin.Context) {
language := ctx.AcceptLanguage(c)
neId := c.Param("neId")
imsi := c.Param("imsi")
if neId == "" || imsi == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("UDM", neId)
if neInfo.NeId != neId || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元主机的Telnet客户端
telnetClient, err := s.neInfoService.NeRunTelnetClient("UDM", neId, 1)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer telnetClient.Close()
// 发送MML
cmd := fmt.Sprintf("dsp imsuser:imsi=%s", imsi)
data, err := telnet.ConvertToMap(telnetClient, cmd)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
if len(data) == 0 {
c.JSON(200, result.ErrMsg("Not found VoLTE user data"))
return
}
// 解析返回的数据
u := s.volteUserService.ParseInfo(imsi, neId, data)
s.volteUserService.Insert(neId, u)
c.JSON(200, result.OkData(u))
}
func (s *Controller) Add(c *gin.Context) {
language := ctx.AcceptLanguage(c)
neId := c.Param("neId")
if neId == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
var body VoLTEUser
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || len(body.IMSI) < IMSI_MAX_LENGTH {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("UDM", neId)
if neInfo.NeId != neId || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元主机的Telnet客户端
telnetClient, err := s.neInfoService.NeRunTelnetClient("UDM", neId, 1)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer telnetClient.Close()
// 发送MML
cmd := fmt.Sprintf("add imsuser:imsi=%s,", body.IMSI)
cmd += s.volteUserService.ParseCommandParams(body)
data, err := telnet.ConvertToStr(telnetClient, cmd)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
// 命令ok时
if strings.Contains(data, "ok") {
body.NeId = neId
s.volteUserService.Insert(neId, body)
}
c.JSON(200, result.OkData(data))
}
func (s *Controller) Adds(c *gin.Context) {
language := ctx.AcceptLanguage(c)
neId := c.Param("neId")
num := c.Param("num")
if neId == "" || num == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
var body VoLTEUser
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || len(body.IMSI) < IMSI_MAX_LENGTH {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("UDM", neId)
if neInfo.NeId != neId || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元主机的Telnet客户端
telnetClient, err := s.neInfoService.NeRunTelnetClient("UDM", neId, 1)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer telnetClient.Close()
// 发送MML
cmd := fmt.Sprintf("baa imsuser:start_imsi=%s,start_msisdn=%s,sub_num=%s,", body.IMSI, body.MSISDN, num)
cmd += s.volteUserService.ParseCommandParams(body)
// 去除msisdn参数避免重复
omemsisdn := fmt.Sprintf(",msisdn=%s,", body.MSISDN)
cmd = strings.Replace(cmd, omemsisdn, ",", 1)
data, err := telnet.ConvertToStr(telnetClient, cmd)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.OkData(data))
}
func (s *Controller) Edit(c *gin.Context) {
language := ctx.AcceptLanguage(c)
neId := c.Param("neId")
if neId == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
var body VoLTEUser
err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil || len(body.IMSI) < IMSI_MAX_LENGTH {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("UDM", neId)
if neInfo.NeId != neId || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元主机的Telnet客户端
telnetClient, err := s.neInfoService.NeRunTelnetClient("UDM", neId, 1)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer telnetClient.Close()
// 发送MML
cmd := fmt.Sprintf("mod imsuser:imsi=%s,", body.IMSI)
cmd += s.volteUserService.ParseCommandParams(body)
data, err := telnet.ConvertToStr(telnetClient, cmd)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
// 命令ok时
if strings.Contains(data, "ok") {
body.NeId = neId
s.volteUserService.Insert(neId, body)
}
c.JSON(200, result.OkData(data))
}
func (s *Controller) Remove(c *gin.Context) {
language := ctx.AcceptLanguage(c)
neId := c.Param("neId")
imsi := c.Param("imsi")
if neId == "" || len(imsi) < IMSI_MAX_LENGTH {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 处理字符转id数组后去重
imsiArr := strings.Split(imsi, ",")
uniqueIDs := parse.RemoveDuplicates(imsiArr)
if len(uniqueIDs) <= 0 {
c.JSON(200, result.Err(nil))
return
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("UDM", neId)
if neInfo.NeId != neId || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元主机的Telnet客户端
telnetClient, err := s.neInfoService.NeRunTelnetClient("UDM", neId, 1)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer telnetClient.Close()
resultData := map[string]string{}
for _, imsi := range uniqueIDs {
// 发送MML
cmd := fmt.Sprintf("del imsuser:imsi=%s", imsi)
data, err := telnet.ConvertToStr(telnetClient, cmd)
if err != nil {
resultData[imsi] = err.Error()
continue
}
// 命令ok时
if strings.Contains(data, "ok") {
s.volteUserService.Delete(neId, imsi)
}
resultData[imsi] = data
}
c.JSON(200, result.OkData(resultData))
}
func (s *Controller) Removes(c *gin.Context) {
language := ctx.AcceptLanguage(c)
neId := c.Param("neId")
imsi := c.Param("imsi")
num := c.Param("num")
if neId == "" || len(imsi) < IMSI_MAX_LENGTH || num == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("UDM", neId)
if neInfo.NeId != neId || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元主机的Telnet客户端
telnetClient, err := s.neInfoService.NeRunTelnetClient("UDM", neId, 1)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer telnetClient.Close()
// 发送MML
cmd := fmt.Sprintf("bde imsuser:start_imsi=%s,sub_num=%s", imsi, num)
data, err := telnet.ConvertToStr(telnetClient, cmd)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
// 命令ok时
if strings.Contains(data, "ok") {
s.volteUserService.LoadData(neId, imsi, num)
}
c.JSON(200, result.OkData(data))
}
func (s *Controller) Export(c *gin.Context) {
language := ctx.AcceptLanguage(c)
// 查询结果,根据查询条件结果,单页最大值限制
querys := ctx.BodyJSONMap(c)
neId := querys["neId"].(string)
fileType := querys["type"].(string)
if neId == "" || fileType == "" {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
if !(fileType == "csv" || fileType == "txt") {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "ne.udm.errImportUserSubFileFormat")))
return
}
// 判断租户角色
loginUser, _ := ctx.LoginUser(c)
if len(loginUser.User.Roles) > 0 {
for _, v := range loginUser.User.Roles {
if v.RoleKey == "tenant" {
querys["tenantName"] = loginUser.User.UserName
break
}
}
}
querys["pageNum"] = 1
querys["pageSize"] = 10000
data := s.volteUserService.SelectPage(querys)
if parse.Number(data["total"]) == 0 {
// 导出数据记录为空
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
return
}
rows := data["rows"].([]VoLTEUser)
// rows := s.volteUserService.SelectList(VoLTEUser{NeId: neId})
if len(rows) <= 0 {
// 导出数据记录为空
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
return
}
// 文件名
fileName := fmt.Sprintf("udm_volte_user_export_%s_%d.%s", neId, time.Now().UnixMilli(), fileType)
filePath := filepath.Join(file.ParseUploadFileDir(uploadsubpath.EXPORT), fileName)
if fileType == "csv" {
// 转换数据
data := [][]string{}
data = append(data, []string{"IMSI", "MSISDN", "VoLTE", "VNI"})
for _, v := range rows {
data = append(data, []string{v.IMSI, v.MSISDN, v.VoLTE, v.VNI})
}
// 输出到文件
if err := file.WriterFileCSV(data, filePath); err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
}
if fileType == "txt" {
// 转换数据
data := [][]string{}
for _, v := range rows {
data = append(data, []string{v.IMSI, v.MSISDN, v.VoLTE, v.VNI})
}
// 输出到文件
if err := file.WriterFileTXT(data, ",", filePath); err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
}
c.FileAttachment(filePath, fileName)
}
func (s *Controller) Import(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var body struct {
NeId string `json:"neId" binding:"required"`
UploadPath string `json:"uploadPath" binding:"required"`
}
if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 判断文件名
if !(strings.HasSuffix(body.UploadPath, ".csv") || strings.HasSuffix(body.UploadPath, ".txt")) {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "ne.udm.errImportUserSubFileFormat")))
return
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID("UDM", body.NeId)
if neInfo.NeId != body.NeId || neInfo.IP == "" {
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
// 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer sshClient.Close()
// 网元主机的SSH客户端进行文件传输
sftpClient, err := sshClient.NewClientSFTP()
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer sftpClient.Close()
// 本地文件
localFilePath := file.ParseUploadFilePath(body.UploadPath)
neFilePath := fmt.Sprintf("/tmp/%s", filepath.Base(localFilePath))
// 复制到远程
if err = sftpClient.CopyFileLocalToRemote(localFilePath, neFilePath); err != nil {
c.JSON(200, result.ErrMsg("error uploading file"))
return
}
// 网元主机的Telnet客户端
telnetClient, err := s.neInfoService.NeRunTelnetClient(neInfo.NeType, neInfo.NeId, 1)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
defer telnetClient.Close()
// 发送MML
cmd := fmt.Sprintf("import imsuser:path=%s", neFilePath)
data, err := telnet.ConvertToStr(telnetClient, cmd)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
// 命令ok时
if strings.Contains(data, "ok") {
if strings.HasSuffix(body.UploadPath, ".csv") {
data := file.ReadFileCSV(localFilePath)
go s.volteUserService.InsertData(neInfo.NeId, "csv", data)
}
if strings.HasSuffix(body.UploadPath, ".txt") {
data := file.ReadFileTXT(",", localFilePath)
go s.volteUserService.InsertData(neInfo.NeId, "txt", data)
}
}
c.JSON(200, result.OkMsg(data))
}

View File

@@ -0,0 +1,26 @@
package ims_user
const (
// IMSI 号码长度
IMSI_MAX_LENGTH = 15
// MSISDN 号码长度
MSISDN_MAX_LENGTH = 15
)
// @Description VoLTE用户信息
type VoLTEUser struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"` // 主键
NeId string `json:"neId" gorm:"column:ne_id"` // UDM网元标识
IMSI string `json:"imsi" gorm:"column:imsi"` // SIM卡/USIM卡ID
MSISDN string `json:"msisdn" gorm:"column:msisdn"` // 用户电话号码
VoLTE string `json:"volte" gorm:"column:volte"` // VoLTE
VNI string `json:"vni" gorm:"column:vni"` // VNI
TenantID string `json:"tenantID" gorm:"column:tenant_id"`
TenantName string `json:"tenantName" gorm:"-"`
}
// TableName 表名称
func (*VoLTEUser) TableName() string {
return "u_ims_user"
}

View File

@@ -0,0 +1,272 @@
package ims_user
import (
"fmt"
"strings"
dborm "be.ems/lib/core/datasource"
"be.ems/lib/log"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/repo"
)
// 实例化数据层 Repository 结构体
var NewVoLTERepository = &Repository{
selectSql: `select
s.id, s.ne_id, s.imsi, s.msisdn, s.volte, s.vni,
t.tenant_id, t.tenant_name
from u_ims_user s
left join sys_tenant t on t.tenant_id = s.tenant_id and t.status = 1`,
resultMap: map[string]string{
"id": "ID",
"ne_id": "NeId",
"imsi": "IMSI",
"msisdn": "MSISDN",
"volte": "VoLTE",
"vni": "VNI",
"tenant_id": "TenantID",
"tenant_name": "TenantName", // Tenant name for multi-tenancy
},
}
// Repository UDM签约信息表 数据层处理
type Repository struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
resultMap map[string]string
}
// convertResultRows 将结果记录转实体结果组
func (r *Repository) convertResultRows(rows []map[string]any) []VoLTEUser {
arr := make([]VoLTEUser, 0)
for _, row := range rows {
item := VoLTEUser{}
for key, value := range row {
if keyMapper, ok := r.resultMap[key]; ok {
repo.SetFieldValue(&item, keyMapper, value)
}
}
arr = append(arr, item)
}
return arr
}
// ClearAndInsert 清空ne_id后新增实体
func (r *Repository) ClearAndInsert(neId string, u []VoLTEUser) int64 {
// 不指定neID时用 TRUNCATE 清空表快
// _, err := datasource.ExecDB("", "TRUNCATE TABLE u_ims_user", nil)
_, err := datasource.ExecDB("", "DELETE FROM u_ims_user WHERE ne_id = ?", []any{neId})
if err != nil {
logger.Errorf("TRUNCATE err => %v", err)
}
return r.Inserts(u)
}
// SelectPage 根据条件分页查询字典类型
func (r *Repository) SelectPage(query map[string]any) map[string]any {
// 查询条件拼接
var conditions []string
var params []any
if v, ok := query["imsi"]; ok && v != "" {
conditions = append(conditions, "imsi like concat(concat('%', ?), '%')")
params = append(params, strings.Trim(v.(string), " "))
}
if v, ok := query["msisdn"]; ok && v != "" {
conditions = append(conditions, "msisdn like concat(concat('%', ?), '%')")
params = append(params, strings.Trim(v.(string), " "))
}
if v, ok := query["imsis"]; ok && v != "" {
placeholder := repo.KeyPlaceholderByQuery(len(v.([]any)))
conditions = append(conditions, fmt.Sprintf("imsi in (%s)", placeholder))
for _, v := range v.([]any) {
params = append(params, v.(string))
}
}
// for multi-tenancy solution
if v, ok := query["tenantName"]; ok && v != "" {
var tenantID []string
err := dborm.DefaultDB().Table("sys_tenant").
Where("tenant_name=? and status=1", v).Cols("tenant_id").Distinct().Find(&tenantID)
if err != nil {
log.Errorf("Find tenant_id from sys_user err => %v", err)
}
log.Tracef("userName=%v, tenantID=%v", v, tenantID)
if len(tenantID) > 0 {
conditions = append(conditions, "s.tenant_id = ?")
params = append(params, tenantID[0])
}
} else if v, ok := query["userName"]; ok && v != "" {
var tenantID string
_, err := dborm.DefaultDB().Table("sys_user").
Where("user_name=?", v).Cols("tenant_id").Distinct().Get(&tenantID)
if err != nil {
log.Errorf("Find tenant_id from sys_user err => %v", err)
}
log.Tracef("userName=%v, tenantID=%v", v, tenantID)
if tenantID != "" {
conditions = append(conditions, "s.tenant_id = ?")
params = append(params, tenantID)
query["neId"] = ""
}
}
if v, ok := query["neId"]; ok && v != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, v)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
result := map[string]any{
"total": 0,
"rows": []VoLTEUser{},
}
// 查询数量 长度为0直接返回
totalSql := "select count(1) as 'total' from u_ims_user s"
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
if err != nil {
logger.Errorf("total err => %v", err)
return result
}
total := parse.Number(totalRows[0]["total"])
if total == 0 {
return result
} else {
result["total"] = total
}
// 分页
pageNum, pageSize := repo.PageNumSize(query["pageNum"], query["pageSize"])
pageSql := " limit ?,? "
params = append(params, pageNum*pageSize)
params = append(params, pageSize)
// 排序
orderSql := ""
if v, ok := query["sortField"]; ok && v != "" {
sortSql := v.(string)
if o, ok := query["sortOrder"]; ok && o != nil && v != "" {
if o == "desc" {
sortSql += " desc "
} else {
sortSql += " asc "
}
}
orderSql = fmt.Sprintf(" order by %s ", sortSql)
}
// 查询数据
querySql := r.selectSql + whereSql + orderSql + pageSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
return result
}
// 转换实体
result["rows"] = r.convertResultRows(results)
return result
}
// SelectList 根据实体查询
func (r *Repository) SelectList(u VoLTEUser) []VoLTEUser {
// 查询条件拼接
var conditions []string
var params []any
if u.IMSI != "" {
conditions = append(conditions, "imsi = ?")
params = append(params, u.IMSI)
}
if u.NeId != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, u.NeId)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
// 查询数据
querySql := r.selectSql + whereSql + " order by imsi asc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
return r.convertResultRows(results)
}
// SelectByIMSIAndNeID 通过imsi和ne_id查询
func (r *Repository) SelectByIMSIAndNeID(imsi, neId string) VoLTEUser {
querySql := r.selectSql + " where imsi = ? and ne_id = ?"
results, err := datasource.RawDB("", querySql, []any{imsi, neId})
if err != nil {
logger.Errorf("query err => %v", err)
return VoLTEUser{}
}
// 转换实体
rows := r.convertResultRows(results)
if len(rows) > 0 {
return rows[0]
}
return VoLTEUser{}
}
// Insert 批量添加
func (r *Repository) Inserts(uArr []VoLTEUser) int64 {
// multi-tenancy
r.SetTenantID(&uArr)
tx := datasource.DefaultDB().CreateInBatches(uArr, 2000)
if err := tx.Error; err != nil {
logger.Errorf("CreateInBatches err => %v", err)
}
return tx.RowsAffected
}
// Delete 删除实体
func (r *Repository) Delete(imsi, neId string) int64 {
tx := datasource.DefaultDB().Where("imsi = ? and ne_id = ?", imsi, neId).Delete(&Repository{})
if err := tx.Error; err != nil {
logger.Errorf("Delete err => %v", err)
}
return tx.RowsAffected
}
// DeletePrefixByIMSI 删除前缀匹配的实体
func (r *Repository) DeletePrefixByIMSI(imsiPrefix, neId string) int64 {
tx := datasource.DefaultDB().Where("imsi like concat(?, '%') and ne_id = ?", imsiPrefix, neId).Delete(&Repository{})
if err := tx.Error; err != nil {
logger.Errorf("DeletePrefixByIMSI err => %v", err)
}
return tx.RowsAffected
}
func (r *Repository) SetTenantID(subArr *[]VoLTEUser) {
for s := 0; s < len(*subArr); s++ {
var tenantID []string
err := dborm.DefaultDB().Table("sys_tenant").
Where("status='1' and tenancy_type='IMSI' and ? like tenancy_key", (*subArr)[s].IMSI).
Cols("parent_id").Distinct().Find(&tenantID)
if err != nil {
logger.Errorf("Find tenant_id err => %v", err)
continue
}
if len(tenantID) > 0 {
(*subArr)[s].TenantID = tenantID[0]
}
}
}

View File

@@ -0,0 +1,67 @@
package ims_user
import (
"be.ems/src/framework/middleware"
"be.ems/src/framework/middleware/collectlogs"
"be.ems/src/framework/middleware/repeat"
"github.com/gin-gonic/gin"
)
// @Description Register Routes for ims_user
func Register(r *gin.RouterGroup) {
volteUserGroup := r.Group("/volte/user")
{
volteUserGroup.PUT("/resetData/:neId",
repeat.RepeatSubmit(5),
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.volteUser", collectlogs.BUSINESS_TYPE_CLEAN)),
NewController.ResetData,
)
volteUserGroup.GET("/list",
middleware.PreAuthorize(nil),
NewController.List,
)
volteUserGroup.GET("/:neId/:imsi",
middleware.PreAuthorize(nil),
NewController.Info,
)
volteUserGroup.POST("/:neId",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.volteUser", collectlogs.BUSINESS_TYPE_INSERT)),
NewController.Add,
)
volteUserGroup.POST("/:neId/:num",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.volteUser", collectlogs.BUSINESS_TYPE_INSERT)),
NewController.Adds,
)
volteUserGroup.PUT("/:neId",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.volteUser", collectlogs.BUSINESS_TYPE_UPDATE)),
NewController.Edit,
)
volteUserGroup.DELETE("/:neId/:imsi",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.volteUser", collectlogs.BUSINESS_TYPE_DELETE)),
NewController.Remove,
)
volteUserGroup.DELETE("/:neId/:imsi/:num",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.volteUser", collectlogs.BUSINESS_TYPE_DELETE)),
NewController.Removes,
)
volteUserGroup.POST("/export",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.volteUser", collectlogs.BUSINESS_TYPE_EXPORT)),
NewController.Export,
)
volteUserGroup.POST("/import",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.volteUser", collectlogs.BUSINESS_TYPE_IMPORT)),
NewController.Import,
)
}
}

View File

@@ -0,0 +1,209 @@
package ims_user
import (
"fmt"
"strconv"
"strings"
"be.ems/src/framework/redis"
neService "be.ems/src/modules/network_element/service"
)
// 实例化服务层 Service 结构体
var NewVoLTEService = &Service{
volteRepository: NewVoLTERepository,
}
// VoLTE用户信息 服务层处理
type Service struct {
volteRepository *Repository // VoLTE用户信息数据信息
}
// dataByRedis UDM签约用户 db:0 中 volte:*
func (r *Service) dataByRedis(imsi, neId string) []VoLTEUser {
arr := []VoLTEUser{}
key := fmt.Sprintf("volte:%s", imsi)
source := fmt.Sprintf("UDM_%s", neId)
// 网元主机的Redis客户端
redisClient, err := neService.NewNeInfo.NeRunRedisClient("UDM", neId)
if err != nil {
return arr
}
defer func() {
redisClient.Close()
redis.ConnectPush(source, nil)
}()
redis.ConnectPush(source, redisClient.Client)
udmsdArr, err := redis.GetKeys(source, key)
if err != nil {
return arr
}
mkv, err := redis.GetHashBatch(source, udmsdArr)
if err != nil {
return arr
}
for k, m := range mkv {
var imsi, msisdn string
KeyParts := strings.Split(k, ":")
switch len(KeyParts) {
case 0, 1:
// 处理单个部分的情况
continue
case 2:
// 处理两个部分的情况
imsi = KeyParts[1]
msisdn = "-"
case 3:
// 处理三个部分的情况
imsi = KeyParts[1]
msisdn = KeyParts[2]
default:
// 处理更多部分的情况
imsi = KeyParts[1]
msisdn = KeyParts[2]
}
var vni string = "-"
impiParts := strings.Split(m["impi"], "@")
if len(impiParts) > 1 {
vni = impiParts[1] // 输出: ims.mnc001.mcc110.3gppnetwork.org
}
a := VoLTEUser{
NeId: neId,
IMSI: imsi, // volte:360000100000130:8612300000130
MSISDN: msisdn, // 8612300000130
VoLTE: m["tag"], // volte = tag
VNI: vni, // ims.mnc001.mcc110.3gppnetwork.org
}
arr = append(arr, a)
}
return arr
}
// ResetData 重置鉴权用户数据清空数据库重新同步Redis数据
func (r *Service) ResetData(neId string) int64 {
subArr := r.dataByRedis("*", neId)
// 数据清空后添加
go r.volteRepository.ClearAndInsert(neId, subArr)
return int64(len(subArr))
}
// ParseInfo 解析单个用户imsi签约信息 data从命令MML得到的结果
func (r *Service) ParseInfo(imsi, neId string, data map[string]string) VoLTEUser {
u := r.volteRepository.SelectByIMSIAndNeID(imsi, neId)
msisdn := data["MSISDN"]
if imsMsisdnLen := strings.Index(msisdn, ","); imsMsisdnLen != -1 {
msisdn = msisdn[:imsMsisdnLen]
}
// 用于更新
u.NeId = neId
u.IMSI = imsi
u.MSISDN = msisdn
u.VoLTE = data["VoLTE"]
u.VNI = data["VNI"]
return u
}
// SelectPage 分页查询数据库
func (r *Service) SelectPage(query map[string]any) map[string]any {
return r.volteRepository.SelectPage(query)
}
// SelectList 查询数据库
func (r *Service) SelectList(u VoLTEUser) []VoLTEUser {
return r.volteRepository.SelectList(u)
}
// Insert 从数据中读取后删除imsi再存入数据库
// imsi长度15ki长度32opc长度0或者32
func (r *Service) Insert(neId string, u VoLTEUser) int64 {
uArr := r.dataByRedis(u.IMSI, neId)
if len(uArr) > 0 {
r.volteRepository.Delete(u.IMSI, neId)
return r.volteRepository.Inserts(uArr)
}
return 0
}
// InsertData 导入文件数据 dataType目前两种txt/csv
func (r *Service) InsertData(neId, dataType string, data any) int64 {
// imsi截取前缀,重新获取部分数据
prefixes := make(map[string]struct{})
if dataType == "csv" {
for _, v := range data.([]map[string]string) {
imsi := v["imsi"]
if len(imsi) < 6 {
continue
}
prefix := imsi[:len(imsi)-4]
prefixes[prefix] = struct{}{}
}
}
if dataType == "txt" {
for _, v := range data.([][]string) {
imsi := v[0]
if len(imsi) < 6 {
continue
}
prefix := imsi[:len(imsi)-4]
prefixes[prefix] = struct{}{}
}
}
// 根据前缀重新加载插入
var num int64 = 0
for prefix := range prefixes {
// keys volte:4600001000004*
arr := r.dataByRedis(prefix+"*", neId)
if len(arr) > 0 {
r.volteRepository.DeletePrefixByIMSI(prefix, neId)
num += r.volteRepository.Inserts(arr)
}
}
return num
}
// Delete 删除单个不重新加载
func (r *Service) Delete(neId, imsi string) int64 {
return r.volteRepository.Delete(imsi, neId)
}
// LoadData 重新加载从imsi开始num的数据
func (r *Service) LoadData(neId, imsi, num string) {
startIMSI, _ := strconv.ParseInt(imsi, 10, 64)
subNum, _ := strconv.ParseInt(num, 10, 64)
var i int64
for i = 0; i < subNum; i++ {
keyIMSI := fmt.Sprintf("%015d", startIMSI+i)
// 删除原数据
r.volteRepository.Delete(keyIMSI, neId)
arr := r.dataByRedis(keyIMSI, neId)
if len(arr) < 1 {
continue
}
r.volteRepository.Inserts(arr)
}
}
// ParseCommandParams 解析数据组成命令参数 msisdn=xx,xx=xx,...
func (r *Service) ParseCommandParams(item VoLTEUser) string {
var conditions []string
if item.MSISDN != "" {
conditions = append(conditions, fmt.Sprintf("msisdn=%s", item.MSISDN))
}
if item.VoLTE != "" {
conditions = append(conditions, fmt.Sprintf("volte=%s", item.VoLTE))
}
if item.VNI != "" {
conditions = append(conditions, fmt.Sprintf("vni=%s", item.VNI))
}
return strings.Join(conditions, ",")
}