优化模块租户检查逻辑,重构数据结构并简化代码
This commit is contained in:
@@ -19,27 +19,36 @@ var (
|
|||||||
TenantModuleAICs = ModuleAssetId["customerService"] // AI客服模块
|
TenantModuleAICs = ModuleAssetId["customerService"] // AI客服模块
|
||||||
)
|
)
|
||||||
|
|
||||||
// TenantModuleType 租户类型
|
type TenantModuleType string
|
||||||
type TenantModuleType struct {
|
|
||||||
|
const (
|
||||||
|
TenantModuleTypePlatform TenantModuleType = "platform"
|
||||||
|
TenantModuleTypePrivate TenantModuleType = "private"
|
||||||
|
TenantModuleTypeSupplier TenantModuleType = "supplier"
|
||||||
|
TenantModuleTypeSmallShop TenantModuleType = "small_shop"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TenantModuleTypeKV 租户类型
|
||||||
|
type TenantModuleTypeKV struct {
|
||||||
Key string
|
Key string
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TenantModuleTypesAssets 资产模块租户类型
|
// TenantModuleTypesAssets 资产模块租户类型
|
||||||
var TenantModuleTypesAssets = []TenantModuleType{
|
var TenantModuleTypesAssets = []TenantModuleTypeKV{
|
||||||
{Key: "private_cloud", Value: "私有云租户"},
|
{Key: string(TenantModuleTypePrivate), Value: "私域租户"},
|
||||||
{Key: "supplier", Value: "供应商"},
|
{Key: string(TenantModuleTypeSupplier), Value: "供应商"},
|
||||||
{Key: "small_shop", Value: "电商小店"},
|
{Key: string(TenantModuleTypeSmallShop), Value: "电商小店"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TenantModuleTypesAd 广告模块租户类型(待定)
|
// TenantModuleTypesAd 广告模块租户类型(待定)
|
||||||
var TenantModuleTypesAd []TenantModuleType
|
var TenantModuleTypesAd []TenantModuleTypeKV
|
||||||
|
|
||||||
// TenantModuleTypesAICs AI客服模块租户类型(待定)
|
// TenantModuleTypesAICs AI客服模块租户类型(待定)
|
||||||
var TenantModuleTypesAICs []TenantModuleType
|
var TenantModuleTypesAICs []TenantModuleTypeKV
|
||||||
|
|
||||||
// GetTenantModuleTypes 获取模块的租户类型列表
|
// GetTenantModuleTypes 获取模块的租户类型列表
|
||||||
func GetTenantModuleTypes(module string) []TenantModuleType {
|
func GetTenantModuleTypes(module string) []TenantModuleTypeKV {
|
||||||
switch module {
|
switch module {
|
||||||
case TenantModuleAssets:
|
case TenantModuleAssets:
|
||||||
return TenantModuleTypesAssets
|
return TenantModuleTypesAssets
|
||||||
@@ -48,7 +57,7 @@ func GetTenantModuleTypes(module string) []TenantModuleType {
|
|||||||
case TenantModuleAICs:
|
case TenantModuleAICs:
|
||||||
return TenantModuleTypesAICs
|
return TenantModuleTypesAICs
|
||||||
default:
|
default:
|
||||||
return []TenantModuleType{}
|
return []TenantModuleTypeKV{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,21 +68,14 @@ type ModuleTenantCheckReq struct {
|
|||||||
|
|
||||||
// ModuleTenantCheckRes 调用admin-go设置模块租户关系的响应
|
// ModuleTenantCheckRes 调用admin-go设置模块租户关系的响应
|
||||||
type ModuleTenantCheckRes struct {
|
type ModuleTenantCheckRes struct {
|
||||||
Status string `json:"status"` // 开通状态:activated(已开通)、expired(已到期)、not_activated(未开通)
|
Status bool `json:"status"`
|
||||||
Message string `json:"message"` // 状态描述
|
CertificationStatus bool `json:"certificationStatus"`
|
||||||
OpenStatus bool `json:"openStatus"` // 开通状态
|
Message string `json:"message"` // 状态描述
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModuleTenant 模块租户关系实体(引用自admin-go)
|
// ModuleTenant 模块租户关系实体(引用自admin-go)
|
||||||
type ModuleTenant struct {
|
type ModuleTenant struct {
|
||||||
Id uint64 `json:"id" description:""`
|
ExpireAt *gtime.Time `json:"expireAt" description:"到期时间"`
|
||||||
CreateBy uint64 `json:"createBy" description:"创建者"`
|
TenantModuleType TenantModuleType `json:"tenantModuleType" description:"租户模块类型"`
|
||||||
UpdateBy uint64 `json:"updateBy" description:"更新者"`
|
CertificationStatus int `json:"certificationStatus" description:"认证状态"`
|
||||||
CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"`
|
|
||||||
UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"`
|
|
||||||
ModuleKey string `json:"moduleKey" description:"模块Key"`
|
|
||||||
TenantId uint64 `json:"tenantId" description:"租户ID"`
|
|
||||||
ExpireAt *gtime.Time `json:"expireAt" description:"到期时间"`
|
|
||||||
AssetId string `json:"assetId" description:"资产ID"`
|
|
||||||
AssetSkuId string `json:"assetSkuId" description:"资产SKU ID"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,21 +13,15 @@ import (
|
|||||||
"github.com/gogf/gf/v2/net/ghttp"
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
"github.com/gogf/gf/v2/os/gtime"
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
"github.com/gogf/gf/v2/util/gconv"
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ModuleTenantCheck(r *ghttp.Request) {
|
func ModuleTenantCheck(r *ghttp.Request) {
|
||||||
//将 http.Header 转换为 map[string]string
|
|
||||||
headers := make(map[string]string)
|
|
||||||
for k, v := range r.Request.Header {
|
|
||||||
if len(v) > 0 {
|
|
||||||
headers[k] = v[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 检查是否是超级管理员
|
// 检查是否是超级管理员
|
||||||
isSuperAdmin := false
|
isSuperAdmin := false
|
||||||
if err := nats.CallRPC(r.Context(), "userService.IsSuperAdmin", nil, &isSuperAdmin); err != nil {
|
if err := nats.CallRPC(r.Context(), "userService.IsSuperAdmin", nil, &isSuperAdmin); err != nil {
|
||||||
SetResponseInfo(r.Context(), r, err)
|
SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, err)
|
||||||
}
|
}
|
||||||
// 如果是超级管理员,则不进行模块租户检查
|
// 如果是超级管理员,则不进行模块租户检查
|
||||||
if isSuperAdmin || r.Request.RequestURI == "/asset/getAssetAndSku?assetId=696b4acd1be1c8b76c4b4c15" {
|
if isSuperAdmin || r.Request.RequestURI == "/asset/getAssetAndSku?assetId=696b4acd1be1c8b76c4b4c15" {
|
||||||
@@ -36,7 +30,7 @@ func ModuleTenantCheck(r *ghttp.Request) {
|
|||||||
}
|
}
|
||||||
getUserInfo, err := utils.GetUserInfo(r.Context())
|
getUserInfo, err := utils.GetUserInfo(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SetResponseInfo(r.Context(), r, err)
|
SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, err)
|
||||||
}
|
}
|
||||||
exit := gconv.Int64(time.Minute * 1)
|
exit := gconv.Int64(time.Minute * 1)
|
||||||
getEX, err := message.GetRedisClientTest("test").GetEX(r.Context(), fmt.Sprintf("module_tenant:tenantId-%v", getUserInfo.TenantId), gredis.GetEXOption{
|
getEX, err := message.GetRedisClientTest("test").GetEX(r.Context(), fmt.Sprintf("module_tenant:tenantId-%v", getUserInfo.TenantId), gredis.GetEXOption{
|
||||||
@@ -45,34 +39,30 @@ func ModuleTenantCheck(r *ghttp.Request) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SetResponseInfo(r.Context(), r, err)
|
SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, err)
|
||||||
}
|
}
|
||||||
// 获取模块key
|
// 获取模块key
|
||||||
moduleKey := g.Cfg().MustGet(context.Background(), "server.name").String()
|
moduleKey := g.Cfg().MustGet(context.Background(), "server.name").String()
|
||||||
if !g.IsEmpty(getEX.String()) {
|
if !g.IsEmpty(getEX.String()) {
|
||||||
list := make([]beans.ModuleTenant, 0)
|
list := new(beans.ModuleTenant)
|
||||||
if err = json.Unmarshal([]byte(getEX.String()), &list); err != nil {
|
if err = json.Unmarshal(getEX.Bytes(), &list); err != nil {
|
||||||
SetResponseInfo(r.Context(), r, err)
|
SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, err)
|
||||||
}
|
|
||||||
var expireAt *gtime.Time
|
|
||||||
for _, value := range list {
|
|
||||||
if value.ModuleKey == moduleKey {
|
|
||||||
expireAt = value.ExpireAt
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 缓存中有数据,检查是否过期
|
// 缓存中有数据,检查是否过期
|
||||||
if !g.IsEmpty(expireAt) {
|
if !g.IsEmpty(list.ExpireAt) {
|
||||||
gt1 := gtime.New(time.Now())
|
gt1 := gtime.New(time.Now())
|
||||||
gt2 := gtime.New(expireAt)
|
gt2 := gtime.New(list.ExpireAt)
|
||||||
if !gt1.Before(gt2) {
|
if !gt1.Before(gt2) {
|
||||||
SetResponseInfo(r.Context(), r, "您访问的模块已过期,请续期后再使用")
|
SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, "功能模块已到期,请续期后再使用")
|
||||||
|
} else {
|
||||||
|
if list.CertificationStatus != 2 {
|
||||||
|
SetResponseInfo(r.Context(), r, http.StatusPreconditionRequired, "功能模块未认证通过,请认证后再使用")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SetResponseInfo(r.Context(), r, "您未开通此模块,请开通后再使用")
|
SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, "您未开通此功能模块,请开通后再使用")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 缓存为空,调用admin-go的Check接口检查模块开通状态
|
|
||||||
checkRes := new(beans.ModuleTenantCheckRes)
|
checkRes := new(beans.ModuleTenantCheckRes)
|
||||||
checkReq := beans.ModuleTenantCheckReq{
|
checkReq := beans.ModuleTenantCheckReq{
|
||||||
ModuleKey: moduleKey,
|
ModuleKey: moduleKey,
|
||||||
@@ -80,25 +70,27 @@ func ModuleTenantCheck(r *ghttp.Request) {
|
|||||||
}
|
}
|
||||||
err = nats.CallRPC(r.Context(), "moduleService.Check", &checkReq, checkRes)
|
err = nats.CallRPC(r.Context(), "moduleService.Check", &checkReq, checkRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SetResponseInfo(r.Context(), r, err)
|
SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, err)
|
||||||
}
|
}
|
||||||
// 根据检查结果判断是否允许访问
|
// 根据检查结果判断是否允许访问
|
||||||
if checkRes.Status == "not_activated" {
|
if !checkRes.Status {
|
||||||
SetResponseInfo(r.Context(), r, "您未开通此模块,请开通后再使用")
|
SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, checkRes.Message)
|
||||||
} else if checkRes.Status == "expired" {
|
} else {
|
||||||
SetResponseInfo(r.Context(), r, "您访问的模块已过期,请续期后再使用")
|
if !checkRes.CertificationStatus {
|
||||||
|
SetResponseInfo(r.Context(), r, http.StatusPreconditionRequired, checkRes.Message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Middleware.Next() // 继续执行后续中间件和路由处理
|
r.Middleware.Next() // 继续执行后续中间件和路由处理
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetResponseInfo 设置响应信息
|
// SetResponseInfo 设置响应信息
|
||||||
func SetResponseInfo(ctx context.Context, r *ghttp.Request, message any) {
|
func SetResponseInfo(ctx context.Context, r *ghttp.Request, code int, message any) {
|
||||||
_ = ctx
|
_ = ctx
|
||||||
r.Response.Status = 402
|
|
||||||
r.Response.WriteJsonExit(map[string]interface{}{
|
r.Response.WriteJsonExit(map[string]interface{}{
|
||||||
"success": false,
|
"success": false,
|
||||||
"code": 402,
|
"code": code,
|
||||||
"message": fmt.Sprintf("服务不可用:%s", message),
|
"message": fmt.Sprintf("服务不可用:%s", message),
|
||||||
})
|
})
|
||||||
r.Exit()
|
r.Exit()
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gitee.com/red-future---jilin-g/common/log/consts"
|
"gitee.com/red-future---jilin-g/common/log/consts"
|
||||||
"reflect"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitee.com/red-future---jilin-g/common/beans"
|
"gitee.com/red-future---jilin-g/common/beans"
|
||||||
@@ -643,7 +642,7 @@ func (m *MongoDB) SaveOrUpdate(ctx context.Context, filter []bson.M, update []bs
|
|||||||
return bulkResult, nil
|
return bulkResult, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildUpdateFilter(ctx context.Context, req interface{}) (filter bson.M, err error) {
|
func BuildUpdateData(ctx context.Context, req interface{}) (filter bson.M, err error) {
|
||||||
_ = ctx
|
_ = ctx
|
||||||
filter = bson.M{}
|
filter = bson.M{}
|
||||||
reqMap := gconv.Map(req)
|
reqMap := gconv.Map(req)
|
||||||
@@ -654,55 +653,3 @@ func BuildUpdateFilter(ctx context.Context, req interface{}) (filter bson.M, err
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// EntityToBson 将 *entity/entity 转换为 bson.M
|
|
||||||
func EntityToBson(entity interface{}) (bson.M, error) {
|
|
||||||
return EntityToBsonWithFilter(entity, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EntityToBsonWithFilter 将 *entity/entity 转换为 bson.M,并可选择是否过滤空值
|
|
||||||
func EntityToBsonWithFilter(entity interface{}, filterEmpty bool) (bson.M, error) {
|
|
||||||
if entity == nil {
|
|
||||||
return nil, fmt.Errorf("传入的 entity 实例为 nil")
|
|
||||||
}
|
|
||||||
bsonBytes, err := bson.Marshal(entity)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("entity 序列化为 BSON 字节流失败:%w", err)
|
|
||||||
}
|
|
||||||
var bsonMap bson.M
|
|
||||||
err = bson.Unmarshal(bsonBytes, &bsonMap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("BSON 字节流反序列化为 bson.M 失败:%w", err)
|
|
||||||
}
|
|
||||||
if filterEmpty {
|
|
||||||
for key, value := range bsonMap {
|
|
||||||
if isEmptyWithZero(value) {
|
|
||||||
delete(bsonMap, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bsonMap, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// isEmptyWithZero 判断是否为空值,但保留 int 类型的 0 值
|
|
||||||
func isEmptyWithZero(value interface{}) bool {
|
|
||||||
if value == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
rv := reflect.ValueOf(value)
|
|
||||||
kind := rv.Kind()
|
|
||||||
if kind == reflect.Ptr {
|
|
||||||
if rv.IsNil() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
kind = rv.Elem().Kind()
|
|
||||||
}
|
|
||||||
switch kind {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
||||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
|
||||||
reflect.Float32, reflect.Float64:
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
return g.IsEmpty(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user