feat:Tiro AI 스타일 회의록 작성 기능 추가 (음성인식+화자분리+AI요약)

- MeetingMinute/MeetingMinuteSegment 모델
- MeetingMinuteService (CRUD, GCS 업로드, Gemini AI 요약)
- MeetingMinuteController (11개 엔드포인트)
- React SPA Blade 뷰 (대화기록/스크립트 탭, AI 요약 사이드패널)
- Web Speech API 실시간 STT + 수동 화자 전환 + MediaRecorder 녹음

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-02-10 09:50:23 +09:00
parent 16309c5f61
commit 418aa0c469
6 changed files with 1703 additions and 0 deletions

View File

@@ -32,6 +32,7 @@
use App\Http\Controllers\RolePermissionController;
use App\Http\Controllers\Sales\SalesProductController;
use App\Http\Controllers\Juil\ConstructionSitePhotoController;
use App\Http\Controllers\Juil\MeetingMinuteController;
use App\Http\Controllers\Juil\PlanningController;
use App\Http\Controllers\Stats\StatDashboardController;
use App\Http\Controllers\System\AiConfigController;
@@ -1327,4 +1328,19 @@
Route::get('/{id}/download/{type}', [ConstructionSitePhotoController::class, 'downloadPhoto'])->name('download');
Route::post('/log-stt-usage', [ConstructionSitePhotoController::class, 'logSttUsage'])->name('log-stt-usage');
});
// 회의록 작성
Route::prefix('meeting-minutes')->name('meeting-minutes.')->group(function () {
Route::get('/', [MeetingMinuteController::class, 'index'])->name('index');
Route::get('/list', [MeetingMinuteController::class, 'list'])->name('list');
Route::post('/', [MeetingMinuteController::class, 'store'])->name('store');
Route::post('/log-stt-usage', [MeetingMinuteController::class, 'logSttUsage'])->name('log-stt-usage');
Route::get('/{id}', [MeetingMinuteController::class, 'show'])->name('show');
Route::put('/{id}', [MeetingMinuteController::class, 'update'])->name('update');
Route::delete('/{id}', [MeetingMinuteController::class, 'destroy'])->name('destroy');
Route::post('/{id}/segments', [MeetingMinuteController::class, 'saveSegments'])->name('save-segments');
Route::post('/{id}/upload-audio', [MeetingMinuteController::class, 'uploadAudio'])->name('upload-audio');
Route::post('/{id}/summarize', [MeetingMinuteController::class, 'summarize'])->name('summarize');
Route::get('/{id}/download-audio', [MeetingMinuteController::class, 'downloadAudio'])->name('download-audio');
});
});