diff --git a/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/Category.kt b/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/Category.kt index 8385c5c..35d25a4 100644 --- a/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/Category.kt +++ b/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/Category.kt @@ -4,5 +4,6 @@ enum class Category { EAT, ATTRACTION, SHOPPING, - TRANSPORTATION, + TRANSPORTATION_HUB, + TRANSPORTATION_TRANSIT, } diff --git a/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/PlaceStatus.kt b/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/PlaceStatus.kt index 94dcfc5..d1b5c08 100644 --- a/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/PlaceStatus.kt +++ b/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/PlaceStatus.kt @@ -11,7 +11,7 @@ enum class PlaceStatus { companion object { fun from(item: TravelItineraryItem): PlaceStatus = when { - item.category == Category.TRANSPORTATION -> NOT_REQUIRED + item.category == Category.TRANSPORTATION_TRANSIT -> NOT_REQUIRED item.placeId != null -> FOUND item.placeSearchCount == 0 -> PENDING item.placeSearchCount >= TravelItineraryItem.MAX_PLACE_SEARCH_COUNT -> NOT_FOUND diff --git a/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/TravelItineraryItem.kt b/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/TravelItineraryItem.kt index 41cdd4c..b620f5d 100644 --- a/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/TravelItineraryItem.kt +++ b/linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/TravelItineraryItem.kt @@ -20,7 +20,7 @@ data class TravelItineraryItem( ) { fun isRetryable(): Boolean = placeId == null && - category != Category.TRANSPORTATION && + category != Category.TRANSPORTATION_TRANSIT && placeSearchCount < MAX_PLACE_SEARCH_COUNT fun isResolved(): Boolean = !isRetryable() diff --git a/linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/TravelItineraryItemTest.kt b/linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/TravelItineraryItemTest.kt index e2b08f3..c2ffafb 100644 --- a/linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/TravelItineraryItemTest.kt +++ b/linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/TravelItineraryItemTest.kt @@ -32,8 +32,8 @@ class TravelItineraryItemTest { @Test fun `교통편 카테고리는_장소 검색이 필요 없으므로_재검색 대상이 아니다`() { - // given - TRANSPORTATION 카테고리인 항목 - val item = createItem(placeId = null, category = Category.TRANSPORTATION, placeSearchCount = 0) + // given - TRANSPORTATION_TRANSIT 카테고리인 항목 + val item = createItem(placeId = null, category = Category.TRANSPORTATION_TRANSIT, placeSearchCount = 0) // when - 재검색 가능 여부를 확인한다 // then - retryable이 아니다 diff --git a/linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt b/linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt index 87eb101..3580bef 100644 --- a/linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt +++ b/linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt @@ -117,7 +117,7 @@ class VideoAnalyzeAdapter( { "day": 1, "items": [ - {"order": 1, "category": "TRANSPORTATION", "name": "나리타 익스프레스", "description": "공항에서 도쿄역까지 이동", "tips": "JR패스 사용 가능"}, + {"order": 1, "category": "TRANSPORTATION_TRANSIT", "name": "나리타 익스프레스", "description": "공항에서 도쿄역까지 이동", "tips": "JR패스 사용 가능"}, {"order": 2, "category": "EAT", "name": "이치란 라멘", "description": "돈코츠 라멘", "tips": "오픈 전 줄서기 추천"}, {"order": 3, "category": "ATTRACTION", "name": "센소지", "description": "아사쿠사의 대표 사찰", "tips": null}, {"order": 4, "category": "SHOPPING", "name": "돈키호테", "description": "과자, 화장품 구매", "tips": "면세 가능"} @@ -180,7 +180,8 @@ class VideoAnalyzeAdapter( EXCLUDE: Generic city names like "Seoul", "Tokyo", "Paris" - "SHOPPING": Stores for NON-FOOD items (clothes, souvenirs, cosmetics), malls, duty-free EXCLUDE: Food at convenience store → use "EAT" - - "TRANSPORTATION": Train, bus, taxi, rental car, passes (JR Pass, Suica, T-money), routes + - "TRANSPORTATION_HUB": Airports, train stations, bus terminals — fixed transportation facilities that travelers visit + - "TRANSPORTATION_TRANSIT": Shuttle buses, subway rides, taxi rides, rental cars, passes (JR Pass, Suica, T-money), routes — the act of moving between places ============================================================ DEDUPLICATION RULES @@ -212,7 +213,7 @@ class VideoAnalyzeAdapter( - "costBasis" is one of "VIDEO_MENTIONED", "ITEM_ESTIMATED", or null - "days" is an array of day objects, each with "day" (int) and "items" (array) - Each item has: order (int), category (string), name (string), description (string or null), tips (string or null) - - category is one of: EAT, ATTRACTION, SHOPPING, TRANSPORTATION + - category is one of: EAT, ATTRACTION, SHOPPING, TRANSPORTATION_HUB, TRANSPORTATION_TRANSIT - Items are in chronological visit order within each day - Convenience store food is "EAT", not "SHOPPING" - No airplane meals included diff --git a/linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt b/linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt index 8bdf308..1da6f09 100644 --- a/linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt +++ b/linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt @@ -66,10 +66,13 @@ data class AiApiResponse( null } - private fun parseCategory(category: String?): Category = - try { - Category.valueOf(category?.trim()?.uppercase() ?: "EAT") + private fun parseCategory(category: String?): Category { + val normalized = category?.trim()?.uppercase() ?: return Category.EAT + if (normalized == "TRANSPORTATION") return Category.TRANSPORTATION_TRANSIT + return try { + Category.valueOf(normalized) } catch (_: IllegalArgumentException) { Category.EAT } + } } diff --git a/linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/TravelItineraryItemEntity.kt b/linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/TravelItineraryItemEntity.kt index 608d11e..11430e7 100644 --- a/linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/TravelItineraryItemEntity.kt +++ b/linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/TravelItineraryItemEntity.kt @@ -34,7 +34,7 @@ class TravelItineraryItemEntity( @Column(name = "item_order", nullable = false) val itemOrder: Int, @Enumerated(EnumType.STRING) - @Column(name = "category", nullable = false, length = 20) + @Column(name = "category", nullable = false, length = 30) val category: Category, @Column(name = "name", nullable = false, length = 255) val name: String, diff --git a/linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/repository/TravelItineraryItemQuerydslRepository.kt b/linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/repository/TravelItineraryItemQuerydslRepository.kt index dbd672d..f4767b3 100644 --- a/linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/repository/TravelItineraryItemQuerydslRepository.kt +++ b/linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/repository/TravelItineraryItemQuerydslRepository.kt @@ -56,7 +56,7 @@ class TravelItineraryItemQuerydslRepository( fun findRetryableItems( videoAnalysisTaskId: String, - excludeCategory: Category = Category.TRANSPORTATION, + excludeCategory: Category = Category.TRANSPORTATION_TRANSIT, maxSearchCount: Int = 10, ): List = queryFactory @@ -75,7 +75,7 @@ class TravelItineraryItemQuerydslRepository( .fetch() fun findVideoAnalysisTaskIdsWithRetryableItems( - excludeCategory: Category = Category.TRANSPORTATION, + excludeCategory: Category = Category.TRANSPORTATION_TRANSIT, maxSearchCount: Int = 10, ): List = queryFactory