Skip to content

Commit

Permalink
✨ [Feature] 음료 상세보기 기능을 구현 (#54)
Browse files Browse the repository at this point in the history
* feat: 음료 상세 조회 기능 구현 (#53)

* feat: drink entity에 index 추가 (#53)

* chore: 웅답 dto 네이밍 변경 (#53)

* chore: dto 패지키 변경 (#53)

* chore: jpql 수정 (#53)
  • Loading branch information
soochangoforit authored Jul 10, 2023
1 parent 8796bc3 commit 253d715
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.depromeet.oversweet.drink.controller;


import com.depromeet.oversweet.drink.dto.request.DrinkInfoRequest;
import com.depromeet.oversweet.drink.dto.request.DrinkWeeklySugarDateRequest;
import com.depromeet.oversweet.drink.dto.response.DrinkDailySugarStatisticsResponse;
import com.depromeet.oversweet.drink.dto.response.DrinkDetailInfoResponse;
import com.depromeet.oversweet.drink.dto.response.DrinkWeeklySugarStatisticsResponse;
import com.depromeet.oversweet.drink.service.DrinkDailyStatisticsService;
import com.depromeet.oversweet.drink.service.DrinkDetailSearchService;
import com.depromeet.oversweet.drink.service.DrinkWeeklyStatisticsService;
import com.depromeet.oversweet.response.DataResponse;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -28,6 +31,7 @@ public class DrinkController {

private final DrinkDailyStatisticsService drinkDailyStatisticsService;
private final DrinkWeeklyStatisticsService drinkWeeklyStatisticsService;
private final DrinkDetailSearchService drinkDetailSearchService;

/**
* 유저 하루(데일리) 먹은 당 통계 및 음료 목록 조회.
Expand Down Expand Up @@ -56,4 +60,16 @@ public ResponseEntity<DataResponse<DrinkWeeklySugarStatisticsResponse>> retrieve
return ResponseEntity.ok()
.body(DataResponse.of(HttpStatus.OK, "유저가 먹은 주간 당 통계 조회 성공", response));
}

/**
* 음료 상세 조회
* 추후 로그인 기능 구현 후, 로그인한 유저의 ID를 받아와야 함 (ex. @AuthenticationPrincipal User user)
*/
@Operation(summary = "음료 상세 조회", description = "음료 상세 정보를 조회합니다.")
@ApiResponses(@ApiResponse(responseCode = "200", description = "음료 상세 조회."))
@GetMapping("/detail")
public ResponseEntity<DataResponse<DrinkDetailInfoResponse>> retrieveDrinkDetail(@RequestBody @Valid final DrinkInfoRequest request) {
DrinkDetailInfoResponse response = drinkDetailSearchService.retrieveDrinkDetail(100L, request);
return ResponseEntity.ok().body(DataResponse.of(HttpStatus.OK, "음료 상세 조회 성공", response));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.depromeet.oversweet.drink.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;

/**
* 음료 상세 정보를 보기 위한 요청 DTO
*/
@Getter
public class DrinkInfoRequest {

@Schema(description = "프랜차이즈 ID", example = "1")
@NotNull(message = "프랜차이즈 ID는 필수 값입니다.")
private Long franchiseId;

@Schema(description = "음료 이름", example = "아메리카노")
@NotEmpty(message = "음료 이름은 필수 값입니다.")
private String drinkName;


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.depromeet.oversweet.drink.dto.response;

import com.depromeet.oversweet.bookmark.dto.response.FranchiseInfo;
import com.depromeet.oversweet.domain.drink.dto.DrinkInfoWithScrapStatus;
import lombok.Getter;

import java.util.List;

/**
* 상세 음료 정보를 응답하는 DTO
*/
@Getter
public class DrinkDetailInfoResponse {

private final FranchiseInfo franchise;

private final List<DrinkInfoWithScrapStatus> drinks;

public DrinkDetailInfoResponse(FranchiseInfo franchise, List<DrinkInfoWithScrapStatus> drinks) {
this.franchise = franchise;
this.drinks = drinks;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.depromeet.oversweet.drink.service;

import com.depromeet.oversweet.bookmark.dto.response.FranchiseInfo;
import com.depromeet.oversweet.domain.drink.dto.DrinkInfoWithScrapStatus;
import com.depromeet.oversweet.domain.drink.repository.FindDrinkRepository;
import com.depromeet.oversweet.domain.franchise.entity.FranchiseEntity;
import com.depromeet.oversweet.domain.franchise.repository.FindFranchiseRepository;
import com.depromeet.oversweet.drink.dto.request.DrinkInfoRequest;
import com.depromeet.oversweet.drink.dto.response.DrinkDetailInfoResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* 음료 상세 정보를 보기 위한 서비스
*/
@Service
@RequiredArgsConstructor
public class DrinkDetailSearchService {

private final FindDrinkRepository findDrinkRepository;
private final FindFranchiseRepository findFranchiseRepository;

/**
* 음료 상세 정보 조회
*
* @param memberId API 접근자 memberId
* @param request 음료 정보를 확인하기 위해 필요한 값을 담고 있는 request dto
* @return 음료 상세 정보
*/
public DrinkDetailInfoResponse retrieveDrinkDetail(Long memberId, DrinkInfoRequest request) {
// 프랜차이즈 ID와 음료 이름으로 => 음료가 존재하는지 학인 => 없으면 exception
findDrinkRepository.checkDrinkExist(request.getFranchiseId(), request.getDrinkName());

// 프랜차이즈 응답을 위한 조회
FranchiseEntity franchise = findFranchiseRepository.findFranchiseById(request.getFranchiseId());

// 음료 정보를 조회 (음료 정보 & 사이즈별 즐겨 찾기 여부)
List<DrinkInfoWithScrapStatus> drinkDetails = findDrinkRepository.findDrinkDetail(memberId, franchise.getId(), request.getDrinkName());

return new DrinkDetailInfoResponse(new FranchiseInfo(franchise), drinkDetails);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.depromeet.oversweet.domain.drink.dto;

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

/**
* 음료 상세 정보를 보기 위한 응답 DTO
*/
@Getter
public class DrinkInfoWithScrapStatus {

@Schema(name = "음료 ID", example = "1")
private final Long id;
@Schema(name = "음료 이름", example = "아메리카노")
private final String name;
@Schema(name = "음료 이미지 URL")
private final String drinkImageUrl;
@Schema(name = "음료 사이즈", example = "355")
private final Integer size;
@Schema(name = "음료 칼로리", example = "10")
private final Integer calorie;
@Schema(name = "음료 당도", example = "10")
private final Integer sugar;
@Schema(name = "즐겨찾기 여부", example = "true")
private final Boolean scrapStatus;

public DrinkInfoWithScrapStatus(Long id, String name, String drinkImageUrl, Integer size, Integer calorie, Integer sugar, Boolean scrapStatus) {
this.id = id;
this.name = name;
this.drinkImageUrl = drinkImageUrl;
this.size = size;
this.calorie = calorie;
this.sugar = sugar;
this.scrapStatus = scrapStatus;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,17 @@
import com.depromeet.oversweet.domain.common.entity.BaseTimeEntity;
import com.depromeet.oversweet.domain.drink.enums.DrinkCategory;
import com.depromeet.oversweet.domain.franchise.entity.FranchiseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Table(name = "drink")
@Table(name = "drink",
indexes = {
@Index(name = "idx_franchise_id_and_name", columnList = "franchise_id,name")
}
)
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
package com.depromeet.oversweet.domain.drink.repository;

import com.depromeet.oversweet.domain.drink.dto.DrinkInfoWithScrapStatus;
import com.depromeet.oversweet.domain.drink.entity.DrinkEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface DrinkJpaRepository extends JpaRepository<DrinkEntity, Long> {

List<DrinkEntity> findByFranchiseIdAndName(Long franchiseId, String drinkName);

@Query("SELECT new com.depromeet.oversweet.domain.drink.dto.DrinkInfoWithScrapStatus(d.id, d.name, d.imageUrl, d.size, d.calorie, d.sugar, " +
"(CASE WHEN db IS NULL THEN false ELSE true END)) " +
"FROM DrinkEntity d " +
"LEFT JOIN DrinkBookmarkEntity db ON d.id = db.drink.id AND db.member.id = :memberId " +
"WHERE d.franchise.id = :franchiseId AND d.name = :drinkName ORDER BY d.size ASC")
List<DrinkInfoWithScrapStatus> findDrinkWithBookmarkStatus(@Param("memberId") Long memberId, @Param("franchiseId") Long franchiseId, @Param("drinkName") String drinkName);

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package com.depromeet.oversweet.domain.drink.repository;

import com.depromeet.oversweet.domain.drink.dto.DrinkInfoWithScrapStatus;
import com.depromeet.oversweet.domain.drink.entity.DrinkEntity;

import java.util.List;

/**
* 음료 정보 조회 Interface
*/
public interface FindDrinkRepository {
DrinkEntity findDrinkById(final Long drinkId);

List<DrinkInfoWithScrapStatus> findDrinkDetail(final Long memberId, final Long franchiseId, final String drinkName);

void checkDrinkExist(final Long franchiseId, final String drinkName);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.depromeet.oversweet.domain.drink.repository;

import com.depromeet.oversweet.domain.drink.dto.DrinkInfoWithScrapStatus;
import com.depromeet.oversweet.domain.drink.entity.DrinkEntity;
import com.depromeet.oversweet.exception.drink.NotFoundDrinkException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

import static com.depromeet.oversweet.exception.ErrorCode.NOT_FOUND_DRINK;

/**
Expand All @@ -29,4 +32,33 @@ public DrinkEntity findDrinkById(final Long drinkId) {
return drinkJpaRepository.findById(drinkId)
.orElseThrow(() -> new NotFoundDrinkException(NOT_FOUND_DRINK));
}

/**
* 음료 상세 정보 조회
* 프랜차이즈 정보 및 즐겨 찾기 여부를 함께 조회한다.
*
* @param franchiseId 상세 보기를 원하는 음료의 프랜차이즈 ID
* @param drinkName 상세 보기를 원하는 음료의 이름
*/
@Override
@Transactional(readOnly = true)
public List<DrinkInfoWithScrapStatus> findDrinkDetail(final Long memberId, final Long franchiseId, final String drinkName) {
return drinkJpaRepository.findDrinkWithBookmarkStatus(memberId, franchiseId, drinkName);
}

/**
* 음료가 존재하는지 확인
*
* @param franchiseId 음료의 프랜차이즈 ID
* @param drinkName 음료 이름
*/
@Override
@Transactional(readOnly = true)
public void checkDrinkExist(final Long franchiseId, final String drinkName) {
if (drinkJpaRepository.findByFranchiseIdAndName(franchiseId, drinkName)
.isEmpty()){
throw new NotFoundDrinkException(NOT_FOUND_DRINK);
}
}

}

0 comments on commit 253d715

Please sign in to comment.