Skip to content
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
2 changes: 1 addition & 1 deletion bottlenote-admin-api/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.0
1.1.1
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ class AdminAlcoholsController(
): ResponseEntity<*> = GlobalResponse.ok(alcoholQueryService.findAdminAlcoholDetailById(alcoholId))

@GetMapping("/categories/reference")
fun getCategoryReference(): ResponseEntity<*> {
val pairs = alcoholQueryService.findAllCategoryPairs()
val response = pairs.map { mapOf("korCategory" to it.left, "engCategory" to it.right) }
return GlobalResponse.ok(response)
}
fun getCategoryReference(): ResponseEntity<*> = GlobalResponse.ok(alcoholQueryService.findAllCategoryReferenceMap())

@PostMapping
fun createAlcohol(
Expand Down
4 changes: 4 additions & 0 deletions bottlenote-admin-api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ server:
context-path: /admin/api/v1
encoding:
charset: UTF-8
compression:
enabled: true
mime-types: application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css
min-response-size: 1024
tomcat:
threads:
max: 15
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import app.bottlenote.alcohols.constant.AdminAlcoholSortType
import app.bottlenote.alcohols.constant.AlcoholCategoryGroup
import app.bottlenote.alcohols.dto.request.AdminAlcoholSearchRequest
import app.bottlenote.alcohols.dto.request.AdminAlcoholUpsertRequest
import app.bottlenote.alcohols.dto.response.CategoryPairItem
import app.bottlenote.alcohols.presentation.AdminAlcoholsController
import app.bottlenote.alcohols.service.AdminAlcoholCommandService
import app.bottlenote.alcohols.service.AlcoholQueryService
import app.bottlenote.global.dto.response.AdminResultResponse
import app.bottlenote.global.service.cursor.SortOrder
import app.helper.alcohols.AlcoholsHelper
import com.fasterxml.jackson.databind.ObjectMapper
import org.apache.commons.lang3.tuple.Pair
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -328,15 +328,29 @@ class AdminAlcoholsControllerDocsTest {
@Test
@DisplayName("카테고리 레퍼런스를 조회할 수 있다")
fun getCategoryReference() {
// given
val categoryPairs = listOf(
Pair.of("싱글 몰트", "Single Malt"),
Pair.of("블렌디드", "Blended"),
Pair.of("버번", "Bourbon")
// given — categoryGroup을 키로 갖는 grouped Map. enum 선언 순서로 LinkedHashMap/EnumMap에 채움
val grouped = linkedMapOf(
AlcoholCategoryGroup.SINGLE_MALT to listOf(
CategoryPairItem("싱글 몰트", "Single Malt")
),
AlcoholCategoryGroup.BLEND to listOf(
CategoryPairItem("블렌디드", "Blend")
),
AlcoholCategoryGroup.BLENDED_MALT to listOf(
CategoryPairItem("블렌디드 몰트", "Blended Malt")
),
AlcoholCategoryGroup.BOURBON to listOf(
CategoryPairItem("버번", "Bourbon")
),
AlcoholCategoryGroup.RYE to emptyList(),
AlcoholCategoryGroup.OTHER to listOf(
CategoryPairItem("테네시", "Tennessee"),
CategoryPairItem("콘", "Corn")
)
)

given(alcoholQueryService.findAllCategoryPairs())
.willReturn(categoryPairs)
given(alcoholQueryService.findAllCategoryReferenceMap())
.willReturn(grouped)

// when & then
assertThat(
Expand All @@ -351,9 +365,23 @@ class AdminAlcoholsControllerDocsTest {
responseFields(
fieldWithPath("success").type(JsonFieldType.BOOLEAN).description("응답 성공 여부"),
fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답 코드"),
fieldWithPath("data").type(JsonFieldType.ARRAY).description("카테고리 페어 목록"),
fieldWithPath("data[].korCategory").type(JsonFieldType.STRING).description("한글 카테고리"),
fieldWithPath("data[].engCategory").type(JsonFieldType.STRING).description("영문 카테고리"),
fieldWithPath("data").type(JsonFieldType.OBJECT).description("categoryGroup별 카테고리 목록 (SINGLE_MALT, BLEND, BLENDED_MALT, BOURBON, RYE, OTHER)"),
fieldWithPath("data.SINGLE_MALT").type(JsonFieldType.ARRAY).description("싱글 몰트 그룹 카테고리 목록"),
fieldWithPath("data.SINGLE_MALT[].korCategory").type(JsonFieldType.STRING).description("한글 카테고리").optional(),
fieldWithPath("data.SINGLE_MALT[].engCategory").type(JsonFieldType.STRING).description("영문 카테고리").optional(),
fieldWithPath("data.BLEND").type(JsonFieldType.ARRAY).description("블렌디드 그룹 카테고리 목록"),
fieldWithPath("data.BLEND[].korCategory").type(JsonFieldType.STRING).description("한글 카테고리").optional(),
fieldWithPath("data.BLEND[].engCategory").type(JsonFieldType.STRING).description("영문 카테고리").optional(),
fieldWithPath("data.BLENDED_MALT").type(JsonFieldType.ARRAY).description("블렌디드 몰트 그룹 카테고리 목록"),
fieldWithPath("data.BLENDED_MALT[].korCategory").type(JsonFieldType.STRING).description("한글 카테고리").optional(),
fieldWithPath("data.BLENDED_MALT[].engCategory").type(JsonFieldType.STRING).description("영문 카테고리").optional(),
fieldWithPath("data.BOURBON").type(JsonFieldType.ARRAY).description("버번 그룹 카테고리 목록"),
fieldWithPath("data.BOURBON[].korCategory").type(JsonFieldType.STRING).description("한글 카테고리").optional(),
fieldWithPath("data.BOURBON[].engCategory").type(JsonFieldType.STRING).description("영문 카테고리").optional(),
fieldWithPath("data.RYE").type(JsonFieldType.ARRAY).description("라이 그룹 카테고리 목록 (데이터 없으면 빈 배열)"),
fieldWithPath("data.OTHER").type(JsonFieldType.ARRAY).description("기타 그룹 카테고리 목록"),
fieldWithPath("data.OTHER[].korCategory").type(JsonFieldType.STRING).description("한글 카테고리").optional(),
fieldWithPath("data.OTHER[].engCategory").type(JsonFieldType.STRING).description("영문 카테고리").optional(),
fieldWithPath("errors").type(JsonFieldType.ARRAY).description("에러 목록"),
fieldWithPath("meta").type(JsonFieldType.OBJECT).description("메타 정보"),
fieldWithPath("meta.serverVersion").type(JsonFieldType.STRING).description("서버 버전").ignored(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class AdminDistilleryControllerDocsTest {
fieldWithPath("data[].logoImgUrl").type(JsonFieldType.STRING).description("로고 이미지 URL"),
fieldWithPath("data[].createdAt").type(JsonFieldType.STRING).description("생성일시"),
fieldWithPath("data[].modifiedAt").type(JsonFieldType.STRING).description("수정일시"),
fieldWithPath("data[].sortOrder").type(JsonFieldType.NUMBER).description("정렬 순서 (미설정: 9999)"),
fieldWithPath("errors").type(JsonFieldType.ARRAY).description("에러 목록"),
fieldWithPath("meta").type(JsonFieldType.OBJECT).description("메타 정보"),
fieldWithPath("meta.page").type(JsonFieldType.NUMBER).description("현재 페이지 번호"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class AdminRegionControllerDocsTest {
fieldWithPath("data[].createdAt").type(JsonFieldType.STRING).description("생성일시"),
fieldWithPath("data[].modifiedAt").type(JsonFieldType.STRING).description("수정일시"),
fieldWithPath("data[].parentId").type(JsonFieldType.NUMBER).description("상위 지역 ID").optional(),
fieldWithPath("data[].sortOrder").type(JsonFieldType.NUMBER).description("정렬 순서 (미설정: 9999)"),
fieldWithPath("errors").type(JsonFieldType.ARRAY).description("에러 목록"),
fieldWithPath("meta").type(JsonFieldType.OBJECT).description("메타 정보"),
fieldWithPath("meta.page").type(JsonFieldType.NUMBER).description("현재 페이지 번호"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ package app.helper.alcohols

import app.bottlenote.alcohols.constant.AlcoholCategoryGroup
import app.bottlenote.alcohols.constant.AlcoholType
import app.bottlenote.alcohols.dto.response.AdminAlcoholDetailResponse
import app.bottlenote.alcohols.dto.response.*
import app.bottlenote.alcohols.dto.response.AdminAlcoholDetailResponse.TastingTagInfo
import app.bottlenote.alcohols.dto.response.AdminAlcoholItem
import app.bottlenote.alcohols.dto.response.AdminDistilleryItem
import app.bottlenote.alcohols.dto.response.AdminRegionItem
import app.bottlenote.alcohols.dto.response.TastingTagNodeItem
import app.bottlenote.global.data.response.GlobalResponse
import app.bottlenote.global.dto.response.AdminResultResponse
import java.time.LocalDateTime
Expand Down Expand Up @@ -151,7 +147,8 @@ object AlcoholsHelper {
"지역 설명 $i",
LocalDateTime.of(2024, 1, i, 0, 0),
LocalDateTime.of(2024, 6, i, 0, 0),
null
null,
9999
)
}

Expand All @@ -162,7 +159,8 @@ object AlcoholsHelper {
listOf("Glenfiddich", "Macallan", "Yamazaki")[i - 1],
"https://example.com/logo$i.png",
LocalDateTime.of(2024, 1, i, 0, 0),
LocalDateTime.of(2024, 6, i, 0, 0)
LocalDateTime.of(2024, 6, i, 0, 0),
9999
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,19 +256,19 @@ class AdminAlcoholsIntegrationTest : IntegrationTestSupport() {
@Test
@DisplayName("동일 한글 카테고리에 다른 영문 카테고리가 별도로 조회된다")
fun differentEngCategoriesAreSeparate() {
// given - 같은 한글 카테고리, 다른 영문 카테고리
// given - 같은 한글 카테고리, 다른 영문 카테고리 (둘 다 SINGLE_MALT 그룹)
alcoholTestFactory.persistAlcoholWithCategory("싱글 몰트", "Single Malt")
alcoholTestFactory.persistAlcoholWithCategory("싱글 몰트", "-")

// when & then - 2개의 다른 페어가 반환됨
// when & then - 그룹된 응답에서 SINGLE_MALT 그룹에 2개의 다른 페어가 반환됨
assertThat(
mockMvcTester
.get()
.uri("/alcohols/categories/reference")
.header("Authorization", "Bearer $accessToken")
).hasStatusOk()
.bodyJson()
.extractingPath("$.data.length()")
.extractingPath("$.data.SINGLE_MALT.length()")
.isEqualTo(2)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@ package app.integration.alcohols
import app.IntegrationTestSupport
import app.bottlenote.alcohols.fixture.AlcoholTestFactory
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.*
import org.springframework.beans.factory.annotation.Autowired

@Tag("admin_integration")
Expand Down Expand Up @@ -113,6 +109,24 @@ class AdminReferenceDataIntegrationTest : IntegrationTestSupport() {
mockMvcTester.get().uri("/regions")
).hasStatus4xxClientError()
}

@Test
@DisplayName("지역 응답에 sortOrder 필드가 포함되며 미설정 시 9999가 반환된다")
fun getRegionsResponseIncludesSortOrderDefault() {
// given - 시드는 sort_order 미지정으로 INSERT, DB default 9999 적용
alcoholTestFactory.persistAlcohols(1)

// when & then
assertThat(
mockMvcTester
.get()
.uri("/regions?page=0&size=10")
.header("Authorization", "Bearer $accessToken")
).hasStatusOk()
.bodyJson()
.extractingPath("$.data[0].sortOrder")
.isEqualTo(9999)
}
}

@Nested
Expand Down Expand Up @@ -160,5 +174,23 @@ class AdminReferenceDataIntegrationTest : IntegrationTestSupport() {
mockMvcTester.get().uri("/distilleries")
).hasStatus4xxClientError()
}

@Test
@DisplayName("증류소 응답에 sortOrder 필드가 포함되며 미설정 시 9999가 반환된다")
fun getDistilleriesResponseIncludesSortOrderDefault() {
// given - 시드는 sort_order 미지정으로 INSERT, DB default 9999 적용
alcoholTestFactory.persistAlcohols(1)

// when & then
assertThat(
mockMvcTester
.get()
.uri("/distilleries?page=0&size=20")
.header("Authorization", "Bearer $accessToken")
).hasStatusOk()
.bodyJson()
.extractingPath("$.data[0].sortOrder")
.isEqualTo(9999)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import app.bottlenote.global.service.cursor.PageResponse;
import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.data.domain.Page;

/** 알코올 조회 질의에 관한 애그리거트를 정의합니다. */
Expand All @@ -37,7 +36,7 @@ public interface AlcoholQueryRepository {

List<CategoryItem> findAllCategories(AlcoholType type);

List<Pair<String, String>> findAllCategoryPairs();
List<CategoryItem> findAllCategoryItems();

Boolean existsByAlcoholId(Long alcoholId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Comment;

import java.util.ArrayList;
import java.util.List;

@Getter
@Builder
@AllArgsConstructor
Expand All @@ -43,6 +44,11 @@ public class Distillery extends BaseEntity {
@Column(name = "logo_img_url")
private String logoImgPath;

@Builder.Default
@Comment("정렬 순서 (미설정: 9999)")
@Column(name = "sort_order", nullable = false)
private Integer sortOrder = 9999;

@Builder.Default
@OneToMany(mappedBy = "distillery")
private List<Alcohol> alcohol = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ public class Region extends BaseEntity {
@Column(name = "description", nullable = true)
private String description;

@Builder.Default
@Comment("정렬 순서 (미설정: 9999)")
@Column(name = "sort_order", nullable = false)
private Integer sortOrder = 9999;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
@Comment("상위 지역")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public record AdminDistilleryItem(
String engName,
String logoImgUrl,
LocalDateTime createdAt,
LocalDateTime modifiedAt) {}
LocalDateTime modifiedAt,
Integer sortOrder) {}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public record AdminRegionItem(
String description,
LocalDateTime createdAt,
LocalDateTime modifiedAt,
Long parentId) {}
Long parentId,
Integer sortOrder) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package app.bottlenote.alcohols.dto.response;

public record CategoryPairItem(String korCategory, String engCategory) {}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ public class RegionsItem {
private final String engName;
private final String description;
private final Long parentId;
private final Integer sortOrder;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
import app.bottlenote.alcohols.dto.response.AdminAlcoholItem;
import app.bottlenote.alcohols.dto.response.AlcoholDetailItem;
import app.bottlenote.alcohols.dto.response.AlcoholSearchResponse;
import app.bottlenote.alcohols.dto.response.CategoryItem;
import app.bottlenote.alcohols.facade.payload.AlcoholSummaryItem;
import app.bottlenote.global.service.cursor.CursorResponse;
import app.bottlenote.global.service.cursor.PageResponse;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.data.domain.Page;

public interface CustomAlcoholQueryRepository {

List<Pair<String, String>> findAllCategoryPairs();
List<CategoryItem> findAllCategoryItems();

PageResponse<AlcoholSearchResponse> searchAlcohols(AlcoholSearchCriteria criteriaDto);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import app.bottlenote.alcohols.dto.response.AlcoholDetailItem;
import app.bottlenote.alcohols.dto.response.AlcoholSearchResponse;
import app.bottlenote.alcohols.dto.response.AlcoholsSearchItem;
import app.bottlenote.alcohols.dto.response.CategoryItem;
import app.bottlenote.alcohols.facade.payload.AlcoholSummaryItem;
import app.bottlenote.global.service.cursor.CursorPageable;
import app.bottlenote.global.service.cursor.CursorResponse;
Expand All @@ -30,7 +31,6 @@
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
Expand All @@ -40,18 +40,20 @@ public class CustomAlcoholQueryRepositoryImpl implements CustomAlcoholQueryRepos
private final JPAQueryFactory queryFactory;
private final AlcoholQuerySupporter supporter;

/** 모든 카테고리 페어(한글, 영문) 조회 */
/** 모든 카테고리(한글, 영문, 그룹) 조회 — 카테고리 레퍼런스 응답용 */
@Override
public List<Pair<String, String>> findAllCategoryPairs() {
public List<CategoryItem> findAllCategoryItems() {
return queryFactory
.select(alcohol.korCategory, alcohol.engCategory)
.select(
Projections.constructor(
CategoryItem.class,
alcohol.korCategory,
alcohol.engCategory,
alcohol.categoryGroup))
.from(alcohol)
.groupBy(alcohol.korCategory, alcohol.engCategory)
.groupBy(alcohol.korCategory, alcohol.engCategory, alcohol.categoryGroup)
.orderBy(alcohol.korCategory.asc())
.fetch()
.stream()
.map(tuple -> Pair.of(tuple.get(alcohol.korCategory), tuple.get(alcohol.engCategory)))
.toList();
.fetch();
}

/** queryDSL 알코올 검색 */
Expand Down
Loading
Loading