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
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public interface MeetingRetrospectiveApi {
"meetingName": "데미안을 읽어보아요",
"meetingDate": "2026-01-15",
"meetingTime": "19:00-20:00",
"meetingLeaderId": 123,
"gathering": {
"gatheringId": 1,
"gatheringName": "독서 모임"
Expand Down Expand Up @@ -285,11 +286,11 @@ ResponseEntity<ApiResponse<MeetingRetrospectiveResponse.CommentResponse>> create
summary = "공동 회고 코멘트 삭제 (developer: 오주현)",
description = """
약속에 대한 공동 회고 코멘트를 삭제합니다.
- 권한: 작성자 또는 모임장
- 권한: 작성자 또는 약속장
""",
parameters = {
@Parameter(name = "meetingId", description = "약속 식별자", in = ParameterIn.PATH, required = true),
@Parameter(name = "meetingRetrospectiveId", description = "공동 회고 식별자", in = ParameterIn.PATH, required = true)
@Parameter(name = "commentId", description = "코멘트 식별자", in = ParameterIn.PATH, required = true)
}
)
@ApiResponses({
Expand All @@ -315,10 +316,10 @@ ResponseEntity<ApiResponse<MeetingRetrospectiveResponse.CommentResponse>> create
{"code": "E000", "message": "서버 에러가 발생했습니다. 담당자에게 문의 바랍니다.", "data": null}
""")))
})
@DeleteMapping(path = "/{meetingRetrospectiveId}", produces = MediaType.APPLICATION_JSON_VALUE)
@DeleteMapping(path = "/{commentId}", produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<ApiResponse<Void>> deleteMeetingRetrospective(
@PathVariable Long meetingId,
@PathVariable Long meetingRetrospectiveId
@PathVariable Long commentId
);

@Operation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ public ResponseEntity<ApiResponse<MeetingRetrospectiveResponse.CommentResponse>>
}

@Override
@DeleteMapping("/{meetingRetrospectiveId}")
@DeleteMapping("/{commentId}")
public ResponseEntity<ApiResponse<Void>> deleteMeetingRetrospective(
@PathVariable Long meetingId,
@PathVariable Long meetingRetrospectiveId
@PathVariable Long commentId
) {
meetingRetrospectiveService.deleteMeetingRetrospective(meetingId, meetingRetrospectiveId);
meetingRetrospectiveService.deleteMeetingRetrospective(meetingId, commentId);

return ApiResponse.deleted("공동 회고 코멘트 삭제 완료");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public record MeetingRetrospectiveResponse(
LocalDate meetingDate,
@Schema(description = "약속 시간", example = "14:00")
String meetingTime,
@Schema(description = "약속장 ID", example = "123")
Long meetingLeaderId,
@Schema(description = "모임 정보")
MeetingResponse.GatheringInfo gathering,
@Schema(description = "주제 목록")
Expand All @@ -37,6 +39,7 @@ public static MeetingRetrospectiveResponse from (
.meetingName(meeting.getMeetingName())
.meetingDate(meeting.getMeetingStartDate().toLocalDate())
.meetingTime(meeting.getFormattedTime())
.meetingLeaderId(meeting.getMeetingLeader().getId())
.gathering(MeetingResponse.GatheringInfo.from(meeting.getGathering()))
.topics(topics)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.dokdok.global.BaseTimeEntity;
import com.dokdok.meeting.entity.Meeting;
import com.dokdok.topic.entity.Topic;
import com.dokdok.user.entity.User;
import jakarta.persistence.*;
import lombok.AccessLevel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public interface RetrospectiveRepository extends JpaRepository<MeetingRetrospect
"JOIN FETCH mr.createdBy " +
"WHERE mr.meeting.id = :meetingId " +
"ORDER BY mr.createdAt DESC, mr.id DESC")
List<MeetingRetrospective> findByTopicIdFirstPage(
List<MeetingRetrospective> findByMeetingIdFirstPage(
@Param("meetingId") Long meetingId,
Pageable pageable
);
Expand All @@ -31,13 +31,13 @@ List<MeetingRetrospective> findByTopicIdFirstPage(
"AND (mr.createdAt < :cursorCreatedAt " +
" OR (mr.createdAt = :cursorCreatedAt AND mr.id < :cursorCommentId)) " +
"ORDER BY mr.createdAt DESC, mr.id DESC")
List<MeetingRetrospective> findByTopicIdAfterCursor(
List<MeetingRetrospective> findByMeetingIdAfterCursor(
@Param("meetingId") Long meetingId,
@Param("cursorCreatedAt") LocalDateTime cursorCreatedAt,
@Param("cursorCommentId") Long cursorCommentId,
Pageable pageable
);

@Query("SELECT COUNT(mr) FROM MeetingRetrospective mr WHERE mr.meeting.id = :meetingId")
int countByTopicId(@Param("meetingId") Long meetingId);
int countByMeetingId(@Param("meetingId") Long meetingId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import com.dokdok.topic.entity.TopicStatus;
import com.dokdok.topic.repository.TopicAnswerRepository;
import com.dokdok.topic.repository.TopicRepository;
import com.dokdok.topic.service.TopicValidator;
import com.dokdok.user.entity.User;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
Expand All @@ -41,7 +40,6 @@ public class MeetingRetrospectiveService {
private final TopicRetrospectiveSummaryRepository topicRetrospectiveSummaryRepository;
private final RetrospectiveRepository retrospectiveRepository;
private final MeetingValidator meetingValidator;
private final TopicValidator topicValidator;
private final StorageService storageService;
private final TopicAnswerRepository topicAnswerRepository;

Expand Down Expand Up @@ -115,11 +113,11 @@ public MeetingRetrospectiveResponse.CommentResponse createMeetingRetrospective(
}

@Transactional
public void deleteMeetingRetrospective(Long meetingId, Long meetingRetrospectiveId) {
public void deleteMeetingRetrospective(Long meetingId, Long commentId) {
Long userId = SecurityUtil.getCurrentUserId();

MeetingRetrospective retrospective = retrospectiveRepository
.findByIdAndMeetingId(meetingRetrospectiveId, meetingId)
.findByIdAndMeetingId(commentId, meetingId)
.orElseThrow(() -> new RetrospectiveException(RetrospectiveErrorCode.MEETING_RETROSPECTIVE_NOT_FOUND));

retrospectiveValidator.validateMeetingRetrospectiveDeletePermission(retrospective, userId);
Expand Down Expand Up @@ -151,8 +149,8 @@ private CursorResponse<MeetingRetrospectiveResponse.CommentResponse, CommentCurs
boolean isFirstPage = cursorCreatedAt == null || cursorCommentId == null;

List<MeetingRetrospective> comments = isFirstPage
? retrospectiveRepository.findByTopicIdFirstPage(meetingId, pageable)
: retrospectiveRepository.findByTopicIdAfterCursor(meetingId, cursorCreatedAt, cursorCommentId, pageable);
? retrospectiveRepository.findByMeetingIdFirstPage(meetingId, pageable)
: retrospectiveRepository.findByMeetingIdAfterCursor(meetingId, cursorCreatedAt, cursorCommentId, pageable);

boolean hasNext = comments.size() > pageSize;
List<MeetingRetrospective> pageComments = hasNext
Expand All @@ -164,7 +162,7 @@ private CursorResponse<MeetingRetrospectiveResponse.CommentResponse, CommentCurs
.toList();

CommentCursor nextCursor = buildNextCursor(pageComments, hasNext);
Integer totalCount = isFirstPage ? retrospectiveRepository.countByTopicId(meetingId) : null;
Integer totalCount = isFirstPage ? retrospectiveRepository.countByMeetingId(meetingId) : null;

return CursorResponse.of(items, pageSize, hasNext, nextCursor, totalCount);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,12 @@ void getMeetingRetrospective_withNoComments_success() {
Long userId = 1L;
Long gatheringId = 1L;

User leader = User.builder().id(userId).nickname("리더").build();
Gathering gathering = Gathering.builder().id(gatheringId).build();
Meeting meeting = Meeting.builder()
.id(meetingId)
.gathering(gathering)
.meetingLeader(leader)
.meetingName("모임")
.meetingStartDate(LocalDateTime.of(2026, 1, 15, 19, 0))
.meetingEndDate(LocalDateTime.of(2026, 1, 15, 21, 0))
Expand Down Expand Up @@ -289,27 +291,27 @@ void createMeetingRetrospective_throwsWhenNoAccess() {
@DisplayName("공동 회고 삭제를 정상적으로 수행한다")
void deleteMeetingRetrospective_success() {
Long meetingId = 1L;
Long retrospectiveId = 10L;
Long commentId = 10L;
Long userId = 1L;

Gathering gathering = Gathering.builder().id(1L).build();
Meeting meeting = Meeting.builder().id(meetingId).gathering(gathering).build();
User user = User.builder().id(userId).build();
MeetingRetrospective retrospective = MeetingRetrospective.builder()
.id(retrospectiveId)
.id(commentId)
.meeting(meeting)
.createdBy(user)
.build();

try (MockedStatic<SecurityUtil> securityUtilMock = mockStatic(SecurityUtil.class)) {
securityUtilMock.when(SecurityUtil::getCurrentUserId).thenReturn(userId);

when(retrospectiveRepository.findByIdAndMeetingId(retrospectiveId, meetingId))
when(retrospectiveRepository.findByIdAndMeetingId(commentId, meetingId))
.thenReturn(Optional.of(retrospective));
doNothing().when(retrospectiveValidator)
.validateMeetingRetrospectiveDeletePermission(retrospective, userId);

meetingRetrospectiveService.deleteMeetingRetrospective(meetingId, retrospectiveId);
meetingRetrospectiveService.deleteMeetingRetrospective(meetingId, commentId);

verify(retrospectiveRepository).delete(retrospective);
}
Expand All @@ -319,16 +321,16 @@ void deleteMeetingRetrospective_success() {
@DisplayName("공동 회고가 없으면 삭제 시 예외가 발생한다")
void deleteMeetingRetrospective_throwsWhenNotFound() {
Long meetingId = 1L;
Long retrospectiveId = 10L;
Long commentId = 10L;
Long userId = 1L;

try (MockedStatic<SecurityUtil> securityUtilMock = mockStatic(SecurityUtil.class)) {
securityUtilMock.when(SecurityUtil::getCurrentUserId).thenReturn(userId);

when(retrospectiveRepository.findByIdAndMeetingId(retrospectiveId, meetingId))
when(retrospectiveRepository.findByIdAndMeetingId(commentId, meetingId))
.thenReturn(Optional.empty());

assertThatThrownBy(() -> meetingRetrospectiveService.deleteMeetingRetrospective(meetingId, retrospectiveId))
assertThatThrownBy(() -> meetingRetrospectiveService.deleteMeetingRetrospective(meetingId, commentId))
.isInstanceOf(RetrospectiveException.class)
.hasFieldOrPropertyWithValue("errorCode", RetrospectiveErrorCode.MEETING_RETROSPECTIVE_NOT_FOUND);

Expand All @@ -340,27 +342,27 @@ void deleteMeetingRetrospective_throwsWhenNotFound() {
@DisplayName("공동 회고 삭제 권한이 없으면 예외가 발생한다")
void deleteMeetingRetrospective_throwsWhenForbidden() {
Long meetingId = 1L;
Long retrospectiveId = 10L;
Long commentId = 10L;
Long userId = 2L;

Gathering gathering = Gathering.builder().id(1L).build();
Meeting meeting = Meeting.builder().id(meetingId).gathering(gathering).build();
User user = User.builder().id(1L).build();
MeetingRetrospective retrospective = MeetingRetrospective.builder()
.id(retrospectiveId)
.id(commentId)
.meeting(meeting)
.createdBy(user)
.build();

try (MockedStatic<SecurityUtil> securityUtilMock = mockStatic(SecurityUtil.class)) {
securityUtilMock.when(SecurityUtil::getCurrentUserId).thenReturn(userId);

when(retrospectiveRepository.findByIdAndMeetingId(retrospectiveId, meetingId))
when(retrospectiveRepository.findByIdAndMeetingId(commentId, meetingId))
.thenReturn(Optional.of(retrospective));
doThrow(new GatheringException(GatheringErrorCode.NOT_GATHERING_LEADER))
.when(retrospectiveValidator).validateMeetingRetrospectiveDeletePermission(retrospective, userId);

assertThatThrownBy(() -> meetingRetrospectiveService.deleteMeetingRetrospective(meetingId, retrospectiveId))
assertThatThrownBy(() -> meetingRetrospectiveService.deleteMeetingRetrospective(meetingId, commentId))
.isInstanceOf(GatheringException.class)
.hasFieldOrPropertyWithValue("errorCode", GatheringErrorCode.NOT_GATHERING_LEADER);

Expand Down