Conversation
📝 WalkthroughWalkthrough비디오 분석 도메인에 제목 및 비용 추정 필드( Changes
Sequence Diagram(s)(생성 생략 — 변경은 주로 필드 확장 및 데이터 흐름 전파이며 새로운 복합 상호작용 흐름을 도식화할 필요 없음.) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
개요비디오 분석 도메인에 비용 추정 기능을 추가합니다. 새로운 변경사항
예상 코드 리뷰 노력🎯 3 (Moderate) | ⏱️ ~25 minutes 관련 이슈
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListener.kt (1)
77-79: 매개변수 이름이 실제 용도와 일치하지 않습니다.
processPendingRequests메서드의destination매개변수는 Line 70에서title ?: destination으로 호출되어 실제로는 여행 계획의 제목을 받습니다. 매개변수 이름을title또는tripPlanTitle로 변경하여 혼란을 방지하세요.♻️ 제안하는 수정
private fun processPendingRequests( videoAnalysisTaskId: String, - destination: String?, + tripPlanTitle: String?, ) { val requests = tripPlanRequestPort.findUnprocessedByVideoAnalysisTaskId(videoAnalysisTaskId) if (requests.isEmpty()) return - val title = destination ?: "여행 계획" + val title = tripPlanTitle ?: "여행 계획"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListener.kt` around lines 77 - 79, The parameter name destination on the method processPendingRequests is misleading because callers pass title ?: destination; rename the parameter to title or tripPlanTitle (e.g., change signature processPendingRequests(videoAnalysisTaskId: String, title: String?) ), update all callers (including the call that uses title ?: destination) to pass the renamed parameter, and update all internal uses inside processPendingRequests to reference the new name so the API and implementation reflect that this value is the trip plan title.linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt (1)
210-212: 비용 필드의 불변식을 체크리스트에도 명시해 주세요.지금은 타입만 확인해서
estimatedMinCost > estimatedMaxCost나 비용 값은 있는데costBasis = null인 응답도 통과할 수 있습니다. Line 164의 규칙을 끝까지 강제하려면0 <= estimatedMinCost <= estimatedMaxCost와 "비용이 있으면costBasis도 필수"를 같이 넣는 편이 안전합니다.수정 예시
- - "estimatedMinCost" and "estimatedMaxCost" are integers in KRW, or both null - - "costBasis" is one of "VIDEO_MENTIONED", "ITEM_ESTIMATED", or null + - "estimatedMinCost" and "estimatedMaxCost" are integers in KRW, or both null + - If cost fields are present, 0 <= estimatedMinCost <= estimatedMaxCost + - If any cost field is present, "costBasis" must be one of "VIDEO_MENTIONED" or "ITEM_ESTIMATED"; otherwise all three must be null🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt` around lines 210 - 212, Update the checklist/validation in VideoAnalyzeAdapter (where "title", "estimatedMinCost", "estimatedMaxCost", "costBasis" are described) to enforce cost invariants: require either both estimatedMinCost and estimatedMaxCost be null, or both present and satisfy 0 <= estimatedMinCost <= estimatedMaxCost, and if either cost is present then costBasis must be non-null (one of "VIDEO_MENTIONED" or "ITEM_ESTIMATED"); implement these checks in the same validation/parsing function that currently documents these fields so responses failing these invariants are rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt`:
- Around line 157-163: In VideoAnalyzeAdapter update the cost-priority logic so
that when a VIDEO_MENTIONED total is available you validate and, if it includes
excluded items (departure/arrival flights, intercity KTX), adjust the total
(subtract excluded items when identifiable) or fallback to next method rather
than blindly accepting it; ensure sums of individual prices explicitly shown in
the video are classified as costBasis = "VIDEO_MENTIONED" (not
"ITEM_ESTIMATED"); only mark costBasis = "ITEM_ESTIMATED" when values are
derived by inference rather than directly shown; modify the code paths that set
costBasis (search for the costBasis assignment logic and the priority handling
in VideoAnalyzeAdapter) to implement these checks and fallbacks.
In
`@linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt`:
- Around line 13-14: AiApiResponse's estimatedMinCost and estimatedMaxCost are
declared as Long? which will throw on float JSON; change these properties in
class AiApiResponse to Double? (estimatedMinCost: Double?, estimatedMaxCost:
Double?) and update downstream conversion where the app expects integers
(convert to Long via rounding or toLong() in the domain mapping code), or
alternatively enable Jackson's DeserializationFeature.ACCEPT_FLOAT_AS_INT on the
ObjectMapper used by deserialization (so Long remains but floats are accepted);
pick one approach and make sure VideoAnalyzeAdapter's handling that consumes
these fields performs the appropriate conversion/validation.
In
`@linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/VideoAnalysisTaskEntity.kt`:
- Around line 35-41: The from(videoAnalysisTask: VideoAnalysisTask) constructor
in VideoAnalysisTaskEntity doesn't populate the new cost fields, causing values
to be lost on save; update the from(...) method to set estimatedMinCost,
estimatedMaxCost, and costBasis from the incoming VideoAnalysisTask (mirroring
what toDomain() now returns) so entity-to-domain and domain-to-entity mappings
remain symmetric and saved records preserve the cost metadata.
---
Nitpick comments:
In
`@linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListener.kt`:
- Around line 77-79: The parameter name destination on the method
processPendingRequests is misleading because callers pass title ?: destination;
rename the parameter to title or tripPlanTitle (e.g., change signature
processPendingRequests(videoAnalysisTaskId: String, title: String?) ), update
all callers (including the call that uses title ?: destination) to pass the
renamed parameter, and update all internal uses inside processPendingRequests to
reference the new name so the API and implementation reflect that this value is
the trip plan title.
In
`@linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt`:
- Around line 210-212: Update the checklist/validation in VideoAnalyzeAdapter
(where "title", "estimatedMinCost", "estimatedMaxCost", "costBasis" are
described) to enforce cost invariants: require either both estimatedMinCost and
estimatedMaxCost be null, or both present and satisfy 0 <= estimatedMinCost <=
estimatedMaxCost, and if either cost is present then costBasis must be non-null
(one of "VIDEO_MENTIONED" or "ITEM_ESTIMATED"); implement these checks in the
same validation/parsing function that currently documents these fields so
responses failing these invariants are rejected.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 578674a6-944b-47d1-8392-9cc4e45bc5fb
📒 Files selected for processing (11)
linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/CostBasis.ktlinktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalysisResult.ktlinktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalysisResultSaver.ktlinktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalysisTask.ktlinktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListener.ktlinktrip-application/src/main/kotlin/com/linktrip/application/port/output/persistence/VideoAnalysisTaskPersistencePort.ktlinktrip-input-http/src/main/kotlin/com/linktrip/input/http/controller/dto/response/VideoAnalyzeResponse.ktlinktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.ktlinktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.ktlinktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/VideoAnalysisTaskAdapter.ktlinktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/VideoAnalysisTaskEntity.kt
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: 자동 검증 (ktlint + test)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-03-18T01:08:05.661Z
Learnt from: toychip
Repo: Link-Trip/BackEnd PR: 24
File: linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/YouTubeVideoPersistenceAdapter.kt:16-28
Timestamp: 2026-03-18T01:08:05.661Z
Learning: In `linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/YouTubeVideoPersistenceAdapter.kt`, the concurrent-write safety (duplicate videoId in batch, race condition on uk_youtube_video_video_id) is intentionally deferred. The maintainer (toychip) confirmed the system is currently single-server, so this is not a concern yet. When replication is introduced in the future, ShedLock will be used for distributed locking to address this. At that point, in-batch videoId deduplication should also be applied.
Applied to files:
linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/VideoAnalysisTaskAdapter.kt
📚 Learning: 2026-03-18T01:07:53.575Z
Learnt from: toychip
Repo: Link-Trip/BackEnd PR: 24
File: linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/YouTubeVideoPersistenceAdapter.kt:16-28
Timestamp: 2026-03-18T01:07:53.575Z
Learning: Apply the same deferred concurrent-write policy to all Kotlin persistence adapters in the MySQL adapter package: ensure writes are coordinated on a single server with ShedLock considered for the replication phase. For YouTubeVideoPersistenceAdapter.kt (and other adapters in this directory), verify that concurrent writes are serialized or properly guarded, document the policy in code comments, and ensure CI checks or deployment gating will catch any regression where multiple instances might attempt a conflicting write during replication.
Applied to files:
linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/VideoAnalysisTaskAdapter.kt
🔇 Additional comments (11)
linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/CostBasis.kt (1)
1-6: LGTM!열거형이 명확하게 정의되어 있고, PR 목적에 따라 비용 추정의 신뢰 수준을 잘 표현합니다.
linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalysisResult.kt (1)
6-9: LGTM!AI 분석 결과의 새로운 필드들이 적절히 추가되었습니다. nullable 타입 사용이 선택적 데이터에 적합합니다.
linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalysisTask.kt (1)
13-15: LGTM!비용 필드들이 기본값
null로 적절히 추가되었습니다. 분석 완료 후 업데이트되는 설계가create()팩토리 패턴과 일관됩니다.linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListener.kt (1)
51-57: LGTM!비용 관련 필드들이
videoAnalysisResultSaver.save()호출에 올바르게 전달됩니다.linktrip-application/src/main/kotlin/com/linktrip/application/port/output/persistence/VideoAnalysisTaskPersistencePort.kt (1)
19-26: LGTM!인터페이스 확장이 기본값을 사용하여 하위 호환성을 유지합니다. 관련 코드 스니펫에서
VideoAnalysisTaskAdapter구현이 시그니처와 일치하는 것을 확인했습니다.linktrip-input-http/src/main/kotlin/com/linktrip/input/http/controller/dto/response/VideoAnalyzeResponse.kt (2)
14-16: LGTM!API 응답에 비용 관련 필드가 올바르게 추가되었습니다.
costBasis를String?으로 노출하는 것은 JSON 직렬화에 적합한 선택입니다.
63-65: LGTM!
VideoAnalysisTask에서 응답 DTO로의 매핑이 정확합니다.?.name을 사용한 안전한 null 처리가 적절합니다.linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt (1)
62-67: LGTM!
parseCostBasis가 null 및 유효하지 않은 값을 안전하게 처리합니다.parseCategory와 일관된 패턴을 따릅니다.linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalysisResultSaver.kt (1)
14-29: LGTM!
save메서드가 새로운 비용 필드를 깔끔하게 처리합니다. 기본값을 통한 하위 호환성 유지와 트랜잭션 경계 설정이 적절합니다.linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/VideoAnalysisTaskAdapter.kt (1)
44-60: 상태와 비용 메타데이터를 같은 트랜잭션에서 함께 갱신한 점 좋습니다.
valid,status, 비용 범위,costBasis를 같은 업데이트 경로에 묶어서 분석 결과가 부분 반영되는 가능성을 줄였습니다.linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt (1)
143-150: 제목 규칙을destination형식과 분리한 방향 좋습니다.
도시, 국가포맷을 금지하고 자연스러운 한국어 예시를 준 덕분에, 제목이 목적지 문자열로 다시 수렴하는 경우를 꽤 잘 막을 수 있겠습니다.
| - INCLUDE: food, accommodation, local transportation, activities, entrance fees, shopping — everything shown or done in the video | ||
| - EXCLUDE: departure/arrival transportation only (international flights, intercity KTX/train to the destination) | ||
| - Priority 1: If the video explicitly shows or mentions total cost → use that as base, set min to -10% and max to +10%, costBasis = "VIDEO_MENTIONED" | ||
| - Priority 2: If no total is shown → sum up individual costs from the video (food prices, hotel rates, ticket prices mentioned/shown), costBasis = "ITEM_ESTIMATED" | ||
| - Priority 3: If individual prices are also unclear → estimate each item's realistic cost based on the specific places and activities extracted above, then sum, costBasis = "ITEM_ESTIMATED" | ||
| - Foreign currency must be converted to KRW at current approximate rates | ||
| - "costBasis" must be one of: "VIDEO_MENTIONED" (video explicitly states total/individual costs), "ITEM_ESTIMATED" (estimated from activities) |
There was a problem hiding this comment.
비용 우선순위 규칙이 제외 조건과 costBasis 의미를 같이 깨뜨립니다.
Line 159는 총액이 보이면 그대로 기준으로 쓰게 해서, 항공/KTX가 포함된 총액이면 Line 158의 제외 규칙이 바로 무너집니다. 그리고 Line 160은 영상에 직접 나온 개별 금액들의 합도 ITEM_ESTIMATED로 분류해서, 이번 costBasis의 신뢰도 의미를 낮춰버립니다. 총액에 제외 대상이 섞였으면 조정하거나 폴백해야 하고, 전부 영상 근거로 합산한 케이스는 VIDEO_MENTIONED로 남기는 쪽이 맞습니다.
수정 예시
- - Priority 1: If the video explicitly shows or mentions total cost → use that as base, set min to -10% and max to +10%, costBasis = "VIDEO_MENTIONED"
- - Priority 2: If no total is shown → sum up individual costs from the video (food prices, hotel rates, ticket prices mentioned/shown), costBasis = "ITEM_ESTIMATED"
- - Priority 3: If individual prices are also unclear → estimate each item's realistic cost based on the specific places and activities extracted above, then sum, costBasis = "ITEM_ESTIMATED"
+ - Priority 1: If the video explicitly shows or mentions a local-trip total that already excludes departure/arrival transport → use that as base, set min to -10% and max to +10%, costBasis = "VIDEO_MENTIONED"
+ - If the mentioned total includes excluded transport, subtract the excluded portion when it is identifiable; otherwise do not use that total as-is
+ - Priority 2: If no valid total is shown → sum up individual costs explicitly mentioned/shown in the video, costBasis = "VIDEO_MENTIONED"
+ - Priority 3: If some item prices are missing or have to be inferred from places/activities → estimate the missing items and set costBasis = "ITEM_ESTIMATED"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt`
around lines 157 - 163, In VideoAnalyzeAdapter update the cost-priority logic so
that when a VIDEO_MENTIONED total is available you validate and, if it includes
excluded items (departure/arrival flights, intercity KTX), adjust the total
(subtract excluded items when identifiable) or fallback to next method rather
than blindly accepting it; ensure sums of individual prices explicitly shown in
the video are classified as costBasis = "VIDEO_MENTIONED" (not
"ITEM_ESTIMATED"); only mark costBasis = "ITEM_ESTIMATED" when values are
derived by inference rather than directly shown; modify the code paths that set
costBasis (search for the costBasis assignment logic and the priority handling
in VideoAnalyzeAdapter) to implement these checks and fallbacks.
linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt
Show resolved
Hide resolved
...ysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/VideoAnalysisTaskEntity.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
🧹 Nitpick comments (1)
linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListenerTest.kt (1)
113-120:anyOrNull()대신isNull()을 사용하면 검증이 더 명확해집니다.현재
anyOrNull()은 null을 포함한 모든 값을 허용하지만,invalidResult의 비용 필드들이 모두null로 설정되어 있으므로isNull()을 사용하면 의도를 더 명확하게 표현할 수 있습니다.♻️ 선택적 개선 제안
+import org.mockito.kotlin.isNullverify( videoAnalysisTaskPersistencePort, ).updateValidAndStatus( eq("s1"), eq(false), eq(VideoAnalysisTaskStatus.INVALID), - anyOrNull(), - anyOrNull(), - anyOrNull(), + isNull(), + isNull(), + isNull(), )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListenerTest.kt` around lines 113 - 120, In the test VideoAnalyzeEventListenerTest, tighten the Mockito verification on updateValidAndStatus by replacing the trailing anyOrNull() matchers with isNull() for the parameters that correspond to the invalidResult cost fields (the calls following eq("s1"), eq(false), eq(VideoAnalysisTaskStatus.INVALID)), so the verification asserts those specific arguments are null rather than any value; update the invocation of updateValidAndStatus to use isNull() for those null cost fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListenerTest.kt`:
- Around line 113-120: In the test VideoAnalyzeEventListenerTest, tighten the
Mockito verification on updateValidAndStatus by replacing the trailing
anyOrNull() matchers with isNull() for the parameters that correspond to the
invalidResult cost fields (the calls following eq("s1"), eq(false),
eq(VideoAnalysisTaskStatus.INVALID)), so the verification asserts those specific
arguments are null rather than any value; update the invocation of
updateValidAndStatus to use isNull() for those null cost fields.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 22f372c9-7b68-4686-bf71-0cdf4716f1c6
📒 Files selected for processing (2)
linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListenerTest.ktlinktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/VideoAnalysisTaskEntity.kt
🚧 Files skipped from review as they are similar to previous changes (1)
- linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/VideoAnalysisTaskEntity.kt
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: 자동 검증 (ktlint + test)
🔇 Additional comments (4)
linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListenerTest.kt (4)
15-15: LGTM!nullable 파라미터 검증을 위한
anyOrNullimport가 적절히 추가되었습니다.
56-86: LGTM!새로운 비용 관련 필드들이 테스트 데이터와 검증 로직에 일관되게 추가되었습니다.
eq()를 사용한 정확한 값 매칭이 적절합니다.
137-137: LGTM!예외 발생 시
save()가 호출되지 않음을 검증하는 로직이 새로운 파라미터에 맞게 올바르게 업데이트되었습니다.
148-196: LGTM!
ITEM_ESTIMATEDcostBasis와 함께 비용 필드가 추가되었고, 각 항목의Category매핑까지 검증하여 테스트 커버리지가 향상되었습니다. argumentCaptor를 사용한 상세 검증이 적절합니다.
관련 이슈
변경 내용
여행 제목 자동 생성
title필드 추가: "도시, 국가" 형식 대신 자연스러운 한국어 제목 생성 (예: "후쿠오카 3박 4일 여행", "오사카 맛집 탐방")예상 비용 추출
costBasis로 구분하여 신뢰도 제공VIDEO_MENTIONED: 영상에서 유튜버가 직접 언급하거나 화면에 표시한 금액 기반ITEM_ESTIMATED: 영상에 가격 정보가 없어 AI가 장소/활동 기반으로 추정estimated_min_cost,estimated_max_cost,cost_basis컬럼 추가체크리스트
Summary by CodeRabbit
릴리스 노트
새로운 기능
테스트