store update

This commit is contained in:
youbeen 2025-06-12 14:29:03 +09:00
parent fc43d07e47
commit cb1ab34a39
7 changed files with 582 additions and 57 deletions

View File

@ -0,0 +1,251 @@
package com.ktds.hi.store.biz.service;
import com.ktds.hi.store.biz.usecase.in.ExternalIntegrationUseCase;
import com.ktds.hi.store.biz.usecase.out.ExternalPlatformPort;
import com.ktds.hi.store.biz.usecase.out.EventPort;
import com.ktds.hi.store.infra.dto.ExternalSyncRequest;
import com.ktds.hi.store.infra.dto.ExternalSyncResponse;
import com.ktds.hi.store.infra.dto.ExternalConnectRequest;
import com.ktds.hi.store.infra.dto.ExternalConnectResponse;
import com.ktds.hi.common.exception.BusinessException;
import com.ktds.hi.common.exception.ExternalServiceException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 외부 연동 인터랙터 클래스
* 외부 플랫폼 연동 비즈니스 로직을 구현
*/
@Service
@RequiredArgsConstructor
@Slf4j
@Transactional
public class ExternalIntegrationInteractor implements ExternalIntegrationUseCase {
private final ExternalPlatformPort externalPlatformPort;
private final EventPort eventPort;
@Override
public ExternalSyncResponse syncReviews(Long storeId, ExternalSyncRequest request) {
log.info("외부 플랫폼 리뷰 동기화 시작: storeId={}, platform={}", storeId, request.getPlatform());
try {
validateSyncRequest(storeId, request);
int syncedCount = 0;
// 플랫폼별 리뷰 동기화
switch (request.getPlatform().toUpperCase()) {
case "NAVER":
syncedCount = externalPlatformPort.syncNaverReviews(storeId, request.getExternalStoreId());
break;
case "KAKAO":
syncedCount = externalPlatformPort.syncKakaoReviews(storeId, request.getExternalStoreId());
break;
case "GOOGLE":
syncedCount = externalPlatformPort.syncGoogleReviews(storeId, request.getExternalStoreId());
break;
case "HIORDER":
syncedCount = externalPlatformPort.syncHiorderReviews(storeId, request.getExternalStoreId());
break;
default:
throw new BusinessException("지원하지 않는 플랫폼입니다: " + request.getPlatform());
}
// 동기화 이벤트 발행
publishSyncEvent(storeId, request.getPlatform(), syncedCount);
log.info("외부 플랫폼 리뷰 동기화 완료: storeId={}, platform={}, syncedCount={}",
storeId, request.getPlatform(), syncedCount);
return ExternalSyncResponse.builder()
.success(true)
.message(String.format("%s 플랫폼에서 %d개의 리뷰를 성공적으로 동기화했습니다",
request.getPlatform(), syncedCount))
.syncedCount(syncedCount)
.build();
} catch (Exception e) {
log.error("외부 플랫폼 리뷰 동기화 실패: storeId={}, platform={}, error={}",
storeId, request.getPlatform(), e.getMessage(), e);
throw new ExternalServiceException(request.getPlatform(),
"리뷰 동기화 중 오류가 발생했습니다: " + e.getMessage());
}
}
@Override
public ExternalConnectResponse connectPlatform(Long storeId, ExternalConnectRequest request) {
log.info("외부 플랫폼 계정 연동 시작: storeId={}, platform={}", storeId, request.getPlatform());
try {
validateConnectRequest(storeId, request);
boolean connected = false;
// 플랫폼별 계정 연동
switch (request.getPlatform().toUpperCase()) {
case "NAVER":
connected = externalPlatformPort.connectNaverAccount(storeId,
request.getUsername(), request.getPassword());
break;
case "KAKAO":
connected = externalPlatformPort.connectKakaoAccount(storeId,
request.getUsername(), request.getPassword());
break;
case "GOOGLE":
connected = externalPlatformPort.connectGoogleAccount(storeId,
request.getUsername(), request.getPassword());
break;
case "HIORDER":
connected = externalPlatformPort.connectHiorderAccount(storeId,
request.getUsername(), request.getPassword());
break;
default:
throw new BusinessException("지원하지 않는 플랫폼입니다: " + request.getPlatform());
}
if (connected) {
// 연동 성공 이벤트 발행
publishConnectEvent(storeId, request.getPlatform());
log.info("외부 플랫폼 계정 연동 완료: storeId={}, platform={}", storeId, request.getPlatform());
return ExternalConnectResponse.builder()
.success(true)
.message(request.getPlatform() + " 계정이 성공적으로 연동되었습니다")
.build();
} else {
throw new ExternalServiceException(request.getPlatform(), "계정 인증에 실패했습니다");
}
} catch (Exception e) {
log.error("외부 플랫폼 계정 연동 실패: storeId={}, platform={}, error={}",
storeId, request.getPlatform(), e.getMessage(), e);
return ExternalConnectResponse.builder()
.success(false)
.message("계정 연동에 실패했습니다: " + e.getMessage())
.build();
}
}
@Override
public ExternalConnectResponse disconnectPlatform(Long storeId, String platform) {
log.info("외부 플랫폼 연동 해제: storeId={}, platform={}", storeId, platform);
try {
// 연동 해제 로직 구현
boolean disconnected = externalPlatformPort.disconnectPlatform(storeId, platform);
if (disconnected) {
// 연동 해제 이벤트 발행
publishDisconnectEvent(storeId, platform);
return ExternalConnectResponse.builder()
.success(true)
.message(platform + " 플랫폼 연동이 해제되었습니다")
.build();
} else {
throw new ExternalServiceException(platform, "연동 해제에 실패했습니다");
}
} catch (Exception e) {
log.error("외부 플랫폼 연동 해제 실패: storeId={}, platform={}, error={}",
storeId, platform, e.getMessage(), e);
return ExternalConnectResponse.builder()
.success(false)
.message("연동 해제에 실패했습니다: " + e.getMessage())
.build();
}
}
@Override
@Transactional(readOnly = true)
public ExternalConnectResponse getConnectedPlatforms(Long storeId) {
log.info("연동된 플랫폼 목록 조회: storeId={}", storeId);
try {
// 연동된 플랫폼 목록 조회 로직
// 실제로는 ExternalPlatformEntity에서 조회
return ExternalConnectResponse.builder()
.success(true)
.message("연동된 플랫폼 목록을 조회했습니다")
.build();
} catch (Exception e) {
log.error("연동된 플랫폼 목록 조회 실패: storeId={}, error={}", storeId, e.getMessage(), e);
return ExternalConnectResponse.builder()
.success(false)
.message("플랫폼 목록 조회에 실패했습니다: " + e.getMessage())
.build();
}
}
// Private helper methods
private void validateSyncRequest(Long storeId, ExternalSyncRequest request) {
if (storeId == null) {
throw new BusinessException("매장 ID는 필수입니다");
}
if (request.getPlatform() == null || request.getPlatform().trim().isEmpty()) {
throw new BusinessException("플랫폼 정보는 필수입니다");
}
if (request.getExternalStoreId() == null || request.getExternalStoreId().trim().isEmpty()) {
throw new BusinessException("외부 매장 ID는 필수입니다");
}
}
private void validateConnectRequest(Long storeId, ExternalConnectRequest request) {
if (storeId == null) {
throw new BusinessException("매장 ID는 필수입니다");
}
if (request.getPlatform() == null || request.getPlatform().trim().isEmpty()) {
throw new BusinessException("플랫폼 정보는 필수입니다");
}
if (request.getUsername() == null || request.getUsername().trim().isEmpty()) {
throw new BusinessException("사용자명은 필수입니다");
}
if (request.getPassword() == null || request.getPassword().trim().isEmpty()) {
throw new BusinessException("비밀번호는 필수입니다");
}
}
private void publishSyncEvent(Long storeId, String platform, int syncedCount) {
try {
// 동기화 이벤트 발행 로직
log.info("동기화 이벤트 발행: storeId={}, platform={}, syncedCount={}",
storeId, platform, syncedCount);
} catch (Exception e) {
log.warn("동기화 이벤트 발행 실패: {}", e.getMessage());
}
}
private void publishConnectEvent(Long storeId, String platform) {
try {
// 연동 이벤트 발행 로직
log.info("연동 이벤트 발행: storeId={}, platform={}", storeId, platform);
} catch (Exception e) {
log.warn("연동 이벤트 발행 실패: {}", e.getMessage());
}
}
private void publishDisconnectEvent(Long storeId, String platform) {
try {
// 연동 해제 이벤트 발행 로직
log.info("연동 해제 이벤트 발행: storeId={}, platform={}", storeId, platform);
} catch (Exception e) {
log.warn("연동 해제 이벤트 발행 실패: {}", e.getMessage());
}
}
}

