Skip to content

[Feat] video title cost extraction#47

Merged
toychip merged 9 commits intomainfrom
feat/#46-video-title-cost-extraction
Mar 27, 2026
Merged

[Feat] video title cost extraction#47
toychip merged 9 commits intomainfrom
feat/#46-video-title-cost-extraction

Conversation

@toychip
Copy link
Copy Markdown
Contributor

@toychip toychip commented Mar 27, 2026

관련 이슈


변경 내용

여행 제목 자동 생성

  • Gemini AI 프롬프트에 title 필드 추가: "도시, 국가" 형식 대신 자연스러운 한국어 제목 생성 (예: "후쿠오카 3박 4일 여행", "오사카 맛집 탐방")
  • EventListener에서 AI가 생성한 title을 TripPlan 제목으로 사용 (title 없으면 destination 폴백)

예상 비용 추출

  • 출발/도착 교통비(항공, KTX)를 제외한 현지 비용을 min~max 범위로 추출
  • 비용 산출 근거를 costBasis로 구분하여 신뢰도 제공
    • VIDEO_MENTIONED: 영상에서 유튜버가 직접 언급하거나 화면에 표시한 금액 기반
    • ITEM_ESTIMATED: 영상에 가격 정보가 없어 AI가 장소/활동 기반으로 추정
  • VideoAnalysisTask에 estimated_min_cost, estimated_max_cost, cost_basis 컬럼 추가
  • GET /video/{id}/schedule API 응답에 비용 및 costBasis 포함

체크리스트

  • Ktlint
  • 테스트 통과 여부

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 비디오 분석 결과에 여행 비용 추정치(최소/최대)와 비용 근거(cost basis) 추가 및 노출
    • AI 분석에서 도출된 비디오 제목을 여행 계획 제목으로 우선 반영
    • 비용 관련 메타데이터가 영속화되어 API 응답에 포함
  • 테스트

    • 비용 및 제목 필드 반영에 따른 단위/통합 테스트 업데이트

@toychip toychip linked an issue Mar 27, 2026 that may be closed by this pull request
8 tasks
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

비디오 분석 도메인에 제목 및 비용 추정 필드(title, estimatedMinCost, estimatedMaxCost, costBasis)와 새로운 열거형 CostBasis를 도입하고, 이들 필드를 DTO, 포트, 영속성 엔티티/어댑터, AI 응답/프롬프트, 이벤트 처리 및 저장 로직 전반에 전파했습니다.

Changes

Cohort / File(s) Summary
Domain Model
com/linktrip/application/domain/video/CostBasis.kt, .../VideoAnalysisResult.kt, .../VideoAnalysisTask.kt
신규 CostBasis 열거형(VIDEO_MENTIONED, ITEM_ESTIMATED) 추가 및 title, estimatedMinCost, estimatedMaxCost, costBasis 필드 추가.
Application Service
.../VideoAnalysisResultSaver.kt, .../VideoAnalyzeEventListener.kt
save() 시 비용 관련 optional 매개변수(estimatedMinCost/Max/CostBasis) 추가; 이벤트 리스너가 분석 결과의 title 및 비용 필드를 캡처해 저장 및 후속 처리(processPendingRequests(title ?: destination))에 전달.
Port Interface
com/linktrip/application/port/output/persistence/VideoAnalysisTaskPersistencePort.kt
updateValidAndStatus() 시 비용 관련 nullable 파라미터(estimatedMinCost/estimatedMaxCost/costBasis) 추가.
Input HTTP DTO
com/linktrip/input/http/controller/dto/response/VideoAnalyzeResponse.kt
응답 DTO에 estimatedMinCost, estimatedMaxCost, costBasis(String?) 추가 및 매퍼에서 엔티티 필드 매핑.
Output HTTP / AI DTO & Adapter
.../dto/AiApiResponse.kt, .../VideoAnalyzeAdapter.kt
AI 응답 DTO에 title, 비용 필드 추가; toDomain()에서 비용/제목 매핑 및 costBasis 파싱 로직 추가; Gemini 프롬프트 확장(제목 추출, 비용 산정 규칙·검증 등).
Persistence Adapter & Entity
.../adapter/VideoAnalysisTaskAdapter.kt, .../entity/VideoAnalysisTaskEntity.kt
업데이터/어댑터가 비용 파라미터를 받아 엔티티에 할당하도록 변경; 엔티티에 estimated_min_cost, estimated_max_cost 컬럼 및 cost_basis(EnumType.STRING) 필드 추가; 매핑(toDomain/from) 확장.
Tests
.../VideoAnalyzeEventListenerTest.kt
테스트에서 성공/비정상 경로의 기대값에 title, 비용 필드 및 CostBasis 검증 추가; Mockito 검증 호출 시 추가 파라미터 반영.

Sequence Diagram(s)

