Compare commits
1 Commits
7fa623d22d
...
v0.1.17
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4407f2d67d |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -18,3 +18,5 @@ k8s/secrets.yaml
|
|||||||
# OS / misc
|
# OS / misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
backend/cookies.txt
|
backend/cookies.txt
|
||||||
|
backend-java/cookies.txt
|
||||||
|
**/cookies.txt
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class ExtractorService {
|
|||||||
%s
|
%s
|
||||||
- price_range: 가격대 (예: 1만원대, 2-3만원) (string | null)
|
- price_range: 가격대 (예: 1만원대, 2-3만원) (string | null)
|
||||||
- foods_mentioned: 언급된 대표 메뉴 (string[], 최대 10개, 우선순위 높은 순, 반드시 한글로 작성)
|
- foods_mentioned: 언급된 대표 메뉴 (string[], 최대 10개, 우선순위 높은 순, 반드시 한글로 작성)
|
||||||
- evaluation: 평가 내용 (string | null)
|
- evaluation: 평가 내용을 100자 이내로 요약 (string | null)
|
||||||
- guests: 함께한 게스트 (string[])
|
- guests: 함께한 게스트 (string[])
|
||||||
|
|
||||||
영상 제목: {title}
|
영상 제목: {title}
|
||||||
@@ -62,6 +62,10 @@ public class ExtractorService {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ExtractionResult extractRestaurants(String title, String transcript, String customPrompt) {
|
public ExtractionResult extractRestaurants(String title, String transcript, String customPrompt) {
|
||||||
|
// #292 — transcript null/blank 가드 (NPE 방지)
|
||||||
|
if (transcript == null || transcript.isBlank()) {
|
||||||
|
return new ExtractionResult(List.of(), "");
|
||||||
|
}
|
||||||
// Truncate very long transcripts
|
// Truncate very long transcripts
|
||||||
if (transcript.length() > 8000) {
|
if (transcript.length() > 8000) {
|
||||||
transcript = transcript.substring(0, 7000) + "\n...(중략)...\n" + transcript.substring(transcript.length() - 1000);
|
transcript = transcript.substring(0, 7000) + "\n...(중략)...\n" + transcript.substring(transcript.length() - 1000);
|
||||||
|
|||||||
@@ -156,7 +156,15 @@ public class GeocodingService {
|
|||||||
|
|
||||||
if (country.isEmpty() && !city.isEmpty()) country = "한국";
|
if (country.isEmpty() && !city.isEmpty()) country = "한국";
|
||||||
if (country.isEmpty()) return null;
|
if (country.isEmpty()) return null;
|
||||||
return country + "|" + city + "|" + district;
|
// #292 — 빈 토큰은 region 문자열에 포함시키지 않는다(`한국||구` 형식 방지).
|
||||||
|
StringBuilder sb = new StringBuilder(country);
|
||||||
|
if (!city.isEmpty()) {
|
||||||
|
sb.append('|').append(city);
|
||||||
|
if (!district.isEmpty()) sb.append('|').append(district);
|
||||||
|
} else if (!district.isEmpty()) {
|
||||||
|
// city 없이 district만 있는 경우는 정확도 낮으므로 무시
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> geocode(String query) {
|
private Map<String, Object> geocode(String query) {
|
||||||
|
|||||||
@@ -87,6 +87,9 @@ public class PipelineService {
|
|||||||
String videoDbId = (String) video.get("id");
|
String videoDbId = (String) video.get("id");
|
||||||
String title = (String) video.get("title");
|
String title = (String) video.get("title");
|
||||||
|
|
||||||
|
// #292 — 외부 가시성을 위해 진입 시 processing 전이 (이미 processing이면 no-op)
|
||||||
|
updateVideoStatus(videoDbId, "processing", null, null);
|
||||||
|
|
||||||
var result = extractorService.extractRestaurants(title, transcript, customPrompt);
|
var result = extractorService.extractRestaurants(title, transcript, customPrompt);
|
||||||
if (result.restaurants().isEmpty()) {
|
if (result.restaurants().isEmpty()) {
|
||||||
updateVideoStatus(videoDbId, "done", null, result.rawResponse());
|
updateVideoStatus(videoDbId, "done", null, result.rawResponse());
|
||||||
@@ -105,18 +108,26 @@ public class PipelineService {
|
|||||||
// Build upsert data
|
// Build upsert data
|
||||||
var data = new HashMap<String, Object>();
|
var data = new HashMap<String, Object>();
|
||||||
data.put("name", name);
|
data.put("name", name);
|
||||||
data.put("address", geo != null ? geo.get("formatted_address") : restData.get("address"));
|
|
||||||
data.put("region", restData.get("region"));
|
data.put("region", restData.get("region"));
|
||||||
data.put("latitude", geo != null ? geo.get("latitude") : null);
|
|
||||||
data.put("longitude", geo != null ? geo.get("longitude") : null);
|
|
||||||
data.put("cuisine_type", restData.get("cuisine_type"));
|
data.put("cuisine_type", restData.get("cuisine_type"));
|
||||||
data.put("price_range", restData.get("price_range"));
|
data.put("price_range", restData.get("price_range"));
|
||||||
data.put("google_place_id", geo != null ? geo.get("google_place_id") : null);
|
// #292 — geocode 실패(geo==null) 시 좌표/주소/place_id 등 기존 값 보존하기 위해
|
||||||
data.put("phone", geo != null ? geo.get("phone") : null);
|
// null을 명시적으로 put하지 않는다. upsert 측에서 누락 컬럼은 그대로 유지.
|
||||||
data.put("website", geo != null ? geo.get("website") : null);
|
if (geo != null) {
|
||||||
data.put("business_status", geo != null ? geo.get("business_status") : null);
|
data.put("address", geo.get("formatted_address"));
|
||||||
data.put("rating", geo != null ? geo.get("rating") : null);
|
data.put("latitude", geo.get("latitude"));
|
||||||
data.put("rating_count", geo != null ? geo.get("rating_count") : null);
|
data.put("longitude", geo.get("longitude"));
|
||||||
|
data.put("google_place_id", geo.get("google_place_id"));
|
||||||
|
data.put("phone", geo.get("phone"));
|
||||||
|
data.put("website", geo.get("website"));
|
||||||
|
data.put("business_status", geo.get("business_status"));
|
||||||
|
data.put("rating", geo.get("rating"));
|
||||||
|
data.put("rating_count", geo.get("rating_count"));
|
||||||
|
} else {
|
||||||
|
// geocode 실패한 첫 등록 케이스에서 최소한의 주소(LLM이 추출한 원시값)는 저장
|
||||||
|
Object rawAddr = restData.get("address");
|
||||||
|
if (rawAddr != null) data.put("address", rawAddr);
|
||||||
|
}
|
||||||
|
|
||||||
String restId = restaurantService.upsert(data);
|
String restId = restaurantService.upsert(data);
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ public class VideoService {
|
|||||||
VideoDetail detail = mapper.findDetail(id);
|
VideoDetail detail = mapper.findDetail(id);
|
||||||
if (detail == null) return null;
|
if (detail == null) return null;
|
||||||
List<VideoRestaurantLink> restaurants = mapper.findVideoRestaurants(id);
|
List<VideoRestaurantLink> restaurants = mapper.findVideoRestaurants(id);
|
||||||
|
if (restaurants != null) {
|
||||||
|
restaurants.forEach(r -> r.setEvaluation(JsonUtil.normalizeEvaluation(r.getEvaluation())));
|
||||||
|
}
|
||||||
detail.setRestaurants(restaurants != null ? restaurants : List.of());
|
detail.setRestaurants(restaurants != null ? restaurants : List.of());
|
||||||
return detail;
|
return detail;
|
||||||
}
|
}
|
||||||
@@ -59,6 +62,7 @@ public class VideoService {
|
|||||||
mapper.cleanupOrphanRestaurant(restaurantId);
|
mapper.cleanupOrphanRestaurant(restaurantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
public int saveVideosBatch(String channelId, List<Map<String, Object>> videos) {
|
public int saveVideosBatch(String channelId, List<Map<String, Object>> videos) {
|
||||||
Set<String> existing = new HashSet<>(mapper.getExistingVideoIds(channelId));
|
Set<String> existing = new HashSet<>(mapper.getExistingVideoIds(channelId));
|
||||||
int saved = 0;
|
int saved = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user