diff --git a/src/main/java/go/kr/project/api/config/VmisIntegrationConfig.java b/src/main/java/go/kr/project/api/config/VmisIntegrationConfig.java index 7c26187..5012fde 100644 --- a/src/main/java/go/kr/project/api/config/VmisIntegrationConfig.java +++ b/src/main/java/go/kr/project/api/config/VmisIntegrationConfig.java @@ -1,6 +1,6 @@ package go.kr.project.api.config; -import go.kr.project.api.VehicleInfoService; +import go.kr.project.api.external.service.VehicleInfoService; import go.kr.project.api.config.properties.VmisProperties; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/go/kr/project/api/external/service/ExternalVehicleApiService.java b/src/main/java/go/kr/project/api/external/service/ExternalVehicleApiService.java index 42cc949..d829915 100644 --- a/src/main/java/go/kr/project/api/external/service/ExternalVehicleApiService.java +++ b/src/main/java/go/kr/project/api/external/service/ExternalVehicleApiService.java @@ -1,33 +1,14 @@ package go.kr.project.api.external.service; -import go.kr.project.api.model.request.BasicRequest; -import go.kr.project.api.model.request.LedgerRequest; -import go.kr.project.api.model.*; import go.kr.project.api.model.response.*; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.*; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; +import go.kr.project.api.model.VehicleApiResponseVO; -import java.util.ArrayList; import java.util.List; /** - * 차량 API 호출 서비스 + * 외부 VMIS-interface API 호출 서비스 인터페이스 * VMIS-interface 프로그램의 API를 호출하여 차량 정보를 조회하는 서비스 */ -@Service -@RequiredArgsConstructor -@Slf4j -public class ExternalVehicleApiService { - - private final RestTemplate restTemplate; - - // VMIS-interface API URL - private static final String VMIS_API_BASE_URL = "http://localhost:8081/api/v1/vehicles"; - private static final String BASIC_INFO_URL = VMIS_API_BASE_URL + "/basic"; - private static final String LEDGER_INFO_URL = VMIS_API_BASE_URL + "/ledger"; +public interface ExternalVehicleApiService { /** * 여러 차량번호에 대한 정보를 일괄 조회 @@ -35,34 +16,7 @@ public class ExternalVehicleApiService { * @param vehicleNumbers 차량번호 리스트 * @return 차량 정보 응답 리스트 */ - public List getVehiclesInfo(List vehicleNumbers) { - log.info("차량 정보 일괄 조회 시작 - 대상 차량 수: {}", vehicleNumbers.size()); - - List responses = new ArrayList<>(); - - for (String vehicleNumber : vehicleNumbers) { - try { - VehicleApiResponseVO response = getVehicleInfo(vehicleNumber); - responses.add(response); - } catch (Exception e) { - log.error("차량번호 {} 조회 중 오류 발생: {}", vehicleNumber, e.getMessage(), e); - - // 오류 발생 시에도 응답 객체 생성하여 추가 - VehicleApiResponseVO errorResponse = VehicleApiResponseVO.builder() - .vhrno(vehicleNumber) - .success(false) - .message("조회 실패: " + e.getMessage()) - .build(); - responses.add(errorResponse); - } - } - - log.info("차량 정보 일괄 조회 완료 - 성공: {}, 실패: {}", - responses.stream().filter(VehicleApiResponseVO::isSuccess).count(), - responses.stream().filter(r -> !r.isSuccess()).count()); - - return responses; - } + List getVehiclesInfo(List vehicleNumbers); /** * 단일 차량번호에 대한 정보 조회 @@ -70,134 +24,5 @@ public class ExternalVehicleApiService { * @param vehicleNumber 차량번호 * @return 차량 정보 응답 */ - public VehicleApiResponseVO getVehicleInfo(String vehicleNumber) { - log.info("차량 정보 조회 시작 - 차량번호: {}", vehicleNumber); - - VehicleApiResponseVO response = new VehicleApiResponseVO(); - response.setVhrno(vehicleNumber); - - try { - // 1. 차량 기본정보 조회 - BasicResponse basicInfo = getBasicInfo(vehicleNumber); - response.setBasicInfo(basicInfo); - - // 2. 자동차 등록원부 조회 - LedgerResponse ledgerInfo = getLedgerInfo(vehicleNumber); - response.setLedgerInfo(ledgerInfo); - - // 3. 결과 검증 - if (basicInfo != null && "00".equals(basicInfo.getCntcResultCode())) { - response.setSuccess(true); - response.setMessage("조회 성공"); - log.info("차량번호 {} 조회 성공", vehicleNumber); - } else { - response.setSuccess(false); - response.setMessage(basicInfo != null ? basicInfo.getCntcResultDtls() : "조회 실패"); - log.warn("차량번호 {} 조회 실패 - {}", vehicleNumber, response.getMessage()); - } - - } catch (Exception e) { - response.setSuccess(false); - response.setMessage("API 호출 오류: " + e.getMessage()); - log.error("차량번호 {} API 호출 중 오류 발생", vehicleNumber, e); - } - - return response; - } - - /** - * 차량 기본정보 조회 API 호출 - * VMIS-interface의 RequestEnricher가 기본 설정값을 자동으로 채워주므로 차량번호만 전송 - * - * @param vehicleNumber 차량번호 - * @return 차량 기본정보 - */ - private BasicResponse getBasicInfo(String vehicleNumber) { - log.debug("차량 기본정보 조회 API 호출 - 차량번호: {}", vehicleNumber); - - // 요청 객체 생성 - 차량번호만 설정 (나머지는 RequestEnricher가 자동 설정) - BasicRequest request = new BasicRequest(); - request.setVhrno(vehicleNumber); - - // Envelope로 감싸기 - Envelope requestEnvelope = new Envelope<>(request); - - // HTTP 헤더 설정 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - - HttpEntity> requestEntity = new HttpEntity<>(requestEnvelope, headers); - - try { - // API 호출 - ResponseEntity> responseEntity = restTemplate.exchange( - BASIC_INFO_URL, - HttpMethod.POST, - requestEntity, - new ParameterizedTypeReference>() {} - ); - - if (responseEntity.getStatusCode() == HttpStatus.OK && responseEntity.getBody() != null) { - List data = responseEntity.getBody().getData(); - if (data != null && !data.isEmpty()) { - return data.get(0); - } - } - - log.warn("차량 기본정보 조회 응답이 비어있음 - 차량번호: {}", vehicleNumber); - return null; - - } catch (Exception e) { - log.error("차량 기본정보 조회 API 호출 실패 - 차량번호: {}", vehicleNumber, e); - throw new RuntimeException("차량 기본정보 조회 실패: " + e.getMessage(), e); - } - } - - /** - * 자동차 등록원부(갑) 조회 API 호출 - * VMIS-interface의 RequestEnricher가 기본 설정값을 자동으로 채워주므로 차량번호만 전송 - * - * @param vehicleNumber 차량번호 - * @return 등록원부 정보 - */ - private LedgerResponse getLedgerInfo(String vehicleNumber) { - log.debug("자동차 등록원부 조회 API 호출 - 차량번호: {}", vehicleNumber); - - // 요청 객체 생성 - 차량번호만 설정 (나머지는 RequestEnricher가 자동 설정) - LedgerRequest request = new LedgerRequest(); - request.setVhrno(vehicleNumber); - - // Envelope로 감싸기 - Envelope requestEnvelope = new Envelope<>(request); - - // HTTP 헤더 설정 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - - HttpEntity> requestEntity = new HttpEntity<>(requestEnvelope, headers); - - try { - // API 호출 - ResponseEntity> responseEntity = restTemplate.exchange( - LEDGER_INFO_URL, - HttpMethod.POST, - requestEntity, - new ParameterizedTypeReference>() {} - ); - - if (responseEntity.getStatusCode() == HttpStatus.OK && responseEntity.getBody() != null) { - List data = responseEntity.getBody().getData(); - if (data != null && !data.isEmpty()) { - return data.get(0); - } - } - - log.warn("자동차 등록원부 조회 응답이 비어있음 - 차량번호: {}", vehicleNumber); - return null; - - } catch (Exception e) { - log.error("자동차 등록원부 조회 API 호출 실패 - 차량번호: {}", vehicleNumber, e); - throw new RuntimeException("자동차 등록원부 조회 실패: " + e.getMessage(), e); - } - } + VehicleApiResponseVO getVehicleInfo(String vehicleNumber); } diff --git a/src/main/java/go/kr/project/api/VehicleInfoService.java b/src/main/java/go/kr/project/api/external/service/VehicleInfoService.java similarity index 97% rename from src/main/java/go/kr/project/api/VehicleInfoService.java rename to src/main/java/go/kr/project/api/external/service/VehicleInfoService.java index ae78d7e..6e10992 100644 --- a/src/main/java/go/kr/project/api/VehicleInfoService.java +++ b/src/main/java/go/kr/project/api/external/service/VehicleInfoService.java @@ -1,4 +1,4 @@ -package go.kr.project.api; +package go.kr.project.api.external.service; import go.kr.project.api.model.VehicleApiResponseVO; diff --git a/src/main/java/go/kr/project/api/external/service/impl/ExternalVehicleApiServiceImpl.java b/src/main/java/go/kr/project/api/external/service/impl/ExternalVehicleApiServiceImpl.java new file mode 100644 index 0000000..b09cf8d --- /dev/null +++ b/src/main/java/go/kr/project/api/external/service/impl/ExternalVehicleApiServiceImpl.java @@ -0,0 +1,195 @@ +package go.kr.project.api.external.service.impl; + +import go.kr.project.api.external.service.ExternalVehicleApiService; +import go.kr.project.api.model.request.BasicRequest; +import go.kr.project.api.model.request.LedgerRequest; +import go.kr.project.api.model.*; +import go.kr.project.api.model.response.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.ArrayList; +import java.util.List; + +/** + * 외부 VMIS-interface API 호출 서비스 구현체 + * VMIS-interface 프로그램의 API를 호출하여 차량 정보를 조회하는 서비스 + */ +@Service +@RequiredArgsConstructor +@Slf4j +public class ExternalVehicleApiServiceImpl implements ExternalVehicleApiService { + + private final RestTemplate restTemplate; + + // VMIS-interface API URL + private static final String VMIS_API_BASE_URL = "http://localhost:8081/api/v1/vehicles"; + private static final String BASIC_INFO_URL = VMIS_API_BASE_URL + "/basic"; + private static final String LEDGER_INFO_URL = VMIS_API_BASE_URL + "/ledger"; + + @Override + public List getVehiclesInfo(List vehicleNumbers) { + log.info("차량 정보 일괄 조회 시작 - 대상 차량 수: {}", vehicleNumbers.size()); + + List responses = new ArrayList<>(); + + for (String vehicleNumber : vehicleNumbers) { + try { + VehicleApiResponseVO response = getVehicleInfo(vehicleNumber); + responses.add(response); + } catch (Exception e) { + log.error("차량번호 {} 조회 중 오류 발생: {}", vehicleNumber, e.getMessage(), e); + + // 오류 발생 시에도 응답 객체 생성하여 추가 + VehicleApiResponseVO errorResponse = VehicleApiResponseVO.builder() + .vhrno(vehicleNumber) + .success(false) + .message("조회 실패: " + e.getMessage()) + .build(); + responses.add(errorResponse); + } + } + + log.info("차량 정보 일괄 조회 완료 - 성공: {}, 실패: {}", + responses.stream().filter(VehicleApiResponseVO::isSuccess).count(), + responses.stream().filter(r -> !r.isSuccess()).count()); + + return responses; + } + + @Override + public VehicleApiResponseVO getVehicleInfo(String vehicleNumber) { + log.info("차량 정보 조회 시작 - 차량번호: {}", vehicleNumber); + + VehicleApiResponseVO response = new VehicleApiResponseVO(); + response.setVhrno(vehicleNumber); + + try { + // 1. 차량 기본정보 조회 + BasicResponse basicInfo = getBasicInfo(vehicleNumber); + response.setBasicInfo(basicInfo); + + // 2. 자동차 등록원부 조회 + LedgerResponse ledgerInfo = getLedgerInfo(vehicleNumber); + response.setLedgerInfo(ledgerInfo); + + // 3. 결과 검증 + if (basicInfo != null && "00".equals(basicInfo.getCntcResultCode())) { + response.setSuccess(true); + response.setMessage("조회 성공"); + log.info("차량번호 {} 조회 성공", vehicleNumber); + } else { + response.setSuccess(false); + response.setMessage(basicInfo != null ? basicInfo.getCntcResultDtls() : "조회 실패"); + log.warn("차량번호 {} 조회 실패 - {}", vehicleNumber, response.getMessage()); + } + + } catch (Exception e) { + response.setSuccess(false); + response.setMessage("API 호출 오류: " + e.getMessage()); + log.error("차량번호 {} API 호출 중 오류 발생", vehicleNumber, e); + } + + return response; + } + + /** + * 차량 기본정보 조회 API 호출 + * VMIS-interface의 RequestEnricher가 기본 설정값을 자동으로 채워주므로 차량번호만 전송 + * + * @param vehicleNumber 차량번호 + * @return 차량 기본정보 + */ + private BasicResponse getBasicInfo(String vehicleNumber) { + log.debug("차량 기본정보 조회 API 호출 - 차량번호: {}", vehicleNumber); + + // 요청 객체 생성 - 차량번호만 설정 (나머지는 RequestEnricher가 자동 설정) + BasicRequest request = new BasicRequest(); + request.setVhrno(vehicleNumber); + + // Envelope로 감싸기 + Envelope requestEnvelope = new Envelope<>(request); + + // HTTP 헤더 설정 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> requestEntity = new HttpEntity<>(requestEnvelope, headers); + + try { + // API 호출 + ResponseEntity> responseEntity = restTemplate.exchange( + BASIC_INFO_URL, + HttpMethod.POST, + requestEntity, + new ParameterizedTypeReference>() {} + ); + + if (responseEntity.getStatusCode() == HttpStatus.OK && responseEntity.getBody() != null) { + List data = responseEntity.getBody().getData(); + if (data != null && !data.isEmpty()) { + return data.get(0); + } + } + + log.warn("차량 기본정보 조회 응답이 비어있음 - 차량번호: {}", vehicleNumber); + return null; + + } catch (Exception e) { + log.error("차량 기본정보 조회 API 호출 실패 - 차량번호: {}", vehicleNumber, e); + throw new RuntimeException("차량 기본정보 조회 실패: " + e.getMessage(), e); + } + } + + /** + * 자동차 등록원부(갑) 조회 API 호출 + * VMIS-interface의 RequestEnricher가 기본 설정값을 자동으로 채워주므로 차량번호만 전송 + * + * @param vehicleNumber 차량번호 + * @return 등록원부 정보 + */ + private LedgerResponse getLedgerInfo(String vehicleNumber) { + log.debug("자동차 등록원부 조회 API 호출 - 차량번호: {}", vehicleNumber); + + // 요청 객체 생성 - 차량번호만 설정 (나머지는 RequestEnricher가 자동 설정) + LedgerRequest request = new LedgerRequest(); + request.setVhrno(vehicleNumber); + + // Envelope로 감싸기 + Envelope requestEnvelope = new Envelope<>(request); + + // HTTP 헤더 설정 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> requestEntity = new HttpEntity<>(requestEnvelope, headers); + + try { + // API 호출 + ResponseEntity> responseEntity = restTemplate.exchange( + LEDGER_INFO_URL, + HttpMethod.POST, + requestEntity, + new ParameterizedTypeReference>() {} + ); + + if (responseEntity.getStatusCode() == HttpStatus.OK && responseEntity.getBody() != null) { + List data = responseEntity.getBody().getData(); + if (data != null && !data.isEmpty()) { + return data.get(0); + } + } + + log.warn("자동차 등록원부 조회 응답이 비어있음 - 차량번호: {}", vehicleNumber); + return null; + + } catch (Exception e) { + log.error("자동차 등록원부 조회 API 호출 실패 - 차량번호: {}", vehicleNumber, e); + throw new RuntimeException("자동차 등록원부 조회 실패: " + e.getMessage(), e); + } + } +} diff --git a/src/main/java/go/kr/project/api/external/service/ExternalVehicleInfoServiceImpl.java b/src/main/java/go/kr/project/api/external/service/impl/ExternalVehicleInfoServiceImpl.java similarity index 93% rename from src/main/java/go/kr/project/api/external/service/ExternalVehicleInfoServiceImpl.java rename to src/main/java/go/kr/project/api/external/service/impl/ExternalVehicleInfoServiceImpl.java index 4bea247..edfdd69 100644 --- a/src/main/java/go/kr/project/api/external/service/ExternalVehicleInfoServiceImpl.java +++ b/src/main/java/go/kr/project/api/external/service/impl/ExternalVehicleInfoServiceImpl.java @@ -1,6 +1,7 @@ -package go.kr.project.api.external.service; +package go.kr.project.api.external.service.impl; -import go.kr.project.api.VehicleInfoService; +import go.kr.project.api.external.service.ExternalVehicleApiService; +import go.kr.project.api.external.service.VehicleInfoService; import go.kr.project.api.model.VehicleApiResponseVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/go/kr/project/api/internal/service/VmisCarBassMatterInqireLogService.java b/src/main/java/go/kr/project/api/internal/service/VmisCarBassMatterInqireLogService.java index 252e1b3..2ed94c3 100644 --- a/src/main/java/go/kr/project/api/internal/service/VmisCarBassMatterInqireLogService.java +++ b/src/main/java/go/kr/project/api/internal/service/VmisCarBassMatterInqireLogService.java @@ -1,56 +1,25 @@ package go.kr.project.api.internal.service; -import go.kr.project.api.internal.mapper.VmisCarBassMatterInqireMapper; import go.kr.project.api.model.response.VmisCarBassMatterInqireVO; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; /** - * 자동차 기본사항 조회 로그 전용 서비스. + * 자동차 기본사항 조회 로그 전용 서비스 인터페이스 * *

로그 적재만 별도 트랜잭션(REQUIRES_NEW)으로 처리하여, * 외부 호출 실패나 상위 트랜잭션 롤백 상황에서도 로그는 영속화되도록 보장한다.

*/ -@Slf4j -@Service -@RequiredArgsConstructor -public class VmisCarBassMatterInqireLogService { - - private final VmisCarBassMatterInqireMapper carBassMatterInqireMapper; +public interface VmisCarBassMatterInqireLogService { /** * 최초 API 요청 정보를 등록한다. (REQUIRES_NEW) * @param request 요청 정보 * @return 생성된 ID */ - @Transactional(propagation = Propagation.REQUIRES_NEW) - public String createInitialRequestNewTx(VmisCarBassMatterInqireVO request) { - String generatedId = carBassMatterInqireMapper.selectNextCarBassMatterInqireId(); - request.setCarBassMatterInqire(generatedId); - int result = carBassMatterInqireMapper.insertCarBassMatterInqire(request); - if (result != 1) { - throw new RuntimeException("자동차 기본 사항 조회 정보 등록 실패"); - } - log.info("[BASIC-REQ-LOG] 요청 정보 저장 완료(별도TX) - ID: {}, 차량번호: {}", generatedId, request.getDmndVhrno()); - return generatedId; - } + String createInitialRequestNewTx(VmisCarBassMatterInqireVO request); /** * 응답/에러 결과를 업데이트한다. (REQUIRES_NEW) * @param response 업데이트 내용 */ - @Transactional(propagation = Propagation.REQUIRES_NEW) - public void updateResponseNewTx(VmisCarBassMatterInqireVO response) { - if (response.getCarBassMatterInqire() == null) { - throw new IllegalArgumentException("자동차 기본 사항 조회 ID는 필수입니다."); - } - int result = carBassMatterInqireMapper.updateCarBassMatterInqire(response); - if (result != 1) { - throw new RuntimeException("자동차 기본 사항 조회 정보 업데이트 실패 - ID: " + response.getCarBassMatterInqire()); - } - log.info("[BASIC-RES-LOG] 응답/에러 정보 저장 완료(별도TX) - ID: {}, 결과코드: {}", response.getCarBassMatterInqire(), response.getCntcResultCode()); - } + void updateResponseNewTx(VmisCarBassMatterInqireVO response); } diff --git a/src/main/java/go/kr/project/api/internal/service/VmisCarBassMatterInqireService.java b/src/main/java/go/kr/project/api/internal/service/VmisCarBassMatterInqireService.java index 05f45b9..65670a9 100644 --- a/src/main/java/go/kr/project/api/internal/service/VmisCarBassMatterInqireService.java +++ b/src/main/java/go/kr/project/api/internal/service/VmisCarBassMatterInqireService.java @@ -1,87 +1,28 @@ package go.kr.project.api.internal.service; -import go.kr.project.api.internal.client.GovernmentApi; -import go.kr.project.api.config.ApiConstant; -import go.kr.project.api.internal.util.ExceptionDetailUtil; import go.kr.project.api.model.request.BasicRequest; import go.kr.project.api.model.response.BasicResponse; -import go.kr.project.api.model.response.VmisCarBassMatterInqireVO; import go.kr.project.api.model.Envelope; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; /** - * 자동차 기본 사항 조회 서비스 + * 자동차 기본사항 조회 서비스 인터페이스 * - *

API 호출 정보를 관리하는 서비스 클래스입니다.

+ *

API 호출 정보를 관리하는 서비스입니다.

*
    - *
  • 최초 요청: createInitialRequest() - 시퀀스로 ID 생성 후 INSERT
  • - *
  • 결과 업데이트: updateResponse() - 응답 데이터 UPDATE
  • + *
  • 요청 데이터 보강
  • + *
  • 최초 요청 로그 저장
  • + *
  • 외부 API 호출
  • + *
  • 응답 로그 업데이트
  • *
*/ -@Slf4j -@Service -@RequiredArgsConstructor -public class VmisCarBassMatterInqireService { - - private final GovernmentApi governmentApi; - private final VmisRequestEnricher enricher; - private final VmisCarBassMatterInqireLogService logService; - - - +public interface VmisCarBassMatterInqireService { /** - * 자동차 기본사항 조회: 보강 -> 최초요청로그 -> 외부호출 -> 응답로그. + * 자동차 기본사항 조회 + * + * @param envelope 요청 Envelope + * @return 응답 Envelope */ - @Transactional - public ResponseEntity> basic(Envelope envelope) { - // 1) 요청 보강 - enricher.enrichBasic(envelope); - - String generatedId = null; - try { - // 2) 최초 요청 로그 저장 (첫 번째 데이터 기준) - if (envelope.getData() != null && !envelope.getData().isEmpty()) { - BasicRequest req = envelope.getData().get(0); - VmisCarBassMatterInqireVO logEntity = VmisCarBassMatterInqireVO.fromRequest(req); - generatedId = logService.createInitialRequestNewTx(logEntity); - } - - // 3) 외부 API 호출 - ResponseEntity> response = governmentApi.callBasic(envelope); - - // 4) 응답 로그 업데이트 - // 원본 소스, 정상적인 호출, 리턴(에러 리턴포함) 일 경우에만 에러 로그 남김 - if (generatedId != null && response.getBody() != null) { - VmisCarBassMatterInqireVO update = VmisCarBassMatterInqireVO.fromResponse(generatedId, response.getBody()); - if (update != null) { - logService.updateResponseNewTx(update); - } - } - - return response; - } catch (Exception e) { - // 5) 오류 로그 업데이트 - if (generatedId != null) { - try { - String detail = ExceptionDetailUtil.buildForLog(e); - VmisCarBassMatterInqireVO errorLog = VmisCarBassMatterInqireVO.builder() - .carBassMatterInqire(generatedId) // 자동차기본사항조회 ID (PK) - .cntcResultCode(ApiConstant.CNTC_RESULT_CODE_ERROR) // 연계결과코드 (에러) - .cntcResultDtls(detail) // 연계결과상세 (에러 메시지) - .build(); - logService.updateResponseNewTx(errorLog); - log.error("[BASIC-ERR-LOG] API 호출 에러 정보 저장 완료(별도TX) - ID: {}, detail: {}", generatedId, detail, e); - } catch (Exception ignore) { - log.error("[BASIC-ERR-LOG] 에러 로그 저장 실패 - ID: {}", generatedId, ignore); - } - } - throw e; - } - } - + ResponseEntity> basic(Envelope envelope); } diff --git a/src/main/java/go/kr/project/api/internal/service/VmisCarLedgerFrmbkLogService.java b/src/main/java/go/kr/project/api/internal/service/VmisCarLedgerFrmbkLogService.java index 2ed852c..841d193 100644 --- a/src/main/java/go/kr/project/api/internal/service/VmisCarLedgerFrmbkLogService.java +++ b/src/main/java/go/kr/project/api/internal/service/VmisCarLedgerFrmbkLogService.java @@ -1,61 +1,33 @@ package go.kr.project.api.internal.service; -import go.kr.project.api.internal.mapper.VmisCarLedgerFrmbkMapper; import go.kr.project.api.model.response.VmisCarLedgerFrmbkDtlVO; import go.kr.project.api.model.response.VmisCarLedgerFrmbkVO; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; import java.util.List; /** - * 자동차 등록 원부(갑) 로그 전용 서비스. + * 자동차 등록 원부(갑) 로그 전용 서비스 인터페이스 * - 모든 로깅(write)은 별도 트랜잭션(REQUIRES_NEW)으로 수행하여 상위 트랜잭션 롤백의 영향을 받지 않도록 한다. */ -@Slf4j -@Service -@RequiredArgsConstructor -public class VmisCarLedgerFrmbkLogService { +public interface VmisCarLedgerFrmbkLogService { - private final VmisCarLedgerFrmbkMapper mapper; + /** + * 최초 요청 정보를 등록한다. (REQUIRES_NEW) + * @param request 요청 정보 + * @return 생성된 ID + */ + String createInitialRequestNewTx(VmisCarLedgerFrmbkVO request); - @Transactional(propagation = Propagation.REQUIRES_NEW) - public String createInitialRequestNewTx(VmisCarLedgerFrmbkVO request) { - String id = mapper.selectNextCarLedgerFrmbkId(); - request.setCarLedgerFrmbkId(id); - int result = mapper.insertCarLedgerFrmbk(request); - if (result != 1) { - throw new RuntimeException("자동차 등록 원부(갑) 최초요청 등록 실패"); - } - log.info("[LEDGER-REQ-LOG] 최초 요청 저장(별도TX) - ID: {}, 차량번호: {}", id, request.getDmndVhrno()); - return id; - } + /** + * 응답 결과를 업데이트한다. (REQUIRES_NEW) + * @param response 업데이트 내용 + */ + void updateResponseNewTx(VmisCarLedgerFrmbkVO response); - @Transactional(propagation = Propagation.REQUIRES_NEW) - public void updateResponseNewTx(VmisCarLedgerFrmbkVO response) { - if (response.getCarLedgerFrmbkId() == null) { - throw new IllegalArgumentException("자동차 등록 원부(갑) ID는 필수입니다."); - } - int updated = mapper.updateCarLedgerFrmbk(response); - if (updated != 1) { - throw new RuntimeException("자동차 등록 원부(갑) 정보 업데이트 실패 - ID: " + response.getCarLedgerFrmbkId()); - } - log.info("[LEDGER-RES-LOG] 마스터 응답 업데이트(별도TX) - ID: {}, 결과코드: {}", - response.getCarLedgerFrmbkId(), response.getCntcResultCode()); - } - - @Transactional(propagation = Propagation.REQUIRES_NEW) - public void saveDetailsNewTx(String masterId, List details) { - if (details == null || details.isEmpty()) return; - for (VmisCarLedgerFrmbkDtlVO dtl : details) { - String dtlId = mapper.selectNextCarLedgerFrmbkDtlId(); - dtl.setCarLedgerFrmbkDtlId(dtlId); - dtl.setCarLedgerFrmbkId(masterId); - mapper.insertCarLedgerFrmbkDtl(dtl); - } - log.info("[LEDGER-RES-LOG] 상세 {}건 저장(별도TX) - ID: {}", details.size(), masterId); - } + /** + * 상세 정보를 저장한다. (REQUIRES_NEW) + * @param masterId 마스터 ID + * @param details 상세 정보 리스트 + */ + void saveDetailsNewTx(String masterId, List details); } diff --git a/src/main/java/go/kr/project/api/internal/service/VmisCarLedgerFrmbkService.java b/src/main/java/go/kr/project/api/internal/service/VmisCarLedgerFrmbkService.java index 5e4e965..3e30c10 100644 --- a/src/main/java/go/kr/project/api/internal/service/VmisCarLedgerFrmbkService.java +++ b/src/main/java/go/kr/project/api/internal/service/VmisCarLedgerFrmbkService.java @@ -1,85 +1,21 @@ package go.kr.project.api.internal.service; -import go.kr.project.api.internal.client.GovernmentApi; -import go.kr.project.api.config.ApiConstant; import go.kr.project.api.model.Envelope; -import go.kr.project.api.model.response.VmisCarLedgerFrmbkDtlVO; -import go.kr.project.api.model.response.VmisCarLedgerFrmbkVO; import go.kr.project.api.model.request.LedgerRequest; import go.kr.project.api.model.response.LedgerResponse; -import go.kr.project.api.internal.util.ExceptionDetailUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; /** - * 자동차 등록 원부(갑) 서비스 (오케스트레이션) + * 자동차 등록 원부(갑) 서비스 인터페이스 * - 요청 보강, 외부 API 호출, 로그 서비스 위임 */ -@Slf4j -@Service -@RequiredArgsConstructor -public class VmisCarLedgerFrmbkService { - - private final GovernmentApi governmentApi; - private final VmisRequestEnricher enricher; - private final VmisCarLedgerFrmbkLogService logService; +public interface VmisCarLedgerFrmbkService { /** - * 자동차 등록원부(갑) 조회: 보강 -> 최초요청로그(별도TX) -> 외부호출 -> 응답로그(마스터/상세, 별도TX) -> 오류 시 에러로그(별도TX). + * 자동차 등록원부(갑) 조회 + * + * @param envelope 요청 Envelope + * @return 응답 Envelope */ - @Transactional - public ResponseEntity> ledger(Envelope envelope) { - // 1) 요청 보강 - enricher.enrichLedger(envelope); - - String generatedId = null; - try { - // 2) 최초 요청 로그 저장 (첫 번째 데이터 기준) - if (envelope.getData() != null && !envelope.getData().isEmpty()) { - LedgerRequest req = envelope.getData().get(0); - VmisCarLedgerFrmbkVO init = VmisCarLedgerFrmbkVO.fromRequest(req); - generatedId = logService.createInitialRequestNewTx(init); - } - - // 3) 외부 API 호출 - ResponseEntity> response = governmentApi.callLedger(envelope); - - // 4) 응답 로그 업데이트 (마스터 + 상세) - if (generatedId != null && response.getBody() != null && - response.getBody().getData() != null && !response.getBody().getData().isEmpty()) { - LedgerResponse body = response.getBody().getData().get(0); - VmisCarLedgerFrmbkVO masterUpdate = VmisCarLedgerFrmbkVO.fromResponseMaster(generatedId, body); - logService.updateResponseNewTx(masterUpdate); - - List details = VmisCarLedgerFrmbkDtlVO.listFromResponse(body, generatedId); - if (details != null && !details.isEmpty()) { - logService.saveDetailsNewTx(generatedId, details); - } - } - - return response; - } catch (Exception e) { - // 5) 오류 로그 업데이트 - if (generatedId != null) { - try { - String detail = ExceptionDetailUtil.buildForLog(e); - VmisCarLedgerFrmbkVO errorLog = VmisCarLedgerFrmbkVO.builder() - .carLedgerFrmbkId(generatedId) - .cntcResultCode(ApiConstant.CNTC_RESULT_CODE_ERROR) - .cntcResultDtls(detail) - .build(); - logService.updateResponseNewTx(errorLog); - log.error("[LEDGER-ERR-LOG] API 호출 에러 정보 저장 완료(별도TX) - ID: {}, detail: {}", generatedId, detail, e); - } catch (Exception ignore) { - log.error("[LEDGER-ERR-LOG] 에러 로그 저장 실패 - ID: {}", generatedId, ignore); - } - } - throw e; - } - } + ResponseEntity> ledger(Envelope envelope); } diff --git a/src/main/java/go/kr/project/api/internal/service/InternalVehicleInfoServiceImpl.java b/src/main/java/go/kr/project/api/internal/service/impl/InternalVehicleInfoServiceImpl.java similarity index 97% rename from src/main/java/go/kr/project/api/internal/service/InternalVehicleInfoServiceImpl.java rename to src/main/java/go/kr/project/api/internal/service/impl/InternalVehicleInfoServiceImpl.java index a1f0438..23e4b05 100644 --- a/src/main/java/go/kr/project/api/internal/service/InternalVehicleInfoServiceImpl.java +++ b/src/main/java/go/kr/project/api/internal/service/impl/InternalVehicleInfoServiceImpl.java @@ -1,6 +1,8 @@ -package go.kr.project.api.internal.service; +package go.kr.project.api.internal.service.impl; -import go.kr.project.api.VehicleInfoService; +import go.kr.project.api.external.service.VehicleInfoService; +import go.kr.project.api.internal.service.VmisCarBassMatterInqireService; +import go.kr.project.api.internal.service.VmisCarLedgerFrmbkService; import go.kr.project.api.model.VehicleApiResponseVO; import go.kr.project.api.model.request.BasicRequest; import go.kr.project.api.model.response.BasicResponse; diff --git a/src/main/java/go/kr/project/api/internal/service/impl/VmisCarBassMatterInqireLogServiceImpl.java b/src/main/java/go/kr/project/api/internal/service/impl/VmisCarBassMatterInqireLogServiceImpl.java new file mode 100644 index 0000000..fdb3ecd --- /dev/null +++ b/src/main/java/go/kr/project/api/internal/service/impl/VmisCarBassMatterInqireLogServiceImpl.java @@ -0,0 +1,59 @@ +package go.kr.project.api.internal.service.impl; + +import go.kr.project.api.internal.mapper.VmisCarBassMatterInqireMapper; +import go.kr.project.api.internal.service.VmisCarBassMatterInqireLogService; +import go.kr.project.api.model.response.VmisCarBassMatterInqireVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +/** + * 자동차 기본사항 조회 로그 전용 서비스 구현체 + * + *

로그 적재만 별도 트랜잭션(REQUIRES_NEW)으로 처리하여, + * 외부 호출 실패나 상위 트랜잭션 롤백 상황에서도 로그는 영속화되도록 보장한다.

+ */ +@Slf4j +@Service +@RequiredArgsConstructor +public class VmisCarBassMatterInqireLogServiceImpl implements VmisCarBassMatterInqireLogService { + + private final VmisCarBassMatterInqireMapper carBassMatterInqireMapper; + + /** + * 최초 API 요청 정보를 등록한다. (REQUIRES_NEW) + * @param request 요청 정보 + * @return 생성된 ID + */ + @Override + @Transactional(propagation = Propagation.REQUIRES_NEW) + public String createInitialRequestNewTx(VmisCarBassMatterInqireVO request) { + String generatedId = carBassMatterInqireMapper.selectNextCarBassMatterInqireId(); + request.setCarBassMatterInqire(generatedId); + int result = carBassMatterInqireMapper.insertCarBassMatterInqire(request); + if (result != 1) { + throw new RuntimeException("자동차 기본 사항 조회 정보 등록 실패"); + } + log.info("[BASIC-REQ-LOG] 요청 정보 저장 완료(별도TX) - ID: {}, 차량번호: {}", generatedId, request.getDmndVhrno()); + return generatedId; + } + + /** + * 응답/에러 결과를 업데이트한다. (REQUIRES_NEW) + * @param response 업데이트 내용 + */ + @Override + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void updateResponseNewTx(VmisCarBassMatterInqireVO response) { + if (response.getCarBassMatterInqire() == null) { + throw new IllegalArgumentException("자동차 기본 사항 조회 ID는 필수입니다."); + } + int result = carBassMatterInqireMapper.updateCarBassMatterInqire(response); + if (result != 1) { + throw new RuntimeException("자동차 기본 사항 조회 정보 업데이트 실패 - ID: " + response.getCarBassMatterInqire()); + } + log.info("[BASIC-RES-LOG] 응답/에러 정보 저장 완료(별도TX) - ID: {}, 결과코드: {}", response.getCarBassMatterInqire(), response.getCntcResultCode()); + } +} diff --git a/src/main/java/go/kr/project/api/internal/service/impl/VmisCarBassMatterInqireServiceImpl.java b/src/main/java/go/kr/project/api/internal/service/impl/VmisCarBassMatterInqireServiceImpl.java new file mode 100644 index 0000000..b0f30b6 --- /dev/null +++ b/src/main/java/go/kr/project/api/internal/service/impl/VmisCarBassMatterInqireServiceImpl.java @@ -0,0 +1,87 @@ +package go.kr.project.api.internal.service.impl; + +import go.kr.project.api.internal.client.GovernmentApi; +import go.kr.project.api.config.ApiConstant; +import go.kr.project.api.internal.service.VmisCarBassMatterInqireLogService; +import go.kr.project.api.internal.service.VmisCarBassMatterInqireService; +import go.kr.project.api.internal.service.VmisRequestEnricher; +import go.kr.project.api.internal.util.ExceptionDetailUtil; +import go.kr.project.api.model.request.BasicRequest; +import go.kr.project.api.model.response.BasicResponse; +import go.kr.project.api.model.response.VmisCarBassMatterInqireVO; +import go.kr.project.api.model.Envelope; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 자동차 기본 사항 조회 서비스 구현체 + * + *

API 호출 정보를 관리하는 서비스 클래스입니다.

+ *
    + *
  • 최초 요청: createInitialRequest() - 시퀀스로 ID 생성 후 INSERT
  • + *
  • 결과 업데이트: updateResponse() - 응답 데이터 UPDATE
  • + *
+ */ +@Slf4j +@Service +@RequiredArgsConstructor +public class VmisCarBassMatterInqireServiceImpl implements VmisCarBassMatterInqireService { + + private final GovernmentApi governmentApi; + private final VmisRequestEnricher enricher; + private final VmisCarBassMatterInqireLogService logService; + + /** + * 자동차 기본사항 조회: 보강 -> 최초요청로그 -> 외부호출 -> 응답로그. + */ + @Override + @Transactional + public ResponseEntity> basic(Envelope envelope) { + // 1) 요청 보강 + enricher.enrichBasic(envelope); + + String generatedId = null; + try { + // 2) 최초 요청 로그 저장 (첫 번째 데이터 기준) + if (envelope.getData() != null && !envelope.getData().isEmpty()) { + BasicRequest req = envelope.getData().get(0); + VmisCarBassMatterInqireVO logEntity = VmisCarBassMatterInqireVO.fromRequest(req); + generatedId = logService.createInitialRequestNewTx(logEntity); + } + + // 3) 외부 API 호출 + ResponseEntity> response = governmentApi.callBasic(envelope); + + // 4) 응답 로그 업데이트 + // 원본 소스, 정상적인 호출, 리턴(에러 리턴포함) 일 경우에만 에러 로그 남김 + if (generatedId != null && response.getBody() != null) { + VmisCarBassMatterInqireVO update = VmisCarBassMatterInqireVO.fromResponse(generatedId, response.getBody()); + if (update != null) { + logService.updateResponseNewTx(update); + } + } + + return response; + } catch (Exception e) { + // 5) 오류 로그 업데이트 + if (generatedId != null) { + try { + String detail = ExceptionDetailUtil.buildForLog(e); + VmisCarBassMatterInqireVO errorLog = VmisCarBassMatterInqireVO.builder() + .carBassMatterInqire(generatedId) // 자동차기본사항조회 ID (PK) + .cntcResultCode(ApiConstant.CNTC_RESULT_CODE_ERROR) // 연계결과코드 (에러) + .cntcResultDtls(detail) // 연계결과상세 (에러 메시지) + .build(); + logService.updateResponseNewTx(errorLog); + log.error("[BASIC-ERR-LOG] API 호출 에러 정보 저장 완료(별도TX) - ID: {}, detail: {}", generatedId, detail, e); + } catch (Exception ignore) { + log.error("[BASIC-ERR-LOG] 에러 로그 저장 실패 - ID: {}", generatedId, ignore); + } + } + throw e; + } + } +} diff --git a/src/main/java/go/kr/project/api/internal/service/impl/VmisCarLedgerFrmbkLogServiceImpl.java b/src/main/java/go/kr/project/api/internal/service/impl/VmisCarLedgerFrmbkLogServiceImpl.java new file mode 100644 index 0000000..7ee1b65 --- /dev/null +++ b/src/main/java/go/kr/project/api/internal/service/impl/VmisCarLedgerFrmbkLogServiceImpl.java @@ -0,0 +1,65 @@ +package go.kr.project.api.internal.service.impl; + +import go.kr.project.api.internal.mapper.VmisCarLedgerFrmbkMapper; +import go.kr.project.api.internal.service.VmisCarLedgerFrmbkLogService; +import go.kr.project.api.model.response.VmisCarLedgerFrmbkDtlVO; +import go.kr.project.api.model.response.VmisCarLedgerFrmbkVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 자동차 등록 원부(갑) 로그 전용 서비스 구현체 + * - 모든 로깅(write)은 별도 트랜잭션(REQUIRES_NEW)으로 수행하여 상위 트랜잭션 롤백의 영향을 받지 않도록 한다. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class VmisCarLedgerFrmbkLogServiceImpl implements VmisCarLedgerFrmbkLogService { + + private final VmisCarLedgerFrmbkMapper mapper; + + @Override + @Transactional(propagation = Propagation.REQUIRES_NEW) + public String createInitialRequestNewTx(VmisCarLedgerFrmbkVO request) { + String id = mapper.selectNextCarLedgerFrmbkId(); + request.setCarLedgerFrmbkId(id); + int result = mapper.insertCarLedgerFrmbk(request); + if (result != 1) { + throw new RuntimeException("자동차 등록 원부(갑) 최초요청 등록 실패"); + } + log.info("[LEDGER-REQ-LOG] 최초 요청 저장(별도TX) - ID: {}, 차량번호: {}", id, request.getDmndVhrno()); + return id; + } + + @Override + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void updateResponseNewTx(VmisCarLedgerFrmbkVO response) { + if (response.getCarLedgerFrmbkId() == null) { + throw new IllegalArgumentException("자동차 등록 원부(갑) ID는 필수입니다."); + } + int updated = mapper.updateCarLedgerFrmbk(response); + if (updated != 1) { + throw new RuntimeException("자동차 등록 원부(갑) 정보 업데이트 실패 - ID: " + response.getCarLedgerFrmbkId()); + } + log.info("[LEDGER-RES-LOG] 마스터 응답 업데이트(별도TX) - ID: {}, 결과코드: {}", + response.getCarLedgerFrmbkId(), response.getCntcResultCode()); + } + + @Override + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void saveDetailsNewTx(String masterId, List details) { + if (details == null || details.isEmpty()) return; + for (VmisCarLedgerFrmbkDtlVO dtl : details) { + String dtlId = mapper.selectNextCarLedgerFrmbkDtlId(); + dtl.setCarLedgerFrmbkDtlId(dtlId); + dtl.setCarLedgerFrmbkId(masterId); + mapper.insertCarLedgerFrmbkDtl(dtl); + } + log.info("[LEDGER-RES-LOG] 상세 {}건 저장(별도TX) - ID: {}", details.size(), masterId); + } +} diff --git a/src/main/java/go/kr/project/api/internal/service/impl/VmisCarLedgerFrmbkServiceImpl.java b/src/main/java/go/kr/project/api/internal/service/impl/VmisCarLedgerFrmbkServiceImpl.java new file mode 100644 index 0000000..6052736 --- /dev/null +++ b/src/main/java/go/kr/project/api/internal/service/impl/VmisCarLedgerFrmbkServiceImpl.java @@ -0,0 +1,89 @@ +package go.kr.project.api.internal.service.impl; + +import go.kr.project.api.internal.client.GovernmentApi; +import go.kr.project.api.config.ApiConstant; +import go.kr.project.api.internal.service.VmisCarLedgerFrmbkLogService; +import go.kr.project.api.internal.service.VmisCarLedgerFrmbkService; +import go.kr.project.api.internal.service.VmisRequestEnricher; +import go.kr.project.api.model.Envelope; +import go.kr.project.api.model.response.VmisCarLedgerFrmbkDtlVO; +import go.kr.project.api.model.response.VmisCarLedgerFrmbkVO; +import go.kr.project.api.model.request.LedgerRequest; +import go.kr.project.api.model.response.LedgerResponse; +import go.kr.project.api.internal.util.ExceptionDetailUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 자동차 등록 원부(갑) 서비스 구현체 (오케스트레이션) + * - 요청 보강, 외부 API 호출, 로그 서비스 위임 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class VmisCarLedgerFrmbkServiceImpl implements VmisCarLedgerFrmbkService { + + private final GovernmentApi governmentApi; + private final VmisRequestEnricher enricher; + private final VmisCarLedgerFrmbkLogService logService; + + /** + * 자동차 등록원부(갑) 조회: 보강 -> 최초요청로그(별도TX) -> 외부호출 -> 응답로그(마스터/상세, 별도TX) -> 오류 시 에러로그(별도TX). + */ + @Override + @Transactional + public ResponseEntity> ledger(Envelope envelope) { + // 1) 요청 보강 + enricher.enrichLedger(envelope); + + String generatedId = null; + try { + // 2) 최초 요청 로그 저장 (첫 번째 데이터 기준) + if (envelope.getData() != null && !envelope.getData().isEmpty()) { + LedgerRequest req = envelope.getData().get(0); + VmisCarLedgerFrmbkVO init = VmisCarLedgerFrmbkVO.fromRequest(req); + generatedId = logService.createInitialRequestNewTx(init); + } + + // 3) 외부 API 호출 + ResponseEntity> response = governmentApi.callLedger(envelope); + + // 4) 응답 로그 업데이트 (마스터 + 상세) + if (generatedId != null && response.getBody() != null && + response.getBody().getData() != null && !response.getBody().getData().isEmpty()) { + LedgerResponse body = response.getBody().getData().get(0); + VmisCarLedgerFrmbkVO masterUpdate = VmisCarLedgerFrmbkVO.fromResponseMaster(generatedId, body); + logService.updateResponseNewTx(masterUpdate); + + List details = VmisCarLedgerFrmbkDtlVO.listFromResponse(body, generatedId); + if (details != null && !details.isEmpty()) { + logService.saveDetailsNewTx(generatedId, details); + } + } + + return response; + } catch (Exception e) { + // 5) 오류 로그 업데이트 + if (generatedId != null) { + try { + String detail = ExceptionDetailUtil.buildForLog(e); + VmisCarLedgerFrmbkVO errorLog = VmisCarLedgerFrmbkVO.builder() + .carLedgerFrmbkId(generatedId) + .cntcResultCode(ApiConstant.CNTC_RESULT_CODE_ERROR) + .cntcResultDtls(detail) + .build(); + logService.updateResponseNewTx(errorLog); + log.error("[LEDGER-ERR-LOG] API 호출 에러 정보 저장 완료(별도TX) - ID: {}, detail: {}", generatedId, detail, e); + } catch (Exception ignore) { + log.error("[LEDGER-ERR-LOG] 에러 로그 저장 실패 - ID: {}", generatedId, ignore); + } + } + throw e; + } + } +} diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index b5b8493..d3eb716 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -163,7 +163,7 @@ juso: # ===== VMIS 통합 설정 (Local 환경) ===== vmis: integration: - mode: external # internal: 내부 VMIS 모듈 직접 호출, external: 외부 REST API 호출 + mode: internal # internal: 내부 VMIS 모듈 직접 호출, external: 외부 REST API 호출 # RestTemplate 설정 (모드별 분기) rest-template: