diff --git a/store/src/main/java/com/ktds/hi/store/biz/service/ExternalIntegrationInteractor.java b/store/src/main/java/com/ktds/hi/store/biz/service/ExternalIntegrationInteractor.java index a08fc0f..da488bc 100644 --- a/store/src/main/java/com/ktds/hi/store/biz/service/ExternalIntegrationInteractor.java +++ b/store/src/main/java/com/ktds/hi/store/biz/service/ExternalIntegrationInteractor.java @@ -33,7 +33,6 @@ import java.util.*; public class ExternalIntegrationInteractor implements ExternalIntegrationUseCase { private final ExternalPlatformPort externalPlatformPort; - private final EventPort eventPort; private final RedisTemplate redisTemplate; private final ObjectMapper objectMapper; diff --git a/store/src/main/java/com/ktds/hi/store/biz/usecase/out/ExternalPlatformPort.java b/store/src/main/java/com/ktds/hi/store/biz/usecase/out/ExternalPlatformPort.java index 0a3c8f2..b0f2f60 100644 --- a/store/src/main/java/com/ktds/hi/store/biz/usecase/out/ExternalPlatformPort.java +++ b/store/src/main/java/com/ktds/hi/store/biz/usecase/out/ExternalPlatformPort.java @@ -96,6 +96,13 @@ public interface ExternalPlatformPort { * @return 연동 해제 성공 여부 */ boolean disconnectPlatform(Long storeId, String platform); + /** + * 연동된 플랫폼 목록 조회 + * + * @param storeId 매장 ID + * @return 연동된 플랫폼 목록 + */ + List getConnectedPlatforms(Long storeId); public List> getTempReviews(Long storeId, String platform); } \ No newline at end of file diff --git a/store/src/main/java/com/ktds/hi/store/infra/gateway/ExternalPlatformAdapter.java b/store/src/main/java/com/ktds/hi/store/infra/gateway/ExternalPlatformAdapter.java index 4a04429..2757df8 100644 --- a/store/src/main/java/com/ktds/hi/store/infra/gateway/ExternalPlatformAdapter.java +++ b/store/src/main/java/com/ktds/hi/store/infra/gateway/ExternalPlatformAdapter.java @@ -18,8 +18,8 @@ import java.time.Duration; import java.util.*; /** - * 외부 플랫폼 어댑터 클래스 - * External Platform Port를 구현하여 외부 API 연동 기능을 제공 + * 외부 플랫폼 연동 어댑터 + * 각 플랫폼별 API 호출 및 Redis 저장을 담당 */ @Component @RequiredArgsConstructor @@ -30,42 +30,40 @@ public class ExternalPlatformAdapter implements ExternalPlatformPort { private final RedisTemplate redisTemplate; private final ObjectMapper objectMapper; - @Value("${external-api.naver.client-id:}") - private String naverClientId; + @Value("${external.kakao.crawler.url:http://localhost:9001}") + private String kakaoCrawlerUrl; - @Value("${external-api.naver.client-secret:}") - private String naverClientSecret; + @Value("${external.naver.crawler.url:http://localhost:9002}") + private String naverCrawlerUrl; - @Value("${external-api.kakao.api-key:}") - private String kakaoApiKey; + @Value("${external.google.crawler.url:http://localhost:9003}") + private String googleCrawlerUrl; - @Value("${external-api.google.api-key:}") - private String googleApiKey; + @Value("${external.hiorder.api.url:http://localhost:8080}") + private String hiorderApiUrl; - @Value("${external-api.hiorder.api-key:}") - private String hiorderApiKey; + // ===== 리뷰 동기화 메서드들 ===== @Override public int syncNaverReviews(Long storeId, String externalStoreId) { log.info("네이버 리뷰 동기화 시작: storeId={}, externalStoreId={}", storeId, externalStoreId); try { - // 네이버 API 호출 (Mock) + // 네이버 크롤링 서비스 호출 + String url = String.format("%s/api/naver/reviews?storeId=%s", naverCrawlerUrl, externalStoreId); + HttpHeaders headers = new HttpHeaders(); - headers.set("X-Naver-Client-Id", naverClientId); - headers.set("X-Naver-Client-Secret", naverClientSecret); + headers.setContentType(MediaType.APPLICATION_JSON); - // 실제 API 호출 로직 - // ResponseEntity response = restTemplate.exchange(...); + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity response = restTemplate.exchange(url, org.springframework.http.HttpMethod.GET, entity, String.class); - // Mock 응답 - int syncedCount = 15; // Mock 데이터 - - log.info("네이버 리뷰 동기화 완료: storeId={}, syncedCount={}", storeId, syncedCount); - return syncedCount; + return processNaverResponse(storeId, "NAVER", response.getBody()); } catch (Exception e) { - log.error("네이버 리뷰 동기화 실패: storeId={}, error={}", storeId, e.getMessage(), e); + log.error("네이버 리뷰 동기화 실패: storeId={}, externalStoreId={}, error={}", + storeId, externalStoreId, e.getMessage()); + updateSyncStatus(storeId, "NAVER", "FAILED", 0); return 0; } } @@ -75,50 +73,45 @@ public class ExternalPlatformAdapter implements ExternalPlatformPort { log.info("카카오 리뷰 동기화 시작: storeId={}, externalStoreId={}", storeId, externalStoreId); try { - // 기존 API 호출 로직 그대로 유지 ⭐ - String url = "http://kakao-review-api.20.249.191.180.nip.io/analyze"; - - Map requestBody = new HashMap<>(); - requestBody.put("store_id", storeId); - requestBody.put("days_limit", 360); - requestBody.put("max_time", 300); + // 카카오 크롤링 서비스 호출 + String url = String.format("%s/api/kakao/reviews?storeId=%s", kakaoCrawlerUrl, externalStoreId); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity> entity = new HttpEntity<>(requestBody, headers); + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity response = restTemplate.exchange(url, org.springframework.http.HttpMethod.GET, entity, String.class); - ResponseEntity response = restTemplate.postForEntity(url, entity, String.class); - - int syncedCount = parseAndStoreToRedis(storeId, "KAKAO", response.getBody()); - - return syncedCount; + return processKakaoResponse(storeId, "KAKAO", response.getBody()); } catch (Exception e) { - log.error("카카오 리뷰 동기화 실패: storeId={}, error={}", storeId, e.getMessage(), e); + log.error("카카오 리뷰 동기화 실패: storeId={}, externalStoreId={}, error={}", + storeId, externalStoreId, e.getMessage()); + updateSyncStatus(storeId, "KAKAO", "FAILED", 0); return 0; } } - - @Override public int syncGoogleReviews(Long storeId, String externalStoreId) { log.info("구글 리뷰 동기화 시작: storeId={}, externalStoreId={}", storeId, externalStoreId); try { - // 구글 Places API 호출 (Mock) - String url = "https://maps.googleapis.com/maps/api/place/details/json?place_id=" + - externalStoreId + "&fields=reviews&key=" + googleApiKey; + // 구글 크롤링 서비스 호출 + String url = String.format("%s/api/google/reviews?storeId=%s", googleCrawlerUrl, externalStoreId); - // Mock 응답 - int syncedCount = 20; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); - log.info("구글 리뷰 동기화 완료: storeId={}, syncedCount={}", storeId, syncedCount); - return syncedCount; + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity response = restTemplate.exchange(url, org.springframework.http.HttpMethod.GET, entity, String.class); + + return processGoogleResponse(storeId, "GOOGLE", response.getBody()); } catch (Exception e) { - log.error("구글 리뷰 동기화 실패: storeId={}, error={}", storeId, e.getMessage(), e); + log.error("구글 리뷰 동기화 실패: storeId={}, externalStoreId={}, error={}", + storeId, externalStoreId, e.getMessage()); + updateSyncStatus(storeId, "GOOGLE", "FAILED", 0); return 0; } } @@ -128,263 +121,248 @@ public class ExternalPlatformAdapter implements ExternalPlatformPort { log.info("하이오더 리뷰 동기화 시작: storeId={}, externalStoreId={}", storeId, externalStoreId); try { - // 하이오더 API 호출 (Mock) + // 하이오더 API 호출 + String url = String.format("%s/api/reviews?storeId=%s", hiorderApiUrl, externalStoreId); + HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", "Bearer " + hiorderApiKey); + headers.setContentType(MediaType.APPLICATION_JSON); - // Mock 응답 - int syncedCount = 8; + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity response = restTemplate.exchange(url, org.springframework.http.HttpMethod.GET, entity, String.class); - log.info("하이오더 리뷰 동기화 완료: storeId={}, syncedCount={}", storeId, syncedCount); - return syncedCount; + return processHiorderResponse(storeId, "HIORDER", response.getBody()); } catch (Exception e) { - log.error("하이오더 리뷰 동기화 실패: storeId={}, error={}", storeId, e.getMessage(), e); + log.error("하이오더 리뷰 동기화 실패: storeId={}, externalStoreId={}, error={}", + storeId, externalStoreId, e.getMessage()); + updateSyncStatus(storeId, "HIORDER", "FAILED", 0); return 0; } } + // ===== 계정 연동 메서드들 ===== + @Override public boolean connectNaverAccount(Long storeId, String username, String password) { - log.info("네이버 계정 연동 시작: storeId={}, username={}", storeId, username); - + log.info("네이버 계정 연동: storeId={}", storeId); try { - // 네이버 계정 인증 로직 (Mock) - // 실제로는 OAuth2 플로우나 ID/PW 인증 - - // Mock 성공 - boolean connected = true; - - if (connected) { - // 연동 정보 저장 - saveExternalConnection(storeId, "NAVER", username); + if (validateCredentials(username, password)) { + saveConnectionInfo(storeId, "NAVER", username); + return true; } - - log.info("네이버 계정 연동 완료: storeId={}, connected={}", storeId, connected); - return connected; - + return false; } catch (Exception e) { - log.error("네이버 계정 연동 실패: storeId={}, error={}", storeId, e.getMessage(), e); + log.error("네이버 계정 연동 실패: storeId={}, error={}", storeId, e.getMessage()); return false; } } @Override public boolean connectKakaoAccount(Long storeId, String username, String password) { - log.info("카카오 계정 연동 시작: storeId={}, username={}", storeId, username); - + log.info("카카오 계정 연동: storeId={}", storeId); try { - // 카카오 계정 인증 로직 (Mock) - boolean connected = true; - - if (connected) { - saveExternalConnection(storeId, "KAKAO", username); + if (validateCredentials(username, password)) { + saveConnectionInfo(storeId, "KAKAO", username); + return true; } - - log.info("카카오 계정 연동 완료: storeId={}, connected={}", storeId, connected); - return connected; - + return false; } catch (Exception e) { - log.error("카카오 계정 연동 실패: storeId={}, error={}", storeId, e.getMessage(), e); + log.error("카카오 계정 연동 실패: storeId={}, error={}", storeId, e.getMessage()); return false; } } @Override public boolean connectGoogleAccount(Long storeId, String username, String password) { - log.info("구글 계정 연동 시작: storeId={}, username={}", storeId, username); - + log.info("구글 계정 연동: storeId={}", storeId); try { - // 구글 계정 인증 로직 (Mock) - boolean connected = true; - - if (connected) { - saveExternalConnection(storeId, "GOOGLE", username); + if (validateCredentials(username, password)) { + saveConnectionInfo(storeId, "GOOGLE", username); + return true; } - - log.info("구글 계정 연동 완료: storeId={}, connected={}", storeId, connected); - return connected; - + return false; } catch (Exception e) { - log.error("구글 계정 연동 실패: storeId={}, error={}", storeId, e.getMessage(), e); + log.error("구글 계정 연동 실패: storeId={}, error={}", storeId, e.getMessage()); return false; } } @Override public boolean connectHiorderAccount(Long storeId, String username, String password) { - log.info("하이오더 계정 연동 시작: storeId={}, username={}", storeId, username); - + log.info("하이오더 계정 연동: storeId={}", storeId); try { - // 하이오더 계정 인증 로직 (Mock) - boolean connected = true; - - if (connected) { - saveExternalConnection(storeId, "HIORDER", username); + if (validateCredentials(username, password)) { + saveConnectionInfo(storeId, "HIORDER", username); + return true; } - - log.info("하이오더 계정 연동 완료: storeId={}, connected={}", storeId, connected); - return connected; - + return false; } catch (Exception e) { - log.error("하이오더 계정 연동 실패: storeId={}, error={}", storeId, e.getMessage(), e); + log.error("하이오더 계정 연동 실패: storeId={}, error={}", storeId, e.getMessage()); return false; } } @Override public boolean disconnectPlatform(Long storeId, String platform) { - log.info("외부 플랫폼 연동 해제 시작: storeId={}, platform={}", storeId, platform); + log.info("플랫폼 연동 해제: storeId={}, platform={}", storeId, platform); try { - // 플랫폼별 연동 해제 로직 - boolean disconnected = false; + String connectionKey = String.format("external:connection:%d:%s", storeId, platform); + redisTemplate.delete(connectionKey); - switch (platform.toUpperCase()) { - case "NAVER": - disconnected = disconnectNaverAccount(storeId); - break; - case "KAKAO": - disconnected = disconnectKakaoAccount(storeId); - break; - case "GOOGLE": - disconnected = disconnectGoogleAccount(storeId); - break; - case "HIORDER": - disconnected = disconnectHiorderAccount(storeId); - break; - default: - log.warn("지원하지 않는 플랫폼: {}", platform); - return false; - } - - if (disconnected) { - removeExternalConnection(storeId, platform); - } - - log.info("외부 플랫폼 연동 해제 완료: storeId={}, platform={}, disconnected={}", - storeId, platform, disconnected); - return disconnected; + log.info("플랫폼 연동 해제 완료: storeId={}, platform={}", storeId, platform); + return true; } catch (Exception e) { - log.error("외부 플랫폼 연동 해제 실패: storeId={}, platform={}, error={}", - storeId, platform, e.getMessage(), e); + log.error("플랫폼 연동 해제 실패: storeId={}, platform={}, error={}", + storeId, platform, e.getMessage()); return false; } } - /** - * 네이버 계정 연동 해제 - */ - private boolean disconnectNaverAccount(Long storeId) { - // 네이버 연동 해제 로직 (Mock) - return true; - } + @Override + public List getConnectedPlatforms(Long storeId) { + log.info("연동된 플랫폼 조회: storeId={}", storeId); - /** - * 카카오 계정 연동 해제 - */ - private boolean disconnectKakaoAccount(Long storeId) { - // 카카오 연동 해제 로직 (Mock) - return true; - } - - /** - * 구글 계정 연동 해제 - */ - private boolean disconnectGoogleAccount(Long storeId) { - // 구글 연동 해제 로직 (Mock) - return true; - } - - /** - * 하이오더 계정 연동 해제 - */ - private boolean disconnectHiorderAccount(Long storeId) { - // 하이오더 연동 해제 로직 (Mock) - return true; - } - - /** - * 외부 연동 정보 저장 Redis 활용 - */ - private void saveExternalConnection(Long storeId, String platform, String username) { try { - String connectionKey = String.format("external:connection:%d:%s", storeId, platform); + List connectedPlatforms = new ArrayList<>(); - Map connectionData = new HashMap<>(); - connectionData.put("storeId", storeId); - connectionData.put("platform", platform); - connectionData.put("username", username); - connectionData.put("connectedAt", System.currentTimeMillis()); - connectionData.put("isActive", true); + String pattern = String.format("external:connection:%d:*", storeId); + Set keys = redisTemplate.keys(pattern); - redisTemplate.opsForValue().set(connectionKey, connectionData, Duration.ofDays(30)); + if (keys != null) { + for (String key : keys) { + String platform = key.substring(key.lastIndexOf(':') + 1); + connectedPlatforms.add(platform.toUpperCase()); + } + } - log.info("외부 연동 정보 Redis 저장 완료: storeId={}, platform={}, username={}", - storeId, platform, username); + return connectedPlatforms; } catch (Exception e) { - log.error("외부 연동 정보 저장 실패: storeId={}, platform={}, error={}", - storeId, platform, e.getMessage()); + log.error("연동된 플랫폼 조회 실패: storeId={}, error={}", storeId, e.getMessage()); + return new ArrayList<>(); } } - /** - * 외부 연동 정보 제거 - */ - private void removeExternalConnection(Long storeId, String platform) { - // 실제로는 ExternalPlatformEntity에서 연동 정보 제거 - log.info("외부 연동 정보 제거: storeId={}, platform={}", storeId, platform); + @Override + public List> getTempReviews(Long storeId, String platform) { + try { + String pattern = String.format("external:reviews:pending:%d:%s:*", storeId, platform); + Set keys = redisTemplate.keys(pattern); + + if (keys != null && !keys.isEmpty()) { + String latestKey = keys.stream() + .max(Comparator.comparing(key -> { + try { + return Long.parseLong(key.substring(key.lastIndexOf(':') + 1)); + } catch (NumberFormatException e) { + return 0L; + } + })) + .orElse(null); + + if (latestKey != null) { + Map cacheData = (Map) redisTemplate.opsForValue().get(latestKey); + if (cacheData != null && cacheData.containsKey("reviews")) { + return (List>) cacheData.get("reviews"); + } + } + } + + return new ArrayList<>(); + + } catch (Exception e) { + log.error("Redis에서 임시 리뷰 데이터 조회 실패: storeId={}, platform={}", storeId, platform); + return new ArrayList<>(); + } } - /** - * 카카오 응답 파싱 및 Redis 저장 (새로 추가) - */ - private int parseAndStoreToRedis(Long storeId, String platform, String responseBody) { + // ===== Private Helper Methods ===== + + private int processNaverResponse(Long storeId, String platform, String responseBody) { try { - log.info("카카오 API 응답: {}", responseBody); - - // JSON 파싱 - JsonNode rootNode = objectMapper.readTree(responseBody); - JsonNode reviewsNode = rootNode.get("reviews"); - - if (reviewsNode == null || !reviewsNode.isArray()) { + if (responseBody == null || responseBody.trim().isEmpty()) { + log.warn("네이버 응답이 비어있음: storeId={}", storeId); return 0; } - // 리뷰 데이터 변환 + JsonNode root = objectMapper.readTree(responseBody); + List> parsedReviews = new ArrayList<>(); - for (JsonNode reviewNode : reviewsNode) { - Map review = new HashMap<>(); - review.put("reviewId", reviewNode.get("review_id").asText()); - review.put("content", reviewNode.get("content").asText()); - review.put("rating", reviewNode.get("rating").asInt()); - review.put("authorName", reviewNode.get("author_name").asText()); - review.put("reviewDate", reviewNode.get("review_date").asText()); - review.put("platform", platform); - parsedReviews.add(review); + + // 네이버 크롤링 응답 처리 + if (root.has("success") && root.get("success").asBoolean() && root.has("data")) { + JsonNode data = root.get("data"); + if (data.has("reviews")) { + JsonNode reviews = data.get("reviews"); + for (JsonNode reviewNode : reviews) { + Map review = new HashMap<>(); + review.put("reviewId", reviewNode.path("reviewId").asText()); + review.put("rating", reviewNode.path("rating").asDouble(0.0)); + review.put("content", reviewNode.path("content").asText()); + review.put("reviewerName", reviewNode.path("reviewerName").asText()); + review.put("createdAt", reviewNode.path("createdAt").asText()); + review.put("platform", platform); + parsedReviews.add(review); + } + } } if (!parsedReviews.isEmpty()) { - // Redis에 저장 (TTL: 24시간) - String redisKey = String.format("external:reviews:pending:%d:%s:%d", - storeId, platform, System.currentTimeMillis()); - - Map cacheData = new HashMap<>(); - cacheData.put("storeId", storeId); - cacheData.put("platform", platform); - cacheData.put("reviews", parsedReviews); - cacheData.put("status", "PENDING"); - cacheData.put("createdAt", System.currentTimeMillis()); - cacheData.put("retryCount", 0); - - redisTemplate.opsForValue().set(redisKey, cacheData, Duration.ofHours(24)); - - log.info("Redis에 리뷰 데이터 저장 완료: key={}, count={}", redisKey, parsedReviews.size()); - - // 동기화 상태 업데이트 + saveToRedis(storeId, platform, parsedReviews); updateSyncStatus(storeId, platform, "SUCCESS", parsedReviews.size()); + + log.info("Redis에 리뷰 데이터 저장 완료: key={}, count={}", + String.format("external:reviews:pending:%d:%s:%d", storeId, platform, System.currentTimeMillis()), + parsedReviews.size()); + } + + return parsedReviews.size(); + + } catch (Exception e) { + log.error("네이버 응답 파싱 및 Redis 저장 실패: {}", e.getMessage()); + updateSyncStatus(storeId, platform, "FAILED", 0); + return 0; + } + } + + private int processKakaoResponse(Long storeId, String platform, String responseBody) { + try { + if (responseBody == null || responseBody.trim().isEmpty()) { + log.warn("카카오 응답이 비어있음: storeId={}", storeId); + return 0; + } + + JsonNode root = objectMapper.readTree(responseBody); + + List> parsedReviews = new ArrayList<>(); + + // 카카오 크롤링 응답 처리 + if (root.has("success") && root.get("success").asBoolean() && root.has("data")) { + JsonNode data = root.get("data"); + if (data.has("reviews")) { + JsonNode reviews = data.get("reviews"); + for (JsonNode reviewNode : reviews) { + Map review = new HashMap<>(); + review.put("reviewId", reviewNode.path("reviewId").asText()); + review.put("rating", reviewNode.path("rating").asDouble(0.0)); + review.put("content", reviewNode.path("content").asText()); + review.put("reviewerName", reviewNode.path("reviewerName").asText()); + review.put("createdAt", reviewNode.path("createdAt").asText()); + review.put("platform", platform); + parsedReviews.add(review); + } + } + } + + if (!parsedReviews.isEmpty()) { + saveToRedis(storeId, platform, parsedReviews); + updateSyncStatus(storeId, platform, "SUCCESS", parsedReviews.size()); + + log.info("Redis에 리뷰 데이터 저장 완료: key={}, count={}", + String.format("external:reviews:pending:%d:%s:%d", storeId, platform, System.currentTimeMillis()), + parsedReviews.size()); } return parsedReviews.size(); @@ -396,6 +374,117 @@ public class ExternalPlatformAdapter implements ExternalPlatformPort { } } + private int processGoogleResponse(Long storeId, String platform, String responseBody) { + try { + if (responseBody == null || responseBody.trim().isEmpty()) { + log.warn("구글 응답이 비어있음: storeId={}", storeId); + return 0; + } + + JsonNode root = objectMapper.readTree(responseBody); + + List> parsedReviews = new ArrayList<>(); + + // 구글 크롤링 응답 처리 + if (root.has("success") && root.get("success").asBoolean() && root.has("data")) { + JsonNode data = root.get("data"); + if (data.has("reviews")) { + JsonNode reviews = data.get("reviews"); + for (JsonNode reviewNode : reviews) { + Map review = new HashMap<>(); + review.put("reviewId", reviewNode.path("reviewId").asText()); + review.put("rating", reviewNode.path("rating").asDouble(0.0)); + review.put("content", reviewNode.path("content").asText()); + review.put("reviewerName", reviewNode.path("reviewerName").asText()); + review.put("createdAt", reviewNode.path("createdAt").asText()); + review.put("platform", platform); + parsedReviews.add(review); + } + } + } + + if (!parsedReviews.isEmpty()) { + saveToRedis(storeId, platform, parsedReviews); + updateSyncStatus(storeId, platform, "SUCCESS", parsedReviews.size()); + } + + return parsedReviews.size(); + + } catch (Exception e) { + log.error("구글 응답 파싱 실패: storeId={}, error={}", storeId, e.getMessage()); + updateSyncStatus(storeId, platform, "FAILED", 0); + return 0; + } + } + + private int processHiorderResponse(Long storeId, String platform, String responseBody) { + try { + if (responseBody == null || responseBody.trim().isEmpty()) { + log.warn("하이오더 응답이 비어있음: storeId={}", storeId); + return 0; + } + + JsonNode root = objectMapper.readTree(responseBody); + + List> parsedReviews = new ArrayList<>(); + + // 하이오더 API 응답 처리 + if (root.has("success") && root.get("success").asBoolean() && root.has("data")) { + JsonNode data = root.get("data"); + for (JsonNode reviewNode : data) { + Map review = new HashMap<>(); + review.put("reviewId", reviewNode.path("reviewId").asText()); + review.put("rating", reviewNode.path("rating").asDouble(0.0)); + review.put("content", reviewNode.path("comment").asText()); + review.put("reviewerName", reviewNode.path("customerName").asText()); + review.put("createdAt", reviewNode.path("reviewDate").asText()); + review.put("platform", platform); + parsedReviews.add(review); + } + } + + if (!parsedReviews.isEmpty()) { + saveToRedis(storeId, platform, parsedReviews); + updateSyncStatus(storeId, platform, "SUCCESS", parsedReviews.size()); + } + + return parsedReviews.size(); + + } catch (Exception e) { + log.error("하이오더 응답 파싱 실패: storeId={}, error={}", storeId, e.getMessage()); + updateSyncStatus(storeId, platform, "FAILED", 0); + return 0; + } + } + + private boolean validateCredentials(String username, String password) { + return username != null && !username.trim().isEmpty() && + password != null && !password.trim().isEmpty(); + } + + private void saveToRedis(Long storeId, String platform, List> reviews) { + try { + long timestamp = System.currentTimeMillis(); + String redisKey = String.format("external:reviews:pending:%d:%s:%d", storeId, platform, timestamp); + + Map cacheData = new HashMap<>(); + cacheData.put("storeId", storeId); + cacheData.put("platform", platform); + cacheData.put("reviews", reviews); + cacheData.put("timestamp", timestamp); + cacheData.put("status", "PENDING"); + cacheData.put("retryCount", 0); + + redisTemplate.opsForValue().set(redisKey, cacheData, Duration.ofDays(1)); + + log.info("Redis에 리뷰 데이터 저장 완료: key={}, count={}", redisKey, reviews.size()); + + } catch (Exception e) { + log.error("Redis 저장 실패: storeId={}, platform={}, error={}", + storeId, platform, e.getMessage()); + } + } + /** * 동기화 상태 Redis에 저장 */ @@ -417,35 +506,24 @@ public class ExternalPlatformAdapter implements ExternalPlatformPort { } } - @Override - public List> getTempReviews(Long storeId, String platform) { + private void saveConnectionInfo(Long storeId, String platform, String username) { try { - // Redis에서 최신 pending 데이터 조회 - String pattern = String.format("external:reviews:pending:%d:%s:*", storeId, platform); - Set keys = redisTemplate.keys(pattern); + String connectionKey = String.format("external:connection:%d:%s", storeId, platform); - if (keys != null && !keys.isEmpty()) { - // 가장 최신 키 선택 (타임스탬프 기준) - String latestKey = keys.stream() - .max(Comparator.comparing(key -> Long.parseLong(key.substring(key.lastIndexOf(':') + 1)))) - .orElse(null); + Map connectionData = new HashMap<>(); + connectionData.put("storeId", storeId); + connectionData.put("platform", platform); + connectionData.put("username", username); + connectionData.put("connectedAt", System.currentTimeMillis()); + connectionData.put("status", "CONNECTED"); - if (latestKey != null) { - Map cacheData = (Map) redisTemplate.opsForValue().get(latestKey); - if (cacheData != null) { - return (List>) cacheData.get("reviews"); - } - } - } + redisTemplate.opsForValue().set(connectionKey, connectionData, Duration.ofDays(30)); - return new ArrayList<>(); + log.info("연동 정보 저장 완료: storeId={}, platform={}", storeId, platform); } catch (Exception e) { - log.error("Redis에서 임시 리뷰 데이터 조회 실패: storeId={}, platform={}", storeId, platform); - return new ArrayList<>(); + log.error("연동 정보 저장 실패: storeId={}, platform={}, error={}", + storeId, platform, e.getMessage()); } } - - - } \ No newline at end of file