View File

@ -0,0 +1,4 @@
package com.ktds.hi.store.biz.service;
public class ExternalIntegrationUseCase {
}

View File

@ -0,0 +1,33 @@
package com.ktds.hi.store.biz.usecase.in;
import com.ktds.hi.store.infra.dto.ExternalSyncRequest;
import com.ktds.hi.store.infra.dto.ExternalSyncResponse;
import com.ktds.hi.store.infra.dto.ExternalConnectRequest;
import com.ktds.hi.store.infra.dto.ExternalConnectResponse;
/**
* 외부 연동 유스케이스 인터페이스
* 외부 플랫폼 연동 관련 비즈니스 로직을 정의
*/
public interface ExternalIntegrationUseCase {
/**
* 외부 플랫폼 리뷰 동기화
*/
ExternalSyncResponse syncReviews(Long storeId, ExternalSyncRequest request);
/**
* 외부 플랫폼 계정 연동
*/
ExternalConnectResponse connectPlatform(Long storeId, ExternalConnectRequest request);
/**
* 외부 플랫폼 연동 해제
*/
ExternalConnectResponse disconnectPlatform(Long storeId, String platform);
/**
* 연동된 플랫폼 목록 조회
*/
ExternalConnectResponse getConnectedPlatforms(Long storeId);
}

