Skip to content

Commit

Permalink
Merge branch 'dev' into feat/58-auction-bid-validate-stock
Browse files Browse the repository at this point in the history
  • Loading branch information
minseok-oh authored Aug 13, 2024
2 parents e67927f + cd9abdc commit ef3e10d
Show file tree
Hide file tree
Showing 14 changed files with 371 additions and 109 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,75 @@
package com.wootecam.luckyvickyauction.core.auction.dto;

import com.wootecam.luckyvickyauction.global.exception.BadRequestException;
import com.wootecam.luckyvickyauction.global.exception.ErrorCode;
import lombok.Builder;

/**
* @param sellerId 판매자의 ID
* @param productName 판매상품의 이름
* @param price 현재 단일 상품의 가격
* @param quantity 현재 경매에 구매할 수 있는 재고 개수
* @param sellerId 판매자의 ID
* @param productName 판매상품의 이름
* @param originPrice 판매상품의 원래 가격
* @param currentPrice 현재 경매에 설정된 가격
* @param stock 현재 경매에 남은 재고 개수
* @param maximumPurchaseLimitCount 최대 구매 가능한 개수
* @param isShowStock 재고를 보여줄지 여부
*/
public record AuctionInfo(Long sellerId, String productName, long price, long quantity) {
@Builder
public record AuctionInfo(Long auctionId, Long sellerId, String productName, long originPrice, long currentPrice, int stock,
int maximumPurchaseLimitCount, boolean isShowStock) {

public static final String ERROR_PRODUCT_NAME = "상품 이름은 비어있을 수 없습니다.";
public static final String ERROR_ORIGIN_PRICE = "상품 원가는 0보다 커야 합니다. 상품 원가: %d";
public static final String ERROR_CURRENT_PRICE = "현재 가격은 0보다 커야 합니다. 현재 가격: %d";
public static final String ERROR_STOCK = "재고는 0보다 작을 수 없습니다. 재고: %d";
public static final String ERROR_MAXIMUM_PURCHASE_LIMIT_COUNT = "최대 구매 수량 제한은 0보다 커야 합니다. 최대 구매 수량 제한: %d";
public static final String ERROR_NULL_VALUE = "%s는 Null일 수 없습니다.";

public AuctionInfo {
validateNotNull(auctionId, "경매 ID");
validateNotNull(sellerId, "판매자 ID");
validateNotNull(productName, "상품 이름");

validateProductName(productName);
validateOriginPrice(originPrice);
validateCurrentPrice(currentPrice);
validateStock(stock);
validateMaximumPurchaseLimitCount(maximumPurchaseLimitCount);
}

private void validateProductName(String productName) {
if (productName.isEmpty()) {
throw new BadRequestException(ERROR_PRODUCT_NAME, ErrorCode.A001);
}
}

private void validateOriginPrice(long originPrice) {
if (originPrice <= 0) {
throw new BadRequestException(String.format(ERROR_ORIGIN_PRICE, originPrice), ErrorCode.A002);
}
}

private void validateCurrentPrice(long currentPrice) {
if (currentPrice <= 0) {
throw new BadRequestException(String.format(ERROR_CURRENT_PRICE, currentPrice), ErrorCode.A013);
}
}

private void validateStock(int stock) {
if (stock < 0) {
throw new BadRequestException(String.format(ERROR_STOCK, stock), ErrorCode.A000);
}
}

private void validateMaximumPurchaseLimitCount(int maximumPurchaseLimitCount) {
if (maximumPurchaseLimitCount <= 0) {
throw new BadRequestException(String.format(ERROR_MAXIMUM_PURCHASE_LIMIT_COUNT, maximumPurchaseLimitCount),
ErrorCode.A003);
}
}

private void validateNotNull(Object value, String fieldName) {
if (value == null) {
throw new BadRequestException(String.format(ERROR_NULL_VALUE, fieldName), ErrorCode.A007);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,22 @@ public void closeAuction(long auctionId) {
* 경매 단건 조회
*/
public AuctionInfo getAuction(long auctionId) {
return null;
// auctionRepository 에서 auctionId로 조회
Auction auction = auctionRepository.findById(auctionId)
.orElseThrow(() -> new NotFoundException("경매(Auction)를 찾을 수 없습니다. AuctionId: " + auctionId,
ErrorCode.A011));

// AuctionInfo 에 정리해서 반환
return AuctionInfo.builder()
.auctionId(auctionId)
.sellerId(auction.getSellerId())
.productName(auction.getProductName())
.originPrice(auction.getOriginPrice())
.currentPrice(auction.getCurrentPrice())
.stock(auction.getStock())
.maximumPurchaseLimitCount(auction.getMaximumPurchaseLimitCount())
.isShowStock(auction.isShowStock())
.build();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,8 @@ public void chargePoint(long price) {
public boolean isBuyer() {
return role.equals(Role.BUYER);
}

public boolean confirmPassword(String password) {
return this.password.equals(password);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ public interface MemberRepository {
Member save(Member member);

Optional<Member> findById(Long id);

Optional<Member> findBySignInId(String signInId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.wootecam.luckyvickyauction.core.member.dto;

import com.wootecam.luckyvickyauction.core.member.domain.Role;
import com.wootecam.luckyvickyauction.global.exception.BadRequestException;
import com.wootecam.luckyvickyauction.global.exception.ErrorCode;
import java.util.Objects;

public record SignInInfo(
Long id,
Role role
) {
private static final String ERROR_NULL_VALUE = "%s는 Null일 수 없습니다.";

public SignInInfo {
validateNotNull(id, "로그인한 사용자의 식별자");
validateNotNull(role, "로그인한 사용자의 역할");
}

private void validateNotNull(Object value, String fieldName) {
if (Objects.isNull(value)) {
throw new BadRequestException(String.format(ERROR_NULL_VALUE, fieldName), ErrorCode.G000);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.wootecam.luckyvickyauction.core.member.dto;

import com.wootecam.luckyvickyauction.global.exception.BadRequestException;
import com.wootecam.luckyvickyauction.global.exception.ErrorCode;
import java.util.Objects;

public record SignInRequestInfo(
String signInId,
String password
) {

private static final String ERROR_NULL_VALUE = "%s는 Null일 수 없습니다.";

public SignInRequestInfo {
validateNotNull(signInId, "로그인 ID");
validateNotNull(password, "로그인 패스워드");
}

private void validateNotNull(Object value, String fieldName) {
if (Objects.isNull(value)) {
throw new BadRequestException(String.format(ERROR_NULL_VALUE, fieldName), ErrorCode.G000);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@
import com.wootecam.luckyvickyauction.global.exception.ErrorCode;
import java.util.Objects;

public record SignUpInfo(
public record SignUpRequestInfo(
String signUpId,
String password,
String userRole
) {

private static final String ERROR_NULL_VALUE = "%s는 Null일 수 없습니다.";

public SignUpInfo {
public SignUpRequestInfo {
validateNotNull(signUpId, "회원가입 ID");
validateNotNull(password, "회원가입 패스워드");
validateNotNull(userRole, "사용자 역할");
}

private void validateNotNull(Object value, String fieldName) {
if (Objects.isNull(value)) {
throw new BadRequestException(String.format(ERROR_NULL_VALUE, fieldName), ErrorCode.A007);
throw new BadRequestException(String.format(ERROR_NULL_VALUE, fieldName), ErrorCode.G000);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import com.wootecam.luckyvickyauction.core.member.domain.Member;
import com.wootecam.luckyvickyauction.core.member.domain.MemberRepository;
import com.wootecam.luckyvickyauction.core.member.dto.SignUpInfo;
import com.wootecam.luckyvickyauction.core.member.dto.SignInInfo;
import com.wootecam.luckyvickyauction.core.member.dto.SignInRequestInfo;
import com.wootecam.luckyvickyauction.core.member.dto.SignUpRequestInfo;
import com.wootecam.luckyvickyauction.global.exception.BadRequestException;
import com.wootecam.luckyvickyauction.global.exception.ErrorCode;
import jakarta.servlet.http.HttpSession;
Expand All @@ -13,19 +15,32 @@ public class MemberService {

private final MemberRepository memberRepository;

public void signUp(SignUpInfo signUpInfo) {
if (memberRepository.isExist(signUpInfo.signUpId())) {
throw new BadRequestException("이미 존재하는 아이디입니다. input=" + signUpInfo.signUpId(), ErrorCode.M000);
public void signUp(SignUpRequestInfo signUpRequestInfo) {
if (memberRepository.isExist(signUpRequestInfo.signUpId())) {
throw new BadRequestException("이미 존재하는 아이디입니다. input=" + signUpRequestInfo.signUpId(), ErrorCode.M000);
}
Member member = Member.createMemberWithRole(
signUpInfo.signUpId(),
signUpInfo.password(),
signUpInfo.userRole()
signUpRequestInfo.signUpId(),
signUpRequestInfo.password(),
signUpRequestInfo.userRole()
);

memberRepository.save(member);
}

public void signIn(SignInRequestInfo signInRequestInfo, HttpSession session) {
Member member = memberRepository.findBySignInId(signInRequestInfo.signInId()).orElseThrow(
() -> new BadRequestException("아이디에 해당되는 사용자를 찾을 수 없습니다. signInId=" + signInRequestInfo.signInId(),
ErrorCode.M002));

if (!member.confirmPassword(signInRequestInfo.password())) {
throw new BadRequestException("패스워드가 일치하지 않습니다.", ErrorCode.M003);
}

SignInInfo signInInfo = new SignInInfo(member.getId(), member.getRole());
session.setAttribute("signInInfo", signInInfo);
}

public void signOut(HttpSession session) {
session.invalidate();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,21 @@ public enum ErrorCode {
A010("할인율은 0 초과 100 미만이어야 합니다."),
A011("경매ID를 기준으로 경매를 찾으려고 했지만 찾을 수 없습니다."),
A012("이미 시작된 경매를 변경하려고 할 때, 예외가 발생합니다."),
A013("경매 정보 생성 시, 현재 가격은 0과 같거나 작을 경우 예외가 발생합니다."),
A014("요청 수량만큼 경매 상품 구입이 불가능 할 때 예외가 발생합니다."),

// Member 관련 예외 코드
M000("로그인(회원가입) 시, 이미 존재하는 회원 아이디로 로그인을 시도한 경우 예외가 발생합니다."),
M001("로그인(회원가입) 시, 사용자의 역할(구매자, 판매자)를 찾을 수 없는 경우 예외가 발생합니다."),
M002("사용자 조회 시, 사용자를 찾을 수 없는 경우 예외가 발생합니다."),
M003("로그인 시, 입력 패스워드와 실제 패스워드가 다른 경우 예외가 발생합니다."),

// Payment 관련 예외 코드
P000("입찰 시, 로그인한 사용자가 구매자가 아닌 경우 예외가 발생합니다."),
P001("입찰 시, 사용자의 포인트가 부족한 경우 예외가 발생합니다.");
P001("입찰 시, 사용자의 포인트가 부족한 경우 예외가 발생합니다."),

// Global 예외
G000("DTO 생성 시, 필드의 값이 NULL인 경우 예외가 발생합니다.");

private final String description;

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.wootecam.luckyvickyauction.core.auction.dto;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertAll;

import com.wootecam.luckyvickyauction.global.exception.BadRequestException;
import com.wootecam.luckyvickyauction.global.exception.ErrorCode;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

class AuctionInfoTest {
static Stream<Arguments> auctionInfoDtoArguments() {
return Stream.of(
Arguments.of("상품 이름은 비어있을 수 없습니다.", ErrorCode.A001, 1L, 1L, "", 10000, 10000, 10, 10),
Arguments.of("상품 원가는 0보다 커야 합니다. 상품 원가: 0", ErrorCode.A002, 1L, 1L, "상품이름", 0, 10000, 10, 10),
Arguments.of("현재 가격은 0보다 커야 합니다. 현재 가격: 0", ErrorCode.A013, 1L, 1L, "상품이름", 10000, 0, 10, 10),
Arguments.of("재고는 0보다 작을 수 없습니다. 재고: -1", ErrorCode.A000, 1L, 1L, "상품이름", 10000, 10000, -1, 10),
Arguments.of("최대 구매 수량 제한은 0보다 커야 합니다. 최대 구매 수량 제한: 0", ErrorCode.A003, 1L, 1L, "상품이름", 10000, 10000, 10, 0)
);
}

@Test
void 경매_정보_생성_요청을_정상적으로_처리한다() {
// given
Long auctionId = 1L;
Long sellerId = 1L;
String productName = "상품이름";
long originPrice = 10000;
long currentPrice = 10000;
int stock = 10;
int maximumPurchaseLimitCount = 10;

// when
AuctionInfo auctionInfo = new AuctionInfo(
auctionId, sellerId, productName, originPrice, currentPrice, stock, maximumPurchaseLimitCount, true
);

// then
assertAll(
() -> assertThat(auctionInfo.sellerId()).isEqualTo(sellerId),
() -> assertThat(auctionInfo.productName()).isEqualTo(productName),
() -> assertThat(auctionInfo.originPrice()).isEqualTo(originPrice),
() -> assertThat(auctionInfo.currentPrice()).isEqualTo(currentPrice),
() -> assertThat(auctionInfo.stock()).isEqualTo(stock),
() -> assertThat(auctionInfo.maximumPurchaseLimitCount()).isEqualTo(maximumPurchaseLimitCount),
() -> assertThat(auctionInfo.isShowStock()).isTrue()
);
}

@ParameterizedTest
@MethodSource("auctionInfoDtoArguments")
void 경매_정보_생성_요청이_잘못된_경우_예외가_발생한다(
String expectedMessage,
ErrorCode expectedErrorCode,
Long auctionId,
Long sellerId,
String productName,
long originPrice,
long currentPrice,
int stock,
int maximumPurchaseLimitCount
) {
// expect
assertThatThrownBy(() -> new AuctionInfo(
auctionId, sellerId, productName, originPrice, currentPrice, stock,
maximumPurchaseLimitCount, true))
.isInstanceOf(BadRequestException.class)
.satisfies(exception -> {
assertThat(exception).hasFieldOrPropertyWithValue("errorCode", expectedErrorCode);
});
}
}
Loading

0 comments on commit ef3e10d

Please sign in to comment.