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

유저 등급에 따른 포트폴리오 거래내역 조회 기능 & 포트폴리오 가격 업데이트 로직 변경 #116

Merged
merged 6 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,27 @@ public static PortfolioResponse.FindByIdDTO FindByIdDTOConvertor(Planner planner
Portfolio portfolio,
List<String> images, List<PriceItem> priceItems,
List<Match> matches, List<Quotation> quotations) {
// 가격 항목 DTO 변환
List<PortfolioResponse.PriceItemDTO> priceItemDTOS = PriceItemDTOConvertor(priceItems);

Long totalPrice = PriceCalculator.calculatePortfolioPrice(priceItemDTOS);
PortfolioResponse.PriceDTO priceDTO = new PortfolioResponse.PriceDTO(totalPrice, priceItemDTOS);

// 거래 내역
List<PortfolioResponse.PaymentDTO> paymentDTOS = PaymentDTOConvertor(matches, quotations);
PortfolioResponse.PaymentHistoryDTO paymentHistoryDTO =
new PortfolioResponse.PaymentHistoryDTO(portfolio.getAvgPrice(), portfolio.getMinPrice(),
portfolio.getMaxPrice(), paymentDTOS);
// 일반 회원의 경우 거래내역으로 null 반환
PortfolioResponse.PaymentHistoryDTO paymentHistoryDTO = null;

// 프리미엄 회원일 경우 paymentHistory 반환
if (!matches.isEmpty() && !quotations.isEmpty()) {
List<PortfolioResponse.PaymentDTO> paymentDTOS = PaymentDTOConvertor(matches, quotations);
paymentHistoryDTO =
new PortfolioResponse.PaymentHistoryDTO(
portfolio.getAvgPrice(),
portfolio.getMinPrice(),
portfolio.getMaxPrice(),
paymentDTOS
);
}


return FindByIdDTOConvertor(planner, portfolio, images, priceDTO, paymentHistoryDTO);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ public ResponseEntity<?> getPortfolios(@RequestParam @Min(-2) Long cursor,
}

@GetMapping("/{id}")
public ResponseEntity<?> getPortfolioInDetail(@PathVariable @Min(1) Long id) {
PortfolioResponse.FindByIdDTO portfolio = portfolioService.getPortfolioById(id);
public ResponseEntity<?> getPortfolioInDetail(@PathVariable @Min(1) Long id,
@AuthenticationPrincipal CustomUserDetails userDetails) {
PortfolioResponse.FindByIdDTO portfolio = portfolioService.getPortfolioById(id, userDetails.getUser().getId());
return ResponseEntity.ok().body(ApiUtils.success(portfolio));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import com.kakao.sunsuwedding.portfolio.price.PriceItem;
import com.kakao.sunsuwedding.portfolio.price.PriceItemJDBCRepository;
import com.kakao.sunsuwedding.portfolio.price.PriceItemJPARepository;
import com.kakao.sunsuwedding.user.base_user.User;
import com.kakao.sunsuwedding.user.base_user.UserJPARepository;
import com.kakao.sunsuwedding.user.constant.Grade;
import com.kakao.sunsuwedding.user.constant.Role;
import com.kakao.sunsuwedding.user.planner.Planner;
import com.kakao.sunsuwedding.user.planner.PlannerJPARepository;
Expand All @@ -43,7 +46,9 @@ public class PortfolioService {
private final MatchJPARepository matchJPARepository;
private final QuotationJPARepository quotationJPARepository;
private final PlannerJPARepository plannerJPARepository;
private final UserJPARepository userJPARepository;

@Transactional
public Pair<Portfolio, Planner> addPortfolio(PortfolioRequest.AddDTO request, Long plannerId) {
// 요청한 플래너 탐색
Planner planner = plannerJPARepository.findById(plannerId)
Expand Down Expand Up @@ -165,30 +170,40 @@ private List<Portfolio> getFilteredPortfoliosByCursor(CursorRequest request, Pag
return portfolioJPARepository.findAll(specification, pageable).getContent();
}

public PortfolioResponse.FindByIdDTO getPortfolioById(Long id) {
List<ImageItem> imageItems = imageItemJPARepository.findByPortfolioId(id);
public PortfolioResponse.FindByIdDTO getPortfolioById(Long portfolioId, Long userId) {
// 요청한 유저의 등급을 확인
User user = userJPARepository.findById(userId)
.orElseThrow(() -> new NotFoundException(BaseException.USER_NOT_FOUND));
Grade userGrade = user.getGrade();

List<ImageItem> imageItems = imageItemJPARepository.findByPortfolioId(portfolioId);
if (imageItems.isEmpty()) {
throw new NotFoundException(BaseException.PORTFOLIO_NOT_FOUND);
}

Portfolio portfolio = imageItems.get(0).getPortfolio();
Planner planner = imageItems.get(0).getPortfolio().getPlanner();


// 플래너 탈퇴 시 조회 X
if (planner == null) {
throw new NotFoundException(BaseException.PLANNER_NOT_FOUND);
}
if (planner == null) { throw new NotFoundException(BaseException.PLANNER_NOT_FOUND); }

List<String> images = imageItems
.stream()
.map(ImageEncoder::encode)
.toList();
List<PriceItem> priceItems = priceItemJPARepository.findAllByPortfolioId(portfolioId);

List<PriceItem> priceItems = priceItemJPARepository.findAllByPortfolioId(id);
// 기본적으로 매칭 내역과 견적서에는 빈 배열 할당
List<Match> matches = new ArrayList<>();
List<Quotation> quotations = new ArrayList<>();

// 거래 내역 조회를 위한 매칭 내역, 견적서 가져오기
List<Match> matches = matchJPARepository.findLatestTenByPlanner(planner);
List<Long> matchIds = matches.stream().map(Match::getId).toList();
List<Quotation> quotations = quotationJPARepository.findAllByMatchIds(matchIds);
// 프리미엄 등급 유저일 경우 최근 거래 내역 조회를 위한 매칭 내역, 견적서 가져오기
if (userGrade == Grade.PREMIUM) {
matches = matchJPARepository.findLatestTenByPlanner(planner);
List<Long> matchIds = matches.stream().map(Match::getId).toList();
quotations = quotationJPARepository.findAllByMatchIds(matchIds);
}

return PortfolioDTOConverter.FindByIdDTOConvertor(planner, portfolio, images, priceItems, matches, quotations);
}
Expand All @@ -208,15 +223,15 @@ public Pair<Portfolio,Planner> updatePortfolio(PortfolioRequest.UpdateDTO reques
.mapToLong(PortfolioRequest.UpdateDTO.ItemDTO::getItemPrice)
.sum();

// 불변 객체 패턴을 고려한 포트폴리오 변경사항 업데이트
// 포트폴리오 변경사항 업데이트 객체 생성 (업데이트 쿼리가 마지막 함수 종료될 때 날아가긴 함)
Portfolio updatedPortfolio = Portfolio.builder()
.id(portfolio.getId())
.planner(planner)
.title(request.getTitle() != null ? request.getTitle() : portfolio.getTitle())
.description(request.getDescription() != null ? request.getDescription() : portfolio.getDescription())
.location(request.getLocation() != null ? request.getLocation() : portfolio.getLocation())
.career(request.getCareer() != null ? request.getCareer() : portfolio.getCareer())
.partnerCompany(request.getPartnerCompany() != null ? request.getPartnerCompany() : portfolio.getPartnerCompany())
.title(request.getTitle())
.description(request.getDescription())
.location(request.getLocation())
.career(request.getCareer())
.partnerCompany(request.getPartnerCompany())
.totalPrice(totalPrice)
.contractCount(portfolio.getContractCount())
.avgPrice(portfolio.getAvgPrice())
Expand All @@ -225,29 +240,30 @@ public Pair<Portfolio,Planner> updatePortfolio(PortfolioRequest.UpdateDTO reques
.build();
portfolioJPARepository.save(updatedPortfolio);

// 해당하는 가격 아이템 탐색 & 업데이트
List<PriceItem> existPriceItems = priceItemJPARepository.findByPortfolioId(portfolio.getId());
List<PriceItem> updatedPriceItems = new ArrayList<>();
for (int i = 0; i < 3; i++) {
PriceItem priceItem = existPriceItems.get(i);
PortfolioRequest.UpdateDTO.ItemDTO item = request.getItems().get(i);
// 기존의 포트폴리오 가격 항목 일괄 삭제
// 특이사항: JPQL 안 쓰니까 DELETE Query N개씩 날아감
priceItemJPARepository.deleteAllByPortfolioId(portfolio.getId());

PriceItem updatedPriceItem = PriceItem.builder()
.id(priceItem.getId())
// 업데이트 가격 항목 새로 저장
List<PriceItem> updatedPriceItems = new ArrayList<>();
for (PortfolioRequest.UpdateDTO.ItemDTO item : request.getItems()) {
PriceItem priceItem = PriceItem.builder()
.portfolio(portfolio)
.itemTitle(item.getItemTitle() != null ? item.getItemTitle() : priceItem.getItemTitle())
.itemPrice(item.getItemPrice() != null ? item.getItemPrice() : priceItem.getItemPrice())
.itemTitle(item.getItemTitle())
.itemPrice(item.getItemPrice())
.build();
updatedPriceItems.add(updatedPriceItem);
updatedPriceItems.add(priceItem);
}
priceItemJDBCRepository.batchUpdatePriceItems(updatedPriceItems);
priceItemJDBCRepository.batchInsertPriceItems(updatedPriceItems);

// 삭제 후 삽입 로직으로 변경 ㅠㅠ 열심히 만든건데 못쓰게 되버림 엉엉
// priceItemJDBCRepository.batchUpdatePriceItems(updatedPriceItems);

// 이미지 처리 로직에 활용하기 위해 포트폴리오 객체 리턴
return Pair.of(updatedPortfolio, planner);

}

@Transactional
public void updateConfirmedPrices(Planner planner) {
List<Match> matches = matchJPARepository.findAllByPlanner(planner);
Optional<Portfolio> portfolioPS = portfolioJPARepository.findByPlanner(planner);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
Expand All @@ -16,5 +17,10 @@ public interface PriceItemJPARepository extends JpaRepository<PriceItem, Long> {
@EntityGraph("PriceItemWithPortfolioAndPlanner")
List<PriceItem> findAllByPortfolioId(Long id);


void deleteAllByPortfolioPlannerId(Long id);

@Modifying
@Query("delete from PriceItem p where p.portfolio.id = :portfolioId")
void deleteAllByPortfolioId(@Param("portfolioId") Long portfolioId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,10 @@ public void get_portfolios_success_test_last_page() throws Exception {


// ============ 포트폴리오 상세 조회 테스트 ============
@DisplayName("포트폴리오 상세 조회 성공 테스트")
@DisplayName("포트폴리오 상세 조회 성공 테스트 - 예비부부 (PREMIUM 등급)")
@Test
public void get_portfolio_by_id_success_test() throws Exception {
@WithUserDetails("[email protected]")
public void get_portfolio_by_id_premium_success_test() throws Exception {
//given
Long id = 1L;
// when
Expand All @@ -252,8 +253,33 @@ public void get_portfolio_by_id_success_test() throws Exception {
result.andExpect(MockMvcResultMatchers.jsonPath("$.response.paymentsHistory.payments[0].confirmedAt").value("2023-10"));
}

@DisplayName("포트폴리오 상세 조회 성공 테스트 - 플래너 (NORMAL 등급)")
@Test
@WithUserDetails("[email protected]")
public void get_portfolio_by_id_normal_success_test() throws Exception {
//given
Long id = 1L;
// when
ResultActions result = mockMvc.perform(
MockMvcRequestBuilders
.get("/portfolios/{id}", id)
);

String responseBody = result.andReturn().getResponse().getContentAsString();
logger.debug("테스트 : " + responseBody);

// then
result.andExpect(MockMvcResultMatchers.jsonPath("$.success").value("true"));
result.andExpect(MockMvcResultMatchers.jsonPath("$.response.id").value(1));
result.andExpect(MockMvcResultMatchers.jsonPath("$.response.userId").value(2));
result.andExpect(MockMvcResultMatchers.jsonPath("$.response.priceInfo.items[0].itemTitle").value("스튜디오1"));
result.andExpect(MockMvcResultMatchers.jsonPath("$.response.priceInfo.items[0].itemPrice").value(500000));
result.andExpect(MockMvcResultMatchers.jsonPath("$.response.paymentsHistory").isEmpty());
}

@DisplayName("포트폴리오 상세 조회 실패 테스트 1 - 존재하지 않는 포트폴리오")
@Test
@WithUserDetails("[email protected]")
public void get_portfolio_by_id_fail_test_portfolio_not_found() throws Exception {
//given
Long id = 30L;
Expand All @@ -274,6 +300,7 @@ public void get_portfolio_by_id_fail_test_portfolio_not_found() throws Exception

@DisplayName("포트폴리오 상세 조회 실패 테스트 2 - 탈퇴한 플래너")
@Test
@WithUserDetails("[email protected]")
public void get_portfolio_by_id_fail_test_planner_not_found() throws Exception {
//given
Long id = 15L; // 탈퇴한 플래너의 포트폴리오 id
Expand Down
Loading