Skip to content

Commit

Permalink
Merge pull request #203 from Team-BC-1/feat/admin-add-coupon
Browse files Browse the repository at this point in the history
어드민 쿠폰 생성 API
  • Loading branch information
Binsreoun authored Jan 30, 2024
2 parents b2febe3 + 26ccc9f commit b5c09db
Show file tree
Hide file tree
Showing 14 changed files with 387 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
package bc1.gream.domain.admin.controller;


import bc1.gream.domain.admin.dto.request.AdminCreateCouponRequestDto;
import bc1.gream.domain.admin.dto.request.AdminGetRefundRequestDto;
import bc1.gream.domain.admin.dto.request.AdminProductRequestDto;
import bc1.gream.domain.admin.dto.request.AdminRefundPassResponseDto;
import bc1.gream.domain.admin.dto.response.AdminCreateCouponResponseDto;
import bc1.gream.domain.admin.dto.response.AdminGetRefundResponseDto;
import bc1.gream.domain.admin.dto.response.AdminProductResponseDto;
import bc1.gream.domain.admin.mapper.RefundMapper;
import bc1.gream.domain.coupon.entity.Coupon;
import bc1.gream.domain.coupon.mapper.CouponMapper;
import bc1.gream.domain.coupon.provider.CouponProvider;
import bc1.gream.domain.product.service.query.ProductService;
import bc1.gream.domain.user.service.command.RefundCommandService;
import bc1.gream.domain.user.service.query.RefundQueryService;
import bc1.gream.global.common.RestResponse;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
Expand All @@ -30,6 +35,7 @@ public class AdminController {
private final ProductService productService;
private final RefundQueryService refundQueryService;
private final RefundCommandService refundCommandService;
private final CouponProvider couponProvider;

@GetMapping("/refunds")
@Operation(summary = "신청된 환급 리스트 조회 요청", description = "사용자가 신청한 환급 요청 리스트를 반환합니다.")
Expand All @@ -48,11 +54,10 @@ public RestResponse<AdminProductResponseDto> addProducts(
@RequestBody AdminProductRequestDto adminProductRequestDto
) {
productService.addProduct(adminProductRequestDto);

return RestResponse.success(new AdminProductResponseDto());
}


@DeleteMapping("/refund/{id}")
@Operation(summary = "유저 환급 승인", description = "유저가 신청한 환급 요청을 승인해주는 기능입니다.")
public RestResponse<AdminRefundPassResponseDto> approveRefund(
Expand All @@ -62,4 +67,14 @@ public RestResponse<AdminRefundPassResponseDto> approveRefund(

return RestResponse.success(responseDto);
}
}

@PostMapping("/coupon")
@Operation(summary = "쿠폰 생성요청 [어드민 ONLY]", description = "어드민 권한의 관리자의 쿠폰 생성 요청을 처리합니다.")
public RestResponse<AdminCreateCouponResponseDto> createCoupon(
@Valid @RequestBody AdminCreateCouponRequestDto adminCreateCouponRequestDto
) {
Coupon coupon = couponProvider.createCoupon(adminCreateCouponRequestDto);
AdminCreateCouponResponseDto responseDto = CouponMapper.INSTANCE.toAdminCreateCouponResponseDto(coupon);
return RestResponse.success(responseDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package bc1.gream.domain.admin.dto.request;

import bc1.gream.domain.coupon.entity.DiscountType;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Positive;
import lombok.Builder;

@JsonIgnoreProperties
@Builder
public record AdminCreateCouponRequestDto(
@NotBlank(message = "null 혹은 공백 입력은 불가합니다.")
@Pattern(
regexp = "^[a-zA-Z0-9가-힣]{4,30}$",
message = "쿠폰이름은 영문자, 한글 및 숫자, 4이상 30이하 길이로 가능합니다."
)
String name,
@NotNull(message = "null 입력은 불가합니다.")
DiscountType discountType,
@NotNull(message = "null 입력은 불가합니다.")
@Positive(message = "음수 혹은 0 입력은 불가합니다.")
Long discount,
@NotBlank(message = "null 입력은 불가합니다.")
String userLoginId
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package bc1.gream.domain.admin.dto.response;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties
public record AdminCreateCouponResponseDto(
) {

}
15 changes: 14 additions & 1 deletion src/main/java/bc1/gream/domain/coupon/entity/CouponStatus.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
package bc1.gream.domain.coupon.entity;

import bc1.gream.global.common.ResultCase;
import bc1.gream.global.exception.GlobalException;
import com.fasterxml.jackson.annotation.JsonCreator;
import java.util.stream.Stream;

public enum CouponStatus {
AVAILABLE,
IN_USE,
ALREADY_USED
ALREADY_USED;

@JsonCreator
public static CouponStatus deserializeRequest(String couponStatusRequest) {
return Stream.of(CouponStatus.values())
.filter(couponStatus -> couponStatus.toString().equals(couponStatusRequest.toUpperCase()))
.findAny()
.orElseThrow(() -> new GlobalException(ResultCase.INVALID_INPUT));
}
}
10 changes: 10 additions & 0 deletions src/main/java/bc1/gream/domain/coupon/entity/DiscountType.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import bc1.gream.global.common.ResultCase;
import bc1.gream.global.exception.GlobalException;
import com.fasterxml.jackson.annotation.JsonCreator;
import java.util.stream.Stream;

public enum DiscountType {
RATE {
Expand All @@ -23,5 +25,13 @@ public Long calculateDiscount(Coupon coupon, Long price) {
}
};

@JsonCreator
public static DiscountType deserializeRequest(String discountTypeRequest) {
return Stream.of(DiscountType.values())
.filter(discountType -> discountType.toString().equals(discountTypeRequest.toUpperCase()))
.findAny()
.orElseThrow(() -> new GlobalException(ResultCase.INVALID_INPUT));
}

public abstract Long calculateDiscount(Coupon coupon, Long price);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package bc1.gream.domain.coupon.mapper;

import bc1.gream.domain.admin.dto.response.AdminCreateCouponResponseDto;
import bc1.gream.domain.coupon.dto.response.CouponAvailableResponseDto;
import bc1.gream.domain.coupon.dto.response.CouponUnavailableResponseDto;
import bc1.gream.domain.coupon.entity.Coupon;
Expand All @@ -14,4 +15,6 @@ public interface CouponMapper {
CouponAvailableResponseDto toCouponListResponseDto(Coupon coupon);

CouponUnavailableResponseDto toCouponUsedListResponseDto(Coupon coupon);

AdminCreateCouponResponseDto toAdminCreateCouponResponseDto(Coupon coupon);
}
27 changes: 27 additions & 0 deletions src/main/java/bc1/gream/domain/coupon/provider/CouponProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package bc1.gream.domain.coupon.provider;

import bc1.gream.domain.admin.dto.request.AdminCreateCouponRequestDto;
import bc1.gream.domain.coupon.entity.Coupon;
import bc1.gream.domain.coupon.service.command.CouponCommandService;
import bc1.gream.domain.user.entity.User;
import bc1.gream.domain.user.repository.UserRepository;
import bc1.gream.global.common.ResultCase;
import bc1.gream.global.exception.GlobalException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class CouponProvider {

private final UserRepository userRepository;
private final CouponCommandService couponCommandService;

@Transactional
public Coupon createCoupon(AdminCreateCouponRequestDto adminCreateCouponRequestDto) {
User user = userRepository.findByLoginId(adminCreateCouponRequestDto.userLoginId())
.orElseThrow(() -> new GlobalException(ResultCase.USER_NOT_FOUND));
return couponCommandService.createCoupon(user, adminCreateCouponRequestDto);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
package bc1.gream.domain.coupon.service.command;

import bc1.gream.domain.admin.dto.request.AdminCreateCouponRequestDto;
import bc1.gream.domain.coupon.entity.Coupon;
import bc1.gream.domain.coupon.entity.CouponStatus;
import bc1.gream.domain.coupon.repository.CouponRepository;
import bc1.gream.domain.user.entity.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class CouponCommandService {

private final CouponRepository couponRepository;

public void changeCouponStatus(Coupon coupon, CouponStatus couponStatus) {
coupon.changeStatus(couponStatus);
}

public Coupon createCoupon(User user, AdminCreateCouponRequestDto adminCreateCouponRequestDto) {
Coupon coupon = Coupon.builder()
.name(adminCreateCouponRequestDto.name())
.discountType(adminCreateCouponRequestDto.discountType())
.discount(adminCreateCouponRequestDto.discount())
.status(CouponStatus.AVAILABLE)
.user(user)
.build();
return couponRepository.save(coupon);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@
import bc1.gream.global.common.ResultCase;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

/**
* RequestBody 파라미터 검증 오류 발생에 대한 핸들러
*
* @param ex RequestBody 파라미터 검증오류에 따른 MethodArgumentNotValidException
* @return 잘못입력된 값 리스트
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<RestResponse<List<InvalidInputResponseDto>>> handlerValidationException(MethodArgumentNotValidException ex) {
List<InvalidInputResponseDto> invalidInputList = ex.getBindingResult()
Expand All @@ -25,6 +32,29 @@ public ResponseEntity<RestResponse<List<InvalidInputResponseDto>>> handlerValida
return RestResponse.error(ResultCase.INVALID_INPUT, invalidInputList);
}

/**
* ModelAttribute 파라미터 검증 오류 발생에 대한 핸들러
*
* @param ex ModelAttribute 파라미터 검증오류에 따른 BindException
* @return 잘못입력된 값 리스트
*/
@ExceptionHandler(BindException.class)
public ResponseEntity<RestResponse<List<InvalidInputResponseDto>>> handlerValidationException(BindException ex) {
List<InvalidInputResponseDto> invalidInputList = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(InvalidInputMapper.INSTANCE::toInvalidInputResponseDto)
.toList();

return RestResponse.error(ResultCase.INVALID_INPUT, invalidInputList);
}

/**
* Business 오류 발생에 대한 핸들러
*
* @param e Business 오류에 따른 GlobalException
* @return 에러케이스와 에러리스폰스
*/
@ExceptionHandler(GlobalException.class)
public ResponseEntity<RestResponse<ErrorResponseDto>> handleGlobalException(GlobalException e) {
return RestResponse.error(e.getResultCase(), new ErrorResponseDto());
Expand Down
Loading

0 comments on commit b5c09db

Please sign in to comment.