diff --git a/src/main/java/go/kr/project/common/service/VehicleInfoService.java b/src/main/java/go/kr/project/common/service/VehicleInfoService.java new file mode 100644 index 0000000..56f2581 --- /dev/null +++ b/src/main/java/go/kr/project/common/service/VehicleInfoService.java @@ -0,0 +1,61 @@ +package go.kr.project.common.service; + +import go.kr.project.externalApi.vo.VehicleApiResponseVO; + +import java.util.List; + +/** + * 차량 정보 조회 서비스 인터페이스 + * + *

이 인터페이스는 차량 정보를 조회하는 두 가지 구현체를 추상화합니다:

+ * + * + *

설정 방법:

+ *
+ * # application.yml
+ * vmis:
+ *   integration:
+ *     mode: internal  # 또는 external
+ * 
+ * + *

사용 예시:

+ *
+ * {@code
+ * @Autowired
+ * private VehicleInfoService vehicleInfoService;
+ *
+ * // 단일 차량 조회
+ * VehicleApiResponseVO response = vehicleInfoService.getVehicleInfo("12가3456");
+ *
+ * // 여러 차량 일괄 조회
+ * List responses = vehicleInfoService.getVehiclesInfo(
+ *     Arrays.asList("12가3456", "34나5678")
+ * );
+ * }
+ * 
+ */ +public interface VehicleInfoService { + + /** + * 단일 차량번호에 대한 정보 조회 + * + *

차량 기본정보와 등록원부 정보를 함께 조회합니다.

+ * + * @param vehicleNumber 차량번호 + * @return 차량 정보 응답 (기본정보 + 등록원부 정보) + */ + VehicleApiResponseVO getVehicleInfo(String vehicleNumber); + + /** + * 여러 차량번호에 대한 정보를 일괄 조회 + * + *

각 차량에 대해 기본정보와 등록원부 정보를 함께 조회합니다.

+ * + * @param vehicleNumbers 차량번호 리스트 + * @return 차량 정보 응답 리스트 + */ + List getVehiclesInfo(List vehicleNumbers); +} diff --git a/src/main/java/go/kr/project/externalApi/service/ExternalVehicleInfoServiceImpl.java b/src/main/java/go/kr/project/externalApi/service/ExternalVehicleInfoServiceImpl.java new file mode 100644 index 0000000..9b921a6 --- /dev/null +++ b/src/main/java/go/kr/project/externalApi/service/ExternalVehicleInfoServiceImpl.java @@ -0,0 +1,84 @@ +package go.kr.project.externalApi.service; + +import go.kr.project.common.service.VehicleInfoService; +import go.kr.project.externalApi.vo.VehicleApiResponseVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 외부 REST API를 호출하는 차량 정보 조회 서비스 구현체 + * + *

이 구현체는 외부 VMIS-interface 서버의 REST API를 호출하여 + * 차량 정보를 조회합니다. 기존 ExternalVehicleApiService를 그대로 활용합니다.

+ * + *

활성화 조건:

+ *
+ * # application.yml
+ * vmis:
+ *   integration:
+ *     mode: external
+ * 
+ * + *

처리 흐름:

+ *
    + *
  1. 기존 ExternalVehicleApiService에 요청 위임
  2. + *
  3. ExternalVehicleApiService가 외부 REST API 호출
  4. + *
  5. 결과를 그대로 반환
  6. + *
+ * + *

외부 API 서버:

