'datetime', 'password' => 'hashed', 'is_admin' => 'boolean', 'role' => 'string', 'permissions' => 'array', 'points' => 'integer', ]; } /** * 等级阈值(积分下限),键名即等级 L1..L5 */ public const LEVELS = [ 1 => 0, 2 => 50, 3 => 200, 4 => 600, 5 => 1500, ]; public const LEVEL_NAMES = [ 1 => '新芯用户', 2 => '活跃用户', 3 => '资深用户', 4 => '核心用户', 5 => '云创芯达人', ]; /** * 兼容旧逻辑:有后台角色即视为「管理员」(用于前台「后台」入口判断)。 * 新逻辑请使用 role() / canPerm()。 */ public function isAdmin(): bool { return $this->role !== 'user'; } /** * 角色等级(1-5),用于层级判断。 */ public function roleLevel(): int { return (int) config("permissions.roles.{$this->role}.level", 1); } public function roleLabel(): string { return config("permissions.roles.{$this->role}.label", '注册用户'); } public function isSuperAdmin(): bool { return $this->role === 'super_admin'; } public function isStaff(): bool { return $this->role === 'staff'; } /** * 细粒度权限判定。 * - 超级管理员:恒 true * - 内部员工:查 users.permissions(管理员勾选分配) * - 其他角色:查 config/permissions.php 中该角色的默认权限集 */ public function canPerm(string $key): bool { if ($this->role === 'super_admin') { return true; } if ($this->role === 'staff') { $perms = is_array($this->permissions) ? $this->permissions : []; return in_array($key, $perms, true); } $def = config("permissions.roles.{$this->role}.permissions", []); if (in_array('*', $def, true)) { return true; } return in_array($key, $def, true); } public function threads() { return $this->hasMany(ForumThread::class); } public function replies() { return $this->hasMany(ForumReply::class); } /** * 增加积分(用于注册、发帖、回帖等动作)。 */ public function awardPoints(int $amount): void { $this->increment('points', max(0, $amount)); } /** * 当前等级(1-5)。 */ public function level(): int { $points = $this->points ?? 0; $level = 1; foreach (self::LEVELS as $lv => $threshold) { if ($points >= $threshold) { $level = $lv; } } return $level; } public function levelName(): string { return self::LEVEL_NAMES[$this->level()] ?? '用户'; } /** * 当前等级进度(用于前台进度条展示)。 */ public function levelProgress(): array { $level = $this->level(); $current = self::LEVELS[$level] ?? 0; $next = self::LEVELS[$level + 1] ?? null; if ($next === null) { return ['level' => $level, 'current' => $this->points, 'next' => null, 'percent' => 100]; } $span = $next - $current; $done = $this->points - $current; $percent = $span > 0 ? (int) min(100, round($done / $span * 100)) : 100; return ['level' => $level, 'current' => $this->points, 'next' => $next, 'percent' => $percent]; } }