From 729b52b03bf4888b31da952ea7de3e104e5182b8 Mon Sep 17 00:00:00 2001 From: mandykr Date: Sat, 26 Apr 2025 15:54:13 +0900 Subject: [PATCH 1/4] =?UTF-8?q?chore:=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/retrip/crew/domain/vo/DemandStatus.java | 2 +- src/main/java/com/retrip/crew/domain/vo/RecruitmentStatus.java | 2 +- .../infra/adapter/in/presentation/rest/DemandController.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/retrip/crew/domain/vo/DemandStatus.java b/src/main/java/com/retrip/crew/domain/vo/DemandStatus.java index 2775da1..7e06025 100644 --- a/src/main/java/com/retrip/crew/domain/vo/DemandStatus.java +++ b/src/main/java/com/retrip/crew/domain/vo/DemandStatus.java @@ -18,7 +18,7 @@ public enum DemandStatus { public static DemandStatus codeOf(String code) { return Arrays.stream(DemandStatus.values()) - .filter(participantStatus -> participantStatus.getCode().equals(code)) + .filter(value -> value.getCode().equals(code)) .findAny() .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 코드입니다.")); } diff --git a/src/main/java/com/retrip/crew/domain/vo/RecruitmentStatus.java b/src/main/java/com/retrip/crew/domain/vo/RecruitmentStatus.java index 7c11d80..da24a44 100644 --- a/src/main/java/com/retrip/crew/domain/vo/RecruitmentStatus.java +++ b/src/main/java/com/retrip/crew/domain/vo/RecruitmentStatus.java @@ -17,7 +17,7 @@ public enum RecruitmentStatus { public static RecruitmentStatus codeOf(String code) { return Arrays.stream(RecruitmentStatus.values()) - .filter(tripStatus -> tripStatus.getCode().equals(code)) + .filter(value -> value.getCode().equals(code)) .findAny() .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 코드입니다.")); } diff --git a/src/main/java/com/retrip/crew/infra/adapter/in/presentation/rest/DemandController.java b/src/main/java/com/retrip/crew/infra/adapter/in/presentation/rest/DemandController.java index 6337344..6d16606 100644 --- a/src/main/java/com/retrip/crew/infra/adapter/in/presentation/rest/DemandController.java +++ b/src/main/java/com/retrip/crew/infra/adapter/in/presentation/rest/DemandController.java @@ -20,7 +20,7 @@ @RequiredArgsConstructor @RequestMapping("/crews") @RestController -@Tag(name = "Crew", description = "크루 참여 모집 서비스") +@Tag(name = "Recruitment", description = "크루 참여 모집 서비스") public class DemandController { private final UpdateRecruitmentUseCase updateRecruitmentUseCase; private final ManageDemandUseCase manageDemandUseCase; From 7cbec30d74d052b74fbb0bc4776da5585eb02dd3 Mon Sep 17 00:00:00 2001 From: mandykr Date: Sat, 26 Apr 2025 16:01:38 +0900 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20CrewService=EC=97=90=EC=84=9C?= =?UTF-8?q?=20DemandService=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/application/in/CrewService.java | 104 +------------- .../crew/application/in/DemandService.java | 114 +++++++++++++++ .../crew/application/in/CrewServiceTest.java | 134 ++---------------- .../application/in/DemandServiceTest.java | 126 ++++++++++++++++ .../com/retrip/crew/common/ServiceTest.java | 14 +- 5 files changed, 258 insertions(+), 234 deletions(-) create mode 100644 src/main/java/com/retrip/crew/application/in/DemandService.java create mode 100644 src/test/java/com/retrip/crew/application/in/DemandServiceTest.java diff --git a/src/main/java/com/retrip/crew/application/in/CrewService.java b/src/main/java/com/retrip/crew/application/in/CrewService.java index 8958dd1..8df5b01 100644 --- a/src/main/java/com/retrip/crew/application/in/CrewService.java +++ b/src/main/java/com/retrip/crew/application/in/CrewService.java @@ -6,8 +6,6 @@ import com.retrip.crew.application.in.request.crew.CrewCreateRequest; import com.retrip.crew.application.in.request.crew.CrewOrder; import com.retrip.crew.application.in.request.crew.CrewUpdateRequest; -import com.retrip.crew.application.in.request.demand.CreateDemandRequest; -import com.retrip.crew.application.in.request.demand.DemandOrder; import com.retrip.crew.application.in.response.IntroductionCreateResponse; import com.retrip.crew.application.in.response.IntroductionDetailResponse; import com.retrip.crew.application.in.response.IntroductionListResponse; @@ -16,56 +14,36 @@ import com.retrip.crew.application.in.response.crew.CrewDetailResponse; import com.retrip.crew.application.in.response.crew.CrewListResponse; import com.retrip.crew.application.in.response.crew.CrewUpdateResponse; -import com.retrip.crew.application.in.response.demand.ApproveDemandResponse; -import com.retrip.crew.application.in.response.demand.ChangeRecruitmentStatusResponse; -import com.retrip.crew.application.in.response.demand.CreateDemandResponse; -import com.retrip.crew.application.in.response.demand.CrewsOfDemandResponse; -import com.retrip.crew.application.in.response.demand.DemandsResponse; -import com.retrip.crew.application.in.response.demand.RejectDemandResponse; import com.retrip.crew.application.in.usecase.GetCrewUseCase; import com.retrip.crew.application.in.usecase.ManageCrewUseCase; -import com.retrip.crew.application.in.usecase.ManageDemandUseCase; import com.retrip.crew.application.in.usecase.ManageIntroductionUseCase; -import com.retrip.crew.application.in.usecase.UpdateRecruitmentUseCase; -import com.retrip.crew.application.out.repository.CrewDemandRepository; -import com.retrip.crew.application.out.repository.CrewMemberRepository; -import com.retrip.crew.application.out.repository.CrewQueryRepository; -import com.retrip.crew.application.out.repository.CrewRepository; -import com.retrip.crew.application.out.repository.IntroductionQueryRepository; -import com.retrip.crew.application.out.repository.IntroductionRepository; +import com.retrip.crew.application.out.repository.*; import com.retrip.crew.domain.entity.Crew; -import com.retrip.crew.domain.entity.Demand; import com.retrip.crew.domain.entity.Introduction; import com.retrip.crew.domain.entity.Recruitment; import com.retrip.crew.domain.exception.CrewNotFoundException; import com.retrip.crew.domain.exception.IntroductionNotFoundException; -import com.retrip.crew.domain.exception.NotCrewLeaderException; -import com.retrip.crew.domain.exception.common.BusinessException; -import com.retrip.crew.domain.exception.common.EntityNotFoundException; import com.retrip.crew.domain.vo.CrewDescription; import com.retrip.crew.domain.vo.CrewTitle; -import com.retrip.crew.domain.vo.DemandStatus; import com.retrip.crew.domain.vo.IntroductionContent; import com.retrip.crew.domain.vo.IntroductionTitle; import com.retrip.crew.infra.adapter.in.presentation.rest.common.ScrollPageResponse; import com.retrip.crew.infra.util.PaginationUtils; -import java.util.UUID; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.UUID; + @Service @Transactional @RequiredArgsConstructor -public class CrewService implements ManageCrewUseCase, UpdateRecruitmentUseCase, ManageDemandUseCase, GetCrewUseCase, ManageIntroductionUseCase{ - +public class CrewService implements ManageCrewUseCase, GetCrewUseCase, ManageIntroductionUseCase{ private final CrewRepository crewRepository; private final CrewMemberRepository crewMemberRepository; private final CrewQueryRepository crewQueryRepository; - private final CrewDemandRepository demandRepository; private final IntroductionRepository introductionRepository; private final IntroductionQueryRepository introductionQueryRepository; @@ -87,80 +65,6 @@ public CrewUpdateResponse updateCrew(UUID crewId, CrewUpdateRequest request) { return CrewUpdateResponse.of(crew); } - @Override - public ChangeRecruitmentStatusResponse startRecruitment(UUID crewId) { - Crew crew = findCrewById(crewId); - crew.startRecruitment(); - return ChangeRecruitmentStatusResponse.of(crew); - } - - @Override - public ChangeRecruitmentStatusResponse stopRecruitment(UUID crewId) { - Crew crew = findCrewById(crewId); - crew.stopRecruitment(); - return ChangeRecruitmentStatusResponse.of(crew); - } - - @Override - public CreateDemandResponse createDemand(UUID crewId, CreateDemandRequest request) { - Crew crew = findCrewById(crewId); - Demand demand = crew.demand(request.memberId()); - return CreateDemandResponse.of(crew.getId(), demand); - } - - @Override - public Page getDemands( - UUID crewId, UUID memberId, String status, Pageable pageable, DemandOrder order, String sort) { - Crew crew = findCrewById(crewId); - throwIfNotLeader(crew, memberId, new NotCrewLeaderException()); - Page demands = demandRepository.findByCrewIdAndStatus( - crewId, DemandStatus.valueOf(status), PaginationUtils.createPageRequest(pageable, order.getField(), sort)); - return demands.map(d -> DemandsResponse.of(crewId, d)); - } - - @Override - public Page getCrewsOfDemand( - UUID crewId, UUID demandId, UUID memberId, Pageable pageable, CrewOrder order, String sort) { - Crew crew = findCrewById(crewId); - throwIfNotLeader(crew, memberId, new NotCrewLeaderException()); - Demand demand = findDemandByIdAndCrewId(demandId, crewId); - return crewQueryRepository.findAllContainsMember(pageable, demand.getMemberId()); - } - - private static void throwIfNotLeader(Crew crew, UUID memberId, BusinessException exception) { - if (!crew.getCrewMembers().isLeader(memberId)) { - throw exception; - } - } - - @Override - public void cancelDemand(UUID crewId, UUID demandId, UUID memberId) { - Demand demand = findDemandByIdAndCrewId(demandId, crewId); - Crew crew = demand.getCrew(); - crew.cancelDemand(demand); - } - - @Override - public RejectDemandResponse rejectDemand(UUID crewId, UUID demandId, UUID memberId) { - Demand demand = findDemandByIdAndCrewId(demandId, crewId); - Crew crew = demand.getCrew(); - crew.rejectDemand(demand); - return RejectDemandResponse.of(demand); - } - - @Override - public ApproveDemandResponse approveDemand(UUID crewId, UUID demandId, UUID memberId) { - Demand demand = findDemandByIdAndCrewId(demandId, crewId); - Crew crew = demand.getCrew(); - crew.approveDemand(demand); - return ApproveDemandResponse.of(demand); - } - - private Demand findDemandByIdAndCrewId(UUID demandId, UUID crewId) { - return demandRepository.findCrewByIdAndCrewId(demandId, crewId) - .orElseThrow(() -> new EntityNotFoundException("참여 요청 엔티티를 찾을 수 없습니다.")); - } - @Override @Transactional(readOnly = true) public ScrollPageResponse getCrews(Pageable pageable, String keyword, CrewOrder order, String sort) { diff --git a/src/main/java/com/retrip/crew/application/in/DemandService.java b/src/main/java/com/retrip/crew/application/in/DemandService.java new file mode 100644 index 0000000..24556f6 --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/DemandService.java @@ -0,0 +1,114 @@ +package com.retrip.crew.application.in; + +import com.retrip.crew.application.in.request.crew.CrewOrder; +import com.retrip.crew.application.in.request.demand.CreateDemandRequest; +import com.retrip.crew.application.in.request.demand.DemandOrder; +import com.retrip.crew.application.in.response.demand.*; +import com.retrip.crew.application.in.usecase.ManageDemandUseCase; +import com.retrip.crew.application.in.usecase.UpdateRecruitmentUseCase; +import com.retrip.crew.application.out.repository.CrewDemandRepository; +import com.retrip.crew.application.out.repository.CrewQueryRepository; +import com.retrip.crew.application.out.repository.CrewRepository; +import com.retrip.crew.domain.entity.Crew; +import com.retrip.crew.domain.entity.Demand; +import com.retrip.crew.domain.exception.CrewNotFoundException; +import com.retrip.crew.domain.exception.NotCrewLeaderException; +import com.retrip.crew.domain.exception.common.BusinessException; +import com.retrip.crew.domain.exception.common.EntityNotFoundException; +import com.retrip.crew.domain.vo.DemandStatus; +import com.retrip.crew.infra.util.PaginationUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.UUID; + +@Service +@Transactional +@RequiredArgsConstructor +public class DemandService implements UpdateRecruitmentUseCase, ManageDemandUseCase { + private final CrewRepository crewRepository; + private final CrewDemandRepository demandRepository; + private final CrewQueryRepository crewQueryRepository; + + @Override + public ChangeRecruitmentStatusResponse startRecruitment(UUID crewId) { + Crew crew = findById(crewId); + crew.startRecruitment(); + return ChangeRecruitmentStatusResponse.of(crew); + } + + @Override + public ChangeRecruitmentStatusResponse stopRecruitment(UUID crewId) { + Crew crew = findById(crewId); + crew.stopRecruitment(); + return ChangeRecruitmentStatusResponse.of(crew); + } + + @Override + public CreateDemandResponse createDemand(UUID crewId, CreateDemandRequest request) { + Crew crew = findById(crewId); + Demand demand = crew.demand(request.memberId()); + return CreateDemandResponse.of(crew.getId(), demand); + } + + @Override + public Page getDemands( + UUID crewId, UUID memberId, String status, Pageable pageable, DemandOrder order, String sort) { + Crew crew = findById(crewId); + throwIfNotLeader(crew, memberId, new NotCrewLeaderException()); + Page demands = demandRepository.findByCrewIdAndStatus( + crewId, DemandStatus.valueOf(status), PaginationUtils.createPageRequest(pageable, order.getField(), sort)); + return demands.map(d -> DemandsResponse.of(crewId, d)); + } + + @Override + public Page getCrewsOfDemand( + UUID crewId, UUID demandId, UUID memberId, Pageable pageable, CrewOrder order, String sort) { + Crew crew = findById(crewId); + throwIfNotLeader(crew, memberId, new NotCrewLeaderException()); + Demand demand = findDemandByIdAndCrewId(demandId, crewId); + return crewQueryRepository.findAllContainsMember(pageable, demand.getMemberId()); + } + + private static void throwIfNotLeader(Crew crew, UUID memberId, BusinessException exception) { + if (!crew.getCrewMembers().isLeader(memberId)) { + throw exception; + } + } + + @Override + public void cancelDemand(UUID crewId, UUID demandId, UUID memberId) { + Demand demand = findDemandByIdAndCrewId(demandId, crewId); + Crew crew = demand.getCrew(); + crew.cancelDemand(demand); + } + + @Override + public RejectDemandResponse rejectDemand(UUID crewId, UUID demandId, UUID memberId) { + Demand demand = findDemandByIdAndCrewId(demandId, crewId); + Crew crew = demand.getCrew(); + crew.rejectDemand(demand); + return RejectDemandResponse.of(demand); + } + + @Override + public ApproveDemandResponse approveDemand(UUID crewId, UUID demandId, UUID memberId) { + Demand demand = findDemandByIdAndCrewId(demandId, crewId); + Crew crew = demand.getCrew(); + crew.approveDemand(demand); + return ApproveDemandResponse.of(demand); + } + + private Demand findDemandByIdAndCrewId(UUID demandId, UUID crewId) { + return demandRepository.findCrewByIdAndCrewId(demandId, crewId) + .orElseThrow(() -> new EntityNotFoundException("참여 요청 엔티티를 찾을 수 없습니다.")); + } + + private Crew findById(UUID crewId){ + return crewRepository.findById(crewId) + .orElseThrow(CrewNotFoundException::new); + } +} diff --git a/src/test/java/com/retrip/crew/application/in/CrewServiceTest.java b/src/test/java/com/retrip/crew/application/in/CrewServiceTest.java index 8ef1a5e..8f47f06 100644 --- a/src/test/java/com/retrip/crew/application/in/CrewServiceTest.java +++ b/src/test/java/com/retrip/crew/application/in/CrewServiceTest.java @@ -1,57 +1,34 @@ package com.retrip.crew.application.in; -import static com.retrip.crew.common.fixture.CrewFixture.LEADER_ID; -import static com.retrip.crew.common.fixture.CrewFixture.MEMBER_ID; -import static com.retrip.crew.common.fixture.CrewFixture.createCrew; -import static com.retrip.crew.common.fixture.CrewFixture.createCrewRequest; -import static com.retrip.crew.common.fixture.CrewFixture.createCrewWithMembers; -import static com.retrip.crew.common.fixture.CrewFixture.createMultipleCrews; -import static com.retrip.crew.common.fixture.CrewFixture.정수_ID; -import static com.retrip.crew.common.fixture.CrewFixture.준호_ID; -import static com.retrip.crew.common.fixture.CrewFixture.지수_ID; -import static com.retrip.crew.common.fixture.CrewFixture.혁진_ID; -import static com.retrip.crew.common.fixture.CrewFixture.홍석_ID; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertThrows; - import com.retrip.crew.application.in.request.IntroductionCreateRequest; import com.retrip.crew.application.in.request.IntroductionDeleteRequest; import com.retrip.crew.application.in.request.IntroductionUpdateRequest; import com.retrip.crew.application.in.request.crew.CrewCreateRequest; import com.retrip.crew.application.in.request.crew.CrewOrder; import com.retrip.crew.application.in.request.crew.CrewUpdateRequest; -import com.retrip.crew.application.in.request.demand.CreateDemandRequest; -import com.retrip.crew.application.in.request.demand.DemandOrder; import com.retrip.crew.application.in.response.IntroductionCreateResponse; import com.retrip.crew.application.in.response.IntroductionDetailResponse; import com.retrip.crew.application.in.response.crew.CrewCreateResponse; import com.retrip.crew.application.in.response.crew.CrewDetailResponse; import com.retrip.crew.application.in.response.crew.CrewListResponse; import com.retrip.crew.application.in.response.crew.CrewUpdateResponse; -import com.retrip.crew.application.in.response.demand.CreateDemandResponse; -import com.retrip.crew.application.in.response.demand.CrewsOfDemandResponse; -import com.retrip.crew.application.in.response.demand.DemandsResponse; import com.retrip.crew.common.ServiceTest; import com.retrip.crew.domain.entity.Crew; import com.retrip.crew.domain.entity.CrewMemberRole; -import com.retrip.crew.domain.entity.Demand; import com.retrip.crew.domain.entity.Introduction; -import com.retrip.crew.domain.exception.DuplicateDemandException; import com.retrip.crew.domain.exception.common.InvalidAccessException; -import com.retrip.crew.domain.vo.DemandStatus; import com.retrip.crew.infra.adapter.in.presentation.rest.common.ScrollPageResponse; -import java.util.List; -import java.util.Optional; -import java.util.UUID; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; +import java.util.UUID; + +import static com.retrip.crew.common.fixture.CrewFixture.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertThrows; class CrewServiceTest extends ServiceTest { @Test @@ -92,101 +69,6 @@ class CrewServiceTest extends ServiceTest { ); } - @Test - void 크루_참여_요청을_생성한다() { - // given - Crew crew = crewRepository.save(createCrew(LEADER_ID)); - CreateDemandRequest request = new CreateDemandRequest(MEMBER_ID); - - // when - CreateDemandResponse response = crewService.createDemand(crew.getId(), request); - - // then - List demands = crew.getRecruitment().getDemands(); - assertAll( - () -> assertThat(demands.size()).isEqualTo(1), - () -> assertThat(response.memberId()).isEqualTo(demands.get(0).getMemberId()) - ); - } - - @Test - void 이미_요청한_사용자는_다시_크루에_요청할_수_없다() { - // given - Crew crew = createCrew(LEADER_ID); - crew.demand(MEMBER_ID); - crew.demand(UUID.randomUUID()); - crew.demand(UUID.randomUUID()); - Crew save = crewRepository.save(crew); - CreateDemandRequest request = new CreateDemandRequest(MEMBER_ID); - - // when, then - assertThatThrownBy(() -> crewService.createDemand(save.getId(), request)) - .isExactlyInstanceOf(DuplicateDemandException.class); - } - - @Test - void 크루_참여_요청을_취소한다() { - // given - Crew crew = crewRepository.save(createCrew(LEADER_ID)); - Demand demand = crew.demand(MEMBER_ID); - - // when - crewService.cancelDemand(crew.getId(), demand.getId(), MEMBER_ID); - - // then - assertThat(demand.getStatus()).isEqualTo(DemandStatus.CANCELED); - } - - @ParameterizedTest - @CsvSource({"PENDING,2", "APPROVED,2", "REJECTED,1"}) - void 리더가_크루_요청_목록을_조회한다(String status, int expected) { - // given - Crew crew = crewRepository.save(createCrew(LEADER_ID)); - Demand 정수 = new Demand(정수_ID, crew); - Demand 홍석 = new Demand(홍석_ID, crew); - Demand 준호 = new Demand(준호_ID, crew); - Demand 지수 = new Demand(지수_ID, crew); - Demand 혁진 = new Demand(혁진_ID, crew); - List demands = List.of(정수, 홍석, 준호, 지수, 혁진); - - ReflectionTestUtils.setField(준호, "status", DemandStatus.APPROVED); - ReflectionTestUtils.setField(지수, "status", DemandStatus.APPROVED); - ReflectionTestUtils.setField(혁진, "status", DemandStatus.REJECTED); - ReflectionTestUtils.setField(crew.getRecruitment(), "demands", demands); - - Pageable pageable = PageRequest.of(0, 10); - - // when - Page response = - crewService.getDemands(crew.getId(), LEADER_ID, status, pageable, DemandOrder.DATE, "desc"); - - // then - assertThat(response.getTotalElements()).isEqualTo(expected); - } - - @Test - void 참여_요청자가_속한_크루_목록을_조회한다() { - // given - Crew crew = createCrew(LEADER_ID); - Demand demand = new Demand(홍석_ID, crew); - ReflectionTestUtils.setField(crew.getRecruitment(), "demands", List.of(demand)); - - crewRepository.save(crew); - crewRepository.save(createCrewWithMembers(MEMBER_ID)); - crewRepository.save(createCrewWithMembers(MEMBER_ID)); - crewRepository.save(createCrewWithMembers(MEMBER_ID)); - crewRepository.save(createCrewWithMembers(MEMBER_ID)); - crewRepository.save(createCrewWithMembers(MEMBER_ID)); - Pageable pageable = PageRequest.of(0, 10); - - // when - Page response = - crewService.getCrewsOfDemand(crew.getId(), demand.getId(), LEADER_ID, pageable, CrewOrder.DATE, "desc"); - - // then - assertThat(response.getTotalElements()).isEqualTo(5); - } - @Test void 크루를_검색_및_정렬_필터링하여_조회한다() { //given diff --git a/src/test/java/com/retrip/crew/application/in/DemandServiceTest.java b/src/test/java/com/retrip/crew/application/in/DemandServiceTest.java new file mode 100644 index 0000000..4ec8692 --- /dev/null +++ b/src/test/java/com/retrip/crew/application/in/DemandServiceTest.java @@ -0,0 +1,126 @@ +package com.retrip.crew.application.in; + +import com.retrip.crew.application.in.request.crew.CrewOrder; +import com.retrip.crew.application.in.request.demand.CreateDemandRequest; +import com.retrip.crew.application.in.request.demand.DemandOrder; +import com.retrip.crew.application.in.response.demand.CreateDemandResponse; +import com.retrip.crew.application.in.response.demand.CrewsOfDemandResponse; +import com.retrip.crew.application.in.response.demand.DemandsResponse; +import com.retrip.crew.common.ServiceTest; +import com.retrip.crew.domain.entity.Crew; +import com.retrip.crew.domain.entity.Demand; +import com.retrip.crew.domain.exception.DuplicateDemandException; +import com.retrip.crew.domain.vo.DemandStatus; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; +import java.util.UUID; + +import static com.retrip.crew.common.fixture.CrewFixture.*; +import static com.retrip.crew.common.fixture.CrewFixture.LEADER_ID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.*; + +class DemandServiceTest extends ServiceTest { + @Test + void 크루_참여_요청을_생성한다() { + // given + Crew crew = crewRepository.save(createCrew(LEADER_ID)); + CreateDemandRequest request = new CreateDemandRequest(MEMBER_ID); + + // when + CreateDemandResponse response = demandService.createDemand(crew.getId(), request); + + // then + List demands = crew.getRecruitment().getDemands(); + assertAll( + () -> assertThat(demands.size()).isEqualTo(1), + () -> assertThat(response.memberId()).isEqualTo(demands.get(0).getMemberId()) + ); + } + + @Test + void 이미_요청한_사용자는_다시_크루에_요청할_수_없다() { + // given + Crew crew = createCrew(LEADER_ID); + crew.demand(MEMBER_ID); + crew.demand(UUID.randomUUID()); + crew.demand(UUID.randomUUID()); + Crew save = crewRepository.save(crew); + CreateDemandRequest request = new CreateDemandRequest(MEMBER_ID); + + // when, then + assertThatThrownBy(() -> demandService.createDemand(save.getId(), request)) + .isExactlyInstanceOf(DuplicateDemandException.class); + } + + @Test + void 크루_참여_요청을_취소한다() { + // given + Crew crew = crewRepository.save(createCrew(LEADER_ID)); + Demand demand = crew.demand(MEMBER_ID); + + // when + demandService.cancelDemand(crew.getId(), demand.getId(), MEMBER_ID); + + // then + assertThat(demand.getStatus()).isEqualTo(DemandStatus.CANCELED); + } + + @ParameterizedTest + @CsvSource({"PENDING,2", "APPROVED,2", "REJECTED,1"}) + void 리더가_크루_참여_요청_목록을_조회한다(String status, int expected) { + // given + Crew crew = crewRepository.save(createCrew(LEADER_ID)); + Demand 정수 = new Demand(정수_ID, crew); + Demand 홍석 = new Demand(홍석_ID, crew); + Demand 준호 = new Demand(준호_ID, crew); + Demand 지수 = new Demand(지수_ID, crew); + Demand 혁진 = new Demand(혁진_ID, crew); + List demands = List.of(정수, 홍석, 준호, 지수, 혁진); + + ReflectionTestUtils.setField(준호, "status", DemandStatus.APPROVED); + ReflectionTestUtils.setField(지수, "status", DemandStatus.APPROVED); + ReflectionTestUtils.setField(혁진, "status", DemandStatus.REJECTED); + ReflectionTestUtils.setField(crew.getRecruitment(), "demands", demands); + + Pageable pageable = PageRequest.of(0, 10); + + // when + Page response = + demandService.getDemands(crew.getId(), LEADER_ID, status, pageable, DemandOrder.DATE, "desc"); + + // then + assertThat(response.getTotalElements()).isEqualTo(expected); + } + + @Test + void 참여_요청자가_속한_크루_목록을_조회한다() { + // given + Crew crew = createCrew(LEADER_ID); + Demand demand = new Demand(홍석_ID, crew); + ReflectionTestUtils.setField(crew.getRecruitment(), "demands", List.of(demand)); + + crewRepository.save(crew); + crewRepository.save(createCrewWithMembers(MEMBER_ID)); + crewRepository.save(createCrewWithMembers(MEMBER_ID)); + crewRepository.save(createCrewWithMembers(MEMBER_ID)); + crewRepository.save(createCrewWithMembers(MEMBER_ID)); + crewRepository.save(createCrewWithMembers(MEMBER_ID)); + Pageable pageable = PageRequest.of(0, 10); + + // when + Page response = + demandService.getCrewsOfDemand(crew.getId(), demand.getId(), LEADER_ID, pageable, CrewOrder.DATE, "desc"); + + // then + assertThat(response.getTotalElements()).isEqualTo(5); + } +} diff --git a/src/test/java/com/retrip/crew/common/ServiceTest.java b/src/test/java/com/retrip/crew/common/ServiceTest.java index 0a7d9b3..65ed4dc 100644 --- a/src/test/java/com/retrip/crew/common/ServiceTest.java +++ b/src/test/java/com/retrip/crew/common/ServiceTest.java @@ -1,14 +1,9 @@ package com.retrip.crew.common; import com.retrip.crew.application.in.CrewService; -import com.retrip.crew.application.out.repository.CrewDemandRepository; -import com.retrip.crew.application.out.repository.CrewMemberRepository; -import com.retrip.crew.application.out.repository.CrewQueryRepository; -import com.retrip.crew.application.out.repository.CrewRepository; -import com.retrip.crew.application.out.repository.IntroductionQueryRepository; -import com.retrip.crew.application.out.repository.IntroductionRepository; +import com.retrip.crew.application.in.DemandService; +import com.retrip.crew.application.out.repository.*; import com.retrip.crew.common.config.QuerydslConfig; -import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; @@ -44,8 +39,11 @@ public class ServiceTest { protected CrewService crewService; + protected DemandService demandService; + @BeforeEach void setUp() { - crewService = new CrewService(crewRepository, crewMemberRepository, crewQueryRepository, demandRepository, introductionRepository, introductionQueryRepository); + crewService = new CrewService(crewRepository, crewMemberRepository, crewQueryRepository, introductionRepository, introductionQueryRepository); + demandService = new DemandService(crewRepository, demandRepository, crewQueryRepository); } } From 184741b373eaf209a08c619baf888b52fb50f4aa Mon Sep 17 00:00:00 2001 From: mandykr Date: Sat, 26 Apr 2025 17:49:31 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=ED=81=AC=EB=A3=A8=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/application/in/CrewService.java | 9 ++ .../request/crew/CrewWithdrawalRequest.java | 17 ++++ .../in/usecase/ManageCrewUseCase.java | 2 + .../java/com/retrip/crew/domain/CrewTrip.java | 34 ++++++++ .../crew/domain/entity/CrewMembers.java | 39 ++++++++- .../ImpossibleWithdrawCrewException.java | 16 ++++ .../domain/exception/common/ErrorCode.java | 1 + .../in/presentation/rest/CrewController.java | 23 +++--- .../crew/common/fixture/CrewFixture.java | 21 +++++ .../com/retrip/crew/domain/CrewTripTest.java | 18 ++++ .../crew/domain/entity/CrewMembersTest.java | 82 +++++++++++++++++++ 11 files changed, 248 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/retrip/crew/application/in/request/crew/CrewWithdrawalRequest.java create mode 100644 src/main/java/com/retrip/crew/domain/CrewTrip.java create mode 100644 src/main/java/com/retrip/crew/domain/exception/ImpossibleWithdrawCrewException.java create mode 100644 src/test/java/com/retrip/crew/domain/CrewTripTest.java diff --git a/src/main/java/com/retrip/crew/application/in/CrewService.java b/src/main/java/com/retrip/crew/application/in/CrewService.java index 8df5b01..ae880c2 100644 --- a/src/main/java/com/retrip/crew/application/in/CrewService.java +++ b/src/main/java/com/retrip/crew/application/in/CrewService.java @@ -6,6 +6,7 @@ import com.retrip.crew.application.in.request.crew.CrewCreateRequest; import com.retrip.crew.application.in.request.crew.CrewOrder; import com.retrip.crew.application.in.request.crew.CrewUpdateRequest; +import com.retrip.crew.application.in.request.crew.CrewWithdrawalRequest; import com.retrip.crew.application.in.response.IntroductionCreateResponse; import com.retrip.crew.application.in.response.IntroductionDetailResponse; import com.retrip.crew.application.in.response.IntroductionListResponse; @@ -19,6 +20,7 @@ import com.retrip.crew.application.in.usecase.ManageIntroductionUseCase; import com.retrip.crew.application.out.repository.*; import com.retrip.crew.domain.entity.Crew; +import com.retrip.crew.domain.entity.CrewMembers; import com.retrip.crew.domain.entity.Introduction; import com.retrip.crew.domain.entity.Recruitment; import com.retrip.crew.domain.exception.CrewNotFoundException; @@ -136,4 +138,11 @@ public Introduction findIntroductionByIdAndCrewId(UUID introductionId, UUID crew return introductionRepository.findByIdAndCrewId(introductionId, crewId) .orElseThrow(IntroductionNotFoundException::new); } + + @Override + public void withdrawCrew(UUID crewId, CrewWithdrawalRequest request) { + Crew crew = findCrewById(crewId); + CrewMembers crewMembers = crew.getCrewMembers(); + crewMembers.withdraw(request.memberId(), request.participatingCrewTrips()); + } } diff --git a/src/main/java/com/retrip/crew/application/in/request/crew/CrewWithdrawalRequest.java b/src/main/java/com/retrip/crew/application/in/request/crew/CrewWithdrawalRequest.java new file mode 100644 index 0000000..b3e9d73 --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/request/crew/CrewWithdrawalRequest.java @@ -0,0 +1,17 @@ +package com.retrip.crew.application.in.request.crew; + +import com.retrip.crew.domain.CrewTrip; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; +import java.util.UUID; + +@Schema(description = "크루 탈퇴 Request") +public record CrewWithdrawalRequest( + @Schema(description = "크루원 ID") + UUID memberId, + + @Schema(description = "참여 중인 크루 여행 목록") + List participatingCrewTrips +) { +} diff --git a/src/main/java/com/retrip/crew/application/in/usecase/ManageCrewUseCase.java b/src/main/java/com/retrip/crew/application/in/usecase/ManageCrewUseCase.java index 6131c7f..411163c 100644 --- a/src/main/java/com/retrip/crew/application/in/usecase/ManageCrewUseCase.java +++ b/src/main/java/com/retrip/crew/application/in/usecase/ManageCrewUseCase.java @@ -2,6 +2,7 @@ import com.retrip.crew.application.in.request.crew.CrewCreateRequest; import com.retrip.crew.application.in.request.crew.CrewUpdateRequest; +import com.retrip.crew.application.in.request.crew.CrewWithdrawalRequest; import com.retrip.crew.application.in.response.crew.CrewCreateResponse; import com.retrip.crew.application.in.response.crew.CrewUpdateResponse; @@ -12,4 +13,5 @@ public interface ManageCrewUseCase { CrewUpdateResponse updateCrew(UUID crewId, CrewUpdateRequest request); + void withdrawCrew(UUID crewId, CrewWithdrawalRequest request); } diff --git a/src/main/java/com/retrip/crew/domain/CrewTrip.java b/src/main/java/com/retrip/crew/domain/CrewTrip.java new file mode 100644 index 0000000..38b62f5 --- /dev/null +++ b/src/main/java/com/retrip/crew/domain/CrewTrip.java @@ -0,0 +1,34 @@ +package com.retrip.crew.domain; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.UUID; + +import static com.retrip.crew.domain.CrewTrip.CrewTripType.EXCLUSION; + +@Schema(description = "크루 여행") +public record CrewTrip( + @Schema(description = "크루 ID") + UUID tripId, + + @Schema(description = "크루 여행 타입") + CrewTripType type +) { + public boolean isImpossibleWithdrawal() { + return this.type == EXCLUSION; + } + + @Getter + @AllArgsConstructor + public enum CrewTripType { + EXCLUSION("EXCLUSION", "크루원 전용"), + INCLUSION("INCLUSION", "크루원 포함"); + + private final String code; + private final String viewName; + } +} + + diff --git a/src/main/java/com/retrip/crew/domain/entity/CrewMembers.java b/src/main/java/com/retrip/crew/domain/entity/CrewMembers.java index eab4d38..ec0af21 100644 --- a/src/main/java/com/retrip/crew/domain/entity/CrewMembers.java +++ b/src/main/java/com/retrip/crew/domain/entity/CrewMembers.java @@ -1,5 +1,7 @@ package com.retrip.crew.domain.entity; +import com.retrip.crew.domain.CrewTrip; +import com.retrip.crew.domain.exception.ImpossibleWithdrawCrewException; import com.retrip.crew.domain.exception.common.IllegalStateException; import com.retrip.crew.domain.exception.common.InvalidValueException; import jakarta.persistence.CascadeType; @@ -8,6 +10,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.List; @@ -40,10 +43,7 @@ public int getSize() { } public boolean isLeader(UUID memberId) { - return this.values.stream() - .filter(m -> memberId.equals(m.getMemberId())) - .findFirst() - .orElseThrow(() -> new InvalidValueException("크루 멤버가 아닙니다.")) + return findMember(memberId) .isLeader(); } @@ -59,4 +59,35 @@ private boolean isDuplicate(Demand demand) { return this.values.stream() .anyMatch(m -> m.getMemberId().equals(demand.getMemberId())); } + + public void withdraw(UUID memberId, List participatingCrewTrips) { + validatePossibleWithdrawal(memberId, participatingCrewTrips); + CrewMember member = findMember(memberId); + this.values.remove(member); + } + + private void validatePossibleWithdrawal(UUID memberId, List crewTrips) { + if (isLeader(memberId)) { + throw new ImpossibleWithdrawCrewException("크루 리더는 탈퇴할 수 없습니다."); + } + + if (!CollectionUtils.isEmpty(crewTrips)) { + validateParticipatingCrewTrips(crewTrips); + } + } + + private void validateParticipatingCrewTrips(List crewTrips) { + boolean participatingExclusionCrewTrip = crewTrips.stream() + .anyMatch(CrewTrip::isImpossibleWithdrawal); + if (participatingExclusionCrewTrip) { + throw new ImpossibleWithdrawCrewException("참여 중인 크루 전용 여행이 있어 탈퇴할 수 없습니다."); + } + } + + private CrewMember findMember(UUID memberId) { + return this.values.stream() + .filter(m -> memberId.equals(m.getMemberId())) + .findFirst() + .orElseThrow(() -> new InvalidValueException("크루 멤버가 아닙니다.")); + } } diff --git a/src/main/java/com/retrip/crew/domain/exception/ImpossibleWithdrawCrewException.java b/src/main/java/com/retrip/crew/domain/exception/ImpossibleWithdrawCrewException.java new file mode 100644 index 0000000..51193ba --- /dev/null +++ b/src/main/java/com/retrip/crew/domain/exception/ImpossibleWithdrawCrewException.java @@ -0,0 +1,16 @@ +package com.retrip.crew.domain.exception; + +import com.retrip.crew.domain.exception.common.ErrorCode; +import com.retrip.crew.domain.exception.common.IllegalStateException; + +public class ImpossibleWithdrawCrewException extends IllegalStateException { + private static final ErrorCode errorCode = ErrorCode.IMPOSSIBLE_WITHDRAW_CREW; + + public ImpossibleWithdrawCrewException() { + super(errorCode); + } + + public ImpossibleWithdrawCrewException(String message) { + super(errorCode, message); + } +} diff --git a/src/main/java/com/retrip/crew/domain/exception/common/ErrorCode.java b/src/main/java/com/retrip/crew/domain/exception/common/ErrorCode.java index bea0b8c..c4027ba 100644 --- a/src/main/java/com/retrip/crew/domain/exception/common/ErrorCode.java +++ b/src/main/java/com/retrip/crew/domain/exception/common/ErrorCode.java @@ -20,6 +20,7 @@ public enum ErrorCode { ILLEGAL_DEMAND_STATE(BAD_REQUEST, "Crew-004", "이미 크루 참여를 요청한 사용자는 다시 요청할 수 없습니다."), INTRODUCTION_NOT_FOUND(BAD_REQUEST, "Crew-005", "크루 자기소개 엔티티를 찾을 수 없습니다."), INVALID_ACCESS(FORBIDDEN, "Crew-006","접근 권한이 존재하지 않습니다."), + IMPOSSIBLE_WITHDRAW_CREW(FORBIDDEN, "Crew-007","크루에서 탈퇴할 수 없습니다."), ; private final HttpStatus status; diff --git a/src/main/java/com/retrip/crew/infra/adapter/in/presentation/rest/CrewController.java b/src/main/java/com/retrip/crew/infra/adapter/in/presentation/rest/CrewController.java index cc3c6b4..a3a1f5b 100644 --- a/src/main/java/com/retrip/crew/infra/adapter/in/presentation/rest/CrewController.java +++ b/src/main/java/com/retrip/crew/infra/adapter/in/presentation/rest/CrewController.java @@ -6,6 +6,7 @@ import com.retrip.crew.application.in.request.crew.CrewCreateRequest; import com.retrip.crew.application.in.request.crew.CrewOrder; import com.retrip.crew.application.in.request.crew.CrewUpdateRequest; +import com.retrip.crew.application.in.request.crew.CrewWithdrawalRequest; import com.retrip.crew.application.in.response.IntroductionCreateResponse; import com.retrip.crew.application.in.response.IntroductionDetailResponse; import com.retrip.crew.application.in.response.IntroductionListResponse; @@ -21,20 +22,13 @@ import com.retrip.crew.infra.adapter.in.presentation.rest.common.ScrollPageResponse; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.UUID; @RequiredArgsConstructor @RequestMapping("/crews") @@ -129,4 +123,13 @@ public ApiResponse> getIntroduction ScrollPageResponse response = manageIntroductionUseCase.getIntroductions(crewId, pageable); return ApiResponse.ok(response); } + + @Schema(description = "크루 탈퇴") + @PutMapping("/{crewId}/withdrawal") + public ApiResponse withdrawCrew( + @PathVariable final UUID crewId, + @RequestBody CrewWithdrawalRequest request) { + manageCrewUseCase.withdrawCrew(crewId, request); + return ApiResponse.noContent(); + } } diff --git a/src/test/java/com/retrip/crew/common/fixture/CrewFixture.java b/src/test/java/com/retrip/crew/common/fixture/CrewFixture.java index e634962..7c59f4c 100644 --- a/src/test/java/com/retrip/crew/common/fixture/CrewFixture.java +++ b/src/test/java/com/retrip/crew/common/fixture/CrewFixture.java @@ -7,12 +7,14 @@ import com.retrip.crew.domain.entity.CrewMembers; import org.springframework.test.util.ReflectionTestUtils; +import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.IntStream; public abstract class CrewFixture { + public static final UUID TRIP_ID = UUID.fromString("b56d1d03-894a-4fe5-afbe-19be496bc1b4"); public static final UUID LEADER_ID = UUID.fromString("caec62d1-f29d-477d-9743-292f48cc66bb"); public static final UUID MEMBER_ID = UUID.fromString("85e05380-3693-4f3f-b74b-203715d15df8"); public static final UUID 정수_ID = UUID.fromString("a7f7215b-081a-42f4-b3e2-f06393de2f8b"); @@ -48,6 +50,25 @@ public static Crew createCrew(UUID leaderId) { leaderId); } + public static Crew createCrewWithMutableMembers(UUID leaderId) { + Crew crew = Crew.create( + "속초 크루원 구함", + "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", + 100, + leaderId); + List crewMemberList = new ArrayList<>(5); + crewMemberList.add(new CrewMember(crew, leaderId, CrewMemberRole.LEADER)); + crewMemberList.add(new CrewMember(crew, 정수_ID, CrewMemberRole.PARTICIPANT)); + crewMemberList.add(new CrewMember(crew, 홍석_ID, CrewMemberRole.PARTICIPANT)); + crewMemberList.add(new CrewMember(crew, 준호_ID, CrewMemberRole.PARTICIPANT)); + crewMemberList.add(new CrewMember(crew, 지수_ID, CrewMemberRole.PARTICIPANT)); + crewMemberList.add(new CrewMember(crew, 혁진_ID, CrewMemberRole.PARTICIPANT)); + CrewMembers crewMembers = new CrewMembers(crew, leaderId); + ReflectionTestUtils.setField(crewMembers, "values", crewMemberList); + ReflectionTestUtils.setField(crew, "crewMembers", crewMembers); + return crew; + } + public static Crew createCrewWithMembers(UUID leaderId) { Crew crew = Crew.create( "속초 크루원 구함", diff --git a/src/test/java/com/retrip/crew/domain/CrewTripTest.java b/src/test/java/com/retrip/crew/domain/CrewTripTest.java new file mode 100644 index 0000000..5ad3f92 --- /dev/null +++ b/src/test/java/com/retrip/crew/domain/CrewTripTest.java @@ -0,0 +1,18 @@ +package com.retrip.crew.domain; + +import com.retrip.crew.common.fixture.CrewFixture; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class CrewTripTest { + + @ParameterizedTest + @CsvSource({"EXCLUSION,true", "INCLUSION,false"}) + void 크루_여행_타입이_크루원_전용이면_ture_크루원_포함이면_false를_반환한다(CrewTrip.CrewTripType type, boolean expected) { + CrewTrip crewTrip = new CrewTrip(CrewFixture.TRIP_ID, type); + boolean actual = crewTrip.isImpossibleWithdrawal(); + assertThat(actual).isEqualTo(expected); + } +} diff --git a/src/test/java/com/retrip/crew/domain/entity/CrewMembersTest.java b/src/test/java/com/retrip/crew/domain/entity/CrewMembersTest.java index b9f0c1d..741495f 100644 --- a/src/test/java/com/retrip/crew/domain/entity/CrewMembersTest.java +++ b/src/test/java/com/retrip/crew/domain/entity/CrewMembersTest.java @@ -1,12 +1,20 @@ package com.retrip.crew.domain.entity; +import com.retrip.crew.domain.CrewTrip; +import com.retrip.crew.domain.exception.ImpossibleWithdrawCrewException; import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.UUID; import static com.retrip.crew.common.fixture.CrewFixture.*; +import static com.retrip.crew.domain.CrewTrip.CrewTripType.EXCLUSION; +import static com.retrip.crew.domain.CrewTrip.CrewTripType.INCLUSION; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; class CrewMembersTest { @@ -52,4 +60,78 @@ class CrewMembersTest { () -> assertThat(ids.contains(정수_ID)).isTrue() ); } + + @Test + void 참여중인_크루_여행이_없으면_크루원은_탈퇴된다() { + // given + Crew crew = createCrew(LEADER_ID); + CrewMember member = new CrewMember(crew, 홍석_ID, CrewMemberRole.PARTICIPANT); + CrewMembers crewMembers = new CrewMembers(crew, LEADER_ID); + + List crewMemberList = new ArrayList<>(5); + crewMemberList.add(member); + crewMemberList.add(new CrewMember(crew, 정수_ID, CrewMemberRole.PARTICIPANT)); + crewMemberList.add(new CrewMember(crew, 준호_ID, CrewMemberRole.PARTICIPANT)); + crewMemberList.add(new CrewMember(crew, 지수_ID, CrewMemberRole.PARTICIPANT)); + crewMemberList.add(new CrewMember(crew, 혁진_ID, CrewMemberRole.PARTICIPANT)); + + ReflectionTestUtils.setField(crewMembers, "values", crewMemberList); + ReflectionTestUtils.setField(crew, "crewMembers", crewMembers); + + List crewTrips = Collections.emptyList(); + + // when + crewMembers.withdraw(홍석_ID, crewTrips); + + // then + assertThat(crewMembers.getValues().contains(member)).isFalse(); + } + + @Test + void 크루_리더가_탈퇴를_요청하면_예외가_발생한다() { + // given + Crew crew = createCrewWithMembers(LEADER_ID); + List crewTrips = List.of(new CrewTrip(TRIP_ID, EXCLUSION)); + + // when, then + assertThatThrownBy(() -> crew.getCrewMembers().withdraw(LEADER_ID, crewTrips)) + .isExactlyInstanceOf(ImpossibleWithdrawCrewException.class); + } + + @Test + void 크루_여행_타입이_크루원_전용인_여행에_참여중이면_예외가_발생한다() { + // given + Crew crew = createCrewWithMembers(LEADER_ID); + List crewTrips = List.of(new CrewTrip(TRIP_ID, EXCLUSION)); + + // when, then + assertThatThrownBy(() -> crew.getCrewMembers().withdraw(홍석_ID, crewTrips)) + .isExactlyInstanceOf(ImpossibleWithdrawCrewException.class); + } + + @Test + void 크루_여행_타입이_크루원_포함인_여행에_참여중이면_크루원이_탈퇴된다() { + // given + Crew crew = createCrew(LEADER_ID); + CrewMember member = new CrewMember(crew, 홍석_ID, CrewMemberRole.PARTICIPANT); + CrewMembers crewMembers = new CrewMembers(crew, LEADER_ID); + + List crewMemberList = new ArrayList<>(5); + crewMemberList.add(member); + crewMemberList.add(new CrewMember(crew, 정수_ID, CrewMemberRole.PARTICIPANT)); + crewMemberList.add(new CrewMember(crew, 준호_ID, CrewMemberRole.PARTICIPANT)); + crewMemberList.add(new CrewMember(crew, 지수_ID, CrewMemberRole.PARTICIPANT)); + crewMemberList.add(new CrewMember(crew, 혁진_ID, CrewMemberRole.PARTICIPANT)); + + ReflectionTestUtils.setField(crewMembers, "values", crewMemberList); + ReflectionTestUtils.setField(crew, "crewMembers", crewMembers); + + List crewTrips = List.of(new CrewTrip(TRIP_ID, INCLUSION)); + + // when + crewMembers.withdraw(홍석_ID, crewTrips); + + // then + assertThat(crewMembers.getValues().contains(member)).isFalse(); + } } From 468262dcf90bc16a16d220f1b43f48c1ab75376a Mon Sep 17 00:00:00 2001 From: mandykr Date: Sun, 27 Apr 2025 16:44:04 +0900 Subject: [PATCH 4/4] =?UTF-8?q?chore:=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/retrip/crew/domain/CrewTrip.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/com/retrip/crew/domain/CrewTrip.java b/src/main/java/com/retrip/crew/domain/CrewTrip.java index 38b62f5..31852a1 100644 --- a/src/main/java/com/retrip/crew/domain/CrewTrip.java +++ b/src/main/java/com/retrip/crew/domain/CrewTrip.java @@ -1,6 +1,5 @@ package com.retrip.crew.domain; -import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Getter; @@ -8,12 +7,8 @@ import static com.retrip.crew.domain.CrewTrip.CrewTripType.EXCLUSION; -@Schema(description = "크루 여행") public record CrewTrip( - @Schema(description = "크루 ID") UUID tripId, - - @Schema(description = "크루 여행 타입") CrewTripType type ) { public boolean isImpossibleWithdrawal() {