service = $service; } // ─── 화면 라우트 ─── public function dashboard(Request $request): View|Response { if ($request->header('HX-Request')) { return response('', 200)->header('HX-Redirect', route('esign-verification.dashboard')); } return view('esign.verification.dashboard'); } public function templates(Request $request): View|Response { if ($request->header('HX-Request')) { return response('', 200)->header('HX-Redirect', route('esign-verification.templates')); } return view('esign.verification.templates'); } public function demo(Request $request): View|Response { if ($request->header('HX-Request')) { return response('', 200)->header('HX-Redirect', route('esign-verification.demo')); } return view('esign.verification.demo'); } // ─── API: 확인 템플릿 CRUD ─── public function indexTemplates(Request $request): JsonResponse { $tenantId = Auth::user()->tenant_id; $templates = EsignVerificationTemplate::where('tenant_id', $tenantId) ->where('is_active', true) ->orderBy('created_at', 'desc') ->get(); return response()->json(['success' => true, 'data' => $templates]); } public function storeTemplate(Request $request): JsonResponse { $request->validate([ 'name' => 'required|string|max:100', 'category' => 'nullable|string|max:50', 'steps' => 'required|array|min:1', 'steps.*.order' => 'required|integer|min:1', 'steps.*.text' => 'required|string|max:200', 'steps.*.threshold' => 'required|numeric|min:50|max:100', 'pass_threshold' => 'nullable|numeric|min:50|max:100', 'max_attempts' => 'nullable|integer|min:1|max:20', ]); $tenantId = Auth::user()->tenant_id; $template = EsignVerificationTemplate::create([ 'tenant_id' => $tenantId, 'name' => $request->input('name'), 'category' => $request->input('category'), 'steps' => $request->input('steps'), 'pass_threshold' => $request->input('pass_threshold', 80.00), 'max_attempts' => $request->input('max_attempts', 5), 'is_active' => true, 'created_by' => Auth::id(), ]); return response()->json(['success' => true, 'data' => $template], 201); } public function showTemplate(int $id): JsonResponse { $tenantId = Auth::user()->tenant_id; $template = EsignVerificationTemplate::where('tenant_id', $tenantId) ->findOrFail($id); return response()->json(['success' => true, 'data' => $template]); } public function updateTemplate(Request $request, int $id): JsonResponse { $request->validate([ 'name' => 'required|string|max:100', 'category' => 'nullable|string|max:50', 'steps' => 'required|array|min:1', 'steps.*.order' => 'required|integer|min:1', 'steps.*.text' => 'required|string|max:200', 'steps.*.threshold' => 'required|numeric|min:50|max:100', 'pass_threshold' => 'nullable|numeric|min:50|max:100', 'max_attempts' => 'nullable|integer|min:1|max:20', ]); $tenantId = Auth::user()->tenant_id; $template = EsignVerificationTemplate::where('tenant_id', $tenantId) ->findOrFail($id); $template->update([ 'name' => $request->input('name'), 'category' => $request->input('category'), 'steps' => $request->input('steps'), 'pass_threshold' => $request->input('pass_threshold', 80.00), 'max_attempts' => $request->input('max_attempts', 5), ]); return response()->json(['success' => true, 'data' => $template]); } public function destroyTemplate(int $id): JsonResponse { $tenantId = Auth::user()->tenant_id; $template = EsignVerificationTemplate::where('tenant_id', $tenantId) ->findOrFail($id); $template->update(['is_active' => false]); return response()->json(['success' => true, 'message' => '템플릿이 비활성화되었습니다.']); } // ─── API: 데모 (인식 테스트) ─── public function demoRecognize(Request $request): JsonResponse { $request->validate([ 'image' => 'required|string', 'expected_text' => 'required|string|max:200', ]); $result = $this->service->demo( $request->input('image'), $request->input('expected_text'), ); return response()->json(['success' => true, 'data' => $result]); } // ─── API: 검증 이력 ─── public function verificationHistory(Request $request): JsonResponse { $tenantId = Auth::user()->tenant_id; $query = EsignHandwritingVerification::where('tenant_id', $tenantId) ->orderBy('created_at', 'desc'); if ($request->filled('contract_id')) { $query->where('contract_id', $request->input('contract_id')); } $data = $query->limit(100)->get(); return response()->json(['success' => true, 'data' => $data]); } // ─── API: 통계 ─── public function stats(): JsonResponse { $tenantId = Auth::user()->tenant_id; $total = EsignHandwritingVerification::where('tenant_id', $tenantId)->count(); $passed = EsignHandwritingVerification::where('tenant_id', $tenantId)->where('is_passed', true)->count(); $failed = $total - $passed; $avgScore = EsignHandwritingVerification::where('tenant_id', $tenantId) ->whereNotNull('similarity_score') ->avg('similarity_score') ?? 0; $avgAttempts = EsignHandwritingVerification::where('tenant_id', $tenantId) ->where('is_passed', true) ->avg('attempt_number') ?? 0; $templateCount = EsignVerificationTemplate::where('tenant_id', $tenantId) ->where('is_active', true) ->count(); return response()->json([ 'success' => true, 'data' => [ 'total_verifications' => $total, 'passed' => $passed, 'failed' => $failed, 'pass_rate' => $total > 0 ? round(($passed / $total) * 100, 1) : 0, 'avg_score' => round($avgScore, 1), 'avg_attempts' => round($avgAttempts, 1), 'template_count' => $templateCount, ], ]); } }