From 8f6adbb16df3039667dd410f6f015742e0634500 Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Thu, 22 Jan 2026 15:04:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A8=A1=E5=9D=97=E7=A7=9F?= =?UTF-8?q?=E6=88=B7=E6=A3=80=E6=9F=A5=E9=80=BB=E8=BE=91=EF=BC=8C=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84=E5=B9=B6=E7=AE=80?= =?UTF-8?q?=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- beans/module_tenant.go | 48 +++++++++++++------------ middleware/module_tenant_check.go | 58 +++++++++++++------------------ mongo/mongo.go | 55 +---------------------------- 3 files changed, 51 insertions(+), 110 deletions(-) diff --git a/beans/module_tenant.go b/beans/module_tenant.go index fb2f328..4b53f8b 100644 --- a/beans/module_tenant.go +++ b/beans/module_tenant.go @@ -19,27 +19,36 @@ var ( TenantModuleAICs = ModuleAssetId["customerService"] // AI客服模块 ) -// TenantModuleType 租户类型 -type TenantModuleType struct { +type TenantModuleType string + +const ( + TenantModuleTypePlatform TenantModuleType = "platform" + TenantModuleTypePrivate TenantModuleType = "private" + TenantModuleTypeSupplier TenantModuleType = "supplier" + TenantModuleTypeSmallShop TenantModuleType = "small_shop" +) + +// TenantModuleTypeKV 租户类型 +type TenantModuleTypeKV struct { Key string Value string } // TenantModuleTypesAssets 资产模块租户类型 -var TenantModuleTypesAssets = []TenantModuleType{ - {Key: "private_cloud", Value: "私有云租户"}, - {Key: "supplier", Value: "供应商"}, - {Key: "small_shop", Value: "电商小店"}, +var TenantModuleTypesAssets = []TenantModuleTypeKV{ + {Key: string(TenantModuleTypePrivate), Value: "私域租户"}, + {Key: string(TenantModuleTypeSupplier), Value: "供应商"}, + {Key: string(TenantModuleTypeSmallShop), Value: "电商小店"}, } // TenantModuleTypesAd 广告模块租户类型(待定) -var TenantModuleTypesAd []TenantModuleType +var TenantModuleTypesAd []TenantModuleTypeKV // TenantModuleTypesAICs AI客服模块租户类型(待定) -var TenantModuleTypesAICs []TenantModuleType +var TenantModuleTypesAICs []TenantModuleTypeKV // GetTenantModuleTypes 获取模块的租户类型列表 -func GetTenantModuleTypes(module string) []TenantModuleType { +func GetTenantModuleTypes(module string) []TenantModuleTypeKV { switch module { case TenantModuleAssets: return TenantModuleTypesAssets @@ -48,7 +57,7 @@ func GetTenantModuleTypes(module string) []TenantModuleType { case TenantModuleAICs: return TenantModuleTypesAICs default: - return []TenantModuleType{} + return []TenantModuleTypeKV{} } } @@ -59,21 +68,14 @@ type ModuleTenantCheckReq struct { // ModuleTenantCheckRes 调用admin-go设置模块租户关系的响应 type ModuleTenantCheckRes struct { - Status string `json:"status"` // 开通状态:activated(已开通)、expired(已到期)、not_activated(未开通) - Message string `json:"message"` // 状态描述 - OpenStatus bool `json:"openStatus"` // 开通状态 + Status bool `json:"status"` + CertificationStatus bool `json:"certificationStatus"` + Message string `json:"message"` // 状态描述 } // ModuleTenant 模块租户关系实体(引用自admin-go) type ModuleTenant struct { - Id uint64 `json:"id" description:""` - CreateBy uint64 `json:"createBy" description:"创建者"` - UpdateBy uint64 `json:"updateBy" 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"` + ExpireAt *gtime.Time `json:"expireAt" description:"到期时间"` + TenantModuleType TenantModuleType `json:"tenantModuleType" description:"租户模块类型"` + CertificationStatus int `json:"certificationStatus" description:"认证状态"` } diff --git a/middleware/module_tenant_check.go b/middleware/module_tenant_check.go index 0fb32bb..1eff872 100644 --- a/middleware/module_tenant_check.go +++ b/middleware/module_tenant_check.go @@ -13,21 +13,15 @@ import ( "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" + "net/http" "time" ) 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 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" { @@ -36,7 +30,7 @@ func ModuleTenantCheck(r *ghttp.Request) { } getUserInfo, err := utils.GetUserInfo(r.Context()) if err != nil { - SetResponseInfo(r.Context(), r, err) + SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, err) } exit := gconv.Int64(time.Minute * 1) 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 { - SetResponseInfo(r.Context(), r, err) + SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, err) } // 获取模块key moduleKey := g.Cfg().MustGet(context.Background(), "server.name").String() if !g.IsEmpty(getEX.String()) { - list := make([]beans.ModuleTenant, 0) - if err = json.Unmarshal([]byte(getEX.String()), &list); err != nil { - SetResponseInfo(r.Context(), r, err) - } - var expireAt *gtime.Time - for _, value := range list { - if value.ModuleKey == moduleKey { - expireAt = value.ExpireAt - break - } + list := new(beans.ModuleTenant) + if err = json.Unmarshal(getEX.Bytes(), &list); err != nil { + SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, err) } // 缓存中有数据,检查是否过期 - if !g.IsEmpty(expireAt) { + if !g.IsEmpty(list.ExpireAt) { gt1 := gtime.New(time.Now()) - gt2 := gtime.New(expireAt) + gt2 := gtime.New(list.ExpireAt) 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 { - SetResponseInfo(r.Context(), r, "您未开通此模块,请开通后再使用") + SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, "您未开通此功能模块,请开通后再使用") } } else { - // 缓存为空,调用admin-go的Check接口检查模块开通状态 checkRes := new(beans.ModuleTenantCheckRes) checkReq := beans.ModuleTenantCheckReq{ ModuleKey: moduleKey, @@ -80,25 +70,27 @@ func ModuleTenantCheck(r *ghttp.Request) { } err = nats.CallRPC(r.Context(), "moduleService.Check", &checkReq, checkRes) if err != nil { - SetResponseInfo(r.Context(), r, err) + SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, err) } // 根据检查结果判断是否允许访问 - if checkRes.Status == "not_activated" { - SetResponseInfo(r.Context(), r, "您未开通此模块,请开通后再使用") - } else if checkRes.Status == "expired" { - SetResponseInfo(r.Context(), r, "您访问的模块已过期,请续期后再使用") + if !checkRes.Status { + SetResponseInfo(r.Context(), r, http.StatusPaymentRequired, checkRes.Message) + } else { + if !checkRes.CertificationStatus { + SetResponseInfo(r.Context(), r, http.StatusPreconditionRequired, checkRes.Message) + } } } + r.Middleware.Next() // 继续执行后续中间件和路由处理 } // SetResponseInfo 设置响应信息 -func SetResponseInfo(ctx context.Context, r *ghttp.Request, message any) { +func SetResponseInfo(ctx context.Context, r *ghttp.Request, code int, message any) { _ = ctx - r.Response.Status = 402 r.Response.WriteJsonExit(map[string]interface{}{ "success": false, - "code": 402, + "code": code, "message": fmt.Sprintf("服务不可用:%s", message), }) r.Exit() diff --git a/mongo/mongo.go b/mongo/mongo.go index f0b10ae..c25d86a 100644 --- a/mongo/mongo.go +++ b/mongo/mongo.go @@ -10,7 +10,6 @@ import ( "errors" "fmt" "gitee.com/red-future---jilin-g/common/log/consts" - "reflect" "time" "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 } -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 filter = bson.M{} reqMap := gconv.Map(req) @@ -654,55 +653,3 @@ func BuildUpdateFilter(ctx context.Context, req interface{}) (filter bson.M, err } 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) - } -}