This commit is contained in:
2023-09-08 09:38:40 +08:00
15 changed files with 659 additions and 20 deletions

237
lib/core/redis/redis.go Normal file
View File

@@ -0,0 +1,237 @@
package redis
import (
"context"
"fmt"
"strings"
"time"
"ems.agt/lib/core/conf"
"ems.agt/lib/log"
"github.com/redis/go-redis/v9"
)
// Redis连接实例
var rdb *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实例
func Connect() {
ctx := context.Background()
client := conf.Get("redis").(map[string]any)
address := fmt.Sprintf("%s:%d", client["host"], client["port"])
// 创建连接
rdb = redis.NewClient(&redis.Options{
Addr: address,
Password: client["password"].(string),
DB: client["db"].(int),
})
// 测试数据库连接
pong, err := rdb.Ping(ctx).Result()
if err != nil {
log.Fatalf("failed error ping redis: %v", err)
}
log.Infof("redis %s connection is successful.", pong)
}
// 关闭Redis实例
func Close() {
if err := rdb.Close(); err != nil {
log.Fatalf("fatal error db close: %s", err)
}
}
// Info 获取redis服务信息
func Info() map[string]map[string]string {
ctx := context.Background()
info, err := rdb.Info(ctx).Result()
if err != nil {
return map[string]map[string]string{}
}
infoObj := make(map[string]map[string]string)
lines := strings.Split(info, "\r\n")
label := ""
for _, line := range lines {
if strings.Contains(line, "#") {
label = strings.Fields(line)[len(strings.Fields(line))-1]
label = strings.ToLower(label)
infoObj[label] = make(map[string]string)
continue
}
kvArr := strings.Split(line, ":")
if len(kvArr) >= 2 {
key := strings.TrimSpace(kvArr[0])
value := strings.TrimSpace(kvArr[len(kvArr)-1])
infoObj[label][key] = value
}
}
return infoObj
}
// KeySize 获取redis当前连接可用键Key总数信息
func KeySize() int64 {
ctx := context.Background()
size, err := rdb.DBSize(ctx).Result()
if err != nil {
return 0
}
return size
}
// CommandStats 获取redis命令状态信息
func CommandStats() []map[string]string {
ctx := context.Background()
commandstats, err := rdb.Info(ctx, "commandstats").Result()
if err != nil {
return []map[string]string{}
}
statsObjArr := make([]map[string]string, 0)
lines := strings.Split(commandstats, "\r\n")
for _, line := range lines {
if !strings.HasPrefix(line, "cmdstat_") {
continue
}
kvArr := strings.Split(line, ":")
key := kvArr[0]
valueStr := kvArr[len(kvArr)-1]
statsObj := make(map[string]string)
statsObj["name"] = key[8:]
statsObj["value"] = valueStr[6:strings.Index(valueStr, ",usec=")]
statsObjArr = append(statsObjArr, statsObj)
}
return statsObjArr
}
// 获取键的剩余有效时间(秒)
func GetExpire(key string) float64 {
ctx := context.Background()
ttl, err := rdb.TTL(ctx, key).Result()
if err != nil {
return 0
}
return ttl.Seconds()
}
// 获得缓存数据的key列表
func GetKeys(pattern string) []string {
// 初始化变量
var keys []string
var cursor uint64 = 0
ctx := context.Background()
// 循环遍历获取匹配的键
for {
// 使用 SCAN 命令获取匹配的键
batchKeys, nextCursor, err := rdb.Scan(ctx, cursor, pattern, 100).Result()
if err != nil {
log.Errorf("Failed to scan keys: %v", err)
break
}
cursor = nextCursor
keys = append(keys, batchKeys...)
// 当 cursor 为 0表示遍历完成
if cursor == 0 {
break
}
}
return keys
}
// 批量获得缓存数据
func GetBatch(keys []string) []any {
if len(keys) == 0 {
return []any{}
}
// 获取缓存数据
result, err := rdb.MGet(context.Background(), keys...).Result()
if err != nil {
log.Errorf("Failed to get batch data: %v", err)
return []any{}
}
return result
}
// 获得缓存数据
func Get(key string) string {
ctx := context.Background()
value, err := rdb.Get(ctx, key).Result()
if err == redis.Nil || err != nil {
return ""
}
return value
}
// 获得缓存数据Hash
func GetHash(key string) map[string]string {
ctx := context.Background()
value, err := rdb.HGetAll(ctx, key).Result()
if err == redis.Nil || err != nil {
return map[string]string{}
}
return value
}
// 判断是否存在
func Has(keys ...string) bool {
ctx := context.Background()
exists, err := rdb.Exists(ctx, keys...).Result()
if err != nil {
return false
}
return exists >= 1
}
// 设置缓存数据
func Set(key string, value any) bool {
ctx := context.Background()
err := rdb.Set(ctx, key, value, 0).Err()
return err == nil
}
// 设置缓存数据与过期时间
func SetByExpire(key string, value any, expiration time.Duration) bool {
ctx := context.Background()
err := rdb.Set(ctx, key, value, expiration).Err()
return err == nil
}
// 删除单个
func Del(key string) bool {
ctx := context.Background()
err := rdb.Del(ctx, key).Err()
return err == nil
}
// 删除多个
func DelKeys(keys []string) bool {
if len(keys) == 0 {
return false
}
ctx := context.Background()
err := rdb.Del(ctx, keys...).Err()
return err == nil
}
// 限流查询并记录
func RateLimit(limitKey string, time, count int64) int64 {
ctx := context.Background()
result, err := rateLimitCommand.Run(ctx, rdb, []string{limitKey}, time, count).Result()
if err != nil {
log.Errorf("redis lua script err %v", err)
return 0
}
return result.(int64)
}

