Skip to content

Commit

Permalink
[COZY-49] feat: 쪽지방 삭제
Browse files Browse the repository at this point in the history
Co-authored-by: Ga Dong Sik <[email protected]>
  • Loading branch information
veronees and eple0329 authored Jul 31, 2024
1 parent d29b6e9 commit fce1517
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package com.cozymate.cozymate_server.domain.chat.repository;

import com.cozymate.cozymate_server.domain.chat.Chat;
import com.cozymate.cozymate_server.domain.chatroom.ChatRoom;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ChatRepository extends JpaRepository<Chat, Long> {

Optional<Chat> findTopByChatRoomOrderByIdDesc(ChatRoom chatRoom);

void deleteAllByChatRoom(ChatRoom chatRoom);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.cozymate.cozymate_server.domain.chat.dto.ChatRequestDto;
import com.cozymate.cozymate_server.domain.chat.repository.ChatRepository;
import com.cozymate.cozymate_server.domain.chatroom.ChatRoom;
import com.cozymate.cozymate_server.domain.chatroom.ChatRoomRepository;
import com.cozymate.cozymate_server.domain.chatroom.repository.ChatRoomRepository;
import com.cozymate.cozymate_server.domain.member.Member;
import com.cozymate.cozymate_server.domain.member.MemberRepository;
import com.cozymate.cozymate_server.domain.chat.converter.ChatConverter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,12 @@ public class ChatRoom extends BaseTimeEntity {
private LocalDateTime memberALastDeleteAt;

private LocalDateTime memberBLastDeleteAt;

public void updateMemberALastDeleteAt() {
this.memberALastDeleteAt = LocalDateTime.now();
}

public void updateMemberBLastDeleteAt() {
this.memberBLastDeleteAt = LocalDateTime.now();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.cozymate.cozymate_server.domain.chatroom.controller;

import com.cozymate.cozymate_server.domain.chatroom.service.ChatRoomCommandService;
import com.cozymate.cozymate_server.global.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/chatrooms")
public class ChatRoomController {

private final ChatRoomCommandService chatRoomCommandService;

// TODO: 파라미터로 받는 myId는 추후 시큐리티 인증 객체에서 받아오는 것으로 변경 예정
@DeleteMapping("/{chatRoomId}")
@Operation(summary = "[베로] 쪽지방 삭제 기능", description = "chatRoomId : 나갈 쪽지방 pk")
public ResponseEntity<ApiResponse<String>> deleteChatRoom(
@RequestParam Long myId, @PathVariable Long chatRoomId) {
chatRoomCommandService.deleteChatRoom(myId, chatRoomId);
return ResponseEntity.ok(ApiResponse.onSuccess("쪽지방 삭제 완료"));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.cozymate.cozymate_server.domain.chatroom;
package com.cozymate.cozymate_server.domain.chatroom.repository;

import com.cozymate.cozymate_server.domain.chatroom.ChatRoom;
import com.cozymate.cozymate_server.domain.member.Member;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.cozymate.cozymate_server.domain.chatroom.service;

import com.cozymate.cozymate_server.domain.chat.repository.ChatRepository;
import com.cozymate.cozymate_server.domain.chatroom.ChatRoom;
import com.cozymate.cozymate_server.domain.chatroom.repository.ChatRoomRepository;
import com.cozymate.cozymate_server.global.response.code.status.ErrorStatus;
import com.cozymate.cozymate_server.global.response.exception.GeneralException;
import java.time.LocalDateTime;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class ChatRoomCommandService {

private final ChatRoomRepository chatRoomRepository;
private final ChatRepository chatRepository;

public void deleteChatRoom(Long myId, Long chatRoomId) {
ChatRoom chatRoom = chatRoomRepository.findById(chatRoomId)
.orElseThrow(() -> new GeneralException(ErrorStatus._CHATROOM_NOT_FOUND));

softDeleteChatRoom(chatRoom, myId);

tryHardDeleteChatRoom(chatRoom);
}

private void softDeleteChatRoom(ChatRoom chatRoom, Long myId) {
if (chatRoom.getMemberA().getId().equals(myId)) {
chatRoom.updateMemberALastDeleteAt();
} else if (chatRoom.getMemberB().getId().equals(myId)) {
chatRoom.updateMemberBLastDeleteAt();
} else {
throw new GeneralException(ErrorStatus._CHATROOM_FORBIDDEN);
}
}

private void tryHardDeleteChatRoom(ChatRoom chatRoom) {
LocalDateTime memberALastDeleteAt = chatRoom.getMemberALastDeleteAt();
LocalDateTime memberBLastDeleteAt = chatRoom.getMemberBLastDeleteAt();

if (memberALastDeleteAt != null && memberBLastDeleteAt != null && canHardDelete(chatRoom,
memberALastDeleteAt, memberBLastDeleteAt)) {
hardDeleteChatRoom(chatRoom);
}
}

private boolean canHardDelete(ChatRoom chatRoom, LocalDateTime memberALastDeleteAt,
LocalDateTime memberBLastDeleteAt) {
return chatRepository.findTopByChatRoomOrderByIdDesc(chatRoom)
.map(chat -> chat.getCreatedAt().isBefore(memberALastDeleteAt) && chat.getCreatedAt()
.isBefore(memberBLastDeleteAt))
.orElse(false);
}

private void hardDeleteChatRoom(ChatRoom chatRoom) {
chatRepository.deleteAllByChatRoom(chatRoom);
chatRoomRepository.delete(chatRoom);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public enum ErrorStatus implements BaseErrorCode {
_MEMBERSTAT_EXISTS(HttpStatus.BAD_REQUEST,"MEMBERSTAT400","멤버 상세정보가 이미 존재합니다."),
_MEMBERSTAT_MERIDIAN_NOT_VALID(HttpStatus.BAD_REQUEST,"MEMBERSTAT401","오전, 오후를 정확하게 입력하세요."),

// ChatRoom 관련 애러
_CHATROOM_NOT_FOUND(HttpStatus.BAD_REQUEST, "CHATROOM400", "쪽지방을 찾을 수 없습니다."),
_CHATROOM_FORBIDDEN(HttpStatus.BAD_REQUEST,"CHATROOM401", "해당 쪽지방을 삭제할 권한이 없습니다."),

// Mate 관련
_MATE_NOT_FOUND(HttpStatus.BAD_REQUEST, "MATE400", "해당하는 메이트 정보가 없습니다."),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
import com.cozymate.cozymate_server.domain.chat.ChatTestBuilder;
import com.cozymate.cozymate_server.domain.chat.dto.ChatRequestDto;
import com.cozymate.cozymate_server.domain.chat.repository.ChatRepository;
import com.cozymate.cozymate_server.domain.chat.service.ChatCommandService;
import com.cozymate.cozymate_server.domain.chatroom.ChatRoom;
import com.cozymate.cozymate_server.domain.chatroom.ChatRoomRepository;
import com.cozymate.cozymate_server.domain.chatroom.repository.ChatRoomRepository;
import com.cozymate.cozymate_server.domain.chatroom.ChatRoomTestBuilder;
import com.cozymate.cozymate_server.domain.member.Member;
import com.cozymate.cozymate_server.domain.member.MemberRepository;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package com.cozymate.cozymate_server.domain.chatroom.service;

import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;

import com.cozymate.cozymate_server.domain.chat.Chat;
import com.cozymate.cozymate_server.domain.chat.repository.ChatRepository;
import com.cozymate.cozymate_server.domain.chatroom.ChatRoom;
import com.cozymate.cozymate_server.domain.chatroom.repository.ChatRoomRepository;
import com.cozymate.cozymate_server.domain.chatroom.ChatRoomTestBuilder;
import com.cozymate.cozymate_server.global.response.exception.GeneralException;
import java.time.LocalDateTime;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.*;

@ExtendWith(MockitoExtension.class)
@DisplayName("ChatRoomCommandService 클래스의")
public class ChatRoomCommandServiceTest {
@Mock
ChatRoomRepository chatRoomRepository;
@Mock
ChatRepository chatRepository;
@InjectMocks
ChatRoomCommandService chatRoomCommandService;
ChatRoom chatRoom;
Chat chat;
LocalDateTime methodStartTime;

@Nested
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class deleteChatRoom_메서드는 {

@Nested
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class 쪽지방을_한명만_삭제한_경우 {

@BeforeEach
void setUp() {
chatRoom = ChatRoomTestBuilder.testChatRoomBuild();
chat = mock(Chat.class);
methodStartTime = LocalDateTime.now();

given(chatRoomRepository.findById(chatRoom.getId())).willReturn(
Optional.of(chatRoom));
}

@Test
@DisplayName("ChatRoom의 deleteAt필드에 삭제 시간을 업데이트하여 논리적으로 삭제한다.")
void it_returns_update_deleteAt_soft_delete() {
chatRoomCommandService.deleteChatRoom(chatRoom.getMemberA().getId(), chatRoom.getId());
LocalDateTime memberALastDeleteAt = chatRoom.getMemberALastDeleteAt();
assertThat(memberALastDeleteAt).isAfter(methodStartTime);
assertThat(chatRoom.getMemberBLastDeleteAt()).isNull();
}
}

@Nested
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class 쪽지방을_두명_다_삭제한_경우 {

@Nested
@DisplayName("둘의 deleteAt 시간 보다 가장 최신 Chat의 createdAt이 빠른 경우")
class Context_with_chat_createAt_earlier_than_two_member_deleteAt {

@BeforeEach
void setUp() {
chatRoom = ChatRoomTestBuilder.testChatRoomBuild();
chat = mock(Chat.class);
chatRoom.updateMemberBLastDeleteAt();

given(chatRoomRepository.findById(chatRoom.getId())).willReturn(
Optional.of(chatRoom));
given(chatRepository.findTopByChatRoomOrderByIdDesc(chatRoom)).willReturn(
Optional.of(chat));
given(chat.getCreatedAt()).willReturn(LocalDateTime.now().minusDays(1));
doNothing().when(chatRepository).deleteAllByChatRoom(chatRoom);
doNothing().when(chatRoomRepository).delete(chatRoom);
}

@Test
@DisplayName("해당 쪽지방과 쪽지방의 쪽지를 물리적으로 DB에서 삭제한다.")
void it_returns_physically_delete() {
chatRoomCommandService.deleteChatRoom(chatRoom.getMemberA().getId(), chatRoom.getId());
then(chatRepository).should(timeout(1)).deleteAllByChatRoom(chatRoom);
then(chatRoomRepository).should(timeout(1)).delete(chatRoom);
}
}

@Nested
@DisplayName("가장 최신 쪽지 생성일이 두 명의 deleteAt 중에서 적어도 하나 보다 최근인 경우")
class Context_with_chat_createdAt_later_than_any_deleteAt {

@BeforeEach
void setUp() {
chatRoom = ChatRoomTestBuilder.testChatRoomBuild();
chat = mock(Chat.class);
chatRoom.updateMemberBLastDeleteAt();
methodStartTime = LocalDateTime.now();

given(chatRoomRepository.findById(chatRoom.getId())).willReturn(
Optional.of(chatRoom));
given(chatRepository.findTopByChatRoomOrderByIdDesc(chatRoom)).willReturn(
Optional.of(chat));
given(chat.getCreatedAt()).willReturn(LocalDateTime.now().plusDays(1));
}

@Test
@DisplayName("ChatRoom의 deleteAt필드에 삭제 시간을 업데이트하여 논리적으로 삭제한다.")
void it_returns_update_deleteAt_soft_delete() {
chatRoomCommandService.deleteChatRoom(chatRoom.getMemberA().getId(), chatRoom.getId());
LocalDateTime memberALastDeleteAt = chatRoom.getMemberALastDeleteAt();
assertThat(memberALastDeleteAt).isAfter(methodStartTime);
}
}
}

@Nested
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class 유효하지_않은_chatRoomId인_경우 {

@BeforeEach
void setUp() {
given(chatRoomRepository.findById(1L)).willReturn(Optional.empty());
}

@Test
@DisplayName("예외를 발생시킨다.")
void it_returns_not_found_chat_room_exception() {
assertThatThrownBy(() -> chatRoomCommandService.deleteChatRoom(1L, 1L))
.isInstanceOf(GeneralException.class);
}
}

@Nested
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class myId가_해당_쪽지방의_멤버가_아닌_경우 {

@BeforeEach
void setUp() {
chatRoom = ChatRoomTestBuilder.testChatRoomBuild();
given(chatRoomRepository.findById(chatRoom.getId())).willReturn(
Optional.of(chatRoom));
}

@Test
@DisplayName("예외를 발생시킨다.")
void it_returns_chat_room_forbidden() {
assertThatThrownBy(() -> chatRoomCommandService.deleteChatRoom(3L, chatRoom.getId()))
.isInstanceOf(GeneralException.class);
}
}
}
}

0 comments on commit fce1517

Please sign in to comment.