From 46fe20ec232bd1b08e9169c62ed8f6488131ef72 Mon Sep 17 00:00:00 2001 From: loading1031 Date: Fri, 16 Feb 2024 23:05:33 +0900 Subject: [PATCH 1/4] :sparkles: Feat: create apiPayLoad --- .../common/apiPayload/ApiResult.java | 36 +++++ .../common/apiPayload/code/BaseCode.java | 7 + .../common/apiPayload/code/BaseErrorCode.java | 6 + .../apiPayload/code/ErrorReasonDTO.java | 17 +++ .../common/apiPayload/code/ReasonDTO.java | 17 +++ .../apiPayload/code/status/ErrorStatus.java | 61 +++++++++ .../apiPayload/code/status/SuccessStatus.java | 40 ++++++ .../apiPayload/exception/ExceptionAdvice.java | 128 ++++++++++++++++++ .../exception/GeneralException.java | 18 +++ 9 files changed, 330 insertions(+) create mode 100644 src/main/java/umc/beanstalk/common/apiPayload/ApiResult.java create mode 100644 src/main/java/umc/beanstalk/common/apiPayload/code/BaseCode.java create mode 100644 src/main/java/umc/beanstalk/common/apiPayload/code/BaseErrorCode.java create mode 100644 src/main/java/umc/beanstalk/common/apiPayload/code/ErrorReasonDTO.java create mode 100644 src/main/java/umc/beanstalk/common/apiPayload/code/ReasonDTO.java create mode 100644 src/main/java/umc/beanstalk/common/apiPayload/code/status/ErrorStatus.java create mode 100644 src/main/java/umc/beanstalk/common/apiPayload/code/status/SuccessStatus.java create mode 100644 src/main/java/umc/beanstalk/common/apiPayload/exception/ExceptionAdvice.java create mode 100644 src/main/java/umc/beanstalk/common/apiPayload/exception/GeneralException.java diff --git a/src/main/java/umc/beanstalk/common/apiPayload/ApiResult.java b/src/main/java/umc/beanstalk/common/apiPayload/ApiResult.java new file mode 100644 index 0000000..2f21ebe --- /dev/null +++ b/src/main/java/umc/beanstalk/common/apiPayload/ApiResult.java @@ -0,0 +1,36 @@ +package umc.beanstalk.common.apiPayload; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.AllArgsConstructor; +import lombok.Getter; +import umc.beanstalk.common.apiPayload.code.BaseCode; +import umc.beanstalk.common.apiPayload.code.status.SuccessStatus; + +@Getter +@AllArgsConstructor +@JsonPropertyOrder({"isSuccess","code","message","result"}) +public class ApiResult { + @JsonProperty("isSuccess") + private final Boolean isSuccess; + private final String code; + private final String message; + @JsonInclude(JsonInclude.Include.NON_NULL) + private T result; + + // 실패한 경우 응답 생성 + public static ApiResult onFailure(String code, String message, T data){ + return new ApiResult<>(true, code, message, data); + } + + //성공한 경우 응답 생성 + public static ApiResult onSuccess(T result){ + return new ApiResult<>(true, SuccessStatus._OK.getCode() , SuccessStatus._OK.getMessage(), result); + } + + public static ApiResult of(BaseCode code, T result){ + return new ApiResult<>(true, code.getReasonHttpStatus().getCode(), code.getReasonHttpStatus().getMessage(), result); + } + +} diff --git a/src/main/java/umc/beanstalk/common/apiPayload/code/BaseCode.java b/src/main/java/umc/beanstalk/common/apiPayload/code/BaseCode.java new file mode 100644 index 0000000..bf6e032 --- /dev/null +++ b/src/main/java/umc/beanstalk/common/apiPayload/code/BaseCode.java @@ -0,0 +1,7 @@ +package umc.beanstalk.common.apiPayload.code; + +public interface BaseCode { + public ReasonDTO getReason(); + + public ReasonDTO getReasonHttpStatus(); +} diff --git a/src/main/java/umc/beanstalk/common/apiPayload/code/BaseErrorCode.java b/src/main/java/umc/beanstalk/common/apiPayload/code/BaseErrorCode.java new file mode 100644 index 0000000..8cb9ac7 --- /dev/null +++ b/src/main/java/umc/beanstalk/common/apiPayload/code/BaseErrorCode.java @@ -0,0 +1,6 @@ +package umc.beanstalk.common.apiPayload.code; + +public interface BaseErrorCode { + public ErrorReasonDTO getReason(); + public ErrorReasonDTO getReasonHttpStatus(); +} diff --git a/src/main/java/umc/beanstalk/common/apiPayload/code/ErrorReasonDTO.java b/src/main/java/umc/beanstalk/common/apiPayload/code/ErrorReasonDTO.java new file mode 100644 index 0000000..4bc2886 --- /dev/null +++ b/src/main/java/umc/beanstalk/common/apiPayload/code/ErrorReasonDTO.java @@ -0,0 +1,17 @@ +package umc.beanstalk.common.apiPayload.code; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@Builder +public class ErrorReasonDTO { + private HttpStatus httpStatus; + + private final Boolean isSuccess; + private final String code; + private final String message; + + public boolean getIsSuccess(){return isSuccess;} +} diff --git a/src/main/java/umc/beanstalk/common/apiPayload/code/ReasonDTO.java b/src/main/java/umc/beanstalk/common/apiPayload/code/ReasonDTO.java new file mode 100644 index 0000000..ec72c20 --- /dev/null +++ b/src/main/java/umc/beanstalk/common/apiPayload/code/ReasonDTO.java @@ -0,0 +1,17 @@ +package umc.beanstalk.common.apiPayload.code; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpStatus; +@Getter + +@Builder +public class ReasonDTO { + private HttpStatus httpStatus; + + private final boolean isSuccess; + private final String code; + private final String message; + + public boolean getIsSuccess(){return isSuccess;} +} diff --git a/src/main/java/umc/beanstalk/common/apiPayload/code/status/ErrorStatus.java b/src/main/java/umc/beanstalk/common/apiPayload/code/status/ErrorStatus.java new file mode 100644 index 0000000..75a1289 --- /dev/null +++ b/src/main/java/umc/beanstalk/common/apiPayload/code/status/ErrorStatus.java @@ -0,0 +1,61 @@ +package umc.beanstalk.common.apiPayload.code.status; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; +import umc.study.apiPayload.code.BaseErrorCode; +import umc.study.apiPayload.code.ErrorReasonDTO; + +@Getter +@AllArgsConstructor +public enum ErrorStatus implements BaseErrorCode { + // 가장 일반적인 응답 + _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,"COMMON500","서버 에러, 관리자에게 문의 바랍니다."), + _BAD_REQUEST(HttpStatus.BAD_REQUEST,"COMMON400","잘못된 요청입니다."), + _UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"COMMON401","인증이 필요합니다."), + _FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."), + // 멤버 관련 응답 + MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST,"MEMBER4001","사용자가 없습니다."), + NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST,"MEMBER4002", "닉네임은 필수 입니다."), + // 멤버 선호 음식 + FOOD_CATEGORY_NOT_FOUND(HttpStatus.BAD_REQUEST,"PREFER4001","음식 카테고리가 없습니다."), + // 유저 미션 + ACCEPT_MISSION_NOT_FOUND(HttpStatus.BAD_REQUEST,"USERMISSION4001","유저가 수락한 미션이 없습니다."), + //식당 관련 응답 + STORE_NOT_FOUND(HttpStatus.BAD_REQUEST,"STORER4002","식당명은 필수 입니다."), + STORE_NOT_EXIST(HttpStatus.BAD_REQUEST,"STORE4001", "식당이 없습니다."), + // 지역 관련 응답 + REGION_NOT_FOUND(HttpStatus.BAD_REQUEST,"REGION4001","해당 지역이 없습니다."), + //미션 관련 응답 + MISSION_NOT_EXIST(HttpStatus.BAD_REQUEST,"MISSION4001", "미션이 없습니다."), + Mission_Already_Accept(HttpStatus.BAD_REQUEST,"MISSION4002", "이미 수락한 미션입니다."), + MISSION_COMPLETE(HttpStatus.BAD_REQUEST,"MISSION4003", "이미 완료한 미션입니다."), + //페이지 관련 + PAGE_NOT_FOUND(HttpStatus.BAD_REQUEST,"PAGE4001","해당 페이지가 없습니다."), + // 예시 ... + ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "게시글이 없습니다."), + TEMP_EXCEPTION(HttpStatus.BAD_REQUEST,"TEMP40001","이거는 테스트"); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReasonDTO getReason(){ + return ErrorReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(false) + .build(); + } + @Override + public ErrorReasonDTO getReasonHttpStatus() { + return ErrorReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(false) + .httpStatus(httpStatus) + .build() + ; + } +} diff --git a/src/main/java/umc/beanstalk/common/apiPayload/code/status/SuccessStatus.java b/src/main/java/umc/beanstalk/common/apiPayload/code/status/SuccessStatus.java new file mode 100644 index 0000000..961dc94 --- /dev/null +++ b/src/main/java/umc/beanstalk/common/apiPayload/code/status/SuccessStatus.java @@ -0,0 +1,40 @@ +package umc.beanstalk.common.apiPayload.code.status; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; +import umc.study.apiPayload.code.BaseCode; +import umc.study.apiPayload.code.ReasonDTO; + +@Getter +@AllArgsConstructor +public enum SuccessStatus implements BaseCode{ + // 일반적인 응답 + _OK(HttpStatus.OK, "COMMON200", "성공입니다."); + + // 멤버 관련 응답 + + // ~~ 관련 응답 + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ReasonDTO getReason() { + return ReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(true) + .build(); + } + + @Override + public ReasonDTO getReasonHttpStatus() { + return ReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(true) + .httpStatus(httpStatus) + .build(); + } +} diff --git a/src/main/java/umc/beanstalk/common/apiPayload/exception/ExceptionAdvice.java b/src/main/java/umc/beanstalk/common/apiPayload/exception/ExceptionAdvice.java new file mode 100644 index 0000000..cde3da6 --- /dev/null +++ b/src/main/java/umc/beanstalk/common/apiPayload/exception/ExceptionAdvice.java @@ -0,0 +1,128 @@ +package umc.beanstalk.common.apiPayload.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +import umc.beanstalk.common.apiPayload.ApiResult; +import umc.beanstalk.common.apiPayload.code.ErrorReasonDTO; +import umc.beanstalk.common.apiPayload.code.status.ErrorStatus; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +@Slf4j +@RestControllerAdvice(annotations = {RestController.class}) +public class ExceptionAdvice extends ResponseEntityExceptionHandler { + + @ExceptionHandler(ConstraintViolationException.class) + public ResponseEntity handleConstraintViolationException(ConstraintViolationException e, WebRequest request) { + String errorMessage = e.getConstraintViolations().stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.joining("; ")); + + return handleExceptionInternalConstraint(e, ErrorStatus.valueOf(errorMessage), HttpHeaders.EMPTY, request); + } + + @Override + public ResponseEntity handleMethodArgumentNotValid( + MethodArgumentNotValidException e, HttpHeaders headers, HttpStatus status, WebRequest request) { + + Map errors = new LinkedHashMap<>(); + + e.getBindingResult().getFieldErrors().stream() + .forEach(fieldError -> { + String fieldName = fieldError.getField(); + String errorMessage = Optional.ofNullable(fieldError.getDefaultMessage()).orElse(""); + errors.merge(fieldName, errorMessage, (existingErrorMessage, newErrorMessage) -> existingErrorMessage + ", " + newErrorMessage); + }); + + return handleExceptionInternalArgs(e, HttpHeaders.EMPTY, ErrorStatus.valueOf("_BAD_REQUEST"), request, errors); + } + + @ExceptionHandler + public ResponseEntity exception(Exception e, WebRequest request) { + e.printStackTrace(); + + return handleExceptionInternalFalse(e, ErrorStatus._INTERNAL_SERVER_ERROR, HttpHeaders.EMPTY, ErrorStatus._INTERNAL_SERVER_ERROR.getHttpStatus(), request, e.getMessage()); + } + + @ExceptionHandler(value = GeneralException.class) + public ResponseEntity onThrowException(GeneralException generalException, HttpServletRequest request) { + ErrorReasonDTO errorReasonHttpStatus = generalException.getErrorReasonHttpStatus(); + return handleExceptionInternal(generalException, errorReasonHttpStatus, null, request); + } + /* + @ExceptionHandler(ConstraintViolationException.class) + public ResponseEntity handleConstraintViolationException(ConstraintViolationException ex) { + // 여기서 ConstraintViolationException에서 얻은 정보를 활용하여 적절한 응답을 생성 + // ex.getMessage() 등을 활용하여 에러 메시지를 추출할 수 있음 + String errorMessage = "Validation failed: " + ex.getMessage(); + return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST); + } + + */ + private ResponseEntity handleExceptionInternal(Exception e, ErrorReasonDTO reason, + HttpHeaders headers, HttpServletRequest request) { + + ApiResult body = ApiResult.onFailure(reason.getCode(), reason.getMessage(), null); +// e.printStackTrace(); + + WebRequest webRequest = new ServletWebRequest(request); + return super.handleExceptionInternal( + e, + body, + headers, + reason.getHttpStatus(), + webRequest + ); + } + + private ResponseEntity handleExceptionInternalFalse(Exception e, ErrorStatus errorCommonStatus, + HttpHeaders headers, HttpStatus status, WebRequest request, String errorPoint) { + ApiResult body = ApiResult.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), errorPoint); + return super.handleExceptionInternal( + e, + body, + headers, + status, + request + ); + } + + private ResponseEntity handleExceptionInternalArgs(Exception e, HttpHeaders headers, ErrorStatus errorCommonStatus, + WebRequest request, Map errorArgs) { + ApiResult body = ApiResult.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), errorArgs); + return super.handleExceptionInternal( + e, + body, + headers, + errorCommonStatus.getHttpStatus(), + request + ); + } + + private ResponseEntity handleExceptionInternalConstraint(Exception e, ErrorStatus errorCommonStatus, + HttpHeaders headers, WebRequest request) { + ApiResult body = ApiResult.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), null); + return super.handleExceptionInternal( + e, + body, + headers, + errorCommonStatus.getHttpStatus(), + request + ); + } +} diff --git a/src/main/java/umc/beanstalk/common/apiPayload/exception/GeneralException.java b/src/main/java/umc/beanstalk/common/apiPayload/exception/GeneralException.java new file mode 100644 index 0000000..3ed6de4 --- /dev/null +++ b/src/main/java/umc/beanstalk/common/apiPayload/exception/GeneralException.java @@ -0,0 +1,18 @@ +package umc.beanstalk.common.apiPayload.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import umc.beanstalk.common.apiPayload.code.BaseErrorCode; +import umc.beanstalk.common.apiPayload.code.ErrorReasonDTO; + +@Getter +@AllArgsConstructor +public class GeneralException extends RuntimeException{ + private BaseErrorCode code; + public ErrorReasonDTO getErrorReason(){ + return this.code.getReason(); + } + public ErrorReasonDTO getErrorReasonHttpStatus(){ + return this.code.getReasonHttpStatus(); + } +} From 7cfd83db90bcc932b05bf99332fa67259631262e Mon Sep 17 00:00:00 2001 From: loading1031 Date: Fri, 16 Feb 2024 23:11:12 +0900 Subject: [PATCH 2/4] :heavy_plus_sign: Add: validation --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 3e0de46..279d3e5 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,7 @@ dependencies { implementation 'io.springfox:springfox-swagger-ui:2.9.2' //aws implementation group: 'io.awspring.cloud', name: 'spring-cloud-starter-aws', version: '2.4.4' + implementation 'org.springframework.boot:spring-boot-starter-validation' } jar { From f5eac15355310b145cf6121946f243961dba9abf Mon Sep 17 00:00:00 2001 From: loading1031 Date: Fri, 16 Feb 2024 23:11:42 +0900 Subject: [PATCH 3/4] :bug: Fix: status --- .../common/apiPayload/code/status/ErrorStatus.java | 4 ++-- .../common/apiPayload/code/status/SuccessStatus.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/umc/beanstalk/common/apiPayload/code/status/ErrorStatus.java b/src/main/java/umc/beanstalk/common/apiPayload/code/status/ErrorStatus.java index 75a1289..f2961e5 100644 --- a/src/main/java/umc/beanstalk/common/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/umc/beanstalk/common/apiPayload/code/status/ErrorStatus.java @@ -3,8 +3,8 @@ import lombok.AllArgsConstructor; import lombok.Getter; import org.springframework.http.HttpStatus; -import umc.study.apiPayload.code.BaseErrorCode; -import umc.study.apiPayload.code.ErrorReasonDTO; +import umc.beanstalk.common.apiPayload.code.BaseErrorCode; +import umc.beanstalk.common.apiPayload.code.ErrorReasonDTO; @Getter @AllArgsConstructor diff --git a/src/main/java/umc/beanstalk/common/apiPayload/code/status/SuccessStatus.java b/src/main/java/umc/beanstalk/common/apiPayload/code/status/SuccessStatus.java index 961dc94..7f6961b 100644 --- a/src/main/java/umc/beanstalk/common/apiPayload/code/status/SuccessStatus.java +++ b/src/main/java/umc/beanstalk/common/apiPayload/code/status/SuccessStatus.java @@ -3,12 +3,12 @@ import lombok.AllArgsConstructor; import lombok.Getter; import org.springframework.http.HttpStatus; -import umc.study.apiPayload.code.BaseCode; -import umc.study.apiPayload.code.ReasonDTO; +import umc.beanstalk.common.apiPayload.code.BaseCode; +import umc.beanstalk.common.apiPayload.code.ReasonDTO; @Getter @AllArgsConstructor -public enum SuccessStatus implements BaseCode{ +public enum SuccessStatus implements BaseCode { // 일반적인 응답 _OK(HttpStatus.OK, "COMMON200", "성공입니다."); From 0c14863b45d179f80125ab7097d47a6451c8a289 Mon Sep 17 00:00:00 2001 From: loading1031 Date: Fri, 16 Feb 2024 23:17:01 +0900 Subject: [PATCH 4/4] :sparkles: Feat: create Choice and BaseEntity --- .../beanstalk/common/domain/BaseEntity.java | 23 +++++++++++++++++++ .../user_choice/data/domain/UserChoice.java | 19 +++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/main/java/umc/beanstalk/common/domain/BaseEntity.java create mode 100644 src/main/java/umc/beanstalk/user_choice/data/domain/UserChoice.java diff --git a/src/main/java/umc/beanstalk/common/domain/BaseEntity.java b/src/main/java/umc/beanstalk/common/domain/BaseEntity.java new file mode 100644 index 0000000..741a433 --- /dev/null +++ b/src/main/java/umc/beanstalk/common/domain/BaseEntity.java @@ -0,0 +1,23 @@ +package umc.beanstalk.common.domain; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import java.time.LocalDateTime; + +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +@Getter +public class BaseEntity { + @CreatedDate + private LocalDateTime createdAt; + + @Setter + @LastModifiedDate + private LocalDateTime updatedAt; +} diff --git a/src/main/java/umc/beanstalk/user_choice/data/domain/UserChoice.java b/src/main/java/umc/beanstalk/user_choice/data/domain/UserChoice.java new file mode 100644 index 0000000..17f9004 --- /dev/null +++ b/src/main/java/umc/beanstalk/user_choice/data/domain/UserChoice.java @@ -0,0 +1,19 @@ +package umc.beanstalk.user_choice.data.domain; + +import lombok.*; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class UserChoice { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + Long id; +}