From fb4659d713909518f9567fae3a4066a87ff03477 Mon Sep 17 00:00:00 2001 From: Cho Sangwook <82208159+Sangwook02@users.noreply.github.com> Date: Mon, 12 Feb 2024 23:12:19 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=98=A8=EB=B3=B4=EB=94=A9=20=EB=A9=A4?= =?UTF-8?q?=EB=B2=84=20=EA=B0=80=EC=9E=85=20=EC=8B=A0=EC=B2=AD=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * rename: MemberService를 AdminMemberService로 변경 * feat: 회원 가입 신청 구현 * rename: signupMember로 변경 * refactor: 재학생 인증 상태 검증 추가 --- .../member/api/AdminMemberController.java | 14 ++++----- .../api/OnboardingMemberController.java | 29 +++++++++++++++++++ ...erService.java => AdminMemberService.java} | 2 +- .../application/OnboardingMemberService.java | 29 +++++++++++++++++++ .../gdsc/domain/member/domain/Member.java | 26 +++++++++++++++++ .../domain/member/domain/Requirement.java | 4 +++ .../dto/request/MemberSignupRequest.java | 26 +++++++++++++++++ .../gdsc/global/exception/ErrorCode.java | 5 +++- ...eTest.java => AdminMemberServiceTest.java} | 6 ++-- 9 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/gdschongik/gdsc/domain/member/api/OnboardingMemberController.java rename src/main/java/com/gdschongik/gdsc/domain/member/application/{MemberService.java => AdminMemberService.java} (98%) create mode 100644 src/main/java/com/gdschongik/gdsc/domain/member/application/OnboardingMemberService.java create mode 100644 src/main/java/com/gdschongik/gdsc/domain/member/dto/request/MemberSignupRequest.java rename src/test/java/com/gdschongik/gdsc/domain/member/application/{MemberServiceTest.java => AdminMemberServiceTest.java} (87%) diff --git a/src/main/java/com/gdschongik/gdsc/domain/member/api/AdminMemberController.java b/src/main/java/com/gdschongik/gdsc/domain/member/api/AdminMemberController.java index 809f5edee..5a7d6c5d0 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/member/api/AdminMemberController.java +++ b/src/main/java/com/gdschongik/gdsc/domain/member/api/AdminMemberController.java @@ -1,6 +1,6 @@ package com.gdschongik.gdsc.domain.member.api; -import com.gdschongik.gdsc.domain.member.application.MemberService; +import com.gdschongik.gdsc.domain.member.application.AdminMemberService; import com.gdschongik.gdsc.domain.member.dto.request.MemberGrantRequest; import com.gdschongik.gdsc.domain.member.dto.request.MemberQueryRequest; import com.gdschongik.gdsc.domain.member.dto.request.MemberUpdateRequest; @@ -28,26 +28,26 @@ @RequiredArgsConstructor public class AdminMemberController { - private final MemberService memberService; + private final AdminMemberService adminMemberService; @Operation(summary = "전체 회원 목록 조회", description = "전체 회원 목록을 조회합니다.") @GetMapping public ResponseEntity> getMembers(MemberQueryRequest queryRequest, Pageable pageable) { - Page response = memberService.findAll(queryRequest, pageable); + Page response = adminMemberService.findAll(queryRequest, pageable); return ResponseEntity.ok().body(response); } @Operation(summary = "회원 탈퇴", description = "회원을 탈퇴시킵니다.") @DeleteMapping("/{memberId}") public ResponseEntity withdrawMember(@PathVariable Long memberId) { - memberService.withdrawMember(memberId); + adminMemberService.withdrawMember(memberId); return ResponseEntity.ok().build(); } @Operation(summary = "대기중인 회원 목록 조회", description = "대기중인 회원 목록을 조회합니다.") @GetMapping("/pending") public ResponseEntity> getPendingMembers(Pageable pageable) { - Page response = memberService.findAllPendingMembers(pageable); + Page response = adminMemberService.findAllPendingMembers(pageable); return ResponseEntity.ok().body(response); } @@ -55,14 +55,14 @@ public ResponseEntity> getPendingMembers(Page @PutMapping("/{memberId}") public ResponseEntity updateMember( @PathVariable Long memberId, @Valid @RequestBody MemberUpdateRequest request) { - memberService.updateMember(memberId, request); + adminMemberService.updateMember(memberId, request); return ResponseEntity.ok().build(); } @Operation(summary = "회원 승인", description = "회원의 가입을 승인합니다.") @PutMapping("/grant") public ResponseEntity grantMember(@Valid @RequestBody MemberGrantRequest request) { - MemberGrantResponse response = memberService.grantMember(request); + MemberGrantResponse response = adminMemberService.grantMember(request); return ResponseEntity.ok().body(response); } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/member/api/OnboardingMemberController.java b/src/main/java/com/gdschongik/gdsc/domain/member/api/OnboardingMemberController.java new file mode 100644 index 000000000..98288479c --- /dev/null +++ b/src/main/java/com/gdschongik/gdsc/domain/member/api/OnboardingMemberController.java @@ -0,0 +1,29 @@ +package com.gdschongik.gdsc.domain.member.api; + +import com.gdschongik.gdsc.domain.member.application.OnboardingMemberService; +import com.gdschongik.gdsc.domain.member.dto.request.MemberSignupRequest; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "Onboarding Member", description = "회원 온보딩 API입니다.") +@RestController +@RequestMapping("/onboarding/members") +@RequiredArgsConstructor +public class OnboardingMemberController { + + private final OnboardingMemberService onboardingMemberService; + + @Operation(summary = "회원 가입 신청", description = "회원 가입을 신청합니다.") + @PostMapping + public ResponseEntity signupMember(@Valid @RequestBody MemberSignupRequest request) { + onboardingMemberService.signupMember(request); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/com/gdschongik/gdsc/domain/member/application/MemberService.java b/src/main/java/com/gdschongik/gdsc/domain/member/application/AdminMemberService.java similarity index 98% rename from src/main/java/com/gdschongik/gdsc/domain/member/application/MemberService.java rename to src/main/java/com/gdschongik/gdsc/domain/member/application/AdminMemberService.java index 7eb813bc4..c20b9544a 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/member/application/MemberService.java +++ b/src/main/java/com/gdschongik/gdsc/domain/member/application/AdminMemberService.java @@ -24,7 +24,7 @@ @Service @RequiredArgsConstructor @Transactional(readOnly = true) -public class MemberService { +public class AdminMemberService { private final MemberRepository memberRepository; diff --git a/src/main/java/com/gdschongik/gdsc/domain/member/application/OnboardingMemberService.java b/src/main/java/com/gdschongik/gdsc/domain/member/application/OnboardingMemberService.java new file mode 100644 index 000000000..279b0bcbc --- /dev/null +++ b/src/main/java/com/gdschongik/gdsc/domain/member/application/OnboardingMemberService.java @@ -0,0 +1,29 @@ +package com.gdschongik.gdsc.domain.member.application; + +import com.gdschongik.gdsc.domain.member.domain.Member; +import com.gdschongik.gdsc.domain.member.dto.request.MemberSignupRequest; +import com.gdschongik.gdsc.global.util.MemberUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class OnboardingMemberService { + + private final MemberUtil memberUtil; + + @Transactional + public void signupMember(MemberSignupRequest request) { + Member currentMember = memberUtil.getCurrentMember(); + currentMember.signup( + request.studentId(), + request.name(), + request.phone(), + request.department(), + request.email(), + request.discordUsername(), + request.nickname()); + } +} diff --git a/src/main/java/com/gdschongik/gdsc/domain/member/domain/Member.java b/src/main/java/com/gdschongik/gdsc/domain/member/domain/Member.java index 21a309caf..3e62a495e 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/member/domain/Member.java +++ b/src/main/java/com/gdschongik/gdsc/domain/member/domain/Member.java @@ -98,6 +98,26 @@ public static Member createGuestMember(String oauthId) { .build(); } + public void signup( + String studentId, + String name, + String phone, + String department, + String email, + String discordUsername, + String nickname) { + validateStatusUpdatable(); + validateUnivStatus(); + + this.studentId = studentId; + this.name = name; + this.phone = phone; + this.department = department; + this.email = email; + this.discordUsername = discordUsername; + this.nickname = nickname; + } + public void withdraw() { if (isDeleted()) { throw new CustomException(MEMBER_DELETED); @@ -141,6 +161,12 @@ private void validateStatusUpdatable() { } } + private void validateUnivStatus() { + if (this.requirement.isUnivPending()) { + throw new CustomException(UNIV_NOT_VERIFIED); + } + } + public void grant() { this.role = MemberRole.USER; } diff --git a/src/main/java/com/gdschongik/gdsc/domain/member/domain/Requirement.java b/src/main/java/com/gdschongik/gdsc/domain/member/domain/Requirement.java index b6edbac89..f4f29c21f 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/member/domain/Requirement.java +++ b/src/main/java/com/gdschongik/gdsc/domain/member/domain/Requirement.java @@ -39,4 +39,8 @@ public static Requirement createRequirement() { .paymentStatus(PENDING) .build(); } + + public boolean isUnivPending() { + return this.univStatus == PENDING; + } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/member/dto/request/MemberSignupRequest.java b/src/main/java/com/gdschongik/gdsc/domain/member/dto/request/MemberSignupRequest.java new file mode 100644 index 000000000..86ade754a --- /dev/null +++ b/src/main/java/com/gdschongik/gdsc/domain/member/dto/request/MemberSignupRequest.java @@ -0,0 +1,26 @@ +package com.gdschongik.gdsc.domain.member.dto.request; + +import static com.gdschongik.gdsc.global.common.constant.RegexConstant.*; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; + +public record MemberSignupRequest( + @NotBlank + @Pattern(regexp = STUDENT_ID, message = "학번은 " + STUDENT_ID + " 형식이어야 합니다.") + @Schema(description = "학번", pattern = STUDENT_ID) + String studentId, + @NotBlank @Schema(description = "이름") String name, + @NotBlank + @Pattern(regexp = PHONE, message = "전화번호는 " + PHONE + " 형식이어야 합니다.") + @Schema(description = "전화번호", pattern = PHONE) + String phone, + @NotBlank @Schema(description = "학과") String department, + @NotBlank @Email @Schema(description = "이메일") String email, + @NotBlank @Schema(description = "discord username") String discordUsername, + @NotBlank + @Pattern(regexp = NICKNAME, message = "닉네임은 " + NICKNAME + " 형식이어야 합니다.") + @Schema(description = "커뮤니티 닉네임", pattern = NICKNAME) + String nickname) {} diff --git a/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java b/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java index 05bc663b2..0e39ed850 100644 --- a/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java +++ b/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java @@ -24,7 +24,10 @@ public enum ErrorCode { MEMBER_FORBIDDEN(HttpStatus.CONFLICT, "차단된 회원입니다."), // Parameter - INVALID_QUERY_PARAMETER(HttpStatus.BAD_REQUEST, "잘못된 쿼리 파라미터입니다."); + INVALID_QUERY_PARAMETER(HttpStatus.BAD_REQUEST, "잘못된 쿼리 파라미터입니다."), + + // Requirement + UNIV_NOT_VERIFIED(HttpStatus.CONFLICT, "재학생 인증이 되지 않았습니다."); private final HttpStatus status; private final String message; diff --git a/src/test/java/com/gdschongik/gdsc/domain/member/application/MemberServiceTest.java b/src/test/java/com/gdschongik/gdsc/domain/member/application/AdminMemberServiceTest.java similarity index 87% rename from src/test/java/com/gdschongik/gdsc/domain/member/application/MemberServiceTest.java rename to src/test/java/com/gdschongik/gdsc/domain/member/application/AdminMemberServiceTest.java index b138910cd..00ecd1aa8 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/member/application/MemberServiceTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/member/application/AdminMemberServiceTest.java @@ -12,12 +12,12 @@ import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class MemberServiceTest { +class AdminMemberServiceTest { @Autowired private MemberRepository memberRepository; @Autowired - private MemberService memberService; + private AdminMemberService adminMemberService; @Test void status가_DELETED라면_예외_발생() { @@ -29,7 +29,7 @@ class MemberServiceTest { // when & then MemberUpdateRequest requestBody = new MemberUpdateRequest( "A111111", "name", "010-1234-5678", "department", "email@email.com", "discordUsername", "한글"); - assertThatThrownBy(() -> memberService.updateMember(member.getId(), requestBody)) + assertThatThrownBy(() -> adminMemberService.updateMember(member.getId(), requestBody)) .isInstanceOf(CustomException.class) .hasMessage(ErrorCode.MEMBER_DELETED.getMessage()); }