Skip to content

Commit

Permalink
Merge pull request #12 from UMC-7/week08/wayne
Browse files Browse the repository at this point in the history
Feat: Week8 실습 - 회원가입(간략화) API 구현
  • Loading branch information
ParkJh38 authored Nov 20, 2024
2 parents 5052c5a + 559bc02 commit 10146f2
Show file tree
Hide file tree
Showing 19 changed files with 478 additions and 2 deletions.
7 changes: 5 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ dependencies {
implementation 'org.hibernate:hibernate-core:6.1.7.Final'

// OpenAPI
implementation 'org.springdoc:springdoc-openapi-ui:1.6.9'
implementation 'org.springdoc:springdoc-openapi-data-rest:1.6.9'
//implementation 'org.springdoc:springdoc-openapi-ui:1.6.9'
//implementation 'org.springdoc:springdoc-openapi-data-rest:1.6.9'

implementation ('org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0')

}

tasks.named('test') {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/umc/workbook7/Workbook7Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class Workbook7Application {

public static void main(String[] args) {
Expand Down
120 changes: 120 additions & 0 deletions src/main/java/umc/workbook7/apiPayload/exception/ExceptionAdvice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package umc.workbook7.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.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.workbook7.apiPayload.ApiResponse;
import umc.workbook7.apiPayload.code.ErrorReasonDTO;
import umc.workbook7.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<Object> 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<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException e, HttpHeaders headers, HttpStatus status, WebRequest request) {

Map<String, String> 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);
}

@org.springframework.web.bind.annotation.ExceptionHandler
public ResponseEntity<Object> 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<Object> handleExceptionInternal(Exception e, ErrorReasonDTO reason,
HttpHeaders headers, HttpServletRequest request) {

ApiResponse<Object> 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<Object> handleExceptionInternalFalse(Exception e, ErrorStatus errorCommonStatus,
HttpHeaders headers, HttpStatus status, WebRequest request, String errorPoint) {
ApiResponse<Object> body = ApiResponse.onFailure(errorCommonStatus.getCode(),errorCommonStatus.getMessage(),errorPoint);
return super.handleExceptionInternal(
e,
body,
headers,
status,
request
);
}

private ResponseEntity<Object> handleExceptionInternalArgs(Exception e, HttpHeaders headers, ErrorStatus errorCommonStatus,
WebRequest request, Map<String, String> errorArgs) {
ApiResponse<Object> body = ApiResponse.onFailure(errorCommonStatus.getCode(),errorCommonStatus.getMessage(),errorArgs);
return super.handleExceptionInternal(
e,
body,
headers,
errorCommonStatus.getHttpStatus(),
request
);
}

private ResponseEntity<Object> handleExceptionInternalConstraint(Exception e, ErrorStatus errorCommonStatus,
HttpHeaders headers, WebRequest request) {
ApiResponse<Object> body = ApiResponse.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), null);
return super.handleExceptionInternal(
e,
body,
headers,
errorCommonStatus.getHttpStatus(),
request
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package umc.workbook7.apiPayload.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;
import umc.workbook7.apiPayload.code.BaseErrorCode;
import umc.workbook7.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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package umc.workbook7.apiPayload.exception.handler;

import umc.workbook7.apiPayload.code.BaseErrorCode;
import umc.workbook7.apiPayload.exception.GeneralException;

public class FoodCategoryHandler extends GeneralException {

public FoodCategoryHandler(BaseErrorCode errorCode) {
super(errorCode);
}
}
39 changes: 39 additions & 0 deletions src/main/java/umc/workbook7/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package umc.workbook7.config;

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 io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
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 WorkBook7 API")
.description("UMC Server WorkBook7 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);
}
}
43 changes: 43 additions & 0 deletions src/main/java/umc/workbook7/converter/MemberConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package umc.workbook7.converter;

import umc.workbook7.domain.entity.Member;
import umc.workbook7.domain.enums.Gender;
import umc.workbook7.web.dto.Member.MemberRequestDTO;
import umc.workbook7.web.dto.Member.MemberResponseDTO;

import java.time.LocalDateTime;
import java.util.ArrayList;

public class MemberConverter { // Member 객체를 만드는 작업 (클라이언트가 보낸 DTO to Entity) Converter에서 수행

public static MemberResponseDTO.JoinResultDTO toJoinResultDTO(Member member) {
return MemberResponseDTO.JoinResultDTO.builder()
.memberId(member.getMemberId())
.createdAt(LocalDateTime.now())
.build();
}

public static Member toMember(MemberRequestDTO.JoinDto request) {

Gender gender = null;

switch (request.getGender()){
case 1:
gender = Gender.MALE;
break;
case 2:
gender = Gender.FEMALE;
break;
case 3:
gender = Gender.NONE;
}

return Member.builder()
.address(request.getAddress())
.specAddress(request.getSpecAddress())
.gender(gender)
.name(request.getName())
.memberPreferList(new ArrayList<>())
.build();
}
}
21 changes: 21 additions & 0 deletions src/main/java/umc/workbook7/converter/MemberPreferConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package umc.workbook7.converter;

import umc.workbook7.domain.entity.FoodCategory;
import umc.workbook7.domain.entity.mapping.MemberPrefer;

import java.util.List;
import java.util.stream.Collectors;

public class MemberPreferConverter {

public static List<MemberPrefer> toMemberPreferList(List<FoodCategory> foodCategoryList) {

return foodCategoryList.stream()
.map(foodCategory ->
MemberPrefer.builder()
.foodCategory(foodCategory)
.build()
).collect(Collectors.toList());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package umc.workbook7.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import umc.workbook7.domain.entity.FoodCategory;

public interface FoodCategoryRepository extends JpaRepository<FoodCategory, Long> {
}
7 changes: 7 additions & 0 deletions src/main/java/umc/workbook7/repository/MemberRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package umc.workbook7.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import umc.workbook7.domain.entity.Member;

public interface MemberRepository extends JpaRepository<Member, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package umc.workbook7.service.MemberService;

import umc.workbook7.domain.entity.Member;
import umc.workbook7.web.dto.Member.MemberRequestDTO;

public interface MemberCommandService {
Member joinMember(MemberRequestDTO.JoinDto request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package umc.workbook7.service.MemberService;

import umc.workbook7.apiPayload.code.status.ErrorStatus;
import umc.workbook7.apiPayload.exception.handler.FoodCategoryHandler;
import umc.workbook7.converter.MemberConverter;
import umc.workbook7.converter.MemberPreferConverter;
import umc.workbook7.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import umc.workbook7.domain.entity.FoodCategory;
import umc.workbook7.domain.entity.Member;
import umc.workbook7.domain.entity.mapping.MemberPrefer;
import umc.workbook7.repository.FoodCategoryRepository;
import umc.workbook7.web.dto.Member.MemberRequestDTO;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberCommandServiceImpl implements MemberCommandService{

private final MemberRepository memberRepository;
private final FoodCategoryRepository foodCategoryRepository;

@Override
@Transactional // Transactional:
public Member joinMember(MemberRequestDTO.JoinDto request) {

Member newMember = MemberConverter.toMember(request);
List<FoodCategory> foodCategoryList = request.getPreferCategory().stream()
.map(category -> {
return foodCategoryRepository.findById(category).orElseThrow(() -> new FoodCategoryHandler(ErrorStatus.FOOD_CATEGORY_NOT_FOUND));
}).collect(Collectors.toList());

List<MemberPrefer> memberPreferList = MemberPreferConverter.toMemberPreferList(foodCategoryList);

memberPreferList.forEach(memberPrefer -> {memberPrefer.setMember(newMember);});

return memberRepository.save(newMember);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package umc.workbook7.service.MemberService;

public interface MemberQueryService {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package umc.workbook7.service.MemberService;

public class MemberQueryServiceImpl {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package umc.workbook7.validation.annotation;

import java.lang.annotation.*;
import java.lang.annotation.Documented;
import umc.workbook7.validation.validator.CategoriesExistValidator;
import jakarta.validation.Payload;
import jakarta.validation.Constraint;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Documented
@Constraint(validatedBy = CategoriesExistValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExistCategories {

String message() default "해당하는 카테고리가 존재하지 않습니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Loading

0 comments on commit 10146f2

Please sign in to comment.