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
16 changes: 8 additions & 8 deletions common/jpa/src/main/java/com/kustaurant/map/MapConstantsV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ private MapConstantsV2() {}
List.of(
new CoordinateV2(37.5401732,127.062852),
new CoordinateV2(37.5378977,127.0696049),
new CoordinateV2(37.5422127,127.071636), //
new CoordinateV2(37.5428253,127.0710213), //
new CoordinateV2(37.5422656,127.0707644), //
new CoordinateV2(37.5441701,127.0651452) //
new CoordinateV2(37.5422127,127.071636),
new CoordinateV2(37.5428253,127.0710213),
new CoordinateV2(37.5422656,127.0707644),
new CoordinateV2(37.5441701,127.0651452)
)
),
new ZonePolygon( // 중문~어대
com.kustaurant.map.ZoneType.MIDDLE_TO_PARK,
ZoneType.MIDDLE_TO_PARK,
List.of(
new CoordinateV2(37.5422127,127.071636),
new CoordinateV2(37.5428253,127.0710213),
Expand All @@ -32,7 +32,7 @@ private MapConstantsV2() {}
)
),
new ZonePolygon( // 후문
com.kustaurant.map.ZoneType.BACK_GATE,
ZoneType.BACK_GATE,
List.of(
new CoordinateV2(37.5445367,127.0728555),
new CoordinateV2(37.5444815,127.0731477),
Expand All @@ -48,7 +48,7 @@ private MapConstantsV2() {}
)
),
new ZonePolygon( // 정문
com.kustaurant.map.ZoneType.FRONT_GATE,
ZoneType.FRONT_GATE,
List.of(
new CoordinateV2(37.5397225,127.0708216),
new CoordinateV2(37.5385701,127.0750329),
Expand All @@ -66,7 +66,7 @@ private MapConstantsV2() {}
)
),
new ZonePolygon( // 구의역
com.kustaurant.map.ZoneType.GUI_STATION,
ZoneType.GUI_STATION,
List.of(
new CoordinateV2(37.536197,127.0837349),
new CoordinateV2(37.5370672,127.0876883),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class RestaurantCrawlController {
private final ZoneTestCrawler testCrawler;
private final ZoneCrawlJobService zoneCrawlJobService;

// 1. 단건 크롤 & 저장
// 1. 단건 크롤 & 저장 .
@PostMapping({"/crawl-one"})
public RestaurantRaw crawlOne(@RequestBody @Valid RestaurantCrawlRequest request) {
return crawler.crawl(request.normalizedPlaceUrl());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
public class RestaurantSingleCrawler {

private static final int MAX_RETRIES = 2;
private static final String NAVER_PLACE_NOT_FOUND_TEXT = "요청하신 페이지를 찾을 수 없습니다.";

private final PlaywrightManager playwrightManager;
private final RestaurantPageDriver pageDriver;
Expand Down Expand Up @@ -156,9 +157,10 @@ private RestaurantRaw crawlInternal(String placeUrl, boolean analyzeMode) {
log.warn("home html 캡처 실패. sourcePlaceId={}, placeId={}", placeId, placeUrl);
}
log.info(
" === 네이버플레이스 분석 완료. sourcePlaceId={}, placeName={}, category={}, restaurantAddress={}, phone={}, lat={}, lng={}, menuCount={}",
" === 네이버플레이스 분석 완료. sourcePlaceId={}, placeName={}, category={}, restaurantAddress={}, phone={}, lat={}, lng={}, zoneType={}, zoneDescription={}, menuCount={}",
result.sourcePlaceId(), result.placeName(), result.category(), result.restaurantAddress(),
result.phoneNumber(), result.latitude(), result.longitude(),
zoneType, zoneType == null ? null : zoneType.getDescription(),
result.menus() == null ? 0 : result.menus().size()
);
} else {
Expand All @@ -177,6 +179,7 @@ private RestaurantRaw crawlInternal(String placeUrl, boolean analyzeMode) {
private boolean shouldRetryWithBackoff(RestaurantRaw result, int attempt) {
if (attempt > MAX_RETRIES) return false;
if (result == null) return true;
if (isNaverPlaceNotFoundResult(result)) return false;

boolean noBasic = isBlank(result.placeName()) && isBlank(result.category())
&& isBlank(result.restaurantAddress()) && isBlank(result.phoneNumber());
Expand All @@ -185,6 +188,11 @@ private boolean shouldRetryWithBackoff(RestaurantRaw result, int attempt) {
return (noBasic && menuCount == 0) || missingCoordinates;
}

private boolean isNaverPlaceNotFoundResult(RestaurantRaw result) {
String address = result.restaurantAddress();
return address != null && address.contains(NAVER_PLACE_NOT_FOUND_TEXT);
}

private long retryBackoffMillis(int attempt) {
if (attempt == 1) return 30_000;
return 60_000;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@RequiredArgsConstructor
public class RestaurantRawSaveService {
private static final int IMAGE_URL_MAX_LENGTH = 512;

private final RestaurantCrawlerClient crawlerClient;
private final RestaurantCrawlRawRepository rawRepository;
private final RestaurantMenuRawRepository menuRawRepository;
private final PlatformTransactionManager transactionManager;

@Transactional
public RestaurantCrawlResponse crawlAndSave(String placeId) {
Expand All @@ -39,6 +45,7 @@ public RestaurantCrawlResponse saveResult(RestaurantRaw result, ZoneType crawlSc
deleteExistingRawByPlaceIdOrUrl(result);

ZoneType zoneType = crawlScope != null ? crawlScope : (result.crawlScope() == null ? ZoneType.OUT_OF_ZONE : result.crawlScope());
String normalizedImageUrl = normalizeImageUrl(result.imageUrl());
RestaurantCrawlRawEntity rawEntity = rawRepository.save(
RestaurantCrawlRawEntity.success(
result.sourcePlaceId(),
Expand All @@ -49,7 +56,7 @@ public RestaurantCrawlResponse saveResult(RestaurantRaw result, ZoneType crawlSc
result.phoneNumber(),
result.latitude(),
result.longitude(),
result.imageUrl(),
normalizedImageUrl,
zoneType
)
);
Expand Down Expand Up @@ -83,7 +90,6 @@ public RestaurantCrawlResponse saveResult(RestaurantRaw result, ZoneType crawlSc
);
}

@Transactional
public BatchSaveResult saveResultsBatch(List<RestaurantRaw> results, ZoneType crawlScope) {
if (results == null || results.isEmpty()) {
return new BatchSaveResult(0, 0, List.of());
Expand All @@ -92,14 +98,16 @@ public BatchSaveResult saveResultsBatch(List<RestaurantRaw> results, ZoneType cr
int savedCount = 0;
int failedCount = 0;
Set<String> failedPlaceIds = new LinkedHashSet<>();
TransactionTemplate requiresNewTx = new TransactionTemplate(transactionManager);
requiresNewTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

for (RestaurantRaw result : results) {
String placeId = result == null ? null : result.sourcePlaceId();
try {
if (result == null) {
throw new IllegalArgumentException("naver place crawl result is null");
}
saveResult(result, crawlScope);
requiresNewTx.executeWithoutResult(status -> saveResult(result, crawlScope));
savedCount++;
} catch (Exception e) {
failedCount++;
Expand Down Expand Up @@ -162,6 +170,13 @@ private String defaultIfBlank(String value, String fallback) {
return (value == null || value.isBlank()) ? fallback : value;
}

private String normalizeImageUrl(String imageUrl) {
if (imageUrl == null) {
return null;
}
return imageUrl.length() > IMAGE_URL_MAX_LENGTH ? null : imageUrl;
}

public record BatchSaveResult(
int savedCount,
int failedCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto.RestaurantSyncCandidateActionResponse;
import com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto.RestaurantSyncCandidateApproveRequest;
import com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto.RestaurantSyncCandidateResponse;
import com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto.ClosedCandidateAutoProcessResponse;
import com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto.ClosedCandidateAutoProcessJobStartResponse;
import com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto.ClosedCandidateAutoProcessJobStatusResponse;
import com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto.NewCandidateAutoApproveResponse;
import com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto.RestaurantSyncRunRequest;
import com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto.RestaurantSyncRunResponse;
import com.kustaurant.kustaurant.admin.RestaurantSync.domain.SyncCandidateStatus;
Expand Down Expand Up @@ -64,4 +68,34 @@ public RestaurantSyncCandidateActionResponse reject(
) {
return restaurantSyncService.reject(candidateId, principal == null ? "UNKNOWN_ADMIN" : principal.getName());
}

@PreAuthorize("isAuthenticated() and hasRole('ROLE_ADMIN')")
@PostMapping("/candidates/closed/auto-process")
public ClosedCandidateAutoProcessResponse autoProcessClosedCandidates(Principal principal) {
return restaurantSyncService.autoProcessClosedCandidates(
principal == null ? "UNKNOWN_ADMIN" : principal.getName()
);
}

@PreAuthorize("isAuthenticated() and hasRole('ROLE_ADMIN')")
@PostMapping("/candidates/closed/auto-process/jobs")
public ClosedCandidateAutoProcessJobStartResponse startClosedAutoProcessJob(Principal principal) {
return restaurantSyncService.startClosedAutoProcessJob(
principal == null ? "UNKNOWN_ADMIN" : principal.getName()
);
}

@PreAuthorize("isAuthenticated() and hasRole('ROLE_ADMIN')")
@GetMapping("/candidates/closed/auto-process/jobs/{jobId}")
public ClosedCandidateAutoProcessJobStatusResponse getClosedAutoProcessJobStatus(@PathVariable String jobId) {
return restaurantSyncService.getClosedAutoProcessJobStatus(jobId);
}

@PreAuthorize("isAuthenticated() and hasRole('ROLE_ADMIN')")
@PostMapping("/candidates/new/auto-approve")
public NewCandidateAutoApproveResponse autoApproveNewCandidates(Principal principal) {
return restaurantSyncService.autoApproveNewCandidates(
principal == null ? "UNKNOWN_ADMIN" : principal.getName()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto;

public record ClosedCandidateAutoProcessJobStartResponse(
String jobId
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto;

public record ClosedCandidateAutoProcessJobStatusResponse(
String jobId,
String status,
int total,
int processed,
int autoClosedCount,
int recrawledCount,
int failedCount,
boolean done
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto;

public record ClosedCandidateAutoProcessResponse(
int totalPendingClosed,
int autoClosedCount,
int recrawledCount,
int failedCount
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.kustaurant.kustaurant.admin.RestaurantSync.controller.dto;

public record NewCandidateAutoApproveResponse(
int totalPendingNew,
int approvedCount,
int failedCount
) {
}
Loading
Loading