diff --git a/src/main/java/com/vmis/interfaceapp/client/GovernmentApi.java b/src/main/java/com/vmis/interfaceapp/client/GovernmentApi.java new file mode 100644 index 0000000..f4e06b2 --- /dev/null +++ b/src/main/java/com/vmis/interfaceapp/client/GovernmentApi.java @@ -0,0 +1,21 @@ +package com.vmis.interfaceapp.client; + +import com.vmis.interfaceapp.model.basic.BasicRequest; +import com.vmis.interfaceapp.model.basic.BasicResponse; +import com.vmis.interfaceapp.model.common.Envelope; +import com.vmis.interfaceapp.model.ledger.LedgerRequest; +import com.vmis.interfaceapp.model.ledger.LedgerResponse; +import org.springframework.http.ResponseEntity; + +/** + * 정부 시스템 연계 API 추상화 인터페이스. + * + *

외부 정부 시스템과의 통신 계약을 명확히 하여 테스트 용이성과 + * 추후 교체 가능성을 높입니다.

+ */ +public interface GovernmentApi { + + ResponseEntity> callBasic(Envelope envelope); + + ResponseEntity> callLedger(Envelope envelope); +} diff --git a/src/main/java/com/vmis/interfaceapp/client/GovernmentApiClient.java b/src/main/java/com/vmis/interfaceapp/client/GovernmentApiClient.java index eed35c3..e8822dd 100644 --- a/src/main/java/com/vmis/interfaceapp/client/GovernmentApiClient.java +++ b/src/main/java/com/vmis/interfaceapp/client/GovernmentApiClient.java @@ -6,7 +6,6 @@ import com.vmis.interfaceapp.config.properties.VmisProperties; import com.vmis.interfaceapp.gpki.GpkiService; import com.vmis.interfaceapp.model.basic.BasicRequest; import com.vmis.interfaceapp.model.basic.BasicResponse; -import com.vmis.interfaceapp.model.basic.CarBassMatterInqireVO; import com.vmis.interfaceapp.model.common.Envelope; import com.vmis.interfaceapp.model.ledger.LedgerRequest; import com.vmis.interfaceapp.model.ledger.LedgerResponse; @@ -60,7 +59,7 @@ import java.nio.charset.StandardCharsets; @Slf4j @RequiredArgsConstructor @Component -public class GovernmentApiClient { +public class GovernmentApiClient implements GovernmentApi { /** * Spring RestTemplate @@ -125,12 +124,6 @@ public class GovernmentApiClient { */ private final ObjectMapper objectMapper; - /** - * 자동차 기본 사항 조회 로그 서비스 - * - *

API 호출 정보를 DB에 로그성으로 저장합니다.

- */ - private final com.vmis.interfaceapp.service.CarBassMatterInqireService carBassMatterInqireService; /** * 서비스 타입 열거형 @@ -322,34 +315,8 @@ public class GovernmentApiClient { * @return ResponseEntity<Envelope<BasicResponse>> 조회 결과를 담은 응답 */ public ResponseEntity> callBasic(Envelope envelope) { - String generatedId = null; - - try { - // 1. 요청 정보 DB 저장 (첫 번째 요청만 저장) - if (envelope.getData() != null && !envelope.getData().isEmpty()) { - com.vmis.interfaceapp.model.basic.BasicRequest request = envelope.getData().get(0); - generatedId = saveRequestLog(request); - log.info("[BASIC-REQ-LOG] 요청 정보 저장 완료 - ID: {}, 차량번호: {}", generatedId, request.getVhrno()); - } - - // 2. 정부 API 호출 - ResponseEntity> response = callModel(ServiceType.BASIC, envelope, new TypeReference>(){}); - - // 3. 응답 정보 DB 업데이트 - if (generatedId != null && response.getBody() != null) { - updateResponseLog(generatedId, response.getBody()); - log.info("[BASIC-RES-LOG] 응답 정보 저장 완료 - ID: {}", generatedId); - } - - return response; - - } catch (Exception e) { - // 4. 에러 발생 시 에러 정보 DB 업데이트 - if (generatedId != null) { - updateErrorLog(generatedId, e); - } - throw e; - } + // 순수한 전송 책임만 수행: DB 로깅은 서비스 레이어에서 처리 + return callModel(ServiceType.BASIC, envelope, new TypeReference>(){}); } /** @@ -657,174 +624,4 @@ public class GovernmentApiClient { } } - /** - * 요청 정보를 DB에 저장 - * - * @param request 요청 정보 - * @return 생성된 ID - */ - private String saveRequestLog(com.vmis.interfaceapp.model.basic.BasicRequest request) { - CarBassMatterInqireVO logEntity = CarBassMatterInqireVO.builder() - .infoSysId(request.getInfoSysId()) - .infoSysIp(request.getInfoSysIp()) - .sigunguCode(request.getSigunguCode()) - .cntcInfoCode(request.getCntcInfoCode()) - .chargerId(request.getChargerId()) - .chargerIp(request.getChargerIp()) - .chargerNm(request.getChargerNm()) - .dmndLevyStdde(request.getLevyStdde()) - .dmndInqireSeCode(request.getInqireSeCode()) - .dmndVhrno(request.getVhrno()) - .dmndVin(request.getVin()) - .rgtr("SYSTEM") - .build(); - - return carBassMatterInqireService.createInitialRequest(logEntity); - } - - /** - * 응답 정보를 DB에 업데이트 - * - * @param id 저장된 ID - * @param envelope 응답 정보 - */ - private void updateResponseLog(String id, Envelope envelope) { - com.vmis.interfaceapp.model.basic.BasicResponse response = envelope.getData() != null && !envelope.getData().isEmpty() - ? envelope.getData().get(0) - : null; - - if (response == null) { - log.warn("[BASIC-RES-LOG] 응답 데이터가 없습니다 - ID: {}", id); - return; - } - - // Builder 패턴으로 응답 정보 매핑 - CarBassMatterInqireVO.CarBassMatterInqireVOBuilder builder = CarBassMatterInqireVO.builder() - .carBassMatterInqire(id) - .cntcResultCode(response.getCntcResultCode()) - .cntcResultDtls(response.getCntcResultDtls()); - - // record가 있으면 첫 번째 record의 정보를 매핑 - if (response.getRecord() != null && !response.getRecord().isEmpty()) { - com.vmis.interfaceapp.model.basic.BasicResponse.Record record = response.getRecord().get(0); - mapRecordToEntity(builder, record); - } - - carBassMatterInqireService.updateResponse(builder.build()); - } - - /** - * Record 정보를 Entity에 매핑 - * - * @param builder Entity Builder - * @param record Record 정보 - */ - private void mapRecordToEntity(CarBassMatterInqireVO.CarBassMatterInqireVOBuilder builder, com.vmis.interfaceapp.model.basic.BasicResponse.Record record) { - builder - .prye(record.getPrye()) - .registDe(record.getRegistDe()) - .ersrRegistSeCode(record.getErsrRegistSeCode()) - .ersrRegistSeNm(record.getErsrRegistSeNm()) - .ersrRegistDe(record.getErsrRegistDe()) - .registDetailCode(record.getRegistDetailCode()) - .dsplvl(record.getDsplvl()) - .useStrnghldLegaldongCode(record.getUseStrnghldLegaldongCode()) - .useStrnghldAdstrdCode(record.getUseStrnghldAdstrdCode()) - .useStrnghldMntn(record.getUseStrnghldMntn()) - .useStrnghldLnbr(record.getUseStrnghldLnbr()) - .useStrnghldHo(record.getUseStrnghldHo()) - .useStrnghldAdresNm(record.getUseStrnghldAdresNm()) - .useStrnghldRoadNmCode(record.getUseStrnghldRoadNmCode()) - .usgsrhldUndgrndBuldSeCode(record.getUsgsrhldUndgrndBuldSeCode()) - .useStrnghldBuldMainNo(record.getUseStrnghldBuldMainNo()) - .useStrnghldBuldSubNo(record.getUseStrnghldBuldSubNo()) - .usgsrhldAdresFull(record.getUsgsrhldAdresFull()) - .mberSeCode(record.getMberSeCode()) - .mberSeNo(record.getMberSeNo()) - .mberNm(record.getMberNm()) - .telno(record.getTelno()) - .ownerLegaldongCode(record.getOwnerLegaldongCode()) - .ownerAdstrdCode(record.getOwnerAdstrdCode()) - .ownerMntn(record.getOwnerMntn()) - .ownerLnbr(record.getOwnerLnbr()) - .ownerHo(record.getOwnerHo()) - .ownerAdresNm(record.getOwnerAdresNm()) - .ownerRoadNmCode(record.getOwnerRoadNmCode()) - .ownerUndgrndBuldSeCode(record.getOwnerUndgrndBuldSeCode()) - .ownerBuldMainNo(record.getOwnerBuldMainNo()) - .ownerBuldSubNo(record.getOwnerBuldSubNo()) - .ownrWholaddr(record.getOwnerAdresFull()) - .aftrVhrno(record.getAftrVhrno()) - .useFuelCode(record.getUseFuelCode()) - .prposSeCode(record.getPrposSeCode()) - .mtrsFomNm(record.getMtrsFomNm()) - .frntVhrno(record.getFrntVhrno()) - .vhclno(record.getVhrno()) - .vin(record.getVin()) - .cnm(record.getCnm()) - .vhcleTotWt(record.getVhcleTotWt()) - .caagEndde(record.getCaagEndde()) - .changeDe(record.getChangeDe()) - .vhctyAsortCode(record.getVhctyAsortCode()) - .vhctyTyCode(record.getVhctyTyCode()) - .vhctySeCode(record.getVhctySeCode()) - .mxmmLdg(record.getMxmmLdg()) - .vhctyAsortNm(record.getVhctyAsortNm()) - .vhctyTyNm(record.getVhctyTyNm()) - .vhctySeNm(record.getVhctySeNm()) - .frstRegistDe(record.getFrstRegistDe()) - .fomNm(record.getFomNm()) - .acqsDe(record.getAcqsDe()) - .acqsEndDe(record.getAcqsEndDe()) - .yblMd(record.getYblMd()) - .transrRegistDe(record.getTransrRegistDe()) - .spcfRegistSttusCode(record.getSpcfRegistSttusCode()) - .colorNm(record.getColorNm()) - .mrtgCo(record.getMrtgCo()) - .seizrCo(record.getSeizrCo()) - .stmdCo(record.getStmdCo()) - .nmplCsdyAt(record.getNmplCsdyAt()) - .nmplCsdyRemnrDe(record.getNmplCsdyRemnrDe()) - .originSeCode(record.getOriginSeCode()) - .nmplStndrdCode(record.getNmplStndrdCode()) - .acqsAmount(record.getAcqsAmount()) - .insptValidPdBgnde(record.getInsptValidPdBgnde()) - .insptValidPdEndde(record.getInsptValidPdEndde()) - .useStrnghldGrcCode(record.getUseStrnghldGrcCode()) - .tkcarPscapCo(record.getTkcarPscapCo()) - .spmnno(record.getSpmnno()) - .trvlDstnc(record.getTrvlDstnc()) - .frstRegistRqrcno(record.getFrstRegistRqrcno()) - .vlntErsrPrvntcNticeDe(record.getVlntErsrPrvntcNticeDe()) - .registInsttNm(record.getRegistInsttNm()) - .processImprtyResnCode(record.getProcessImprtyResnCode()) - .processImprtyResnDtls(record.getProcessImprtyResnDtls()) - .cbdLt(record.getCbdLt()) - .cbdBt(record.getCbdBt()) - .cbdHg(record.getCbdHg()) - .frstMxmmLdg(record.getFrstMxmmLdg()) - .fuelCnsmpRt(record.getFuelCnsmpRt()) - .elctyCmpndFuelCnsmpRt(record.getElctyCmpndFuelCnsmpRt()); - } - - /** - * 에러 정보를 DB에 업데이트 - * - * @param id 저장된 ID - * @param exception 발생한 예외 - */ - private void updateErrorLog(String id, Exception exception) { - try { - CarBassMatterInqireVO errorLog = CarBassMatterInqireVO.builder() - .carBassMatterInqire(id) - .cntcResultCode("99") - .cntcResultDtls("오류: " + exception.getMessage()) - .build(); - - carBassMatterInqireService.updateResponse(errorLog); - log.error("[BASIC-ERR-LOG] API 호출 에러 정보 저장 완료 - ID: {}", id, exception); - } catch (Exception e) { - log.error("[BASIC-ERR-LOG] 에러 로그 저장 실패 - ID: {}", id, e); - } - } } diff --git a/src/main/java/com/vmis/interfaceapp/controller/VehicleInterfaceController.java b/src/main/java/com/vmis/interfaceapp/controller/VehicleInterfaceController.java index 900aab8..f86d4e5 100644 --- a/src/main/java/com/vmis/interfaceapp/controller/VehicleInterfaceController.java +++ b/src/main/java/com/vmis/interfaceapp/controller/VehicleInterfaceController.java @@ -1,12 +1,11 @@ package com.vmis.interfaceapp.controller; -import com.vmis.interfaceapp.client.GovernmentApiClient; import com.vmis.interfaceapp.model.basic.BasicRequest; import com.vmis.interfaceapp.model.basic.BasicResponse; import com.vmis.interfaceapp.model.common.Envelope; import com.vmis.interfaceapp.model.ledger.LedgerRequest; import com.vmis.interfaceapp.model.ledger.LedgerResponse; -import com.vmis.interfaceapp.service.RequestEnricher; +import com.vmis.interfaceapp.service.VehicleInterfaceService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; @@ -42,11 +41,10 @@ import org.springframework.web.bind.annotation.*; *

아키텍처 설계:

*
    *
  • 컨트롤러는 얇은(Thin) 레이어로 설계되어 비즈니스 로직을 포함하지 않음
  • - *
  • 모든 실제 처리는 {@link GovernmentApiClient}에 위임
  • + *
  • 모든 실제 처리는 {@link com.vmis.interfaceapp.service.VehicleInterfaceService}에 위임
  • *
  • Swagger/OpenAPI 문서 자동 생성을 위한 어노테이션 포함
  • *
* - * @see GovernmentApiClient * @see Envelope */ @RestController @@ -56,24 +54,8 @@ import org.springframework.web.bind.annotation.*; @Tag(name = "Vehicle Interfaces", description = "시군구연계 자동차 정보 연계 API") public class VehicleInterfaceController { - private final RequestEnricher enricher; + private final VehicleInterfaceService vehicleService; - /** - * 정부 API 클라이언트 - * - *

정부 시스템과의 실제 통신을 담당하는 클라이언트 객체입니다. - * 이 클라이언트는 다음 기능들을 포함합니다:

- *
    - *
  • HTTP 요청/응답 처리
  • - *
  • GPKI 암호화/복호화
  • - *
  • 헤더 구성 및 관리
  • - *
  • 에러 처리 및 로깅
  • - *
- * - *

생성자 주입(Constructor Injection)을 통해 의존성이 주입되며, - * final 키워드로 선언되어 불변성을 보장합니다.

- */ - private final GovernmentApiClient govClient; /** * 자동차 기본사항 조회 API @@ -149,10 +131,8 @@ public class VehicleInterfaceController { public ResponseEntity> basic( @org.springframework.web.bind.annotation.RequestBody Envelope envelope ) { - // YAML 설정값으로 공통 필드 자동 채움 - enricher.enrichBasic(envelope); - // 실제 처리는 GovernmentApiClient에 위임 - return govClient.callBasic(envelope); + // 서비스에서 요청 보강/로깅/호출을 모두 오케스트레이션 + return vehicleService.basic(envelope); } /** @@ -247,9 +227,7 @@ public class VehicleInterfaceController { public ResponseEntity> ledger( @org.springframework.web.bind.annotation.RequestBody Envelope envelope ) { - // YAML 설정값으로 공통 필드 자동 채움 - enricher.enrichLedger(envelope); - // 실제 처리는 GovernmentApiClient에 위임 - return govClient.callLedger(envelope); + // 서비스에서 요청 보강/호출을 오케스트레이션 + return vehicleService.ledger(envelope); } } diff --git a/src/main/java/com/vmis/interfaceapp/mapper/SampleMapper.java b/src/main/java/com/vmis/interfaceapp/mapper/SampleMapper.java deleted file mode 100644 index 528aa2c..0000000 --- a/src/main/java/com/vmis/interfaceapp/mapper/SampleMapper.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.vmis.interfaceapp.mapper; - -import org.apache.ibatis.annotations.Mapper; - -/** - * 샘플 MyBatis Mapper 인터페이스 - * - *

이 인터페이스는 MyBatis 설정 테스트 및 예제 용도입니다. - * 실제 프로젝트에서는 비즈니스 요구사항에 맞는 Mapper를 작성하세요.

- * - *

@Mapper 어노테이션을 사용하면 Spring이 자동으로 구현체를 생성합니다. - * XML 파일의 namespace와 이 인터페이스의 경로가 일치해야 합니다.

- */ -@Mapper -public interface SampleMapper { - - /** - * 데이터베이스 연결 테스트용 쿼리 - * - *

현재 시간을 조회하여 데이터베이스 연결이 정상적으로 - * 작동하는지 확인할 수 있습니다.

- * - * @return 현재 시간 문자열 - */ - String selectCurrentTime(); -} diff --git a/src/main/java/com/vmis/interfaceapp/service/VehicleInterfaceService.java b/src/main/java/com/vmis/interfaceapp/service/VehicleInterfaceService.java new file mode 100644 index 0000000..1987d75 --- /dev/null +++ b/src/main/java/com/vmis/interfaceapp/service/VehicleInterfaceService.java @@ -0,0 +1,228 @@ +package com.vmis.interfaceapp.service; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.vmis.interfaceapp.client.GovernmentApi; +import com.vmis.interfaceapp.model.basic.BasicRequest; +import com.vmis.interfaceapp.model.basic.BasicResponse; +import com.vmis.interfaceapp.model.basic.CarBassMatterInqireVO; +import com.vmis.interfaceapp.model.common.Envelope; +import com.vmis.interfaceapp.model.ledger.LedgerRequest; +import com.vmis.interfaceapp.model.ledger.LedgerResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 차량 연계 서비스: 컨트롤러-클라이언트 사이의 오케스트레이션 계층. + * - 요청 보강(RequestEnricher) + * - 요청/응답 DB 로깅(CarBassMatterInqire) + * - 정부 API 클라이언트 위임(GovernmentApi) + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class VehicleInterfaceService { + + private final GovernmentApi governmentApi; + private final RequestEnricher enricher; + private final CarBassMatterInqireService carBassMatterInqireService; + + /** + * 자동차 기본사항 조회: 보강 -> 최초요청로그 -> 외부호출 -> 응답로그. + */ + @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); + CarBassMatterInqireVO logEntity = mapInitialLog(req); + generatedId = carBassMatterInqireService.createInitialRequest(logEntity); + log.info("[BASIC-REQ-LOG] 요청 정보 저장 완료 - ID: {}, 차량번호: {}", generatedId, req.getVhrno()); + } + + // 3) 외부 API 호출 + ResponseEntity> response = governmentApi.callBasic(envelope); + + // 4) 응답 로그 업데이트 + // 원본 소스, 정상적인 호출, 리턴(에러 리턴포함) 일 경우에만 에러 로그 남김 + if (generatedId != null && response.getBody() != null) { + CarBassMatterInqireVO update = mapResponseLog(generatedId, response.getBody()); + if (update != null) { + carBassMatterInqireService.updateResponse(update); + log.info("[BASIC-RES-LOG] 응답 정보 저장 완료 - ID: {}", generatedId); + } + } + + return response; + } catch (Exception e) { + // 5) 오류 로그 업데이트 + if (generatedId != null) { + try { + String detail = buildExceptionDetail(e); + CarBassMatterInqireVO errorLog = CarBassMatterInqireVO.builder() + .carBassMatterInqire(generatedId) + .cntcResultCode("99") + .cntcResultDtls(detail) + .build(); + carBassMatterInqireService.updateResponse(errorLog); + log.error("[BASIC-ERR-LOG] API 호출 에러 정보 저장 완료 - ID: {}, detail: {}", generatedId, detail, e); + } catch (Exception ignore) { + log.error("[BASIC-ERR-LOG] 에러 로그 저장 실패 - ID: {}", generatedId, ignore); + } + } + throw e; + } + } + + /** + * 자동차 등록원부(갑) 조회: 보강 -> 외부호출. (별도 로그 테이블 미정) + */ + public ResponseEntity> ledger(Envelope envelope) { + enricher.enrichLedger(envelope); + return governmentApi.callLedger(envelope); + } + + private CarBassMatterInqireVO mapInitialLog(BasicRequest request) { + return CarBassMatterInqireVO.builder() + .infoSysId(request.getInfoSysId()) + .infoSysIp(request.getInfoSysIp()) + .sigunguCode(request.getSigunguCode()) + .cntcInfoCode(request.getCntcInfoCode()) + .chargerId(request.getChargerId()) + .chargerIp(request.getChargerIp()) + .chargerNm(request.getChargerNm()) + .dmndLevyStdde(request.getLevyStdde()) + .dmndInqireSeCode(request.getInqireSeCode()) + .dmndVhrno(request.getVhrno()) + .dmndVin(request.getVin()) + .rgtr("SYSTEM") + .build(); + } + + private CarBassMatterInqireVO mapResponseLog(String id, Envelope envelope) { + if (envelope.getData() == null || envelope.getData().isEmpty()) return null; + BasicResponse response = envelope.getData().get(0); + CarBassMatterInqireVO.CarBassMatterInqireVOBuilder builder = CarBassMatterInqireVO.builder() + .carBassMatterInqire(id) + .cntcResultCode(response.getCntcResultCode()) + .cntcResultDtls(response.getCntcResultDtls()); + + if (response.getRecord() != null && !response.getRecord().isEmpty()) { + BasicResponse.Record record = response.getRecord().get(0); + mapRecordToEntity(builder, record); + } + return builder.build(); + } + + private void mapRecordToEntity(CarBassMatterInqireVO.CarBassMatterInqireVOBuilder builder, BasicResponse.Record record) { + builder + .prye(record.getPrye()) + .registDe(record.getRegistDe()) + .ersrRegistSeCode(record.getErsrRegistSeCode()) + .ersrRegistSeNm(record.getErsrRegistSeNm()) + .ersrRegistDe(record.getErsrRegistDe()) + .registDetailCode(record.getRegistDetailCode()) + .dsplvl(record.getDsplvl()) + .useStrnghldLegaldongCode(record.getUseStrnghldLegaldongCode()) + .useStrnghldAdstrdCode(record.getUseStrnghldAdstrdCode()) + .useStrnghldMntn(record.getUseStrnghldMntn()) + .useStrnghldLnbr(record.getUseStrnghldLnbr()) + .useStrnghldHo(record.getUseStrnghldHo()) + .useStrnghldAdresNm(record.getUseStrnghldAdresNm()) + .useStrnghldRoadNmCode(record.getUseStrnghldRoadNmCode()) + .usgsrhldUndgrndBuldSeCode(record.getUsgsrhldUndgrndBuldSeCode()) + .useStrnghldBuldMainNo(record.getUseStrnghldBuldMainNo()) + .useStrnghldBuldSubNo(record.getUseStrnghldBuldSubNo()) + .usgsrhldAdresFull(record.getUsgsrhldAdresFull()) + .mberSeCode(record.getMberSeCode()) + .mberSeNo(record.getMberSeNo()) + .mberNm(record.getMberNm()) + .telno(record.getTelno()) + .ownerLegaldongCode(record.getOwnerLegaldongCode()) + .ownerAdstrdCode(record.getOwnerAdstrdCode()) + .ownerMntn(record.getOwnerMntn()) + .ownerLnbr(record.getOwnerLnbr()) + .ownerHo(record.getOwnerHo()) + .ownerAdresNm(record.getOwnerAdresNm()) + .ownerRoadNmCode(record.getOwnerRoadNmCode()) + .ownerUndgrndBuldSeCode(record.getOwnerUndgrndBuldSeCode()) + .ownerBuldMainNo(record.getOwnerBuldMainNo()) + .ownerBuldSubNo(record.getOwnerBuldSubNo()) + .ownrWholaddr(record.getOwnerAdresFull()) + .aftrVhrno(record.getAftrVhrno()) + .useFuelCode(record.getUseFuelCode()) + .prposSeCode(record.getPrposSeCode()) + .mtrsFomNm(record.getMtrsFomNm()) + .frntVhrno(record.getFrntVhrno()) + .vhclno(record.getVhrno()) + .vin(record.getVin()) + .cnm(record.getCnm()) + .vhcleTotWt(record.getVhcleTotWt()) + .caagEndde(record.getCaagEndde()) + .changeDe(record.getChangeDe()) + .vhctyAsortCode(record.getVhctyAsortCode()) + .vhctyTyCode(record.getVhctyTyCode()) + .vhctySeCode(record.getVhctySeCode()) + .mxmmLdg(record.getMxmmLdg()) + .vhctyAsortNm(record.getVhctyAsortNm()) + .vhctyTyNm(record.getVhctyTyNm()) + .vhctySeNm(record.getVhctySeNm()) + .frstRegistDe(record.getFrstRegistDe()) + .fomNm(record.getFomNm()) + .acqsDe(record.getAcqsDe()) + .acqsEndDe(record.getAcqsEndDe()) + .yblMd(record.getYblMd()) + .transrRegistDe(record.getTransrRegistDe()) + .spcfRegistSttusCode(record.getSpcfRegistSttusCode()) + .colorNm(record.getColorNm()) + .mrtgCo(record.getMrtgCo()) + .seizrCo(record.getSeizrCo()) + .stmdCo(record.getStmdCo()) + .nmplCsdyAt(record.getNmplCsdyAt()) + .nmplCsdyRemnrDe(record.getNmplCsdyRemnrDe()) + .originSeCode(record.getOriginSeCode()) + .nmplStndrdCode(record.getNmplStndrdCode()) + .acqsAmount(record.getAcqsAmount()) + .insptValidPdBgnde(record.getInsptValidPdBgnde()) + .insptValidPdEndde(record.getInsptValidPdEndde()) + .useStrnghldGrcCode(record.getUseStrnghldGrcCode()) + .tkcarPscapCo(record.getTkcarPscapCo()) + .spmnno(record.getSpmnno()) + .trvlDstnc(record.getTrvlDstnc()) + .frstRegistRqrcno(record.getFrstRegistRqrcno()) + .vlntErsrPrvntcNticeDe(record.getVlntErsrPrvntcNticeDe()) + .registInsttNm(record.getRegistInsttNm()) + .processImprtyResnCode(record.getProcessImprtyResnCode()) + .processImprtyResnDtls(record.getProcessImprtyResnDtls()) + .cbdLt(record.getCbdLt()) + .cbdBt(record.getCbdBt()) + .cbdHg(record.getCbdHg()) + .frstMxmmLdg(record.getFrstMxmmLdg()) + .fuelCnsmpRt(record.getFuelCnsmpRt()) + .elctyCmpndFuelCnsmpRt(record.getElctyCmpndFuelCnsmpRt()); + } + + private String buildExceptionDetail(Throwable t) { + if (t == null) return "오류: unknown"; + Throwable root = t; + int guard = 0; + while (root.getCause() != null && root.getCause() != root && guard++ < 20) { + root = root.getCause(); + } + String className = root.getClass().getName(); + String message = root.getMessage(); + String detail = (message != null && !message.isEmpty()) ? (className + ": " + message) : root.toString(); + // DB 컬럼(CNTC_RESULT_DTLS) 길이: 200 + if (detail.length() > 200) { + detail = detail.substring(0, 200); + } + return detail; + } +} diff --git a/src/main/resources/mybatis/mapper/sample_maria.xml b/src/main/resources/mybatis/mapper/sample_maria.xml deleted file mode 100644 index b1a4c7f..0000000 --- a/src/main/resources/mybatis/mapper/sample_maria.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - -