Files
qitongxue-api/internal/logic/level.go
2026-09-17 17:38:45 +08:00

73 lines
2.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}