Files
sam-manage/app/Services/TenantService.php
hskwon 575e9df431 feat: Phase 4-1 테넌트 관리 백엔드 구현
- TenantService 생성 (CRUD, 통계, 복원/영구삭제)
- API Controller 구현 (HTMX 요청 감지, HTML/JSON 이중 응답)
- FormRequest 검증 (StoreTenantRequest, UpdateTenantRequest)
- Tenant 모델 확장 (17개 필드, 관계 설정, accessor)
- Department, Menu, Role 모델 복사 (admin → mng)
- Web Controller 수정 (index/create/edit 화면)
- MIGRATION_PLAN.md 작성 (HTMX + API 아키텍처)
2025-11-21 14:46:13 +09:00

149 lines
3.9 KiB
PHP

<?php
namespace App\Services;
use App\Models\Tenants\Tenant;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Collection;
class TenantService
{
/**
* 테넌트 목록 조회 (페이지네이션)
*/
public function getTenants(array $filters = [], int $perPage = 15): LengthAwarePaginator
{
$query = Tenant::query()
->withCount(['users', 'departments', 'menus', 'roles'])
->withTrashed();
// 검색 필터
if (!empty($filters['search'])) {
$search = $filters['search'];
$query->where(function ($q) use ($search) {
$q->where('company_name', 'like', "%{$search}%")
->orWhere('code', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
}
// 상태 필터
if (!empty($filters['tenant_st_code'])) {
$query->where('tenant_st_code', $filters['tenant_st_code']);
}
// Soft Delete 필터
if (isset($filters['trashed'])) {
if ($filters['trashed'] === 'only') {
$query->onlyTrashed();
} elseif ($filters['trashed'] === 'with') {
$query->withTrashed();
}
}
// 정렬
$sortBy = $filters['sort_by'] ?? 'id';
$sortDirection = $filters['sort_direction'] ?? 'desc';
$query->orderBy($sortBy, $sortDirection);
return $query->paginate($perPage);
}
/**
* 특정 테넌트 조회
*/
public function getTenantById(int $id, bool $withTrashed = false): ?Tenant
{
$query = Tenant::query()->withCount(['users', 'departments', 'menus', 'roles']);
if ($withTrashed) {
$query->withTrashed();
}
return $query->find($id);
}
/**
* 테넌트 생성
*/
public function createTenant(array $data): Tenant
{
return Tenant::create($data);
}
/**
* 테넌트 수정
*/
public function updateTenant(int $id, array $data): bool
{
$tenant = Tenant::findOrFail($id);
return $tenant->update($data);
}
/**
* 테넌트 삭제 (Soft Delete)
*/
public function deleteTenant(int $id): bool
{
$tenant = Tenant::findOrFail($id);
return $tenant->delete();
}
/**
* 테넌트 복원
*/
public function restoreTenant(int $id): bool
{
$tenant = Tenant::onlyTrashed()->findOrFail($id);
return $tenant->restore();
}
/**
* 테넌트 영구 삭제 (슈퍼관리자 전용)
*/
public function forceDeleteTenant(int $id): bool
{
$tenant = Tenant::withTrashed()->findOrFail($id);
return $tenant->forceDelete();
}
/**
* 활성 테넌트 목록 (드롭다운용)
*/
public function getActiveTenants(): Collection
{
return Tenant::query()
->active()
->orderBy('company_name')
->get(['id', 'company_name', 'code']);
}
/**
* 테넌트 코드 중복 체크
*/
public function isCodeExists(string $code, ?int $excludeId = null): bool
{
$query = Tenant::where('code', $code);
if ($excludeId) {
$query->where('id', '!=', $excludeId);
}
return $query->exists();
}
/**
* 테넌트 통계
*/
public function getTenantStats(): array
{
return [
'total' => Tenant::count(),
'active' => Tenant::where('tenant_st_code', 'active')->count(),
'trial' => Tenant::where('tenant_st_code', 'trial')->count(),
'suspended' => Tenant::where('tenant_st_code', 'suspended')->count(),
'expired' => Tenant::where('tenant_st_code', 'expired')->count(),
'trashed' => Tenant::onlyTrashed()->count(),
];
}
}