From 5efb00fde473037259b5cc5e6428a7072ededee1 Mon Sep 17 00:00:00 2001 From: Choi-seokun <142799427+Choi-seokun@users.noreply.github.com> Date: Mon, 11 Nov 2024 22:24:52 +0900 Subject: [PATCH 1/3] mission --- .../umc/spring/apiPayload/ApiResponse.java | 35 ++++++ .../umc/spring/apiPayload/code/BaseCode.java | 9 ++ .../spring/apiPayload/code/BaseErrorCode.java | 7 ++ .../apiPayload/code/ErrorReasonDTO.java | 17 +++ .../umc/spring/apiPayload/code/ReasonDTO.java | 17 +++ .../apiPayload/code/status/ErrorStatus.java | 52 ++++++++ .../apiPayload/code/status/SuccessStatus.java | 38 ++++++ .../apiPayload/exception/ExceptionAdvice.java | 119 ++++++++++++++++++ .../exception/GeneralException.java | 20 +++ .../exception/handler/TempHandler.java | 10 ++ .../umc/spring/converter/TempConverter.java | 17 +++ .../RestaurantQueryServiceImpl.java | 1 + .../RestaurantService/TempQueryService.java | 5 + .../TempQueryServiceImpl.java | 16 +++ .../web/controller/TempRestController.java | 30 +++++ .../java/umc/spring/web/dto/TempRequest.java | 4 + .../java/umc/spring/web/dto/TempResponse.java | 24 ++++ 17 files changed, 421 insertions(+) create mode 100644 src/main/java/umc/spring/apiPayload/ApiResponse.java create mode 100644 src/main/java/umc/spring/apiPayload/code/BaseCode.java create mode 100644 src/main/java/umc/spring/apiPayload/code/BaseErrorCode.java create mode 100644 src/main/java/umc/spring/apiPayload/code/ErrorReasonDTO.java create mode 100644 src/main/java/umc/spring/apiPayload/code/ReasonDTO.java create mode 100644 src/main/java/umc/spring/apiPayload/code/status/ErrorStatus.java create mode 100644 src/main/java/umc/spring/apiPayload/code/status/SuccessStatus.java create mode 100644 src/main/java/umc/spring/apiPayload/exception/ExceptionAdvice.java create mode 100644 src/main/java/umc/spring/apiPayload/exception/GeneralException.java create mode 100644 src/main/java/umc/spring/apiPayload/exception/handler/TempHandler.java create mode 100644 src/main/java/umc/spring/converter/TempConverter.java create mode 100644 src/main/java/umc/spring/service/RestaurantService/TempQueryService.java create mode 100644 src/main/java/umc/spring/service/RestaurantService/TempQueryServiceImpl.java create mode 100644 src/main/java/umc/spring/web/controller/TempRestController.java create mode 100644 src/main/java/umc/spring/web/dto/TempRequest.java create mode 100644 src/main/java/umc/spring/web/dto/TempResponse.java diff --git a/src/main/java/umc/spring/apiPayload/ApiResponse.java b/src/main/java/umc/spring/apiPayload/ApiResponse.java new file mode 100644 index 0000000..d4915e2 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/ApiResponse.java @@ -0,0 +1,35 @@ +package umc.spring.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.spring.apiPayload.code.BaseCode; +import umc.spring.apiPayload.code.status.SuccessStatus; + +@Getter +@AllArgsConstructor +@JsonPropertyOrder({"isSuccess", "code", "message", "result"}) +public class ApiResponse { + + @JsonProperty("isSuccess") + private final Boolean isSuccess; + private final String code; + private final String message; + @JsonInclude(JsonInclude.Include.NON_NULL) + private T result; + + //성공한 경우 응답 생성 + public static ApiResponse onSuccess(T result){ + return new ApiResponse<>(true, SuccessStatus._OK.getCode() , SuccessStatus._OK.getMessage(), result); + } + + public static ApiResponse of(BaseCode code, T result){ + return new ApiResponse<>(true, code.getReasonHttpStatus().getCode() , code.getReasonHttpStatus().getMessage(), result); + } + + public static ApiResponse onFailure(String code, String message, T data){ + return new ApiResponse<>(false, code, message, data); + } +} diff --git a/src/main/java/umc/spring/apiPayload/code/BaseCode.java b/src/main/java/umc/spring/apiPayload/code/BaseCode.java new file mode 100644 index 0000000..2716dbd --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/code/BaseCode.java @@ -0,0 +1,9 @@ +package umc.spring.apiPayload.code; + +import java.security.cert.CertPathValidatorException; + +public interface BaseCode { + ReasonDTO getReason(); + + ReasonDTO getReasonHttpStatus(); +} diff --git a/src/main/java/umc/spring/apiPayload/code/BaseErrorCode.java b/src/main/java/umc/spring/apiPayload/code/BaseErrorCode.java new file mode 100644 index 0000000..c5fa04e --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/code/BaseErrorCode.java @@ -0,0 +1,7 @@ +package umc.spring.apiPayload.code; + +public interface BaseErrorCode { + ErrorReasonDTO getReason(); + + ErrorReasonDTO getReasonHttpStatus(); +} diff --git a/src/main/java/umc/spring/apiPayload/code/ErrorReasonDTO.java b/src/main/java/umc/spring/apiPayload/code/ErrorReasonDTO.java new file mode 100644 index 0000000..668dcca --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/code/ErrorReasonDTO.java @@ -0,0 +1,17 @@ +package umc.spring.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/spring/apiPayload/code/ReasonDTO.java b/src/main/java/umc/spring/apiPayload/code/ReasonDTO.java new file mode 100644 index 0000000..eee2f5b --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/code/ReasonDTO.java @@ -0,0 +1,17 @@ +package umc.spring.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/spring/apiPayload/code/status/ErrorStatus.java b/src/main/java/umc/spring/apiPayload/code/status/ErrorStatus.java new file mode 100644 index 0000000..a64d3c7 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/code/status/ErrorStatus.java @@ -0,0 +1,52 @@ +package umc.spring.apiPayload.code.status; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; +import umc.spring.apiPayload.code.BaseErrorCode; +import umc.spring.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", "닉네임은 필수 입니다."), + + // 예시,,, + ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "게시글이 없습니다."), + + //테스트 에러 + TEMP_EXCEPTION(HttpStatus.BAD_REQUEST, "TEMP4001", "이거는 테스트"); + + 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/spring/apiPayload/code/status/SuccessStatus.java b/src/main/java/umc/spring/apiPayload/code/status/SuccessStatus.java new file mode 100644 index 0000000..253d30f --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/code/status/SuccessStatus.java @@ -0,0 +1,38 @@ +package umc.spring.apiPayload.code.status; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; +import umc.spring.apiPayload.code.BaseCode; +import umc.spring.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/spring/apiPayload/exception/ExceptionAdvice.java b/src/main/java/umc/spring/apiPayload/exception/ExceptionAdvice.java new file mode 100644 index 0000000..8b81fa3 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/ExceptionAdvice.java @@ -0,0 +1,119 @@ +package umc.spring.apiPayload.exception; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.ConstraintViolationException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +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.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.spring.apiPayload.ApiResponse; +import umc.spring.apiPayload.code.ErrorReasonDTO; +import umc.spring.apiPayload.code.status.ErrorStatus; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +@Slf4j +@RestControllerAdvice(annotations = {RestController.class}) +public class ExceptionAdvice extends ResponseEntityExceptionHandler { + + + @ExceptionHandler + public ResponseEntity validation(ConstraintViolationException e, WebRequest request) { + String errorMessage = e.getConstraintViolations().stream() + .map(constraintViolation -> constraintViolation.getMessage()) + .findFirst() + .orElseThrow(() -> new RuntimeException("ConstraintViolationException 추출 도중 에러 발생")); + + return handleExceptionInternalConstraint(e, ErrorStatus.valueOf(errorMessage), HttpHeaders.EMPTY,request); + } + + @Override + public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException e, HttpHeaders headers, HttpStatusCode 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); + } + + private ResponseEntity handleExceptionInternal(Exception e, ErrorReasonDTO reason, + HttpHeaders headers, HttpServletRequest request) { + + ApiResponse body = ApiResponse.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) { + ApiResponse body = ApiResponse.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) { + ApiResponse body = ApiResponse.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) { + ApiResponse body = ApiResponse.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), null); + return super.handleExceptionInternal( + e, + body, + headers, + errorCommonStatus.getHttpStatus(), + request + ); + } +} diff --git a/src/main/java/umc/spring/apiPayload/exception/GeneralException.java b/src/main/java/umc/spring/apiPayload/exception/GeneralException.java new file mode 100644 index 0000000..e8cfe92 --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/GeneralException.java @@ -0,0 +1,20 @@ +package umc.spring.apiPayload.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import umc.spring.apiPayload.code.BaseErrorCode; +import umc.spring.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(); + } +} diff --git a/src/main/java/umc/spring/apiPayload/exception/handler/TempHandler.java b/src/main/java/umc/spring/apiPayload/exception/handler/TempHandler.java new file mode 100644 index 0000000..5a07f8c --- /dev/null +++ b/src/main/java/umc/spring/apiPayload/exception/handler/TempHandler.java @@ -0,0 +1,10 @@ +package umc.spring.apiPayload.exception.handler; + +import umc.spring.apiPayload.code.BaseErrorCode; +import umc.spring.apiPayload.exception.GeneralException; + +public class TempHandler extends GeneralException { + public TempHandler(BaseErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/umc/spring/converter/TempConverter.java b/src/main/java/umc/spring/converter/TempConverter.java new file mode 100644 index 0000000..cfba835 --- /dev/null +++ b/src/main/java/umc/spring/converter/TempConverter.java @@ -0,0 +1,17 @@ +package umc.spring.converter; + +import umc.spring.web.dto.TempResponse; + +public class TempConverter { + public static TempResponse.TempTestDTO toTempTestDTO(){ + return TempResponse.TempTestDTO.builder() + .testString("This is Test!") + .build(); + } + + public static TempResponse.TempExceptionDTO toTempExceptionDTO(Integer flag){ + return TempResponse.TempExceptionDTO.builder() + .flag(flag) + .build(); + } +} diff --git a/src/main/java/umc/spring/service/RestaurantService/RestaurantQueryServiceImpl.java b/src/main/java/umc/spring/service/RestaurantService/RestaurantQueryServiceImpl.java index ad64276..917181f 100644 --- a/src/main/java/umc/spring/service/RestaurantService/RestaurantQueryServiceImpl.java +++ b/src/main/java/umc/spring/service/RestaurantService/RestaurantQueryServiceImpl.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import umc.spring.apiPayload.exception.handler.TempHandler; import umc.spring.domain.restaurant; import umc.spring.repository.RestaurantRepository.RestaurantRepository; diff --git a/src/main/java/umc/spring/service/RestaurantService/TempQueryService.java b/src/main/java/umc/spring/service/RestaurantService/TempQueryService.java new file mode 100644 index 0000000..707c58f --- /dev/null +++ b/src/main/java/umc/spring/service/RestaurantService/TempQueryService.java @@ -0,0 +1,5 @@ +package umc.spring.service.RestaurantService; + +public interface TempQueryService { + void CheckFlag(Integer flag); +} diff --git a/src/main/java/umc/spring/service/RestaurantService/TempQueryServiceImpl.java b/src/main/java/umc/spring/service/RestaurantService/TempQueryServiceImpl.java new file mode 100644 index 0000000..0c47ffe --- /dev/null +++ b/src/main/java/umc/spring/service/RestaurantService/TempQueryServiceImpl.java @@ -0,0 +1,16 @@ +package umc.spring.service.RestaurantService; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import umc.spring.apiPayload.code.status.ErrorStatus; +import umc.spring.apiPayload.exception.handler.TempHandler; + +@Service +@RequiredArgsConstructor +public class TempQueryServiceImpl implements TempQueryService{ + @Override + public void CheckFlag(Integer flag) { + if (flag == 1) + throw new TempHandler(ErrorStatus.TEMP_EXCEPTION); + } +} diff --git a/src/main/java/umc/spring/web/controller/TempRestController.java b/src/main/java/umc/spring/web/controller/TempRestController.java new file mode 100644 index 0000000..1f2e4e7 --- /dev/null +++ b/src/main/java/umc/spring/web/controller/TempRestController.java @@ -0,0 +1,30 @@ +package umc.spring.web.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import umc.spring.apiPayload.ApiResponse; +import umc.spring.converter.TempConverter; +import umc.spring.service.RestaurantService.TempQueryService; +import umc.spring.web.dto.TempResponse; + +@RestController +@RequestMapping("/temp") +@RequiredArgsConstructor +public class TempRestController { + private final TempQueryService tempQueryService; + @GetMapping("/test") + public ApiResponse testAPI(){ + + return ApiResponse.onSuccess(TempConverter.toTempTestDTO()); + } + + @GetMapping("/exception") + public ApiResponse exceptionAPI(@RequestParam Integer flag){ + tempQueryService.CheckFlag(flag); + return ApiResponse.onSuccess(TempConverter.toTempExceptionDTO(flag)); + } +} \ No newline at end of file diff --git a/src/main/java/umc/spring/web/dto/TempRequest.java b/src/main/java/umc/spring/web/dto/TempRequest.java new file mode 100644 index 0000000..6978f93 --- /dev/null +++ b/src/main/java/umc/spring/web/dto/TempRequest.java @@ -0,0 +1,4 @@ +package umc.spring.web.dto; + +public class TempRequest { +} diff --git a/src/main/java/umc/spring/web/dto/TempResponse.java b/src/main/java/umc/spring/web/dto/TempResponse.java new file mode 100644 index 0000000..563770d --- /dev/null +++ b/src/main/java/umc/spring/web/dto/TempResponse.java @@ -0,0 +1,24 @@ +package umc.spring.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public class TempResponse { + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class TempTestDTO{ + String testString; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class TempExceptionDTO{ + Integer flag; + } +} From d329a8326b99be7b6420d234d8c6fefa86f7ca4d Mon Sep 17 00:00:00 2001 From: Choi-seokun <142799427+Choi-seokun@users.noreply.github.com> Date: Mon, 25 Nov 2024 22:31:49 +0900 Subject: [PATCH 2/3] mission --- build.gradle | 3 + .../umc/spring/domain/mapping/Qmission.java | 4 +- .../spring/domain/mapping/Quser_favorite.java | 4 +- .../spring/domain/mapping/Quser_mission.java | 10 +-- .../spring/domain/mapping/Quser_review.java | 8 +- .../java/umc/spring/config/SwaggerConfig.java | 38 ++++++++ .../umc/spring/converter/StoreConverter.java | 86 +++++++++++++++++++ .../umc/spring/domain/mapping/mission.java | 4 +- .../spring/domain/mapping/user_favorite.java | 2 +- .../spring/domain/mapping/user_mission.java | 8 +- .../spring/domain/mapping/user_review.java | 4 +- .../java/umc/spring/domain/restaurant.java | 4 +- src/main/java/umc/spring/domain/user.java | 6 +- .../spring/repository/MissionRepository.java | 16 ++++ .../spring/repository/ReviewRepository.java | 12 +++ .../repository/UserMissionRepository.java | 12 +++ .../umc/spring/repository/UserRepository.java | 8 ++ .../spring/service/MissionQueryService.java | 14 +++ .../service/MissionQueryServiceImpl.java | 46 ++++++++++ .../umc/spring/service/StoreQueryService.java | 8 ++ .../spring/service/StoreQueryServiceImpl.java | 28 ++++++ .../annotation/ExistRestaurant.java | 17 ++++ .../validator/RestaurantExistValidator.java | 50 +++++++++++ .../web/controller/MissionRestController.java | 44 ++++++++++ .../web/controller/StoreRestController.java | 64 ++++++++++++++ .../umc/spring/web/dto/StoreResponseDTO.java | 86 +++++++++++++++++++ 26 files changed, 559 insertions(+), 27 deletions(-) create mode 100644 src/main/java/umc/spring/config/SwaggerConfig.java create mode 100644 src/main/java/umc/spring/converter/StoreConverter.java create mode 100644 src/main/java/umc/spring/repository/MissionRepository.java create mode 100644 src/main/java/umc/spring/repository/ReviewRepository.java create mode 100644 src/main/java/umc/spring/repository/UserMissionRepository.java create mode 100644 src/main/java/umc/spring/repository/UserRepository.java create mode 100644 src/main/java/umc/spring/service/MissionQueryService.java create mode 100644 src/main/java/umc/spring/service/MissionQueryServiceImpl.java create mode 100644 src/main/java/umc/spring/service/StoreQueryService.java create mode 100644 src/main/java/umc/spring/service/StoreQueryServiceImpl.java create mode 100644 src/main/java/umc/spring/validation/annotation/ExistRestaurant.java create mode 100644 src/main/java/umc/spring/validation/validator/RestaurantExistValidator.java create mode 100644 src/main/java/umc/spring/web/controller/MissionRestController.java create mode 100644 src/main/java/umc/spring/web/controller/StoreRestController.java create mode 100644 src/main/java/umc/spring/web/dto/StoreResponseDTO.java diff --git a/build.gradle b/build.gradle index bf6b4d9..3f9f173 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,9 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + implementation 'org.springframework.boot:spring-boot-starter-validation' } tasks.named('test') { diff --git a/src/main/generated/umc/spring/domain/mapping/Qmission.java b/src/main/generated/umc/spring/domain/mapping/Qmission.java index 249d0bd..523eaa0 100644 --- a/src/main/generated/umc/spring/domain/mapping/Qmission.java +++ b/src/main/generated/umc/spring/domain/mapping/Qmission.java @@ -28,7 +28,7 @@ public class Qmission extends EntityPathBase { public final NumberPath id = createNumber("id", Long.class); - public final umc.spring.domain.Qrestaurant restaurant_id; + public final umc.spring.domain.Qrestaurant restaurant; public final NumberPath reward = createNumber("reward", Long.class); @@ -52,7 +52,7 @@ public Qmission(PathMetadata metadata, PathInits inits) { public Qmission(Class type, PathMetadata metadata, PathInits inits) { super(type, metadata, inits); - this.restaurant_id = inits.isInitialized("restaurant_id") ? new umc.spring.domain.Qrestaurant(forProperty("restaurant_id")) : null; + this.restaurant = inits.isInitialized("restaurant") ? new umc.spring.domain.Qrestaurant(forProperty("restaurant")) : null; } } diff --git a/src/main/generated/umc/spring/domain/mapping/Quser_favorite.java b/src/main/generated/umc/spring/domain/mapping/Quser_favorite.java index 0558738..db09690 100644 --- a/src/main/generated/umc/spring/domain/mapping/Quser_favorite.java +++ b/src/main/generated/umc/spring/domain/mapping/Quser_favorite.java @@ -26,7 +26,7 @@ public class Quser_favorite extends EntityPathBase { public final NumberPath id = createNumber("id", Long.class); - public final umc.spring.domain.Quser user_id; + public final umc.spring.domain.Quser user; public Quser_favorite(String variable) { this(user_favorite.class, forVariable(variable), INITS); @@ -46,7 +46,7 @@ public Quser_favorite(PathMetadata metadata, PathInits inits) { public Quser_favorite(Class type, PathMetadata metadata, PathInits inits) { super(type, metadata, inits); - this.user_id = inits.isInitialized("user_id") ? new umc.spring.domain.Quser(forProperty("user_id")) : null; + this.user = inits.isInitialized("user") ? new umc.spring.domain.Quser(forProperty("user")) : null; } } diff --git a/src/main/generated/umc/spring/domain/mapping/Quser_mission.java b/src/main/generated/umc/spring/domain/mapping/Quser_mission.java index c2ffcdc..1a425a5 100644 --- a/src/main/generated/umc/spring/domain/mapping/Quser_mission.java +++ b/src/main/generated/umc/spring/domain/mapping/Quser_mission.java @@ -24,11 +24,11 @@ public class Quser_mission extends EntityPathBase { public final NumberPath id = createNumber("id", Long.class); - public final EnumPath mission_check = createEnum("mission_check", umc.spring.domain.enums.MissionCheck.class); + public final Qmission mission; - public final Qmission mission_id; + public final EnumPath missioncheck = createEnum("missioncheck", umc.spring.domain.enums.MissionCheck.class); - public final umc.spring.domain.Quser user_id; + public final umc.spring.domain.Quser user; public Quser_mission(String variable) { this(user_mission.class, forVariable(variable), INITS); @@ -48,8 +48,8 @@ public Quser_mission(PathMetadata metadata, PathInits inits) { public Quser_mission(Class type, PathMetadata metadata, PathInits inits) { super(type, metadata, inits); - this.mission_id = inits.isInitialized("mission_id") ? new Qmission(forProperty("mission_id"), inits.get("mission_id")) : null; - this.user_id = inits.isInitialized("user_id") ? new umc.spring.domain.Quser(forProperty("user_id")) : null; + this.mission = inits.isInitialized("mission") ? new Qmission(forProperty("mission"), inits.get("mission")) : null; + this.user = inits.isInitialized("user") ? new umc.spring.domain.Quser(forProperty("user")) : null; } } diff --git a/src/main/generated/umc/spring/domain/mapping/Quser_review.java b/src/main/generated/umc/spring/domain/mapping/Quser_review.java index ec5c3a5..6978072 100644 --- a/src/main/generated/umc/spring/domain/mapping/Quser_review.java +++ b/src/main/generated/umc/spring/domain/mapping/Quser_review.java @@ -30,9 +30,9 @@ public class Quser_review extends EntityPathBase { public final NumberPath rating = createNumber("rating", Long.class); - public final umc.spring.domain.Qrestaurant restaurant_id; + public final umc.spring.domain.Qrestaurant restaurant; - public final umc.spring.domain.Quser user_id; + public final umc.spring.domain.Quser user; public Quser_review(String variable) { this(user_review.class, forVariable(variable), INITS); @@ -52,8 +52,8 @@ public Quser_review(PathMetadata metadata, PathInits inits) { public Quser_review(Class type, PathMetadata metadata, PathInits inits) { super(type, metadata, inits); - this.restaurant_id = inits.isInitialized("restaurant_id") ? new umc.spring.domain.Qrestaurant(forProperty("restaurant_id")) : null; - this.user_id = inits.isInitialized("user_id") ? new umc.spring.domain.Quser(forProperty("user_id")) : null; + this.restaurant = inits.isInitialized("restaurant") ? new umc.spring.domain.Qrestaurant(forProperty("restaurant")) : null; + this.user = inits.isInitialized("user") ? new umc.spring.domain.Quser(forProperty("user")) : null; } } diff --git a/src/main/java/umc/spring/config/SwaggerConfig.java b/src/main/java/umc/spring/config/SwaggerConfig.java new file mode 100644 index 0000000..521c1eb --- /dev/null +++ b/src/main/java/umc/spring/config/SwaggerConfig.java @@ -0,0 +1,38 @@ +package umc.spring.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.servers.Server; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + @Bean + public OpenAPI UMCstudyAPI() { + Info info = new Info() + .title("UMC Server WorkBook API") + .description("UMC Server WorkBook API 명세서") + .version("1.0.0"); + + String jwtSchemeName = "JWT TOKEN"; + // API 요청헤더에 인증정보 포함 + SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwtSchemeName); + // SecuritySchemes 등록 + Components components = new Components() + .addSecuritySchemes(jwtSchemeName, new SecurityScheme() + .name(jwtSchemeName) + .type(SecurityScheme.Type.HTTP) // HTTP 방식 + .scheme("bearer") + .bearerFormat("JWT")); + + return new OpenAPI() + .addServersItem(new Server().url("/")) + .info(info) + .addSecurityItem(securityRequirement) + .components(components); + } +} diff --git a/src/main/java/umc/spring/converter/StoreConverter.java b/src/main/java/umc/spring/converter/StoreConverter.java new file mode 100644 index 0000000..4664389 --- /dev/null +++ b/src/main/java/umc/spring/converter/StoreConverter.java @@ -0,0 +1,86 @@ +package umc.spring.converter; + +import org.springframework.data.domain.Page; +import umc.spring.domain.mapping.Quser_mission; +import umc.spring.domain.mapping.mission; +import umc.spring.domain.mapping.user_mission; +import umc.spring.domain.mapping.user_review; +import umc.spring.web.dto.StoreResponseDTO; + +import java.util.List; +import java.util.stream.Collectors; + +public class StoreConverter { + public static StoreResponseDTO.ReviewPreViewDTO reviewPreViewDTO(user_review review){ + return StoreResponseDTO.ReviewPreViewDTO.builder() + .ownerNickname(review.getUser().getName()) + .score(review.getRating()) + .createdAt(review.getDate().toLocalDate()) + .body(review.getContent()) + .build(); + } + public static StoreResponseDTO.ReviewPreViewListDTO reviewPreViewListDTO(Page reviewList){ + + List reviewPreViewDTOList = reviewList.stream() + .map(StoreConverter::reviewPreViewDTO).collect(Collectors.toList()); + + return StoreResponseDTO.ReviewPreViewListDTO.builder() + .isLast(reviewList.isLast()) + .isFirst(reviewList.isFirst()) + .totalPage(reviewList.getTotalPages()) + .totalElements(reviewList.getTotalElements()) + .listSize(reviewPreViewDTOList.size()) + .reviewList(reviewPreViewDTOList) + .build(); + } + + public static StoreResponseDTO.MissionPreViewDTO missionPreViewDTO(mission m){ + return StoreResponseDTO.MissionPreViewDTO.builder() + .restaurantname(m.getRestaurant().getName()) + .reward(m.getReward()) + .deadline(m.getDeadline()) + .body(m.getContent()) + .build(); + } + + public static StoreResponseDTO.MissionPreViewListDTO missionPreViewListDTO(Page missionList){ + + List missionPreViewDTOList = missionList.stream() + .map(StoreConverter::missionPreViewDTO).collect(Collectors.toList()); + + return StoreResponseDTO.MissionPreViewListDTO.builder() + .isLast(missionList.isLast()) + .isFirst(missionList.isFirst()) + .totalPage(missionList.getTotalPages()) + .totalElements(missionList.getTotalElements()) + .listSize(missionPreViewDTOList.size()) + .missionList(missionPreViewDTOList) + .build(); + } + + public static StoreResponseDTO.UserMissionPreViewDTO usermissionPreViewDTO(user_mission m){ + return StoreResponseDTO.UserMissionPreViewDTO.builder() + .username(m.getUser().getName()) + .restaurantname(m.getMission().getRestaurant().getName()) + .reward(m.getMission().getReward()) + .deadline(m.getMission().getDeadline()) + .body(m.getMission().getContent()) + .missionCheck(m.getMissioncheck()) + .build(); + } + + public static StoreResponseDTO.UserMissionPreViewListDTO usermissionPreViewListDTO(Page missionList){ + + List usermissionPreViewDTOList = missionList.stream() + .map(StoreConverter::usermissionPreViewDTO).collect(Collectors.toList()); + + return StoreResponseDTO.UserMissionPreViewListDTO.builder() + .isLast(missionList.isLast()) + .isFirst(missionList.isFirst()) + .totalPage(missionList.getTotalPages()) + .totalElements(missionList.getTotalElements()) + .listSize(usermissionPreViewDTOList.size()) + .usermissionList(usermissionPreViewDTOList) + .build(); + } +} diff --git a/src/main/java/umc/spring/domain/mapping/mission.java b/src/main/java/umc/spring/domain/mapping/mission.java index 82d0e63..09bb9c4 100644 --- a/src/main/java/umc/spring/domain/mapping/mission.java +++ b/src/main/java/umc/spring/domain/mapping/mission.java @@ -29,8 +29,8 @@ public class mission { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "restaurant_id") - private restaurant restaurant_id; + private restaurant restaurant; - @OneToMany(mappedBy = "mission_id", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "mission", cascade = CascadeType.ALL) private List user_reviewList = new ArrayList<>(); } diff --git a/src/main/java/umc/spring/domain/mapping/user_favorite.java b/src/main/java/umc/spring/domain/mapping/user_favorite.java index 6ffe43a..3997051 100644 --- a/src/main/java/umc/spring/domain/mapping/user_favorite.java +++ b/src/main/java/umc/spring/domain/mapping/user_favorite.java @@ -19,6 +19,6 @@ public class user_favorite { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") - private user user_id; + private user user; } diff --git a/src/main/java/umc/spring/domain/mapping/user_mission.java b/src/main/java/umc/spring/domain/mapping/user_mission.java index 9242227..0d627a1 100644 --- a/src/main/java/umc/spring/domain/mapping/user_mission.java +++ b/src/main/java/umc/spring/domain/mapping/user_mission.java @@ -16,14 +16,14 @@ public class user_mission { private Long id; @Enumerated(EnumType.STRING) - @Column(columnDefinition = "VARCHAR(15) DEFAULT 'Challengeable'") - private MissionCheck mission_check; + @Column(name = "mission_check", columnDefinition = "VARCHAR(15) DEFAULT 'Challengeable'") + private MissionCheck missioncheck; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") - private user user_id; + private user user; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "mission_id") - private mission mission_id; + private mission mission; } diff --git a/src/main/java/umc/spring/domain/mapping/user_review.java b/src/main/java/umc/spring/domain/mapping/user_review.java index 969bee4..58f5891 100644 --- a/src/main/java/umc/spring/domain/mapping/user_review.java +++ b/src/main/java/umc/spring/domain/mapping/user_review.java @@ -29,9 +29,9 @@ public class user_review { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") - private user user_id; + private user user; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "restaurant_id") - private restaurant restaurant_id; + private restaurant restaurant; } diff --git a/src/main/java/umc/spring/domain/restaurant.java b/src/main/java/umc/spring/domain/restaurant.java index b14afa4..504291a 100644 --- a/src/main/java/umc/spring/domain/restaurant.java +++ b/src/main/java/umc/spring/domain/restaurant.java @@ -37,10 +37,10 @@ public class restaurant { @Column(nullable = false) private LocalTime close; - @OneToMany(mappedBy = "restaurant_id", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL) private List missionList = new ArrayList<>(); - @OneToMany(mappedBy = "restaurant_id", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL) private List user_reviewList = new ArrayList<>(); @Override public String toString() { diff --git a/src/main/java/umc/spring/domain/user.java b/src/main/java/umc/spring/domain/user.java index bdd297b..dfa2ed9 100644 --- a/src/main/java/umc/spring/domain/user.java +++ b/src/main/java/umc/spring/domain/user.java @@ -48,12 +48,12 @@ public class user { @Column(nullable = false) private Long point; - @OneToMany(mappedBy = "user_id", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List user_favoriteList = new ArrayList<>(); - @OneToMany(mappedBy = "user_id", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List user_missionList = new ArrayList<>(); - @OneToMany(mappedBy = "user_id", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List user_reviewList = new ArrayList<>(); } diff --git a/src/main/java/umc/spring/repository/MissionRepository.java b/src/main/java/umc/spring/repository/MissionRepository.java new file mode 100644 index 0000000..608ed06 --- /dev/null +++ b/src/main/java/umc/spring/repository/MissionRepository.java @@ -0,0 +1,16 @@ +package umc.spring.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.repository.JpaRepository; +import umc.spring.domain.enums.MissionCheck; +import umc.spring.domain.mapping.Quser_mission; +import umc.spring.domain.mapping.mission; +import umc.spring.domain.mapping.user_mission; +import umc.spring.domain.mapping.user_review; +import umc.spring.domain.restaurant; +import umc.spring.domain.user; + +public interface MissionRepository extends JpaRepository { + Page findAllByRestaurant(restaurant restaurant_id, PageRequest pageRequest); +} diff --git a/src/main/java/umc/spring/repository/ReviewRepository.java b/src/main/java/umc/spring/repository/ReviewRepository.java new file mode 100644 index 0000000..476e29c --- /dev/null +++ b/src/main/java/umc/spring/repository/ReviewRepository.java @@ -0,0 +1,12 @@ +package umc.spring.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.repository.JpaRepository; +import umc.spring.domain.Qrestaurant; +import umc.spring.domain.mapping.user_review; +import umc.spring.domain.restaurant; + +public interface ReviewRepository extends JpaRepository { + Page findAllByRestaurant(restaurant restaurant_id, PageRequest pageRequest); +} diff --git a/src/main/java/umc/spring/repository/UserMissionRepository.java b/src/main/java/umc/spring/repository/UserMissionRepository.java new file mode 100644 index 0000000..493a6ed --- /dev/null +++ b/src/main/java/umc/spring/repository/UserMissionRepository.java @@ -0,0 +1,12 @@ +package umc.spring.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.repository.JpaRepository; +import umc.spring.domain.enums.MissionCheck; +import umc.spring.domain.mapping.user_mission; +import umc.spring.domain.user; + +public interface UserMissionRepository extends JpaRepository { + Page findAllByUserAndMissioncheck(user user_id, MissionCheck missionCheck, PageRequest pageRequest); +} diff --git a/src/main/java/umc/spring/repository/UserRepository.java b/src/main/java/umc/spring/repository/UserRepository.java new file mode 100644 index 0000000..99c25e6 --- /dev/null +++ b/src/main/java/umc/spring/repository/UserRepository.java @@ -0,0 +1,8 @@ +package umc.spring.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import umc.spring.domain.mapping.user_review; +import umc.spring.domain.user; + +public interface UserRepository extends JpaRepository { +} diff --git a/src/main/java/umc/spring/service/MissionQueryService.java b/src/main/java/umc/spring/service/MissionQueryService.java new file mode 100644 index 0000000..e91d373 --- /dev/null +++ b/src/main/java/umc/spring/service/MissionQueryService.java @@ -0,0 +1,14 @@ +package umc.spring.service; + +import org.springframework.data.domain.Page; +import umc.spring.domain.enums.MissionCheck; +import umc.spring.domain.mapping.Quser_mission; +import umc.spring.domain.mapping.mission; +import umc.spring.domain.mapping.user_mission; +import umc.spring.domain.mapping.user_review; + +public interface MissionQueryService { + Page getMissionList(Long StoreId, Integer page); + + Page getUserMissionList(Long UserId, Integer page, MissionCheck mission_check); +} diff --git a/src/main/java/umc/spring/service/MissionQueryServiceImpl.java b/src/main/java/umc/spring/service/MissionQueryServiceImpl.java new file mode 100644 index 0000000..2bb5ae7 --- /dev/null +++ b/src/main/java/umc/spring/service/MissionQueryServiceImpl.java @@ -0,0 +1,46 @@ +package umc.spring.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import umc.spring.domain.enums.MissionCheck; +import umc.spring.domain.mapping.Quser_mission; +import umc.spring.domain.mapping.mission; +import umc.spring.domain.mapping.user_mission; +import umc.spring.domain.mapping.user_review; +import umc.spring.domain.restaurant; +import umc.spring.domain.user; +import umc.spring.repository.MissionRepository; +import umc.spring.repository.RestaurantRepository.RestaurantRepository; +import umc.spring.repository.UserMissionRepository; +import umc.spring.repository.UserRepository; + +@Service +@RequiredArgsConstructor +public class MissionQueryServiceImpl implements MissionQueryService{ + private final MissionRepository missionRepository; + + private final UserMissionRepository userMissionRepository; + + private final RestaurantRepository restaurantRepository; + + private final UserRepository userRepository; + + + @Override + public Page getMissionList(Long StoreId, Integer page) { + restaurant store = restaurantRepository.findById(StoreId).get(); + + Page StorePage = missionRepository.findAllByRestaurant(store, PageRequest.of(page, 10)); + return StorePage; + } + + @Override + public Page getUserMissionList(Long UserId, Integer page, MissionCheck mission_check) { + user u = userRepository.findById(UserId).get(); + + Page userMissionPage = userMissionRepository.findAllByUserAndMissioncheck(u, mission_check, PageRequest.of(page, 10)); + return userMissionPage; + } +} diff --git a/src/main/java/umc/spring/service/StoreQueryService.java b/src/main/java/umc/spring/service/StoreQueryService.java new file mode 100644 index 0000000..a785b5e --- /dev/null +++ b/src/main/java/umc/spring/service/StoreQueryService.java @@ -0,0 +1,8 @@ +package umc.spring.service; + +import org.springframework.data.domain.Page; +import umc.spring.domain.mapping.user_review; + +public interface StoreQueryService { + Page getReviewList(Long StoreId, Integer page); +} diff --git a/src/main/java/umc/spring/service/StoreQueryServiceImpl.java b/src/main/java/umc/spring/service/StoreQueryServiceImpl.java new file mode 100644 index 0000000..a78b160 --- /dev/null +++ b/src/main/java/umc/spring/service/StoreQueryServiceImpl.java @@ -0,0 +1,28 @@ +package umc.spring.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import umc.spring.domain.Qrestaurant; +import umc.spring.domain.mapping.Quser_review; +import umc.spring.domain.mapping.user_review; +import umc.spring.domain.restaurant; +import umc.spring.repository.RestaurantRepository.RestaurantRepository; +import umc.spring.repository.ReviewRepository; + +@Service +@RequiredArgsConstructor +public class StoreQueryServiceImpl implements StoreQueryService{ + private final RestaurantRepository storeRepository; + + private final ReviewRepository reviewRepository; + + @Override + public Page getReviewList(Long StoreId, Integer page) { + restaurant store = storeRepository.findById(StoreId).get(); + + Page StorePage = reviewRepository.findAllByRestaurant(store, PageRequest.of(page, 10)); + return StorePage; + } +} diff --git a/src/main/java/umc/spring/validation/annotation/ExistRestaurant.java b/src/main/java/umc/spring/validation/annotation/ExistRestaurant.java new file mode 100644 index 0000000..88515ca --- /dev/null +++ b/src/main/java/umc/spring/validation/annotation/ExistRestaurant.java @@ -0,0 +1,17 @@ +package umc.spring.validation.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import umc.spring.validation.validator.RestaurantExistValidator; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = RestaurantExistValidator.class) +@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExistRestaurant { + String message() default "해당하는 카테고리가 존재하지 않습니다."; + Class[] groups() default {}; + Class[] payload() default {}; +} diff --git a/src/main/java/umc/spring/validation/validator/RestaurantExistValidator.java b/src/main/java/umc/spring/validation/validator/RestaurantExistValidator.java new file mode 100644 index 0000000..b4adefa --- /dev/null +++ b/src/main/java/umc/spring/validation/validator/RestaurantExistValidator.java @@ -0,0 +1,50 @@ +package umc.spring.validation.validator; + + + +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import umc.spring.apiPayload.code.status.ErrorStatus; +import umc.spring.repository.RestaurantRepository.RestaurantRepository; +import umc.spring.validation.annotation.ExistRestaurant; + +import java.util.List; + +@Component +@RequiredArgsConstructor +public class RestaurantExistValidator implements ConstraintValidator { + + private final RestaurantRepository restaurantRepository; + @Override + public void initialize(ExistRestaurant constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + +// @Override +// public boolean isValid(List values, ConstraintValidatorContext context) { +// boolean isValid = values.stream() +// .allMatch(value -> restaurantRepository.existsById(value)); +// +// if (!isValid) { +// context.disableDefaultConstraintViolation(); +// context.buildConstraintViolationWithTemplate(ErrorStatus.MEMBER_NOT_FOUND.toString()).addConstraintViolation(); +// } +// +// return isValid; +// } + + @Override + public boolean isValid(Long value, ConstraintValidatorContext context) { + boolean isValid = restaurantRepository.existsById(value); + + if (!isValid) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(ErrorStatus.MEMBER_NOT_FOUND.toString()).addConstraintViolation(); + } + + return isValid; + } +} diff --git a/src/main/java/umc/spring/web/controller/MissionRestController.java b/src/main/java/umc/spring/web/controller/MissionRestController.java new file mode 100644 index 0000000..1ca5123 --- /dev/null +++ b/src/main/java/umc/spring/web/controller/MissionRestController.java @@ -0,0 +1,44 @@ +package umc.spring.web.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import umc.spring.apiPayload.ApiResponse; +import umc.spring.converter.StoreConverter; +import umc.spring.domain.enums.MissionCheck; +import umc.spring.domain.mapping.mission; +import umc.spring.domain.mapping.user_mission; +import umc.spring.service.MissionQueryService; +import umc.spring.validation.annotation.ExistRestaurant; +import umc.spring.web.dto.StoreResponseDTO; + +@RestController +@RequiredArgsConstructor +@Validated +@RequestMapping("/missions") +public class MissionRestController { + private final MissionQueryService missionQueryService; + + @GetMapping("/{userId}/") + @Operation(summary = "특정 유저의 진행중 미션 목록 조회 API",description = "특정 유저가 진행중인 미션 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200",description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "acess 토큰 만료",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "acess 토큰 모양이 이상함",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + }) + @Parameters({ + @Parameter(name = "userId", description = "가게의 아이디, path variable 입니다!") + }) + public ApiResponse getUserMissionList(@ExistRestaurant @PathVariable(name = "userId") Long userId, @RequestParam(name = "page") Integer page, @RequestParam(name = "missioncheck") MissionCheck missionCheck){ + Page usermissionList = missionQueryService.getUserMissionList(userId, page, missionCheck); + return ApiResponse.onSuccess(StoreConverter.usermissionPreViewListDTO(usermissionList)); + } +} diff --git a/src/main/java/umc/spring/web/controller/StoreRestController.java b/src/main/java/umc/spring/web/controller/StoreRestController.java new file mode 100644 index 0000000..2b58fcb --- /dev/null +++ b/src/main/java/umc/spring/web/controller/StoreRestController.java @@ -0,0 +1,64 @@ +package umc.spring.web.controller; + + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import umc.spring.apiPayload.ApiResponse; +import umc.spring.converter.StoreConverter; +import umc.spring.domain.mapping.Quser_mission; +import umc.spring.domain.mapping.mission; +import umc.spring.domain.mapping.user_mission; +import umc.spring.domain.mapping.user_review; +import umc.spring.service.MissionQueryService; +import umc.spring.service.StoreQueryService; +import umc.spring.validation.annotation.ExistRestaurant; +import umc.spring.web.dto.StoreResponseDTO; + +@RestController +@RequiredArgsConstructor +@Validated +@RequestMapping("/stores") +public class StoreRestController { + private final StoreQueryService storeQueryService; + private final MissionQueryService missionQueryService; + + @GetMapping("/{storeId}/reviews") + @Operation(summary = "특정 가게의 리뷰 목록 조회 API",description = "특정 가게의 리뷰들의 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200",description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "acess 토큰 만료",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "acess 토큰 모양이 이상함",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + }) + @Parameters({ + @Parameter(name = "storeId", description = "가게의 아이디, path variable 입니다!") + }) + public ApiResponse getReviewList(@ExistRestaurant @PathVariable(name = "storeId") Long storeId,@RequestParam(name = "page") Integer page){ + Page reviewList = storeQueryService.getReviewList(storeId,page); + return ApiResponse.onSuccess(StoreConverter.reviewPreViewListDTO(reviewList)); + } + + @GetMapping("/{storeId}/missions") + @Operation(summary = "특정 가게의 미션 목록 조회 API",description = "특정 가게의 미션들의 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200",description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "acess 토큰 만료",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "acess 토큰 모양이 이상함",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + }) + @Parameters({ + @Parameter(name = "storeId", description = "가게의 아이디, path variable 입니다!") + }) + public ApiResponse getMissionList(@ExistRestaurant @PathVariable(name = "storeId") Long storeId,@RequestParam(name = "page") Integer page){ + Page missionList = missionQueryService.getMissionList(storeId,page); + return ApiResponse.onSuccess(StoreConverter.missionPreViewListDTO(missionList)); + } +} diff --git a/src/main/java/umc/spring/web/dto/StoreResponseDTO.java b/src/main/java/umc/spring/web/dto/StoreResponseDTO.java new file mode 100644 index 0000000..678491d --- /dev/null +++ b/src/main/java/umc/spring/web/dto/StoreResponseDTO.java @@ -0,0 +1,86 @@ +package umc.spring.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import umc.spring.domain.enums.MissionCheck; + +import java.time.LocalDate; +import java.util.List; + +public class StoreResponseDTO { + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class ReviewPreViewListDTO { + List reviewList; + Integer listSize; + Integer totalPage; + Long totalElements; + Boolean isFirst; + Boolean isLast; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class ReviewPreViewDTO { + String ownerNickname; + Long score; + String body; + LocalDate createdAt; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class MissionPreViewListDTO { + List missionList; + Integer listSize; + Integer totalPage; + Long totalElements; + Boolean isFirst; + Boolean isLast; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class MissionPreViewDTO { + String restaurantname; + Long reward; + String body; + LocalDate deadline; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class UserMissionPreViewListDTO { + List usermissionList; + Integer listSize; + Integer totalPage; + Long totalElements; + Boolean isFirst; + Boolean isLast; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class UserMissionPreViewDTO { + String username; + String restaurantname; + Long reward; + String body; + LocalDate deadline; + MissionCheck missionCheck; + } +} From cd4f777a0e3ccbd7e5310c07c51e24684ed6df48 Mon Sep 17 00:00:00 2001 From: Choi-seokun <142799427+Choi-seokun@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:16:05 +0900 Subject: [PATCH 3/3] practice --- build.gradle | 5 ++ .../generated/umc/spring/domain/Quser.java | 4 ++ .../security/CustomOAuth2UserService.java | 66 +++++++++++++++++++ .../security/CustomUserDetailsService.java | 28 ++++++++ .../config/security/SecurityConfig.java | 45 +++++++++++++ .../umc/spring/converter/UserConverter.java | 30 +++++++++ .../java/umc/spring/domain/enums/Gender.java | 2 +- .../java/umc/spring/domain/enums/Role.java | 5 ++ src/main/java/umc/spring/domain/user.java | 19 ++++-- .../umc/spring/repository/UserRepository.java | 3 + .../MemberCommandService.java | 8 +++ .../MemberCommandServiceImpl.java | 30 +++++++++ .../web/controller/MemberViewController.java | 60 +++++++++++++++++ .../umc/spring/web/dto/UserRequestDTO.java | 35 ++++++++++ src/main/resources/application.yml | 21 +++++- src/main/resources/templates/admin.html | 10 +++ src/main/resources/templates/home.html | 17 +++++ src/main/resources/templates/login.html | 25 +++++++ src/main/resources/templates/signup.html | 50 ++++++++++++++ 19 files changed, 457 insertions(+), 6 deletions(-) create mode 100644 src/main/java/umc/spring/config/security/CustomOAuth2UserService.java create mode 100644 src/main/java/umc/spring/config/security/CustomUserDetailsService.java create mode 100644 src/main/java/umc/spring/config/security/SecurityConfig.java create mode 100644 src/main/java/umc/spring/converter/UserConverter.java create mode 100644 src/main/java/umc/spring/domain/enums/Role.java create mode 100644 src/main/java/umc/spring/service/RestaurantService/MemberCommandService.java create mode 100644 src/main/java/umc/spring/service/RestaurantService/MemberCommandServiceImpl.java create mode 100644 src/main/java/umc/spring/web/controller/MemberViewController.java create mode 100644 src/main/java/umc/spring/web/dto/UserRequestDTO.java create mode 100644 src/main/resources/templates/admin.html create mode 100644 src/main/resources/templates/home.html create mode 100644 src/main/resources/templates/login.html create mode 100644 src/main/resources/templates/signup.html diff --git a/build.gradle b/build.gradle index 3f9f173..035059c 100644 --- a/build.gradle +++ b/build.gradle @@ -45,6 +45,11 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.1.RELEASE' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' } tasks.named('test') { diff --git a/src/main/generated/umc/spring/domain/Quser.java b/src/main/generated/umc/spring/domain/Quser.java index 7fdc327..d7394d5 100644 --- a/src/main/generated/umc/spring/domain/Quser.java +++ b/src/main/generated/umc/spring/domain/Quser.java @@ -34,10 +34,14 @@ public class Quser extends EntityPathBase { public final StringPath name = createString("name"); + public final StringPath password = createString("password"); + public final StringPath phone_num = createString("phone_num"); public final NumberPath point = createNumber("point", Long.class); + public final EnumPath role = createEnum("role", umc.spring.domain.enums.Role.class); + public final ListPath user_favoriteList = this.createList("user_favoriteList", umc.spring.domain.mapping.user_favorite.class, umc.spring.domain.mapping.Quser_favorite.class, PathInits.DIRECT2); public final ListPath user_missionList = this.createList("user_missionList", umc.spring.domain.mapping.user_mission.class, umc.spring.domain.mapping.Quser_mission.class, PathInits.DIRECT2); diff --git a/src/main/java/umc/spring/config/security/CustomOAuth2UserService.java b/src/main/java/umc/spring/config/security/CustomOAuth2UserService.java new file mode 100644 index 0000000..2c2c84a --- /dev/null +++ b/src/main/java/umc/spring/config/security/CustomOAuth2UserService.java @@ -0,0 +1,66 @@ +package umc.spring.config.security; + +import jakarta.validation.constraints.Null; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; +import umc.spring.domain.enums.Gender; +import umc.spring.domain.enums.Role; +import umc.spring.domain.user; +import umc.spring.repository.UserRepository; + +import java.lang.reflect.Member; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class CustomOAuth2UserService extends DefaultOAuth2UserService { + + private final UserRepository memberRepository; + private final PasswordEncoder passwordEncoder; + + @Override + public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { + OAuth2User oAuth2User = super.loadUser(userRequest); + + Map attributes = oAuth2User.getAttributes(); + Map properties = (Map) attributes.get("properties"); + + String nickname = (String) properties.get("nickname"); + String email = nickname + "@kakao.com"; // 임시 이메일 생성 + + // 사용자 정보 저장 또는 업데이트 + user member = saveOrUpdateUser(email, nickname); + + // 이메일을 Principal로 사용하기 위해 attributes 수정 + Map modifiedAttributes = new HashMap<>(attributes); + modifiedAttributes.put("email", email); + + return new DefaultOAuth2User( + oAuth2User.getAuthorities(), + modifiedAttributes, + "email" // email Principal로 설정 + ); + } + + private user saveOrUpdateUser(String email, String nickname) { + user member = memberRepository.findByEmail(email) + .orElse(user.builder() + .email(email) + .name(nickname) + .password(passwordEncoder.encode("OAUTH_USER_" + UUID.randomUUID())) + .gender(Gender.NULL) // 기본값 설정 + .address("소셜로그인") // 기본값 설정 + .role(Role.USER) + .build()); + + return memberRepository.save(member); + } +} diff --git a/src/main/java/umc/spring/config/security/CustomUserDetailsService.java b/src/main/java/umc/spring/config/security/CustomUserDetailsService.java new file mode 100644 index 0000000..095895b --- /dev/null +++ b/src/main/java/umc/spring/config/security/CustomUserDetailsService.java @@ -0,0 +1,28 @@ +package umc.spring.config.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import umc.spring.domain.user; +import umc.spring.repository.UserRepository; + +@Service +@RequiredArgsConstructor +public class CustomUserDetailsService implements UserDetailsService { + + private final UserRepository memberRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + user member = memberRepository.findByEmail(username) + .orElseThrow(() -> new UsernameNotFoundException("해당 이메일을 가진 유저가 존재하지 않습니다: " + username)); + + return org.springframework.security.core.userdetails.User + .withUsername(member.getEmail()) + .password(member.getPassword()) + .roles(member.getRole().name()) + .build(); + } +} diff --git a/src/main/java/umc/spring/config/security/SecurityConfig.java b/src/main/java/umc/spring/config/security/SecurityConfig.java new file mode 100644 index 0000000..b8f7cfd --- /dev/null +++ b/src/main/java/umc/spring/config/security/SecurityConfig.java @@ -0,0 +1,45 @@ +package umc.spring.config.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@EnableWebSecurity +@Configuration +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((requests) -> requests + .requestMatchers("/", "/home", "/signup", "/members/signup", "/css/**").permitAll() + .requestMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ) + .formLogin((form) -> form + .loginPage("/login") + .defaultSuccessUrl("/home", true) + .permitAll() + ) + .logout((logout) -> logout + .logoutUrl("/logout") + .logoutSuccessUrl("/login?logout") + .permitAll() + ) + .oauth2Login(oauth2 -> oauth2 + .loginPage("/login") + .defaultSuccessUrl("/home", true) + .permitAll() + ); + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/umc/spring/converter/UserConverter.java b/src/main/java/umc/spring/converter/UserConverter.java new file mode 100644 index 0000000..cd273e3 --- /dev/null +++ b/src/main/java/umc/spring/converter/UserConverter.java @@ -0,0 +1,30 @@ +package umc.spring.converter; + +import org.apache.catalina.User; +import umc.spring.domain.enums.Gender; +import umc.spring.domain.user; +import umc.spring.web.dto.UserRequestDTO; + +import java.lang.reflect.Member; + +public class UserConverter { + public static user toMember(UserRequestDTO.JoinDto request) { + Gender gender = null; + switch (request.getGender()) { + case MALE: gender = Gender.MALE; break; + case FEMALE: gender = Gender.FEMALE; break; + } + System.out.println(request.getPassword()); + + return user.builder() + .name(request.getName()) + .email(request.getEmail()) // 추가된 코드 + .password(request.getPassword()) // 추가된 코드 + .gender(gender) + .birth(request.getBirth()) + .address(request.getAddress()) + .role(request.getRole()) // 추가된 코드 + .build(); + } + // ... 기타 메소드들 ... +} \ No newline at end of file diff --git a/src/main/java/umc/spring/domain/enums/Gender.java b/src/main/java/umc/spring/domain/enums/Gender.java index b74c14e..5c46615 100644 --- a/src/main/java/umc/spring/domain/enums/Gender.java +++ b/src/main/java/umc/spring/domain/enums/Gender.java @@ -1,5 +1,5 @@ package umc.spring.domain.enums; public enum Gender { - MALE, FEMALE + MALE, FEMALE, NULL } diff --git a/src/main/java/umc/spring/domain/enums/Role.java b/src/main/java/umc/spring/domain/enums/Role.java new file mode 100644 index 0000000..61d3b50 --- /dev/null +++ b/src/main/java/umc/spring/domain/enums/Role.java @@ -0,0 +1,5 @@ +package umc.spring.domain.enums; + +public enum Role { + ADMIN, USER +} diff --git a/src/main/java/umc/spring/domain/user.java b/src/main/java/umc/spring/domain/user.java index dfa2ed9..06ecd06 100644 --- a/src/main/java/umc/spring/domain/user.java +++ b/src/main/java/umc/spring/domain/user.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.*; import umc.spring.domain.enums.Gender; +import umc.spring.domain.enums.Role; import umc.spring.domain.mapping.mission; import umc.spring.domain.mapping.user_favorite; import umc.spring.domain.mapping.user_mission; @@ -36,16 +37,22 @@ public class user { @Column(nullable = false, length = 100) private String address; - @Column(nullable = false, length = 30) + @Column(nullable = false, unique = true) private String email; - @Column(nullable = false, length = 20) + @Column(nullable = false,length = 255) + private String password; + + @Enumerated(EnumType.STRING) + private Role role; + + @Column(length = 20) private String phone_num; - @Column(nullable = false) + @Column() private Long mission_count; - @Column(nullable = false) + @Column() private Long point; @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) @@ -56,4 +63,8 @@ public class user { @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List user_reviewList = new ArrayList<>(); + + public void encodePassword(String password) { + this.password = password; + } } diff --git a/src/main/java/umc/spring/repository/UserRepository.java b/src/main/java/umc/spring/repository/UserRepository.java index 99c25e6..19f3c43 100644 --- a/src/main/java/umc/spring/repository/UserRepository.java +++ b/src/main/java/umc/spring/repository/UserRepository.java @@ -4,5 +4,8 @@ import umc.spring.domain.mapping.user_review; import umc.spring.domain.user; +import java.util.Optional; + public interface UserRepository extends JpaRepository { + Optional findByEmail(String email); } diff --git a/src/main/java/umc/spring/service/RestaurantService/MemberCommandService.java b/src/main/java/umc/spring/service/RestaurantService/MemberCommandService.java new file mode 100644 index 0000000..726c0e7 --- /dev/null +++ b/src/main/java/umc/spring/service/RestaurantService/MemberCommandService.java @@ -0,0 +1,8 @@ +package umc.spring.service.RestaurantService; + +import umc.spring.domain.user; +import umc.spring.web.dto.UserRequestDTO; + +public interface MemberCommandService { + public user joinMember(UserRequestDTO.JoinDto request); +} diff --git a/src/main/java/umc/spring/service/RestaurantService/MemberCommandServiceImpl.java b/src/main/java/umc/spring/service/RestaurantService/MemberCommandServiceImpl.java new file mode 100644 index 0000000..240d90b --- /dev/null +++ b/src/main/java/umc/spring/service/RestaurantService/MemberCommandServiceImpl.java @@ -0,0 +1,30 @@ +package umc.spring.service.RestaurantService; + +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import umc.spring.converter.UserConverter; +import umc.spring.converter.UserConverter; +import umc.spring.domain.user; +import umc.spring.repository.UserRepository; +import umc.spring.web.dto.UserRequestDTO; + +@Service +@RequiredArgsConstructor +public class MemberCommandServiceImpl implements MemberCommandService{ + private final PasswordEncoder passwordEncoder; + + private final UserRepository userRepository; + + @Override + @Transactional + public user joinMember(UserRequestDTO.JoinDto request) { + + user newMember = UserConverter.toMember(request); + + newMember.encodePassword(passwordEncoder.encode(request.getPassword())); + + return userRepository.save(newMember); + } +} diff --git a/src/main/java/umc/spring/web/controller/MemberViewController.java b/src/main/java/umc/spring/web/controller/MemberViewController.java new file mode 100644 index 0000000..d4001c9 --- /dev/null +++ b/src/main/java/umc/spring/web/controller/MemberViewController.java @@ -0,0 +1,60 @@ +package umc.spring.web.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import umc.spring.service.RestaurantService.MemberCommandService; +import umc.spring.web.dto.UserRequestDTO; + +@Controller +public class MemberViewController { + MemberCommandService memberCommandService; + + public MemberViewController(MemberCommandService memberCommandService) { + this.memberCommandService = memberCommandService; + } + + @GetMapping("/login") + public String loginPage() { + return "login"; + } + + @GetMapping("/signup") + public String signupPage(Model model) { + model.addAttribute("memberJoinDto", new UserRequestDTO.JoinDto()); + + return "signup"; + } + + @GetMapping("/home") + public String home() { + return "home"; + } + + @GetMapping("/admin") + public String admin() { + return "admin"; + } + + @PostMapping("/members/signup") + public String joinMember(@ModelAttribute("memberJoinDto") UserRequestDTO.JoinDto request, // 협업시에는 기존 RequestBody 어노테이션을 붙여주시면 됩니다! + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + // 뷰에 데이터 바인딩이 실패할 경우 signup 페이지를 유지합니다. + return "signup"; + } + + try { + memberCommandService.joinMember(request); + return "redirect:/login"; + } catch (Exception e) { + // 회원가입 과정에서 에러가 발생할 경우 에러 메시지를 보내고, signup 페이디를 유지합니다. + model.addAttribute("error", e.getMessage()); + return "signup"; + } + } +} diff --git a/src/main/java/umc/spring/web/dto/UserRequestDTO.java b/src/main/java/umc/spring/web/dto/UserRequestDTO.java new file mode 100644 index 0000000..a763720 --- /dev/null +++ b/src/main/java/umc/spring/web/dto/UserRequestDTO.java @@ -0,0 +1,35 @@ +package umc.spring.web.dto; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; +import umc.spring.domain.enums.Gender; +import umc.spring.domain.enums.Role; + +import java.time.LocalDate; +import java.util.List; + +public class UserRequestDTO { + @Getter + @Setter // thymeleaf에서 사용하기 위해 추가 + public static class JoinDto { + @NotBlank + String name; + @NotBlank + @Email + String email; // 이메일 필드 추가 + @NotBlank + String password; // 비밀번호 필드 추가 + @NotNull + Gender gender; + @NotNull + LocalDate birth; + @Size(min = 5, max = 12) + String address; + @NotNull + Role role; // 역할 필드 추가 + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4263e6f..54cff8b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,4 +16,23 @@ spring: use_sql_comments: true hbm2ddl: auto: update - default_batch_fetch_size: 1000 \ No newline at end of file + default_batch_fetch_size: 1000 + # --- 이전 설정 --- + security: + oauth2: + client: + registration: + kakao: + client-authentication-method: client_secret_post + client-id: 2b6b6767c7ad029ce1297dd574f9ea11 + client-secret: vk20pzhZqelkz90eHsMHhxPtMDQybrtw + redirect-uri: http://localhost:8080/login/oauth2/code/kakao + authorization-grant-type: authorization_code + scope: profile_nickname + client-name: Kakao + provider: + kakao: + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id \ No newline at end of file diff --git a/src/main/resources/templates/admin.html b/src/main/resources/templates/admin.html new file mode 100644 index 0000000..55dbff1 --- /dev/null +++ b/src/main/resources/templates/admin.html @@ -0,0 +1,10 @@ + + + + Admin Page + + +

Admin Page

+

관리자만 접근할 수 있는 페이지입니다.

+ + \ No newline at end of file diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html new file mode 100644 index 0000000..529b72c --- /dev/null +++ b/src/main/resources/templates/home.html @@ -0,0 +1,17 @@ + + + + Home + + +

Welcome to Home Page!

+

+ + + +
+ +
+ \ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..920dd71 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,25 @@ + + + + Login + + +

Login

+
+
+ + +
+
+ + +
+ +
+

사용자 이름 또는 비밀번호가 잘못되었습니다.

+

로그아웃되었습니다.

+ +카카오로 로그인 + + + \ No newline at end of file diff --git a/src/main/resources/templates/signup.html b/src/main/resources/templates/signup.html new file mode 100644 index 0000000..133752c --- /dev/null +++ b/src/main/resources/templates/signup.html @@ -0,0 +1,50 @@ + + + + 회원가입 + + + +

회원가입

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + \ No newline at end of file