Files
sam-api/app/Services/LaborService.php
kent f59dd1b9fb feat(labor): 노임관리 API 구현
- Labor 모델 (BelongsToTenant, SoftDeletes)
- LaborController 7개 엔드포인트
- LaborService 비즈니스 로직
- FormRequest 4개 (Index/Store/Update/BulkDelete)
- 마이그레이션 및 라우트 등록

API: GET/POST /labor, GET/PUT/DELETE /labor/{id}, DELETE /labor/bulk, GET /labor/stats

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-11 23:29:32 +09:00

197 lines
4.9 KiB
PHP

<?php
namespace App\Services;
use App\Models\Labor;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class LaborService extends Service
{
/**
* 노임 목록 조회
*/
public function index(array $params): LengthAwarePaginator
{
$tenantId = $this->tenantId();
$size = (int) ($params['size'] ?? 20);
$search = trim((string) ($params['search'] ?? ''));
$category = $params['category'] ?? null;
$status = $params['status'] ?? null;
$startDate = $params['start_date'] ?? null;
$endDate = $params['end_date'] ?? null;
$sortOrder = $params['sort_order'] ?? 'latest';
$query = Labor::query()
->where('tenant_id', $tenantId);
// 검색어 필터
if ($search !== '') {
$query->search($search);
}
// 구분 필터
if ($category && $category !== 'all') {
$query->byCategory($category);
}
// 상태 필터
if ($status && $status !== 'all') {
$query->byStatus($status);
}
// 날짜 필터
if ($startDate) {
$query->whereDate('created_at', '>=', $startDate);
}
if ($endDate) {
$query->whereDate('created_at', '<=', $endDate);
}
// 정렬
if ($sortOrder === 'oldest') {
$query->orderBy('created_at', 'asc');
} else {
$query->orderByDesc('created_at');
}
return $query->paginate($size);
}
/**
* 노임 통계 조회
*/
public function stats(): array
{
$tenantId = $this->tenantId();
$baseQuery = Labor::where('tenant_id', $tenantId);
$total = (clone $baseQuery)->count();
$active = (clone $baseQuery)->byStatus('사용')->count();
return [
'total' => $total,
'active' => $active,
];
}
/**
* 노임 상세 조회
*/
public function show(int $id): Labor
{
$tenantId = $this->tenantId();
$labor = Labor::query()
->where('tenant_id', $tenantId)
->find($id);
if (! $labor) {
throw new NotFoundHttpException(__('error.not_found'));
}
return $labor;
}
/**
* 노임 등록
*/
public function store(array $data): Labor
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
return DB::transaction(function () use ($data, $tenantId, $userId) {
$payload = array_merge($data, [
'tenant_id' => $tenantId,
'status' => $data['status'] ?? '사용',
'is_active' => true,
'created_by' => $userId,
]);
return Labor::create($payload);
});
}
/**
* 노임 수정
*/
public function update(int $id, array $data): Labor
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
return DB::transaction(function () use ($id, $data, $tenantId, $userId) {
$labor = Labor::query()
->where('tenant_id', $tenantId)
->find($id);
if (! $labor) {
throw new NotFoundHttpException(__('error.not_found'));
}
$data['updated_by'] = $userId;
$labor->update($data);
$labor->refresh();
return $labor;
});
}
/**
* 노임 삭제 (soft delete)
*/
public function destroy(int $id): void
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
DB::transaction(function () use ($id, $tenantId, $userId) {
$labor = Labor::query()
->where('tenant_id', $tenantId)
->find($id);
if (! $labor) {
throw new NotFoundHttpException(__('error.not_found'));
}
$labor->deleted_by = $userId;
$labor->save();
$labor->delete();
});
}
/**
* 노임 일괄 삭제 (soft delete)
*/
public function bulkDestroy(array $ids): int
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
return DB::transaction(function () use ($ids, $tenantId, $userId) {
$labors = Labor::query()
->where('tenant_id', $tenantId)
->whereIn('id', $ids)
->get();
if ($labors->isEmpty()) {
throw new NotFoundHttpException(__('error.not_found'));
}
$deletedCount = 0;
foreach ($labors as $labor) {
$labor->deleted_by = $userId;
$labor->save();
$labor->delete();
$deletedCount++;
}
return $deletedCount;
});
}
}