Skip to content

Commit

Permalink
[Feature] #101 sale (#112)
Browse files Browse the repository at this point in the history
* [feat] 판매 가격 계산 로직 facade -> saleDto

* [refactor] 불필요한 로직 제거

* [feat] update 트랜잭션 전파수준 변경

* [feat] sale 도메인 내에서 판매 가격 로직 제거

* [test] 판매 가격 로직 saleDto로 변경
  • Loading branch information
JinDDung2 authored Jul 1, 2024
1 parent acc7d21 commit 10df0f4
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -1,52 +1,51 @@
package com.jinddung2.givemeticon.domain.sale.controller.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.jinddung2.givemeticon.domain.item.domain.Item;
import com.jinddung2.givemeticon.domain.sale.domain.Sale;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Date;
import java.time.LocalDate;
import java.time.LocalDateTime;

@Getter
@NoArgsConstructor
@EqualsAndHashCode
public class SaleDto {
import static com.jinddung2.givemeticon.domain.trade.domain.DiscountRatePolicy.STANDARD;
import static com.jinddung2.givemeticon.domain.trade.domain.DiscountRatePolicy.WEEKLY_DISCOUNT;

private int id;
private int itemId;
private int sellerId;
private String barcode;
private LocalDate expirationDate;
@JsonProperty(value = "isBought")
private boolean isBought;
private Date isBoughtDate;
private LocalDateTime createdDate;
public record SaleDto(
int id,
int itemId,
int sellerId,
String barcode,
BigDecimal discountedPrice,
LocalDate expirationDate,
@JsonProperty(value = "isBought") boolean isBought,
Date isBoughtDate,
LocalDateTime createdDate
) {

@Builder
public SaleDto(int id, int itemId, int sellerId, String barcode, LocalDate expirationDate, boolean isBought, Date isBoughtDate, LocalDateTime createdDate) {
this.id = id;
this.itemId = itemId;
this.sellerId = sellerId;
this.barcode = barcode;
this.expirationDate = expirationDate;
this.isBought = isBought;
this.isBoughtDate = isBoughtDate;
this.createdDate = createdDate;
public static SaleDto of(Sale sale, Item item) {
long restDay = sale.getRestDay();
double discountRate = restDay > 7L ? STANDARD.getDiscountRate() : WEEKLY_DISCOUNT.getDiscountRate();
return new SaleDto(
sale.getId(),
sale.getItemId(),
sale.getSellerId(),
sale.getBarcode(),
calculateSalePrice(item.getPrice(), discountRate),
sale.getExpirationDate(),
sale.isBought(),
sale.getIsBoughtDate(),
sale.getCreatedDate()
);
}

public static SaleDto of(Sale sale) {
return SaleDto.builder()
.id(sale.getId())
.itemId(sale.getItemId())
.sellerId(sale.getSellerId())
.barcode(sale.getBarcode())
.expirationDate(sale.getExpirationDate())
.isBought(sale.isBought())
.isBoughtDate(sale.getIsBoughtDate())
.build();
private static BigDecimal calculateSalePrice(int price, double discountRate) {
BigDecimal originalPrice = BigDecimal.valueOf(price);
BigDecimal discountPrice = originalPrice.multiply(BigDecimal.valueOf(discountRate))
.setScale(0, RoundingMode.HALF_UP);

return originalPrice.subtract(discountPrice);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.sql.Date;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

@Getter
@NoArgsConstructor
Expand Down Expand Up @@ -60,4 +61,8 @@ public void updateBoughtState() {
this.isBought = true;
this.isBoughtDate = Date.valueOf(LocalDate.now());
}

public long getRestDay() {
return ChronoUnit.DAYS.between(LocalDate.now(), this.expirationDate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public BigDecimal getTotalAmountForSales(int userId) {
}

public SaleDto getAvailableSales(int saleId) {
return saleService.getAvailableSaleForItem(saleId);
Sale sale = saleService.getSale(saleId);
Item item = itemService.getItem(sale.getItemId());
return saleService.getAvailableSaleForItem(sale, item);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
import com.jinddung2.givemeticon.domain.trade.exception.AlreadyBoughtSaleException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;
Expand All @@ -31,15 +29,14 @@ public class SaleService {
public int save(int itemId, int sellerId, SaleCreateRequest request) {
validateDuplicateBarcode(request.barcode());

Sale itemVariant = request.toEntity();
itemVariant.updateItemId(itemId);
itemVariant.updateSellerId(sellerId);
Sale sale = request.toEntity();
sale.updateItemId(itemId);
sale.updateSellerId(sellerId);

saleMapper.save(itemVariant);
return itemVariant.getId();
saleMapper.save(sale);
return sale.getId();
}

@Transactional(propagation = Propagation.REQUIRED)
public int update(Sale sale) {
saleMapper.update(sale);
return sale.getId();
Expand All @@ -55,9 +52,7 @@ public Sale getSale(int saleId) {
return saleMapper.findById(saleId).orElseThrow(NotFoundSaleException::new);
}

public SaleDto getAvailableSaleForItem(int saleId) {
Sale sale = getSale(saleId);

public SaleDto getAvailableSaleForItem(Sale sale, Item item) {
if (sale.isBought() && sale.getIsBoughtDate() != null) {
throw new AlreadyBoughtSaleException();
}
Expand All @@ -66,14 +61,14 @@ public SaleDto getAvailableSaleForItem(int saleId) {
throw new ExpiredSaleException();
}

return SaleDto.of(sale);
return SaleDto.of(sale, item);
}

public List<SaleDto> getAvailableSalesForItem(Item item) {
List<Sale> sales = saleMapper.findNotBoughtSalesByItemId(item.getId());
return sales.stream()
.filter(sale -> !sale.getExpirationDate().isBefore(LocalDate.now()))
.map(SaleDto::of)
.map(sale -> SaleDto.of(sale, item))
.collect(Collectors.toList());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ void get_Sale_Success() throws Exception {
User seller = UserFixture.createUserFixture(now);
Item item = ItemFixture.createItemFixture();
Sale sale = SaleFixture.createSaleFixture(seller, item);
SaleDto result = SaleDto.of(sale);
SaleDto result = SaleDto.of(sale, item);

when(saleReadFacade.getAvailableSales(sale.getId())).thenReturn(result);
mockMvc.perform(MockMvcRequestBuilders
Expand All @@ -178,14 +178,13 @@ void get_Sale_Success() throws Exception {
.sessionAttr(LOGIN_USER, seller.getId()))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$.message").value("SUCCESS"))
.andExpect(jsonPath("$.data.id").value(result.getId()))
.andExpect(jsonPath("$.data.itemId").value(result.getItemId()))
.andExpect(jsonPath("$.data.sellerId").value(result.getSellerId()))
.andExpect(jsonPath("$.data.barcode").value(result.getBarcode()))
.andExpect(jsonPath("$.data.expirationDate").value(result.getExpirationDate().toString()))
.andExpect(jsonPath("$.data.id").value(result.id()))
.andExpect(jsonPath("$.data.itemId").value(result.itemId()))
.andExpect(jsonPath("$.data.sellerId").value(result.sellerId()))
.andExpect(jsonPath("$.data.barcode").value(result.barcode()))
.andExpect(jsonPath("$.data.expirationDate").value(result.expirationDate().toString()))
.andExpect(jsonPath("$.data.isBought").value(result.isBought()))
.andExpect(jsonPath("$.data.isBoughtDate").value(result.getIsBoughtDate().toString()))
.andExpect(jsonPath("$.data.createdDate").value(result.getCreatedDate()));
.andExpect(jsonPath("$.data.isBoughtDate").value(result.isBoughtDate().toString()));

verify(saleReadFacade).getAvailableSales(sale.getId());
}
Expand Down Expand Up @@ -220,10 +219,11 @@ void get_Sales_By_ItemId_Success() throws Exception {
Sale sale1 = SaleFixture.createSaleFixture(10, seller, item);
Sale sale2 = SaleFixture.createSaleFixture(10, seller, item);
Sale sale3 = SaleFixture.createSaleFixture(10, seller, item);

List<SaleDto> responseBody = new ArrayList<>();
responseBody.add(SaleDto.of(sale1));
responseBody.add(SaleDto.of(sale2));
responseBody.add(SaleDto.of(sale3));
responseBody.add(SaleDto.of(sale1, item));
responseBody.add(SaleDto.of(sale2, item));
responseBody.add(SaleDto.of(sale3, item));

when(saleReadFacade.getSalesForItem(item.getId())).thenReturn(responseBody);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,14 @@ class SaleReadFacadeTest {
void get_Sales_By_ItemId_Success() {
Item item = ItemFixture.createItemFixture();
User seller = UserFixture.createUserFixture(now);
SaleDto sale1 = SaleDto.of(SaleFixture.createSaleFixture(10, seller, item));
SaleDto sale2 = SaleDto.of(SaleFixture.createSaleFixture(20, seller, item));
SaleDto sale3 = SaleDto.of(SaleFixture.createSaleFixture(30, seller, item));
Sale sale1 = SaleFixture.createSaleFixture(10, seller, item);
Sale sale2 = SaleFixture.createSaleFixture(20, seller, item);
Sale sale3 = SaleFixture.createSaleFixture(30, seller, item);
SaleDto saleDto1 = SaleDto.of(sale1, item);
SaleDto saleDto2 = SaleDto.of(sale2, item);
SaleDto saleDto3 = SaleDto.of(sale3, item);
when(itemService.getItem(item.getId())).thenReturn(item);
List<SaleDto> sales = List.of(sale1, sale2, sale3);
List<SaleDto> sales = List.of(saleDto1, saleDto2, saleDto3);
when(saleService.getAvailableSalesForItem(item)).thenReturn(sales);

List<SaleDto> result = sut.getSalesForItem(item.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,10 @@ void get_Sale_Success() {
User seller = UserFixture.createUserFixture(now);
Item item = ItemFixture.createItemFixture();
Sale sale = SaleFixture.createSaleFixture(seller, item);
Mockito.when(saleMapper.findById(sale.getId())).thenReturn(Optional.of(sale));
SaleDto expected = SaleDto.of(sale);
SaleDto expected = SaleDto.of(sale, item);

SaleDto result = sut.getAvailableSaleForItem(sale.getId());
SaleDto result = sut.getAvailableSaleForItem(sale, item);

Mockito.verify(saleMapper).findById(sale.getId());
assertThat(result).isEqualTo(expected);
}

Expand All @@ -101,25 +99,24 @@ void get_Sale_Fail_Not_Found_Sale() {
}

@Test
@DisplayName("판매 상품이 유효기긴이 지났기에 단건 조회에 실패한다.")
@DisplayName("판매 상품이 유효기간이 지나서 단건 조회에 실패한다.")
void get_Sale_Fail_Expired() {
Sale fakeSale = Sale.builder().id(20).expirationDate(LocalDate.now().minusDays(1)).build();
Mockito.when(saleMapper.findById(fakeSale.getId())).thenReturn(Optional.of(fakeSale));
Item item = ItemFixture.createItemFixture();
Sale sale = Sale.builder().id(20).itemId(item.getId()).expirationDate(LocalDate.now().minusDays(1)).build();

Assertions.assertThrows(ExpiredSaleException.class,
() -> sut.getAvailableSaleForItem(fakeSale.getId()));
() -> sut.getAvailableSaleForItem(sale, item));
}

@Test
@DisplayName("판매 상품이 이미 거래 되어 단건 조회에 실패한다.")
@DisplayName("이미 거래된 상품이라 단건 조회에 실패한다.")
void get_Sale_Fail_Already_Bought() {
User seller = UserFixture.createUserFixture(now);
Item item = ItemFixture.createItemFixture();
Sale sale = SaleFixture.createBoughtSaleFixture(seller, item);
Mockito.when(saleMapper.findById(sale.getId())).thenReturn(Optional.of(sale));

Assertions.assertThrows(AlreadyBoughtSaleException.class,
() -> sut.getAvailableSaleForItem(sale.getId()));
() -> sut.getAvailableSaleForItem(sale, item));
}

@Test
Expand All @@ -131,7 +128,9 @@ void get_Sales_By_ItemId_Success() {
Sale sale2 = SaleFixture.createSaleFixture(20, seller, item);
Sale sale3 = SaleFixture.createSaleFixture(30, seller, item);
List<Sale> sales = List.of(sale1, sale2, sale3);
List<SaleDto> expected = sales.stream().map(SaleDto::of).toList();
List<SaleDto> expected = sales.stream()
.map(sale -> SaleDto.of(sale, item))
.toList();

Mockito.when(saleMapper.findNotBoughtSalesByItemId(item.getId())).thenReturn(sales);

Expand Down

0 comments on commit 10df0f4

Please sign in to comment.