feat: refactor super admin to use is_super_admin flag and enhance site deletion logic

- Changed super admin detection from role-based to `is_super_admin` flag in AdminUser model
- Added `requireDefaultAdminSiteId()` method to throw validation error when no integration site exists
- Enhanced site deletion to migrate platform role bindings to fallback site and auto-delete site-specific admin accounts
- Made agent line code optional with auto-generation fallback using `{site_code}-agent-{counter}` format
This commit is contained in:
2026-06-12 20:47:40 +08:00
parent 980f3c9593
commit 395e1c7400
36 changed files with 1193 additions and 153 deletions

View File

@@ -29,6 +29,7 @@ final class AdminUser extends Authenticatable
'email',
'password',
'status',
'is_super_admin',
];
protected $hidden = [
@@ -42,27 +43,40 @@ final class AdminUser extends Authenticatable
'email_verified_at' => 'datetime',
'last_login_at' => 'datetime',
'password' => 'hashed',
'is_super_admin' => 'boolean',
];
}
public static function defaultAdminSiteId(): int
public static function defaultAdminSiteId(): ?int
{
static $cached = null;
if ($cached !== null) {
static $resolved = false;
if ($resolved) {
return $cached;
}
$resolved = true;
$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;
$cached = $id !== null ? (int) $id : null;
return $cached;
}
public static function requireDefaultAdminSiteId(): int
{
$siteId = self::requireDefaultAdminSiteId();
if ($siteId === null) {
throw ValidationException::withMessages([
'admin_site_id' => [__('admin.no_integration_site')],
]);
}
return $siteId;
}
/**
* 用户在各站点上的角色(多站点 RBAC
*
@@ -188,7 +202,7 @@ final class AdminUser extends Authenticatable
public function syncRoleSlugsForDefaultSite(array $slugs): void
{
$siteId = self::defaultAdminSiteId();
$siteId = self::requireDefaultAdminSiteId();
$slugs = array_values(array_unique($slugs));
$roleIds = DB::table('admin_roles')
->whereIn('slug', $slugs)
@@ -225,7 +239,7 @@ final class AdminUser extends Authenticatable
*/
public function syncSystemRoleSlugs(array $slugs): void
{
$this->syncSystemRoleSlugsForSite(self::defaultAdminSiteId(), $slugs);
$this->syncSystemRoleSlugsForSite(self::requireDefaultAdminSiteId(), $slugs);
}
/**
@@ -236,6 +250,8 @@ final class AdminUser extends Authenticatable
public function syncSystemRoleSlugsForSite(int $siteId, array $slugs): void
{
$slugs = array_values(array_unique($slugs));
\App\Support\SuperAdminAccount::assertNotSiteRoleAssignment($slugs);
$roleIds = DB::table('admin_roles')
->where('scope_type', AdminRole::SCOPE_SYSTEM)
->whereIn('slug', $slugs)
@@ -269,11 +285,7 @@ final class AdminUser extends Authenticatable
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();
return (bool) $this->is_super_admin;
}
public function primaryAgentNodeId(): ?int
@@ -348,16 +360,25 @@ final class AdminUser extends Authenticatable
*/
public function directMenuActionPermissionCodes(): array
{
if ($this->isSuperAdmin()) {
return [];
}
$siteId = self::defaultAdminSiteId();
$rows = DB::table('admin_user_menu_actions as uma')
$query = 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 {
->where('ma.status', 1);
if ($siteId !== null) {
$query->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();
});
} else {
$query->whereNull('uma.site_id');
}
$rows = $query->pluck('ma.permission_code')->all();
$out = [];
foreach ($rows as $code) {
@@ -485,11 +506,17 @@ final class AdminUser extends Authenticatable
{
$this->loadMissing('roles');
return $this->roles
$slugs = $this->roles
->pluck('slug')
->filter(static fn ($slug): bool => is_string($slug) && $slug !== '')
->unique()
->values()
->all();
if ($this->isSuperAdmin()) {
$slugs = array_values(array_unique(array_merge([self::ROLE_SUPER_ADMIN], $slugs)));
}
return $slugs;
}
}