(생성 생략 — 변경은 주로 필드 확장 및 데이터 흐름 전파이며 새로운 복합 상호작용 흐름을 도식화할 필요 없음.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

개요

비디오 분석 도메인에 비용 추정 기능을 추가합니다. 새로운 CostBasis 열거형과 함께 estimatedMinCost, estimatedMaxCost, costBasis, title 필드를 도메인 모델, DTO, 영속성 계층 전반에 도입하며, Gemini AI 프롬프트를 확장하여 비용 데이터 추출을 지시합니다.

변경사항

Cohort / File(s) 요약
Domain Model
com/linktrip/application/domain/video/CostBasis.kt, VideoAnalysisResult.kt, VideoAnalysisTask.kt
CostBasis 열거형(VIDEO_MENTIONED, ITEM_ESTIMATED) 신규 도입 및 비용 추정 필드(estimatedMinCost, estimatedMaxCost, costBasis) 추가.
Application Service
VideoAnalysisResultSaver.kt, VideoAnalyzeEventListener.kt
save() 메서드에 비용 관련 선택적 매개변수 추가; 이벤트 리스너에서 분석 결과의 제목 및 비용 데이터를 캡처하여 영속성 및 요청 처리에 전달.
Port Interface
com/linktrip/application/port/output/persistence/VideoAnalysisTaskPersistencePort.kt
updateValidAndStatus() 메서드 서명 확장으로 선택적 비용 매개변수(estimatedMinCost, estimatedMaxCost, costBasis) 지원.
Input HTTP DTO
com/linktrip/input/http/controller/dto/response/VideoAnalyzeResponse.kt
응답 DTO에 비용 필드 추가; 매퍼에서 videoAnalysisTask로부터 해당 필드 매핑.
Output HTTP DTO & AI Integration
com/linktrip/output/http/dto/AiApiResponse.kt, VideoAnalyzeAdapter.kt
AiApiResponse에 비용 및 제목 필드 추가; parseCostBasis() 헬퍼로 문자열 변환; Gemini 프롬프트 확장으로 제목 추출 및 비용 계산 규칙(우선순위, 환율, 포함 범위) 지정.
Persistence Adapter
com/linktrip/output/persistence/mysql/adapter/VideoAnalysisTaskAdapter.kt, entity/VideoAnalysisTaskEntity.kt
어댑터에서 비용 매개변수 처리; 엔티티에 estimated_min_cost, estimated_max_cost 칼럼 및 @Enumerated(EnumType.STRING) 기반 cost_basis 추가; toDomain() 변환 확장.

예상 코드 리뷰 노력

🎯 3 (Moderate) | ⏱️ ~25 minutes

관련 이슈

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경사항을 명확하게 요약합니다. 비디오 제목 추출과 비용 추출 기능이 모두 포함되어 있으며, 이는 raw_summary와 pr_objectives에서 설명하는 핵심 변경사항들과 일치합니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#46-video-title-cost-extraction

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 12cceb9 and e690155.

📒 Files selected for processing (11)
  • linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/CostBasis.kt
  • linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalysisResult.kt
  • linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalysisResultSaver.kt
  • linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalysisTask.kt
  • linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListener.kt
  • linktrip-application/src/main/kotlin/com/linktrip/application/port/output/persistence/VideoAnalysisTaskPersistencePort.kt
  • linktrip-input-http/src/main/kotlin/com/linktrip/input/http/controller/dto/response/VideoAnalyzeResponse.kt
  • linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt
  • linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt
  • linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/VideoAnalysisTaskAdapter.kt
  • 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 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 응답에 비용 관련 필드가 올바르게 추가되었습니다. costBasisString?으로 노출하는 것은 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 형식과 분리한 방향 좋습니다.

도시, 국가 포맷을 금지하고 자연스러운 한국어 예시를 준 덕분에, 제목이 목적지 문자열로 다시 수렴하는 경우를 꽤 잘 막을 수 있겠습니다.

Comment on lines +157 to +163
- 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)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

비용 우선순위 규칙이 제외 조건과 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.isNull
 verify(
     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

📥 Commits

Reviewing files that changed from the base of the PR and between e690155 and cb95de2.

📒 Files selected for processing (2)
  • linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListenerTest.kt
  • linktrip-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 파라미터 검증을 위한 anyOrNull import가 적절히 추가되었습니다.


56-86: LGTM!

새로운 비용 관련 필드들이 테스트 데이터와 검증 로직에 일관되게 추가되었습니다. eq()를 사용한 정확한 값 매칭이 적절합니다.


137-137: LGTM!

예외 발생 시 save()가 호출되지 않음을 검증하는 로직이 새로운 파라미터에 맞게 올바르게 업데이트되었습니다.


148-196: LGTM!

ITEM_ESTIMATED costBasis와 함께 비용 필드가 추가되었고, 각 항목의 Category 매핑까지 검증하여 테스트 커버리지가 향상되었습니다. argumentCaptor를 사용한 상세 검증이 적절합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 영상 분석 시 여행 제목 자동 생성 및 예상 비용 추출

1 participant