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)
|
|||
|
|
}
|