Files

73 lines
2.2 KiB
Go
Raw Permalink Normal View History

2026-09-17 17:38:45 +08:00
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)
}