Files
sam-api/app/Services/CompanyService.php
hskwon 7781253491 feat: Phase 8.3 회사 추가 API 구현
- 사업자등록번호 유효성 검사 API (바로빌 연동)
- 회사 추가 신청/승인/반려 워크플로우
- 신청 승인 시 테넌트 자동 생성 및 사용자 연결
- 관리자용 신청 목록/상세 조회
- 사용자용 내 신청 목록 조회
- Swagger 문서 및 i18n 메시지 추가
2025-12-22 15:30:38 +09:00

259 lines
8.7 KiB
PHP

<?php
namespace App\Services;
use App\Models\CompanyRequest;
use App\Models\Members\UserTenant;
use App\Models\Tenants\Tenant;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class CompanyService extends Service
{
public function __construct(
private readonly BarobillService $barobillService,
private readonly TenantService $tenantService
) {}
// =========================================================================
// 사업자등록번호 검증
// =========================================================================
/**
* 사업자등록번호 유효성 검사
*/
public function checkBusinessNumber(string $businessNumber): array
{
// 바로빌 서비스를 통한 사업자등록번호 검증
$result = $this->barobillService->checkBusinessNumber($businessNumber);
// 이미 등록된 회사인지 확인
$normalizedNumber = preg_replace('/[^0-9]/', '', $businessNumber);
$existingTenant = Tenant::query()
->where('business_num', $normalizedNumber)
->orWhere('business_num', $this->formatBusinessNumber($normalizedNumber))
->first();
if ($existingTenant) {
$result['already_exists'] = true;
$result['existing_company_name'] = $existingTenant->company_name;
} else {
$result['already_exists'] = false;
$result['existing_company_name'] = null;
}
return $result;
}
/**
* 사업자등록번호 포맷 (하이픈 추가)
*/
private function formatBusinessNumber(string $number): string
{
$number = preg_replace('/[^0-9]/', '', $number);
if (strlen($number) === 10) {
return substr($number, 0, 3).'-'.substr($number, 3, 2).'-'.substr($number, 5, 5);
}
return $number;
}
// =========================================================================
// 회사 추가 신청
// =========================================================================
/**
* 회사 추가 신청 생성
*/
public function createRequest(array $data): CompanyRequest
{
$userId = $this->apiUserId();
$businessNumber = preg_replace('/[^0-9]/', '', $data['business_number']);
// 중복 신청 확인 (대기중인 신청이 있는지)
$existingRequest = CompanyRequest::query()
->where('user_id', $userId)
->where('business_number', $businessNumber)
->where('status', CompanyRequest::STATUS_PENDING)
->first();
if ($existingRequest) {
throw new BadRequestHttpException(__('error.company.request_already_exists'));
}
// 이미 등록된 회사인지 확인
$existingTenant = Tenant::query()
->where('business_num', $businessNumber)
->orWhere('business_num', $this->formatBusinessNumber($businessNumber))
->first();
if ($existingTenant) {
// 이미 존재하는 회사에 합류 신청
// 해당 회사의 관리자에게 알림을 보내거나 별도 처리 가능
throw new BadRequestHttpException(__('error.company.already_registered'));
}
// 사업자등록번호 유효성 검사
$validationResult = $this->barobillService->checkBusinessNumber($businessNumber);
return CompanyRequest::create([
'user_id' => $userId,
'business_number' => $businessNumber,
'company_name' => $data['company_name'],
'ceo_name' => $data['ceo_name'] ?? $validationResult['ceo_name'] ?? null,
'address' => $data['address'] ?? null,
'phone' => $data['phone'] ?? null,
'email' => $data['email'] ?? null,
'message' => $data['message'] ?? null,
'status' => CompanyRequest::STATUS_PENDING,
'barobill_response' => $validationResult,
]);
}
/**
* 회사 추가 신청 목록 (관리자용)
*/
public function getRequests(array $params): LengthAwarePaginator
{
$query = CompanyRequest::query()
->with(['user:id,name,email', 'approver:id,name,email', 'createdTenant:id,company_name,code']);
// 상태 필터
if (! empty($params['status'])) {
$query->ofStatus($params['status']);
}
// 검색 (사업자번호, 회사명, 신청자명)
if (! empty($params['search'])) {
$search = $params['search'];
$query->where(function ($q) use ($search) {
$q->where('business_number', 'like', "%{$search}%")
->orWhere('company_name', 'like', "%{$search}%")
->orWhereHas('user', function ($uq) use ($search) {
$uq->where('name', 'like', "%{$search}%");
});
});
}
// 날짜 범위 필터
if (! empty($params['start_date'])) {
$query->whereDate('created_at', '>=', $params['start_date']);
}
if (! empty($params['end_date'])) {
$query->whereDate('created_at', '<=', $params['end_date']);
}
// 정렬
$sortBy = $params['sort_by'] ?? 'created_at';
$sortDir = $params['sort_dir'] ?? 'desc';
$query->orderBy($sortBy, $sortDir);
$perPage = $params['per_page'] ?? 20;
return $query->paginate($perPage);
}
/**
* 회사 추가 신청 상세
*/
public function getRequest(int $id): CompanyRequest
{
return CompanyRequest::query()
->with(['user:id,name,email', 'approver:id,name,email', 'createdTenant:id,company_name,code'])
->findOrFail($id);
}
// =========================================================================
// 신청 처리 (관리자)
// =========================================================================
/**
* 회사 추가 신청 승인
*/
public function approveRequest(int $id): CompanyRequest
{
$userId = $this->apiUserId();
$request = CompanyRequest::findOrFail($id);
if (! $request->is_pending) {
throw new BadRequestHttpException(__('error.company.request_not_pending'));
}
return DB::transaction(function () use ($request, $userId) {
// 테넌트 생성
$tenantCode = $this->tenantService->generateTenantCode($request->company_name);
$tenant = Tenant::create([
'code' => $tenantCode,
'company_name' => $request->company_name,
'business_num' => $request->business_number,
'ceo_name' => $request->ceo_name,
'address' => $request->address,
'phone' => $request->phone,
'email' => $request->email,
]);
// 신청자를 테넌트에 연결
UserTenant::create([
'user_id' => $request->user_id,
'tenant_id' => $tenant->id,
'is_active' => true,
'is_default' => true,
'joined_at' => now(),
]);
// 신청 승인 처리
$request->approve($userId, $tenant->id);
return $request->fresh(['user', 'approver', 'createdTenant']);
});
}
/**
* 회사 추가 신청 반려
*/
public function rejectRequest(int $id, ?string $reason = null): CompanyRequest
{
$userId = $this->apiUserId();
$request = CompanyRequest::findOrFail($id);
if (! $request->is_pending) {
throw new BadRequestHttpException(__('error.company.request_not_pending'));
}
$request->reject($userId, $reason);
return $request->fresh(['user', 'approver']);
}
// =========================================================================
// 내 신청 목록 (사용자용)
// =========================================================================
/**
* 내 회사 추가 신청 목록
*/
public function getMyRequests(array $params): LengthAwarePaginator
{
$userId = $this->apiUserId();
$query = CompanyRequest::query()
->where('user_id', $userId)
->with(['approver:id,name,email', 'createdTenant:id,company_name,code']);
// 상태 필터
if (! empty($params['status'])) {
$query->ofStatus($params['status']);
}
// 정렬
$sortBy = $params['sort_by'] ?? 'created_at';
$sortDir = $params['sort_dir'] ?? 'desc';
$query->orderBy($sortBy, $sortDir);
$perPage = $params['per_page'] ?? 20;
return $query->paginate($perPage);
}
}