Skip to content
Open
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 @@ -53,6 +53,7 @@ public List<CategoryItem> findAllCategoryItems() {
alcohol.engCategory,
alcohol.categoryGroup))
.from(alcohol)
.where(alcohol.type.eq(WHISKY), supporter.isNotDeleted())
.groupBy(alcohol.korCategory, alcohol.engCategory, alcohol.categoryGroup)
.orderBy(alcohol.korCategory.asc())
.fetch();
Expand Down Expand Up @@ -130,6 +131,7 @@ public PageResponse<AlcoholSearchResponse> searchAlcohols(AlcoholSearchCriteria
.leftJoin(review)
.on(alcohol.id.eq(review.alcoholId))
.where(
supporter.isNotDeleted(),
supporter.keywordMatch(criteriaDto.keyword()),
supporter.eqCurationId(criteriaDto.curationId()),
supporter.eqCategory(criteriaDto.category()),
Expand All @@ -153,6 +155,7 @@ public PageResponse<AlcoholSearchResponse> searchAlcohols(AlcoholSearchCriteria
.select(alcohol.id.count())
.from(alcohol)
.where(
supporter.isNotDeleted(),
supporter.keywordMatch(criteriaDto.keyword()),
supporter.eqCurationId(criteriaDto.curationId()),
supporter.eqCategory(criteriaDto.category()),
Expand Down Expand Up @@ -214,7 +217,7 @@ public AlcoholDetailItem findAlcoholDetailById(Long alcoholId, Long userId) {
.on(alcohol.region.id.eq(region.id))
.join(distillery)
.on(alcohol.distillery.id.eq(distillery.id))
.where(alcohol.id.eq(alcoholId))
.where(alcohol.id.eq(alcoholId), supporter.isNotDeleted())
.groupBy(
alcohol.id,
alcohol.imageUrl,
Expand Down Expand Up @@ -254,7 +257,7 @@ public Optional<AlcoholSummaryItem> findAlcoholInfoById(Long alcoholId, Long use
.on(alcohol.region.id.eq(region.id))
.join(distillery)
.on(alcohol.distillery.id.eq(distillery.id))
.where(alcohol.id.eq(alcoholId))
.where(alcohol.id.eq(alcoholId), supporter.isNotDeleted())
.groupBy(
alcohol.id,
alcohol.korCategory,
Expand Down Expand Up @@ -336,7 +339,7 @@ public CursorResponse<AlcoholDetailItem> getStandardExplore(ExploreStandardCrite
.on(alcohol.region.id.eq(region.id))
.join(distillery)
.on(alcohol.distillery.id.eq(distillery.id))
.where(alcohol.id.in(candidateIds))
.where(alcohol.id.in(candidateIds), supporter.isNotDeleted())
.groupBy(
alcohol.id,
alcohol.imageUrl,
Expand Down Expand Up @@ -395,7 +398,8 @@ private List<Long> fetchCandidateIds(ExploreStandardCriteria criteria, int fetch
supporter.eqCategory(criteria.category()),
supporter.inRegionIds(criteria.regionIds()),
supporter.inDistilleryIds(criteria.distilleryIds()),
supporter.eqCurationId(criteria.curationId()));
supporter.eqCurationId(criteria.curationId()),
supporter.isNotDeleted());

if (sortType == SearchSortType.RANDOM) {
// seed 는 Service 가 null 방어/생성 후 주입. id.asc() tiebreaker 로 동일 RAND 값 충돌 시에도 결정론적 순서 보장.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ public interface JpaAlcoholQueryRepository
Select new app.bottlenote.alcohols.dto.response.CategoryItem(a.korCategory, a.engCategory,a.categoryGroup)
from alcohol a
where a.type = :type
and a.deletedAt is null
group by a.korCategory, a.engCategory,a.categoryGroup
order by
case when a.categoryGroup = app.bottlenote.alcohols.constant.AlcoholCategoryGroup.OTHER then 1 else 0 end,a.korCategory
""")
List<CategoryItem> findAllCategories(AlcoholType type);

@Query("SELECT COUNT(a) > 0 FROM alcohol a WHERE a.id = :alcoholId")
@Query("SELECT COUNT(a) > 0 FROM alcohol a WHERE a.id = :alcoholId AND a.deletedAt IS NULL")
Boolean existsByAlcoholId(@Param("alcoholId") Long alcoholId);

@Query("SELECT COUNT(a) > 0 FROM alcohol a WHERE a.distillery.id = :distilleryId")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package app.bottlenote.alcohols.service;

import static app.bottlenote.alcohols.exception.AlcoholExceptionCode.ALCOHOL_NOT_FOUND;

import app.bottlenote.alcohols.constant.AlcoholCategoryGroup;
import app.bottlenote.alcohols.constant.SearchSortType;
import app.bottlenote.alcohols.domain.Alcohol;
Expand All @@ -19,7 +21,6 @@
import app.bottlenote.alcohols.dto.response.ExploreStandardResponse;
import app.bottlenote.alcohols.dto.response.FriendsDetailResponse;
import app.bottlenote.alcohols.exception.AlcoholException;
import app.bottlenote.alcohols.exception.AlcoholExceptionCode;
import app.bottlenote.alcohols.repository.CustomAlcoholQueryRepository.AdminAlcoholDetailProjection;
import app.bottlenote.global.data.response.GlobalResponse;
import app.bottlenote.global.service.cursor.CursorResponse;
Expand All @@ -32,6 +33,7 @@
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -73,16 +75,17 @@ public PageResponse<AlcoholSearchResponse> searchAlcohols(
*/
@Transactional(readOnly = true)
public AlcoholDetailResponse findAlcoholDetailById(Long alcoholId, Long userId) {
AlcoholDetailItem alcoholDetail =
alcoholQueryRepository.findAlcoholDetailById(alcoholId, userId);
AlcoholDetailItem alcoholDetailItem =
Optional.ofNullable(alcoholQueryRepository.findAlcoholDetailById(alcoholId, userId))
.orElseThrow(() -> new AlcoholException(ALCOHOL_NOT_FOUND));

// 조회 기록 저장 (게스트 사용자 제외)
if (userId > 0 && alcoholDetail != null) viewHistoryService.recordView(userId, alcoholDetail);
if (userId > 0) viewHistoryService.recordView(userId, alcoholDetailItem);

FriendsDetailResponse friendInfos = getFriendInfos(alcoholId, userId);

return AlcoholDetailResponse.builder()
.alcohols(alcoholDetail)
.alcohols(alcoholDetailItem)
.friendsInfo(friendInfos)
.reviewInfo(reviewFacade.getReviewInfoList(alcoholId, userId))
.build();
Expand Down Expand Up @@ -146,12 +149,12 @@ public AdminAlcoholDetailResponse findAdminAlcoholDetailById(Long alcoholId) {
AdminAlcoholDetailProjection projection =
alcoholQueryRepository
.findAdminAlcoholDetailById(alcoholId)
.orElseThrow(() -> new AlcoholException(AlcoholExceptionCode.ALCOHOL_NOT_FOUND));
.orElseThrow(() -> new AlcoholException(ALCOHOL_NOT_FOUND));

Alcohol alcohol =
alcoholQueryRepository
.findById(alcoholId)
.orElseThrow(() -> new AlcoholException(AlcoholExceptionCode.ALCOHOL_NOT_FOUND));
.orElseThrow(() -> new AlcoholException(ALCOHOL_NOT_FOUND));

List<TastingTagInfo> tastingTags =
alcohol.getAlcoholsTastingTags().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,25 @@ void explore_empty_result() {
result.assertThat().bodyJson().extractingPath("$.meta.pageable.hasNext").isEqualTo(false);
}

@Test
@DisplayName("삭제 처리된 알코올은 Product 둘러보기에서 제외된다")
void explore_excludes_deleted_alcohol() {
Alcohol visible = alcoholTestFactory.persistAlcoholWithName("둘러보기 노출", "Explore Visible");
Alcohol deleted = alcoholTestFactory.persistAlcoholWithName("둘러보기 삭제", "Explore Deleted");
deleted.delete();

MvcTestResult result = exchangeGet(b -> b.param("keywords", "둘러보기").param("size", "10"));

result
.assertThat()
.hasStatusOk()
.bodyJson()
.extractingPath("$.data.items[*].alcoholId")
.asArray()
.contains(visible.getId().intValue())
.doesNotContain(deleted.getId().intValue());
}

@Test
@DisplayName("응답 item에 reviewCount, pickCount 필드가 포함된다")
void explore_response_includes_count_fields() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,35 @@ void test_1() throws Exception {
assertEquals(alcohols.size(), alcoholSearchResponse.getTotalCount());
}

@Test
@DisplayName("삭제 처리된 알코올은 Product 목록 검색에서 제외된다.")
void product_search_excludes_deleted_alcohol() throws Exception {
Alcohol visible = alcoholTestFactory.persistAlcoholWithName("노출 위스키", "Visible Whisky");
Alcohol deleted = alcoholTestFactory.persistAlcoholWithName("삭제 위스키", "Deleted Whisky");
deleted.delete();
em.flush();
em.clear();

MvcTestResult result =
mockMvcTester
.get()
.uri("/api/v1/alcohols/search")
.param("keyword", "위스키")
.contentType(APPLICATION_JSON)
.header("Authorization", "Bearer " + getToken())
.with(csrf())
.exchange();

AlcoholSearchResponse response = extractData(result, AlcoholSearchResponse.class);
Set<Long> resultIds =
response.getAlcohols().stream()
.map(AlcoholsSearchItem::getAlcoholId)
.collect(java.util.stream.Collectors.toSet());

assertTrue(resultIds.contains(visible.getId()));
assertFalse(resultIds.contains(deleted.getId()));
}

@Test
@DisplayName("키워드를 알코올 목록조회를 할 수 있다.")
void test_1_1() throws Exception {
Expand Down Expand Up @@ -126,6 +155,26 @@ void test_2() throws Exception {
assertNotNull(alcoholDetail.friendsInfo());
}

@Test
@DisplayName("삭제 처리된 알코올은 Product 상세 조회에서 찾을 수 없다.")
void product_detail_excludes_deleted_alcohol() {
Alcohol alcohol = alcoholTestFactory.persistAlcohol();
alcohol.delete();
em.flush();
em.clear();

MvcTestResult result =
mockMvcTester
.get()
.uri("/api/v1/alcohols/{alcoholId}", alcohol.getId())
.contentType(APPLICATION_JSON)
.header("Authorization", "Bearer " + getToken())
.with(csrf())
.exchange();

result.assertThat().hasStatus(org.springframework.http.HttpStatus.NOT_FOUND);
}

@Test
@DisplayName("알코올 상세 조회 시 해당 알콤올을 마셔본 팔로잉 유저의 정보를 조회할 수 있다.")
void test_3() throws Exception {
Expand Down
Loading