View File

@ -0,0 +1,96 @@
package com.ktds.hi.store.biz.usecase.out;
/**
* 외부 플랫폼 포트 인터페이스
* 외부 플랫폼 연동 기능을 정의
*
* @author 하이오더 개발팀
* @version 1.0.0
*/
public interface ExternalPlatformPort {
/**
* 네이버 리뷰 동기화
*
* @param storeId 매장 ID
* @param externalStoreId 외부 매장 ID
* @return 동기화된 리뷰
*/
int syncNaverReviews(Long storeId, String externalStoreId);
/**
* 카카오 리뷰 동기화
*
* @param storeId 매장 ID
* @param externalStoreId 외부 매장 ID
* @return 동기화된 리뷰
*/
int syncKakaoReviews(Long storeId, String externalStoreId);
/**
* 구글 리뷰 동기화
*
* @param storeId 매장 ID
* @param externalStoreId 외부 매장 ID
* @return 동기화된 리뷰
*/
int syncGoogleReviews(Long storeId, String externalStoreId);
/**
* 하이오더 리뷰 동기화
*
* @param storeId 매장 ID
* @param externalStoreId 외부 매장 ID
* @return 동기화된 리뷰
*/
int syncHiorderReviews(Long storeId, String externalStoreId);
/**
* 네이버 계정 연동
*
* @param storeId 매장 ID
* @param username 사용자명
* @param password 비밀번호
* @return 연동 성공 여부
*/
boolean connectNaverAccount(Long storeId, String username, String password);
/**
* 카카오 계정 연동
*
* @param storeId 매장 ID
* @param username 사용자명
* @param password 비밀번호
* @return 연동 성공 여부
*/
boolean connectKakaoAccount(Long storeId, String username, String password);
/**
* 구글 계정 연동
*
* @param storeId 매장 ID
* @param username 사용자명
* @param password 비밀번호
* @return 연동 성공 여부
*/
boolean connectGoogleAccount(Long storeId, String username, String password);
/**
* 하이오더 계정 연동
*
* @param storeId 매장 ID
* @param username 사용자명
* @param password 비밀번호
* @return 연동 성공 여부
*/
boolean connectHiorderAccount(Long storeId, String username, String password);
/**
* 외부 플랫폼 연동 해제
*
* @param storeId 매장 ID
* @param platform 플랫폼명 (NAVER, KAKAO, GOOGLE, HIORDER)
* @return 연동 해제 성공 여부
*/
boolean disconnectPlatform(Long storeId, String platform);
}

View File

