Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.whylog.server.domain.decision.repository;

import com.whylog.server.domain.decision.entity.Application;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface ApplicationRepository extends JpaRepository<Application, Long> {

@Modifying
@Query("DELETE FROM Application a WHERE a.decision.meeting.team.id = :teamId")
void deleteByTeamId(@Param("teamId") Long teamId);

@Modifying
@Query("DELETE FROM Application a WHERE a.decision.meeting.id = :meetingId")
void deleteByMeetingId(@Param("meetingId") Long meetingId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.whylog.server.domain.decision.repository;

import com.whylog.server.domain.decision.entity.Decision;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface DecisionRepository extends JpaRepository<Decision, Long> {

@Modifying
@Query("DELETE FROM Application a WHERE a.decision.meeting.team.id = :teamId")
void deleteApplicationsByTeamId(@Param("teamId") Long teamId);

@Modifying
@Query("DELETE FROM Application a WHERE a.decision.meeting.id = :meetingId")
void deleteApplicationsByMeetingId(@Param("meetingId") Long meetingId);

@Modifying
@Query("DELETE FROM Decision d WHERE d.meeting.team.id = :teamId")
void deleteByTeamId(@Param("teamId") Long teamId);

@Modifying
@Query("DELETE FROM Decision d WHERE d.meeting.id = :meetingId")
void deleteByMeetingId(@Param("meetingId") Long meetingId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,24 @@ public ApiResponse<MeetingResponse.MeetingEndResponseDTO> endMeeting(
return ApiResponse.onSuccess(meetingCommandService.endMeeting(memberId, meetingId));
}

@DeleteMapping("/meetings/{meetingId}")
@Operation(summary = "회의 삭제 API", description = """
특정 회의를 삭제하는 API입니다.
회의 삭제 시 회의 참여자, 대화 기록, 결정사항, 분석 데이터가 함께 삭제됩니다.
""")
@ApiErrorCodeExamples({
@ApiErrorCodeExample(value = ErrorStatus.class, name = "_UNAUTHORIZED"),
@ApiErrorCodeExample(value = ErrorStatus.class, name = "_BAD_REQUEST"),
@ApiErrorCodeExample(value = MeetingErrorCode.class, name = "MEETING_NOT_FOUND"),
@ApiErrorCodeExample(value = MeetingErrorCode.class, name = "MEETING_NOT_OWNER")
})
public ApiResponse<MeetingResponse.MeetingDeleteResponseDTO> deleteMeeting(
@Parameter(hidden = true) @CurrentMember Long memberId,
@PathVariable Long meetingId
) {
return ApiResponse.onSuccess(meetingCommandService.deleteMeeting(memberId, meetingId));
}

@GetMapping("/meetings/{meetingId}")
@Operation(summary = "회의 기본 정보 조회 API", description = """
특정 회의의 기본 정보를 조회하는 API입니다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,20 @@ public static class MeetingEndResponseDTO {
private LocalDateTime endDateTime;
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "회의 삭제 응답")
public static class MeetingDeleteResponseDTO {

@Schema(description = "회의 ID", example = "1")
private Long meetingId;

@Schema(description = "삭제 성공 여부", example = "true")
private Boolean isRemoved;
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.whylog.server.domain.meeting.entity;

import com.whylog.server.domain.decision.entity.Decision;
import com.whylog.server.domain.meeting.dto.MeetingRequest;
import com.whylog.server.domain.meeting.enums.MeetingStatus;
import com.whylog.server.domain.team.entity.Team;
Expand Down Expand Up @@ -90,12 +91,12 @@ public String getElapse() {
@OneToMany(mappedBy = "meeting", cascade = CascadeType.ALL, orphanRemoval = true)
private final List<MeetingMember> meetingMembers = new ArrayList<>();
//
// @OneToOne(mappedBy = "meeting", cascade = CascadeType.ALL, orphanRemoval = true)
// private final MeetingAnalysis meetingAnalyses;
@OneToOne(mappedBy = "meeting", cascade = CascadeType.ALL, orphanRemoval = true)
private final MeetingAnalysis meetingAnalyses = new MeetingAnalysis();
//
// @OneToMany(mappedBy = "meeting", cascade = CascadeType.ALL, orphanRemoval = true)
// private final List<Dialogue> dialogues = new ArrayList<>();
@OneToMany(mappedBy = "meeting", cascade = CascadeType.ALL, orphanRemoval = true)
private final List<Dialogue> dialogues = new ArrayList<>();
//
// @OneToOne(mappedBy = "meeting", cascade = CascadeType.ALL, orphanRemoval = true)
// private Decision decision;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum MeetingErrorCode implements BaseErrorCode {
MEETING_NOT_FOUND(HttpStatus.NOT_FOUND, "MEETING_404", "존재하지 않는 회의입니다."),
MEETING_ALREADY_ENDED(HttpStatus.CONFLICT, "MEETING_409", "이미 종료된 회의입니다."),
MEETING_INVALID_MEMBER(HttpStatus.CONFLICT, "MEETING_410", "회의에 소속된 참여자가 아닙니다."),
MEETING_NOT_OWNER(HttpStatus.FORBIDDEN, "MEETING_403", "회의 삭제 권한이 없습니다."),
;

private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.whylog.server.domain.meeting.repository;

import com.whylog.server.domain.meeting.entity.Dialogue;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface DialogueRepository extends JpaRepository<Dialogue, Long> {

@Modifying
@Query("DELETE FROM Dialogue d WHERE d.meeting.team.id = :teamId")
void deleteByTeamId(@Param("teamId") Long teamId);

@Modifying
@Query("DELETE FROM Dialogue d WHERE d.meeting.id = :meetingId")
void deleteByMeetingId(@Param("meetingId") Long meetingId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.whylog.server.domain.meeting.repository;

import com.whylog.server.domain.meeting.entity.MeetingAnalysis;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface MeetingAnalysisRepository extends JpaRepository<MeetingAnalysis, Long> {

@Modifying
@Query("DELETE FROM MeetingAnalysis ma WHERE ma.meeting.team.id = :teamId")
void deleteByTeamId(@Param("teamId") Long teamId);

@Modifying
@Query("DELETE FROM MeetingAnalysis ma WHERE ma.meeting.id = :meetingId")
void deleteByMeetingId(@Param("meetingId") Long meetingId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,41 @@

import com.whylog.server.domain.meeting.entity.MeetingMember;
import com.whylog.server.domain.meeting.entity.MeetingMemberId;
import com.whylog.server.domain.meeting.enums.MeetingRole;
import java.util.Optional;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface MeetingMemberRepository extends JpaRepository<MeetingMember, MeetingMemberId> {
boolean existsByMemberIdAndMeetingId(Long memberId, Long meetingId);

@Query("""
SELECT mm
FROM MeetingMember mm
JOIN FETCH mm.meeting
WHERE mm.member.id = :memberId
AND mm.meeting.id = :meetingId
AND mm.role = :role
""")
Optional<MeetingMember> findOwnerMeetingMember(
@Param("memberId") Long memberId,
@Param("meetingId") Long meetingId,
@Param("role") MeetingRole role
);

@Modifying
@Query("""
DELETE FROM MeetingMember mm
WHERE mm.meeting.team.id = :teamId
""")
void deleteByTeamId(@Param("teamId") Long teamId);

@Modifying
@Query("""
DELETE FROM MeetingMember mm
WHERE mm.meeting.id = :meetingId
""")
void deleteByMeetingId(@Param("meetingId") Long meetingId);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.whylog.server.domain.meeting.repository;

import com.whylog.server.domain.meeting.entity.Meeting;
import com.whylog.server.domain.meeting.enums.MeetingStatus;
import io.swagger.v3.oas.annotations.Parameter;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

Expand All @@ -13,19 +12,33 @@
public interface MeetingRepository extends JpaRepository<Meeting, Long> {

@Query("""
SELECT m FROM Meeting m
LEFT JOIN FETCH Team t
ON t.id = :teamId
SELECT m
FROM Meeting m
WHERE m.team.id = :teamId
""")
List<Meeting> findByTeamId(@Param("teamId") Long teamId);

@Query("""
SELECT m FROM Meeting m
LEFT JOIN FETCH MeetingMember mm
ON mm.meeting.id = m.id
SELECT DISTINCT m
FROM Meeting m
LEFT JOIN FETCH m.meetingMembers
WHERE m.id = :meetingId
""")
Optional<Meeting> findWithMembers(@Param("meetingId") Long meetingId);

@Query("""
SELECT m.id
FROM Meeting m
WHERE m.team.id = :teamId
""")
List<Long> findIdsByTeamId(@Param("teamId") Long teamId);

@Modifying
@Query("""
DELETE FROM Meeting m
WHERE m.team.id = :teamId
""")
void deleteByTeamId(@Param("teamId") Long teamId);


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.whylog.server.domain.meeting.service;

import com.whylog.server.domain.decision.repository.ApplicationRepository;
import com.whylog.server.domain.decision.repository.DecisionRepository;
import com.whylog.server.domain.meeting.repository.DialogueRepository;
import com.whylog.server.domain.meeting.repository.MeetingAnalysisRepository;
import com.whylog.server.domain.meeting.repository.MeetingMemberRepository;
import com.whylog.server.domain.meeting.repository.MeetingRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class MeetingCleanupService {

private final ApplicationRepository applicationRepository;
private final DecisionRepository decisionRepository;
private final MeetingAnalysisRepository meetingAnalysisRepository;
private final DialogueRepository dialogueRepository;
private final MeetingMemberRepository meetingMemberRepository;
private final MeetingRepository meetingRepository;

@Transactional
public List<Long> deleteByTeamId(Long teamId) {
List<Long> meetingIds = meetingRepository.findIdsByTeamId(teamId);
deleteChildrenByTeamId(teamId);
meetingRepository.deleteByTeamId(teamId);
return meetingIds;
}

@Transactional
public void deleteByMeetingId(Long meetingId) {
deleteChildrenByMeetingId(meetingId);
meetingRepository.deleteById(meetingId);
}

private void deleteChildrenByTeamId(Long teamId) {
applicationRepository.deleteByTeamId(teamId);
decisionRepository.deleteByTeamId(teamId);
meetingAnalysisRepository.deleteByTeamId(teamId);
dialogueRepository.deleteByTeamId(teamId);
meetingMemberRepository.deleteByTeamId(teamId);
}

private void deleteChildrenByMeetingId(Long meetingId) {
applicationRepository.deleteByMeetingId(meetingId);
decisionRepository.deleteByMeetingId(meetingId);
meetingAnalysisRepository.deleteByMeetingId(meetingId);
dialogueRepository.deleteByMeetingId(meetingId);
meetingMemberRepository.deleteByMeetingId(meetingId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,25 @@
import com.whylog.server.domain.meeting.entity.Meeting;
import com.whylog.server.domain.meeting.entity.MeetingMember;
import com.whylog.server.domain.meeting.enums.MeetingRole;
import com.whylog.server.domain.meeting.exception.MeetingErrorCode;
import com.whylog.server.domain.meeting.exception.MeetingAlreadyEndedException;
import com.whylog.server.domain.meeting.exception.MeetingInvalidMemberException;
import com.whylog.server.domain.meeting.exception.MeetingNotFoundException;
import com.whylog.server.domain.meeting.repository.MeetingMemberRepository;
import com.whylog.server.domain.meeting.repository.MeetingRepository;
import com.whylog.server.domain.meeting.socket.MeetingSocketRoomService;
import com.whylog.server.domain.meeting.service.MeetingCleanupService;
import com.whylog.server.domain.team.entity.Team;
import com.whylog.server.domain.team.service.TeamUseCase;
import com.whylog.server.domain.user.entity.Member;
import com.whylog.server.domain.user.service.MemberUseCase;
import com.whylog.server.global.apiPayload.exception.handler.ErrorHandler;
import com.whylog.server.global.apiPayload.exception.ParameterRequiredException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.time.LocalDateTime;

Expand All @@ -28,6 +33,7 @@ public class MeetingCommandService {

private final MeetingMemberRepository meetingMemberRepository;
private final MeetingRepository meetingRepository;
private final MeetingCleanupService meetingCleanupService;

private final MemberUseCase memberUseCase;
private final TeamUseCase teamUseCase;
Expand Down Expand Up @@ -106,4 +112,36 @@ public MeetingResponse.MeetingEndResponseDTO endMeeting(Long memberId, Long meet
.build();
}

@Transactional
public MeetingResponse.MeetingDeleteResponseDTO deleteMeeting(Long memberId, Long meetingId) {

meetingRepository.findById(meetingId)
.orElseThrow(MeetingNotFoundException::new);

meetingMemberRepository.findOwnerMeetingMember(memberId, meetingId, MeetingRole.OWNER)
.orElseThrow(() -> new ErrorHandler(MeetingErrorCode.MEETING_NOT_OWNER));

meetingCleanupService.deleteByMeetingId(meetingId);
scheduleAfterCommit(() -> meetingSocketRoomService.closeRoom(meetingId));

return MeetingResponse.MeetingDeleteResponseDTO.builder()
.meetingId(meetingId)
.isRemoved(true)
.build();
}

private void scheduleAfterCommit(Runnable task) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
task.run();
}
});
return;
}

task.run();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public MeetingRtcService(
this.liveKitTokenExpireTime = liveKitTokenExpireTime;
}

@Transactional(readOnly = true)
@Transactional
public MeetingResponse.MeetingRtcTokenDTO issueRtcToken(Long memberId, Long meetingId) {
Meeting meeting = meetingRepository.findById(meetingId)
.orElseThrow(MeetingNotFoundException::new);
Expand All @@ -73,7 +73,6 @@ private void ensureMeetingParticipant(Meeting meeting, Member member) {
if (meetingMemberRepository.existsByMemberIdAndMeetingId(member.getId(), meeting.getId())) {
return;
}

meetingMemberRepository.save(MeetingMember.create(meeting, member, MeetingRole.GENERAL));
}

Expand Down
Loading
Loading