From c27a816c99035e2e63db96123937e125c4a577fb Mon Sep 17 00:00:00 2001 From: mandykr Date: Sat, 8 Mar 2025 14:10:34 +0900 Subject: [PATCH 1/8] fix: conflict repair --- .../in/request/CrewCreateRequest.java | 8 +++--- .../com/retrip/crew/domain/entity/Crew.java | 16 ++++++----- .../crew/domain/entity/CrewMembers.java | 5 +++- .../crew/domain/entity/Recruitment.java | 23 ++++++++++++++++ .../crew/domain/entity/Subscription.java | 27 +++++++++++++++++++ .../com/retrip/crew/CrewApplicationTests.java | 17 +----------- .../retrip/crew/domain/entity/CrewTest.java | 10 +++---- .../crew/domain/entity/RecruitmentTest.java | 13 +++++++++ 8 files changed, 88 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/retrip/crew/domain/entity/Recruitment.java create mode 100644 src/main/java/com/retrip/crew/domain/entity/Subscription.java create mode 100644 src/test/java/com/retrip/crew/domain/entity/RecruitmentTest.java diff --git a/src/main/java/com/retrip/crew/application/in/request/CrewCreateRequest.java b/src/main/java/com/retrip/crew/application/in/request/CrewCreateRequest.java index 0ee1e93..145bd3a 100644 --- a/src/main/java/com/retrip/crew/application/in/request/CrewCreateRequest.java +++ b/src/main/java/com/retrip/crew/application/in/request/CrewCreateRequest.java @@ -2,7 +2,6 @@ import com.retrip.crew.domain.entity.Crew; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Size; import java.util.UUID; @@ -11,14 +10,17 @@ public record CrewCreateRequest( @Schema(description = "리더 ID") UUID leader, + @Schema(description = "크루 타이틀") @Size(min = 1, max = 30) String title, + @Schema(description = "크루 설명") @Size(min = 1, max = 500) String description, - @Schema(description = "크루 최대 인원") - @Min(1) + + @Schema(description = "크루 최대 인원수") + @Size(min = 5, max = 1000) int maxMembers ){ diff --git a/src/main/java/com/retrip/crew/domain/entity/Crew.java b/src/main/java/com/retrip/crew/domain/entity/Crew.java index f5dc7c9..aa80d39 100644 --- a/src/main/java/com/retrip/crew/domain/entity/Crew.java +++ b/src/main/java/com/retrip/crew/domain/entity/Crew.java @@ -10,24 +10,28 @@ import java.util.UUID; @Entity -@Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter public class Crew extends BaseEntity { @Id @Column(columnDefinition = "varbinary(16)") + @Getter private UUID id; + @Version + private long version; + @Embedded private CrewTitle title; @Embedded + @Getter private CrewDescription description; + @Embedded private CrewMembers crewMembers; - @Column(name = "max_members", nullable = false) - private int maxMembers; @Embedded private Posts posts; @@ -38,14 +42,14 @@ public class Crew extends BaseEntity { @Embedded private Introductions introductions; - @Version - private long version; + @Embedded + private Recruitment recruitment; private Crew(String name, String description, int maxMembers, UUID leader) { this.id = UUID.randomUUID(); this.title = new CrewTitle(name); this.description = new CrewDescription(description); - this.maxMembers = maxMembers; + this.recruitment = new Recruitment(maxMembers); this.crewMembers = new CrewMembers(this, leader); this.posts = new Posts(); this.announcements = new Announcements(); 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 90feba1..9d71eb2 100644 --- a/src/main/java/com/retrip/crew/domain/entity/CrewMembers.java +++ b/src/main/java/com/retrip/crew/domain/entity/CrewMembers.java @@ -27,6 +27,9 @@ private List createLeader(Crew crew, UUID memberId) { } public CrewMember getLeader() { - return this.values.stream().filter(it -> it.getCrewMemberRole() == CrewMemberRole.LEADER).findFirst().orElse(null); + return this.values.stream() + .filter(it -> it.getCrewMemberRole() == CrewMemberRole.LEADER) + .findFirst() + .orElse(null); } } diff --git a/src/main/java/com/retrip/crew/domain/entity/Recruitment.java b/src/main/java/com/retrip/crew/domain/entity/Recruitment.java new file mode 100644 index 0000000..78ab12d --- /dev/null +++ b/src/main/java/com/retrip/crew/domain/entity/Recruitment.java @@ -0,0 +1,23 @@ +package com.retrip.crew.domain.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED, force = true) +@Embeddable +public class Recruitment { + private int maxMembers; + + @OneToMany(mappedBy = "crew", cascade = CascadeType.ALL, orphanRemoval = true) + private List subscriptions = new ArrayList<>(); + + public Recruitment(int maxMembers) { + this.maxMembers = maxMembers; + } +} diff --git a/src/main/java/com/retrip/crew/domain/entity/Subscription.java b/src/main/java/com/retrip/crew/domain/entity/Subscription.java new file mode 100644 index 0000000..bdcf846 --- /dev/null +++ b/src/main/java/com/retrip/crew/domain/entity/Subscription.java @@ -0,0 +1,27 @@ +package com.retrip.crew.domain.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.UUID; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED, force = true) +@Getter +public class Subscription { + @Id + @Column(columnDefinition = "varbinary(16)") + private UUID id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "crew_id", + nullable = false, + columnDefinition = "varbinary(16)", + foreignKey = @ForeignKey(name = "fk_subscription_to_crew") + ) + private Crew crew; + +} diff --git a/src/test/java/com/retrip/crew/CrewApplicationTests.java b/src/test/java/com/retrip/crew/CrewApplicationTests.java index 7ecfcc6..f5ca28b 100644 --- a/src/test/java/com/retrip/crew/CrewApplicationTests.java +++ b/src/test/java/com/retrip/crew/CrewApplicationTests.java @@ -1,23 +1,8 @@ package com.retrip.crew; -import com.retrip.crew.domain.entity.Crew; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThatCode; - class CrewApplicationTests { - @DisplayName("제목을 입력해 크루를 생성할 수 있다.") @Test - void create() { - assertThatCode(() -> Crew.create( - "인천 크루 모집", - "인천 크루원을 모집합니다.\n 가입나이: 20~29살 \n 금지 사항\n - 연애 금지\n - 만남 당일 취소 금지\n - 사적 연락 금지", - 4, - UUID.randomUUID() - )).doesNotThrowAnyException(); - } - + void test() {} } diff --git a/src/test/java/com/retrip/crew/domain/entity/CrewTest.java b/src/test/java/com/retrip/crew/domain/entity/CrewTest.java index 187e9a7..de0cfa5 100644 --- a/src/test/java/com/retrip/crew/domain/entity/CrewTest.java +++ b/src/test/java/com/retrip/crew/domain/entity/CrewTest.java @@ -1,6 +1,5 @@ package com.retrip.crew.domain.entity; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.UUID; @@ -8,13 +7,14 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; class CrewTest { - @Test - public void 크루_생성_테스트() { + public void 크루를_생성한다() { assertThatCode(() -> Crew.create( "속초 크루원 구함", "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", - 4, - UUID.randomUUID())).doesNotThrowAnyException(); + 100, + UUID.randomUUID()) + ).doesNotThrowAnyException(); } + } diff --git a/src/test/java/com/retrip/crew/domain/entity/RecruitmentTest.java b/src/test/java/com/retrip/crew/domain/entity/RecruitmentTest.java new file mode 100644 index 0000000..298ebf5 --- /dev/null +++ b/src/test/java/com/retrip/crew/domain/entity/RecruitmentTest.java @@ -0,0 +1,13 @@ +package com.retrip.crew.domain.entity; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; + +class RecruitmentTest { + @Test + void 모집을_생성한다() { + assertThatCode(() -> new Recruitment(100)) + .doesNotThrowAnyException(); + } +} From d704fcef834ff150081e8cd70521c43d03406838 Mon Sep 17 00:00:00 2001 From: mandykr Date: Sat, 8 Mar 2025 15:11:10 +0900 Subject: [PATCH 2/8] fix: conflict repair --- .../crew/application/in/CrewService.java | 35 +++++++------ .../ChangeRecruitmentStatusResponse.java | 19 +++++++ .../in/response/CrewCreateResponse.java | 12 ++++- .../in/usecase/UpdateRecruitmentUseCase.java | 11 +++++ .../com/retrip/crew/domain/entity/Crew.java | 12 +++-- .../crew/domain/entity/Recruitment.java | 13 +++++ .../crew/domain/vo/RecruitmentStatus.java | 24 +++++++++ .../in/presentation/rest/CrewController.java | 29 ++++++----- .../retrip/crew/domain/entity/CrewTest.java | 49 ++++++++++++++++++- 9 files changed, 171 insertions(+), 33 deletions(-) create mode 100644 src/main/java/com/retrip/crew/application/in/response/ChangeRecruitmentStatusResponse.java create mode 100644 src/main/java/com/retrip/crew/application/in/usecase/UpdateRecruitmentUseCase.java create mode 100644 src/main/java/com/retrip/crew/domain/vo/RecruitmentStatus.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 0f56285..09f1ad0 100644 --- a/src/main/java/com/retrip/crew/application/in/CrewService.java +++ b/src/main/java/com/retrip/crew/application/in/CrewService.java @@ -1,30 +1,18 @@ package com.retrip.crew.application.in; import com.retrip.crew.application.in.request.CrewCreateRequest; -import com.retrip.crew.application.in.request.CrewOrder; import com.retrip.crew.application.in.response.CrewCreateResponse; -import com.retrip.crew.application.in.response.CrewDetailResponse; -import com.retrip.crew.application.in.response.CrewListResponse; import com.retrip.crew.application.in.usecase.CreateCrewUseCase; -import com.retrip.crew.application.in.usecase.GetCrewUseCase; -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.domain.entity.Crew; -import com.retrip.crew.domain.exception.CrewNotFoundException; -import com.retrip.crew.infra.adapter.in.presentation.rest.common.ScrollPageResponse; -import com.retrip.crew.infra.util.PaginationUtils; -import java.util.UUID; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; @Service @Transactional @RequiredArgsConstructor -public class CrewService implements CreateCrewUseCase, GetCrewUseCase { +public class CrewService implements CreateCrewUseCase, UpdateRecruitmentUseCase, GetCrewUseCase { private final CrewRepository crewRepository; private final CrewMemberRepository crewMemberRepository; private final CrewQueryRepository crewQueryRepository; @@ -36,6 +24,25 @@ public CrewCreateResponse createCrew(CrewCreateRequest request) { return CrewCreateResponse.of(crew); } + @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); + } + + private Crew findById(UUID crewId){ + return crewRepository.findById(crewId) + .orElseThrow(CrewNotFoundException::new); + } + @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/response/ChangeRecruitmentStatusResponse.java b/src/main/java/com/retrip/crew/application/in/response/ChangeRecruitmentStatusResponse.java new file mode 100644 index 0000000..0a56b73 --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/response/ChangeRecruitmentStatusResponse.java @@ -0,0 +1,19 @@ +package com.retrip.crew.application.in.response; + +import com.retrip.crew.domain.entity.Crew; +import com.retrip.crew.domain.vo.RecruitmentStatus; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.UUID; + +public record ChangeRecruitmentStatusResponse( + @Schema(description = "크루 ID") + UUID id, + + @Schema(description = "모집 상태") + RecruitmentStatus status +) { + public static ChangeRecruitmentStatusResponse of(Crew crew) { + return new ChangeRecruitmentStatusResponse(crew.getId(), crew.getRecruitment().getStatus()); + } +} diff --git a/src/main/java/com/retrip/crew/application/in/response/CrewCreateResponse.java b/src/main/java/com/retrip/crew/application/in/response/CrewCreateResponse.java index cdb54e9..a81cced 100644 --- a/src/main/java/com/retrip/crew/application/in/response/CrewCreateResponse.java +++ b/src/main/java/com/retrip/crew/application/in/response/CrewCreateResponse.java @@ -1,6 +1,7 @@ package com.retrip.crew.application.in.response; import com.retrip.crew.domain.entity.Crew; +import com.retrip.crew.domain.vo.RecruitmentStatus; import io.swagger.v3.oas.annotations.media.Schema; import java.util.UUID; @@ -9,19 +10,26 @@ public record CrewCreateResponse( @Schema(description = "크루 ID") UUID id, + @Schema(description = "크루 타이틀") String title, + @Schema(description = "크루 설명") String description, + @Schema(description = "리더 ID") - UUID leaderId + UUID leaderId, + + @Schema(description = "모집 상태") + RecruitmentStatus status ) { public static CrewCreateResponse of(Crew crew) { return new CrewCreateResponse( crew.getId(), crew.getTitle().getValue(), crew.getDescription(), - crew.getLeader().getId() + crew.getLeader().getId(), + crew.getRecruitment().getStatus() ); } } diff --git a/src/main/java/com/retrip/crew/application/in/usecase/UpdateRecruitmentUseCase.java b/src/main/java/com/retrip/crew/application/in/usecase/UpdateRecruitmentUseCase.java new file mode 100644 index 0000000..f96e831 --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/usecase/UpdateRecruitmentUseCase.java @@ -0,0 +1,11 @@ +package com.retrip.crew.application.in.usecase; + +import com.retrip.crew.application.in.response.ChangeRecruitmentStatusResponse; + +import java.util.UUID; + +public interface UpdateRecruitmentUseCase { + ChangeRecruitmentStatusResponse startRecruitment(UUID crewId); + + ChangeRecruitmentStatusResponse stopRecruitment(UUID crewId); +} diff --git a/src/main/java/com/retrip/crew/domain/entity/Crew.java b/src/main/java/com/retrip/crew/domain/entity/Crew.java index aa80d39..1d74a9a 100644 --- a/src/main/java/com/retrip/crew/domain/entity/Crew.java +++ b/src/main/java/com/retrip/crew/domain/entity/Crew.java @@ -15,7 +15,6 @@ public class Crew extends BaseEntity { @Id @Column(columnDefinition = "varbinary(16)") - @Getter private UUID id; @Version @@ -25,14 +24,11 @@ public class Crew extends BaseEntity { private CrewTitle title; @Embedded - @Getter private CrewDescription description; - @Embedded private CrewMembers crewMembers; - @Embedded private Posts posts; @@ -64,6 +60,14 @@ public CrewMember getLeader() { return crewMembers.getLeader(); } + public void startRecruitment() { + this.recruitment.start(); + } + + public void stopRecruitment() { + this.recruitment.stop(); + } + public String getDescription(){ return description.getValue(); } diff --git a/src/main/java/com/retrip/crew/domain/entity/Recruitment.java b/src/main/java/com/retrip/crew/domain/entity/Recruitment.java index 78ab12d..366e90e 100644 --- a/src/main/java/com/retrip/crew/domain/entity/Recruitment.java +++ b/src/main/java/com/retrip/crew/domain/entity/Recruitment.java @@ -1,5 +1,6 @@ package com.retrip.crew.domain.entity; +import com.retrip.crew.domain.vo.RecruitmentStatus; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; @@ -8,16 +9,28 @@ import java.util.ArrayList; import java.util.List; +import static com.retrip.crew.domain.vo.RecruitmentStatus.*; + @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED, force = true) @Embeddable public class Recruitment { private int maxMembers; + private RecruitmentStatus status; @OneToMany(mappedBy = "crew", cascade = CascadeType.ALL, orphanRemoval = true) private List subscriptions = new ArrayList<>(); public Recruitment(int maxMembers) { this.maxMembers = maxMembers; + this.status = RECRUITING; + } + + public void start() { + this.status = RECRUITING; + } + + public void stop() { + this.status = STOPPED; } } diff --git a/src/main/java/com/retrip/crew/domain/vo/RecruitmentStatus.java b/src/main/java/com/retrip/crew/domain/vo/RecruitmentStatus.java new file mode 100644 index 0000000..7c11d80 --- /dev/null +++ b/src/main/java/com/retrip/crew/domain/vo/RecruitmentStatus.java @@ -0,0 +1,24 @@ +package com.retrip.crew.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +@Getter +@AllArgsConstructor +public enum RecruitmentStatus { + RECRUITING("RECRUITING", "모집 중"), + STOPPED("RECRUITMENT_CLOSED", "모집 중지") + ; + + private final String code; + private final String viewName; + + public static RecruitmentStatus codeOf(String code) { + return Arrays.stream(RecruitmentStatus.values()) + .filter(tripStatus -> tripStatus.getCode().equals(code)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 코드입니다.")); + } +} 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 cea499d..13469b4 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 @@ -1,20 +1,10 @@ package com.retrip.crew.infra.adapter.in.presentation.rest; import com.retrip.crew.application.in.request.CrewCreateRequest; -import com.retrip.crew.application.in.request.CrewOrder; import com.retrip.crew.application.in.response.CrewCreateResponse; -import com.retrip.crew.application.in.response.CrewDetailResponse; -import com.retrip.crew.application.in.response.CrewListResponse; import com.retrip.crew.application.in.usecase.CreateCrewUseCase; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; -import com.retrip.crew.application.in.usecase.GetCrewUseCase; -import com.retrip.crew.infra.adapter.in.presentation.rest.common.ApiResponse; -import com.retrip.crew.infra.adapter.in.presentation.rest.common.ScrollPageResponse; -import java.util.UUID; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -27,13 +17,28 @@ @Tag(name = "Crew", description = "크루 서비스") public class CrewController { private final CreateCrewUseCase createCrewUseCase; + private final UpdateRecruitmentUseCase updateRecruitmentUseCase; private final GetCrewUseCase getCrewUseCase; @PostMapping @Schema(description = "크루 생성") - public ResponseEntity createCrew(@RequestBody CrewCreateRequest request) { + public ApiResponse createCrew(@RequestBody CrewCreateRequest request) { CrewCreateResponse crew = createCrewUseCase.createCrew(request); - return ResponseEntity.created(URI.create("/crews/" + crew.id())).body(crew); + return ApiResponse.created(crew); + } + + @PutMapping("/{crewId}/recruitments/start") + @Schema(description = "크루 모집 시작") + public ApiResponse startRecruitment(@PathVariable final UUID crewId) { + ChangeRecruitmentStatusResponse recruitment = updateRecruitmentUseCase.startRecruitment(crewId); + return ApiResponse.ok(recruitment); + } + + @PutMapping("/{crewId}/recruitments/stop") + @Schema(description = "크루 모집 중지") + public ApiResponse stopRecruitment(@PathVariable final UUID crewId) { + ChangeRecruitmentStatusResponse recruitment = updateRecruitmentUseCase.stopRecruitment(crewId); + return ApiResponse.ok(recruitment); } @GetMapping diff --git a/src/test/java/com/retrip/crew/domain/entity/CrewTest.java b/src/test/java/com/retrip/crew/domain/entity/CrewTest.java index de0cfa5..726bae2 100644 --- a/src/test/java/com/retrip/crew/domain/entity/CrewTest.java +++ b/src/test/java/com/retrip/crew/domain/entity/CrewTest.java @@ -1,14 +1,16 @@ package com.retrip.crew.domain.entity; +import com.retrip.crew.domain.vo.RecruitmentStatus; import org.junit.jupiter.api.Test; import java.util.UUID; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; class CrewTest { @Test - public void 크루를_생성한다() { + void 크루를_생성한다() { assertThatCode(() -> Crew.create( "속초 크루원 구함", "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", @@ -17,4 +19,49 @@ class CrewTest { ).doesNotThrowAnyException(); } + @Test + void 크루가_생성되면_모집을_시작한다() { + // given, when + Crew crew = Crew.create( + "속초 크루원 구함", + "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", + 100, + UUID.randomUUID()); + + // then + assertThat(crew.getRecruitment().getStatus()).isEqualTo(RecruitmentStatus.RECRUITING); + } + + @Test + void 크루_모집을_중지한다() { + // given + Crew crew = Crew.create( + "속초 크루원 구함", + "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", + 100, + UUID.randomUUID()); + + // when + crew.stopRecruitment(); + + // then + assertThat(crew.getRecruitment().getStatus()).isEqualTo(RecruitmentStatus.STOPPED); + } + + @Test + void 크루_모집을_시작한다() { + // given + Crew crew = Crew.create( + "속초 크루원 구함", + "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", + 100, + UUID.randomUUID()); + crew.stopRecruitment(); + + // when + crew.startRecruitment(); + + // then + assertThat(crew.getRecruitment().getStatus()).isEqualTo(RecruitmentStatus.RECRUITING); + } } From b7ad288fdb389b2d45b90a3462828dcc4f91502c Mon Sep 17 00:00:00 2001 From: mandykr Date: Sat, 8 Mar 2025 15:58:45 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=ED=81=AC=EB=A3=A8=20=EB=AA=A8?= =?UTF-8?q?=EC=A7=91=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD=EC=8B=9C=20?= =?UTF-8?q?=EC=B5=9C=EB=8C=80=20=EC=9D=B8=EC=9B=90=EC=88=98=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=B6=94=EA=B0=80=20-=20IllegalStateException=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20-=20common=20exception=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/retrip/crew/domain/entity/Crew.java | 3 +- .../crew/domain/entity/Recruitment.java | 11 ++++++- .../exception/common/BusinessException.java | 5 +++ .../common/EntityNotFoundException.java | 5 +++ .../domain/exception/common/ErrorCode.java | 1 + .../common/IllegalStateException.java | 21 ++++++++++++ .../common/InvalidValueException.java | 4 +++ .../retrip/crew/domain/entity/CrewTest.java | 32 +++++++++++++++++-- 8 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/retrip/crew/domain/exception/common/IllegalStateException.java diff --git a/src/main/java/com/retrip/crew/domain/entity/Crew.java b/src/main/java/com/retrip/crew/domain/entity/Crew.java index 1d74a9a..645af90 100644 --- a/src/main/java/com/retrip/crew/domain/entity/Crew.java +++ b/src/main/java/com/retrip/crew/domain/entity/Crew.java @@ -61,7 +61,8 @@ public CrewMember getLeader() { } public void startRecruitment() { - this.recruitment.start(); + int membersSize = crewMembers.getValues().size(); + this.recruitment.start(membersSize); } public void stopRecruitment() { diff --git a/src/main/java/com/retrip/crew/domain/entity/Recruitment.java b/src/main/java/com/retrip/crew/domain/entity/Recruitment.java index 366e90e..919a526 100644 --- a/src/main/java/com/retrip/crew/domain/entity/Recruitment.java +++ b/src/main/java/com/retrip/crew/domain/entity/Recruitment.java @@ -1,5 +1,6 @@ package com.retrip.crew.domain.entity; +import com.retrip.crew.domain.exception.common.IllegalStateException; import com.retrip.crew.domain.vo.RecruitmentStatus; import jakarta.persistence.*; import lombok.AccessLevel; @@ -26,11 +27,19 @@ public Recruitment(int maxMembers) { this.status = RECRUITING; } - public void start() { + public void start(int membersSize) { + if (isRecruitmentComplete(membersSize)) { + stop(); + throw new IllegalStateException("최대 인원을 모두 모집 완료하여 더 이상 멤버를 모집할 수 없습니다."); + } this.status = RECRUITING; } public void stop() { this.status = STOPPED; } + + private boolean isRecruitmentComplete(int membersSize) { + return this.maxMembers == membersSize; + } } diff --git a/src/main/java/com/retrip/crew/domain/exception/common/BusinessException.java b/src/main/java/com/retrip/crew/domain/exception/common/BusinessException.java index ea05e90..5b4711d 100644 --- a/src/main/java/com/retrip/crew/domain/exception/common/BusinessException.java +++ b/src/main/java/com/retrip/crew/domain/exception/common/BusinessException.java @@ -6,6 +6,11 @@ public class BusinessException extends RuntimeException { private final ErrorCode errorCode; + public BusinessException() { + super(ErrorCode.SERVER_ERROR.getMessage()); + this.errorCode = ErrorCode.SERVER_ERROR; + } + public BusinessException(ErrorCode errorCode) { super(errorCode.getMessage()); this.errorCode = errorCode; diff --git a/src/main/java/com/retrip/crew/domain/exception/common/EntityNotFoundException.java b/src/main/java/com/retrip/crew/domain/exception/common/EntityNotFoundException.java index 2c6d127..359e1f9 100644 --- a/src/main/java/com/retrip/crew/domain/exception/common/EntityNotFoundException.java +++ b/src/main/java/com/retrip/crew/domain/exception/common/EntityNotFoundException.java @@ -2,6 +2,11 @@ public class EntityNotFoundException extends BusinessException { private static final ErrorCode errorCode = ErrorCode.ENTITY_NOT_FOUND; + + public EntityNotFoundException() { + super(errorCode); + } + public EntityNotFoundException(ErrorCode errorCode) { super(errorCode); } 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 520a72c..4d05d8e 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 @@ -11,6 +11,7 @@ public enum ErrorCode { INVALID_INPUT_VALUE(BAD_REQUEST, "Common-002", "Invalid input value"), HANDLE_ACCESS_DENIED(FORBIDDEN, "Common-003", "Access is denied"), ENTITY_NOT_FOUND(BAD_REQUEST, "Common-004", "Entity not found"), + ILLEGAL_STATE(BAD_REQUEST, "Common-005", "Illegal state"), CREW_NOT_FOUND(BAD_REQUEST, "Crew-001", "크루 엔티티를 찾을 수 없습니다.") ; diff --git a/src/main/java/com/retrip/crew/domain/exception/common/IllegalStateException.java b/src/main/java/com/retrip/crew/domain/exception/common/IllegalStateException.java new file mode 100644 index 0000000..f7f1889 --- /dev/null +++ b/src/main/java/com/retrip/crew/domain/exception/common/IllegalStateException.java @@ -0,0 +1,21 @@ +package com.retrip.crew.domain.exception.common; + +public class IllegalStateException extends BusinessException { + private static final ErrorCode errorCode = ErrorCode.ILLEGAL_STATE; + + public IllegalStateException() { + super(errorCode); + } + + public IllegalStateException(ErrorCode errorCode) { + super(errorCode); + } + + public IllegalStateException(String message) { + super(errorCode, message); + } + + public IllegalStateException(ErrorCode errorCode, String message) { + super(errorCode, message); + } +} diff --git a/src/main/java/com/retrip/crew/domain/exception/common/InvalidValueException.java b/src/main/java/com/retrip/crew/domain/exception/common/InvalidValueException.java index 546de82..c001268 100644 --- a/src/main/java/com/retrip/crew/domain/exception/common/InvalidValueException.java +++ b/src/main/java/com/retrip/crew/domain/exception/common/InvalidValueException.java @@ -2,6 +2,10 @@ public class InvalidValueException extends BusinessException { private static final ErrorCode errorCode = ErrorCode.INVALID_INPUT_VALUE; + + public InvalidValueException() { + super(errorCode); + } public InvalidValueException(ErrorCode errorCode) { super(errorCode); } diff --git a/src/test/java/com/retrip/crew/domain/entity/CrewTest.java b/src/test/java/com/retrip/crew/domain/entity/CrewTest.java index 726bae2..cb9ed60 100644 --- a/src/test/java/com/retrip/crew/domain/entity/CrewTest.java +++ b/src/test/java/com/retrip/crew/domain/entity/CrewTest.java @@ -1,12 +1,14 @@ package com.retrip.crew.domain.entity; +import com.retrip.crew.domain.exception.common.IllegalStateException; import com.retrip.crew.domain.vo.RecruitmentStatus; import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +import java.util.List; import java.util.UUID; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.assertj.core.api.AssertionsForClassTypes.*; class CrewTest { @Test @@ -64,4 +66,30 @@ class CrewTest { // then assertThat(crew.getRecruitment().getStatus()).isEqualTo(RecruitmentStatus.RECRUITING); } + + @Test + void 크루_멤버가_최대_모집_인원과_같으면_모집을_시작할_수_없다() { + // given + Crew crew = Crew.create( + "속초 크루원 구함", + "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", + 5, + UUID.randomUUID()); + List crewMemberList = List.of( + new CrewMember(crew, UUID.randomUUID(), CrewMemberRole.LEADER), + new CrewMember(crew, UUID.randomUUID(), CrewMemberRole.PARTICIPANT), + new CrewMember(crew, UUID.randomUUID(), CrewMemberRole.PARTICIPANT), + new CrewMember(crew, UUID.randomUUID(), CrewMemberRole.PARTICIPANT), + new CrewMember(crew, UUID.randomUUID(), CrewMemberRole.PARTICIPANT) + ); + crew.stopRecruitment(); + CrewMembers crewMembers = new CrewMembers(crew, UUID.randomUUID()); + ReflectionTestUtils.setField(crewMembers, "values", crewMemberList); + ReflectionTestUtils.setField(crew, "crewMembers", crewMembers); + + // when, then + assertThatThrownBy(crew::startRecruitment) + .isExactlyInstanceOf(IllegalStateException.class); + assertThat(crew.getRecruitment().getStatus()).isEqualTo(RecruitmentStatus.STOPPED); + } } From cb36d7e3bfdc9f8bddf85fa01886ed3b9546336f Mon Sep 17 00:00:00 2001 From: mandykr Date: Tue, 11 Mar 2025 15:17:53 +0900 Subject: [PATCH 4/8] fix: conflict repair --- .../crew/application/in/CrewService.java | 19 ++++++- .../in/request/CrewUpdateRequest.java | 20 +++++++ .../in/response/CrewCreateResponse.java | 6 +++ .../in/response/CrewUpdateResponse.java | 38 +++++++++++++ .../in/usecase/CreateCrewUseCase.java | 8 --- .../in/usecase/ManageCrewUseCase.java | 14 +++++ .../com/retrip/crew/domain/entity/Crew.java | 5 ++ .../crew/domain/entity/Recruitment.java | 4 ++ .../in/presentation/rest/CrewController.java | 18 +++++-- .../crew/application/in/CrewServiceTest.java | 53 ++++++++++++++----- 10 files changed, 158 insertions(+), 27 deletions(-) create mode 100644 src/main/java/com/retrip/crew/application/in/request/CrewUpdateRequest.java create mode 100644 src/main/java/com/retrip/crew/application/in/response/CrewUpdateResponse.java delete mode 100644 src/main/java/com/retrip/crew/application/in/usecase/CreateCrewUseCase.java create mode 100644 src/main/java/com/retrip/crew/application/in/usecase/ManageCrewUseCase.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 09f1ad0..824c97a 100644 --- a/src/main/java/com/retrip/crew/application/in/CrewService.java +++ b/src/main/java/com/retrip/crew/application/in/CrewService.java @@ -1,18 +1,23 @@ package com.retrip.crew.application.in; import com.retrip.crew.application.in.request.CrewCreateRequest; +import com.retrip.crew.application.in.response.ChangeRecruitmentStatusResponse; import com.retrip.crew.application.in.response.CrewCreateResponse; import com.retrip.crew.application.in.usecase.CreateCrewUseCase; +import com.retrip.crew.application.in.usecase.UpdateRecruitmentUseCase; import com.retrip.crew.application.out.repository.CrewRepository; import com.retrip.crew.domain.entity.Crew; +import com.retrip.crew.domain.exception.CrewNotFoundException; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.UUID; + @Service @Transactional @RequiredArgsConstructor -public class CrewService implements CreateCrewUseCase, UpdateRecruitmentUseCase, GetCrewUseCase { +public class CrewService implements ManageCrewUseCase, UpdateRecruitmentUseCase, GetCrewUseCase { private final CrewRepository crewRepository; private final CrewMemberRepository crewMemberRepository; private final CrewQueryRepository crewQueryRepository; @@ -24,6 +29,18 @@ public CrewCreateResponse createCrew(CrewCreateRequest request) { return CrewCreateResponse.of(crew); } + @Override + public CrewUpdateResponse updateCrew(UUID crewId, CrewUpdateRequest request) { + Crew crew = findById(crewId); + CrewTitle title = new CrewTitle(request.title()); + CrewDescription description = new CrewDescription(request.description()); + Recruitment recruitment = crew.getRecruitment(); + + crew.update(title, description); + recruitment.updateMaxMembers(request.maxMembers()); + return CrewUpdateResponse.of(crew); + } + @Override public ChangeRecruitmentStatusResponse startRecruitment(UUID crewId) { Crew crew = findById(crewId); diff --git a/src/main/java/com/retrip/crew/application/in/request/CrewUpdateRequest.java b/src/main/java/com/retrip/crew/application/in/request/CrewUpdateRequest.java new file mode 100644 index 0000000..9d5a509 --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/request/CrewUpdateRequest.java @@ -0,0 +1,20 @@ +package com.retrip.crew.application.in.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Size; + +@Schema(description = "크루 정보 수정 Request") +public record CrewUpdateRequest( + @Schema(description = "크루 타이틀") + @Size(min = 1, max = 30) + String title, + + @Schema(description = "크루 설명") + @Size(min = 1, max = 500) + String description, + + @Schema(description = "크루 최대 인원수") + @Size(min = 5, max = 1000) + int maxMembers +) { +} diff --git a/src/main/java/com/retrip/crew/application/in/response/CrewCreateResponse.java b/src/main/java/com/retrip/crew/application/in/response/CrewCreateResponse.java index a81cced..ad231f9 100644 --- a/src/main/java/com/retrip/crew/application/in/response/CrewCreateResponse.java +++ b/src/main/java/com/retrip/crew/application/in/response/CrewCreateResponse.java @@ -20,8 +20,13 @@ public record CrewCreateResponse( @Schema(description = "리더 ID") UUID leaderId, + @Schema(description = "크루 최대 인원수") + int maxMembers, + @Schema(description = "모집 상태") RecruitmentStatus status + + ) { public static CrewCreateResponse of(Crew crew) { return new CrewCreateResponse( @@ -29,6 +34,7 @@ public static CrewCreateResponse of(Crew crew) { crew.getTitle().getValue(), crew.getDescription(), crew.getLeader().getId(), + crew.getRecruitment().getMaxMembers(), crew.getRecruitment().getStatus() ); } diff --git a/src/main/java/com/retrip/crew/application/in/response/CrewUpdateResponse.java b/src/main/java/com/retrip/crew/application/in/response/CrewUpdateResponse.java new file mode 100644 index 0000000..97ca8a1 --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/response/CrewUpdateResponse.java @@ -0,0 +1,38 @@ +package com.retrip.crew.application.in.response; + +import com.retrip.crew.domain.entity.Crew; +import com.retrip.crew.domain.vo.RecruitmentStatus; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.UUID; + +public record CrewUpdateResponse( + @Schema(description = "크루 ID") + UUID id, + + @Schema(description = "크루 타이틀") + String title, + + @Schema(description = "크루 설명") + String description, + + @Schema(description = "리더 ID") + UUID leaderId, + + @Schema(description = "크루 최대 인원수") + int maxMembers, + + @Schema(description = "모집 상태") + RecruitmentStatus status +) { + public static CrewUpdateResponse of(Crew crew) { + return new CrewUpdateResponse( + crew.getId(), + crew.getTitle().getValue(), + crew.getDescription().getValue(), + crew.getLeader().getId(), + crew.getRecruitment().getMaxMembers(), + crew.getRecruitment().getStatus() + ); + } +} diff --git a/src/main/java/com/retrip/crew/application/in/usecase/CreateCrewUseCase.java b/src/main/java/com/retrip/crew/application/in/usecase/CreateCrewUseCase.java deleted file mode 100644 index e581d24..0000000 --- a/src/main/java/com/retrip/crew/application/in/usecase/CreateCrewUseCase.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.retrip.crew.application.in.usecase; - -import com.retrip.crew.application.in.request.CrewCreateRequest; -import com.retrip.crew.application.in.response.CrewCreateResponse; - -public interface CreateCrewUseCase { - CrewCreateResponse createCrew(CrewCreateRequest createRequest); -} 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 new file mode 100644 index 0000000..b8fb910 --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/usecase/ManageCrewUseCase.java @@ -0,0 +1,14 @@ +package com.retrip.crew.application.in.usecase; + +import com.retrip.crew.application.in.request.CrewCreateRequest; +import com.retrip.crew.application.in.request.CrewUpdateRequest; +import com.retrip.crew.application.in.response.CrewCreateResponse; +import com.retrip.crew.application.in.response.CrewUpdateResponse; + +import java.util.UUID; + +public interface ManageCrewUseCase { + CrewCreateResponse createCrew(CrewCreateRequest request); + + CrewUpdateResponse updateCrew(UUID crewId, CrewUpdateRequest request); +} diff --git a/src/main/java/com/retrip/crew/domain/entity/Crew.java b/src/main/java/com/retrip/crew/domain/entity/Crew.java index 645af90..f22a476 100644 --- a/src/main/java/com/retrip/crew/domain/entity/Crew.java +++ b/src/main/java/com/retrip/crew/domain/entity/Crew.java @@ -69,6 +69,11 @@ public void stopRecruitment() { this.recruitment.stop(); } + public void update(CrewTitle title, CrewDescription description) { + this.title = title; + this.description = description; + } + public String getDescription(){ return description.getValue(); } diff --git a/src/main/java/com/retrip/crew/domain/entity/Recruitment.java b/src/main/java/com/retrip/crew/domain/entity/Recruitment.java index 919a526..c10f411 100644 --- a/src/main/java/com/retrip/crew/domain/entity/Recruitment.java +++ b/src/main/java/com/retrip/crew/domain/entity/Recruitment.java @@ -42,4 +42,8 @@ public void stop() { private boolean isRecruitmentComplete(int membersSize) { return this.maxMembers == membersSize; } + + public void updateMaxMembers(int maxMembers) { + this.maxMembers = maxMembers; + } } 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 13469b4..56e4d21 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 @@ -2,31 +2,41 @@ import com.retrip.crew.application.in.request.CrewCreateRequest; import com.retrip.crew.application.in.response.CrewCreateResponse; +import com.retrip.crew.application.in.response.ChangeRecruitmentStatusResponse; import com.retrip.crew.application.in.usecase.CreateCrewUseCase; +import com.retrip.crew.application.in.usecase.UpdateRecruitmentUseCase; +import com.retrip.crew.infra.adapter.in.presentation.rest.common.ApiResponse; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.net.URI; +import java.util.UUID; @RequiredArgsConstructor @RequestMapping("/crews") @RestController @Tag(name = "Crew", description = "크루 서비스") public class CrewController { - private final CreateCrewUseCase createCrewUseCase; + private final ManageCrewUseCase manageCrewUseCase; private final UpdateRecruitmentUseCase updateRecruitmentUseCase; private final GetCrewUseCase getCrewUseCase; @PostMapping @Schema(description = "크루 생성") public ApiResponse createCrew(@RequestBody CrewCreateRequest request) { - CrewCreateResponse crew = createCrewUseCase.createCrew(request); + CrewCreateResponse crew = manageCrewUseCase.createCrew(request); return ApiResponse.created(crew); } + @PutMapping("/{crewId}") + @Schema(description = "크루 정보 수정") + public ApiResponse updateCrew( + @PathVariable UUID crewId, @RequestBody CrewUpdateRequest request) { + CrewUpdateResponse crew = manageCrewUseCase.updateCrew(crewId, request); + return ApiResponse.ok(crew); + } + @PutMapping("/{crewId}/recruitments/start") @Schema(description = "크루 모집 시작") public ApiResponse startRecruitment(@PathVariable final UUID crewId) { 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 994ac86..95b0ca4 100644 --- a/src/test/java/com/retrip/crew/application/in/CrewServiceTest.java +++ b/src/test/java/com/retrip/crew/application/in/CrewServiceTest.java @@ -1,25 +1,25 @@ package com.retrip.crew.application.in; import com.retrip.crew.application.in.request.CrewCreateRequest; -import com.retrip.crew.application.in.request.CrewOrder; import com.retrip.crew.application.in.response.CrewCreateResponse; -import com.retrip.crew.application.in.response.CrewDetailResponse; -import com.retrip.crew.application.in.response.CrewListResponse; -import com.retrip.crew.common.BaseTest; -import com.retrip.crew.domain.entity.CrewMemberRole; -import com.retrip.crew.infra.adapter.in.presentation.rest.common.ScrollPageResponse; -import java.util.List; -import java.util.UUID; +import com.retrip.crew.application.out.repository.CrewRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import java.util.UUID; -import static com.retrip.crew.common.fixture.CrewFixture.createCrew; -import static com.retrip.crew.common.fixture.CrewFixture.createMultipleCrews; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; class CrewServiceTest extends BaseTest { + @Autowired + CrewRepository crewRepository; + + CrewService crewService; + UUID memberId = UUID.randomUUID(); @Test void 크루를_생성_한다() { @@ -28,7 +28,7 @@ class CrewServiceTest extends BaseTest { 정수_ID, "속초 크루원 구함", "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", - 5 + 100 ); //when @@ -39,6 +39,31 @@ class CrewServiceTest extends BaseTest { assertThat(response.leaderId()).isNotNull(); } + @Test + void 크루의_제목_설명_최대_인원수를_수정한다() { + // given + Crew crew = crewRepository.save(Crew.create( + "속초 크루원 구함", + "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", + 100, + memberId + )); + CrewUpdateRequest request = new CrewUpdateRequest( + "강릉 크루원 구함", + "강릉 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", + 200 + ); + + // when + CrewUpdateResponse response = crewService.updateCrew(crew.getId(), request); + + // then + assertAll( + () -> assertThat(response.title()).isEqualTo("강릉 크루원 구함"), + () -> assertThat(response.maxMembers()).isEqualTo(200) + ); + } + @Test void 크루를_검색_및_정렬_필터링하여_조회한다(){ //given From fae20edb15b25b735cc8711ed2ad86d206672f84 Mon Sep 17 00:00:00 2001 From: mandykr Date: Tue, 11 Mar 2025 16:35:11 +0900 Subject: [PATCH 5/8] fix: conflict repair --- .../crew/application/in/CrewService.java | 16 ++++++- .../in/request/CreateDemandRequest.java | 8 ++++ .../in/response/CreateDemandResponse.java | 14 ++++++ .../in/usecase/ManageDemandUseCase.java | 10 ++++ .../com/retrip/crew/domain/entity/Crew.java | 4 ++ .../entity/{Subscription.java => Demand.java} | 10 +++- .../crew/domain/entity/Recruitment.java | 25 ++++++++-- .../in/presentation/rest/CrewController.java | 14 +++++- src/main/resources/application.yml | 1 + .../crew/application/in/CrewServiceTest.java | 47 ++++++++++++++++++- 10 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/retrip/crew/application/in/request/CreateDemandRequest.java create mode 100644 src/main/java/com/retrip/crew/application/in/response/CreateDemandResponse.java create mode 100644 src/main/java/com/retrip/crew/application/in/usecase/ManageDemandUseCase.java rename src/main/java/com/retrip/crew/domain/entity/{Subscription.java => Demand.java} (66%) 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 824c97a..e8698c6 100644 --- a/src/main/java/com/retrip/crew/application/in/CrewService.java +++ b/src/main/java/com/retrip/crew/application/in/CrewService.java @@ -1,13 +1,18 @@ package com.retrip.crew.application.in; import com.retrip.crew.application.in.request.CrewCreateRequest; +import com.retrip.crew.application.in.request.CrewUpdateRequest; import com.retrip.crew.application.in.response.ChangeRecruitmentStatusResponse; import com.retrip.crew.application.in.response.CrewCreateResponse; -import com.retrip.crew.application.in.usecase.CreateCrewUseCase; +import com.retrip.crew.application.in.response.CrewUpdateResponse; +import com.retrip.crew.application.in.usecase.ManageCrewUseCase; import com.retrip.crew.application.in.usecase.UpdateRecruitmentUseCase; import com.retrip.crew.application.out.repository.CrewRepository; import com.retrip.crew.domain.entity.Crew; +import com.retrip.crew.domain.entity.Recruitment; import com.retrip.crew.domain.exception.CrewNotFoundException; +import com.retrip.crew.domain.vo.CrewDescription; +import com.retrip.crew.domain.vo.CrewTitle; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -17,7 +22,7 @@ @Service @Transactional @RequiredArgsConstructor -public class CrewService implements ManageCrewUseCase, UpdateRecruitmentUseCase, GetCrewUseCase { +public class CrewService implements ManageCrewUseCase, UpdateRecruitmentUseCase, ManageDemandUseCase, GetCrewUseCase { private final CrewRepository crewRepository; private final CrewMemberRepository crewMemberRepository; private final CrewQueryRepository crewQueryRepository; @@ -55,6 +60,13 @@ public ChangeRecruitmentStatusResponse stopRecruitment(UUID crewId) { 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); + } + private Crew findById(UUID crewId){ return crewRepository.findById(crewId) .orElseThrow(CrewNotFoundException::new); diff --git a/src/main/java/com/retrip/crew/application/in/request/CreateDemandRequest.java b/src/main/java/com/retrip/crew/application/in/request/CreateDemandRequest.java new file mode 100644 index 0000000..537f1ea --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/request/CreateDemandRequest.java @@ -0,0 +1,8 @@ +package com.retrip.crew.application.in.request; + +import java.util.UUID; + +public record CreateDemandRequest( + UUID memberId +) { +} diff --git a/src/main/java/com/retrip/crew/application/in/response/CreateDemandResponse.java b/src/main/java/com/retrip/crew/application/in/response/CreateDemandResponse.java new file mode 100644 index 0000000..1c42d65 --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/response/CreateDemandResponse.java @@ -0,0 +1,14 @@ +package com.retrip.crew.application.in.response; + +import com.retrip.crew.domain.entity.Demand; + +import java.util.UUID; + +public record CreateDemandResponse( + UUID crewId, + UUID memberId +) { + public static CreateDemandResponse of(UUID crewId, Demand demand) { + return new CreateDemandResponse(crewId, demand.getMemberId()); + } +} diff --git a/src/main/java/com/retrip/crew/application/in/usecase/ManageDemandUseCase.java b/src/main/java/com/retrip/crew/application/in/usecase/ManageDemandUseCase.java new file mode 100644 index 0000000..ae7008f --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/usecase/ManageDemandUseCase.java @@ -0,0 +1,10 @@ +package com.retrip.crew.application.in.usecase; + +import com.retrip.crew.application.in.request.CreateDemandRequest; +import com.retrip.crew.application.in.response.CreateDemandResponse; + +import java.util.UUID; + +public interface ManageDemandUseCase { + CreateDemandResponse createDemand(UUID crewId, CreateDemandRequest request); +} diff --git a/src/main/java/com/retrip/crew/domain/entity/Crew.java b/src/main/java/com/retrip/crew/domain/entity/Crew.java index f22a476..ee1bc49 100644 --- a/src/main/java/com/retrip/crew/domain/entity/Crew.java +++ b/src/main/java/com/retrip/crew/domain/entity/Crew.java @@ -74,6 +74,10 @@ public void update(CrewTitle title, CrewDescription description) { this.description = description; } + public Demand demand(UUID memberId) { + return recruitment.addDemand(memberId, this); + } + public String getDescription(){ return description.getValue(); } diff --git a/src/main/java/com/retrip/crew/domain/entity/Subscription.java b/src/main/java/com/retrip/crew/domain/entity/Demand.java similarity index 66% rename from src/main/java/com/retrip/crew/domain/entity/Subscription.java rename to src/main/java/com/retrip/crew/domain/entity/Demand.java index bdcf846..8fae633 100644 --- a/src/main/java/com/retrip/crew/domain/entity/Subscription.java +++ b/src/main/java/com/retrip/crew/domain/entity/Demand.java @@ -10,18 +10,24 @@ @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED, force = true) @Getter -public class Subscription { +public class Demand { @Id @Column(columnDefinition = "varbinary(16)") private UUID id; + private UUID memberId; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn( name = "crew_id", nullable = false, columnDefinition = "varbinary(16)", - foreignKey = @ForeignKey(name = "fk_subscription_to_crew") + foreignKey = @ForeignKey(name = "fk_demand_to_crew") ) private Crew crew; + public Demand(UUID memberId, Crew crew) { + this.id = UUID.randomUUID(); + this.memberId = memberId; + this.crew = crew; + } } diff --git a/src/main/java/com/retrip/crew/domain/entity/Recruitment.java b/src/main/java/com/retrip/crew/domain/entity/Recruitment.java index c10f411..e445277 100644 --- a/src/main/java/com/retrip/crew/domain/entity/Recruitment.java +++ b/src/main/java/com/retrip/crew/domain/entity/Recruitment.java @@ -2,15 +2,19 @@ import com.retrip.crew.domain.exception.common.IllegalStateException; import com.retrip.crew.domain.vo.RecruitmentStatus; -import jakarta.persistence.*; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Embeddable; +import jakarta.persistence.OneToMany; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import java.util.ArrayList; import java.util.List; +import java.util.UUID; -import static com.retrip.crew.domain.vo.RecruitmentStatus.*; +import static com.retrip.crew.domain.vo.RecruitmentStatus.RECRUITING; +import static com.retrip.crew.domain.vo.RecruitmentStatus.STOPPED; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED, force = true) @@ -20,7 +24,7 @@ public class Recruitment { private RecruitmentStatus status; @OneToMany(mappedBy = "crew", cascade = CascadeType.ALL, orphanRemoval = true) - private List subscriptions = new ArrayList<>(); + private List demands = new ArrayList<>(); public Recruitment(int maxMembers) { this.maxMembers = maxMembers; @@ -46,4 +50,19 @@ private boolean isRecruitmentComplete(int membersSize) { public void updateMaxMembers(int maxMembers) { this.maxMembers = maxMembers; } + + public Demand addDemand(UUID memberId, Crew crew) { + if (isDuplicate(memberId)) { + throw new IllegalStateException("이미 요청한 사용자는 다시 요청할 수 없습니다."); + } + Demand demand = new Demand(memberId, crew); + demands.add(demand); + return demand; + } + + private boolean isDuplicate(UUID memberId) { + return demands.stream() + .map(Demand::getMemberId) + .anyMatch(id -> id.equals(memberId)); + } } 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 56e4d21..def7da0 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 @@ -1,9 +1,11 @@ package com.retrip.crew.infra.adapter.in.presentation.rest; import com.retrip.crew.application.in.request.CrewCreateRequest; +import com.retrip.crew.application.in.request.CrewUpdateRequest; import com.retrip.crew.application.in.response.CrewCreateResponse; import com.retrip.crew.application.in.response.ChangeRecruitmentStatusResponse; -import com.retrip.crew.application.in.usecase.CreateCrewUseCase; +import com.retrip.crew.application.in.response.CrewUpdateResponse; +import com.retrip.crew.application.in.usecase.ManageCrewUseCase; import com.retrip.crew.application.in.usecase.UpdateRecruitmentUseCase; import com.retrip.crew.infra.adapter.in.presentation.rest.common.ApiResponse; import io.swagger.v3.oas.annotations.media.Schema; @@ -20,6 +22,7 @@ public class CrewController { private final ManageCrewUseCase manageCrewUseCase; private final UpdateRecruitmentUseCase updateRecruitmentUseCase; + private ManageDemandUseCase manageDemandUseCase; private final GetCrewUseCase getCrewUseCase; @PostMapping @@ -51,6 +54,15 @@ public ApiResponse stopRecruitment(@PathVariabl return ApiResponse.ok(recruitment); } + @PostMapping("/{crewId}/demands") + @Schema(description = "크루 참여 요청") + public ApiResponse createDemand( + @PathVariable final UUID crewId, + @RequestBody CreateDemandRequest request) { + CreateDemandResponse demand = manageDemandUseCase.createDemand(crewId, request); + return ApiResponse.created(demand); + } + @GetMapping @Schema(description = "크루 리스트 조회") public ResponseEntity>> getCrews( diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f9bb91b..cb6bbcd 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -19,6 +19,7 @@ spring: logging: level: org.hibernate.type.descriptor.sql: trace + org.hibernate.orm.jdbc.bind: TRACE org.springframework.web.client.RestTemplate: DEBUG springdoc: 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 95b0ca4..9131554 100644 --- a/src/test/java/com/retrip/crew/application/in/CrewServiceTest.java +++ b/src/test/java/com/retrip/crew/application/in/CrewServiceTest.java @@ -1,10 +1,12 @@ package com.retrip.crew.application.in; import com.retrip.crew.application.in.request.CrewCreateRequest; +import com.retrip.crew.application.in.request.CrewUpdateRequest; import com.retrip.crew.application.in.response.CrewCreateResponse; +import com.retrip.crew.application.in.response.CrewUpdateResponse; import com.retrip.crew.application.out.repository.CrewRepository; +import com.retrip.crew.domain.entity.Crew; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; @@ -13,6 +15,7 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; class CrewServiceTest extends BaseTest { @Autowired @@ -21,6 +24,11 @@ class CrewServiceTest extends BaseTest { CrewService crewService; UUID memberId = UUID.randomUUID(); + @BeforeEach + void setUp() { + crewService = new CrewService(crewRepository); + } + @Test void 크루를_생성_한다() { //given @@ -64,6 +72,43 @@ class CrewServiceTest extends BaseTest { ); } + @Test + void 크루_참여_요청을_생성한다() { + Crew crew = crewRepository.save(Crew.create( + "속초 크루원 구함", + "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", + 100, + memberId + )); + CreateDemandRequest request = new CreateDemandRequest(memberId); + + CreateDemandResponse response = crewService.createDemand(crew.getId(), request); + + List demands = crew.getRecruitment().getDemands(); + assertAll( + () -> assertThat(demands.size()).isEqualTo(1), + () -> assertThat(response.memberId()).isEqualTo(demands.get(0).getMemberId()) + ); + } + + @Test + void 이미_요청한_사용자는_다시_크루에_요청할_수_없다() { + Crew crew = Crew.create( + "속초 크루원 구함", + "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", + 100, + memberId + ); + crew.demand(memberId); + crew.demand(UUID.randomUUID()); + crew.demand(UUID.randomUUID()); + Crew save = crewRepository.save(crew); + CreateDemandRequest request = new CreateDemandRequest(memberId); + + assertThatThrownBy(() -> crewService.createDemand(save.getId(), request)) + .isExactlyInstanceOf(IllegalStateException.class); + } + @Test void 크루를_검색_및_정렬_필터링하여_조회한다(){ //given From 2a6704e766f6d0bb0e522fc74c5d10861b89477a Mon Sep 17 00:00:00 2001 From: mandykr Date: Tue, 11 Mar 2025 17:29:08 +0900 Subject: [PATCH 6/8] fix: conflict repair --- .../infra/adapter/in/presentation/rest/CrewController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 def7da0..ae1c03f 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 @@ -1,11 +1,14 @@ package com.retrip.crew.infra.adapter.in.presentation.rest; +import com.retrip.crew.application.in.request.CreateDemandRequest; import com.retrip.crew.application.in.request.CrewCreateRequest; import com.retrip.crew.application.in.request.CrewUpdateRequest; +import com.retrip.crew.application.in.response.CreateDemandResponse; import com.retrip.crew.application.in.response.CrewCreateResponse; import com.retrip.crew.application.in.response.ChangeRecruitmentStatusResponse; import com.retrip.crew.application.in.response.CrewUpdateResponse; import com.retrip.crew.application.in.usecase.ManageCrewUseCase; +import com.retrip.crew.application.in.usecase.ManageDemandUseCase; import com.retrip.crew.application.in.usecase.UpdateRecruitmentUseCase; import com.retrip.crew.infra.adapter.in.presentation.rest.common.ApiResponse; import io.swagger.v3.oas.annotations.media.Schema; @@ -22,7 +25,7 @@ public class CrewController { private final ManageCrewUseCase manageCrewUseCase; private final UpdateRecruitmentUseCase updateRecruitmentUseCase; - private ManageDemandUseCase manageDemandUseCase; + private final ManageDemandUseCase manageDemandUseCase; private final GetCrewUseCase getCrewUseCase; @PostMapping From ca4810f6ca6de7003aebb06bff7e4f46dcb91076 Mon Sep 17 00:00:00 2001 From: mandykr Date: Mon, 17 Mar 2025 17:10:10 +0900 Subject: [PATCH 7/8] =?UTF-8?q?refactor:=20CrewMembers=20size=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/retrip/crew/domain/entity/Crew.java | 2 +- src/main/java/com/retrip/crew/domain/entity/CrewMembers.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/retrip/crew/domain/entity/Crew.java b/src/main/java/com/retrip/crew/domain/entity/Crew.java index ee1bc49..fd2180e 100644 --- a/src/main/java/com/retrip/crew/domain/entity/Crew.java +++ b/src/main/java/com/retrip/crew/domain/entity/Crew.java @@ -61,7 +61,7 @@ public CrewMember getLeader() { } public void startRecruitment() { - int membersSize = crewMembers.getValues().size(); + int membersSize = crewMembers.getSize(); this.recruitment.start(membersSize); } 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 9d71eb2..4a358b1 100644 --- a/src/main/java/com/retrip/crew/domain/entity/CrewMembers.java +++ b/src/main/java/com/retrip/crew/domain/entity/CrewMembers.java @@ -32,4 +32,8 @@ public CrewMember getLeader() { .findFirst() .orElse(null); } + + public int getSize() { + return this.values.size(); + } } From 7d791cc2bb8ac8806b1bd721ba744ad284769a5d Mon Sep 17 00:00:00 2001 From: mandykr Date: Mon, 17 Mar 2025 17:52:53 +0900 Subject: [PATCH 8/8] fix: conflict repair --- .../crew/application/in/CrewService.java | 23 +++---- .../in/response/CrewDetailResponse.java | 5 +- .../in/response/CrewUpdateResponse.java | 2 +- .../in/presentation/rest/CrewController.java | 11 ++-- .../mysql/query/CrewQuerydslRepository.java | 15 ++--- .../crew/application/in/CrewServiceTest.java | 60 +++++++++---------- .../{BaseTest.java => ServiceTest.java} | 11 ++-- .../crew/common/fixture/CrewFixture.java | 4 +- 8 files changed, 66 insertions(+), 65 deletions(-) rename src/test/java/com/retrip/crew/common/{BaseTest.java => ServiceTest.java} (73%) 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 e8698c6..ed978f0 100644 --- a/src/main/java/com/retrip/crew/application/in/CrewService.java +++ b/src/main/java/com/retrip/crew/application/in/CrewService.java @@ -1,21 +1,30 @@ package com.retrip.crew.application.in; +import com.retrip.crew.application.in.request.CreateDemandRequest; import com.retrip.crew.application.in.request.CrewCreateRequest; +import com.retrip.crew.application.in.request.CrewOrder; import com.retrip.crew.application.in.request.CrewUpdateRequest; -import com.retrip.crew.application.in.response.ChangeRecruitmentStatusResponse; -import com.retrip.crew.application.in.response.CrewCreateResponse; -import com.retrip.crew.application.in.response.CrewUpdateResponse; +import com.retrip.crew.application.in.response.*; +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.UpdateRecruitmentUseCase; +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.domain.entity.Crew; +import com.retrip.crew.domain.entity.Demand; import com.retrip.crew.domain.entity.Recruitment; import com.retrip.crew.domain.exception.CrewNotFoundException; import com.retrip.crew.domain.vo.CrewDescription; import com.retrip.crew.domain.vo.CrewTitle; -import jakarta.transaction.Transactional; +import com.retrip.crew.infra.adapter.in.presentation.rest.common.ScrollPageResponse; +import com.retrip.crew.infra.util.PaginationUtils; import lombok.RequiredArgsConstructor; +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; @@ -30,7 +39,6 @@ public class CrewService implements ManageCrewUseCase, UpdateRecruitmentUseCase, @Override public CrewCreateResponse createCrew(CrewCreateRequest request) { Crew crew = crewRepository.save(request.to(request.leader())); - return CrewCreateResponse.of(crew); } @@ -67,11 +75,6 @@ public CreateDemandResponse createDemand(UUID crewId, CreateDemandRequest reques return CreateDemandResponse.of(crew.getId(), demand); } - private Crew findById(UUID crewId){ - return crewRepository.findById(crewId) - .orElseThrow(CrewNotFoundException::new); - } - @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/response/CrewDetailResponse.java b/src/main/java/com/retrip/crew/application/in/response/CrewDetailResponse.java index 95d4705..bc95740 100644 --- a/src/main/java/com/retrip/crew/application/in/response/CrewDetailResponse.java +++ b/src/main/java/com/retrip/crew/application/in/response/CrewDetailResponse.java @@ -3,9 +3,10 @@ import com.retrip.crew.domain.entity.Crew; import com.retrip.crew.domain.entity.CrewMember; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; + import java.util.List; import java.util.UUID; -import lombok.Builder; @Schema @Builder @@ -39,7 +40,7 @@ public static CrewDetailResponse of(Crew crew, int memberCount){ .description(crew.getDescription()) .leaderId(crew.getLeader().getMemberId()) .memberCount(memberCount) - .maxMemberCount(crew.getMaxMembers()) + .maxMemberCount(crew.getRecruitment().getMaxMembers()) .members(toList(crew.getCrewMembers().getValues())) .build(); } diff --git a/src/main/java/com/retrip/crew/application/in/response/CrewUpdateResponse.java b/src/main/java/com/retrip/crew/application/in/response/CrewUpdateResponse.java index 97ca8a1..2e513c6 100644 --- a/src/main/java/com/retrip/crew/application/in/response/CrewUpdateResponse.java +++ b/src/main/java/com/retrip/crew/application/in/response/CrewUpdateResponse.java @@ -29,7 +29,7 @@ public static CrewUpdateResponse of(Crew crew) { return new CrewUpdateResponse( crew.getId(), crew.getTitle().getValue(), - crew.getDescription().getValue(), + crew.getDescription(), crew.getLeader().getId(), crew.getRecruitment().getMaxMembers(), crew.getRecruitment().getStatus() 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 ae1c03f..573ff92 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 @@ -2,18 +2,21 @@ import com.retrip.crew.application.in.request.CreateDemandRequest; import com.retrip.crew.application.in.request.CrewCreateRequest; +import com.retrip.crew.application.in.request.CrewOrder; import com.retrip.crew.application.in.request.CrewUpdateRequest; -import com.retrip.crew.application.in.response.CreateDemandResponse; -import com.retrip.crew.application.in.response.CrewCreateResponse; -import com.retrip.crew.application.in.response.ChangeRecruitmentStatusResponse; -import com.retrip.crew.application.in.response.CrewUpdateResponse; +import com.retrip.crew.application.in.response.*; +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.UpdateRecruitmentUseCase; import com.retrip.crew.infra.adapter.in.presentation.rest.common.ApiResponse; +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 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.*; import java.util.UUID; diff --git a/src/main/java/com/retrip/crew/infra/adapter/out/persistence/mysql/query/CrewQuerydslRepository.java b/src/main/java/com/retrip/crew/infra/adapter/out/persistence/mysql/query/CrewQuerydslRepository.java index a88f942..13e7a09 100644 --- a/src/main/java/com/retrip/crew/infra/adapter/out/persistence/mysql/query/CrewQuerydslRepository.java +++ b/src/main/java/com/retrip/crew/infra/adapter/out/persistence/mysql/query/CrewQuerydslRepository.java @@ -1,10 +1,5 @@ package com.retrip.crew.infra.adapter.out.persistence.mysql.query; -import static com.querydsl.jpa.JPAExpressions.select; -import static com.retrip.crew.domain.entity.QCrew.crew; -import static com.retrip.crew.domain.entity.QCrewMember.crewMember; -import static com.retrip.crew.infra.util.PaginationUtils.checkEndPage; - import com.querydsl.core.types.ExpressionUtils; import com.querydsl.core.types.Order; import com.querydsl.core.types.OrderSpecifier; @@ -17,13 +12,19 @@ import com.retrip.crew.application.in.response.CrewListResponse; import com.retrip.crew.application.out.repository.CrewQueryRepository; import com.retrip.crew.domain.entity.QCrewMember; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Repository; +import java.util.List; + +import static com.querydsl.jpa.JPAExpressions.select; +import static com.retrip.crew.domain.entity.QCrew.crew; +import static com.retrip.crew.domain.entity.QCrewMember.crewMember; +import static com.retrip.crew.infra.util.PaginationUtils.checkEndPage; + @Repository @RequiredArgsConstructor public class CrewQuerydslRepository implements CrewQueryRepository { @@ -43,7 +44,7 @@ public Slice getCrews(Pageable pageable, String keyword) { select(subCrewMember.count()) .from(subCrewMember) .where(subCrewMember.crew.id.eq(crew.id)) ,MemberCountAlias), - crew.maxMembers.as("maxMemberCount") + crew.recruitment.maxMembers.as("maxMemberCount") ) ) .from(crew) 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 9131554..a58c76a 100644 --- a/src/test/java/com/retrip/crew/application/in/CrewServiceTest.java +++ b/src/test/java/com/retrip/crew/application/in/CrewServiceTest.java @@ -1,39 +1,35 @@ package com.retrip.crew.application.in; +import com.retrip.crew.application.in.request.CreateDemandRequest; import com.retrip.crew.application.in.request.CrewCreateRequest; +import com.retrip.crew.application.in.request.CrewOrder; import com.retrip.crew.application.in.request.CrewUpdateRequest; -import com.retrip.crew.application.in.response.CrewCreateResponse; -import com.retrip.crew.application.in.response.CrewUpdateResponse; -import com.retrip.crew.application.out.repository.CrewRepository; +import com.retrip.crew.application.in.response.*; +import com.retrip.crew.common.ServiceTest; import com.retrip.crew.domain.entity.Crew; -import org.junit.jupiter.api.BeforeEach; +import com.retrip.crew.domain.entity.CrewMemberRole; +import com.retrip.crew.domain.entity.Demand; +import com.retrip.crew.domain.exception.common.IllegalStateException; +import com.retrip.crew.infra.adapter.in.presentation.rest.common.ScrollPageResponse; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import java.util.List; import java.util.UUID; +import static com.retrip.crew.common.fixture.CrewFixture.createCrewRequest; +import static com.retrip.crew.common.fixture.CrewFixture.createMultipleCrews; 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 CrewServiceTest extends BaseTest { - @Autowired - CrewRepository crewRepository; - - CrewService crewService; - UUID memberId = UUID.randomUUID(); - - @BeforeEach - void setUp() { - crewService = new CrewService(crewRepository); - } - +class CrewServiceTest extends ServiceTest { @Test void 크루를_생성_한다() { //given - CrewCreateRequest request = createCrew( - 정수_ID, + CrewCreateRequest request = createCrewRequest( + MEMBER_ID, "속초 크루원 구함", "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", 100 @@ -54,7 +50,7 @@ void setUp() { "속초 크루원 구함", "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", 100, - memberId + MEMBER_ID )); CrewUpdateRequest request = new CrewUpdateRequest( "강릉 크루원 구함", @@ -78,9 +74,9 @@ void setUp() { "속초 크루원 구함", "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", 100, - memberId + MEMBER_ID )); - CreateDemandRequest request = new CreateDemandRequest(memberId); + CreateDemandRequest request = new CreateDemandRequest(MEMBER_ID); CreateDemandResponse response = crewService.createDemand(crew.getId(), request); @@ -97,13 +93,13 @@ void setUp() { "속초 크루원 구함", "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", 100, - memberId + MEMBER_ID ); - crew.demand(memberId); + crew.demand(MEMBER_ID); crew.demand(UUID.randomUUID()); crew.demand(UUID.randomUUID()); Crew save = crewRepository.save(crew); - CreateDemandRequest request = new CreateDemandRequest(memberId); + CreateDemandRequest request = new CreateDemandRequest(MEMBER_ID); assertThatThrownBy(() -> crewService.createDemand(save.getId(), request)) .isExactlyInstanceOf(IllegalStateException.class); @@ -114,7 +110,7 @@ void setUp() { //given List requests = createMultipleCrews( 10, - 정수_ID, + MEMBER_ID, "속초 크루원 구함", "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", 5 @@ -142,8 +138,8 @@ void setUp() { @Test void 크루_상세를_조회한다(){ //given - CrewCreateRequest request = createCrew( - 정수_ID, + CrewCreateRequest request = createCrewRequest( + MEMBER_ID, "속초 크루원 구함", "속초 친구 구합니다! 나이는 20~40.. 많은 가입 부탁드립니다.", 5 @@ -156,10 +152,10 @@ void setUp() { //then assertAll( () -> assertThat(response.id()).isEqualTo(crewId), - () -> assertThat(response.leaderId()).isEqualTo(정수_ID), + () -> assertThat(response.leaderId()).isEqualTo(MEMBER_ID), () -> assertThat(response.members().size()).isEqualTo(1), () -> assertThat(response.members().getFirst().roleCode()).isEqualTo(CrewMemberRole.LEADER.getCode()), - () -> assertThat(response.members().getFirst().memberId()).isEqualTo(정수_ID) + () -> assertThat(response.members().getFirst().memberId()).isEqualTo(MEMBER_ID) ); } } diff --git a/src/test/java/com/retrip/crew/common/BaseTest.java b/src/test/java/com/retrip/crew/common/ServiceTest.java similarity index 73% rename from src/test/java/com/retrip/crew/common/BaseTest.java rename to src/test/java/com/retrip/crew/common/ServiceTest.java index 582349d..a6452c3 100644 --- a/src/test/java/com/retrip/crew/common/BaseTest.java +++ b/src/test/java/com/retrip/crew/common/ServiceTest.java @@ -5,17 +5,18 @@ import com.retrip.crew.application.out.repository.CrewQueryRepository; import com.retrip.crew.application.out.repository.CrewRepository; 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; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import java.util.UUID; + @DataJpaTest @Import(QuerydslConfig.class) @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) -public class BaseTest { +public class ServiceTest { @Autowired protected CrewRepository crewRepository; @@ -28,11 +29,7 @@ public class BaseTest { protected CrewService crewService; - protected UUID 정수_ID = UUID.fromString("13c8ab91-76bc-4f70-93e9-89f1a65dc640"); - protected UUID 홍석_ID = UUID.fromString("bf97d20b-d1f7-46a9-8362-11b9fa02d67d"); - protected UUID 준호_ID = UUID.fromString("8b9b67fd-1d88-4b30-bfea-cd8f89fc10d9"); - protected UUID 지수_ID = UUID.fromString("de3b60d2-5672-464d-8769-bf5c9de5eaff"); - protected UUID 혁진_ID = UUID.fromString("42880aaf-4b97-4b0c-8a8a-72df4bb592f6"); + protected UUID MEMBER_ID = UUID.fromString("13c8ab91-76bc-4f70-93e9-89f1a65dc640"); @BeforeEach void setUp() { 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 2ed3b6a..34ee232 100644 --- a/src/test/java/com/retrip/crew/common/fixture/CrewFixture.java +++ b/src/test/java/com/retrip/crew/common/fixture/CrewFixture.java @@ -8,7 +8,7 @@ public abstract class CrewFixture { - public static CrewCreateRequest createCrew(UUID memberId, String title, String description, int maxMembers) { + public static CrewCreateRequest createCrewRequest(UUID memberId, String title, String description, int maxMembers) { return new CrewCreateRequest( memberId, title, @@ -22,7 +22,7 @@ public static List createMultipleCrews(int count, UUID member .mapToObj(i -> { String title = baseTitle + " " + (i + 1); String description = baseDescription + " " + (i + 1); - return createCrew(memberId, title, description, maxMembers); + return createCrewRequest(memberId, title, description, maxMembers); }) .collect(Collectors.toList()); }