+ * + * + * @see VehicleInfoService + * @see ExternalVehicleApiService + */ +@Slf4j +@Service +@RequiredArgsConstructor +@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "external", matchIfMissing = true) +public class ExternalVehicleInfoServiceImpl implements VehicleInfoService { + + private final ExternalVehicleApiService externalVehicleApiService; + + @Override + public VehicleApiResponseVO getVehicleInfo(String vehicleNumber) { + log.info("[External Mode] 차량 정보 조회 시작 - 차량번호: {}", vehicleNumber); + + VehicleApiResponseVO response = externalVehicleApiService.getVehicleInfo(vehicleNumber); + + if (response.isSuccess()) { + log.info("[External Mode] 차량번호 {} 조회 성공", vehicleNumber); + } else { + log.warn("[External Mode] 차량번호 {} 조회 실패 - {}", vehicleNumber, response.getMessage()); + } + + return response; + } + + @Override + public List getVehiclesInfo(List vehicleNumbers) { + log.info("[External Mode] 차량 정보 일괄 조회 시작 - 대상 차량 수: {}", vehicleNumbers.size()); + + List responses = externalVehicleApiService.getVehiclesInfo(vehicleNumbers); + + int successCount = 0; + for (VehicleApiResponseVO response : responses) { + if (response.isSuccess()) { + successCount++; + } + } + + log.info("[External Mode] 차량 정보 일괄 조회 완료 - 성공: {}, 실패: {}", + successCount, vehicleNumbers.size() - successCount); + + return responses; + } +} diff --git a/src/main/java/go/kr/project/vmis/client/GovernmentApiClient.java b/src/main/java/go/kr/project/vmis/client/GovernmentApiClient.java index 60d815e..8843fb4 100644 --- a/src/main/java/go/kr/project/vmis/client/GovernmentApiClient.java +++ b/src/main/java/go/kr/project/vmis/client/GovernmentApiClient.java @@ -11,6 +11,7 @@ import go.kr.project.vmis.model.ledger.LedgerRequest; import go.kr.project.vmis.model.ledger.LedgerResponse; import go.kr.project.vmis.util.TxIdUtil; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.web.client.HttpStatusCodeException; @@ -62,7 +63,7 @@ import java.nio.charset.StandardCharsets; public class GovernmentApiClient implements GovernmentApi { /** - * Spring RestTemplate + * Spring RestTemplate (VMIS 정부 API 전용) * *

HTTP 클라이언트로서 실제 네트워크 통신을 수행합니다. * 이 객체는 Spring Bean으로 주입되며, 설정에 따라 다음을 포함할 수 있습니다:

@@ -74,6 +75,7 @@ public class GovernmentApiClient implements GovernmentApi { *
  • 인터셉터 (로깅, 헤더 추가 등)
  • * */ + @Qualifier("vmisRestTemplate") private final RestTemplate restTemplate; /** diff --git a/src/main/java/go/kr/project/vmis/config/HttpClientConfig.java b/src/main/java/go/kr/project/vmis/config/HttpClientConfig.java index 4ff063d..2be084a 100644 --- a/src/main/java/go/kr/project/vmis/config/HttpClientConfig.java +++ b/src/main/java/go/kr/project/vmis/config/HttpClientConfig.java @@ -14,12 +14,13 @@ import org.springframework.web.client.RestTemplate; /** * HttpClient 설정 * Apache HttpClient 4를 사용한 RestTemplate 구성 + * VMIS 정부 API 전용 RestTemplate (vmisRestTemplate) */ @Configuration public class HttpClientConfig { - @Bean - public RestTemplate restTemplate(VmisProperties props, RestTemplateBuilder builder) { + @Bean(name = "vmisRestTemplate") + public RestTemplate vmisRestTemplate(VmisProperties props, RestTemplateBuilder builder) { VmisProperties.GovProps gov = props.getGov(); int connectTimeout = gov.getConnectTimeoutMillis(); int readTimeout = gov.getReadTimeoutMillis(); diff --git a/src/main/java/go/kr/project/vmis/config/VmisIntegrationConfig.java b/src/main/java/go/kr/project/vmis/config/VmisIntegrationConfig.java new file mode 100644 index 0000000..d89d003 --- /dev/null +++ b/src/main/java/go/kr/project/vmis/config/VmisIntegrationConfig.java @@ -0,0 +1,113 @@ +package go.kr.project.vmis.config; + +import go.kr.project.common.service.VehicleInfoService; +import go.kr.project.vmis.config.properties.VmisProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * VMIS 통합 설정 및 모니터링 + * + *

    이 설정 클래스는 VMIS 통합 모드에 대한 정보를 제공하고, + * 애플리케이션 시작 시 현재 활성화된 모드를 로그로 출력합니다.

    + * + *

    주요 기능:

    + *
      + *
    • 애플리케이션 시작 시 VMIS 통합 모드 출력
    • + *
    • 활성화된 VehicleInfoService 구현체 표시
    • + *
    • 설정 검증 및 경고 메시지 출력
    • + *
    + * + *

    지원하는 모드:

    + *
      + *
    • internal: 내부 VMIS 모듈 직접 호출 (InternalVehicleInfoServiceImpl)
    • + *
    • external: 외부 REST API 호출 (ExternalVehicleInfoServiceImpl)
    • + *
    + */ +@Slf4j +@Configuration +@RequiredArgsConstructor +public class VmisIntegrationConfig { + + private final VmisProperties vmisProperties; + + /** + * VMIS 통합 모드 정보 출력 + * + *

    애플리케이션 시작 시 현재 설정된 VMIS 통합 모드와 + * 관련 설정 정보를 로그로 출력합니다.

    + * + * @param vehicleInfoService 활성화된 VehicleInfoService 구현체 + * @return CommandLineRunner + */ + @Bean + public CommandLineRunner vmisIntegrationModeLogger(VehicleInfoService vehicleInfoService) { + return args -> { + String mode = vmisProperties.getIntegration().getMode(); + String implClass = vehicleInfoService.getClass().getSimpleName(); + + log.info("========================================"); + log.info("VMIS Integration Mode: {}", mode); + log.info("Active Implementation: {}", implClass); + log.info("========================================"); + + if ("internal".equalsIgnoreCase(mode)) { + logInternalModeInfo(); + } else if ("external".equalsIgnoreCase(mode)) { + logExternalModeInfo(); + } else { + log.warn("알 수 없는 VMIS 통합 모드: {}. 'internal' 또는 'external'을 사용하세요.", mode); + } + }; + } + + /** + * Internal 모드 설정 정보 출력 + */ + private void logInternalModeInfo() { + log.info("[Internal Mode] 내부 VMIS 모듈을 직접 사용합니다"); + log.info(" - 정부 API 호스트: {}://{}", + vmisProperties.getGov().getScheme(), + vmisProperties.getGov().getHost()); + log.info(" - 기본사항 조회 경로: {}", + vmisProperties.getGov().getServices().getBasic().getPath()); + log.info(" - 등록원부 조회 경로: {}", + vmisProperties.getGov().getServices().getLedger().getPath()); + log.info(" - GPKI 암호화: {}", + vmisProperties.getGpki().getEnabled()); + log.info(" - 연결 타임아웃: {}ms", + vmisProperties.getGov().getConnectTimeoutMillis()); + log.info(" - 읽기 타임아웃: {}ms", + vmisProperties.getGov().getReadTimeoutMillis()); + + if ("Y".equalsIgnoreCase(vmisProperties.getGpki().getEnabled())) { + log.info(" - GPKI 인증서 서버 ID: {}", + vmisProperties.getGpki().getCertServerId()); + log.info(" - GPKI 대상 서버 ID: {}", + vmisProperties.getGpki().getTargetServerId()); + } else { + log.warn(" - GPKI 암호화가 비활성화되어 있습니다. 개발 환경에서만 사용하세요."); + } + } + + /** + * External 모드 설정 정보 출력 + */ + private void logExternalModeInfo() { + log.info("[External Mode] 외부 REST API를 사용합니다"); + log.info(" - 외부 API URL: {}", + vmisProperties.getExternal().getApi().getUrl()); + log.info(" - 연결 타임아웃: {}ms", + vmisProperties.getExternal().getApi().getConnectTimeoutMillis()); + log.info(" - 읽기 타임아웃: {}ms", + vmisProperties.getExternal().getApi().getReadTimeoutMillis()); + log.warn(" - 외부 VMIS-interface 서버가 실행 중이어야 합니다."); + log.info(" - 기본사항 조회: POST {}/basic", + vmisProperties.getExternal().getApi().getUrl()); + log.info(" - 등록원부 조회: POST {}/ledger", + vmisProperties.getExternal().getApi().getUrl()); + } +} diff --git a/src/main/java/go/kr/project/vmis/config/properties/VmisProperties.java b/src/main/java/go/kr/project/vmis/config/properties/VmisProperties.java index 74b5961..aed67d5 100644 --- a/src/main/java/go/kr/project/vmis/config/properties/VmisProperties.java +++ b/src/main/java/go/kr/project/vmis/config/properties/VmisProperties.java @@ -9,19 +9,57 @@ import org.springframework.validation.annotation.Validated; @Validated public class VmisProperties { + @NotNull + private IntegrationProps integration = new IntegrationProps(); @NotNull private SystemProps system = new SystemProps(); @NotNull private GpkiProps gpki = new GpkiProps(); @NotNull private GovProps gov = new GovProps(); + @NotNull + private ExternalProps external = new ExternalProps(); + public IntegrationProps getIntegration() { return integration; } + public void setIntegration(IntegrationProps integration) { this.integration = integration; } public SystemProps getSystem() { return system; } public void setSystem(SystemProps system) { this.system = system; } public GpkiProps getGpki() { return gpki; } public void setGpki(GpkiProps gpki) { this.gpki = gpki; } public GovProps getGov() { return gov; } public void setGov(GovProps gov) { this.gov = gov; } + public ExternalProps getExternal() { return external; } + public void setExternal(ExternalProps external) { this.external = external; } + + public static class IntegrationProps { + @NotBlank + private String mode = "external"; + + public String getMode() { return mode; } + public void setMode(String mode) { this.mode = mode; } + } + + public static class ExternalProps { + @NotNull + private ApiProps api = new ApiProps(); + + public ApiProps getApi() { return api; } + public void setApi(ApiProps api) { this.api = api; } + + public static class ApiProps { + @NotBlank + private String url = "http://localhost:8081/api/v1/vehicles"; + private int connectTimeoutMillis = 5000; + private int readTimeoutMillis = 10000; + + public String getUrl() { return url; } + public void setUrl(String url) { this.url = url; } + public int getConnectTimeoutMillis() { return connectTimeoutMillis; } + public void setConnectTimeoutMillis(int connectTimeoutMillis) { this.connectTimeoutMillis = connectTimeoutMillis; } + public int getReadTimeoutMillis() { return readTimeoutMillis; } + public void setReadTimeoutMillis(int readTimeoutMillis) { this.readTimeoutMillis = readTimeoutMillis; } + } + } public static class SystemProps { @NotBlank diff --git a/src/main/java/go/kr/project/vmis/service/InternalVehicleInfoServiceImpl.java b/src/main/java/go/kr/project/vmis/service/InternalVehicleInfoServiceImpl.java new file mode 100644 index 0000000..43ec36e --- /dev/null +++ b/src/main/java/go/kr/project/vmis/service/InternalVehicleInfoServiceImpl.java @@ -0,0 +1,219 @@ +package go.kr.project.vmis.service; + +import go.kr.project.common.service.VehicleInfoService; +import go.kr.project.externalApi.vo.VehicleApiResponseVO; +import go.kr.project.externalApi.vo.VehicleBasicInfoVO; +import go.kr.project.externalApi.vo.VehicleLedgerVO; +import go.kr.project.vmis.model.basic.BasicRequest; +import go.kr.project.vmis.model.basic.BasicResponse; +import go.kr.project.vmis.model.common.Envelope; +import go.kr.project.vmis.model.ledger.LedgerRequest; +import go.kr.project.vmis.model.ledger.LedgerResponse; +import go.kr.project.vmis.util.VehicleResponseMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 내부 VMIS 모듈을 직접 호출하는 차량 정보 조회 서비스 구현체 + * + *

    이 구현체는 외부 REST API 호출 없이 내부 VMIS 모듈을 직접 사용하여 + * 정부 시스템과 통신합니다. 네트워크 오버헤드가 없어 성능이 향상됩니다.

    + * + *

    활성화 조건:

    + *
    + * # application.yml
    + * vmis:
    + *   integration:
    + *     mode: internal
    + * 
    + * + *

    처리 흐름:

    + *
      + *
    1. 차량번호를 받아 BasicRequest, LedgerRequest 생성
    2. + *
    3. CarBassMatterInqireService.basic() 호출 (기본정보)
    4. + *
    5. CarLedgerFrmbkService.ledger() 호출 (등록원부)
    6. + *
    7. 내부 모델을 외부 VO로 변환 (VehicleResponseMapper 사용)
    8. + *
    9. VehicleApiResponseVO로 결과 반환
    10. + *
    + * + * @see VehicleInfoService + * @see CarBassMatterInqireService + * @see CarLedgerFrmbkService + */ +@Slf4j +@Service +@RequiredArgsConstructor +@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "internal") +public class InternalVehicleInfoServiceImpl implements VehicleInfoService { + + private final CarBassMatterInqireService carBassMatterInqireService; + private final CarLedgerFrmbkService carLedgerFrmbkService; + + @Override + public VehicleApiResponseVO getVehicleInfo(String vehicleNumber) { + log.info("[Internal Mode] 차량 정보 조회 시작 - 차량번호: {}", vehicleNumber); + + VehicleApiResponseVO response = new VehicleApiResponseVO(); + response.setVhrno(vehicleNumber); + + try { + // 1. 차량 기본정보 조회 + VehicleBasicInfoVO basicInfo = getBasicInfo(vehicleNumber); + response.setBasicInfo(basicInfo); + + // 2. 자동차 등록원부 조회 + VehicleLedgerVO ledgerInfo = getLedgerInfo(vehicleNumber); + response.setLedgerInfo(ledgerInfo); + + // 3. 결과 검증 + if (basicInfo != null && "00".equals(basicInfo.getCntcResultCode())) { + response.setSuccess(true); + response.setMessage("조회 성공"); + log.info("[Internal Mode] 차량번호 {} 조회 성공", vehicleNumber); + } else { + response.setSuccess(false); + response.setMessage(basicInfo != null ? basicInfo.getCntcResultDtls() : "조회 실패"); + log.warn("[Internal Mode] 차량번호 {} 조회 실패 - {}", vehicleNumber, response.getMessage()); + } + + } catch (Exception e) { + response.setSuccess(false); + response.setMessage("내부 API 호출 오류: " + e.getMessage()); + log.error("[Internal Mode] 차량번호 {} 내부 API 호출 중 오류 발생", vehicleNumber, e); + } + + return response; + } + + @Override + public List getVehiclesInfo(List vehicleNumbers) { + log.info("[Internal Mode] 차량 정보 일괄 조회 시작 - 대상 차량 수: {}", vehicleNumbers.size()); + + List responses = new ArrayList(); + + for (String vehicleNumber : vehicleNumbers) { + try { + VehicleApiResponseVO response = getVehicleInfo(vehicleNumber); + responses.add(response); + } catch (Exception e) { + log.error("[Internal Mode] 차량번호 {} 조회 중 오류 발생: {}", vehicleNumber, e.getMessage(), e); + + // 오류 발생 시에도 응답 객체 생성하여 추가 + VehicleApiResponseVO errorResponse = VehicleApiResponseVO.builder() + .vhrno(vehicleNumber) + .success(false) + .message("조회 실패: " + e.getMessage()) + .build(); + responses.add(errorResponse); + } + } + + log.info("[Internal Mode] 차량 정보 일괄 조회 완료 - 성공: {}, 실패: {}", + countSuccessful(responses), + vehicleNumbers.size() - countSuccessful(responses)); + + return responses; + } + + /** + * 차량 기본정보 조회 (내부 모듈 직접 호출) + * + * @param vehicleNumber 차량번호 + * @return 차량 기본정보 VO + */ + private VehicleBasicInfoVO getBasicInfo(String vehicleNumber) { + log.debug("[Internal Mode] 차량 기본정보 조회 - 차량번호: {}", vehicleNumber); + + // 요청 객체 생성 - 차량번호만 설정 (나머지는 RequestEnricher가 자동 설정) + BasicRequest request = new BasicRequest(); + request.setVhrno(vehicleNumber); + + // Envelope로 감싸기 + Envelope requestEnvelope = new Envelope(); + requestEnvelope.setData(Collections.singletonList(request)); + + try { + // 내부 서비스 호출 + ResponseEntity> responseEntity = + carBassMatterInqireService.basic(requestEnvelope); + + if (responseEntity.getBody() != null && + responseEntity.getBody().getData() != null && + !responseEntity.getBody().getData().isEmpty()) { + + BasicResponse basicResponse = responseEntity.getBody().getData().get(0); + + // 내부 모델을 외부 VO로 변환 + return VehicleResponseMapper.toVehicleBasicInfoVO(basicResponse); + } + + log.warn("[Internal Mode] 차량 기본정보 조회 응답이 비어있음 - 차량번호: {}", vehicleNumber); + return null; + + } catch (Exception e) { + log.error("[Internal Mode] 차량 기본정보 조회 실패 - 차량번호: {}", vehicleNumber, e); + throw new RuntimeException("차량 기본정보 조회 실패: " + e.getMessage(), e); + } + } + + /** + * 자동차 등록원부(갑) 조회 (내부 모듈 직접 호출) + * + * @param vehicleNumber 차량번호 + * @return 등록원부 정보 VO + */ + private VehicleLedgerVO getLedgerInfo(String vehicleNumber) { + log.debug("[Internal Mode] 자동차 등록원부 조회 - 차량번호: {}", vehicleNumber); + + // 요청 객체 생성 - 차량번호만 설정 (나머지는 RequestEnricher가 자동 설정) + LedgerRequest request = new LedgerRequest(); + request.setVhrno(vehicleNumber); + + // Envelope로 감싸기 + Envelope requestEnvelope = new Envelope(); + requestEnvelope.setData(Collections.singletonList(request)); + + try { + // 내부 서비스 호출 + ResponseEntity> responseEntity = + carLedgerFrmbkService.ledger(requestEnvelope); + + if (responseEntity.getBody() != null && + responseEntity.getBody().getData() != null && + !responseEntity.getBody().getData().isEmpty()) { + + LedgerResponse ledgerResponse = responseEntity.getBody().getData().get(0); + + // 내부 모델을 외부 VO로 변환 + return VehicleResponseMapper.toLedgerVO(ledgerResponse); + } + + log.warn("[Internal Mode] 자동차 등록원부 조회 응답이 비어있음 - 차량번호: {}", vehicleNumber); + return null; + + } catch (Exception e) { + log.error("[Internal Mode] 자동차 등록원부 조회 실패 - 차량번호: {}", vehicleNumber, e); + throw new RuntimeException("자동차 등록원부 조회 실패: " + e.getMessage(), e); + } + } + + /** + * 성공한 응답 개수 계산 + */ + private int countSuccessful(List responses) { + int count = 0; + for (VehicleApiResponseVO response : responses) { + if (response.isSuccess()) { + count++; + } + } + return count; + } +} diff --git a/src/main/java/go/kr/project/vmis/util/VehicleResponseMapper.java b/src/main/java/go/kr/project/vmis/util/VehicleResponseMapper.java new file mode 100644 index 0000000..635c3c4 --- /dev/null +++ b/src/main/java/go/kr/project/vmis/util/VehicleResponseMapper.java @@ -0,0 +1,165 @@ +package go.kr.project.vmis.util; + +import go.kr.project.externalApi.vo.VehicleBasicInfoVO; +import go.kr.project.externalApi.vo.VehicleLedgerVO; +import go.kr.project.vmis.model.basic.BasicResponse; +import go.kr.project.vmis.model.ledger.LedgerResponse; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 차량 응답 모델 변환 유틸리티 + * + *

    내부 VMIS 모델(BasicResponse, LedgerResponse)을 + * 외부 API VO(VehicleBasicInfoVO, VehicleLedgerVO)로 변환합니다.

    + */ +public class VehicleResponseMapper { + + /** + * BasicResponse를 VehicleBasicInfoVO로 변환 + * + * @param basicResponse 내부 기본정보 응답 + * @return 외부 API용 기본정보 VO + */ + public static VehicleBasicInfoVO toVehicleBasicInfoVO(BasicResponse basicResponse) { + if (basicResponse == null) { + return null; + } + + VehicleBasicInfoVO vo = new VehicleBasicInfoVO(); + vo.setCntcResultCode(basicResponse.getCntcResultCode()); + vo.setCntcResultDtls(basicResponse.getCntcResultDtls()); + + // record 변환 + if (basicResponse.getRecord() != null && !basicResponse.getRecord().isEmpty()) { + List records = new ArrayList(); + for (BasicResponse.Record src : basicResponse.getRecord()) { + VehicleBasicInfoVO.Record dest = new VehicleBasicInfoVO.Record(); + + // 주요 필드 매핑 + dest.setVhrno(src.getVhrno()); + dest.setVin(src.getVin()); + dest.setCnm(src.getCnm()); + dest.setMberNm(src.getMberNm()); + dest.setMberSeNo(src.getMberSeNo()); + dest.setMberSeCode(src.getMberSeCode()); + dest.setTelno(src.getTelno()); + dest.setPrye(src.getPrye()); + dest.setRegistDe(src.getRegistDe()); + dest.setFrstRegistDe(src.getFrstRegistDe()); + dest.setColorNm(src.getColorNm()); + dest.setDsplvl(src.getDsplvl()); + dest.setVhctyAsortNm(src.getVhctyAsortNm()); + dest.setVhctyTyNm(src.getVhctyTyNm()); + dest.setVhctySeNm(src.getVhctySeNm()); + dest.setInsptValidPdBgnde(src.getInsptValidPdBgnde()); + dest.setInsptValidPdEndde(src.getInsptValidPdEndde()); + dest.setUsgsrhldAdresFull(src.getUsgsrhldAdresFull()); + dest.setOwnerAdresFull(src.getOwnerAdresFull()); + + // 추가 필드 + dest.setErsrRegistSeCode(src.getErsrRegistSeCode()); + dest.setErsrRegistSeNm(src.getErsrRegistSeNm()); + dest.setErsrRegistDe(src.getErsrRegistDe()); + dest.setRegistDetailCode(src.getRegistDetailCode()); + dest.setUseStrnghldLegaldongCode(src.getUseStrnghldLegaldongCode()); + dest.setUseStrnghldAdstrdCode(src.getUseStrnghldAdstrdCode()); + dest.setUseStrnghldMntn(src.getUseStrnghldMntn()); + dest.setUseStrnghldLnbr(src.getUseStrnghldLnbr()); + dest.setUseStrnghldHo(src.getUseStrnghldHo()); + dest.setUseStrnghldAdresNm(src.getUseStrnghldAdresNm()); + dest.setVhcleTotWt(src.getVhcleTotWt()); + dest.setCaagEndde(src.getCaagEndde()); + dest.setVhctyAsortCode(src.getVhctyAsortCode()); + dest.setVhctyTyCode(src.getVhctyTyCode()); + dest.setVhctySeCode(src.getVhctySeCode()); + dest.setMxmmLdg(src.getMxmmLdg()); + dest.setFomNm(src.getFomNm()); + dest.setUseFuelCode(src.getUseFuelCode()); + dest.setPrposSeCode(src.getPrposSeCode()); + dest.setMtrsFomNm(src.getMtrsFomNm()); + dest.setAcqsAmount(src.getAcqsAmount()); + dest.setTkcarPscapCo(src.getTkcarPscapCo()); + dest.setSpmnno(src.getSpmnno()); + dest.setTrvlDstnc(src.getTrvlDstnc()); + + records.add(dest); + } + vo.setRecord(records); + } + + return vo; + } + + /** + * LedgerResponse를 VehicleLedgerVO로 변환 + * + * @param ledgerResponse 내부 등록원부 응답 + * @return 외부 API용 등록원부 VO + */ + public static VehicleLedgerVO toLedgerVO(LedgerResponse ledgerResponse) { + if (ledgerResponse == null) { + return null; + } + + VehicleLedgerVO vo = new VehicleLedgerVO(); + + // 연계 결과 + vo.setCntcResultCode(ledgerResponse.getCntcResultCode()); + vo.setCntcResultDtls(ledgerResponse.getCntcResultDtls()); + + // 마스터 정보 (VehicleLedgerVO에 실제 존재하는 필드만 설정) + vo.setLedgerGroupNo(ledgerResponse.getLedgerGroupNo()); + vo.setLedgerIndvdlzNo(ledgerResponse.getLedgerIndvdlzNo()); + vo.setVhmno(ledgerResponse.getVhmno()); + vo.setVhrno(ledgerResponse.getVhrno()); + vo.setVin(ledgerResponse.getVin()); + vo.setVhctyAsortCode(ledgerResponse.getVhctyAsortCode()); + vo.setVhctyAsortNm(ledgerResponse.getVhctyAsortNm()); + vo.setCnm(ledgerResponse.getCnm()); + vo.setColorNm(ledgerResponse.getColorNm()); + vo.setPrposSeCode(ledgerResponse.getPrposSeCode()); + vo.setPrposSeNm(ledgerResponse.getPrposSeNm()); + vo.setMtrsFomNm(ledgerResponse.getMtrsFomNm()); + vo.setFomNm(ledgerResponse.getFomNm()); + vo.setAcqsAmount(ledgerResponse.getAcqsAmount()); + vo.setFrstRegistDe(ledgerResponse.getFrstRegistDe()); + vo.setCaagEndde(ledgerResponse.getCaagEndde()); + vo.setPrye(ledgerResponse.getPrye()); + vo.setYblMd(ledgerResponse.getYblMd()); + vo.setTrvlDstnc(ledgerResponse.getTrvlDstnc()); + vo.setInsptValidPdBgnde(ledgerResponse.getInsptValidPdBgnde()); + vo.setInsptValidPdEndde(ledgerResponse.getInsptValidPdEndde()); + vo.setNmplCsdyAt(ledgerResponse.getNmplCsdyAt()); + vo.setErsrRegistDe(ledgerResponse.getErsrRegistDe()); + vo.setErsrRegistSeCode(ledgerResponse.getErsrRegistSeCode()); + vo.setErsrRegistSeNm(ledgerResponse.getErsrRegistSeNm()); + vo.setAdres(ledgerResponse.getAdres()); + vo.setAdresNm(ledgerResponse.getAdresNm()); + vo.setTelno(ledgerResponse.getTelno()); + vo.setMberNm(ledgerResponse.getMberNm()); + vo.setMberSeCode(ledgerResponse.getMberSeCode()); + vo.setMberSeNo(ledgerResponse.getMberSeNo()); + + // record 변경내역 변환 + if (ledgerResponse.getRecord() != null && !ledgerResponse.getRecord().isEmpty()) { + List records = new ArrayList(); + for (LedgerResponse.Record src : ledgerResponse.getRecord()) { + VehicleLedgerVO.Record dest = new VehicleLedgerVO.Record(); + dest.setMainchk(src.getMainchk()); + dest.setChangeJobSeCode(src.getChangeJobSeCode()); + dest.setMainno(src.getMainno()); + dest.setSubno(src.getSubno()); + dest.setDtls(src.getDtls()); + dest.setChangeDe(src.getChangeDe()); + dest.setGubunNm(src.getGubunNm()); + records.add(dest); + } + vo.setRecord(records); + } + + return vo; + } +}