Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 특정 상품의 다음 상환평가일 조회 기능 개발 #38

Merged
merged 8 commits into from
Jul 31, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import com.wl2c.elswhereproductservice.domain.product.model.dto.list.SummarizedProductDto;
import com.wl2c.elswhereproductservice.domain.product.model.dto.request.RequestProductSearchDto;
import com.wl2c.elswhereproductservice.domain.product.model.dto.response.ResponseNextRepaymentEvaluationDateDto;
import com.wl2c.elswhereproductservice.domain.product.model.dto.response.ResponseProductComparisonTargetDto;
import com.wl2c.elswhereproductservice.domain.product.model.dto.response.ResponseSingleProductDto;
import com.wl2c.elswhereproductservice.domain.product.service.RepaymentEvaluationDatesService;
import com.wl2c.elswhereproductservice.domain.product.service.ProductService;
import com.wl2c.elswhereproductservice.global.model.dto.ResponsePage;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -24,6 +26,7 @@
public class ProductController {

private final ProductService productService;
private final RepaymentEvaluationDatesService repaymentEvaluationDatesService;

/**
* 청약 중인 상품 목록
Expand Down Expand Up @@ -69,6 +72,18 @@ public ResponsePage<SummarizedProductDto> listByEndSale(@RequestParam(name = "ty

/**
* 상품 단건 조회
* <p>
* maturityEvaluationDateType(만기 평가일 개수)을 제공하는 이유<br/>
* 발행사에서 만기 평가일의 경우, 예정거래일 중 거래소 또는 관련 거래소가 개장하지 못하거나 시장교란 사유가 발생한 날을 대비하여 여러 만기 평가일을 제공하는 경우가 있습니다.<br/>
* 실제 서버에서는 maturityEvaluationDate는 최초 만기 평가일 날짜만 가져오지만, 발행사가 만기 평가일을 여러 날로 설정했음을 나타내고자 해당 maturityEvaluationDateType을 제공합니다.<br/><br/>
*
* maturityEvaluationDateType(만기 평가일 개수)의 각 값들의 의미는 아래와 같습니다.
*
* 한 개의 만기 평가일 : SINGLE
* 여러 개의 만기 평가일 : MULTIPLE
* 파악되지 않음(서버에서 추가 파싱 필요 혹은 투자 설명서 오류) : UNKNOWN
*
* </p>
*
* @param id 조회할 상품 id
* @return 상품 상세 정보
Expand Down Expand Up @@ -108,4 +123,19 @@ public ResponsePage<SummarizedProductDto> searchProduct(@Valid @RequestBody Requ
Page<SummarizedProductDto> result = productService.searchProduct(requestProductSearchDto, pageable);
return new ResponsePage<>(result);
}

/**
* 특정 상품의 다음 상환평가일 조회
* <p>
* 조기상환평가일이 모두 지나면, 가장 마지막은 만기상환평가일을 보여줍니다.
* </p>
*
* @param id 상품 id
* @return 다음 상환평가일
*/
@GetMapping("/next/evaluation/{id}")
public ResponseNextRepaymentEvaluationDateDto findNextRepaymentEvaluationDate(@PathVariable Long id) {
return repaymentEvaluationDatesService.findNextRepaymentEvaluationDate(id);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.wl2c.elswhereproductservice.domain.product.exception;

import com.wl2c.elswhereproductservice.global.exception.LocalizedMessageException;
import org.springframework.http.HttpStatus;

public class ProductEarlyRepaymentEvaluationDateFoundException extends LocalizedMessageException {
public ProductEarlyRepaymentEvaluationDateFoundException() {
super(HttpStatus.NOT_FOUND, "notfound.product-early-repayment-evaluation-date");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.wl2c.elswhereproductservice.domain.product.exception;

import com.wl2c.elswhereproductservice.global.exception.LocalizedMessageException;
import org.springframework.http.HttpStatus;

public class ProductMaturityEvaluationDateNotFoundException extends LocalizedMessageException {
public ProductMaturityEvaluationDateNotFoundException() {
super(HttpStatus.NOT_FOUND, "notfound.product-maturity-repayment-evaluation-date");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.wl2c.elswhereproductservice.domain.product.model;

public enum MaturityEvaluationDateType {

/**
* 한 개의 만기 평가일
*/
SINGLE,

/**
* 여러 개의 만기 평가일
*/
MULTIPLE,

/**
* 파악되지 않음
*/
UNKNOWN

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.wl2c.elswhereproductservice.domain.product.model.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

import java.time.LocalDate;

@Getter
public class ResponseNextRepaymentEvaluationDateDto {

@Schema(description = "다음 상환평가일", example = "2027-01-13")
private final LocalDate nextRepaymentEvaluationDate;

public ResponseNextRepaymentEvaluationDateDto(LocalDate nextRepaymentEvaluationDate) {
this.nextRepaymentEvaluationDate = nextRepaymentEvaluationDate;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.wl2c.elswhereproductservice.domain.product.model.dto.response;

import com.wl2c.elswhereproductservice.domain.product.model.MaturityEvaluationDateType;
import com.wl2c.elswhereproductservice.domain.product.model.ProductType;
import com.wl2c.elswhereproductservice.domain.product.model.entity.Product;
import io.swagger.v3.oas.annotations.media.Schema;
Expand Down Expand Up @@ -37,6 +38,12 @@ public class ResponseSingleProductDto {
@Schema(description = "자동조기상환평가일", example = "1차: 2024년 12월 20일 / 2차: 2025년 06월 20일 / 3차: 2025년 12월 19일")
private final String earlyRepaymentEvaluationDates;

@Schema(description = "만기상환평가일", example = "2026년 6월 19일")
private final LocalDate maturityRepaymentEvaluationDates;

@Schema(description = "만기상환평가일 개수", example = "SINGLE or MULTIPLE or UNKNOWN")
private final MaturityEvaluationDateType maturityRepaymentEvaluationDateType;

@Schema(description = "발행일", example = "2024-06-21")
private final LocalDate issuedDate;

Expand Down Expand Up @@ -88,6 +95,8 @@ public ResponseSingleProductDto(Product product, Map<String, String> equityTicke
this.knockIn = product.getKnockIn();
this.volatilites = product.getVolatilites();
this.earlyRepaymentEvaluationDates = product.getEarlyRepaymentEvaluationDates();
this.maturityRepaymentEvaluationDates = product.getMaturityEvaluationDate();
this.maturityRepaymentEvaluationDateType = product.getMaturityEvaluationDateType();
this.issuedDate = product.getIssuedDate();
this.maturityDate = product.getMaturityDate();
this.yieldIfConditionsMet = product.getYieldIfConditionsMet();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.wl2c.elswhereproductservice.domain.product.model.entity;

import com.wl2c.elswhereproductservice.domain.product.model.MaturityEvaluationDateType;
import com.wl2c.elswhereproductservice.domain.product.model.ProductState;
import com.wl2c.elswhereproductservice.domain.product.model.ProductType;
import com.wl2c.elswhereproductservice.global.base.BaseEntity;
Expand Down Expand Up @@ -41,6 +42,13 @@ public class Product extends BaseEntity {
@NotNull
private LocalDate issuedDate;

private LocalDate maturityEvaluationDate;

@NotNull
@ColumnDefault("'UNKNOWN'")
@Enumerated(STRING)
private MaturityEvaluationDateType maturityEvaluationDateType;

@NotNull
private LocalDate maturityDate;

Expand Down Expand Up @@ -95,6 +103,8 @@ private Product (@NonNull String issuer,
@NonNull String equities,
int equityCount,
@NonNull LocalDate issuedDate,
LocalDate maturityEvaluationDate,
@NonNull MaturityEvaluationDateType maturityEvaluationDateType,
@NonNull LocalDate maturityDate,
@NonNull BigDecimal yieldIfConditionsMet,
@NonNull BigDecimal maximumLossRate,
Expand All @@ -117,6 +127,8 @@ private Product (@NonNull String issuer,
this.equityCount = equityCount;
this.knockIn = knockIn;
this.issuedDate = issuedDate;
this.maturityEvaluationDate = maturityEvaluationDate;
this.maturityEvaluationDateType = maturityEvaluationDateType;
this.maturityDate = maturityDate;
this.yieldIfConditionsMet = yieldIfConditionsMet;
this.maximumLossRate = maximumLossRate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

import com.wl2c.elswhereproductservice.domain.product.model.entity.EarlyRepaymentEvaluationDates;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.web.bind.annotation.RequestParam;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

public interface EarlyRepaymentEvaluationDatesRepository extends JpaRepository<EarlyRepaymentEvaluationDates, Long> {

@Query("select e.earlyRepaymentEvaluationDate from EarlyRepaymentEvaluationDates e " +
"where e.product.productState = 'ACTIVE' " +
"and e.product.id = :productId " +
"and e.earlyRepaymentEvaluationDate > CURRENT_DATE " +
"order by e.earlyRepaymentEvaluationDate asc " +
"LIMIT 1 ")
Optional<LocalDate> findNextEarlyRepaymentEvaluationDate(@RequestParam("productId") Long productId);

@Query("select e from EarlyRepaymentEvaluationDates e " +
"where e.product.productState = 'ACTIVE' " +
"and e.product.id = :productId ")
List<EarlyRepaymentEvaluationDates> findAllEarlyRepaymentEvaluationDate(@RequestParam("productId") Long productId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.wl2c.elswhereproductservice.domain.product.service;

import com.wl2c.elswhereproductservice.domain.product.exception.ProductEarlyRepaymentEvaluationDateFoundException;
import com.wl2c.elswhereproductservice.domain.product.exception.ProductMaturityEvaluationDateNotFoundException;
import com.wl2c.elswhereproductservice.domain.product.exception.ProductNotFoundException;
import com.wl2c.elswhereproductservice.domain.product.model.dto.response.ResponseNextRepaymentEvaluationDateDto;
import com.wl2c.elswhereproductservice.domain.product.model.entity.EarlyRepaymentEvaluationDates;
import com.wl2c.elswhereproductservice.domain.product.repository.EarlyRepaymentEvaluationDatesRepository;
import com.wl2c.elswhereproductservice.domain.product.repository.ProductRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;

@Service
@RequiredArgsConstructor
@Slf4j
public class RepaymentEvaluationDatesService {

private final EarlyRepaymentEvaluationDatesRepository earlyRepaymentEvaluationDatesRepository;
private final ProductRepository productRepository;

public ResponseNextRepaymentEvaluationDateDto findNextRepaymentEvaluationDate(Long productId) {

List<EarlyRepaymentEvaluationDates> earlyRepaymentEvaluationDatesList = earlyRepaymentEvaluationDatesRepository.findAllEarlyRepaymentEvaluationDate(productId);
if (earlyRepaymentEvaluationDatesList.isEmpty()) {
throw new ProductEarlyRepaymentEvaluationDateFoundException();
}

LocalDate nextDate;
if (earlyRepaymentEvaluationDatesRepository.findNextEarlyRepaymentEvaluationDate(productId).isEmpty()) {
// 자동 조기 상환일이 다 끝났다면, 마지막 만기 상환 평가일을 보여주도록함
nextDate = productRepository.findOne(productId).orElseThrow(ProductNotFoundException::new).getMaturityEvaluationDate();
if (nextDate == null) {
throw new ProductMaturityEvaluationDateNotFoundException();
}

} else {
nextDate = earlyRepaymentEvaluationDatesRepository.findNextEarlyRepaymentEvaluationDate(productId).get();
}
return new ResponseNextRepaymentEvaluationDateDto(nextDate);
}
}
2 changes: 2 additions & 0 deletions src/main/resources/errors.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
notfound.product=해당 상품을 찾을 수 없습니다.
notfound.product-early-repayment-evaluation-date=해당 상품의 조기 상환 평가일을 찾을 수 없습니다.
notfound.product-maturity-repayment-evaluation-date=해당 상품의 만기 평가일을 찾을 수 없습니다.

invalid.product-sort-type=유효하지 않은 상품 목록 조회를 위한 정렬 타입입니다.

Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/errors_en_US.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
notfound.product=No such product was found.
notfound.product-early-repayment-evaluation-date=The early repayment evaluation date for the product could not be found.
notfound.product-maturity-repayment-evaluation-date=The maturity repayment evaluation date for the product could not be found.

invalid.product-sort-type=The sort type for invalid product list inquiry.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.wl2c.elswhereproductservice.domain.product.repository;

import com.wl2c.elswhereproductservice.domain.product.model.MaturityEvaluationDateType;
import com.wl2c.elswhereproductservice.domain.product.model.ProductState;
import com.wl2c.elswhereproductservice.domain.product.model.ProductType;
import com.wl2c.elswhereproductservice.domain.product.model.entity.Product;
Expand Down Expand Up @@ -34,6 +35,8 @@ void before() {
3,
LocalDate.now().minusDays(1),
LocalDate.now().plusYears(3),
LocalDate.now().plusYears(3).minusDays(5),
MaturityEvaluationDateType.SINGLE,
new BigDecimal("10.423"),
LocalDate.now().minusDays(14),
LocalDate.now().minusDays(1),
Expand All @@ -49,6 +52,8 @@ void before() {
3,
LocalDate.now().plusDays(1),
LocalDate.now().plusYears(3),
LocalDate.now().plusYears(3).minusDays(3),
MaturityEvaluationDateType.SINGLE,
new BigDecimal("15.34"),
LocalDate.now().minusDays(14),
LocalDate.now(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.wl2c.elswhereproductservice.domain.product.repository;

import com.wl2c.elswhereproductservice.domain.product.model.MaturityEvaluationDateType;
import com.wl2c.elswhereproductservice.domain.product.model.ProductState;
import com.wl2c.elswhereproductservice.domain.product.model.ProductType;
import com.wl2c.elswhereproductservice.domain.product.model.UnderlyingAssetType;
Expand Down Expand Up @@ -76,6 +77,8 @@ void before() {
3,
LocalDate.now().minusDays(1),
LocalDate.now().plusYears(3),
LocalDate.now().plusYears(3).minusDays(5),
MaturityEvaluationDateType.SINGLE,
new BigDecimal("10.423"),
LocalDate.now().minusDays(14),
LocalDate.now().minusDays(1),
Expand All @@ -91,6 +94,8 @@ void before() {
3,
LocalDate.now().plusDays(1),
LocalDate.now().plusYears(3),
LocalDate.now().plusYears(3).minusDays(3),
MaturityEvaluationDateType.SINGLE,
new BigDecimal("15.34"),
LocalDate.now().minusDays(14),
LocalDate.now(),
Expand All @@ -106,6 +111,8 @@ void before() {
3,
LocalDate.now().plusDays(1),
LocalDate.now().plusYears(3),
LocalDate.now().plusYears(3).minusDays(5),
MaturityEvaluationDateType.MULTIPLE,
new BigDecimal("11.234"),
LocalDate.now().minusDays(14),
LocalDate.now(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.wl2c.elswhereproductservice.mock;

import com.wl2c.elswhereproductservice.domain.product.model.MaturityEvaluationDateType;
import com.wl2c.elswhereproductservice.domain.product.model.ProductState;
import com.wl2c.elswhereproductservice.domain.product.model.ProductType;
import com.wl2c.elswhereproductservice.domain.product.model.entity.Product;
Expand All @@ -15,6 +16,8 @@ public static Product create(String issuer,
int equityCount,
LocalDate issuedDate,
LocalDate maturityDate,
LocalDate maturityEvaluationDate,
MaturityEvaluationDateType maturityEvaluationDateType,
BigDecimal yieldIfConditionsMet,
LocalDate subscriptionStartDate,
LocalDate subscriptionEndDate,
Expand All @@ -29,6 +32,8 @@ public static Product create(String issuer,
.equityCount(equityCount)
.issuedDate(issuedDate)
.maturityDate(maturityDate)
.maturityEvaluationDate(maturityEvaluationDate)
.maturityEvaluationDateType(maturityEvaluationDateType)
.yieldIfConditionsMet(yieldIfConditionsMet)
.subscriptionStartDate(subscriptionStartDate)
.subscriptionEndDate(subscriptionEndDate)
Expand Down