Files
sam-api/app/Services/Equipment/EquipmentInspectionService.php
김보곤 069d0206a0 feat: [equipment] 설비관리 API 백엔드 구현 (Phase 1)
- 모델 6개: Equipment, EquipmentInspection, EquipmentInspectionDetail, EquipmentInspectionTemplate, EquipmentRepair, EquipmentProcess
- InspectionCycle Enum: 6주기(일/주/월/격월/분기/반기) 날짜 해석
- 서비스 4개: EquipmentService, EquipmentInspectionService, EquipmentRepairService, EquipmentPhotoService
- 컨트롤러 4개: CRUD + 점검 토글/결과 설정/메모/초기화 + 템플릿 관리 + 수리이력 + 사진
- FormRequest 6개: 설비등록/수정, 수리이력, 점검템플릿, 토글, 메모
- 라우트 26개: equipment prefix 하위 RESTful 엔드포인트
- i18n 메시지: message.equipment.*, error.equipment.*
- 마이그레이션: equipments/equipment_repairs options JSON 컬럼 추가
2026-03-12 10:52:30 +09:00

377 lines
13 KiB
PHP

<?php
namespace App\Services\Equipment;
use App\Enums\InspectionCycle;
use App\Models\Equipment\Equipment;
use App\Models\Equipment\EquipmentInspection;
use App\Models\Equipment\EquipmentInspectionDetail;
use App\Models\Equipment\EquipmentInspectionTemplate;
use App\Services\Service;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class EquipmentInspectionService extends Service
{
public function getInspections(string $cycle, string $period, ?string $productionLine = null, ?int $equipmentId = null): array
{
$equipmentQuery = Equipment::active()
->where('status', '!=', 'disposed')
->with(['manager', 'subManager']);
if ($productionLine) {
$equipmentQuery->byLine($productionLine);
}
if ($equipmentId) {
$equipmentQuery->where('id', $equipmentId);
}
$equipments = $equipmentQuery->orderBy('sort_order')->orderBy('name')->get();
$labels = InspectionCycle::columnLabels($cycle, $period);
$userId = $this->apiUserId();
$result = [];
foreach ($equipments as $equipment) {
$templates = EquipmentInspectionTemplate::where('equipment_id', $equipment->id)
->byCycle($cycle)
->active()
->orderBy('sort_order')
->get();
if ($templates->isEmpty()) {
continue;
}
$inspection = EquipmentInspection::where('equipment_id', $equipment->id)
->where('inspection_cycle', $cycle)
->where('year_month', $period)
->first();
$details = [];
if ($inspection) {
$details = EquipmentInspectionDetail::where('inspection_id', $inspection->id)
->get()
->groupBy(function ($d) {
return $d->template_item_id.'_'.$d->check_date->format('Y-m-d');
});
}
$result[] = [
'equipment' => $equipment,
'templates' => $templates,
'inspection' => $inspection,
'details' => $details,
'labels' => $labels,
'can_inspect' => $equipment->canInspect($userId),
];
}
return $result;
}
public function toggleDetail(int $equipmentId, int $templateItemId, string $checkDate, string $cycle = 'daily'): array
{
return DB::transaction(function () use ($equipmentId, $templateItemId, $checkDate, $cycle) {
$equipment = Equipment::find($equipmentId);
if (! $equipment) {
throw new NotFoundHttpException(__('error.equipment.not_found'));
}
$userId = $this->apiUserId();
if (! $equipment->canInspect($userId)) {
throw new AccessDeniedHttpException(__('error.equipment.no_inspect_permission'));
}
$period = InspectionCycle::resolvePeriod($cycle, $checkDate);
if ($cycle === InspectionCycle::DAILY) {
$tenantId = $this->tenantId();
$holidayDates = InspectionCycle::getHolidayDates($cycle, $period, $tenantId);
if (InspectionCycle::isNonWorkingDay($checkDate, $holidayDates)) {
throw new BadRequestHttpException(__('error.equipment.non_working_day'));
}
}
$tenantId = $this->tenantId();
$inspection = EquipmentInspection::firstOrCreate(
[
'tenant_id' => $tenantId,
'equipment_id' => $equipmentId,
'inspection_cycle' => $cycle,
'year_month' => $period,
],
[
'created_by' => $userId,
]
);
$detail = EquipmentInspectionDetail::where('inspection_id', $inspection->id)
->where('template_item_id', $templateItemId)
->where('check_date', $checkDate)
->first();
if ($detail) {
$nextResult = EquipmentInspectionDetail::getNextResult($detail->result);
if ($nextResult === null) {
$detail->delete();
return ['result' => null, 'symbol' => ''];
}
$detail->update(['result' => $nextResult]);
} else {
$detail = EquipmentInspectionDetail::create([
'inspection_id' => $inspection->id,
'template_item_id' => $templateItemId,
'check_date' => $checkDate,
'result' => 'good',
]);
$nextResult = 'good';
}
return [
'result' => $nextResult,
'symbol' => EquipmentInspectionDetail::getResultSymbol($nextResult),
];
});
}
public function setResult(int $equipmentId, int $templateItemId, string $checkDate, string $cycle, ?string $result): array
{
return DB::transaction(function () use ($equipmentId, $templateItemId, $checkDate, $cycle, $result) {
$equipment = Equipment::find($equipmentId);
if (! $equipment) {
throw new NotFoundHttpException(__('error.equipment.not_found'));
}
$userId = $this->apiUserId();
if (! $equipment->canInspect($userId)) {
throw new AccessDeniedHttpException(__('error.equipment.no_inspect_permission'));
}
$period = InspectionCycle::resolvePeriod($cycle, $checkDate);
if ($cycle === InspectionCycle::DAILY) {
$tenantId = $this->tenantId();
$holidayDates = InspectionCycle::getHolidayDates($cycle, $period, $tenantId);
if (InspectionCycle::isNonWorkingDay($checkDate, $holidayDates)) {
throw new BadRequestHttpException(__('error.equipment.non_working_day'));
}
}
$tenantId = $this->tenantId();
$inspection = EquipmentInspection::firstOrCreate(
[
'tenant_id' => $tenantId,
'equipment_id' => $equipmentId,
'inspection_cycle' => $cycle,
'year_month' => $period,
],
[
'created_by' => $userId,
]
);
$detail = EquipmentInspectionDetail::where('inspection_id', $inspection->id)
->where('template_item_id', $templateItemId)
->where('check_date', $checkDate)
->first();
if ($result === null) {
if ($detail) {
$detail->delete();
}
return ['result' => null, 'symbol' => ''];
}
if ($detail) {
$detail->update(['result' => $result]);
} else {
$detail = EquipmentInspectionDetail::create([
'inspection_id' => $inspection->id,
'template_item_id' => $templateItemId,
'check_date' => $checkDate,
'result' => $result,
]);
}
return [
'result' => $result,
'symbol' => EquipmentInspectionDetail::getResultSymbol($result),
];
});
}
public function updateNotes(int $equipmentId, string $yearMonth, array $data, string $cycle = 'daily'): EquipmentInspection
{
return DB::transaction(function () use ($equipmentId, $yearMonth, $data, $cycle) {
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
$inspection = EquipmentInspection::firstOrCreate(
[
'tenant_id' => $tenantId,
'equipment_id' => $equipmentId,
'inspection_cycle' => $cycle,
'year_month' => $yearMonth,
],
[
'created_by' => $userId,
]
);
$inspection->update($data);
return $inspection->fresh();
});
}
public function resetInspection(int $equipmentId, string $cycle, string $period): int
{
return DB::transaction(function () use ($equipmentId, $cycle, $period) {
$equipment = Equipment::find($equipmentId);
if (! $equipment) {
throw new NotFoundHttpException(__('error.equipment.not_found'));
}
$userId = $this->apiUserId();
if (! $equipment->canInspect($userId)) {
throw new AccessDeniedHttpException(__('error.equipment.no_inspect_permission'));
}
$tenantId = $this->tenantId();
$inspection = EquipmentInspection::where('tenant_id', $tenantId)
->where('equipment_id', $equipmentId)
->where('inspection_cycle', $cycle)
->where('year_month', $period)
->first();
if (! $inspection) {
return 0;
}
$deleted = EquipmentInspectionDetail::where('inspection_id', $inspection->id)->delete();
$inspection->update([
'overall_judgment' => null,
'repair_note' => null,
'issue_note' => null,
'inspector_id' => null,
]);
return $deleted;
});
}
public function saveTemplate(int $equipmentId, array $data): EquipmentInspectionTemplate
{
return DB::transaction(function () use ($equipmentId, $data) {
$tenantId = $this->tenantId();
return EquipmentInspectionTemplate::create(array_merge($data, [
'tenant_id' => $tenantId,
'equipment_id' => $equipmentId,
]));
});
}
public function updateTemplate(int $id, array $data): EquipmentInspectionTemplate
{
return DB::transaction(function () use ($id, $data) {
$template = EquipmentInspectionTemplate::find($id);
if (! $template) {
throw new NotFoundHttpException(__('error.equipment.template_not_found'));
}
$template->update($data);
return $template->fresh();
});
}
public function deleteTemplate(int $id): bool
{
return DB::transaction(function () use ($id) {
$template = EquipmentInspectionTemplate::find($id);
if (! $template) {
throw new NotFoundHttpException(__('error.equipment.template_not_found'));
}
return $template->delete();
});
}
public function copyTemplates(int $equipmentId, string $sourceCycle, array $targetCycles): array
{
return DB::transaction(function () use ($equipmentId, $sourceCycle, $targetCycles) {
$tenantId = $this->tenantId();
$sourceTemplates = EquipmentInspectionTemplate::where('equipment_id', $equipmentId)
->byCycle($sourceCycle)
->active()
->orderBy('sort_order')
->get();
if ($sourceTemplates->isEmpty()) {
throw new BadRequestHttpException(__('error.equipment.no_source_templates'));
}
$copiedCount = 0;
$skippedCount = 0;
foreach ($targetCycles as $targetCycle) {
foreach ($sourceTemplates as $template) {
$exists = EquipmentInspectionTemplate::where('equipment_id', $equipmentId)
->where('inspection_cycle', $targetCycle)
->where('item_no', $template->item_no)
->exists();
if ($exists) {
$skippedCount++;
continue;
}
EquipmentInspectionTemplate::create([
'tenant_id' => $tenantId,
'equipment_id' => $equipmentId,
'inspection_cycle' => $targetCycle,
'item_no' => $template->item_no,
'check_point' => $template->check_point,
'check_item' => $template->check_item,
'check_timing' => $template->check_timing,
'check_frequency' => $template->check_frequency,
'check_method' => $template->check_method,
'sort_order' => $template->sort_order,
'is_active' => true,
]);
$copiedCount++;
}
}
return [
'copied' => $copiedCount,
'skipped' => $skippedCount,
'source_count' => $sourceTemplates->count(),
'target_cycles' => $targetCycles,
];
});
}
public function getActiveCycles(int $equipmentId): array
{
return EquipmentInspectionTemplate::where('equipment_id', $equipmentId)
->active()
->distinct()
->pluck('inspection_cycle')
->toArray();
}
}