@startuml !theme mono title 음성-텍스트 변환 내부 시퀀스 (UFR-STT-020) participant "API Gateway<>" as Gateway participant "SttController" as Controller participant "SttService" as Service participant "TranscriptionEngine" as Engine participant "Azure Speech<>" as Speech participant "SttRepository" as Repository database "PostgreSQL<>" as DB queue "Event Hub<>" as EventHub Gateway -> Controller: POST /api/v1/stt/transcribe\n{sessionId, audioFile} activate Controller Controller -> Service: transcribeAudio(sessionId, audioFile) activate Service Service -> Repository: findSessionById(sessionId) activate Repository Repository -> DB: SELECT * FROM stt_sessions\nWHERE session_id = ? DB --> Repository: session data Repository --> Service: SttSession entity deactivate Repository alt 실시간 변환 모드 Service -> Engine: streamingTranscribe(audioFile) activate Engine Engine -> Speech: createRecognizer()\nsetContinuousRecognition() note right Azure Speech 설정: - Mode: Continuous - Language: ko-KR - Enable diarization - Profanity filter end note Speech --> Engine: recognizer instance loop 오디오 청크 처리 Engine -> Speech: recognizeOnceAsync(audioChunk) Speech --> Engine: recognition result note right 결과 포함: - Text - Confidence - Duration - Speaker ID end note Engine -> Engine: validateConfidence(result) note right 신뢰도 검증: - Threshold: 0.7 - Low confidence 처리 end note Engine --> Service: transcription segment Service -> Repository: saveSttSegment(segment) activate Repository Repository -> DB: INSERT INTO stt_segments\n(session_id, text, confidence, timestamp) DB --> Repository: saved Repository --> Service: segment saved deactivate Repository Service -> EventHub: publish(TranscriptionSegmentEvent) note right Event: - sessionId - segmentId - text - timestamp end note end Engine --> Service: streaming complete deactivate Engine else 배치 변환 모드 Service -> Engine: batchTranscribe(audioFile) activate Engine Engine -> Speech: batchTranscriptionAsync(audioUrl) note right 배치 처리: - 전체 파일 업로드 - 백그라운드 처리 - Callback URL 제공 end note Speech --> Engine: transcription job ID Engine --> Service: job submitted deactivate Engine Service -> Repository: updateSessionStatus(sessionId, "PROCESSING") activate Repository Repository -> DB: UPDATE stt_sessions\nSET status = 'PROCESSING' DB --> Repository: updated Repository --> Service: updated deactivate Repository end Service -> Repository: aggregateTranscription(sessionId) activate Repository Repository -> DB: SELECT text, timestamp\nFROM stt_segments\nWHERE session_id = ?\nORDER BY timestamp DB --> Repository: segments Repository --> Service: ordered segments deactivate Repository Service -> Service: mergeSegments(segments) note right 세그먼트 병합: - 화자별 그룹화 - 시간 순서 정렬 - 문장 경계 보정 end note Service -> Repository: saveTranscription(fullText) activate Repository Repository -> DB: UPDATE stt_sessions\nSET full_text = ?,\nstatus = 'COMPLETED' DB --> Repository: saved Repository --> Service: updated session deactivate Repository Service -> EventHub: publish(TranscriptionCompletedEvent) note right Event: - sessionId - meetingId - fullText - completedAt end note Service --> Controller: TranscriptionResponse\n{sessionId, text, segments} deactivate Service Controller --> Gateway: 200 OK\n{transcription, metadata} deactivate Controller @enduml