package service import ( "fmt" "strconv" "strings" "be.ems/src/framework/database/redis" neService "be.ems/src/modules/network_element/service" "be.ems/src/modules/network_link/model" "be.ems/src/modules/network_link/repository" ) // 实例化服务层 UDMSubUser 结构体 var NewUDMSubUser = &UDMSubUser{ udmSubRepository: repository.NewUDMSub, UDMExtendRepository: repository.NewUDMExtend, } // UDM签约信息 服务层处理 type UDMSubUser struct { udmSubRepository *repository.UDMSubUser // UDM签约信息数据信息 UDMExtendRepository *repository.UDMExtend // UDM用户IMSI信息数据信息 } // dataByRedis UDM签约用户 db:0 中 udm-sd:* func (r *UDMSubUser) dataByRedis(coreUid, neUid, imsi string) []model.UDMSubUser { arr := []model.UDMSubUser{} key := fmt.Sprintf("udm-sd:%s", imsi) source := fmt.Sprintf("UDM_%s", neUid) // 网元主机的Redis客户端 redisClient, err := neService.NewNeInfo.NeRunRedisClient(coreUid, neUid) 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 { if len(k) != 22 { continue } // 跳过-号数据 udm-sd:360000100000130 imsi, hasPrefix := strings.CutPrefix(k, "udm-sd:") if strings.Contains(imsi, "-") || !hasPrefix { continue } a := model.UDMSubUser{ CoreUID: coreUid, NeUID: neUid, NeType: "UDM", IMSI: imsi, // udm-sd:360000100000130 MSISDN: m["gpsi"], // 8612300000130 SmfSel: m["smf-sel"], // def_snssai SmData: m["sm-dat"], // 1-000001&cmnet&ims&3gnet Cag: m["cag"], // def_cag } // def_ambr,def_nssai,0,def_arfb,def_sar,3,1,12000,1,1000,0,1,- if v, ok := m["am-dat"]; ok { arr := strings.Split(v, ",") a.AmDat = v a.UeAmbrTpl = arr[0] a.NssaiTpl = arr[1] a.RatRestrictions = arr[2] a.AreaForbiddenTpl = arr[3] a.ServiceAreaRestrictionTpl = arr[4] a.CnTypeRestrictions = arr[5] a.RfspIndex = arr[6] a.SubsRegTime = arr[7] a.UeUsageType = arr[8] a.ActiveTime = arr[9] a.MicoAllowed = "0" // arr[10] a.OdbPs = "1" // arr[11] a.GroupId = "-" // arr[12] if len(arr) > 10 { a.MicoAllowed = arr[10] } if len(arr) > 11 { a.OdbPs = arr[11] } if len(arr) > 12 && arr[12] != "-" { a.GroupId = arr[12] } } // 1,64,24,65,def_eps,1,2,010200000000,- if v, ok := m["eps-dat"]; ok { arr := strings.Split(v, ",") // 跳过非常规数据 if len(arr) > 9 { continue } a.EpsDat = v a.EpsFlag = arr[0] a.EpsOdb = arr[1] a.HplmnOdb = arr[2] a.Ard = arr[3] a.Epstpl = arr[4] a.ContextId = arr[5] a.ApnNum = arr[6] // 导入和导出不用 a.ApnContext = arr[7] if len(arr) >= 9 { a.StaticIp = arr[8] } } arr = append(arr, a) } return arr } // ResetData 重置鉴权用户数据,清空数据库重新同步Redis数据 func (r *UDMSubUser) ResetData(coreUid, neUid string) int64 { subArr := r.dataByRedis(coreUid, neUid, "*") // 数据清空后添加 go r.udmSubRepository.ClearAndInsert(coreUid, neUid, subArr) return int64(len(subArr)) } // ParseInfo 解析单个用户imsi签约信息 data从命令MML得到的结果 func (r *UDMSubUser) ParseInfo(coreUid, neUid, imsi string, data map[string]string) model.UDMSubUser { u := r.udmSubRepository.SelectByIMSI(coreUid, neUid, imsi) cnType, _ := strconv.ParseInt(data["CNType"][:4], 0, 64) // 0x03(EPC|5GC) rat, _ := strconv.ParseInt(data["RAT"][:4], 0, 64) // 0x00(VIRTUAL|WLAN|EUTRA|NR) msisdn := data["MSISDN"] if imsMsisdnLen := strings.Index(msisdn, ","); imsMsisdnLen != -1 { msisdn = msisdn[:imsMsisdnLen] } // 用于更新 u.CoreUID = coreUid u.NeUID = neUid u.NeType = "UDM" u.IMSI = imsi u.MSISDN = msisdn u.UeAmbrTpl = data["AMBR"] u.NssaiTpl = data["NSSAI"] u.AreaForbiddenTpl = data["AreaForbidden"] u.ServiceAreaRestrictionTpl = data["ServiceAreaRestriction"] u.CnTypeRestrictions = fmt.Sprint(cnType) u.RatRestrictions = fmt.Sprint(rat) u.MicoAllowed = data["MICO"] u.SmData = data["SM-Data(snssai+dnn[1..n])"] u.SmfSel = data["Smf-Selection"] u.Cag = data["cag"] // 1,64,24,65,def_eps,1,2,010200000000,- if v, ok := data["EPS-Data"]; ok { u.EpsDat = v arr := strings.Split(v, ",") u.EpsFlag = arr[0] u.EpsOdb = arr[1] u.HplmnOdb = arr[2] u.Ard = arr[3] u.Epstpl = arr[4] u.ContextId = arr[5] u.ApnNum = arr[6] // 导入和导出不用 u.ApnContext = arr[7] u.StaticIp = arr[8] } // 补充用户拓展信息 info := r.UDMExtendRepository.SelectByIMSI(coreUid, neUid, imsi) if info.IMSI == imsi { u.Remark = info.Remark } return u } // FindByPage 分页查询数据库 func (r *UDMSubUser) FindByPage(query map[string]string) (int64, []model.UDMSubUser) { return r.udmSubRepository.SelectPage(query) } // Find 查询数据库 func (r *UDMSubUser) Find(u model.UDMSubUser) []model.UDMSubUser { return r.udmSubRepository.SelectList(u) } // Insert 从数据中读取后删除imsi再存入数据库 // imsi长度15,ki长度32,opc长度0或者32 func (r *UDMSubUser) Insert(coreUid, neUid string, u model.UDMSubUser) int64 { uArr := r.dataByRedis(coreUid, neUid, u.IMSI) if len(uArr) > 0 { r.udmSubRepository.DeleteByIMSI(coreUid, neUid, u.IMSI) // 新增到拓展信息 if u.Remark != "" { r.UDMExtendRepository.DeleteByIMSI(coreUid, "%", u.IMSI) r.UDMExtendRepository.Inserts([]model.UDMExtend{{ CoreUID: u.CoreUID, NeUID: u.NeUID, NeType: u.NeType, IMSI: u.IMSI, MSISDN: u.MSISDN, Remark: u.Remark, }}) } return r.udmSubRepository.Inserts(uArr) } return 0 } // InsertData 导入文件数据 dataType目前两种:txt/csv func (r *UDMSubUser) InsertData(coreUid, neUid, 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 udm-sd:4600001000004* arr := r.dataByRedis(coreUid, neUid, prefix+"*") if len(arr) > 0 { r.udmSubRepository.DeletePrefixByIMSI(coreUid, neUid, prefix) num += r.udmSubRepository.Inserts(arr) } } return num } // Delete 删除单个不重新加载 func (r *UDMSubUser) Delete(coreUid, neUid, imsi string) int64 { // 删除拓展信息 r.UDMExtendRepository.DeleteByIMSI(coreUid, neUid, imsi) return r.udmSubRepository.DeleteByIMSI(coreUid, neUid, imsi) } // LoadData 重新加载从imsi开始num的数据 // remark不为空,则新增到拓展信息,删除标记为-(Deleted)- func (r *UDMSubUser) LoadData(coreUid, neUid, imsi string, num int64, remark string) { startIMSI, _ := strconv.ParseInt(imsi, 10, 64) var i int64 for i = 0; i < num; i++ { keyIMSI := fmt.Sprintf("%015d", startIMSI+i) // 删除原数据 r.udmSubRepository.DeleteByIMSI(coreUid, neUid, keyIMSI) if remark == "-(Deleted)-" { r.UDMExtendRepository.DeleteByIMSI(coreUid, "%", keyIMSI) } // 加载数据,删除标记为-(Deleted)-加载为空不插入 arr := r.dataByRedis(coreUid, neUid, keyIMSI) if len(arr) < 1 { continue } r.udmSubRepository.Inserts(arr) // 拓展信息 if remark != "" { uarr := make([]model.UDMExtend, 0, len(arr)) for _, v := range arr { uarr = append(uarr, model.UDMExtend{ CoreUID: v.CoreUID, NeUID: v.NeUID, NeType: v.NeType, IMSI: v.IMSI, MSISDN: v.MSISDN, Remark: remark, }) } r.UDMExtendRepository.DeleteByIMSI(coreUid, neUid, keyIMSI) r.UDMExtendRepository.Inserts(uarr) } } } // ParseCommandParams 解析数据组成命令参数 msisdn=xx,xx=xx,... func (r *UDMSubUser) ParseCommandParams(item model.UDMSubUser) string { var conditions []string if item.MSISDN != "" { conditions = append(conditions, fmt.Sprintf("msisdn=%s", item.MSISDN)) } // AmData if item.UeAmbrTpl != "" { conditions = append(conditions, fmt.Sprintf("ambr=%s", item.UeAmbrTpl)) } if item.NssaiTpl != "" { conditions = append(conditions, fmt.Sprintf("nssai=%s", item.NssaiTpl)) } if item.AreaForbiddenTpl != "" { conditions = append(conditions, fmt.Sprintf("arfb=%s", item.AreaForbiddenTpl)) } if item.ServiceAreaRestrictionTpl != "" { conditions = append(conditions, fmt.Sprintf("sar=%s", item.ServiceAreaRestrictionTpl)) } if item.RatRestrictions != "" { conditions = append(conditions, fmt.Sprintf("rat=%s", item.RatRestrictions)) } if item.CnTypeRestrictions != "" { conditions = append(conditions, fmt.Sprintf("cn=%s", item.CnTypeRestrictions)) } if item.MicoAllowed != "" { conditions = append(conditions, fmt.Sprintf("mico=%s", item.MicoAllowed)) } // EpsDat // if item.EpsDat != "" { // conditions = append(conditions, fmt.Sprintf("eps_dat=%s", item.EpsDat)) // } if item.EpsFlag != "" { conditions = append(conditions, fmt.Sprintf("eps_flag=%s", item.EpsFlag)) } if item.EpsOdb != "" { conditions = append(conditions, fmt.Sprintf("eps_odb=%s", item.EpsOdb)) } if item.HplmnOdb != "" { conditions = append(conditions, fmt.Sprintf("hplmn_odb=%s", item.HplmnOdb)) } if item.Epstpl != "" { conditions = append(conditions, fmt.Sprintf("epstpl=%s", item.Epstpl)) } if item.Ard != "" { conditions = append(conditions, fmt.Sprintf("ard=%s", item.Ard)) } if item.ContextId != "" { conditions = append(conditions, fmt.Sprintf("context_id=%s", item.ContextId)) } if item.ApnContext != "" { conditions = append(conditions, fmt.Sprintf("apn_context=%s", item.ApnContext)) } // static_ip指给4G UE分配的静态IP,没有可不带此字段名,批量添加IP会自动递增 if item.StaticIp != "" { conditions = append(conditions, fmt.Sprintf("static_ip=%s", item.StaticIp)) } // 其他 if item.SmfSel != "" { conditions = append(conditions, fmt.Sprintf("smf_sel=%s", item.SmfSel)) } if item.SmData != "" { conditions = append(conditions, fmt.Sprintf("sm_data=%s", item.SmData)) } conditions = append(conditions, fmt.Sprintf("cag=%s", item.Cag)) return strings.Join(conditions, ",") }