feat: 数据库引用变更
This commit is contained in:
209
features/ue/ims_user/service.go
Normal file
209
features/ue/ims_user/service.go
Normal file
@@ -0,0 +1,209 @@
|
||||
package ims_user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"be.ems/src/framework/database/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
|
||||
Tag: 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.Tag = data["volte_tag"]
|
||||
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长度15,ki长度32,opc长度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.Tag != "" {
|
||||
conditions = append(conditions, fmt.Sprintf("volte=%s", item.Tag))
|
||||
}
|
||||
if item.VNI != "" {
|
||||
conditions = append(conditions, fmt.Sprintf("vni=%s", item.VNI))
|
||||
}
|
||||
|
||||
return strings.Join(conditions, ",")
|
||||
}
|
||||
234
features/ue/service/ims_user.go
Normal file
234
features/ue/service/ims_user.go
Normal file
@@ -0,0 +1,234 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"be.ems/features/ue/model"
|
||||
"be.ems/features/ue/repository"
|
||||
"be.ems/src/framework/database/redis"
|
||||
neService "be.ems/src/modules/network_element/service"
|
||||
)
|
||||
|
||||
// 实例化服务层 IMSUserService 结构体
|
||||
var NewIMSUserService = &IMSUserService{
|
||||
imsUserRepository: repository.NewIMSUserRepository,
|
||||
}
|
||||
|
||||
// VoLTE用户信息 服务层处理
|
||||
type IMSUserService struct {
|
||||
imsUserRepository *repository.IMSUserRepository // VoLTE用户信息数据信息
|
||||
}
|
||||
|
||||
// dataByRedis UDM签约用户 db:0 中 volte:*
|
||||
func (r *IMSUserService) dataByRedis(imsi, neId string) []model.IMSUser {
|
||||
arr := []model.IMSUser{}
|
||||
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 := model.IMSUser{
|
||||
NeId: neId,
|
||||
IMSI: imsi, // volte:360000100000130:8612300000130
|
||||
MSISDN: msisdn, // 8612300000130
|
||||
Tag: model.ParseCallTag(m["tag"]), // volte = tag
|
||||
VNI: vni, // ims.mnc001.mcc110.3gppnetwork.org
|
||||
}
|
||||
arr = append(arr, a)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// ResetData 重置鉴权用户数据,清空数据库重新同步Redis数据
|
||||
func (r *IMSUserService) ResetData(neId string) int64 {
|
||||
subArr := r.dataByRedis("*", neId)
|
||||
// 数据清空后添加
|
||||
go r.imsUserRepository.ClearAndInsert(neId, subArr)
|
||||
return int64(len(subArr))
|
||||
}
|
||||
|
||||
// ParseInfo 解析单个用户imsi签约信息 data从命令MML得到的结果
|
||||
func (r *IMSUserService) ParseInfo(imsi, neId string, data map[string]string) model.IMSUser {
|
||||
u := r.imsUserRepository.SelectByIMSIAndNeID(imsi, neId)
|
||||
|
||||
msisdn := data["msisdn"]
|
||||
if imsMsisdnLen := strings.Index(msisdn, ","); imsMsisdnLen != -1 {
|
||||
msisdn = msisdn[:imsMsisdnLen]
|
||||
}
|
||||
var vni string = "-"
|
||||
impiParts := strings.Split(data["impi"], "@")
|
||||
if len(impiParts) > 1 {
|
||||
vni = impiParts[1] // 输出: ims.mnc001.mcc110.3gppnetwork.org
|
||||
}
|
||||
|
||||
// 用于更新
|
||||
u.NeId = neId
|
||||
u.IMSI = imsi
|
||||
u.MSISDN = msisdn
|
||||
u.Tag = model.ParseCallTag(data["volte_tag"])
|
||||
u.VNI = vni
|
||||
return u
|
||||
}
|
||||
|
||||
// SelectPage 分页查询数据库
|
||||
func (r *IMSUserService) SelectPage(query map[string]any) map[string]any {
|
||||
return r.imsUserRepository.SelectPage(query)
|
||||
}
|
||||
|
||||
// SelectList 查询数据库
|
||||
func (r *IMSUserService) SelectList(u model.IMSUser) []model.IMSUser {
|
||||
return r.imsUserRepository.SelectList(u)
|
||||
}
|
||||
|
||||
// Insert 从数据中读取后删除imsi再存入数据库
|
||||
// imsi长度15,ki长度32,opc长度0或者32
|
||||
func (r *IMSUserService) Insert(neId string, u model.IMSUser) int64 {
|
||||
uArr := r.dataByRedis(u.IMSI+":*", neId)
|
||||
if len(uArr) > 0 {
|
||||
r.imsUserRepository.Delete(u.IMSI, neId)
|
||||
return r.imsUserRepository.Inserts(uArr)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// InsertData 导入文件数据 dataType目前两种:txt/csv
|
||||
func (r *IMSUserService) 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.imsUserRepository.DeletePrefixByIMSI(prefix, neId)
|
||||
num += r.imsUserRepository.Inserts(arr)
|
||||
}
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
// Delete 删除单个不重新加载
|
||||
func (r *IMSUserService) Delete(neId, imsi string) int64 {
|
||||
return r.imsUserRepository.Delete(imsi, neId)
|
||||
}
|
||||
|
||||
// LoadData 重新加载从imsi开始num的数据
|
||||
func (r *IMSUserService) 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++ {
|
||||
var keyIMSI string
|
||||
if len(imsi) == model.IMSI_MAX_LENGTH {
|
||||
keyIMSI = fmt.Sprintf("%015d", startIMSI+i)
|
||||
} else {
|
||||
// 处理不满15位的IMSI, tag=TAG_VoIP
|
||||
keyIMSI = fmt.Sprintf("%d", startIMSI+i)
|
||||
}
|
||||
|
||||
// 删除原数据
|
||||
r.imsUserRepository.Delete(keyIMSI, neId)
|
||||
arr := r.dataByRedis(keyIMSI+":*", neId)
|
||||
if len(arr) < 1 {
|
||||
continue
|
||||
}
|
||||
r.imsUserRepository.Inserts(arr)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseCommandParams 解析数据组成命令参数 msisdn=xx,xx=xx,...
|
||||
func (r *IMSUserService) ParseCommandParams(item model.IMSUser) string {
|
||||
var conditions []string
|
||||
if item.MSISDN != "" {
|
||||
conditions = append(conditions, fmt.Sprintf("msisdn=%s", item.MSISDN))
|
||||
}
|
||||
|
||||
if item.Tag != model.ParseCallTag("") {
|
||||
conditions = append(conditions, fmt.Sprintf("volte=%d", item.Tag))
|
||||
}
|
||||
if item.VNI != "" {
|
||||
conditions = append(conditions, fmt.Sprintf("vni=%s", item.VNI))
|
||||
}
|
||||
|
||||
return strings.Join(conditions, ",")
|
||||
}
|
||||
|
||||
// ResetDataWithResult 重置鉴权用户数据,清空数据库重新同步Redis数据
|
||||
// 通过 channel 返回 ClearAndInsert 的执行结果
|
||||
func (r *IMSUserService) ResetDataWithResult(neId string) chan int64 {
|
||||
arr := r.dataByRedis("*", neId)
|
||||
resultCh := make(chan int64, 1)
|
||||
go func() {
|
||||
resultCh <- r.imsUserRepository.ClearAndInsert(neId, arr)
|
||||
}()
|
||||
return resultCh
|
||||
}
|
||||
186
features/ue/service/voip_auth.go
Normal file
186
features/ue/service/voip_auth.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"be.ems/features/ue/model"
|
||||
"be.ems/features/ue/repository"
|
||||
"be.ems/src/framework/database/redis"
|
||||
neService "be.ems/src/modules/network_element/service"
|
||||
)
|
||||
|
||||
// 实例化服务层 VoIPAuthService 结构体
|
||||
var NewVoIPAuthService = &VoIPAuthService{
|
||||
voipAuthRepository: repository.NewVoIPAuthRepository,
|
||||
}
|
||||
|
||||
// VoLTE用户信息 服务层处理
|
||||
type VoIPAuthService struct {
|
||||
voipAuthRepository *repository.VoIPAuthRepository // VoLTE用户信息数据信息
|
||||
}
|
||||
|
||||
// dataByRedis VoIP鉴权数据 db:0 中 voip:*
|
||||
func (r *VoIPAuthService) dataByRedis(userName, neId string) []model.VoIPAuth {
|
||||
arr := []model.VoIPAuth{}
|
||||
key := fmt.Sprintf("voip:%s", userName)
|
||||
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 userName string
|
||||
KeyParts := strings.Split(k, ":")
|
||||
if len(KeyParts) > 1 {
|
||||
userName = KeyParts[1]
|
||||
}
|
||||
|
||||
a := model.VoIPAuth{
|
||||
NeId: neId,
|
||||
UserName: userName, // userName
|
||||
Password: m["password"], //
|
||||
}
|
||||
arr = append(arr, a)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// ResetData 重置鉴权用户数据,清空数据库重新同步Redis数据
|
||||
func (r *VoIPAuthService) ResetData(neId string) int64 {
|
||||
subArr := r.dataByRedis("*", neId)
|
||||
// 数据清空后添加
|
||||
go r.voipAuthRepository.ClearAndInsert(neId, subArr)
|
||||
return int64(len(subArr))
|
||||
}
|
||||
|
||||
// ParseInfo 解析单个用户userName信息 data从命令MML得到的结果
|
||||
func (r *VoIPAuthService) ParseInfo(userName, neId string, data map[string]string) model.VoIPAuth {
|
||||
u := r.voipAuthRepository.SelectByUserNameAndNeID(userName, neId)
|
||||
password := data["password"]
|
||||
|
||||
// 用于更新
|
||||
u.NeId = neId
|
||||
u.UserName = userName
|
||||
u.Password = password
|
||||
return u
|
||||
}
|
||||
|
||||
// SelectPage 分页查询数据库
|
||||
func (r *VoIPAuthService) SelectPage(query map[string]any) map[string]any {
|
||||
return r.voipAuthRepository.SelectPage(query)
|
||||
}
|
||||
|
||||
// SelectList 查询数据库
|
||||
func (r *VoIPAuthService) SelectList(u model.VoIPAuth) []model.VoIPAuth {
|
||||
return r.voipAuthRepository.SelectList(u)
|
||||
}
|
||||
|
||||
// Insert 从数据中读取后删除userName再存入数据库
|
||||
func (r *VoIPAuthService) Insert(neId string, u model.VoIPAuth) int64 {
|
||||
uArr := r.dataByRedis(u.UserName, neId)
|
||||
if len(uArr) > 0 {
|
||||
r.voipAuthRepository.Delete(u.UserName, neId)
|
||||
return r.voipAuthRepository.Inserts(uArr)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// InsertData 导入文件数据 dataType目前两种:txt/csv
|
||||
func (r *VoIPAuthService) InsertData(neId, dataType string, data any) int64 {
|
||||
userNames := make(map[string]struct{})
|
||||
|
||||
if dataType == "csv" {
|
||||
for _, v := range data.([]map[string]string) {
|
||||
userName := v["userName"]
|
||||
// if len(userName) < 6 {
|
||||
// continue
|
||||
// }
|
||||
// prefix := userName[:len(userName)-4]
|
||||
userNames[userName] = struct{}{}
|
||||
}
|
||||
}
|
||||
if dataType == "txt" {
|
||||
for _, v := range data.([][]string) {
|
||||
userName := v[0]
|
||||
// if len(userName) < 6 {
|
||||
// continue
|
||||
// }
|
||||
// prefix := userName[:len(userName)-4]
|
||||
userNames[userName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// 根据前缀重新加载插入
|
||||
var num int64 = 0
|
||||
for userName := range userNames {
|
||||
// keys voip:11111
|
||||
arr := r.dataByRedis(userName, neId)
|
||||
if len(arr) > 0 {
|
||||
r.voipAuthRepository.DeleteByUserName(userName, neId)
|
||||
num += r.voipAuthRepository.Inserts(arr)
|
||||
}
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
// Delete 删除单个不重新加载
|
||||
func (r *VoIPAuthService) Delete(neId, userName string) int64 {
|
||||
return r.voipAuthRepository.Delete(userName, neId)
|
||||
}
|
||||
|
||||
// LoadData 重新加载从userName开始num的数据
|
||||
func (r *VoIPAuthService) LoadData(neId, userName, num string) {
|
||||
startUserName, _ := strconv.ParseInt(userName, 10, 64)
|
||||
subNum, _ := strconv.ParseInt(num, 10, 64)
|
||||
var i int64
|
||||
for i = 0; i < subNum; i++ {
|
||||
keyUserName := fmt.Sprintf("%d", startUserName+i)
|
||||
// 删除原数据
|
||||
r.voipAuthRepository.Delete(keyUserName, neId)
|
||||
arr := r.dataByRedis(keyUserName, neId)
|
||||
if len(arr) < 1 {
|
||||
continue
|
||||
}
|
||||
r.voipAuthRepository.Inserts(arr)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseCommandParams 解析数据组成命令参数 msisdn=xx,xx=xx,...
|
||||
func (r *VoIPAuthService) ParseCommandParams(item model.VoIPAuth) string {
|
||||
var conditions []string
|
||||
if item.Password != "" {
|
||||
conditions = append(conditions, fmt.Sprintf("password=%s", item.Password))
|
||||
}
|
||||
|
||||
return strings.Join(conditions, ",")
|
||||
}
|
||||
|
||||
// ResetDataWithResult 重置鉴权用户数据,清空数据库重新同步Redis数据
|
||||
// 通过 channel 返回 ClearAndInsert 的执行结果
|
||||
func (r *VoIPAuthService) ResetDataWithResult(neId string) chan int64 {
|
||||
arr := r.dataByRedis("*", neId)
|
||||
resultCh := make(chan int64, 1)
|
||||
go func() {
|
||||
resultCh <- r.voipAuthRepository.ClearAndInsert(neId, arr)
|
||||
}()
|
||||
return resultCh
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/cron"
|
||||
"be.ems/src/framework/datasource"
|
||||
"be.ems/src/framework/database/db"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/logger"
|
||||
"be.ems/src/framework/redis"
|
||||
)
|
||||
|
||||
//go:embed config/*.yaml
|
||||
@@ -23,7 +23,7 @@ func ConfigurationInit() {
|
||||
// 初始程序日志
|
||||
logger.InitLogger()
|
||||
// 连接数据库实例
|
||||
datasource.Connect()
|
||||
db.Connect()
|
||||
// 连接Redis实例
|
||||
redis.Connect()
|
||||
// 启动调度任务实例
|
||||
@@ -37,7 +37,7 @@ func ConfigurationClose() {
|
||||
// 关闭Redis实例
|
||||
redis.Close()
|
||||
// 关闭数据库实例
|
||||
datasource.Close()
|
||||
db.Close()
|
||||
// 关闭程序日志
|
||||
logger.Close()
|
||||
}
|
||||
|
||||
207
src/framework/database/db/db.go
Normal file
207
src/framework/database/db/db.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
gormLog "gorm.io/gorm/logger"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/logger"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
)
|
||||
|
||||
// 数据库连接实例
|
||||
var dbMap = make(map[string]*gorm.DB)
|
||||
|
||||
type dialectInfo struct {
|
||||
dialectic gorm.Dialector
|
||||
logging bool
|
||||
}
|
||||
|
||||
// 载入数据库连接
|
||||
func loadDialect() map[string]dialectInfo {
|
||||
dialects := make(map[string]dialectInfo)
|
||||
|
||||
// 读取数据源配置
|
||||
datasource := config.Get("gorm.datasource").(map[string]any)
|
||||
for key, value := range datasource {
|
||||
item := value.(map[string]any)
|
||||
// 数据库类型对应的数据库连接
|
||||
switch item["type"] {
|
||||
case "mysql":
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
item["username"],
|
||||
item["password"],
|
||||
item["host"],
|
||||
item["port"],
|
||||
item["database"],
|
||||
)
|
||||
dialects[key] = dialectInfo{
|
||||
dialectic: mysql.Open(dsn),
|
||||
logging: parse.Boolean(item["logging"]),
|
||||
}
|
||||
default:
|
||||
logger.Warnf("%s: %v\n Not Load DB Config Type", key, item)
|
||||
}
|
||||
}
|
||||
|
||||
return dialects
|
||||
}
|
||||
|
||||
// 载入连接日志配置
|
||||
func loadLogger() gormLog.Interface {
|
||||
newLogger := gormLog.New(
|
||||
log.New(os.Stdout, "[GORM] ", log.LstdFlags), // 将日志输出到控制台
|
||||
gormLog.Config{
|
||||
SlowThreshold: time.Second, // Slow SQL 阈值
|
||||
LogLevel: gormLog.Info, // 日志级别 Silent不输出任何日志
|
||||
ParameterizedQueries: false, // 参数化查询SQL 用实际值带入?的执行语句
|
||||
Colorful: false, // 彩色日志输出
|
||||
},
|
||||
)
|
||||
return newLogger
|
||||
}
|
||||
|
||||
// Connect 连接数据库实例
|
||||
func Connect() {
|
||||
// 遍历进行连接数据库实例
|
||||
for key, info := range loadDialect() {
|
||||
opts := &gorm.Config{
|
||||
Logger: gormLog.Discard,
|
||||
}
|
||||
// 是否需要日志输出
|
||||
if info.logging {
|
||||
opts.Logger = loadLogger()
|
||||
}
|
||||
// 创建连接
|
||||
db, err := gorm.Open(info.dialectic, opts)
|
||||
if err != nil {
|
||||
logger.Errorf("failed error db connect: %s", err)
|
||||
continue
|
||||
}
|
||||
// 获取底层 SQL 数据库连接
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
logger.Fatalf("failed error underlying SQL database: %v", err)
|
||||
}
|
||||
// 测试数据库连接
|
||||
err = sqlDB.Ping()
|
||||
if err != nil {
|
||||
logger.Fatalf("failed error ping database: %v", err)
|
||||
}
|
||||
// SetMaxIdleConns 用于设置连接池中空闲连接的最大数量。
|
||||
sqlDB.SetMaxIdleConns(10)
|
||||
// SetMaxOpenConns 设置打开数据库连接的最大数量。
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
// SetConnMaxLifetime 设置了连接可复用的最大时间。
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
logger.Infof("database %s connection is successful.", key)
|
||||
dbMap[key] = db
|
||||
}
|
||||
}
|
||||
|
||||
// Close 关闭数据库实例
|
||||
func Close() {
|
||||
for _, db := range dbMap {
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if err := sqlDB.Close(); err != nil {
|
||||
logger.Errorf("fatal error db close: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DB 获取数据源
|
||||
//
|
||||
// source-数据源
|
||||
func DB(source string) *gorm.DB {
|
||||
// 不指定时获取默认实例
|
||||
if source == "" {
|
||||
source = config.Get("gorm.defaultDataSourceName").(string)
|
||||
}
|
||||
db := dbMap[source]
|
||||
if db == nil {
|
||||
logger.Errorf("not database source: %s", source)
|
||||
return nil
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
// Names 获取数据源名称列表
|
||||
func Names() []string {
|
||||
var names []string
|
||||
for key := range dbMap {
|
||||
names = append(names, key)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// RawDB 原生语句查询
|
||||
//
|
||||
// source-数据源
|
||||
// sql-预编译的SQL语句
|
||||
// parameters-预编译的SQL语句参数
|
||||
func RawDB(source string, sql string, parameters []any) ([]map[string]any, error) {
|
||||
var rows []map[string]any
|
||||
// 数据源
|
||||
db := DB(source)
|
||||
if db == nil {
|
||||
return rows, fmt.Errorf("not database source")
|
||||
}
|
||||
// 使用正则表达式替换连续的空白字符为单个空格
|
||||
fmtSql := regexp.MustCompile(`\s+`).ReplaceAllString(sql, " ")
|
||||
// 查询结果
|
||||
res := db.Raw(fmtSql, parameters...).Scan(&rows)
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
// ExecDB 原生语句执行
|
||||
//
|
||||
// source-数据源
|
||||
// sql-预编译的SQL语句
|
||||
// parameters-预编译的SQL语句参数
|
||||
func ExecDB(source string, sql string, parameters []any) (int64, error) {
|
||||
// 数据源
|
||||
db := DB(source)
|
||||
if db == nil {
|
||||
return 0, fmt.Errorf("not database source")
|
||||
}
|
||||
// 使用正则表达式替换连续的空白字符为单个空格
|
||||
fmtSql := regexp.MustCompile(`\s+`).ReplaceAllString(sql, " ")
|
||||
// 执行结果
|
||||
res := db.Exec(fmtSql, parameters...)
|
||||
if res.Error != nil {
|
||||
return 0, res.Error
|
||||
}
|
||||
return res.RowsAffected, nil
|
||||
}
|
||||
|
||||
// PageNumSize 分页页码记录数
|
||||
//
|
||||
// pageNum-页码
|
||||
// pageSize-记录数
|
||||
func PageNumSize(pageNum, pageSize any) (int, int) {
|
||||
// 记录起始索引
|
||||
num := parse.Number(pageNum)
|
||||
if num < 1 {
|
||||
num = 1
|
||||
}
|
||||
|
||||
// 显示记录数
|
||||
size := parse.Number(pageSize)
|
||||
if size < 0 {
|
||||
size = 10
|
||||
}
|
||||
return int(num - 1), int(size)
|
||||
}
|
||||
104
src/framework/database/db/expand.go
Normal file
104
src/framework/database/db/expand.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
)
|
||||
|
||||
// ImportSQL 导入SQL
|
||||
func ImportSQL() {
|
||||
sqlPath := config.Get("sqlPath").(string)
|
||||
if sqlPath == "" {
|
||||
return
|
||||
}
|
||||
sqlSource := config.Get("sqlSource").(string)
|
||||
if sqlSource == "" {
|
||||
sqlSource = config.Get("database.defaultDataSourceName").(string)
|
||||
}
|
||||
|
||||
// 数据源
|
||||
db := DB(sqlSource)
|
||||
if db == nil {
|
||||
log.Fatalln("not database source")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取路径信息
|
||||
fileInfo, err := os.Stat(sqlPath)
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 处理目录或文件
|
||||
if fileInfo.IsDir() {
|
||||
// 处理目录
|
||||
files, err := os.ReadDir(sqlPath)
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
if !strings.HasSuffix(file.Name(), ".sql") {
|
||||
continue
|
||||
}
|
||||
processSQLFile(db, filepath.Join(sqlPath, file.Name()))
|
||||
}
|
||||
} else {
|
||||
// 处理单个文件
|
||||
processSQLFile(db, sqlPath)
|
||||
}
|
||||
|
||||
log.Println("process success")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// 处理单个SQL文件的通用函数
|
||||
func processSQLFile(db *gorm.DB, filePath string) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 逐行读取 SQL 文件
|
||||
scanner := bufio.NewScanner(file)
|
||||
var sqlBuilder strings.Builder
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
// 跳过注释和空行
|
||||
if strings.HasPrefix(line, "--") || strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
// 跳过配置语句
|
||||
if strings.HasPrefix(line, "/*!") {
|
||||
continue
|
||||
}
|
||||
|
||||
sqlBuilder.WriteString(line + "\n")
|
||||
|
||||
// 当遇到分号时,执行 SQL 语句
|
||||
if strings.HasSuffix(line, ";") {
|
||||
// 执行 SQL 语句
|
||||
if err := db.Exec(sqlBuilder.String()).Error; err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
sqlBuilder.Reset()
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,35 +2,21 @@ package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/logger"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// Redis连接实例
|
||||
var rdbMap = make(map[string]*redis.Client)
|
||||
|
||||
// 声明定义限流脚本命令
|
||||
var rateLimitCommand = redis.NewScript(`
|
||||
local key = KEYS[1]
|
||||
local time = tonumber(ARGV[1])
|
||||
local count = tonumber(ARGV[2])
|
||||
local current = redis.call('get', key);
|
||||
if current and tonumber(current) >= count then
|
||||
return tonumber(current);
|
||||
end
|
||||
current = redis.call('incr', key)
|
||||
if tonumber(current) == 1 then
|
||||
redis.call('expire', key, time)
|
||||
end
|
||||
return tonumber(current);`)
|
||||
|
||||
// 连接Redis实例
|
||||
// Connect 连接Redis实例
|
||||
func Connect() {
|
||||
ctx := context.Background()
|
||||
// 读取数据源配置
|
||||
@@ -47,29 +33,23 @@ func Connect() {
|
||||
// 测试数据库连接
|
||||
pong, err := rdb.Ping(ctx).Result()
|
||||
if err != nil {
|
||||
logger.Fatalf("Ping redis %s is %v", k, err)
|
||||
logger.Fatalf("failed error redis connect: %s is %v", k, err)
|
||||
}
|
||||
logger.Infof("redis %s %s %d connection is successful.", k, pong, client["db"].(int))
|
||||
logger.Infof("redis %s %d %s connection is successful.", k, client["db"].(int), pong)
|
||||
rdbMap[k] = rdb
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭Redis实例
|
||||
// Close 关闭Redis实例
|
||||
func Close() {
|
||||
for _, rdb := range rdbMap {
|
||||
if err := rdb.Close(); err != nil {
|
||||
logger.Errorf("fatal error db close: %s", err)
|
||||
logger.Errorf("redis db close: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取默认实例
|
||||
func DefaultRDB() *redis.Client {
|
||||
source := config.Get("redis.defaultDataSourceName").(string)
|
||||
return rdbMap[source]
|
||||
}
|
||||
|
||||
// 获取实例
|
||||
// RDB 获取实例
|
||||
func RDB(source string) *redis.Client {
|
||||
// 不指定时获取默认实例
|
||||
if source == "" {
|
||||
@@ -80,18 +60,19 @@ func RDB(source string) *redis.Client {
|
||||
|
||||
// Info 获取redis服务信息
|
||||
func Info(source string) map[string]map[string]string {
|
||||
infoObj := make(map[string]map[string]string)
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return infoObj
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
info, err := rdb.Info(ctx).Result()
|
||||
if err != nil {
|
||||
return map[string]map[string]string{}
|
||||
return infoObj
|
||||
}
|
||||
infoObj := make(map[string]map[string]string)
|
||||
|
||||
lines := strings.Split(info, "\r\n")
|
||||
label := ""
|
||||
for _, line := range lines {
|
||||
@@ -114,9 +95,9 @@ func Info(source string) map[string]map[string]string {
|
||||
// KeySize 获取redis当前连接可用键Key总数信息
|
||||
func KeySize(source string) int64 {
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -129,18 +110,19 @@ func KeySize(source string) int64 {
|
||||
|
||||
// CommandStats 获取redis命令状态信息
|
||||
func CommandStats(source string) []map[string]string {
|
||||
statsObjArr := make([]map[string]string, 0)
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return statsObjArr
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
commandstats, err := rdb.Info(ctx, "commandstats").Result()
|
||||
if err != nil {
|
||||
return []map[string]string{}
|
||||
return statsObjArr
|
||||
}
|
||||
statsObjArr := make([]map[string]string, 0)
|
||||
|
||||
lines := strings.Split(commandstats, "\r\n")
|
||||
for _, line := range lines {
|
||||
if !strings.HasPrefix(line, "cmdstat_") {
|
||||
@@ -157,12 +139,12 @@ func CommandStats(source string) []map[string]string {
|
||||
return statsObjArr
|
||||
}
|
||||
|
||||
// 获取键的剩余有效时间(秒)
|
||||
func GetExpire(source string, key string) (float64, error) {
|
||||
// GetExpire 获取键的剩余有效时间(秒)
|
||||
func GetExpire(source string, key string) (int64, error) {
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return 0, fmt.Errorf("redis not client")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -170,168 +152,195 @@ func GetExpire(source string, key string) (float64, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ttl.Seconds(), nil
|
||||
return int64(ttl.Seconds()), nil
|
||||
}
|
||||
|
||||
// 获得缓存数据的key列表
|
||||
func GetKeys(source string, match string) ([]string, error) {
|
||||
// GetKeys 获得缓存数据的key列表
|
||||
func GetKeys(source string, pattern string) ([]string, error) {
|
||||
keys := make([]string, 0)
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return keys, fmt.Errorf("redis not client")
|
||||
}
|
||||
|
||||
keys := make([]string, 0)
|
||||
// 游标
|
||||
var cursor uint64 = 0
|
||||
var count int64 = 100
|
||||
ctx := context.Background()
|
||||
iter := rdb.Scan(ctx, 0, match, 1000).Iterator()
|
||||
if err := iter.Err(); err != nil {
|
||||
logger.Errorf("Failed to scan keys: %v", err)
|
||||
// 循环遍历获取匹配的键
|
||||
for {
|
||||
// 使用 SCAN 命令获取匹配的键
|
||||
batchKeys, nextCursor, err := rdb.Scan(ctx, cursor, pattern, count).Result()
|
||||
if err != nil {
|
||||
logger.Errorf("failed to scan keys: %v", err)
|
||||
return keys, err
|
||||
}
|
||||
for iter.Next(ctx) {
|
||||
keys = append(keys, iter.Val())
|
||||
cursor = nextCursor
|
||||
keys = append(keys, batchKeys...)
|
||||
// 当 cursor 为 0,表示遍历完成
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// 批量获得缓存数据
|
||||
// GetBatch 批量获得缓存数据
|
||||
func GetBatch(source string, keys []string) ([]any, error) {
|
||||
result := make([]any, 0)
|
||||
if len(keys) == 0 {
|
||||
return []any{}, fmt.Errorf("not keys")
|
||||
return result, fmt.Errorf("not keys")
|
||||
}
|
||||
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return result, fmt.Errorf("redis not client")
|
||||
}
|
||||
|
||||
// 获取缓存数据
|
||||
result, err := rdb.MGet(context.Background(), keys...).Result()
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to get batch data: %v", err)
|
||||
return []any{}, err
|
||||
v, err := rdb.MGet(context.Background(), keys...).Result()
|
||||
if err != nil || errors.Is(err, redis.Nil) {
|
||||
logger.Errorf("failed to get batch data: %v", err)
|
||||
return result, err
|
||||
}
|
||||
return result, nil
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// 获得缓存数据
|
||||
// Get 获得缓存数据
|
||||
func Get(source, key string) (string, error) {
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return "", fmt.Errorf("redis not client")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
value, err := rdb.Get(ctx, key).Result()
|
||||
if err == redis.Nil || err != nil {
|
||||
v, err := rdb.Get(ctx, key).Result()
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return "", fmt.Errorf("no keys")
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return value, nil
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// 判断是否存在
|
||||
func Has(source string, keys ...string) (bool, error) {
|
||||
// Has 判断是否存在
|
||||
func Has(source string, keys ...string) (int64, error) {
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return 0, fmt.Errorf("redis not client")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
exists, err := rdb.Exists(ctx, keys...).Result()
|
||||
if err != nil {
|
||||
return false, err
|
||||
return 0, err
|
||||
}
|
||||
return exists >= 1, nil
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// 设置缓存数据
|
||||
func Set(source, key string, value any) (bool, error) {
|
||||
// Set 设置缓存数据
|
||||
func Set(source, key string, value any) error {
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return fmt.Errorf("redis not client")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
err := rdb.Set(ctx, key, value, 0).Err()
|
||||
if err != nil {
|
||||
logger.Errorf("redis Set err %v", err)
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
return true, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// 设置缓存数据与过期时间
|
||||
func SetByExpire(source, key string, value any, expiration time.Duration) (bool, error) {
|
||||
// SetByExpire 设置缓存数据与过期时间
|
||||
func SetByExpire(source, key string, value any, expiration time.Duration) error {
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return fmt.Errorf("redis not client")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
err := rdb.Set(ctx, key, value, expiration).Err()
|
||||
if err != nil {
|
||||
logger.Errorf("redis SetByExpire err %v", err)
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
return true, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除单个
|
||||
func Del(source string, key string) (bool, error) {
|
||||
// Del 删除单个
|
||||
func Del(source string, key string) error {
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return fmt.Errorf("redis not client")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
err := rdb.Del(ctx, key).Err()
|
||||
if err != nil {
|
||||
if err := rdb.Del(ctx, key).Err(); err != nil {
|
||||
logger.Errorf("redis Del err %v", err)
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
return true, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除多个
|
||||
func DelKeys(source string, keys []string) (bool, error) {
|
||||
// DelKeys 删除多个
|
||||
func DelKeys(source string, keys []string) error {
|
||||
if len(keys) == 0 {
|
||||
return false, fmt.Errorf("no keys")
|
||||
return fmt.Errorf("no keys")
|
||||
}
|
||||
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return fmt.Errorf("redis not client")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
err := rdb.Del(ctx, keys...).Err()
|
||||
if err != nil {
|
||||
if err := rdb.Del(ctx, keys...).Err(); err != nil {
|
||||
logger.Errorf("redis DelKeys err %v", err)
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
return true, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// 限流查询并记录
|
||||
// RateLimit 限流查询并记录
|
||||
func RateLimit(source, limitKey string, time, count int64) (int64, error) {
|
||||
// 数据源
|
||||
rdb := DefaultRDB()
|
||||
if source != "" {
|
||||
rdb = RDB(source)
|
||||
rdb := RDB(source)
|
||||
if rdb == nil {
|
||||
return 0, fmt.Errorf("redis not client")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
result, err := rateLimitCommand.Run(ctx, rdb, []string{limitKey}, time, count).Result()
|
||||
if err != nil {
|
||||
logger.Errorf("redis RateLimit err %v", err)
|
||||
logger.Errorf("redis lua script err %v", err)
|
||||
return 0, err
|
||||
}
|
||||
return result.(int64), err
|
||||
}
|
||||
|
||||
// 声明定义限流脚本命令
|
||||
var rateLimitCommand = redis.NewScript(`
|
||||
local key = KEYS[1]
|
||||
local time = tonumber(ARGV[1])
|
||||
local count = tonumber(ARGV[2])
|
||||
local current = redis.call('get', key);
|
||||
if current and tonumber(current) >= count then
|
||||
return tonumber(current);
|
||||
end
|
||||
current = redis.call('incr', key)
|
||||
if tonumber(current) == 1 then
|
||||
redis.call('expire', key, time)
|
||||
end
|
||||
return tonumber(current);`)
|
||||
@@ -1,127 +1,22 @@
|
||||
package datasource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/logger"
|
||||
"be.ems/src/framework/database/db"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
gormLog "gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
// 数据库连接实例
|
||||
var dbMap = make(map[string]*gorm.DB)
|
||||
|
||||
type dialectInfo struct {
|
||||
dialector gorm.Dialector
|
||||
logging bool
|
||||
}
|
||||
|
||||
// 载入数据库连接
|
||||
func loadDialect() map[string]dialectInfo {
|
||||
dialects := make(map[string]dialectInfo, 0)
|
||||
|
||||
// 读取数据源配置
|
||||
datasource := config.Get("gorm.datasource").(map[string]any)
|
||||
for key, value := range datasource {
|
||||
item := value.(map[string]any)
|
||||
// 数据库类型对应的数据库连接
|
||||
switch item["type"] {
|
||||
case "mysql":
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
item["username"],
|
||||
item["password"],
|
||||
item["host"],
|
||||
item["port"],
|
||||
item["database"],
|
||||
)
|
||||
dialects[key] = dialectInfo{
|
||||
dialector: mysql.Open(dsn),
|
||||
logging: item["logging"].(bool),
|
||||
}
|
||||
default:
|
||||
logger.Fatalf("%s: %v\n Not Load DB Config Type", key, item)
|
||||
}
|
||||
}
|
||||
|
||||
return dialects
|
||||
}
|
||||
|
||||
// 载入连接日志配置
|
||||
func loadLogger() gormLog.Interface {
|
||||
newLogger := gormLog.New(
|
||||
log.New(os.Stdout, "[GORM] ", log.LstdFlags), // 将日志输出到控制台
|
||||
gormLog.Config{
|
||||
SlowThreshold: time.Second, // Slow SQL 阈值
|
||||
LogLevel: gormLog.Info, // 日志级别 Silent不输出任何日志
|
||||
ParameterizedQueries: false, // 参数化查询SQL 用实际值带入?的执行语句
|
||||
Colorful: false, // 彩色日志输出
|
||||
},
|
||||
)
|
||||
return newLogger
|
||||
}
|
||||
|
||||
// 连接数据库实例
|
||||
func Connect() {
|
||||
// 遍历进行连接数据库实例
|
||||
for key, info := range loadDialect() {
|
||||
opts := &gorm.Config{}
|
||||
// 是否需要日志输出
|
||||
if info.logging {
|
||||
opts.Logger = loadLogger()
|
||||
}
|
||||
// 创建连接
|
||||
db, err := gorm.Open(info.dialector, opts)
|
||||
if err != nil {
|
||||
logger.Fatalf("failed error db connect: %s", err)
|
||||
}
|
||||
// 获取底层 SQL 数据库连接
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
logger.Fatalf("failed error underlying SQL database: %v", err)
|
||||
}
|
||||
// 测试数据库连接
|
||||
err = sqlDB.Ping()
|
||||
if err != nil {
|
||||
logger.Fatalf("failed error ping database: %v", err)
|
||||
}
|
||||
logger.Infof("database %s connection is successful.", key)
|
||||
dbMap[key] = db
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭数据库实例
|
||||
func Close() {
|
||||
for _, db := range dbMap {
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if err := sqlDB.Close(); err != nil {
|
||||
logger.Errorf("fatal error db close: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取默认数据源
|
||||
func DefaultDB() *gorm.DB {
|
||||
source := config.Get("gorm.defaultDataSourceName").(string)
|
||||
return dbMap[source]
|
||||
return db.DB("")
|
||||
}
|
||||
|
||||
// 获取数据源
|
||||
func DB(source string) *gorm.DB {
|
||||
if source == "" {
|
||||
source = config.Get("gorm.defaultDataSourceName").(string)
|
||||
}
|
||||
return dbMap[source]
|
||||
return db.DB(source)
|
||||
}
|
||||
|
||||
// RawDB 原生查询语句
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
"be.ems/src/framework/constants/cachekey"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/utils/ctx"
|
||||
"be.ems/src/framework/utils/ip2region"
|
||||
"be.ems/src/framework/vo/result"
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
"be.ems/src/framework/constants/cachekey"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/logger"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/utils/ctx"
|
||||
"be.ems/src/framework/utils/ip2region"
|
||||
"be.ems/src/framework/vo/result"
|
||||
|
||||
160
src/framework/reqctx/auth.go
Normal file
160
src/framework/reqctx/auth.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package reqctx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/vo"
|
||||
)
|
||||
|
||||
// LoginUser 登录用户信息
|
||||
func LoginUser(c *gin.Context) (vo.LoginUser, error) {
|
||||
value, exists := c.Get(constants.CTX_LOGIN_USER)
|
||||
if exists && value != nil {
|
||||
return value.(vo.LoginUser), nil
|
||||
}
|
||||
return vo.LoginUser{}, fmt.Errorf("invalid login user information")
|
||||
}
|
||||
|
||||
// LoginUserToUserID 登录用户信息-用户ID
|
||||
func LoginUserToUserID(c *gin.Context) string {
|
||||
info, err := LoginUser(c)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return info.UserID
|
||||
}
|
||||
|
||||
// LoginUserToUserName 登录用户信息-用户名称
|
||||
func LoginUserToUserName(c *gin.Context) string {
|
||||
info, err := LoginUser(c)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return info.User.UserName
|
||||
}
|
||||
|
||||
// LoginUserByContainRoles 登录用户信息-包含角色KEY
|
||||
func LoginUserByContainRoles(c *gin.Context, target string) bool {
|
||||
info, err := LoginUser(c)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if config.IsAdmin(info.UserID) {
|
||||
return true
|
||||
}
|
||||
roles := info.User.Roles
|
||||
for _, item := range roles {
|
||||
if item.RoleKey == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LoginUserByContainPerms 登录用户信息-包含权限标识
|
||||
func LoginUserByContainPerms(c *gin.Context, target string) bool {
|
||||
loginUser, err := LoginUser(c)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if config.IsAdmin(loginUser.UserID) {
|
||||
return true
|
||||
}
|
||||
perms := loginUser.Permissions
|
||||
for _, str := range perms {
|
||||
if str == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LoginUserToDataScopeSQL 登录用户信息-角色数据范围过滤SQL字符串
|
||||
func LoginUserToDataScopeSQL(c *gin.Context, deptAlias string, userAlias string) string {
|
||||
dataScopeSQL := ""
|
||||
// 登录用户信息
|
||||
info, err := LoginUser(c)
|
||||
if err != nil {
|
||||
return dataScopeSQL
|
||||
}
|
||||
userInfo := info.User
|
||||
|
||||
// 如果是系统管理员,则不过滤数据
|
||||
if config.IsAdmin(userInfo.UserID) {
|
||||
return dataScopeSQL
|
||||
}
|
||||
// 无用户角色
|
||||
if len(userInfo.Roles) <= 0 {
|
||||
return dataScopeSQL
|
||||
}
|
||||
|
||||
// 记录角色权限范围定义添加过, 非自定数据权限不需要重复拼接SQL
|
||||
var scopeKeys []string
|
||||
var conditions []string
|
||||
for _, role := range userInfo.Roles {
|
||||
dataScope := role.DataScope
|
||||
|
||||
if constants.ROLE_SCOPE_ALL == dataScope {
|
||||
break
|
||||
}
|
||||
|
||||
if constants.ROLE_SCOPE_CUSTOM != dataScope {
|
||||
hasKey := false
|
||||
for _, key := range scopeKeys {
|
||||
if key == dataScope {
|
||||
hasKey = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasKey {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if constants.ROLE_SCOPE_CUSTOM == dataScope {
|
||||
sql := fmt.Sprintf(`%s.dept_id IN
|
||||
( SELECT dept_id FROM sys_role_dept WHERE role_id = %s )
|
||||
AND %s.dept_id NOT IN
|
||||
(
|
||||
SELECT d.parent_id FROM sys_dept d
|
||||
INNER JOIN sys_role_dept rd ON rd.dept_id = d.dept_id
|
||||
AND rd.role_id = %s
|
||||
)`, deptAlias, role.RoleID, deptAlias, role.RoleID)
|
||||
conditions = append(conditions, sql)
|
||||
}
|
||||
|
||||
if constants.ROLE_SCOPE_DEPT == dataScope {
|
||||
sql := fmt.Sprintf("%s.dept_id = %s", deptAlias, userInfo.DeptID)
|
||||
conditions = append(conditions, sql)
|
||||
}
|
||||
|
||||
if constants.ROLE_SCOPE_DEPT_CHILD == dataScope {
|
||||
sql := fmt.Sprintf("%s.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = %s OR find_in_set(%s, ancestors ) )", deptAlias, userInfo.DeptID, userInfo.DeptID)
|
||||
conditions = append(conditions, sql)
|
||||
}
|
||||
|
||||
if constants.ROLE_SCOPE_SELF == dataScope {
|
||||
if userAlias == "" {
|
||||
sql := fmt.Sprintf("%s.dept_id = %s", deptAlias, userInfo.DeptID)
|
||||
conditions = append(conditions, sql)
|
||||
} else {
|
||||
sql := fmt.Sprintf("%s.user_id = %s", userAlias, userInfo.UserID)
|
||||
conditions = append(conditions, sql)
|
||||
}
|
||||
}
|
||||
|
||||
// 记录角色范围
|
||||
scopeKeys = append(scopeKeys, dataScope)
|
||||
}
|
||||
|
||||
// 构建查询条件语句
|
||||
if len(conditions) > 0 {
|
||||
dataScopeSQL = fmt.Sprintf(" ( %s ) ", strings.Join(conditions, " OR "))
|
||||
}
|
||||
return dataScopeSQL
|
||||
}
|
||||
106
src/framework/reqctx/context.go
Normal file
106
src/framework/reqctx/context.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package reqctx
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"be.ems/src/framework/constants"
|
||||
)
|
||||
|
||||
// QueryMap Query参数转换Map
|
||||
func QueryMap(c *gin.Context) map[string]string {
|
||||
queryValues := c.Request.URL.Query()
|
||||
queryParams := make(map[string]string, len(queryValues))
|
||||
for key, values := range queryValues {
|
||||
queryParams[key] = values[0]
|
||||
}
|
||||
return queryParams
|
||||
}
|
||||
|
||||
// BodyJSONMap JSON参数转换Map
|
||||
func BodyJSONMap(c *gin.Context) map[string]any {
|
||||
params := make(map[string]any, 0)
|
||||
c.ShouldBindBodyWithJSON(¶ms)
|
||||
return params
|
||||
}
|
||||
|
||||
// RequestParamsMap 请求参数转换Map
|
||||
func RequestParamsMap(c *gin.Context) map[string]any {
|
||||
params := make(map[string]any, 0)
|
||||
// json
|
||||
if strings.HasPrefix(c.ContentType(), "application/json") {
|
||||
c.ShouldBindBodyWithJSON(¶ms)
|
||||
}
|
||||
|
||||
// 表单
|
||||
formParams := c.Request.PostForm
|
||||
for key, value := range formParams {
|
||||
if _, ok := params[key]; !ok {
|
||||
params[key] = value[0]
|
||||
}
|
||||
}
|
||||
|
||||
// 查询
|
||||
queryParams := c.Request.URL.Query()
|
||||
for key, value := range queryParams {
|
||||
if _, ok := params[key]; !ok {
|
||||
params[key] = value[0]
|
||||
}
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
// Authorization 解析请求头
|
||||
func Authorization(c *gin.Context) string {
|
||||
// Query请求查询
|
||||
if authQuery, ok := c.GetQuery(constants.ACCESS_TOKEN); ok && authQuery != "" {
|
||||
return authQuery
|
||||
}
|
||||
// Header请求头
|
||||
if authHeader := c.GetHeader(constants.ACCESS_TOKEN); authHeader != "" {
|
||||
return authHeader
|
||||
}
|
||||
|
||||
// Query请求查询
|
||||
if authQuery, ok := c.GetQuery(constants.ACCESS_TOKEN_QUERY); ok && authQuery != "" {
|
||||
return authQuery
|
||||
}
|
||||
// Header请求头
|
||||
authHeader := c.GetHeader(constants.HEADER_KEY)
|
||||
if authHeader == "" {
|
||||
return ""
|
||||
}
|
||||
// 拆分 Authorization 请求头,提取 JWT 令牌部分
|
||||
arr := strings.SplitN(authHeader, constants.HEADER_PREFIX, 2)
|
||||
if len(arr) < 2 {
|
||||
return ""
|
||||
}
|
||||
return arr[1]
|
||||
}
|
||||
|
||||
// AcceptLanguage 解析客户端接收语言 zh:中文 en: 英文
|
||||
func AcceptLanguage(c *gin.Context) string {
|
||||
preferredLanguage := language.English
|
||||
|
||||
// Query请求查询
|
||||
if v, ok := c.GetQuery("language"); ok && v != "" {
|
||||
tags, _, _ := language.ParseAcceptLanguage(v)
|
||||
if len(tags) > 0 {
|
||||
preferredLanguage = tags[0]
|
||||
}
|
||||
}
|
||||
// Header请求头
|
||||
if v := c.GetHeader("Accept-Language"); v != "" {
|
||||
tags, _, _ := language.ParseAcceptLanguage(v)
|
||||
if len(tags) > 0 {
|
||||
preferredLanguage = tags[0]
|
||||
}
|
||||
}
|
||||
|
||||
// 只取前缀
|
||||
lang := preferredLanguage.String()
|
||||
arr := strings.Split(lang, "-")
|
||||
return arr[0]
|
||||
}
|
||||
35
src/framework/reqctx/param.go
Normal file
35
src/framework/reqctx/param.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package reqctx
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"be.ems/src/framework/utils/ip2region"
|
||||
"be.ems/src/framework/utils/ua"
|
||||
)
|
||||
|
||||
// IPAddrLocation 解析ip地址
|
||||
func IPAddrLocation(c *gin.Context) (string, string) {
|
||||
ip := ip2region.ClientIP(c.ClientIP())
|
||||
location := "-" //ip2region.RealAddressByIp(ip)
|
||||
return ip, location
|
||||
}
|
||||
|
||||
// UaOsBrowser 解析请求用户代理信息
|
||||
func UaOsBrowser(c *gin.Context) (string, string) {
|
||||
userAgent := c.GetHeader("user-agent")
|
||||
uaInfo := ua.Info(userAgent)
|
||||
|
||||
browser := "-"
|
||||
if bName, bVersion := uaInfo.Browser(); bName != "" {
|
||||
browser = bName
|
||||
if bVersion != "" {
|
||||
browser = bName + " " + bVersion
|
||||
}
|
||||
}
|
||||
|
||||
os := "-"
|
||||
if bos := uaInfo.OS(); bos != "" {
|
||||
os = bos
|
||||
}
|
||||
return os, browser
|
||||
}
|
||||
74
src/framework/resp/api.go
Normal file
74
src/framework/resp/api.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package resp
|
||||
|
||||
const (
|
||||
// CODE_ERROR 响应-code错误失败
|
||||
CODE_ERROR = 0
|
||||
// MSG_ERROR 响应-msg错误失败
|
||||
MSG_ERROR = "error"
|
||||
|
||||
// CODE_SUCCESS 响应-msg正常成功
|
||||
CODE_SUCCESS = 1
|
||||
// MSG_SUCCCESS 响应-code正常成功
|
||||
MSG_SUCCCESS = "success"
|
||||
|
||||
// 响应-code加密数据
|
||||
CODE_ENCRYPT = 2
|
||||
// 响应-msg加密数据
|
||||
MSG_ENCRYPT = "encrypt"
|
||||
)
|
||||
|
||||
// Resp 响应结构体
|
||||
type Resp struct {
|
||||
Code int `json:"code"` // 响应状态码
|
||||
Msg string `json:"msg"` // 响应信息
|
||||
Data any `json:"data,omitempty"` // 响应数据
|
||||
}
|
||||
|
||||
// CodeMsg 响应结果
|
||||
func CodeMsg(code int, msg string) Resp {
|
||||
return Resp{Code: code, Msg: msg}
|
||||
}
|
||||
|
||||
// Ok 响应成功结果
|
||||
func Ok(v map[string]any) map[string]any {
|
||||
args := make(map[string]any)
|
||||
args["code"] = CODE_SUCCESS
|
||||
args["msg"] = MSG_SUCCCESS
|
||||
// v合并到args
|
||||
for key, value := range v {
|
||||
args[key] = value
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// OkMsg 响应成功结果信息
|
||||
func OkMsg(msg string) Resp {
|
||||
return Resp{Code: CODE_SUCCESS, Msg: msg}
|
||||
}
|
||||
|
||||
// OkData 响应成功结果数据
|
||||
func OkData(data any) Resp {
|
||||
return Resp{Code: CODE_SUCCESS, Msg: MSG_SUCCCESS, Data: data}
|
||||
}
|
||||
|
||||
// Err 响应失败结果 map[string]any{}
|
||||
func Err(v map[string]any) map[string]any {
|
||||
args := make(map[string]any)
|
||||
args["code"] = CODE_ERROR
|
||||
args["msg"] = MSG_ERROR
|
||||
// v合并到args
|
||||
for key, value := range v {
|
||||
args[key] = value
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// ErrMsg 响应失败结果信息
|
||||
func ErrMsg(msg string) Resp {
|
||||
return Resp{Code: CODE_ERROR, Msg: msg}
|
||||
}
|
||||
|
||||
// ErrData 响应失败结果数据
|
||||
func ErrData(data any) Resp {
|
||||
return Resp{Code: CODE_ERROR, Msg: MSG_ERROR, Data: data}
|
||||
}
|
||||
23
src/framework/resp/error.go
Normal file
23
src/framework/resp/error.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package resp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
// FormatBindError 格式化Gin ShouldBindWith绑定错误
|
||||
//
|
||||
// binding:"required" 验证失败返回: field=id type=string tag=required value=
|
||||
func FormatBindError(err error) string {
|
||||
if errs, ok := err.(validator.ValidationErrors); ok {
|
||||
var errMsgs []string
|
||||
for _, e := range errs {
|
||||
str := fmt.Sprintf("[field=%s, type=%s, tag=%s, param=%s, value=%v]", e.Field(), e.Type().Name(), e.Tag(), e.Param(), e.Value())
|
||||
errMsgs = append(errMsgs, str)
|
||||
}
|
||||
return strings.Join(errMsgs, ", ")
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
@@ -91,31 +91,16 @@ func ConvertToCamelCase(str string) string {
|
||||
return strings.Join(words, "")
|
||||
}
|
||||
|
||||
// Bit 比特位为单位
|
||||
// Bit 比特位为单位 1023.00 B --> 1.00 KB
|
||||
func Bit(bit float64) string {
|
||||
var GB, MB, KB string
|
||||
|
||||
if bit > float64(1<<30) {
|
||||
GB = fmt.Sprintf("%0.2f", bit/(1<<30))
|
||||
units := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
||||
for i := 0; i < len(units); i++ {
|
||||
if bit < 1024 || i == len(units)-1 {
|
||||
return fmt.Sprintf("%.2f %s", bit, units[i])
|
||||
}
|
||||
|
||||
if bit > float64(1<<20) && bit < (1<<30) {
|
||||
MB = fmt.Sprintf("%.2f", bit/(1<<20))
|
||||
}
|
||||
|
||||
if bit > float64(1<<10) && bit < (1<<20) {
|
||||
KB = fmt.Sprintf("%.2f", bit/(1<<10))
|
||||
}
|
||||
|
||||
if GB != "" {
|
||||
return GB + "GB"
|
||||
} else if MB != "" {
|
||||
return MB + "MB"
|
||||
} else if KB != "" {
|
||||
return KB + "KB"
|
||||
} else {
|
||||
return fmt.Sprintf("%vB", bit)
|
||||
bit /= 1024
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// CronExpression 解析 Cron 表达式,返回下一次执行的时间戳(毫秒)
|
||||
@@ -146,11 +131,11 @@ func SafeContent(value string) string {
|
||||
}
|
||||
|
||||
// RemoveDuplicates 数组内字符串去重
|
||||
func RemoveDuplicates(ids []string) []string {
|
||||
func RemoveDuplicates(arr []string) []string {
|
||||
uniqueIDs := make(map[string]bool)
|
||||
uniqueIDSlice := make([]string, 0)
|
||||
|
||||
for _, id := range ids {
|
||||
for _, id := range arr {
|
||||
_, ok := uniqueIDs[id]
|
||||
if !ok && id != "" {
|
||||
uniqueIDs[id] = true
|
||||
@@ -161,6 +146,29 @@ func RemoveDuplicates(ids []string) []string {
|
||||
return uniqueIDSlice
|
||||
}
|
||||
|
||||
// RemoveDuplicatesToArray 数组内字符串分隔去重转为字符数组
|
||||
func RemoveDuplicatesToArray(keyStr, sep string) []string {
|
||||
arr := make([]string, 0)
|
||||
if keyStr == "" {
|
||||
return arr
|
||||
}
|
||||
if strings.Contains(keyStr, sep) {
|
||||
// 处理字符转数组后去重
|
||||
strArr := strings.Split(keyStr, sep)
|
||||
uniqueKeys := make(map[string]bool)
|
||||
for _, str := range strArr {
|
||||
_, ok := uniqueKeys[str]
|
||||
if !ok && str != "" {
|
||||
uniqueKeys[str] = true
|
||||
arr = append(arr, str)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
arr = append(arr, keyStr)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// Color 解析颜色 #fafafa
|
||||
func Color(colorStr string) *color.RGBA {
|
||||
// 去除 # 号
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"be.ems/src/framework/config"
|
||||
cachekeyConstants "be.ems/src/framework/constants/cachekey"
|
||||
tokenConstants "be.ems/src/framework/constants/token"
|
||||
redisCahe "be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/logger"
|
||||
redisCahe "be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/utils/generate"
|
||||
"be.ems/src/framework/utils/machine"
|
||||
"be.ems/src/framework/vo"
|
||||
@@ -28,7 +28,7 @@ func Remove(tokenStr string) string {
|
||||
uuid := claims[tokenConstants.JWT_UUID].(string)
|
||||
tokenKey := cachekeyConstants.LOGIN_TOKEN_KEY + uuid
|
||||
hasKey, _ := redisCahe.Has("", tokenKey)
|
||||
if hasKey {
|
||||
if hasKey > 0 {
|
||||
redisCahe.Del("", tokenKey)
|
||||
}
|
||||
return claims[tokenConstants.JWT_NAME].(string)
|
||||
@@ -141,7 +141,7 @@ func LoginUser(claims jwt.MapClaims) vo.LoginUser {
|
||||
tokenKey := cachekeyConstants.LOGIN_TOKEN_KEY + uuid
|
||||
hasKey, _ := redisCahe.Has("", tokenKey)
|
||||
var loginUser vo.LoginUser
|
||||
if hasKey {
|
||||
if hasKey > 0 {
|
||||
loginUserStr, _ := redisCahe.Get("", tokenKey)
|
||||
if loginUserStr == "" {
|
||||
return loginUser
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/constants/cachekey"
|
||||
"be.ems/src/framework/constants/captcha"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/logger"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
"be.ems/src/framework/vo/result"
|
||||
systemService "be.ems/src/modules/system/service"
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
adminConstants "be.ems/src/framework/constants/admin"
|
||||
"be.ems/src/framework/constants/cachekey"
|
||||
"be.ems/src/framework/constants/common"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/utils/crypto"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
"be.ems/src/framework/vo"
|
||||
@@ -131,9 +131,9 @@ func (s *Account) UpdateLoginDateAndIP(loginUser *vo.LoginUser) bool {
|
||||
func (s *Account) ClearLoginRecordCache(username string) bool {
|
||||
cacheKey := cachekey.PWD_ERR_CNT_KEY + username
|
||||
hasKey, _ := redis.Has("", cacheKey)
|
||||
if hasKey {
|
||||
delOk, _ := redis.Del("", cacheKey)
|
||||
return delOk
|
||||
if hasKey > 0 {
|
||||
err := redis.Del("", cacheKey)
|
||||
return err == nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"be.ems/src/framework/constants/cachekey"
|
||||
"be.ems/src/framework/constants/common"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
systemModel "be.ems/src/modules/system/model"
|
||||
systemService "be.ems/src/modules/system/service"
|
||||
|
||||
@@ -2,8 +2,8 @@ package controller
|
||||
|
||||
import (
|
||||
"be.ems/src/framework/constants/cachekey"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/utils/ctx"
|
||||
"be.ems/src/framework/vo/result"
|
||||
"be.ems/src/modules/monitor/model"
|
||||
@@ -116,8 +116,8 @@ func (s *SysCacheController) ClearCacheName(c *gin.Context) {
|
||||
c.JSON(200, result.ErrMsg(err.Error()))
|
||||
return
|
||||
}
|
||||
ok, _ := redis.DelKeys("", cacheKeys)
|
||||
if ok {
|
||||
err = redis.DelKeys("", cacheKeys)
|
||||
if err == nil {
|
||||
c.JSON(200, result.Ok(nil))
|
||||
return
|
||||
}
|
||||
@@ -136,8 +136,8 @@ func (s *SysCacheController) ClearCacheKey(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
ok, _ := redis.Del("", cacheName+":"+cacheKey)
|
||||
if ok {
|
||||
err := redis.Del("", cacheName+":"+cacheKey)
|
||||
if err == nil {
|
||||
c.JSON(200, result.Ok(nil))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"be.ems/src/framework/constants/cachekey"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/utils/ctx"
|
||||
"be.ems/src/framework/vo"
|
||||
"be.ems/src/framework/vo/result"
|
||||
@@ -130,8 +130,8 @@ func (s *SysUserOnlineController) ForceLogout(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 删除token
|
||||
ok, _ := redis.Del("", cachekey.LOGIN_TOKEN_KEY+tokenId)
|
||||
if ok {
|
||||
err := redis.Del("", cachekey.LOGIN_TOKEN_KEY+tokenId)
|
||||
if err == nil {
|
||||
c.JSON(200, result.Ok(nil))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"be.ems/src/framework/constants/cachekey"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
"be.ems/src/modules/network_data/model"
|
||||
"be.ems/src/modules/network_data/repository"
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/modules/network_data/model"
|
||||
"be.ems/src/modules/network_data/repository"
|
||||
neService "be.ems/src/modules/network_element/service"
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/modules/network_data/model"
|
||||
"be.ems/src/modules/network_data/repository"
|
||||
neService "be.ems/src/modules/network_element/service"
|
||||
|
||||
@@ -3,8 +3,8 @@ package controller
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/telnet"
|
||||
"be.ems/src/framework/utils/ctx"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
|
||||
@@ -10,31 +10,10 @@ import (
|
||||
"be.ems/src/modules/network_element/model"
|
||||
)
|
||||
|
||||
// NeTraceInfo 网元跟踪任务信息
|
||||
func NeTraceInfo(neInfo model.NeInfo, traceId string) (map[string]any, error) {
|
||||
// 跟踪任务信息
|
||||
neUrl := fmt.Sprintf("http://%s:%d/api/rest/traceManagement/v1/subscriptions?id=%s", neInfo.IP, neInfo.Port, traceId)
|
||||
resBytes, err := fetch.Get(neUrl, nil, 30_000)
|
||||
if err != nil {
|
||||
logger.Warnf("NeTraceInfo Get \"%s\"", neUrl)
|
||||
logger.Errorf("NeTraceInfo %s", err.Error())
|
||||
return nil, fmt.Errorf("NeService Trace Info API Error")
|
||||
}
|
||||
|
||||
// 序列化结果
|
||||
var resData map[string]any
|
||||
err = json.Unmarshal(resBytes, &resData)
|
||||
if err != nil {
|
||||
logger.Errorf("NeTraceInfo Unmarshal %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
return resData, nil
|
||||
}
|
||||
|
||||
// NeTraceAdd 网元跟踪任务新增
|
||||
func NeTraceAdd(neInfo model.NeInfo, data map[string]any) (map[string]any, error) {
|
||||
func NeTraceAdd(neInfo model.NeInfo, data any) (map[string]any, error) {
|
||||
// 跟踪任务创建
|
||||
neUrl := fmt.Sprintf("http://%s:%d/api/rest/traceManagement/v1/subscriptions", neInfo.IP, neInfo.Port)
|
||||
neUrl := fmt.Sprintf("http://%s:%d/api/rest/traceManagement/v2/%s/subscriptions", neInfo.IP, neInfo.Port, neInfo.NeType)
|
||||
resBytes, err := fetch.PostJSON(neUrl, data, nil)
|
||||
var resData map[string]any
|
||||
if err != nil {
|
||||
@@ -60,48 +39,17 @@ func NeTraceAdd(neInfo model.NeInfo, data map[string]any) (map[string]any, error
|
||||
return resData, nil
|
||||
}
|
||||
|
||||
// NeTraceEdit 网元跟踪任务编辑
|
||||
func NeTraceEdit(neInfo model.NeInfo, data map[string]any) (map[string]any, error) {
|
||||
// 网元参数配置新增(array)
|
||||
neUrl := fmt.Sprintf("http://%s:%d/api/rest/traceManagement/v1/subscriptions", neInfo.IP, neInfo.Port)
|
||||
resBytes, err := fetch.PutJSON(neUrl, data, nil)
|
||||
var resData map[string]any
|
||||
if err != nil {
|
||||
errStr := err.Error()
|
||||
logger.Warnf("NeTraceEdit PUT \"%s\"", neUrl)
|
||||
if strings.HasPrefix(errStr, "201") || strings.HasPrefix(errStr, "204") {
|
||||
return resData, nil
|
||||
}
|
||||
logger.Errorf("NeTraceEdit %s", errStr)
|
||||
return nil, fmt.Errorf("NeService Trace Edit API Error")
|
||||
}
|
||||
|
||||
// 200 成功无数据时
|
||||
if len(resBytes) == 0 {
|
||||
return resData, nil
|
||||
}
|
||||
|
||||
// 序列化结果
|
||||
err = json.Unmarshal(resBytes, &resData)
|
||||
if err != nil {
|
||||
logger.Errorf("NeTraceEdit Unmarshal %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
return resData, nil
|
||||
}
|
||||
|
||||
// NeTraceDelete 网元跟踪任务删除
|
||||
func NeTraceDelete(neInfo model.NeInfo, traceId string) (map[string]any, error) {
|
||||
// 网元参数配置删除(array)
|
||||
neUrl := fmt.Sprintf("http://%s:%d/api/rest/traceManagement/v1/subscriptions?id=%s", neInfo.IP, neInfo.Port, traceId)
|
||||
neUrl := fmt.Sprintf("http://%s:%d/api/rest/traceManagement/v2/%s/subscriptions?id=%s", neInfo.IP, neInfo.Port, neInfo.NeType, traceId)
|
||||
resBytes, err := fetch.Delete(neUrl, nil)
|
||||
var resData map[string]any
|
||||
if err != nil {
|
||||
errStr := err.Error()
|
||||
logger.Warnf("NeTraceDelete Delete \"%s\"", neUrl)
|
||||
if strings.HasPrefix(errStr, "201") || strings.HasPrefix(errStr, "204") {
|
||||
return resData, nil
|
||||
}
|
||||
logger.Warnf("NeTraceDelete Delete \"%s\"", neUrl)
|
||||
logger.Errorf("NeTraceDelete %s", errStr)
|
||||
return nil, fmt.Errorf("NeService Trace Delete API Error")
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"be.ems/src/framework/constants/cachekey"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/modules/network_element/model"
|
||||
"be.ems/src/modules/network_element/repository"
|
||||
)
|
||||
@@ -81,8 +81,8 @@ func (r *NeConfig) ClearNeCacheByNeType(neType string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
delOk, _ := redis.DelKeys("", keys)
|
||||
return delOk
|
||||
err = redis.DelKeys("", keys)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// SelectNeConfigByNeType 查询网元类型参数配置
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"be.ems/src/framework/constants/cachekey"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/modules/system/model"
|
||||
"be.ems/src/modules/system/repository"
|
||||
)
|
||||
@@ -154,8 +154,8 @@ func (r *SysConfigImpl) clearConfigCache(configKey string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
delOk, _ := redis.DelKeys("", keys)
|
||||
return delOk
|
||||
err = redis.DelKeys("", keys)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// SelectConfigByKey 查询配置信息BY键
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"be.ems/src/framework/constants/cachekey"
|
||||
"be.ems/src/framework/constants/common"
|
||||
"be.ems/src/framework/redis"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/modules/system/model"
|
||||
"be.ems/src/modules/system/repository"
|
||||
)
|
||||
@@ -183,8 +183,8 @@ func (r *SysDictType) ClearDictCache(dictType string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
delOk, _ := redis.DelKeys("", keys)
|
||||
return delOk
|
||||
err = redis.DelKeys("", keys)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// DictDataCache 获取字典数据缓存数据
|
||||
|
||||
Reference in New Issue
Block a user