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 ae880c2..133f5a0 100644 --- a/src/main/java/com/retrip/crew/application/in/CrewService.java +++ b/src/main/java/com/retrip/crew/application/in/CrewService.java @@ -3,26 +3,17 @@ 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.crew.CrewWithdrawalRequest; +import com.retrip.crew.application.in.request.crew.*; import com.retrip.crew.application.in.response.IntroductionCreateResponse; import com.retrip.crew.application.in.response.IntroductionDetailResponse; import com.retrip.crew.application.in.response.IntroductionListResponse; import com.retrip.crew.application.in.response.IntroductionUpdateResponse; -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.crew.*; import com.retrip.crew.application.in.usecase.GetCrewUseCase; import com.retrip.crew.application.in.usecase.ManageCrewUseCase; 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.entity.*; import com.retrip.crew.domain.exception.CrewNotFoundException; import com.retrip.crew.domain.exception.IntroductionNotFoundException; import com.retrip.crew.domain.vo.CrewDescription; @@ -145,4 +136,12 @@ public void withdrawCrew(UUID crewId, CrewWithdrawalRequest request) { CrewMembers crewMembers = crew.getCrewMembers(); crewMembers.withdraw(request.memberId(), request.participatingCrewTrips()); } + + @Override + public CrewLeaderDelegateResponse delegateCrewLeader(UUID crewId, CrewLeaderDelegateRequest request) { + Crew crew = findCrewById(crewId); + CrewMembers crewMembers = crew.getCrewMembers(); + CrewMember newLeader = crewMembers.delegateLeader(request.leaderId(), request.newLeaderId()); + return CrewLeaderDelegateResponse.of(newLeader); + } } diff --git a/src/main/java/com/retrip/crew/application/in/request/crew/CrewLeaderDelegateRequest.java b/src/main/java/com/retrip/crew/application/in/request/crew/CrewLeaderDelegateRequest.java new file mode 100644 index 0000000..8ba1f90 --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/request/crew/CrewLeaderDelegateRequest.java @@ -0,0 +1,15 @@ +package com.retrip.crew.application.in.request.crew; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.UUID; + +@Schema(description = "크루 리더 위임 Request") +public record CrewLeaderDelegateRequest( + @Schema(description = "리더 ID") + UUID leaderId, + + @Schema(description = "위임 리더 ID") + UUID newLeaderId +) { +} diff --git a/src/main/java/com/retrip/crew/application/in/response/crew/CrewLeaderDelegateResponse.java b/src/main/java/com/retrip/crew/application/in/response/crew/CrewLeaderDelegateResponse.java new file mode 100644 index 0000000..26d2bfc --- /dev/null +++ b/src/main/java/com/retrip/crew/application/in/response/crew/CrewLeaderDelegateResponse.java @@ -0,0 +1,26 @@ +package com.retrip.crew.application.in.response.crew; + +import com.retrip.crew.domain.entity.CrewMember; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.UUID; + +@Schema(description = "크루 리더 위임 Response") +public record CrewLeaderDelegateResponse( + @Schema(description = "리더 ID") + UUID leaderId, + + @Schema(description = "크루원 역할 코드") + String roleCode, + + @Schema(description = "크루원 역할명") + String roleName +) { + public static CrewLeaderDelegateResponse of(CrewMember newLeader) { + return new CrewLeaderDelegateResponse( + newLeader.getMemberId(), + newLeader.getCrewMemberRole().getCode(), + newLeader.getCrewMemberRole().getViewName() + ); + } +} 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 411163c..550cc4f 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 @@ -1,9 +1,11 @@ package com.retrip.crew.application.in.usecase; import com.retrip.crew.application.in.request.crew.CrewCreateRequest; +import com.retrip.crew.application.in.request.crew.CrewLeaderDelegateRequest; 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.CrewLeaderDelegateResponse; import com.retrip.crew.application.in.response.crew.CrewUpdateResponse; import java.util.UUID; @@ -14,4 +16,6 @@ public interface ManageCrewUseCase { CrewUpdateResponse updateCrew(UUID crewId, CrewUpdateRequest request); void withdrawCrew(UUID crewId, CrewWithdrawalRequest request); + + CrewLeaderDelegateResponse delegateCrewLeader(UUID crewId, CrewLeaderDelegateRequest request); } diff --git a/src/main/java/com/retrip/crew/domain/entity/CrewMember.java b/src/main/java/com/retrip/crew/domain/entity/CrewMember.java index fd290b2..476ab74 100644 --- a/src/main/java/com/retrip/crew/domain/entity/CrewMember.java +++ b/src/main/java/com/retrip/crew/domain/entity/CrewMember.java @@ -40,4 +40,8 @@ public CrewMember(Crew crew, UUID memberId, CrewMemberRole crewMemberRole) { public boolean isLeader() { return CrewMemberRole.isLeaderRole(this.crewMemberRole); } + + public void changeRole(CrewMemberRole role) { + this.crewMemberRole = role; + } } 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 ec0af21..f871150 100644 --- a/src/main/java/com/retrip/crew/domain/entity/CrewMembers.java +++ b/src/main/java/com/retrip/crew/domain/entity/CrewMembers.java @@ -2,6 +2,7 @@ import com.retrip.crew.domain.CrewTrip; import com.retrip.crew.domain.exception.ImpossibleWithdrawCrewException; +import com.retrip.crew.domain.exception.NotCrewLeaderException; import com.retrip.crew.domain.exception.common.IllegalStateException; import com.retrip.crew.domain.exception.common.InvalidValueException; import jakarta.persistence.CascadeType; @@ -33,9 +34,9 @@ private CrewMember createLeader(Crew crew, UUID memberId) { public CrewMember getLeader() { return this.values.stream() - .filter(it -> it.getCrewMemberRole() == CrewMemberRole.LEADER) + .filter(CrewMember::isLeader) .findFirst() - .orElse(null); + .orElseThrow(() -> new InvalidValueException("크루 리더를 찾을 수 없습니다.")); } public int getSize() { @@ -90,4 +91,20 @@ private CrewMember findMember(UUID memberId) { .findFirst() .orElseThrow(() -> new InvalidValueException("크루 멤버가 아닙니다.")); } + + public CrewMember delegateLeader(UUID leaderId, UUID newLeaderId) { + CrewMember leader = findMember(leaderId); + CrewMember newLeader = findMember(newLeaderId); + validatePossibleDelegate(leader); + + leader.changeRole(CrewMemberRole.PARTICIPANT); + newLeader.changeRole(CrewMemberRole.LEADER); + return newLeader; + } + + private void validatePossibleDelegate(CrewMember leader) { + if (!leader.isLeader()) { + throw new NotCrewLeaderException(); + } + } } 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 a3a1f5b..3da6303 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 @@ -3,18 +3,12 @@ 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.crew.CrewWithdrawalRequest; +import com.retrip.crew.application.in.request.crew.*; import com.retrip.crew.application.in.response.IntroductionCreateResponse; import com.retrip.crew.application.in.response.IntroductionDetailResponse; import com.retrip.crew.application.in.response.IntroductionListResponse; import com.retrip.crew.application.in.response.IntroductionUpdateResponse; -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.crew.*; import com.retrip.crew.application.in.usecase.GetCrewUseCase; import com.retrip.crew.application.in.usecase.ManageCrewUseCase; import com.retrip.crew.application.in.usecase.ManageIntroductionUseCase; @@ -96,12 +90,12 @@ public ApiResponse updateIntroduction( @Schema(description = "크루 자기소개 삭제") @DeleteMapping("/{crewId}/introductions/{introductionId}") - public ApiResponse deleteIntroduction( + public ApiResponse deleteIntroduction( @PathVariable("crewId") final UUID crewId, @PathVariable("introductionId") final UUID introductionId, @RequestBody IntroductionDeleteRequest request) { manageIntroductionUseCase.deleteIntroduction(crewId, introductionId, request); - return ApiResponse.created(null); + return ApiResponse.noContent(); } @Schema(description = "크루 자기소개 상세 조회") @@ -132,4 +126,13 @@ public ApiResponse withdrawCrew( manageCrewUseCase.withdrawCrew(crewId, request); return ApiResponse.noContent(); } + + @Schema(description = "크루 리더 위임") + @PutMapping("/{crewId}/members/delegate") + public ApiResponse delegateCrewLeader( + @PathVariable final UUID crewId, + @RequestBody CrewLeaderDelegateRequest request) { + CrewLeaderDelegateResponse response = manageCrewUseCase.delegateCrewLeader(crewId, request); + return ApiResponse.ok(response); + } } 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 741495f..9a5a573 100644 --- a/src/test/java/com/retrip/crew/domain/entity/CrewMembersTest.java +++ b/src/test/java/com/retrip/crew/domain/entity/CrewMembersTest.java @@ -134,4 +134,19 @@ class CrewMembersTest { // then assertThat(crewMembers.getValues().contains(member)).isFalse(); } + + @Test + void 크루_리더를_위임한다() { + // given + Crew crew = createCrewWithMembers(LEADER_ID); + CrewMember leader = crew.getCrewMembers().getLeader(); + + // when + CrewMember newLeader = crew.getCrewMembers().delegateLeader(LEADER_ID, 홍석_ID); + + // then + assertThat(newLeader.getMemberId()).isEqualTo(홍석_ID); + assertThat(newLeader.getCrewMemberRole()).isEqualTo(CrewMemberRole.LEADER); + assertThat(leader.getCrewMemberRole()).isEqualTo(CrewMemberRole.PARTICIPANT); + } }