Skip to content

Commit

Permalink
fix: 포트폴리오 검증 수정 (#319)
Browse files Browse the repository at this point in the history
  • Loading branch information
yonghwankim-dev authored Apr 22, 2024
1 parent 146ceb2 commit eada9fc
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 120 deletions.
29 changes: 29 additions & 0 deletions src/main/java/codesquad/fineants/domain/portfolio/Portfolio.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
import codesquad.fineants.domain.notification.type.NotificationType;
import codesquad.fineants.domain.portfolio_gain_history.PortfolioGainHistory;
import codesquad.fineants.domain.portfolio_holding.PortfolioHolding;
import codesquad.fineants.spring.api.common.errors.errorcode.PortfolioErrorCode;
import codesquad.fineants.spring.api.common.errors.exception.BadRequestException;
import codesquad.fineants.spring.api.common.errors.exception.FineAntsException;
import codesquad.fineants.spring.api.kis.manager.CurrentPriceManager;
import codesquad.fineants.spring.api.notification.manager.NotificationSentManager;
import codesquad.fineants.spring.api.notification.response.NotifyMessage;
Expand Down Expand Up @@ -91,9 +94,16 @@ public class Portfolio extends BaseEntity {
@OneToMany(mappedBy = "portfolio")
private final List<PortfolioHolding> portfolioHoldings = new ArrayList<>();

private Portfolio(String name, String securitiesFirm, Money budget, Money targetGain, Money maximumLoss,
Boolean targetGainIsActive, Boolean maximumLossIsActive, Member member) {
this(null, name, securitiesFirm, budget, targetGain, maximumLoss, targetGainIsActive, maximumLossIsActive,
member);
}

@Builder
public Portfolio(Long id, String name, String securitiesFirm, Money budget, Money targetGain, Money maximumLoss,
Boolean targetGainIsActive, Boolean maximumLossIsActive, Member member) {
validateBudget(budget, targetGain, maximumLoss);
this.id = id;
this.name = name;
this.securitiesFirm = securitiesFirm;
Expand All @@ -105,6 +115,25 @@ public Portfolio(Long id, String name, String securitiesFirm, Money budget, Mone
this.member = member;
}

private void validateBudget(Money budget, Money targetGain, Money maximumLoss) {
if (budget.isZero()) {
return;
}
// 목표 수익 금액이 예산 보다 큰지 검증
if (budget.compareTo(targetGain) >= 0) {
throw new FineAntsException(PortfolioErrorCode.TARGET_GAIN_LOSS_IS_EQUAL_LESS_THAN_BUDGET);
}
// 최대 손실 금액이 예산 보다 작은지 검증
if (budget.compareTo(maximumLoss) <= 0) {
throw new BadRequestException(PortfolioErrorCode.MAXIMUM_LOSS_IS_EQUAL_GREATER_THAN_BUDGET);
}
}

public static Portfolio noActive(String name, String securitiesFirm, Money budget, Money targetGain,
Money maximumLoss, Member member) {
return new Portfolio(name, securitiesFirm, budget, targetGain, maximumLoss, false, false, member);
}

//== 연관관계 메소드 ==//
public void addPortfolioStock(PortfolioHolding portFolioHolding) {
if (!portfolioHoldings.contains(portFolioHolding)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,6 @@ public class PortfolioCreateRequest {
private Money maximumLoss;

public Portfolio toEntity(Member member) {
return Portfolio.builder()
.name(name)
.securitiesFirm(securitiesFirm)
.budget(budget)
.targetGain(targetGain)
.maximumLoss(maximumLoss)
.targetGainIsActive(false)
.maximumLossIsActive(false)
.member(member)
.build();
return Portfolio.noActive(name, securitiesFirm, budget, targetGain, maximumLoss, member);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,6 @@ public class PortfolioModifyRequest {
private Money maximumLoss;

public Portfolio toEntity(Member member) {
return Portfolio.builder()
.name(name)
.securitiesFirm(securitiesFirm)
.budget(budget)
.targetGain(targetGain)
.maximumLoss(maximumLoss)
.member(member)
.build();
return Portfolio.noActive(name, securitiesFirm, budget, targetGain, maximumLoss, member);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import codesquad.fineants.domain.common.money.Money;
import codesquad.fineants.domain.member.Member;
import codesquad.fineants.domain.member.MemberRepository;
import codesquad.fineants.domain.oauth.support.AuthMember;
Expand Down Expand Up @@ -52,8 +51,6 @@ public class PortFolioService {

@Transactional
public PortFolioCreateResponse createPortfolio(PortfolioCreateRequest request, AuthMember authMember) {
validateTargetGainIsEqualLessThanBudget(request.getTargetGain(), request.getBudget());
validateMaximumLossIsEqualGraterThanBudget(request.getMaximumLoss(), request.getBudget());
validateSecuritiesFirm(request.getSecuritiesFirm());

Member member = findMember(authMember.getMemberId());
Expand All @@ -68,18 +65,6 @@ private Member findMember(Long memberId) {
.orElseThrow(() -> new NotFoundResourceException(MemberErrorCode.NOT_FOUND_MEMBER));
}

private void validateTargetGainIsEqualLessThanBudget(Money targetGain, Money budget) {
if (budget.compareTo(Money.zero()) > 0 && targetGain.compareTo(budget) <= 0) {
throw new BadRequestException(PortfolioErrorCode.TARGET_GAIN_LOSS_IS_EQUAL_LESS_THAN_BUDGET);
}
}

private void validateMaximumLossIsEqualGraterThanBudget(Money maximumLoss, Money budget) {
if (budget.compareTo(Money.zero()) > 0 && maximumLoss.compareTo(budget) >= 0) {
throw new BadRequestException(PortfolioErrorCode.MAXIMUM_LOSS_IS_EQUAL_GREATER_THAN_BUDGET);
}
}

private void validateSecuritiesFirm(String securitiesFirm) {
if (!portfolioPropertiesManager.contains(securitiesFirm)) {
throw new BadRequestException(PortfolioErrorCode.SECURITIES_FIRM_IS_NOT_CONTAINS);
Expand All @@ -96,10 +81,6 @@ private void validateUniquePortfolioName(String name, Member member) {
public PortfolioModifyResponse updatePortfolio(PortfolioModifyRequest request, Long portfolioId,
AuthMember authMember) {
log.info("포트폴리오 수정 서비스 요청 : request={}, portfolioId={}, authMember={}", request, portfolioId, authMember);

validateTargetGainIsEqualLessThanBudget(request.getTargetGain(), request.getBudget());
validateMaximumLossIsEqualGraterThanBudget(request.getMaximumLoss(), request.getBudget());

Member member = findMember(authMember.getMemberId());
Portfolio originalPortfolio = findPortfolio(portfolioId);
Portfolio changePortfolio = request.toEntity(member);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.util.List;

import org.assertj.core.groups.Tuple;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.test.context.ActiveProfiles;
Expand All @@ -23,43 +22,12 @@
@ActiveProfiles("test")
class PortfolioTest {

private Portfolio portfolio;

private Stock stock;

private Stock stock2;

@BeforeEach
void init() {
portfolio = Portfolio.builder()
.budget(Money.from(1000000L))
.targetGain(Money.from(1500000L))
.maximumLoss(Money.from(900000L))
.build();

stock = Stock.builder()
.stockCode("KR7005930003")
.tickerSymbol("005930")
.companyName("삼성전자보통주")
.companyNameEng("SamsungElectronics")
.market(Market.KOSPI)
.sector("전기전자")
.build();

stock2 = Stock.builder()
.stockCode("KR7000020008")
.tickerSymbol("000020")
.companyName("동화약품보통주")
.companyNameEng("DongwhaPharm")
.market(Market.KOSPI)
.sector("의약품")
.build();
}

@DisplayName("포트폴리오의 총 손익을 계산한다")
@Test
void calculateTotalGain() {
// given
Portfolio portfolio = createPortfolio();
Stock stock = createSamsungStock();
PortfolioHolding portFolioHolding = PortfolioHolding.of(portfolio, stock, Money.from(20000L));

PurchaseHistory purchaseHistory1 = PurchaseHistory.builder()
Expand Down Expand Up @@ -92,6 +60,8 @@ void calculateTotalGain() {
@Test
void calculateTotalReturnRate() {
// given
Portfolio portfolio = createPortfolio();
Stock stock = createSamsungStock();
PortfolioHolding portFolioHolding = PortfolioHolding.of(portfolio, stock, Money.from(20000L));

PurchaseHistory purchaseHistory1 = PurchaseHistory.builder()
Expand Down Expand Up @@ -122,6 +92,9 @@ void calculateTotalReturnRate() {
@Test
void createPieChart() {
// given
Portfolio portfolio = createPortfolio();
Stock stock = createSamsungStock();
Stock stock2 = createDongHwa();
PortfolioHolding holding1 = PortfolioHolding.of(portfolio, stock, Money.from(20000L));
PortfolioHolding holding2 = PortfolioHolding.of(portfolio, stock2, Money.from(20000L));

Expand Down Expand Up @@ -164,6 +137,9 @@ void createPieChart() {
@Test
void createSectorChart() {
// given
Portfolio portfolio = createPortfolio();
Stock stock = createSamsungStock();
Stock stock2 = createDongHwa();
PortfolioHolding holding1 = PortfolioHolding.of(portfolio, stock, Money.from(20000L));
PortfolioHolding holding2 = PortfolioHolding.of(portfolio, stock2, Money.from(20000L));

Expand Down Expand Up @@ -197,4 +173,34 @@ void createSectorChart() {
.extracting("sector")
.containsExactlyInAnyOrder("현금", "의약품", "전기전자");
}

private Portfolio createPortfolio() {
return Portfolio.builder()
.budget(Money.from(1000000L))
.targetGain(Money.from(1500000L))
.maximumLoss(Money.from(900000L))
.build();
}

private Stock createSamsungStock() {
return Stock.builder()
.stockCode("KR7005930003")
.tickerSymbol("005930")
.companyName("삼성전자보통주")
.companyNameEng("SamsungElectronics")
.market(Market.KOSPI)
.sector("전기전자")
.build();
}

private Stock createDongHwa() {
return Stock.builder()
.stockCode("KR7000020008")
.tickerSymbol("000020")
.companyName("동화약품보통주")
.companyNameEng("DongwhaPharm")
.market(Market.KOSPI)
.sector("의약품")
.build();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package codesquad.fineants.spring.api.portfolio;
package codesquad.fineants.spring.api.portfolio.service;

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
Expand Down Expand Up @@ -47,14 +47,14 @@
import codesquad.fineants.spring.AbstractContainerBaseTest;
import codesquad.fineants.spring.api.common.errors.exception.BadRequestException;
import codesquad.fineants.spring.api.common.errors.exception.ConflictException;
import codesquad.fineants.spring.api.common.errors.exception.FineAntsException;
import codesquad.fineants.spring.api.common.errors.exception.ForBiddenException;
import codesquad.fineants.spring.api.kis.manager.CurrentPriceManager;
import codesquad.fineants.spring.api.portfolio.request.PortfolioCreateRequest;
import codesquad.fineants.spring.api.portfolio.request.PortfolioModifyRequest;
import codesquad.fineants.spring.api.portfolio.request.PortfoliosDeleteRequest;
import codesquad.fineants.spring.api.portfolio.response.PortFolioCreateResponse;
import codesquad.fineants.spring.api.portfolio.response.PortfoliosResponse;
import codesquad.fineants.spring.api.portfolio.service.PortFolioService;
import codesquad.fineants.spring.util.ObjectMapperUtil;

class PortFolioServiceTest extends AbstractContainerBaseTest {
Expand Down Expand Up @@ -135,9 +135,8 @@ void addPortfolioWithTargetGainIsEqualLessThanBudget(Long targetGain) {

// then
assertThat(throwable)
.isInstanceOf(BadRequestException.class)
.extracting("message")
.isEqualTo("목표 수익금액은 예산보다 커야 합니다");
.isInstanceOf(FineAntsException.class)
.hasMessage("목표 수익금액은 예산보다 커야 합니다");
}

@DisplayName("회원은 포트폴리오를 추가할때 최대손실율이 예산보다 같거나 크면 안된다")
Expand Down Expand Up @@ -334,7 +333,7 @@ void updatePortfolio_whenMemberAttemptsToUpdateWithBudgetLessThanTargetGain_then

// then
assertThat(throwable)
.isInstanceOf(BadRequestException.class)
.isInstanceOf(FineAntsException.class)
.hasMessage("목표 수익금액은 예산보다 커야 합니다");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.firebase.messaging.Message;

import codesquad.fineants.domain.common.count.Count;
Expand Down Expand Up @@ -71,9 +70,6 @@ class PurchaseHistoryServiceTest extends AbstractContainerBaseTest {
@Autowired
private StockRepository stockRepository;

@Autowired
private ObjectMapper objectMapper;

@Autowired
private PurchaseHistoryRepository purchaseHistoryRepository;

Expand Down Expand Up @@ -479,7 +475,7 @@ private Portfolio createPortfolio(Member member, BigDecimal budget) {
.name("내꿈은 워렌버핏")
.securitiesFirm("토스")
.budget(Money.from(budget))
.targetGain(Money.from(1500000L))
.targetGain(Money.from(budget).add(Money.from(100000L)))
.maximumLoss(Money.from(900000L))
.member(member)
.targetGainIsActive(true)
Expand Down

0 comments on commit eada9fc

Please sign in to comment.