From 0095fe15e059951bae65229821c48be0ee2b8b6b Mon Sep 17 00:00:00 2001 From: junyong Date: Thu, 4 Jun 2026 19:02:32 +0900 Subject: [PATCH] =?UTF-8?q?Fix:=20=ED=9A=8C=EC=9D=98=EC=82=AD=EC=A0=9C=20A?= =?UTF-8?q?PI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ApplicationCommitsRepository.java | 14 ++++++++++++++ .../repository/DecisionCommitsRepository.java | 14 ++++++++++++++ .../service/MeetingCleanupService.java | 8 ++++++++ .../service/MeetingCommandService.java | 11 ++++++++++- .../external/livekit/LiveKitEgressClient.java | 19 ++++++++++++++++++- 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/whylog/server/domain/decision/repository/ApplicationCommitsRepository.java b/src/main/java/com/whylog/server/domain/decision/repository/ApplicationCommitsRepository.java index b4000ef..fbaf195 100644 --- a/src/main/java/com/whylog/server/domain/decision/repository/ApplicationCommitsRepository.java +++ b/src/main/java/com/whylog/server/domain/decision/repository/ApplicationCommitsRepository.java @@ -36,6 +36,20 @@ SELECT AVG(ac.confidence) """) void deleteByDecisionId(@Param("decisionId") Long decisionId); + @Modifying(clearAutomatically = true, flushAutomatically = true) + @Query(""" + DELETE FROM ApplicationCommits ac + WHERE ac.application.decision.meeting.team.id = :teamId + """) + void deleteByTeamId(@Param("teamId") Long teamId); + + @Modifying(clearAutomatically = true, flushAutomatically = true) + @Query(""" + DELETE FROM ApplicationCommits ac + WHERE ac.application.decision.meeting.id = :meetingId + """) + void deleteByMeetingId(@Param("meetingId") Long meetingId); + @Modifying(clearAutomatically = true, flushAutomatically = true) @Query(""" DELETE FROM ApplicationCommits ac diff --git a/src/main/java/com/whylog/server/domain/decision/repository/DecisionCommitsRepository.java b/src/main/java/com/whylog/server/domain/decision/repository/DecisionCommitsRepository.java index c492e63..475c026 100644 --- a/src/main/java/com/whylog/server/domain/decision/repository/DecisionCommitsRepository.java +++ b/src/main/java/com/whylog/server/domain/decision/repository/DecisionCommitsRepository.java @@ -20,6 +20,20 @@ public interface DecisionCommitsRepository extends JpaRepository meetingIds) { + applicationCommitsRepository.deleteByTeamId(teamId); applicationTimelineRepository.deleteByTeamId(teamId); applicationBaseRepository.deleteByTeamId(teamId); + decisionCommitsRepository.deleteByTeamId(teamId); decisionTimelineRepository.deleteByTeamId(teamId); decisionBaseRepository.deleteByTeamId(teamId); applicationRepository.deleteByTeamId(teamId); @@ -57,8 +63,10 @@ private void deleteChildrenByTeamId(Long teamId, List meetingIds) { } private void deleteChildrenByMeetingId(Long meetingId) { + applicationCommitsRepository.deleteByMeetingId(meetingId); applicationTimelineRepository.deleteByMeetingId(meetingId); applicationBaseRepository.deleteByMeetingId(meetingId); + decisionCommitsRepository.deleteByMeetingId(meetingId); decisionTimelineRepository.deleteByMeetingId(meetingId); decisionBaseRepository.deleteByMeetingId(meetingId); applicationRepository.deleteByMeetingId(meetingId); diff --git a/src/main/java/com/whylog/server/domain/meeting/service/MeetingCommandService.java b/src/main/java/com/whylog/server/domain/meeting/service/MeetingCommandService.java index be2bf1b..5ba3f86 100644 --- a/src/main/java/com/whylog/server/domain/meeting/service/MeetingCommandService.java +++ b/src/main/java/com/whylog/server/domain/meeting/service/MeetingCommandService.java @@ -136,7 +136,7 @@ public MeetingResponse.MeetingDeleteResponseDTO deleteMeeting(Long memberId, Lon meetingMemberRepository.findOwnerMeetingMember(memberId, meetingId, MeetingRole.OWNER) .orElseThrow(() -> new ErrorHandler(MeetingErrorCode.MEETING_NOT_OWNER)); - stopRecording(meeting); + stopRecordingForDelete(meeting); meetingCleanupService.deleteByMeetingId(meetingId); meetingLiveMessageRepository.clear(meetingId); scheduleAfterCommit(() -> meetingSocketRoomService.closeRoom(meetingId)); @@ -171,6 +171,15 @@ private void stopRecording(Meeting meeting) { liveKitEgressClient.stopEgress(egressToken, meeting.getAudioEgressId()); } + private void stopRecordingForDelete(Meeting meeting) { + try { + stopRecording(meeting); + } catch (Exception exception) { + log.warn("Failed to stop meeting recording before delete: meetingId={}, egressId={}", + meeting.getId(), meeting.getAudioEgressId(), exception); + } + } + private MeetingResponse.MeetingEndResponseDTO finishMeeting(Meeting meeting, boolean broadcastEnded) { LocalDateTime endDateTime = meeting.endMeeting(); meetingRepository.save(meeting); diff --git a/src/main/java/com/whylog/server/global/external/livekit/LiveKitEgressClient.java b/src/main/java/com/whylog/server/global/external/livekit/LiveKitEgressClient.java index 66cdccc..32c675f 100644 --- a/src/main/java/com/whylog/server/global/external/livekit/LiveKitEgressClient.java +++ b/src/main/java/com/whylog/server/global/external/livekit/LiveKitEgressClient.java @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; @@ -70,7 +71,23 @@ public void createRoom(String roomCreateToken, String roomName) { public void stopEgress(String egressToken, String egressId) { Map request = Map.of("egress_id", egressId); - post("/twirp/livekit.Egress/StopEgress", egressToken, request); + try { + post("/twirp/livekit.Egress/StopEgress", egressToken, request); + } catch (HttpClientErrorException e) { + if (isAlreadyCompletedEgress(e)) { + log.info("LiveKit egress already completed: egressId={}", egressId); + return; + } + + throw e; + } + } + + private boolean isAlreadyCompletedEgress(HttpClientErrorException e) { + String responseBody = e.getResponseBodyAsString(); + return e.getStatusCode().value() == HttpStatus.PRECONDITION_FAILED.value() + && responseBody.contains("EGRESS_COMPLETE") + && responseBody.contains("cannot be stopped"); } public void removeParticipant(String roomAdminToken, String roomName, String identity) {