feat(user-modal): 사용자 정보 모달 및 컨텍스트 메뉴 확장
사용자 모달 기능: - 사용자 정보 모달 팝업 (조회/삭제/수정) - 권한 요약 정보 (Web/API 권한 카운트) - 2x2 그리드 레이아웃 (테넌트, 역할, 부서, 권한) - 테이블 행 클릭으로 모달 열기 - 권한 관리 링크 클릭 시 해당 사용자 자동 선택 컨텍스트 메뉴 확장: - permission-analyze 페이지 사용자 이름에 컨텍스트 메뉴 - user-permissions 페이지 사용자 버튼에 컨텍스트 메뉴 - 사용자 모달 내 테넌트 칩에 컨텍스트 메뉴 - 헤더 테넌트 배지에 컨텍스트 메뉴 - 테넌트 메뉴에 "이 테넌트로 전환" 기능 추가
This commit is contained in:
@@ -18,6 +18,14 @@ public function getUsers(array $filters = [], int $perPage = 15): LengthAwarePag
|
||||
$tenantId = session('selected_tenant_id');
|
||||
$query = User::query()->withTrashed();
|
||||
|
||||
// 역할/부서 관계 eager loading (테넌트별)
|
||||
if ($tenantId) {
|
||||
$query->with([
|
||||
'userRoles' => fn ($q) => $q->where('tenant_id', $tenantId)->with('role'),
|
||||
'departmentUsers' => fn ($q) => $q->where('tenant_id', $tenantId)->with('department'),
|
||||
]);
|
||||
}
|
||||
|
||||
// 테넌트 필터링 (user_tenants pivot을 통한 필터링)
|
||||
if ($tenantId) {
|
||||
$query->whereHas('tenants', function ($q) use ($tenantId) {
|
||||
@@ -226,6 +234,132 @@ public function forceDeleteUser(int $id): bool
|
||||
return $user->forceDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 모달용 사용자 상세 정보 조회
|
||||
*/
|
||||
public function getUserForModal(int $id): ?User
|
||||
{
|
||||
$tenantId = session('selected_tenant_id');
|
||||
|
||||
$query = User::query()
|
||||
->with('deletedByUser')
|
||||
->withTrashed();
|
||||
|
||||
// 역할/부서 관계 eager loading (테넌트별)
|
||||
if ($tenantId) {
|
||||
$query->with([
|
||||
'userRoles' => fn ($q) => $q->where('tenant_id', $tenantId)->with('role'),
|
||||
'departmentUsers' => fn ($q) => $q->where('tenant_id', $tenantId)->with('department'),
|
||||
'tenants',
|
||||
]);
|
||||
} else {
|
||||
$query->with(['userRoles.role', 'departmentUsers.department', 'tenants']);
|
||||
}
|
||||
|
||||
$user = $query->find($id);
|
||||
|
||||
// 권한 카운트 추가
|
||||
if ($user && $tenantId) {
|
||||
$permissionCounts = $this->getUserPermissionCounts($user->id, $tenantId);
|
||||
$user->web_permission_count = $permissionCounts['web'];
|
||||
$user->api_permission_count = $permissionCounts['api'];
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자별 guard별 권한 개수 조회 (역할 + 부서 + 개인 오버라이드 통합)
|
||||
*/
|
||||
private function getUserPermissionCounts(int $userId, int $tenantId): array
|
||||
{
|
||||
$result = ['web' => 0, 'api' => 0];
|
||||
$now = now();
|
||||
|
||||
foreach (['web', 'api'] as $guardName) {
|
||||
// 1. 역할 권한
|
||||
$rolePermissions = \DB::table('model_has_roles as mhr')
|
||||
->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'mhr.role_id')
|
||||
->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
|
||||
->where('mhr.model_type', User::class)
|
||||
->where('mhr.model_id', $userId)
|
||||
->where('p.guard_name', $guardName)
|
||||
->where('p.name', 'like', 'menu:%')
|
||||
->pluck('p.name')
|
||||
->toArray();
|
||||
|
||||
// 2. 부서 권한
|
||||
$deptPermissions = \DB::table('department_user as du')
|
||||
->join('permission_overrides as po', function ($j) use ($now, $tenantId) {
|
||||
$j->on('po.model_id', '=', 'du.department_id')
|
||||
->where('po.model_type', 'App\\Models\\Tenants\\Department')
|
||||
->where('po.tenant_id', $tenantId)
|
||||
->whereNull('po.deleted_at')
|
||||
->where('po.effect', 1)
|
||||
->where(function ($w) use ($now) {
|
||||
$w->whereNull('po.effective_from')->orWhere('po.effective_from', '<=', $now);
|
||||
})
|
||||
->where(function ($w) use ($now) {
|
||||
$w->whereNull('po.effective_to')->orWhere('po.effective_to', '>=', $now);
|
||||
});
|
||||
})
|
||||
->join('permissions as p', 'p.id', '=', 'po.permission_id')
|
||||
->whereNull('du.deleted_at')
|
||||
->where('du.user_id', $userId)
|
||||
->where('du.tenant_id', $tenantId)
|
||||
->where('p.guard_name', $guardName)
|
||||
->where('p.name', 'like', 'menu:%')
|
||||
->pluck('p.name')
|
||||
->toArray();
|
||||
|
||||
// 3. 개인 오버라이드 (ALLOW)
|
||||
$personalAllows = \DB::table('permission_overrides as po')
|
||||
->join('permissions as p', 'p.id', '=', 'po.permission_id')
|
||||
->where('po.model_type', User::class)
|
||||
->where('po.model_id', $userId)
|
||||
->where('po.tenant_id', $tenantId)
|
||||
->where('po.effect', 1)
|
||||
->whereNull('po.deleted_at')
|
||||
->where(function ($w) use ($now) {
|
||||
$w->whereNull('po.effective_from')->orWhere('po.effective_from', '<=', $now);
|
||||
})
|
||||
->where(function ($w) use ($now) {
|
||||
$w->whereNull('po.effective_to')->orWhere('po.effective_to', '>=', $now);
|
||||
})
|
||||
->where('p.guard_name', $guardName)
|
||||
->where('p.name', 'like', 'menu:%')
|
||||
->pluck('p.name')
|
||||
->toArray();
|
||||
|
||||
// 4. 개인 오버라이드 (DENY) - 제외할 권한
|
||||
$personalDenies = \DB::table('permission_overrides as po')
|
||||
->join('permissions as p', 'p.id', '=', 'po.permission_id')
|
||||
->where('po.model_type', User::class)
|
||||
->where('po.model_id', $userId)
|
||||
->where('po.tenant_id', $tenantId)
|
||||
->where('po.effect', 0)
|
||||
->whereNull('po.deleted_at')
|
||||
->where(function ($w) use ($now) {
|
||||
$w->whereNull('po.effective_from')->orWhere('po.effective_from', '<=', $now);
|
||||
})
|
||||
->where(function ($w) use ($now) {
|
||||
$w->whereNull('po.effective_to')->orWhere('po.effective_to', '>=', $now);
|
||||
})
|
||||
->where('p.guard_name', $guardName)
|
||||
->where('p.name', 'like', 'menu:%')
|
||||
->pluck('p.name')
|
||||
->toArray();
|
||||
|
||||
// 통합: (역할 OR 부서 OR 개인ALLOW) - 개인DENY
|
||||
$allAllowed = array_unique(array_merge($rolePermissions, $deptPermissions, $personalAllows));
|
||||
$effectivePermissions = array_diff($allAllowed, $personalDenies);
|
||||
|
||||
$result[$guardName] = count($effectivePermissions);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 활성 사용자 목록 조회 (드롭다운용)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user