73 lines
2.2 KiB
Go
73 lines
2.2 KiB
Go
package logic
|
||
|
||
import (
|
||
"context"
|
||
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
"github.com/gogf/gf/v2/os/gtime"
|
||
|
||
v1 "tool-api/api/user/v1"
|
||
"tool-api/internal/consts"
|
||
"tool-api/internal/model/entity"
|
||
)
|
||
|
||
// ===== 等级有效期(会员)判定 =====
|
||
//
|
||
// 背景:users.level_key 是单值,无法表达「会员到期」。引入 users.level_expire_at 后,
|
||
// 所有依赖等级的地方都必须先经过 EffectiveLevelKey 收敛,否则会出现「会员已过期但
|
||
// 工具仍可用」的越权问题。
|
||
//
|
||
// 约定:
|
||
// - level_expire_at IS NULL → 永久有效(管理员手动分配 / 种子默认等级)
|
||
// - level_expire_at <= now → 已过期,回落 consts.DefaultLevelKey
|
||
// - level_expire_at > now → 正常返回 users.level_key
|
||
|
||
// EffectiveLevelKey 计算用户当前实际生效的等级 key。一处判定,全处复用。
|
||
func EffectiveLevelKey(user *entity.Users) string {
|
||
if user == nil || user.LevelKey == "" {
|
||
return consts.DefaultLevelKey
|
||
}
|
||
if IsLevelExpired(user) {
|
||
return consts.DefaultLevelKey
|
||
}
|
||
return user.LevelKey
|
||
}
|
||
|
||
// IsLevelExpired 等级是否已过期
|
||
func IsLevelExpired(user *entity.Users) bool {
|
||
if user == nil || user.LevelExpireAt == nil || user.LevelExpireAt.IsZero() {
|
||
return false
|
||
}
|
||
return user.LevelExpireAt.Before(gtime.Now())
|
||
}
|
||
|
||
// EffectiveLevelInfo 当前生效等级(含已授权模块),已过期时返回默认等级
|
||
func EffectiveLevelInfo(ctx context.Context, user *entity.Users) (*v1.LevelInfo, error) {
|
||
return GetLevelByKey(ctx, EffectiveLevelKey(user))
|
||
}
|
||
|
||
// levelWeight 等级权限权重,取 levels.sort(越大权限越高)。
|
||
// 查询失败时回落 0,保证不因权重查询异常而误升权。
|
||
func levelWeight(ctx context.Context, levelKey string) int {
|
||
if levelKey == "" {
|
||
return 0
|
||
}
|
||
value, err := g.Model(consts.TableLevels).
|
||
Where("level_key", levelKey).
|
||
Where("is_enabled", 1).
|
||
Value("sort")
|
||
if err != nil || value == nil {
|
||
return 0
|
||
}
|
||
return value.Int()
|
||
}
|
||
|
||
// CanLevelAccess 判断某个等级是否包含指定模块(供发货后自检 / 管理端复用)
|
||
func CanLevelAccess(ctx context.Context, levelKey, moduleKey string) bool {
|
||
level, err := GetLevelByKey(ctx, levelKey)
|
||
if err != nil {
|
||
return false
|
||
}
|
||
return slicesContain(level.Modules, moduleKey)
|
||
}
|