diff --git a/features/udm_user/model/udm_sub_user.go b/features/udm_user/model/udm_sub_user.go new file mode 100644 index 00000000..cab3c771 --- /dev/null +++ b/features/udm_user/model/udm_sub_user.go @@ -0,0 +1,30 @@ +package model + +// UdmSubUser UDM签约用户 +type UdmSubUser struct { + ID string `json:"id" xorm:"pk 'id' autoincr"` + Msisdn string `json:"msisdn" xorm:"msisdn"` // 相当手机号 + Imsi string `json:"imsi" xorm:"imsi"` // SIM卡号 + Ambr string `json:"ambr" xorm:"ambr"` + Nssai string `json:"nssai" xorm:"nssai"` + Rat string `json:"rat" xorm:"rat"` + Arfb string `json:"arfb" xorm:"arfb"` + Sar string `json:"sar" xorm:"sar"` + Cn string `json:"cn" xorm:"cn"` + SmData string `json:"smData" xorm:"sm_data"` + SmfSel string `json:"smfSel" xorm:"smf_sel"` + EpsDat string `json:"epsDat" xorm:"eps_dat"` + NeID string `json:"neId" xorm:"ne_id"` // UDM网元标识-子系统 + + EpsFlag string `json:"epsFlag" xorm:"eps_flag"` + EpsOdb string `json:"epsOdb" xorm:"eps_odb"` + HplmnOdb string `json:"hplmnOdb" xorm:"hplmn_odb"` + Ard string `json:"ard" xorm:"ard"` + Epstpl string `json:"epstpl" xorm:"epstpl"` + ContextId string `json:"contextId" xorm:"context_id"` + ApnContext string `json:"apnContext" xorm:"apn_context"` + StaticIp string `json:"staticIp" xorm:"static_ip"` + TenantIDs string `json:"tenantIDs" xorm:"tenant_ids"` + + SubNum string `json:"subNum,omitempty" xorm:"-"` // 批量数 +} diff --git a/features/udm_user/repo/repo_udm_sub_user.go b/features/udm_user/repo/repo_udm_sub_user.go new file mode 100644 index 00000000..d4cbb774 --- /dev/null +++ b/features/udm_user/repo/repo_udm_sub_user.go @@ -0,0 +1,489 @@ +package repo + +import ( + "fmt" + "strconv" + "strings" + + "be.ems/features/udm_user/model" + "be.ems/lib/core/datasource" + "be.ems/lib/core/utils/parse" + "be.ems/lib/log" +) + +// 实例化数据层 RepoUdmSubUser 结构体 +var NewRepoUdmSubUser = &RepoUdmSubUser{ + selectSql: `select + id, msisdn, imsi, ambr, nssai, rat, arfb, sar, cn, sm_data, smf_sel, eps_dat, ne_id, eps_flag, eps_odb, hplmn_odb, ard, epstpl, context_id, apn_context, static_ip, + tenant_ids + from u_sub_user`, + + resultMap: map[string]string{ + "id": "ID", + "msisdn": "Msisdn", + "imsi": "Imsi", + "ambr": "Ambr", + "nssai": "Nssai", + "rat": "Rat", + "arfb": "Arfb", + "sar": "Sar", + "cn": "Cn", + "sm_data": "SmData", + "smf_sel": "SmfSel", + "eps_dat": "EpsDat", + "ne_id": "NeID", + "eps_flag": "EpsFlag", + "eps_odb": "EpsOdb", + "hplmn_odb": "HplmnOdb", + "ard": "Ard", + "epstpl": "Epstpl", + "context_id": "ContextId", + "apn_context": "ApnContext", + "static_ip": "StaticIp", + "tenant_ids": "TenantIDs", + }, +} + +// RepoUdmSubUser UDM签约用户 数据层处理 +type RepoUdmSubUser struct { + // 查询视图对象SQL + selectSql string + // 结果字段与实体映射 + resultMap map[string]string +} + +// convertResultRows 将结果记录转实体结果组 +func (r *RepoUdmSubUser) convertResultRows(rows []map[string]any) []model.UdmSubUser { + arr := make([]model.UdmSubUser, 0) + for _, row := range rows { + UdmUser := model.UdmSubUser{} + for key, value := range row { + if keyMapper, ok := r.resultMap[key]; ok { + datasource.SetFieldValue(&UdmUser, keyMapper, value) + } + } + arr = append(arr, UdmUser) + } + return arr +} + +// SelectPage 根据条件分页查询字典类型 +func (r *RepoUdmSubUser) SelectPage(query map[string]any) map[string]any { + // 查询条件拼接 + var conditions []string + var params []any + if v, ok := query["msisdn"]; ok && v != "" { + conditions = append(conditions, "msisdn like concat(concat('%', ?), '%')") + params = append(params, v) + } + if v, ok := query["imsi"]; ok && v != "" { + conditions = append(conditions, "imsi like concat(concat('%', ?), '%')") + params = append(params, v) + } + if v, ok := query["neId"]; ok && v != "" { + conditions = append(conditions, "ne_id = ?") + params = append(params, v) + } + // for multi-tenancy solution + if v, ok := query["tenantIDs"]; ok && v != "" { + conditions = append(conditions, "tenant_ids like concat(concat('%', ?), '%')") + params = append(params, v) + } + + // 构建查询条件语句 + whereSql := "" + if len(conditions) > 0 { + whereSql += " where " + strings.Join(conditions, " and ") + } + + result := map[string]any{ + "total": 0, + "rows": []model.UdmAuthUser{}, + } + + // 查询数量 长度为0直接返回 + totalSql := "select count(1) as 'total' from u_sub_user" + totalRows, err := datasource.RawDB("", totalSql+whereSql, params) + if err != nil { + log.Errorf("total err => %v", err) + return result + } + total := parse.Number(totalRows[0]["total"]) + if total == 0 { + return result + } else { + result["total"] = total + } + + // 分页 + pageNum, pageSize := datasource.PageNumSize(query["pageNum"], query["pageSize"]) + pageSql := " limit ?,? " + params = append(params, pageNum*pageSize) + params = append(params, pageSize) + + // 排序 + sortSql := "" + if v, ok := query["sortField"]; ok && v != "" { + if v == "imsi" { + sortSql += " order by imsi " + } + if v == "msisdn" { + sortSql += " order by msisdn " + } + if o, ok := query["sortOrder"]; ok && o != nil && v != "" { + if o == "desc" { + sortSql += " desc " + } else { + sortSql += " asc " + } + } + } + + // 查询数据 + querySql := r.selectSql + whereSql + sortSql + pageSql + results, err := datasource.RawDB("", querySql, params) + if err != nil { + log.Errorf("query err => %v", err) + return result + } + + // 转换实体 + result["rows"] = r.convertResultRows(results) + return result +} + +// SelectList 根据实体查询 +func (r *RepoUdmSubUser) SelectList(auth model.UdmSubUser) []model.UdmSubUser { + // 查询条件拼接 + var conditions []string + var params []any + if auth.Imsi != "" { + conditions = append(conditions, "imsi = ?") + params = append(params, auth.Imsi) + } + if auth.NeID != "" { + conditions = append(conditions, "ne_id = ?") + params = append(params, auth.NeID) + } + if auth.TenantIDs != "" { + conditions = append(conditions, "tenant_ids like concat(concat('%', ?), '%')") + params = append(params, auth.TenantIDs) + } + + // 构建查询条件语句 + whereSql := "" + if len(conditions) > 0 { + whereSql += " where " + strings.Join(conditions, " and ") + } + + // 查询数据 + querySql := r.selectSql + whereSql + " order by imsi asc " + results, err := datasource.RawDB("", querySql, params) + if err != nil { + log.Errorf("query err => %v", err) + } + + // 转换实体 + return r.convertResultRows(results) +} + +// ClearAndInsert 清空ne_id后新增实体 +func (r *RepoUdmSubUser) ClearAndInsert(neID string, subArr []model.UdmSubUser) int64 { + var num int64 = 0 + + // 清空指定ne_id + _, err := datasource.ExecDB("", "TRUNCATE TABLE u_sub_user", nil) + // _, err := datasource.ExecDB("", "DELETE FROM u_sub_user WHERE ne_id = ?", []any{neID}) + if err != nil { + log.Errorf("TRUNCATE err => %v", err) + } + + n := len(subArr) + batchSize := 2000 + for i := 0; i < n; i += batchSize { + end := i + batchSize + if end > n { + end = n + } + batch := subArr[i:end] + + // multi-tenancy + r.SetTenantIDs(batch) + + // 调用 InsertMulti 函数将批量数据插入数据库 + results, err := datasource.DefaultDB().Table("u_sub_user").InsertMulti(batch) + if err != nil { + log.Errorf("InsertMulti err => %v", err) + continue + } + num += results + } + + // for _, u := range subArr { + // u.NeID = neID + // results, err := datasource.DefaultDB().Table("u_sub_user").Insert(u) + // if err != nil { + // return num + // } + // num += results + // } + return num +} + +// Insert 新增实体 +func (r *RepoUdmSubUser) Insert(subUser model.UdmSubUser) int64 { + results, err := datasource.DefaultDB().Table("u_sub_user").Insert(subUser) + if err != nil { + log.Errorf("Insert err => %v", err) + return results + } + return results +} + +// Insert 批量添加 +func (r *RepoUdmSubUser) Inserts(subUser []model.UdmSubUser) int64 { + var num int64 + n := len(subUser) + batchSize := 2000 + for i := 0; i < n; i += batchSize { + end := i + batchSize + if end > n { + end = n + } + batch := subUser[i:end] + + // multi-tenancy + r.SetTenantIDs(batch) + + // 调用 InsertMulti 函数将批量数据插入数据库 + results, err := datasource.DefaultDB().Table("u_sub_user").InsertMulti(batch) + if err != nil { + log.Errorf("Insert err => %v", err) + continue + } + num += results + } + return num +} + +// Insert4G 批量添加4G用户 +func (r *RepoUdmSubUser) Insert4G(neID string, subUser model.UdmSubUser) int64 { + var insertNum int64 + + imsiV, err := strconv.Atoi(subUser.Imsi) + if err != nil { + return 0 + } + numV, err := strconv.Atoi(subUser.SubNum) + if err != nil { + return 0 + } + + subUser.NeID = neID + for i := 0; i < numV; i++ { + subUser.Imsi = fmt.Sprint(imsiV + i) + + // multi-tenancy + subUserSlice := []model.UdmSubUser{subUser} + r.SetTenantIDs(subUserSlice) + + results, err := datasource.DefaultDB().Table("u_sub_user").Insert(subUser) + if err == nil { + log.Errorf("Insert err => %v", err) + insertNum += results + } + } + + return insertNum +} + +// Update 修改更新 +func (r *RepoUdmSubUser) Update(neID string, authUser model.UdmSubUser) int64 { + // 查询先 + var user model.UdmSubUser + has, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi = ? and ne_id = ?", authUser.Imsi, neID).Get(&user) + if !has || err != nil { + return 0 + } + + if authUser.Msisdn != "" && authUser.Msisdn != user.Msisdn { + user.Msisdn = authUser.Msisdn + } + if authUser.Ambr != "" && authUser.Ambr != user.Ambr { + user.Ambr = authUser.Ambr + } + if authUser.Arfb != "" && authUser.Arfb != user.Arfb { + user.Arfb = authUser.Arfb + } + if authUser.Sar != "" && authUser.Sar != user.Sar { + user.Sar = authUser.Sar + } + if authUser.Rat != "" && authUser.Rat != user.Rat { + user.Rat = authUser.Rat + } + if authUser.Cn != "" && authUser.Cn != user.Cn { + user.Cn = authUser.Cn + } + if authUser.SmfSel != "" && authUser.SmfSel != user.SmfSel { + user.SmfSel = authUser.SmfSel + } + if authUser.SmData != "" && authUser.SmData != user.SmData { + user.SmData = authUser.SmData + } + if authUser.EpsDat != "" && authUser.EpsDat != user.EpsDat { + user.EpsDat = authUser.EpsDat + } + if authUser.EpsFlag != "" && authUser.EpsFlag != user.EpsFlag { + user.EpsFlag = authUser.EpsFlag + } + if authUser.EpsOdb != "" && authUser.EpsDat != user.EpsDat { + user.EpsOdb = authUser.EpsOdb + } + if authUser.HplmnOdb != "" && authUser.HplmnOdb != user.HplmnOdb { + user.HplmnOdb = authUser.HplmnOdb + } + if authUser.Epstpl != "" && authUser.Epstpl != user.Epstpl { + user.Epstpl = authUser.Epstpl + } + if authUser.Ard != "" && authUser.Ard != user.Ard { + user.Ard = authUser.Ard + } + if authUser.ContextId != "" && authUser.ContextId != user.ContextId { + user.ContextId = authUser.ContextId + } + if authUser.ApnContext != "" && authUser.ApnContext != user.ApnContext { + user.ApnContext = authUser.ApnContext + } + if authUser.StaticIp != "" && authUser.StaticIp != user.StaticIp { + user.StaticIp = authUser.StaticIp + } + // for multi-tenancy solution + if authUser.TenantIDs != "" && authUser.TenantIDs != user.TenantIDs { + user.TenantIDs = authUser.TenantIDs + } + + results, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi = ? and ne_id = ?", user.Imsi, user.NeID).Update(user) + if err != nil { + log.Errorf("Update err => %v", err) + return 0 + } + return results +} + +// Update4GIP 批量修改4G IP +func (r *RepoUdmSubUser) Update4GIP(neID string, subUser model.UdmSubUser) int64 { + var insertNum int64 + + imsiV, err := strconv.Atoi(subUser.Imsi) + if err != nil || subUser.StaticIp == "" { + return insertNum + } + numV, err := strconv.Atoi(subUser.SubNum) + if err != nil { + return insertNum + } + + for i := 0; i < numV; i++ { + subUser.Imsi = fmt.Sprint(imsiV + i) + + // 查询先 + var user model.UdmSubUser + has, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi = ? and ne_id = ?", subUser.Imsi, neID).Get(&user) + if has && err == nil { + // IP会自动递增 + parts := strings.Split(subUser.StaticIp, ".") + lastPart := parts[3] + lastNum, _ := strconv.Atoi(lastPart) + lastNum += i + newLastPart := strconv.Itoa(lastNum) + parts[3] = newLastPart + newIP := strings.Join(parts, ".") + user.StaticIp = newIP + // 更新 + results, err := datasource.DefaultDB().Table("u_sub_user").Update(user) + if err == nil { + log.Errorf("Update err => %v", err) + insertNum += results + } + } + } + return insertNum +} + +// UpdateSmData 批量修改sm-data +func (r *RepoUdmSubUser) UpdateSmData(neID string, subUser model.UdmSubUser) int64 { + var insertNum int64 + + imsiV, err := strconv.Atoi(subUser.Imsi) + if err != nil || subUser.StaticIp == "" { + return insertNum + } + numV, err := strconv.Atoi(subUser.SubNum) + if err != nil { + return insertNum + } + + for i := 0; i < numV; i++ { + subUser.Imsi = fmt.Sprint(imsiV + i) + + // 查询先 + var user model.UdmSubUser + has, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi = ? and ne_id = ?", subUser.Imsi, neID).Get(&user) + if has && err == nil { + // IP会自动递增,需提前规划好DNN对应的IP;如dnn不需要绑定IP则不带此字段名 + // parts := strings.Split(subUser.SmData, "&") + user.SmData = subUser.SmData + // 更新 + results, err := datasource.DefaultDB().Table("u_sub_user").Update(user) + if err == nil { + log.Errorf("Update err => %v", err) + insertNum += results + } + } + } + return insertNum +} + +// Delete 删除实体 +func (r *RepoUdmSubUser) Delete(neID, imsi string) int64 { + results, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi = ? and ne_id = ?", imsi, neID).Delete() + if err != nil { + log.Errorf("Delete err => %v", err) + return results + } + return results +} + +// Delete 删除范围实体 +func (r *RepoUdmSubUser) Deletes(neID, imsi, num string) int64 { + imsiV, err := strconv.Atoi(imsi) + if err != nil { + return 0 + } + + numV, err := strconv.Atoi(num) + if err != nil { + return 0 + } + + results, err := datasource.DefaultDB().Table("u_sub_user").Where("imsi >= ? and imsi < ? and ne_id = ?", imsiV, imsiV+numV, neID).Delete() + if err != nil { + log.Errorf("Delete err => %v", err) + return results + } + return results +} + +// multi-tenancy solution, get tenant_ids by imsi +func (r *RepoUdmSubUser) SetTenantIDs(subArr []model.UdmSubUser) { + for s := 0; s < len(subArr); s++ { + var tenantIDs []string + err := datasource.DefaultDB().Table("sys_tenant_map"). + Where("mapping_type='UE' and mapping_key like ?", subArr[s].Imsi).Cols("tenant_id").Find(tenantIDs) + if err != nil { + log.Errorf("Find tenant_ids err => %v", err) + continue + } + subArr[s].TenantIDs = strings.Join(tenantIDs, ",") + } +} diff --git a/restagent/etc/restconf.yaml b/restagent/etc/restconf.yaml index 9fb9aeac..f4077422 100644 --- a/restagent/etc/restconf.yaml +++ b/restagent/etc/restconf.yaml @@ -59,14 +59,14 @@ redis: # OMC系统使用库 default: port: 6379 # Redis port - host: "192.168.2.219" # Redis host - password: "123456" + host: "127.0.0.1" # Redis host + password: "helloearth" db: 10 # Redis db_num # UDM网元用户库 udmuser: port: 6379 # Redis port - host: "192.168.2.219" - password: "123456" + host: "127.0.0.1" + password: "helloearth" db: 0 # Redis db_num # 多个数据源时可以用这个指定默认的数据源 defaultDataSourceName: "default" diff --git a/src/modules/network_element/model/udm_sub.go b/src/modules/network_element/model/udm_sub.go index 55a56ef1..e295fb9d 100644 --- a/src/modules/network_element/model/udm_sub.go +++ b/src/modules/network_element/model/udm_sub.go @@ -24,6 +24,7 @@ type UDMSub struct { ContextId string `json:"contextId" gorm:"column:context_id"` ApnContext string `json:"apnContext" gorm:"column:apn_context"` StaticIp string `json:"staticIp" gorm:"column:static_ip"` + TenantIDs string `json:"tenantIDs" gorm:"column:tenant_ids"` // ====== 非数据库字段属性 ====== diff --git a/src/modules/network_element/repository/udm_sub.impl.go b/src/modules/network_element/repository/udm_sub.impl.go index 9a1dc91a..b16bdc0a 100644 --- a/src/modules/network_element/repository/udm_sub.impl.go +++ b/src/modules/network_element/repository/udm_sub.impl.go @@ -14,7 +14,8 @@ import ( // 实例化数据层 UDMSubImpl 结构体 var NewUDMSubImpl = &UDMSubImpl{ selectSql: `select - id, msisdn, imsi, ambr, nssai, rat, arfb, sar, cn, sm_data, smf_sel, eps_dat, ne_id, eps_flag, eps_odb, hplmn_odb, ard, epstpl, context_id, apn_context, static_ip + id, msisdn, imsi, ambr, nssai, rat, arfb, sar, cn, sm_data, smf_sel, eps_dat, ne_id, eps_flag, eps_odb, hplmn_odb, ard, epstpl, context_id, apn_context, static_ip, + tenant_ids from u_sub_user`, resultMap: map[string]string{ @@ -39,6 +40,7 @@ var NewUDMSubImpl = &UDMSubImpl{ "context_id": "ContextId", "apn_context": "ApnContext", "static_ip": "StaticIp", + "tenant_ids": "TenantIDs", // Tenant ID for multi-tenancy }, } @@ -93,6 +95,11 @@ func (r *UDMSubImpl) SelectPage(query map[string]any) map[string]any { conditions = append(conditions, "ne_id = ?") params = append(params, v) } + // for multi-tenancy solution + if v, ok := query["tenantIDs"]; ok && v != "" { + conditions = append(conditions, "tenant_ids like concat(concat('%', ?), '%')") + params = append(params, v) + } // 构建查询条件语句 whereSql := ""