View File

@@ -47,16 +47,6 @@ func ShouldBindJSON(r *http.Request, args any) error {
// JSON 相应json数据
func JSON(w http.ResponseWriter, code int, data any) {
// 跨域响应头
// To solve cross domain issue
w.Header().Set("Access-Control-Allow-Origin", "*")
// w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Methods", "*")
w.Header().Set("Access-Control-Allow-Headers", "*")
// w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
// w.Header().Set("Access-Control-Allow-Headers", "AccessToken")
w.Header().Set("Access-Control-Expose-Headers", "Access-Control-Allow-Headers, Token")
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
response, err := json.Marshal(data)

View File

@@ -181,17 +181,17 @@ func InsertDataWithJson(insertData interface{}) (int64, error) {
type NeInfo struct {
Id int `json:"id" xorm:"pk 'id' autoincr"`
NeType string `json:"neType" xorm:"ne_type"`
NeId string `json:"neId" xorm:"ne_id"` // neUID/rmUID 网元唯一标识
RmUID string `json:"rmUID" xorm:"rm_uid"` // neUID/rmUID网元UID
NeName string `json:"neName" xorm:"ne_name"` // NeName/UserLabel 网元名称/网元设备友好名称
NeType string `json:"ne_type" xorm:"ne_type"`
NeId string `json:"ne_id" xorm:"ne_id"` // neUID/rmUID 网元唯一标识
RmUID string `json:"rm_uid" xorm:"rm_uid"` // neUID/rmUID网元UID
NeName string `json:"ne_name" xorm:"ne_name"` // NeName/UserLabel 网元名称/网元设备友好名称
Ip string `json:"ip" xorm:"ip"`
Port string `json:"port" xorm:"port"`
PvFlag string `json:"pvFlag" xorm:"pv_flag"` // 网元虚实性标识 VNF/PNF: 虚拟/物理
NeAddress string `json:"neAddress" xorm:"ne_address"` // 只对PNF
Province string `json:"province" xorm:"province"` // 网元所在省份
VendorName string `json:"vendorName" xorm:"vendor_name"` // 厂商名称
Dn string `json:"dn" xorm:"dn"` // 网络标识
PvFlag string `json:"pv_flag" xorm:"pv_flag"` // 网元虚实性标识 VNF/PNF: 虚拟/物理
NeAddress string `json:"ne_address" xorm:"ne_address"` // 只对PNF
Province string `json:"province" xorm:"province"` // 网元所在省份
VendorName string `json:"vendor_name" xorm:"vendor_name"` // 厂商名称
Dn string `json:"dn" xorm:"dn"` // 网络标识
Status int `json:"status" xorm:"status"`
UpdateTime string `json:"-" xorm:"-"`
}

64
lib/midware/cors.go Normal file
View File

@@ -0,0 +1,64 @@
package midware
import (
"net/http"
"strings"
)
// Cors 跨域
func Cors(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 设置Vary头部
w.Header().Set("Vary", "Origin")
w.Header().Set("Keep-Alive", "timeout=5")
requestOrigin := r.Header.Get("Origin")
if requestOrigin == "" {
next.ServeHTTP(w, r)
return
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Credentials", "true")
// OPTIONS
if r.Method == "OPTIONS" {
requestMethod := r.Header.Get("Access-Control-Request-Method")
if requestMethod == "" {
next.ServeHTTP(w, r)
return
}
// 响应最大时间值
w.Header().Set("Access-Control-Max-Age", "31536000")
// 允许方法
allowMethods := []string{
"OPTIONS",
"HEAD",
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
}
w.Header().Set("Access-Control-Allow-Methods", strings.Join(allowMethods, ","))
// 允许请求头
allowHeaders := []string{
"Accesstoken",
}
w.Header().Set("Access-Control-Allow-Headers", strings.Join(allowHeaders, ","))
w.WriteHeader(500)
return
}
// 暴露请求头
exposeHeaders := []string{"X-RepeatSubmit-Rest", "AccessToken"}
w.Header().Set("Access-Control-Expose-Headers", strings.Join(exposeHeaders, ","))
next.ServeHTTP(w, r)
})
}

View File

@@ -37,6 +37,7 @@ func LoggerTrace(next http.Handler) http.Handler {
})
}
// 已禁用
func OptionProcess(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "OPTIONS" {
@@ -48,6 +49,7 @@ func OptionProcess(next http.Handler) http.Handler {
})
}
// 已禁用
func CheckPermission(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("AccessToken")

View File

@@ -25,6 +25,7 @@ import (
sysrole "ems.agt/features/sys_role"
sysuser "ems.agt/features/sys_user"
"ems.agt/features/trace"
udmuser "ems.agt/features/udm_user"
"ems.agt/lib/midware"
"ems.agt/lib/services"
@@ -327,6 +328,11 @@ func init() {
for _, v := range sysuser.Routers() {
Register(v.Method, v.Pattern, v.Handler, v.Middleware)
}
// UDM 用户信息接口添加到路由
for _, v := range udmuser.Routers() {
Register(v.Method, v.Pattern, v.Handler, v.Middleware)
}
}
// To resolv rest POST/PUT/DELETE/PATCH cross domain
@@ -342,7 +348,8 @@ func NewRouter() *mux.Router {
r.MethodNotAllowedHandler = services.CustomResponseMethodNotAllowed405Handler()
r.Use(midware.LoggerTrace)
r.Use(midware.OptionProcess)
r.Use(midware.Cors)
// r.Use(midware.OptionProcess)
// r.Use(midware.ArrowIPAddr)
for _, router := range routers {