Skip to content

Commit

Permalink
feat: 어드민 멤버 정보 수정 api 구현 (#39)
Browse files Browse the repository at this point in the history
* feat: 어드민 멤버 수정 api 추가

* feat: 어드민 멤버 수정 request dto 추가

* feat: 정규표현식 상수 클래스 추가

* feat: 어드민 멤버 수정 서비스 추가

* feat: 멤버 수정 메서드 추가

* feat: 존재하지 않는 멤버 예외 추가

* fix: 닉네임 최소 1자 이상이도록 수정

* fix: RegexConstant 수정

* style: 개행 제거

* feat: 검증 메서드 추가

* refactor: update 메서드 수정

* feat: ErrorCode 추가

* feat: 삭제된 멤버 수정 못하도록 검증 추가

* fix: 필드명 변경

* refactor: if문 제거

* refactor: NotBlank로 변경

* feat: dto 검증 예외 처리 메서드 생성

* test: 탈퇴한 회원 정보 수정 시 예외 처리 테스트 추가

* refactor: 탈퇴 여부 확인을 MemberStatus이 처리

* fix: 메서드명 수정

* remove: 중복 null-check 제거

* fix: 메서드명 수정

* refactor: 멤버 상태 검증 시 차단 여부도 확인

* refactor: db에 하이픈 없이 저장하도록 수정

* fix: 메서드명 수정

* refactor: 검증 메서드를 수정 메서드 내부로 이동

* refactor: ErrorCode의 이름을 Response 내에서 처리하도록 수정

* refactor: 수정 로직 수정

* remove: 사용하지 않는 import 제거

* style: spotless apply
  • Loading branch information
Sangwook02 authored Feb 11, 2024
1 parent b6197e1 commit f6865b9
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

import com.gdschongik.gdsc.domain.member.application.MemberService;
import com.gdschongik.gdsc.domain.member.dto.request.MemberQueryRequest;
import com.gdschongik.gdsc.domain.member.dto.request.MemberUpdateRequest;
import com.gdschongik.gdsc.domain.member.dto.response.MemberFindAllResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -31,4 +35,11 @@ public ResponseEntity<Void> withdrawMember(@PathVariable Long memberId) {
memberService.withdrawMember(memberId);
return ResponseEntity.ok().build();
}

@PutMapping("/{memberId}")
public ResponseEntity<Void> updateMember(
@PathVariable Long memberId, @Valid @RequestBody MemberUpdateRequest request) {
memberService.updateMember(memberId, request);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import com.gdschongik.gdsc.domain.member.dao.MemberRepository;
import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.member.dto.request.MemberQueryRequest;
import com.gdschongik.gdsc.domain.member.dto.request.MemberUpdateRequest;
import com.gdschongik.gdsc.domain.member.dto.response.MemberFindAllResponse;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.exception.ErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand All @@ -30,4 +32,11 @@ public void withdrawMember(Long memberId) {
Member member = memberRepository.findById(memberId).orElseThrow(() -> new CustomException(MEMBER_NOT_FOUND));
member.withdraw();
}

@Transactional
public void updateMember(Long memberId, MemberUpdateRequest request) {
Member member =
memberRepository.findById(memberId).orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND));
member.updateMemberInfo(request);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static com.gdschongik.gdsc.global.exception.ErrorCode.*;

import com.gdschongik.gdsc.domain.common.model.BaseTimeEntity;
import com.gdschongik.gdsc.domain.member.dto.request.MemberUpdateRequest;
import com.gdschongik.gdsc.domain.requirement.domain.Requirement;
import com.gdschongik.gdsc.global.exception.CustomException;
import jakarta.persistence.Column;
Expand Down Expand Up @@ -106,7 +107,32 @@ public void withdraw() {
this.status = MemberStatus.DELETED;
}

public boolean isDeleted() {
private boolean isDeleted() {
return this.status.isDeleted();
}

private boolean isForbidden() {
return this.status.isForbidden();
}

public void updateMemberInfo(MemberUpdateRequest request) {
validateStatusUpdatable();

this.studentId = request.studentId();
this.name = request.name();
this.phone = request.phone();
this.department = request.department();
this.email = request.email();
this.discordUsername = request.discordUsername();
this.nickname = request.nickname();
}

private void validateStatusUpdatable() {
if (isDeleted()) {
throw new CustomException(MEMBER_DELETED);
}
if (isForbidden()) {
throw new CustomException(MEMBER_FORBIDDEN);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ public enum MemberStatus {
public boolean isDeleted() {
return this.equals(DELETED);
}

public boolean isForbidden() {
return this.equals(FORBIDDEN);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.gdschongik.gdsc.domain.member.dto.request;

import static com.gdschongik.gdsc.global.common.constant.RegexConstant.*;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;

public record MemberUpdateRequest(
@NotBlank @Pattern(regexp = STUDENT_ID, message = "학번은 " + STUDENT_ID + " 형식이어야 합니다.") String studentId,
@NotBlank String name,
@NotBlank @Pattern(regexp = PHONE, message = "전화번호는 " + PHONE + " 형식이어야 합니다.") String phone,
@NotBlank String department,
@NotBlank @Email String email,
@NotBlank String discordUsername,
@NotBlank @Pattern(regexp = NICKNAME, message = "닉네임은 " + NICKNAME + " 형식이어야 합니다.") String nickname) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.gdschongik.gdsc.global.common.constant;

public class RegexConstant {
public static final String STUDENT_ID = "^[A-C]{1}[0-9]{6}$";
public static final String PHONE = "^010-[0-9]{4}-[0-9]{4}$";
public static final String PHONE_WITHOUT_HYPHEN = "^010[0-9]{8}$";
public static final String NICKNAME = "[ㄱ-ㅣ가-힣]{1,6}$";

private RegexConstant() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@
@AllArgsConstructor
public enum ErrorCode {
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 에러입니다."),
METHOD_ARGUMENT_NULL(HttpStatus.BAD_REQUEST, "인자는 null이 될 수 없습니다."),
METHOD_ARGUMENT_NOT_VALID(HttpStatus.BAD_REQUEST, "인자가 유효하지 않습니다."),
REGEX_VIOLATION(HttpStatus.BAD_REQUEST, "정규표현식을 위반했습니다."),

// Auth
INVALID_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 JWT 토큰입니다."),
EXPIRED_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 JWT 토큰입니다."),
AUTH_NOT_EXIST(HttpStatus.INTERNAL_SERVER_ERROR, "시큐리티 인증 정보가 존재하지 않습니다."),
AUTH_NOT_PARSABLE(HttpStatus.INTERNAL_SERVER_ERROR, "시큐리티 인증 정보 파싱에 실패했습니다."),

// Parameter
INVALID_QUERY_PARAMETER(HttpStatus.BAD_REQUEST, "잘못된 쿼리 파라미터입니다."),

// Member
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다."),
MEMBER_DELETED(HttpStatus.CONFLICT, "탈퇴한 회원입니다."),
;
MEMBER_FORBIDDEN(HttpStatus.CONFLICT, "차단된 회원입니다."),

// Parameter
INVALID_QUERY_PARAMETER(HttpStatus.BAD_REQUEST, "잘못된 쿼리 파라미터입니다.");

private final HttpStatus status;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ public record ErrorResponse(String errorCodeName, String errorMessage) {
public static ErrorResponse of(ErrorCode errorCode) {
return new ErrorResponse(errorCode.name(), errorCode.getMessage());
}

public static ErrorResponse of(ErrorCode errorCode, String errorMessage) {
return new ErrorResponse(errorCode.name(), errorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.gdschongik.gdsc.global.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@Slf4j
Expand All @@ -22,4 +26,13 @@ public ResponseEntity<ErrorResponse> handleException(Exception e) {
return ResponseEntity.status(ErrorCode.INTERNAL_SERVER_ERROR.getStatus())
.body(ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR));
}

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException e, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
log.error("METHOD_ARGUMENT_NOT_VALID : {}", e.getMessage(), e);
String errorMessage = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return ResponseEntity.status(status.value())
.body(ErrorResponse.of(ErrorCode.METHOD_ARGUMENT_NOT_VALID, errorMessage));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.gdschongik.gdsc.domain.member.application;

import static org.assertj.core.api.Assertions.*;

import com.gdschongik.gdsc.domain.member.dao.MemberRepository;
import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.member.dto.request.MemberUpdateRequest;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.exception.ErrorCode;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MemberServiceTest {
@Autowired
private MemberRepository memberRepository;

@Autowired
private MemberService memberService;

@Test
void status가_DELETED라면_예외_발생() {
// given
Member member = Member.createGuestMember("oAuthId");
member.withdraw();
memberRepository.save(member);

// when & then
MemberUpdateRequest requestBody = new MemberUpdateRequest(
"A111111", "name", "010-1234-5678", "department", "[email protected]", "discordUsername", "한글");
assertThatThrownBy(() -> memberService.updateMember(member.getId(), requestBody))
.isInstanceOf(CustomException.class)
.hasMessage(ErrorCode.MEMBER_DELETED.getMessage());
}
}

0 comments on commit f6865b9

Please sign in to comment.