diff --git a/app/Http/Controllers/Api/Admin/MeetingLogController.php b/app/Http/Controllers/Api/Admin/MeetingLogController.php index a1c44cc2..c8c6550c 100644 --- a/app/Http/Controllers/Api/Admin/MeetingLogController.php +++ b/app/Http/Controllers/Api/Admin/MeetingLogController.php @@ -214,4 +214,37 @@ public function summary(int $id): View|JsonResponse ], ]); } + + /** + * 오디오 파일 업로드 및 처리 (회의록 AI 요약) + */ + public function uploadFile(Request $request): JsonResponse + { + $validated = $request->validate([ + 'audio_file' => 'required|file|mimes:webm,wav,mp3,ogg,m4a,mp4|max:102400', + 'title' => 'nullable|string|max:200', + ]); + + $meeting = $this->meetingLogService->create([ + 'title' => $validated['title'] ?? '업로드된 회의록', + ]); + + $result = $this->meetingLogService->processUploadedFile( + $meeting, + $request->file('audio_file') + ); + + if (! $result['ok']) { + return response()->json([ + 'success' => false, + 'message' => $result['error'] ?? '처리 중 오류가 발생했습니다.', + ], 500); + } + + return response()->json([ + 'success' => true, + 'message' => '회의록이 생성되었습니다.', + 'data' => $result['meeting'], + ]); + } } diff --git a/app/Services/MeetingLogService.php b/app/Services/MeetingLogService.php index 5459c252..ed41011d 100644 --- a/app/Services/MeetingLogService.php +++ b/app/Services/MeetingLogService.php @@ -246,4 +246,82 @@ public function cleanupExpiredFiles(): int return $count; } + + /** + * 업로드된 오디오 파일 처리 (회의록 AI 요약) + */ + public function processUploadedFile(MeetingLog $meeting, \Illuminate\Http\UploadedFile $file): array + { + try { + $meeting->update(['status' => MeetingLog::STATUS_PROCESSING]); + + // 임시 저장 + $tempPath = $file->store('temp', 'local'); + $fullPath = storage_path('app/'.$tempPath); + + // 파일 크기로 대략적인 재생 시간 추정 (12KB/초 기준) + $fileSize = $file->getSize(); + $estimatedDuration = max(1, intval($fileSize / 12000)); + $meeting->update(['duration_seconds' => $estimatedDuration]); + + // 1. GCS에 오디오 업로드 + $extension = $file->getClientOriginalExtension() ?: 'webm'; + $objectName = sprintf( + 'meetings/%d/%d/%s.%s', + $meeting->tenant_id, + $meeting->id, + now()->format('YmdHis'), + $extension + ); + + $gcsUri = $this->googleCloudService->uploadToStorage($fullPath, $objectName); + + if (! $gcsUri) { + @unlink($fullPath); + throw new \Exception('오디오 파일 업로드 실패'); + } + + $meeting->update([ + 'audio_file_path' => $objectName, + 'audio_gcs_uri' => $gcsUri, + ]); + + // 2. Speech-to-Text 변환 + $transcript = $this->googleCloudService->speechToText($gcsUri); + + // 임시 파일 삭제 + @unlink($fullPath); + + if (! $transcript) { + throw new \Exception('음성 인식 실패'); + } + + $meeting->update(['transcript_text' => $transcript]); + + // 3. AI 요약 생성 + $summary = $this->generateSummary($transcript); + + $meeting->update([ + 'summary_text' => $summary, + 'status' => MeetingLog::STATUS_COMPLETED, + ]); + + return [ + 'ok' => true, + 'meeting' => $meeting->fresh(), + ]; + } catch (\Exception $e) { + Log::error('MeetingLog 파일 처리 실패', [ + 'meeting_id' => $meeting->id, + 'error' => $e->getMessage(), + ]); + + $meeting->update(['status' => MeetingLog::STATUS_FAILED]); + + return [ + 'ok' => false, + 'error' => $e->getMessage(), + ]; + } + } } diff --git a/resources/views/lab/ai/meeting-summary.blade.php b/resources/views/lab/ai/meeting-summary.blade.php index 2730c7b7..1d0a43ca 100644 --- a/resources/views/lab/ai/meeting-summary.blade.php +++ b/resources/views/lab/ai/meeting-summary.blade.php @@ -4,59 +4,429 @@ @push('styles') @endpush @section('content') -
-
-
- - - -

회의록 AI 요약

-

- 회의 녹음 파일을 업로드하면 AI가 자동으로 - 전사하고 핵심 내용을 구조화된 회의록으로 정리합니다. -

-
AI/Automation
-
+
+ {{-- 헤더 --}} +
+

회의록 AI 요약

+

회의 녹음 파일을 업로드하면 AI가 자동으로 회의록을 작성합니다

+
-
-
-

- - - - - 예정 기능 -

-
-
-

음성 처리

-
    -
  • • 다양한 오디오 포맷 지원
  • -
  • • 화자 분리 (Speaker Diarization)
  • -
  • • 한국어 최적화
  • -
-
-
-

회의록 생성

-
    -
  • • 안건별 정리
  • -
  • • 결정사항/액션아이템 추출
  • -
  • • PDF/Word 내보내기
  • -
-
+ {{-- 업로드 섹션 --}} +
+
+ @csrf + {{-- 제목 입력 --}} +
+ + +
+ + {{-- 파일 드롭존 --}} +
+ + + +

파일을 드래그하거나 클릭하여 업로드

+

지원 형식: WebM, WAV, MP3, OGG, M4A, MP4 (최대 100MB)

+ +
+ + {{-- 선택된 파일 정보 --}} + + + {{-- 업로드 버튼 --}} + +
+
+ + {{-- 처리 중 상태 --}} + + + {{-- 결과 섹션 --}} + + + {{-- 최근 회의록 목록 --}} +
+

+ + + + 최근 회의록 +

+ +
+
+
@endsection + +@push('scripts') + + +{{-- Markdown 파서 --}} + +@endpush diff --git a/routes/api.php b/routes/api.php index 32aebdff..4e5b1d60 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,10 +1,11 @@ name('store'); + // 파일 업로드 (회의록 AI 요약) + Route::post('/upload', [MeetingLogController::class, 'uploadFile'])->name('upload'); + // 상세 조회 Route::get('/{id}', [MeetingLogController::class, 'show'])->name('show');