211 lines
5.9 KiB
PHP
211 lines
5.9 KiB
PHP
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use App\Support\AdminPermissionBridge;
|
||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
use Illuminate\Notifications\Notifiable;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Laravel\Sanctum\HasApiTokens;
|
||
|
||
class AdminUser extends Authenticatable
|
||
{
|
||
use HasApiTokens;
|
||
use Notifiable;
|
||
|
||
public const ROLE_SUPER_ADMIN = 'super_admin';
|
||
|
||
protected $table = 'admin_users';
|
||
|
||
protected $fillable = [
|
||
'username',
|
||
'name',
|
||
'email',
|
||
'password',
|
||
'status',
|
||
];
|
||
|
||
protected $hidden = [
|
||
'password',
|
||
'remember_token',
|
||
];
|
||
|
||
protected function casts(): array
|
||
{
|
||
return [
|
||
'email_verified_at' => '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<AdminRole, AdminUser>
|
||
*/
|
||
public function roles(): BelongsToMany
|
||
{
|
||
return $this->belongsToMany(
|
||
AdminRole::class,
|
||
'admin_user_site_roles',
|
||
'admin_user_id',
|
||
'role_id',
|
||
)->withPivot(['site_id', 'granted_at']);
|
||
}
|
||
|
||
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();
|
||
}
|
||
|
||
/**
|
||
* 仅来自「直接授权」的 menu_action.permission_code(默认站点,含 site_id 为 null 的历史行)。
|
||
*
|
||
* @return list<string>
|
||
*/
|
||
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<string>
|
||
*/
|
||
public function directLegacyPermissionSlugs(): array
|
||
{
|
||
return AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes($this->directMenuActionPermissionCodes());
|
||
}
|
||
|
||
/**
|
||
* 角色 + 直接授权合并后的 menu_action.permission_code。
|
||
*
|
||
* @return list<string>
|
||
*/
|
||
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<string> 与 Next 侧栏、`admin.permission` 中间件一致的 `prd.*` slug 列表
|
||
*/
|
||
public function adminPermissionSlugs(): array
|
||
{
|
||
if ($this->isSuperAdmin()) {
|
||
return AdminPermissionBridge::allLegacySlugs();
|
||
}
|
||
|
||
return AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes($this->effectiveMenuActionPermissionCodes());
|
||
}
|
||
|
||
/**
|
||
* @return list<string>
|
||
*/
|
||
public function adminRoleSlugs(): array
|
||
{
|
||
$this->loadMissing('roles');
|
||
|
||
return $this->roles
|
||
->pluck('slug')
|
||
->filter(static fn ($slug): bool => is_string($slug) && $slug !== '')
|
||
->unique()
|
||
->values()
|
||
->all();
|
||
}
|
||
}
|