'datetime', 'last_login_at' => 'datetime', 'password' => 'hashed', ]; } public static function defaultAdminSiteId(): int { static $cached = null; if ($cached !== null) { return $cached; } $id = DB::table('admin_sites')->where('is_default', true)->value('id'); if ($id === null) { $id = DB::table('admin_sites')->orderBy('id')->value('id'); } if ($id === null) { throw new \RuntimeException('No admin_sites row found.'); } $cached = (int) $id; return $cached; } /** * 用户在各站点上的角色(多站点 RBAC)。 * * @return BelongsToMany */ public function roles(): BelongsToMany { return $this->belongsToMany( AdminRole::class, 'admin_user_site_roles', 'admin_user_id', 'role_id', )->withPivot(['site_id', 'granted_at']); } /** * 将用户在默认站点上的角色设为指定 slug 集合(全量替换该站点 pivot)。 * * @param list $slugs */ public function syncRoleSlugsForDefaultSite(array $slugs): void { $siteId = self::defaultAdminSiteId(); $slugs = array_values(array_unique($slugs)); $roleIds = DB::table('admin_roles') ->whereIn('slug', $slugs) ->pluck('id') ->all(); DB::transaction(function () use ($siteId, $roleIds): void { DB::table('admin_user_site_roles') ->where('admin_user_id', $this->id) ->where('site_id', $siteId) ->delete(); $now = now(); foreach ($roleIds as $rid) { DB::table('admin_user_site_roles')->insert([ 'admin_user_id' => $this->id, 'site_id' => $siteId, 'role_id' => (int) $rid, 'granted_at' => $now, ]); } }); } public function isSuperAdmin(): bool { if ($this->relationLoaded('roles')) { return $this->roles->contains('slug', self::ROLE_SUPER_ADMIN); } return $this->roles()->where('admin_roles.slug', self::ROLE_SUPER_ADMIN)->exists(); } /** * 可访问的 admin_sites.id 列表;`null` 表示不限制(超管)。 * * @return list|null */ public function accessibleAdminSiteIds(): ?array { if ($this->isSuperAdmin()) { return null; } $ids = DB::table('admin_user_site_roles') ->where('admin_user_id', $this->id) ->distinct() ->pluck('site_id') ->map(static fn ($id): int => (int) $id) ->values() ->all(); return $ids; } /** * 仅来自「直接授权」的 menu_action.permission_code(默认站点,含 site_id 为 null 的历史行)。 * * @return list */ public function directMenuActionPermissionCodes(): array { $siteId = self::defaultAdminSiteId(); $rows = DB::table('admin_user_menu_actions as uma') ->join('admin_menu_actions as ma', 'ma.id', '=', 'uma.menu_action_id') ->where('uma.admin_user_id', $this->id) ->where(function ($q) use ($siteId): void { $q->where('uma.site_id', $siteId)->orWhereNull('uma.site_id'); }) ->where('ma.status', 1) ->pluck('ma.permission_code') ->all(); $out = []; foreach ($rows as $code) { if (is_string($code) && $code !== '') { $out[$code] = true; } } return array_keys($out); } /** * 直接授权对应的 `prd.*` 展示列表(与 {@see self::directMenuActionPermissionCodes()} 桥接)。 * * @return list */ public function directLegacyPermissionSlugs(): array { return AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes($this->directMenuActionPermissionCodes()); } /** * 角色 + 直接授权合并后的 menu_action.permission_code。 * * @return list */ public function effectiveMenuActionPermissionCodes(): array { if ($this->isSuperAdmin()) { $codes = DB::table('admin_menu_actions')->where('status', 1)->pluck('permission_code')->all(); $out = []; foreach ($codes as $c) { if (is_string($c) && $c !== '') { $out[$c] = true; } } return array_keys($out); } $fromRoles = DB::table('admin_user_site_roles as usr') ->join('admin_role_menu_actions as rma', 'rma.role_id', '=', 'usr.role_id') ->join('admin_menu_actions as ma', 'ma.id', '=', 'rma.menu_action_id') ->where('usr.admin_user_id', $this->id) ->where('ma.status', 1) ->pluck('ma.permission_code') ->all(); $merged = []; foreach (array_merge($fromRoles, $this->directMenuActionPermissionCodes()) as $c) { if (is_string($c) && $c !== '') { $merged[$c] = true; } } return array_keys($merged); } /** 是否具备指定权限:`prd.*` 走 legacy_map;否则按 permission_code 精确匹配。含 `super_admin` 全放行。 */ public function hasAdminPermission(string $slug): bool { if ($this->isSuperAdmin()) { return true; } $effective = $this->effectiveMenuActionPermissionCodes(); if ($slug !== '' && in_array($slug, $effective, true)) { return true; } if (! str_starts_with($slug, 'prd.')) { return false; } $needed = AdminPermissionBridge::menuActionCodesForLegacy($slug); if ($needed === []) { return false; } return count(array_intersect($needed, $effective)) > 0; } /** * @return list 与 Next 侧栏兼容的 `prd.*` slug 列表 */ public function adminPermissionSlugs(): array { if ($this->isSuperAdmin()) { return AdminPermissionBridge::allLegacySlugs(); } return AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes($this->effectiveMenuActionPermissionCodes()); } /** * @return list */ public function adminRoleSlugs(): array { $this->loadMissing('roles'); return $this->roles ->pluck('slug') ->filter(static fn ($slug): bool => is_string($slug) && $slug !== '') ->unique() ->values() ->all(); } }