From c5d5b5d076a8fccac9015c6eea3782558484730e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Fri, 6 Mar 2026 20:59:17 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[=EB=AC=B8=EC=84=9C=EC=8A=A4=EB=83=85?= =?UTF-8?q?=EC=83=B7]=20Lazy=20Snapshot=20API=20-=20snapshot=20=EC=97=94?= =?UTF-8?q?=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20+=20resolve=EC=97=90=20s?= =?UTF-8?q?napshot=5Fdocument=5Fid=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PATCH /documents/{id}/snapshot: canEdit 체크 없이 rendered_html만 업데이트 - DocumentService::patchSnapshot() 메서드 추가 - WorkOrderService::resolveInspectionDocument()에 snapshot_document_id 반환 (상태 무관, rendered_html NULL인 문서) Co-Authored-By: Claude Opus 4.6 --- .../Api/V1/Documents/DocumentController.php | 16 ++++++++++++++ app/Services/DocumentService.php | 22 +++++++++++++++++++ app/Services/WorkOrderService.php | 15 +++++++++++++ routes/api/v1/documents.php | 1 + 4 files changed, 54 insertions(+) diff --git a/app/Http/Controllers/Api/V1/Documents/DocumentController.php b/app/Http/Controllers/Api/V1/Documents/DocumentController.php index 729ed46..d63bdf9 100644 --- a/app/Http/Controllers/Api/V1/Documents/DocumentController.php +++ b/app/Http/Controllers/Api/V1/Documents/DocumentController.php @@ -74,6 +74,22 @@ public function destroy(int $id): JsonResponse }, __('message.deleted')); } + /** + * rendered_html 스냅샷 저장 (Lazy Snapshot) + * PATCH /v1/documents/{id}/snapshot + */ + public function patchSnapshot(int $id, UpdateRequest $request): JsonResponse + { + return ApiResponse::handle(function () use ($id, $request) { + $renderedHtml = $request->validated()['rendered_html'] ?? null; + if (! $renderedHtml) { + throw new \Symfony\Component\HttpKernel\Exception\BadRequestHttpException('rendered_html is required'); + } + + return $this->service->patchSnapshot($id, $renderedHtml); + }, __('message.updated')); + } + // ========================================================================= // FQC 일괄생성 (제품검사) // ========================================================================= diff --git a/app/Services/DocumentService.php b/app/Services/DocumentService.php index 2fef635..82783b6 100644 --- a/app/Services/DocumentService.php +++ b/app/Services/DocumentService.php @@ -718,6 +718,28 @@ public function fqcStatus(int $orderId, int $templateId): array ]; } + // ========================================================================= + // Snapshot (Lazy Snapshot) + // ========================================================================= + + /** + * rendered_html만 업데이트 (상태 무관, canEdit 체크 없음) + * Lazy Snapshot: 조회 시 rendered_html이 없으면 프론트에서 캡처 후 저장 + */ + public function patchSnapshot(int $id, string $renderedHtml): Document + { + $tenantId = $this->tenantId(); + + $document = Document::query() + ->where('tenant_id', $tenantId) + ->findOrFail($id); + + $document->rendered_html = $renderedHtml; + $document->save(); + + return $document; + } + // ========================================================================= // Resolve/Upsert (React 연동용) // ========================================================================= diff --git a/app/Services/WorkOrderService.php b/app/Services/WorkOrderService.php index ee5d342..ba39c09 100644 --- a/app/Services/WorkOrderService.php +++ b/app/Services/WorkOrderService.php @@ -2431,11 +2431,26 @@ public function resolveInspectionDocument(int $workOrderId, array $params = []): ->latest() ->first(); + // Lazy Snapshot 대상: rendered_html이 없는 문서 (상태 무관) + $snapshotDocumentId = null; + $snapshotCandidate = Document::query() + ->where('tenant_id', $tenantId) + ->where('template_id', $templateId) + ->where('linkable_type', 'work_order') + ->where('linkable_id', $workOrderId) + ->whereNull('rendered_html') + ->latest() + ->value('id'); + if ($snapshotCandidate) { + $snapshotDocumentId = $snapshotCandidate; + } + return [ 'work_order_id' => $workOrderId, 'template_id' => $templateId, 'template' => $formattedTemplate, 'existing_document' => $existingDocument, + 'snapshot_document_id' => $snapshotDocumentId, 'work_order_info' => $this->buildWorkOrderInfo($workOrder), ]; } diff --git a/routes/api/v1/documents.php b/routes/api/v1/documents.php index ae56c70..2044ab4 100644 --- a/routes/api/v1/documents.php +++ b/routes/api/v1/documents.php @@ -33,6 +33,7 @@ Route::get('/{id}', [DocumentController::class, 'show'])->whereNumber('id')->name('v1.documents.show'); Route::post('/', [DocumentController::class, 'store'])->name('v1.documents.store'); Route::patch('/{id}', [DocumentController::class, 'update'])->whereNumber('id')->name('v1.documents.update'); + Route::patch('/{id}/snapshot', [DocumentController::class, 'patchSnapshot'])->whereNumber('id')->name('v1.documents.snapshot'); Route::delete('/{id}', [DocumentController::class, 'destroy'])->whereNumber('id')->name('v1.documents.destroy'); // 결재 워크플로우