@ -0,0 +1,32 @@
package com.ktds.hi.store.infra.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* 외부 플랫폼 계정 연동 요청 DTO
*/
@Getter
@Setter
@NoArgsConstructor
@Schema(description = "외부 플랫폼 계정 연동 요청")
public class ExternalConnectRequest {
@NotBlank(message = "플랫폼 정보는 필수입니다")
@Schema(description = "플랫폼 타입", example = "NAVER", allowableValues = {"NAVER", "KAKAO", "GOOGLE", "HIORDER"})
private String platform;
@NotBlank(message = "사용자명은 필수입니다")
@Schema(description = "외부 플랫폼 사용자명", example = "store_owner@example.com")
private String username;
@NotBlank(message = "비밀번호는 필수입니다")
@Schema(description = "외부 플랫폼 비밀번호", example = "password123")
private String password;
@Schema(description = "추가 인증 정보", example = "api_key_or_token")
private String additionalAuth;
}

View File

@ -0,0 +1,34 @@
package com.ktds.hi.store.infra.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* 외부 플랫폼 동기화 요청 DTO
*/
@Getter
@Setter
@NoArgsConstructor
@Schema(description = "외부 플랫폼 동기화 요청")
public class ExternalSyncRequest {
@NotBlank(message = "플랫폼 정보는 필수입니다")
@Schema(description = "플랫폼 타입", example = "NAVER", allowableValues = {"NAVER", "KAKAO", "GOOGLE", "HIORDER"})
private String platform;
@NotBlank(message = "외부 매장 ID는 필수입니다")
@Schema(description = "외부 플랫폼의 매장 ID", example = "naver_store_12345")
private String externalStoreId;
@Schema(description = "동기화 옵션", example = "FULL")
private String syncOption = "FULL"; // FULL, INCREMENTAL
@Schema(description = "시작 날짜", example = "2024-01-01")
private String startDate;
@Schema(description = "종료 날짜", example = "2024-12-31")
private String endDate;
}

View File

@ -4,15 +4,10 @@ import com.ktds.hi.store.biz.usecase.out.ExternalPlatformPort;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
/**
* 외부 플랫폼 어댑터 클래스
* External Platform Port를 구현하여 외부 API 연동 기능을 제공
@ -92,7 +87,7 @@ public class ExternalPlatformAdapter implements ExternalPlatformPort {
try {
// 구글 Places API 호출 (Mock)
String url = "https://maps.googleapis.com/maps/api/place/details/json?place_id=" +
externalStoreId + "&fields=reviews&key=" + googleApiKey;
externalStoreId + "&fields=reviews&key=" + googleApiKey;
// Mock 응답
int syncedCount = 20;
@ -215,6 +210,79 @@ public class ExternalPlatformAdapter implements ExternalPlatformPort {
}
}
@Override
public boolean disconnectPlatform(Long storeId, String platform) {
log.info("외부 플랫폼 연동 해제 시작: storeId={}, platform={}", storeId, platform);
try {
// 플랫폼별 연동 해제 로직
boolean disconnected = false;
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;
} catch (Exception e) {
log.error("외부 플랫폼 연동 해제 실패: storeId={}, platform={}, error={}",
storeId, platform, e.getMessage(), e);
return false;
}
}
/**
* 네이버 계정 연동 해제
*/
private boolean disconnectNaverAccount(Long storeId) {
// 네이버 연동 해제 로직 (Mock)
return true;
}
/**
* 카카오 계정 연동 해제
*/
private boolean disconnectKakaoAccount(Long storeId) {
// 카카오 연동 해제 로직 (Mock)
return true;
}
/**
* 구글 계정 연동 해제
*/
private boolean disconnectGoogleAccount(Long storeId) {
// 구글 연동 해제 로직 (Mock)
return true;
}
/**
* 하이오더 계정 연동 해제
*/
private boolean disconnectHiorderAccount(Long storeId) {
// 하이오더 연동 해제 로직 (Mock)
return true;
}
/**
* 외부 연동 정보 저장
*/
@ -222,5 +290,12 @@ public class ExternalPlatformAdapter implements ExternalPlatformPort {
// 실제로는 ExternalPlatformEntity에 연동 정보 저장
log.info("외부 연동 정보 저장: storeId={}, platform={}, username={}", storeId, platform, username);
}
}
/**
* 외부 연동 정보 제거
*/
private void removeExternalConnection(Long storeId, String platform) {
// 실제로는 ExternalPlatformEntity에서 연동 정보 제거
log.info("외부 연동 정보 제거: storeId={}, platform={}", storeId, platform);
}
}