Skip to content

Commit

Permalink
orderForm 유효성 검사 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
daadaadaah committed Jun 1, 2023
1 parent 0061aad commit f0c4867
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 67 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'mysql:mysql-connector-java:8.0.22'
implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-mysql'
Expand Down
17 changes: 14 additions & 3 deletions src/main/java/com/hcommerce/heecommerce/order/OrderController.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.hcommerce.heecommerce.order;

import com.hcommerce.heecommerce.common.dto.ResponseDto;
import jakarta.validation.Valid;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -14,6 +17,11 @@
@RestController
public class OrderController {

@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new OrderFormValidator());
}

private final OrderService orderService;

@Autowired
Expand All @@ -30,19 +38,21 @@ public ResponseDto completeOrderReceipt(@PathVariable("orderUuid") UUID orderUui
return ResponseDto.builder()
.code(HttpStatus.OK.name())
.message("주문 접수 완료가 처리되었습니다.")
.data(null)
.build();
}

@PostMapping("/orders")
public ResponseEntity placeOrder(@RequestBody OrderForm orderForm) {
public ResponseEntity placeOrder(@Valid @RequestBody OrderForm orderForm) {
/**
* 예상 예외 상황
* - 로그인 유저의 요청이 아닌 경우 UNAUTHORIZED 예외
* - OrderForm 유효성 검사에 어긋난 경우 -> 유효성 공부 후 결정하기
* - OrderForm 유효성 검사에 어긋난 경우 -> 사용자가 PaymentType Enum에 없는 값으로 HTTP 요청한 경우 제외하고 일단 완성함
* : 사용자가 PaymentType Enum에 없는 값으로 HTTP 요청한 경우, 유효성 검사 어떻게 할지 고민
* - userId 가 DB에 없는 경우 NOT FOUND 예외
* - dealProductUuid 가 DB에 없는 경우 NOT FOUND 예외
* - ProductUuid 가 DB에 없는 경우 NOT FOUND 예외
* - 결제 실패로 주문이 불가능한 경우 예외 -> 이 경우는 HTTP 상태코드 몇번으로 해야하는
* - 결제 실패로 주문이 불가능한 경우 예외 -> 결제 API 검토 후 생각해보기(참고 : https://stackoverflow.com/questions/60112667/what-http-code-response-to-use-when-payment-fails)
* - 재고 부족으로 주문이 불가능한 경우 409 error 예외
*/

Expand All @@ -51,6 +61,7 @@ public ResponseEntity placeOrder(@RequestBody OrderForm orderForm) {
ResponseDto responseDto = ResponseDto.builder()
.code(HttpStatus.CREATED.name())
.message("주문 접수가 완료되었습니다.")
.data(null)
.build();

return new ResponseEntity(responseDto, HttpStatus.CREATED);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.hcommerce.heecommerce.common.dto.ResponseDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
Expand All @@ -29,9 +30,19 @@ public ResponseDto orderOverStockExceptionExceptionHandler(OrderOverStockExcepti
.build();
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler
public ResponseDto methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
return ResponseDto.builder()
.code(HttpStatus.BAD_REQUEST.name())
.message(e.getBindingResult().getAllErrors().get(0).getDefaultMessage())
.build();
}

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public ResponseDto fallbackExceptionHandler(Exception e) {
log.error("[{}] message = {} ", e.getClass(), e.getMessage());
return ResponseDto.builder()
.code(HttpStatus.INTERNAL_SERVER_ERROR.name())
.message("내부 서버 오류가 발생했습니다.")
Expand Down
34 changes: 25 additions & 9 deletions src/main/java/com/hcommerce/heecommerce/order/OrderForm.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
package com.hcommerce.heecommerce.order;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.beans.ConstructorProperties;
import lombok.Builder;
import lombok.Getter;

@Getter
public class OrderForm {

private OrdererInfo ordererInfo;
private RecipientInfo recipientInfo;
private PaymentInfo paymentInfo;
@Valid
@NotNull(message = "주문자 정보는 필수입니다.")
private OrdererInfoForm ordererInfoForm;

@Valid
@NotNull(message = "수령자 정보는 필수입니다.")
private RecipientInfoForm recipientInfoForm;

@Valid
@NotNull(message = "결제 정보는 필수입니다.")
private PaymentInfoForm paymentInfoForm;

@Builder
@ConstructorProperties({
"ordererInfoForm",
"recipientInfoForm",
"paymentInfoForm"}
)
public OrderForm(
OrdererInfo ordererInfo,
RecipientInfo recipientInfo,
PaymentInfo paymentInfo
OrdererInfoForm ordererInfoForm,
RecipientInfoForm recipientInfoForm,
PaymentInfoForm paymentInfoForm
) {
this.ordererInfo = ordererInfo;
this.recipientInfo = recipientInfo;
this.paymentInfo = paymentInfo;
this.ordererInfoForm = ordererInfoForm;
this.recipientInfoForm = recipientInfoForm;
this.paymentInfoForm = paymentInfoForm;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.hcommerce.heecommerce.order;

import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

@Component
public class OrderFormValidator implements Validator {

@Override
public boolean supports(Class<?> clazz) {
return OrderForm.class.equals(clazz);
}

@Override
public void validate(Object target, Errors errors) {
OrderForm orderForm = (OrderForm) target;

if(orderForm.getPaymentInfoForm() != null) {
validatePaymentInfoForm(orderForm.getPaymentInfoForm(), errors);
}
}

private void validatePaymentInfoForm(PaymentInfoForm paymentInfoForm, Errors errors) {

if (paymentInfoForm.getDiscountAmount() > paymentInfoForm.getOriginPrice()) {
errors.rejectValue("paymentInfoForm.totalDiscountAmount", "paymentInfoForm.discountAmount.invalid", "할인 금액은 상품 가격을 초과할 수 없습니다.");
return;
}

if (paymentInfoForm.getTotalDiscountAmount() > paymentInfoForm.getTotalDealProductAmount()) {
errors.rejectValue("paymentInfoForm.totalDiscountAmount", "paymentInfoForm.discountAmount.invalid", "총 할인 금액은 총 상품 가격을 초과할 수 없습니다.");
return;
}

int MAX_ORDER_QUANTITY_PER_ORDER = 10; // TODO: 딜 상품마다 최대 주문량을 다른 값을 갖도록 상황을 가정했으므로, DB에서 가져올 예정
// (참고: https://github.com/f-lab-edu/home-delivery/blob/b78ba80d38dd6e1e59554ebea59343a52d770e1d/src/main/java/com/flab/delivery/controller/validator/OrderValidator.java#L1)

if (paymentInfoForm.getOrderQuantity() > MAX_ORDER_QUANTITY_PER_ORDER) {
errors.rejectValue("paymentInfoForm.orderQuantity", "paymentInfoForm.orderQuantity.invalid", "최대 주문 수량은 " + MAX_ORDER_QUANTITY_PER_ORDER + "개 입니다.");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
package com.hcommerce.heecommerce.order;

import jakarta.validation.constraints.NotNull;
import java.beans.ConstructorProperties;
import lombok.Builder;
import lombok.Getter;
import org.hibernate.validator.constraints.Range;

@Getter
class OrdererInfo {
class OrdererInfoForm {

@Range(min = 1, message = "주문자 ID를 확인해주세요.")
private final int userId;

@NotNull(message = "주문자 이름을 입력해주세요.")
private final String ordererName;

@NotNull(message = "주문자 전화번호을 입력해주세요.")
private final String ordererPhoneNumber;

@Builder
@ConstructorProperties({"userId", "ordererName", "ordererPhoneNumber"})
public OrdererInfo(int userId, String ordererName, String ordererPhoneNumber) {
public OrdererInfoForm(int userId, String ordererName, String ordererPhoneNumber) {
this.userId = userId;
this.ordererName = ordererName;
this.ordererPhoneNumber = ordererPhoneNumber;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,45 @@
package com.hcommerce.heecommerce.order;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.beans.ConstructorProperties;
import java.util.UUID;
import lombok.Builder;
import lombok.Getter;

@Getter
class PaymentInfo {
class PaymentInfoForm {

@NotNull(message = "딜 상품 UUID를 입력해주세요.")
private final UUID dealProductUuid;

private final String dealProductTitle;

@NotNull(message = "상품 UUID를 입력해주세요.")
private final UUID ProductUuid;

@Min(value = 1, message = "유효하지 않는 상품 가격입니다. 상품 가격을 확인해주세요.")
private final int originPrice;

@Min(value = 0, message = "유효하지 않는 할인 금액입니다. 할인 금액을 확인해주세요.")
private final int discountAmount;

@Min(value = 0, message = "유효하지 않는 총 할인 금액입니다. 총 할인 금액을 확인해주세요.")
private final int totalDiscountAmount;

@Min(value = 1, message = "주문 수량은 1개 이상이어야 합니다.")
private final int orderQuantity;

@Min(value = 1, message = "유효하지 않은 총 상품 금액입니다. 총 상품 금액을 확인해주세요.")
private final int totalDealProductAmount;

@Min(value = 0, message = "유효하지 않은 배송료는 입니다. 배송료를 확인해주세요.")
private final int shippingFee;

@Min(value = 1, message = "유효하지 않은 결제 금액입니다. 결제 금액을 확인해주세요.")
private final int totalPaymentAmount;

@NotNull(message = "결제 유형을 입력해주세요.")
private final PaymentType paymentType;

@Builder
Expand All @@ -33,7 +56,7 @@ class PaymentInfo {
"totalPaymentAmount",
"paymentType"}
)
public PaymentInfo(
public PaymentInfoForm(
UUID dealProductUuid,
String dealProductTitle,
UUID productUuid,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
package com.hcommerce.heecommerce.order;

import jakarta.validation.constraints.NotNull;
import java.beans.ConstructorProperties;
import lombok.Builder;
import lombok.Getter;

@Getter
class RecipientInfo {
class RecipientInfoForm {

@NotNull(message = "수령인 이름을 입력해주세요.")
private final String recipientName;

@NotNull(message = "수령인 전화번호를 입력해주세요.")
private final String recipientPhoneNumber;

@NotNull(message = "수령 주소를 입력해주세요.")
private final String recipientAddress;

private final String recipientDetailAddress;

private final String shippingRequest;

@Builder
Expand All @@ -20,7 +29,7 @@ class RecipientInfo {
"recipientDetailAddress",
"shippingRequest"}
)
public RecipientInfo(
public RecipientInfoForm(
String recipientName,
String recipientPhoneNumber,
String recipientAddress,
Expand Down
Loading

0 comments on commit f0c4867

Please sign in to comment.