From 2a14d6b2129f4ce0fd47fa9fad096c5f7cc5ead0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B1=EC=98=81?= Date: Wed, 5 Nov 2025 12:41:35 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B0=A8=EB=9F=89=EA=B8=B0=EB=B3=B8=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20API,=20DB=20=EC=A0=80=EC=9E=A5=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20=EC=B6=94=EA=B0=80=EC=A0=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=86=8C=EC=8A=A4=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20=EC=98=88=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 + ddl/ibmsdb/seq_car_bass_matter_inqire.sql | 11 + ddl/ibmsdb/tb_car_bass_matter_inqire.sql | 106 ++++ .../client/GovernmentApiClient.java | 355 +++++++----- .../interfaceapp/config/DatabaseConfig.java | 57 ++ .../com/vmis/interfaceapp/config/Globals.java | 39 ++ .../mapper/CarBassMatterInqireMapper.java | 54 ++ .../interfaceapp/mapper/SampleMapper.java | 26 + .../model/basic/CarBassMatterInqireVO.java | 524 ++++++++++++++++++ .../service/CarBassMatterInqireService.java | 126 +++++ src/main/resources/application-dev.yml | 38 ++ src/main/resources/application-prd.yml | 38 ++ src/main/resources/application.yml | 4 + .../CarBassMatterInqireMapper_maria.xml | 148 +++++ .../resources/mybatis/mapper/sample_maria.xml | 44 ++ src/main/resources/mybatis/mybatis-config.xml | 47 ++ 16 files changed, 1486 insertions(+), 136 deletions(-) create mode 100644 ddl/ibmsdb/seq_car_bass_matter_inqire.sql create mode 100644 ddl/ibmsdb/tb_car_bass_matter_inqire.sql create mode 100644 src/main/java/com/vmis/interfaceapp/config/DatabaseConfig.java create mode 100644 src/main/java/com/vmis/interfaceapp/config/Globals.java create mode 100644 src/main/java/com/vmis/interfaceapp/mapper/CarBassMatterInqireMapper.java create mode 100644 src/main/java/com/vmis/interfaceapp/mapper/SampleMapper.java create mode 100644 src/main/java/com/vmis/interfaceapp/model/basic/CarBassMatterInqireVO.java create mode 100644 src/main/java/com/vmis/interfaceapp/service/CarBassMatterInqireService.java create mode 100644 src/main/resources/mybatis/mapper/CarBassMatterInqireMapper_maria.xml create mode 100644 src/main/resources/mybatis/mapper/sample_maria.xml create mode 100644 src/main/resources/mybatis/mybatis-config.xml diff --git a/build.gradle b/build.gradle index 82198f5..21ab4f7 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,11 @@ dependencies { // GPKI JNI local library implementation files('lib/libgpkiapi_jni_1.5.jar') + // Database & MyBatis + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' + implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.3' + // Configuration metadata annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' diff --git a/ddl/ibmsdb/seq_car_bass_matter_inqire.sql b/ddl/ibmsdb/seq_car_bass_matter_inqire.sql new file mode 100644 index 0000000..e9a1f80 --- /dev/null +++ b/ddl/ibmsdb/seq_car_bass_matter_inqire.sql @@ -0,0 +1,11 @@ +-- 자동차 기본 사항 조회 ID 시퀀스 +-- CBMI000000000001, CBMI000000000002, ... 형태로 생성 +CREATE SEQUENCE seq_car_bass_matter_inqire + START WITH 1 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9999999999 + CACHE 1000 + NOCYCLE; + +-- SELECT CONCAT('CBMI', LPAD(NEXTVAL(seq_car_bass_matter_inqire), 16, '0')); \ No newline at end of file diff --git a/ddl/ibmsdb/tb_car_bass_matter_inqire.sql b/ddl/ibmsdb/tb_car_bass_matter_inqire.sql new file mode 100644 index 0000000..d86fb32 --- /dev/null +++ b/ddl/ibmsdb/tb_car_bass_matter_inqire.sql @@ -0,0 +1,106 @@ +create table tb_car_bass_matter_inqire +( + CAR_BASS_MATTER_INQIRE varchar(20) not null comment '자동차 기본 사항 조회 ID' + primary key, + INFO_SYS_ID varchar(6) null comment '정보 시스템 ID', + INFO_SYS_IP varchar(23) null comment '정보 시스템 IP', + SIGUNGU_CODE varchar(5) null comment '시군구 코드', + CNTC_INFO_CODE varchar(10) null comment '연계 정보 코드', + CHARGER_ID varchar(15) null comment '담당자 ID', + CHARGER_IP varchar(23) null comment '담당자 IP', + CHARGER_NM varchar(75) null comment '담당자명', + DMND_LEVY_STDDE varchar(8) null comment '요청 부과 기준일', + DMND_INQIRE_SE_CODE varchar(1) null comment '요청 조회 구분 코드', + DMND_VHRNO varchar(30) null comment '요청 자동차등록번호', + DMND_VIN varchar(17) null comment '요청 차대번호', + CNTC_RESULT_CODE varchar(8) null comment '연계 결과 코드', + CNTC_RESULT_DTLS varchar(200) null comment '연계 결과 상세', + PRYE varchar(4) null comment '연식', + REGIST_DE varchar(8) null comment '등록일', + ERSR_REGIST_SE_CODE varchar(4) null comment '말소 등록 구분 코드', + ERSR_REGIST_SE_NM varchar(30) null comment '말소 등록 구분명', + ERSR_REGIST_DE varchar(8) null comment '말소 등록일', + REGIST_DETAIL_CODE varchar(3) null comment '등록 상세 코드', + DSPLVL varchar(6) null comment '배기량', + USE_STRNGHLD_LEGALDONG_CODE varchar(10) null comment '사용 본거지 법정동 코드', + USE_STRNGHLD_ADSTRD_CODE varchar(10) null comment '사용 본거지 행정동 코드', + USE_STRNGHLD_MNTN varchar(2) null comment '사용 본거지 산', + USE_STRNGHLD_LNBR varchar(4) null comment '사용 본거지 번지', + USE_STRNGHLD_HO varchar(4) null comment '사용 본거지 호', + USE_STRNGHLD_ADRES_NM varchar(300) null comment '사용 본거지 상세주소', + USE_STRNGHLD_ROAD_NM_CODE varchar(12) null comment '사용 본거지 도로명 코드', + USGSRHLD_UNDGRND_BULD_SE_CODE varchar(1) null comment '사용 본거지 지하 건물 구분 코드', + USE_STRNGHLD_BULD_MAIN_NO varchar(5) null comment '사용 본거지 건물 주요 번호', + USE_STRNGHLD_BULD_SUB_NO varchar(5) null comment '사용 본거지 건물 부 번호', + USGSRHLD_ADRES_FULL varchar(750) null comment '사용 본거지 전체주소', + MBER_SE_CODE varchar(2) null comment '대표소유자 회원 구분 코드', + MBER_SE_NO varchar(100) null comment '대표소유자 회원 번호', + TELNO varchar(20) null comment '대표소유자 전화번호', + OWNER_LEGALDONG_CODE varchar(10) null comment '소유자 법정동 코드', + OWNER_ADSTRD_CODE varchar(10) null comment '소유자 행정동 코드', + OWNER_MNTN varchar(2) null comment '소유자 산', + OWNER_LNBR varchar(4) null comment '소유자 번지', + OWNER_HO varchar(4) null comment '소유자 호', + OWNER_ADRES_NM varchar(300) null comment '소유자 상세주소', + OWNER_ROAD_NM_CODE varchar(12) null comment '소유자 도로명 코드', + OWNER_UNDGRND_BULD_SE_CODE varchar(1) null comment '소유자 지하건물 구분 코드', + OWNER_BULD_MAIN_NO varchar(5) null comment '소유자 건물 주요 번호', + OWNER_BULD_SUB_NO varchar(5) null comment '소유자 건물 부 번호', + OWNR_WHOLADDR varchar(750) null comment '소유자 전체주소', + AFTR_VHRNO varchar(30) null comment '신 차량번호', + USE_FUEL_CODE varchar(1) null comment '사용 연료 코드', + PRPOS_SE_CODE varchar(2) null comment '용도 구분 코드', + MTRS_FOM_NM varchar(75) null comment '원동기 형식명', + FRNT_VHRNO varchar(30) null comment '이전 차량번호', + VHCLNO varchar(30) null comment '차량번호', + VIN varchar(17) null comment '차대번호', + CNM varchar(75) null comment '차명', + VHCLE_TOT_WT varchar(6) null comment '차량 총 중량', + CAAG_ENDDE varchar(8) null comment '차령 만료일자', + CHANGE_DE varchar(8) null comment '차번호 변경시기', + VHCTY_ASORT_CODE varchar(1) null comment '차종 종별 코드', + VHCTY_TY_CODE varchar(1) null comment '차종 유형 코드', + VHCTY_SE_CODE varchar(1) null comment '차종 분류 코드', + MXMM_LDG varchar(10) null comment '최대 적재량', + VHCTY_ASORT_NM varchar(150) null comment '차종 종별명', + VHCTY_TY_NM varchar(150) null comment '차종 유형명', + VHCTY_SE_NM varchar(150) null comment '차종 분류명', + FRST_REGIST_DE varchar(8) null comment '최초 등록일', + FOM_NM varchar(75) null comment '형식', + ACQS_DE varchar(8) null comment '취득 일자', + ACQS_END_DE varchar(8) null comment '취득 종료일자', + YBL_MD varchar(8) null comment '제작 년월일', + TRANSR_REGIST_DE varchar(8) null comment '이전 등록일', + SPCF_REGIST_STTUS_CODE varchar(6) null comment '제원 등록 상태 코드', + COLOR_NM varchar(75) null comment '색상명', + MRTG_CO varchar(9) null comment '저당수', + SEIZR_CO varchar(9) null comment '압류건수', + STMD_CO varchar(9) null comment '구조변경수', + NMPL_CSDY_AT varchar(1) null comment '번호판 영치 여부', + NMPL_CSDY_REMNR_DE varchar(8) null comment '번호판 영치 최고일', + ORIGIN_SE_CODE varchar(1) null comment '출처 구분 코드', + NMPL_STNDRD_CODE varchar(1) null comment '번호판 규격 코드', + ACQS_AMOUNT varchar(18) null comment '취득 금액', + INSPT_VALID_PD_BGNDE varchar(8) null comment '검사 유효 기간 시작일', + INSPT_VALID_PD_ENDDE varchar(8) null comment '검사 유효 기간 종료일', + USE_STRNGHLD_GRC_CODE varchar(4) null comment '사용 본거지 관청 코드', + TKCAR_PSCAP_CO varchar(3) null comment '승차정원수', + SPMNNO varchar(17) null comment '제원관리번호', + TRVL_DSTNC varchar(10) null comment '주행거리', + FRST_REGIST_RQRCNO varchar(20) null comment '최초 등록 접수번호', + VLNT_ERSR_PRVNTC_NTICE_DE varchar(8) null comment '예고통지일', + REGIST_INSTT_NM varchar(150) null comment '등록 기관명', + PROCESS_IMPRTY_RESN_CODE varchar(2) null comment '처리 불가 사유 코드', + PROCESS_IMPRTY_RESN_DTLS varchar(75) null comment '처리 불가 사유 명세', + CBD_LT varchar(10) null comment '차체 길이', + CBD_BT varchar(10) null comment '차체 너비', + CBD_HG varchar(10) null comment '차체 높이', + FRST_MXMM_LDG varchar(10) null comment '최초 최대 적재량', + FUEL_CNSMP_RT varchar(5) null comment '연료 소비율', + ELCTY_CMPND_FUEL_CNSMP_RT varchar(5) null comment '전기 복합 연료 소비율', + REG_DT datetime null comment '등록 일시', + RGTR varchar(11) null comment '등록자', + MBER_NM varchar(75) null comment '대표소유자 성명' +) + comment '자동차 기본 사항 조회'; + diff --git a/src/main/java/com/vmis/interfaceapp/client/GovernmentApiClient.java b/src/main/java/com/vmis/interfaceapp/client/GovernmentApiClient.java index 5e77dd7..eed35c3 100644 --- a/src/main/java/com/vmis/interfaceapp/client/GovernmentApiClient.java +++ b/src/main/java/com/vmis/interfaceapp/client/GovernmentApiClient.java @@ -6,10 +6,12 @@ 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; import com.vmis.interfaceapp.util.TxIdUtil; +import lombok.RequiredArgsConstructor; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.web.client.HttpStatusCodeException; @@ -56,6 +58,7 @@ import java.nio.charset.StandardCharsets; * @see VmisProperties */ @Slf4j +@RequiredArgsConstructor @Component public class GovernmentApiClient { @@ -122,6 +125,13 @@ public class GovernmentApiClient { */ private final ObjectMapper objectMapper; + /** + * 자동차 기본 사항 조회 로그 서비스 + * + *

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

+ */ + private final com.vmis.interfaceapp.service.CarBassMatterInqireService carBassMatterInqireService; + /** * 서비스 타입 열거형 * @@ -156,134 +166,6 @@ public class GovernmentApiClient { */ LEDGER } - /** - * 생성자를 통한 의존성 주입 - * - *

Spring의 생성자 주입 방식을 사용하여 필요한 모든 의존성을 주입받습니다. - * - * @param restTemplate HTTP 통신을 위한 RestTemplate 객체 - * @param props VMIS 애플리케이션 설정 속성 - * @param gpkiService GPKI 암호화/복호화 서비스 - * @param objectMapper JSON 직렬화/역직렬화를 위한 ObjectMapper - * @Component 어노테이션과 함께 사용되어 자동으로 Spring Bean으로 등록됩니다.

생성자 주입의 장점:

- */ - public GovernmentApiClient(RestTemplate restTemplate, VmisProperties props, GpkiService gpkiService, ObjectMapper objectMapper) { - this.restTemplate = restTemplate; - this.props = props; - this.gpkiService = gpkiService; - this.objectMapper = objectMapper; - } - - /** - * 정부 API 호출 (문자열 기반) - * - *

이 메서드는 JSON 문자열을 직접 받아 정부 API를 호출하는 저수준(Low-level) 메서드입니다. - * 타입 안전성이 필요한 경우 {@link #callModel} 메서드를 사용하는 것이 권장됩니다.

- * - *

처리 흐름:

- *
    - *
  1. 설정 로드: 서비스 타입에 따라 적절한 정부 API 설정을 가져옴
  2. - *
  3. URL 구성: 호스트, 포트, 경로를 결합하여 완전한 URL 생성
  4. - *
  5. 트랜잭션 ID 생성: 요청 추적을 위한 고유 ID 생성 (TxIdUtil 사용)
  6. - *
  7. 헤더 구성: 필수 헤더(API 키, 시스템 정보, GPKI 설정 등) 추가
  8. - *
  9. 암호화 처리: GPKI가 활성화된 경우 요청 바디를 암호화
  10. - *
  11. HTTP 요청: RestTemplate을 사용하여 POST 요청 전송
  12. - *
  13. 복호화 처리: 성공 응답이고 GPKI가 활성화된 경우 응답 바디를 복호화
  14. - *
  15. 응답 반환: 최종 응답을 ResponseEntity로 반환
  16. - *
- * - *

에러 처리 전략:

- * - * - *

로깅:

- * - * - * @param type 서비스 타입 (BASIC 또는 LEDGER) - * @param jsonBody 전송할 JSON 문자열 (암호화 전 평문) - * @return ResponseEntity<String> 정부 API 응답 (복호화 완료된 JSON 문자열) - * @throws RuntimeException GPKI 암호화/복호화 실패 시 - */ - public ResponseEntity call(ServiceType type, String jsonBody) { - // 1. 서비스 타입에 따른 설정 로드 - // props.getGov()는 정부 API 관련 모든 설정을 포함하는 객체를 반환 - VmisProperties.GovProps gov = props.getGov(); - - // 삼항 연산자를 사용하여 BASIC 또는 LEDGER 서비스 설정 선택 - VmisProperties.GovProps.Service svc = (type == ServiceType.BASIC) - ? gov.getServices().getBasic() - : gov.getServices().getLedger(); - - // 2. 완전한 URL 구성 (예: https://api.gov.kr:8080/vmis/basic) - String url = gov.buildServiceUrl(svc.getPath()); - - // 3. 트랜잭션 ID 생성 (요청 추적 및 디버깅 용도) - // 일반적으로 타임스탬프 + UUID 조합으로 생성됨 - String txId = TxIdUtil.generate(); - - // 4. HTTP 헤더 구성 - // API 키, 시스템 정보, GPKI 설정 등 필수 헤더 포함 - HttpHeaders headers = buildHeaders(svc, txId); - - // 5. GPKI 암호화 처리 - String bodyToSend = jsonBody; - if (gpkiService.isEnabled()) { - try { - // 평문 JSON을 암호화된 문자열로 변환 - // 암호화 결과는 Base64 인코딩된 문자열 - bodyToSend = gpkiService.encrypt(jsonBody); - } catch (Exception e) { - // 암호화 실패는 치명적 오류이므로 즉시 예외 발생 - throw new RuntimeException("GPKI 암호화 실패", e); - } - } - - // 6. HTTP 엔티티 생성 (헤더 + 바디) - HttpEntity request = new HttpEntity<>(bodyToSend, headers); - - // 7. 요청 로그 기록 - // 디버깅을 위해 URL, 트랜잭션 ID, GPKI 활성화 여부, 바디 길이 로깅 - log.info("[GOV-REQ] url={}, tx_id={}, gpki={}, length={}", url, txId, gpkiService.isEnabled(), bodyToSend != null ? bodyToSend.length() : 0); - - try { - // 8. 실제 HTTP POST 요청 전송 - // RestTemplate.exchange()는 HTTP 메서드, 헤더, 바디를 모두 지정 가능 - ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, request, String.class); - String respBody = response.getBody(); - - // 9. 성공 응답인 경우 GPKI 복호화 처리 - if (gpkiService.isEnabled() && response.getStatusCode().is2xxSuccessful()) { - try { - // 암호화된 응답을 평문 JSON으로 복호화 - String decrypted = gpkiService.decrypt(respBody); - // 복호화된 바디로 새로운 ResponseEntity 생성 - // 원본 응답의 상태 코드와 헤더는 그대로 유지 - return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders()).body(decrypted); - } catch (Exception e) { - // 복호화 실패는 치명적 오류이므로 즉시 예외 발생 - throw new RuntimeException("GPKI 복호화 실패", e); - } - } - // GPKI가 비활성화되어 있거나 에러 응답인 경우 원본 그대로 반환 - return response; - } catch (HttpStatusCodeException ex) { - // 10. HTTP 에러 처리 (4xx, 5xx 상태 코드) - // 경고 로그 기록 (에러는 정상적인 비즈니스 흐름일 수 있으므로 WARN 레벨) - log.warn("[GOV-ERR] status={}, body={}", ex.getStatusCode(), ex.getResponseBodyAsString()); - - // 에러 응답을 ResponseEntity로 변환하여 반환 - // 호출자가 상태 코드와 에러 메시지를 확인할 수 있도록 함 - // 헤더가 null인 경우를 대비하여 빈 HttpHeaders 사용 - return ResponseEntity.status(ex.getStatusCode()).headers(ex.getResponseHeaders() != null ? ex.getResponseHeaders() : new HttpHeaders()).body(ex.getResponseBodyAsString()); - } - } - /** * HTTP 헤더 구성 * @@ -398,10 +280,6 @@ public class GovernmentApiClient { // CVMIS(Car Vehicle Management Information System) 전용 API 키 headers.add("cvmis_apikey", svc.getCvmisApikey()); - // 9. 정보시스템 ID - // 호출하는 시스템을 식별하는 필수 값 - headers.add("INFO_SYS_ID", props.getSystem().getInfoSysId()); - // 구성 완료된 헤더 반환 return headers; } @@ -414,11 +292,20 @@ public class GovernmentApiClient { * *

특징:

*
    - *
  • 제네릭 타입으로 컴파일 타임 타입 체크
  • + *
  • 제네릭 타입으로 컴파일 타입 타입 체크
  • *
  • 요청/응답 객체가 Envelope로 감싸져 있음
  • *
  • Jackson TypeReference를 사용한 제네릭 역직렬화
  • + *
  • API 호출 전후로 DB에 로그성 데이터 저장
  • *
* + *

처리 흐름:

+ *
    + *
  1. 요청 정보를 DB에 INSERT (로그 저장)
  2. + *
  3. 정부 API 호출
  4. + *
  5. 응답 정보를 DB에 UPDATE
  6. + *
  7. 에러 발생 시 에러 정보도 DB에 UPDATE
  8. + *
+ * *

사용 예시:

*
      * BasicRequest request = new BasicRequest();
@@ -435,9 +322,34 @@ public class GovernmentApiClient {
      * @return ResponseEntity<Envelope<BasicResponse>> 조회 결과를 담은 응답
      */
     public ResponseEntity> callBasic(Envelope envelope) {
-        // TypeReference를 사용하여 제네릭 타입 정보 전달
-        // 익명 클래스를 생성하여 타입 소거(Type Erasure) 문제 해결
-        return callModel(ServiceType.BASIC, envelope, new TypeReference>(){});
+        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;
+        }
     }
 
     /**
@@ -744,4 +656,175 @@ public class GovernmentApiClient {
             throw new RuntimeException("GPKI 복호화 실패", e);
         }
     }
+
+    /**
+     * 요청 정보를 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/config/DatabaseConfig.java b/src/main/java/com/vmis/interfaceapp/config/DatabaseConfig.java
new file mode 100644
index 0000000..d6a1d66
--- /dev/null
+++ b/src/main/java/com/vmis/interfaceapp/config/DatabaseConfig.java
@@ -0,0 +1,57 @@
+package com.vmis.interfaceapp.config;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+
+/**
+ * 데이터베이스 및 트랜잭션 설정
+ *
+ * 

이 클래스는 데이터베이스 연결과 트랜잭션 관리를 위한 설정을 제공합니다. + * Spring Boot의 자동 설정을 활용하되, 명시적인 트랜잭션 관리를 위해 + * TransactionManager를 직접 설정합니다.

+ * + *
    + *
  • DataSource: application.yml에서 자동 설정
  • + *
  • SqlSessionFactory: MyBatis Spring Boot Starter가 자동 생성
  • + *
  • TransactionManager: 명시적으로 설정하여 트랜잭션 관리
  • + *
  • MapperScan: com.vmis.interfaceapp.mapper 패키지의 Mapper 인터페이스 자동 스캔
  • + *
+ */ +@Configuration +@EnableTransactionManagement +@MapperScan("com.vmis.interfaceapp.mapper") +public class DatabaseConfig { + + /** + * 트랜잭션 관리자를 설정합니다. + * + *

DataSourceTransactionManager는 JDBC 기반의 트랜잭션을 관리합니다. + * @Transactional 어노테이션을 사용하여 선언적 트랜잭션 관리가 가능합니다.

+ * + *

트랜잭션 전파(Propagation), 격리 수준(Isolation), 타임아웃 등의 + * 세부 설정은 @Transactional 어노테이션의 속성으로 지정할 수 있습니다.

+ * + *

예제:

+ *
+     * {@code
+     * @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
+     * public void saveData() {
+     *     // 트랜잭션 처리가 필요한 로직
+     * }
+     * }
+     * 
+ * + * @param dataSource Spring Boot가 자동 생성한 DataSource 빈 + * @return PlatformTransactionManager 트랜잭션 관리자 인스턴스 + */ + @Bean + public PlatformTransactionManager transactionManager(DataSource dataSource) { + return new DataSourceTransactionManager(dataSource); + } +} diff --git a/src/main/java/com/vmis/interfaceapp/config/Globals.java b/src/main/java/com/vmis/interfaceapp/config/Globals.java new file mode 100644 index 0000000..d175beb --- /dev/null +++ b/src/main/java/com/vmis/interfaceapp/config/Globals.java @@ -0,0 +1,39 @@ +package com.vmis.interfaceapp.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 전역 상수 및 설정값을 관리하는 클래스 + * + *

이 클래스는 애플리케이션 전반에서 사용되는 전역 설정값을 제공합니다. + * Spring의 @Value를 통해 application.yml의 설정값을 주입받아 사용합니다.

+ */ +@Component +public class Globals { + + /** + * 데이터베이스 타입 (예: mariadb, oracle, mysql 등) + * + *

MyBatis Mapper XML 파일 선택 시 사용됩니다. + * mapper-locations: classpath:mybatis/mapper/**\/*_${Globals.DbType}.xml

+ * + *

예시: + *

    + *
  • DbType = "mariadb" → user_mariadb.xml 매핑
  • + *
  • DbType = "oracle" → user_oracle.xml 매핑
  • + *
+ *

+ */ + public static String DbType; + + /** + * application.yml에서 Globals.DbType 값을 주입합니다. + * + * @param dbType 데이터베이스 타입 (기본값: mariadb) + */ + @Value("${Globals.DbType:mariadb}") + public void setDbType(String dbType) { + Globals.DbType = dbType; + } +} diff --git a/src/main/java/com/vmis/interfaceapp/mapper/CarBassMatterInqireMapper.java b/src/main/java/com/vmis/interfaceapp/mapper/CarBassMatterInqireMapper.java new file mode 100644 index 0000000..e192e25 --- /dev/null +++ b/src/main/java/com/vmis/interfaceapp/mapper/CarBassMatterInqireMapper.java @@ -0,0 +1,54 @@ +package com.vmis.interfaceapp.mapper; + +import com.vmis.interfaceapp.model.basic.CarBassMatterInqireVO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 자동차 기본 사항 조회 Mapper + * + *

API 호출 정보를 관리하는 Mapper 인터페이스입니다.

+ *
    + *
  • 최초 요청 시: insertCarBassMatterInqire() 호출
  • + *
  • 결과 수신 시: updateCarBassMatterInqire() 호출
  • + *
+ */ +@Mapper +public interface CarBassMatterInqireMapper { + + /** + * 시퀀스로 새로운 자동차 기본 사항 조회 ID를 생성합니다. + * + *

형식: CBMI000000000001

+ * + * @return 생성된 ID + */ + String selectNextCarBassMatterInqireId(); + + /** + * 최초 API 요청 정보를 등록합니다. + * + *

요청 시점의 정보만 저장하며, 응답 정보는 null 상태입니다.

+ * + * @param carBassMatterInqireVO 요청 정보 + * @return 등록된 행 수 + */ + int insertCarBassMatterInqire(CarBassMatterInqireVO carBassMatterInqireVO); + + /** + * API 응답 결과를 업데이트합니다. + * + *

응답 받은 데이터를 기존 레코드에 업데이트합니다.

+ * + * @param carBassMatterInqireVO 응답 정보 (carBassMatterInqire 필드는 필수) + * @return 업데이트된 행 수 + */ + int updateCarBassMatterInqire(CarBassMatterInqireVO carBassMatterInqireVO); + + /** + * ID로 조회 정보를 조회합니다. + * + * @param carBassMatterInqire 자동차 기본 사항 조회 ID + * @return 조회된 정보 + */ + CarBassMatterInqireVO selectCarBassMatterInqireById(String carBassMatterInqire); +} diff --git a/src/main/java/com/vmis/interfaceapp/mapper/SampleMapper.java b/src/main/java/com/vmis/interfaceapp/mapper/SampleMapper.java new file mode 100644 index 0000000..528aa2c --- /dev/null +++ b/src/main/java/com/vmis/interfaceapp/mapper/SampleMapper.java @@ -0,0 +1,26 @@ +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/model/basic/CarBassMatterInqireVO.java b/src/main/java/com/vmis/interfaceapp/model/basic/CarBassMatterInqireVO.java new file mode 100644 index 0000000..53e7838 --- /dev/null +++ b/src/main/java/com/vmis/interfaceapp/model/basic/CarBassMatterInqireVO.java @@ -0,0 +1,524 @@ +package com.vmis.interfaceapp.model.basic; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * 자동차 기본 사항 조회 엔티티 + * + *

API 호출 정보를 저장하는 테이블 매핑 클래스입니다. + * 최초 요청 시 INSERT, 결과 수신 시 UPDATE 형태로 사용됩니다.

+ */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CarBassMatterInqireVO { + + /** + * 자동차 기본 사항 조회 ID (PK) + * 형식: CBMI000000000001 + */ + private String carBassMatterInqire; + + // ===== 요청 정보 ===== + /** + * 정보 시스템 ID + */ + private String infoSysId; + + /** + * 정보 시스템 IP + */ + private String infoSysIp; + + /** + * 시군구 코드 + */ + private String sigunguCode; + + /** + * 연계 정보 코드 + */ + private String cntcInfoCode; + + /** + * 담당자 ID + */ + private String chargerId; + + /** + * 담당자 IP + */ + private String chargerIp; + + /** + * 담당자명 + */ + private String chargerNm; + + /** + * 요청 부과 기준일 + */ + private String dmndLevyStdde; + + /** + * 요청 조회 구분 코드 + */ + private String dmndInqireSeCode; + + /** + * 요청 자동차등록번호 + */ + private String dmndVhrno; + + /** + * 요청 차대번호 + */ + private String dmndVin; + + // ===== 응답 정보 (결과 수신 시 UPDATE) ===== + /** + * 연계 결과 코드 + */ + private String cntcResultCode; + + /** + * 연계 결과 상세 + */ + private String cntcResultDtls; + + /** + * 연식 + */ + private String prye; + + /** + * 등록일 + */ + private String registDe; + + /** + * 말소 등록 구분 코드 + */ + private String ersrRegistSeCode; + + /** + * 말소 등록 구분명 + */ + private String ersrRegistSeNm; + + /** + * 말소 등록일 + */ + private String ersrRegistDe; + + /** + * 등록 상세 코드 + */ + private String registDetailCode; + + /** + * 배기량 + */ + private String dsplvl; + + /** + * 사용 본거지 법정동 코드 + */ + private String useStrnghldLegaldongCode; + + /** + * 사용 본거지 행정동 코드 + */ + private String useStrnghldAdstrdCode; + + /** + * 사용 본거지 산 + */ + private String useStrnghldMntn; + + /** + * 사용 본거지 번지 + */ + private String useStrnghldLnbr; + + /** + * 사용 본거지 호 + */ + private String useStrnghldHo; + + /** + * 사용 본거지 상세주소 + */ + private String useStrnghldAdresNm; + + /** + * 사용 본거지 도로명 코드 + */ + private String useStrnghldRoadNmCode; + + /** + * 사용 본거지 지하 건물 구분 코드 + */ + private String usgsrhldUndgrndBuldSeCode; + + /** + * 사용 본거지 건물 주요 번호 + */ + private String useStrnghldBuldMainNo; + + /** + * 사용 본거지 건물 부 번호 + */ + private String useStrnghldBuldSubNo; + + /** + * 사용 본거지 전체주소 + */ + private String usgsrhldAdresFull; + + /** + * 대표소유자 회원 구분 코드 + */ + private String mberSeCode; + + /** + * 대표소유자 회원 번호 + */ + private String mberSeNo; + + /** + * 대표소유자 전화번호 + */ + private String telno; + + /** + * 소유자 법정동 코드 + */ + private String ownerLegaldongCode; + + /** + * 소유자 행정동 코드 + */ + private String ownerAdstrdCode; + + /** + * 소유자 산 + */ + private String ownerMntn; + + /** + * 소유자 번지 + */ + private String ownerLnbr; + + /** + * 소유자 호 + */ + private String ownerHo; + + /** + * 소유자 상세주소 + */ + private String ownerAdresNm; + + /** + * 소유자 도로명 코드 + */ + private String ownerRoadNmCode; + + /** + * 소유자 지하건물 구분 코드 + */ + private String ownerUndgrndBuldSeCode; + + /** + * 소유자 건물 주요 번호 + */ + private String ownerBuldMainNo; + + /** + * 소유자 건물 부 번호 + */ + private String ownerBuldSubNo; + + /** + * 소유자 전체주소 + */ + private String ownrWholaddr; + + /** + * 신 차량번호 + */ + private String aftrVhrno; + + /** + * 사용 연료 코드 + */ + private String useFuelCode; + + /** + * 용도 구분 코드 + */ + private String prposSeCode; + + /** + * 원동기 형식명 + */ + private String mtrsFomNm; + + /** + * 이전 차량번호 + */ + private String frntVhrno; + + /** + * 차량번호 + */ + private String vhclno; + + /** + * 차대번호 + */ + private String vin; + + /** + * 차명 + */ + private String cnm; + + /** + * 차량 총 중량 + */ + private String vhcleTotWt; + + /** + * 차령 만료일자 + */ + private String caagEndde; + + /** + * 차번호 변경시기 + */ + private String changeDe; + + /** + * 차종 종별 코드 + */ + private String vhctyAsortCode; + + /** + * 차종 유형 코드 + */ + private String vhctyTyCode; + + /** + * 차종 분류 코드 + */ + private String vhctySeCode; + + /** + * 최대 적재량 + */ + private String mxmmLdg; + + /** + * 차종 종별명 + */ + private String vhctyAsortNm; + + /** + * 차종 유형명 + */ + private String vhctyTyNm; + + /** + * 차종 분류명 + */ + private String vhctySeNm; + + /** + * 최초 등록일 + */ + private String frstRegistDe; + + /** + * 형식 + */ + private String fomNm; + + /** + * 취득 일자 + */ + private String acqsDe; + + /** + * 취득 종료일자 + */ + private String acqsEndDe; + + /** + * 제작 년월일 + */ + private String yblMd; + + /** + * 이전 등록일 + */ + private String transrRegistDe; + + /** + * 제원 등록 상태 코드 + */ + private String spcfRegistSttusCode; + + /** + * 색상명 + */ + private String colorNm; + + /** + * 저당수 + */ + private String mrtgCo; + + /** + * 압류건수 + */ + private String seizrCo; + + /** + * 구조변경수 + */ + private String stmdCo; + + /** + * 번호판 영치 여부 + */ + private String nmplCsdyAt; + + /** + * 번호판 영치 최고일 + */ + private String nmplCsdyRemnrDe; + + /** + * 출처 구분 코드 + */ + private String originSeCode; + + /** + * 번호판 규격 코드 + */ + private String nmplStndrdCode; + + /** + * 취득 금액 + */ + private String acqsAmount; + + /** + * 검사 유효 기간 시작일 + */ + private String insptValidPdBgnde; + + /** + * 검사 유효 기간 종료일 + */ + private String insptValidPdEndde; + + /** + * 사용 본거지 관청 코드 + */ + private String useStrnghldGrcCode; + + /** + * 승차정원수 + */ + private String tkcarPscapCo; + + /** + * 제원관리번호 + */ + private String spmnno; + + /** + * 주행거리 + */ + private String trvlDstnc; + + /** + * 최초 등록 접수번호 + */ + private String frstRegistRqrcno; + + /** + * 예고통지일 + */ + private String vlntErsrPrvntcNticeDe; + + /** + * 등록 기관명 + */ + private String registInsttNm; + + /** + * 처리 불가 사유 코드 + */ + private String processImprtyResnCode; + + /** + * 처리 불가 사유 명세 + */ + private String processImprtyResnDtls; + + /** + * 차체 길이 + */ + private String cbdLt; + + /** + * 차체 너비 + */ + private String cbdBt; + + /** + * 차체 높이 + */ + private String cbdHg; + + /** + * 최초 최대 적재량 + */ + private String frstMxmmLdg; + + /** + * 연료 소비율 + */ + private String fuelCnsmpRt; + + /** + * 전기 복합 연료 소비율 + */ + private String elctyCmpndFuelCnsmpRt; + + /** + * 등록 일시 + */ + private LocalDateTime regDt; + + /** + * 등록자 + */ + private String rgtr; + + /** + * 대표소유자 성명 + */ + private String mberNm; +} diff --git a/src/main/java/com/vmis/interfaceapp/service/CarBassMatterInqireService.java b/src/main/java/com/vmis/interfaceapp/service/CarBassMatterInqireService.java new file mode 100644 index 0000000..f69f410 --- /dev/null +++ b/src/main/java/com/vmis/interfaceapp/service/CarBassMatterInqireService.java @@ -0,0 +1,126 @@ +package com.vmis.interfaceapp.service; + +import com.vmis.interfaceapp.mapper.CarBassMatterInqireMapper; +import com.vmis.interfaceapp.model.basic.CarBassMatterInqireVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 자동차 기본 사항 조회 서비스 + * + *

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

+ *
    + *
  • 최초 요청: createInitialRequest() - 시퀀스로 ID 생성 후 INSERT
  • + *
  • 결과 업데이트: updateResponse() - 응답 데이터 UPDATE
  • + *
+ */ +@Slf4j +@Service +@RequiredArgsConstructor +public class CarBassMatterInqireService { + + private final CarBassMatterInqireMapper carBassMatterInqireMapper; + + /** + * 최초 API 요청 정보를 등록합니다. + * + *

시퀀스로 새로운 ID를 생성하여 요청 정보를 저장합니다. + * 트랜잭션 내에서 실행되며, 예외 발생 시 롤백됩니다.

+ * + *

사용 예시:

+ *
+     * {@code
+     * CarBassMatterInqire request = CarBassMatterInqire.builder()
+     *     .infoSysId("41-345")
+     *     .infoSysIp("105.19.10.135")
+     *     .sigunguCode("41460")
+     *     .cntcInfoCode("AC1_FD11_01")
+     *     .dmndVhrno("12가3456")
+     *     .rgtr("SYSTEM")
+     *     .build();
+     *
+     * String generatedId = service.createInitialRequest(request);
+     * log.info("생성된 ID: {}", generatedId);
+     * }
+     * 
+ * + * @param request 요청 정보 (ID 제외) + * @return 생성된 자동차 기본 사항 조회 ID (CBMI000000000001 형식) + * @throws RuntimeException 저장 실패 시 + */ + @Transactional + public String createInitialRequest(CarBassMatterInqireVO request) { + // 시퀀스로 새로운 ID 생성 + String generatedId = carBassMatterInqireMapper.selectNextCarBassMatterInqireId(); + log.debug("생성된 자동차 기본 사항 조회 ID: {}", generatedId); + + // 생성된 ID 설정 + request.setCarBassMatterInqire(generatedId); + + // INSERT + int result = carBassMatterInqireMapper.insertCarBassMatterInqire(request); + if (result != 1) { + throw new RuntimeException("자동차 기본 사항 조회 정보 등록 실패"); + } + + log.info("자동차 기본 사항 조회 정보 등록 완료 - ID: {}, 차량번호: {}, 차대번호: {}", + generatedId, request.getDmndVhrno(), request.getDmndVin()); + + return generatedId; + } + + /** + * API 응답 결과를 업데이트합니다. + * + *

기존 레코드에 응답 데이터를 업데이트합니다. + * null이 아닌 필드만 업데이트되며, 트랜잭션 내에서 실행됩니다.

+ * + *

사용 예시:

+ *
+     * {@code
+     * CarBassMatterInqire response = CarBassMatterInqire.builder()
+     *     .carBassMatterInqire("CBMI000000000001")
+     *     .cntcResultCode("00")
+     *     .cntcResultDtls("성공")
+     *     .vhclno("12가3456")
+     *     .vin("KMHXX00XXXX000000")
+     *     .cnm("소나타")
+     *     // ... 기타 응답 필드
+     *     .build();
+     *
+     * service.updateResponse(response);
+     * }
+     * 
+ * + * @param response 응답 정보 (carBassMatterInqire 필드 필수) + * @throws RuntimeException 업데이트 실패 시 (레코드가 없는 경우 포함) + */ + @Transactional + public void updateResponse(CarBassMatterInqireVO 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("자동차 기본 사항 조회 정보 업데이트 완료 - ID: {}, 결과코드: {}, 차량번호: {}", + response.getCarBassMatterInqire(), response.getCntcResultCode(), response.getVhclno()); + } + + /** + * ID로 조회 정보를 조회합니다. + * + * @param carBassMatterInqire 자동차 기본 사항 조회 ID + * @return 조회된 정보 (없으면 null) + */ + @Transactional(readOnly = true) + public CarBassMatterInqireVO getById(String carBassMatterInqire) { + return carBassMatterInqireMapper.selectCarBassMatterInqireById(carBassMatterInqire); + } + +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index d496d2f..69b9ba3 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,6 +1,44 @@ server: port: 8080 +spring: + # DataSource 설정 - MariaDB + datasource: + driver-class-name: org.mariadb.jdbc.Driver + url: jdbc:mariadb://211.119.124.117:53306/ibmsdb?characterEncoding=UTF-8&allowMultiQueries=true + username: root + password: xit5811807 + hikari: + # 커넥션 풀 크기 설정 (4코어 32GB 서버 기준) + # 동시에 사용할 수 있는 최대 커넥션 수 + # 권장값: (코어수 × 2) + (동시사용자 × 0.1) = (4 × 2) + (300 × 0.1) = 38 → 40 + maximum-pool-size: 40 + # 풀에서 유지할 최소 유휴 커넥션 수 + # 권장값: maximum-pool-size의 25% (40 × 0.25 = 10) + minimum-idle: 10 + # 커넥션을 얻기 위한 최대 대기 시간 (밀리초) + # 권장값: 30초 - 네트워크 지연이나 데이터베이스 부하 시 적절한 대기 시간 + connection-timeout: 30000 + # 커넥션 유효성 검사 타임아웃 (밀리초) + validation-timeout: 60000 + # 커넥션의 최대 생명 시간 (밀리초) + # 권장값: 30분 - 데이터베이스 연결이 너무 오래 유지되지 않도록 제한 + max-lifetime: 1800000 + # 유휴 커넥션을 제거하기 위한 최소 대기 시간 (밀리초) + # 권장값: 10분 - 메모리 절약과 커넥션 재사용의 균형점 + idle-timeout: 600000 + # auto-commit을 false로 설정하여 명시적 트랜잭션 관리 + auto-commit: false + +# MyBatis 설정 +mybatis: + # MyBatis 전역 설정 파일 위치 + config-location: classpath:mybatis/mybatis-config.xml + # Mapper XML 파일 위치 (DbType 변수 사용) + mapper-locations: classpath:mybatis/mapper/**/*_${Globals.DbType}.xml + # 타입 별칭 패키지 (하위 패키지 자동 스캔) + type-aliases-package: com.vmis.interfaceapp.model + # 로그 설정 - 개발(DEV) 환경 logging: config: classpath:logback-spring.xml diff --git a/src/main/resources/application-prd.yml b/src/main/resources/application-prd.yml index e5983b5..9ac423d 100644 --- a/src/main/resources/application-prd.yml +++ b/src/main/resources/application-prd.yml @@ -1,6 +1,44 @@ server: port: 8080 +spring: + # DataSource 설정 - MariaDB + datasource: + driver-class-name: org.mariadb.jdbc.Driver + url: jdbc:mariadb://211.119.124.117:53306/ibmsdb?characterEncoding=UTF-8&allowMultiQueries=true + username: root + password: xit5811807 + hikari: + # 커넥션 풀 크기 설정 (4코어 32GB 서버 기준) + # 동시에 사용할 수 있는 최대 커넥션 수 + # 권장값: (코어수 × 2) + (동시사용자 × 0.1) = (4 × 2) + (300 × 0.1) = 38 → 40 + maximum-pool-size: 40 + # 풀에서 유지할 최소 유휴 커넥션 수 + # 권장값: maximum-pool-size의 25% (40 × 0.25 = 10) + minimum-idle: 10 + # 커넥션을 얻기 위한 최대 대기 시간 (밀리초) + # 권장값: 30초 - 네트워크 지연이나 데이터베이스 부하 시 적절한 대기 시간 + connection-timeout: 30000 + # 커넥션 유효성 검사 타임아웃 (밀리초) + validation-timeout: 60000 + # 커넥션의 최대 생명 시간 (밀리초) + # 권장값: 30분 - 데이터베이스 연결이 너무 오래 유지되지 않도록 제한 + max-lifetime: 1800000 + # 유휴 커넥션을 제거하기 위한 최소 대기 시간 (밀리초) + # 권장값: 10분 - 메모리 절약과 커넥션 재사용의 균형점 + idle-timeout: 600000 + # auto-commit을 false로 설정하여 명시적 트랜잭션 관리 + auto-commit: false + +# MyBatis 설정 +mybatis: + # MyBatis 전역 설정 파일 위치 + config-location: classpath:mybatis/mybatis-config.xml + # Mapper XML 파일 위치 (DbType 변수 사용) + mapper-locations: classpath:mybatis/mapper/**/*_${Globals.DbType}.xml + # 타입 별칭 패키지 (하위 패키지 자동 스캔) + type-aliases-package: com.vmis.interfaceapp.model + # 로그 설정 - 운영(PRD) 환경 logging: config: classpath:logback-spring.xml diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0718988..97c8ccb 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -5,5 +5,9 @@ spring: pathmatch: matching-strategy: ant_path_matcher +# 전역 설정 +Globals: + DbType: maria + server: port: 8080 \ No newline at end of file diff --git a/src/main/resources/mybatis/mapper/CarBassMatterInqireMapper_maria.xml b/src/main/resources/mybatis/mapper/CarBassMatterInqireMapper_maria.xml new file mode 100644 index 0000000..cc6dd0b --- /dev/null +++ b/src/main/resources/mybatis/mapper/CarBassMatterInqireMapper_maria.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + INSERT INTO tb_car_bass_matter_inqire ( + CAR_BASS_MATTER_INQIRE, + INFO_SYS_ID, + INFO_SYS_IP, + SIGUNGU_CODE, + CNTC_INFO_CODE, + CHARGER_ID, + CHARGER_IP, + CHARGER_NM, + DMND_LEVY_STDDE, + DMND_INQIRE_SE_CODE, + DMND_VHRNO, + DMND_VIN, + REG_DT, + RGTR + ) VALUES ( + #{carBassMatterInqire}, + #{infoSysId}, + #{infoSysIp}, + #{sigunguCode}, + #{cntcInfoCode}, + #{chargerId}, + #{chargerIp}, + #{chargerNm}, + #{dmndLevyStdde}, + #{dmndInqireSeCode}, + #{dmndVhrno}, + #{dmndVin}, + NOW(), + #{rgtr} + ) + + + + + UPDATE tb_car_bass_matter_inqire + + CNTC_RESULT_CODE = #{cntcResultCode}, + CNTC_RESULT_DTLS = #{cntcResultDtls}, + PRYE = #{prye}, + REGIST_DE = #{registDe}, + ERSR_REGIST_SE_CODE = #{ersrRegistSeCode}, + ERSR_REGIST_SE_NM = #{ersrRegistSeNm}, + ERSR_REGIST_DE = #{ersrRegistDe}, + REGIST_DETAIL_CODE = #{registDetailCode}, + DSPLVL = #{dsplvl}, + USE_STRNGHLD_LEGALDONG_CODE = #{useStrnghldLegaldongCode}, + USE_STRNGHLD_ADSTRD_CODE = #{useStrnghldAdstrdCode}, + USE_STRNGHLD_MNTN = #{useStrnghldMntn}, + USE_STRNGHLD_LNBR = #{useStrnghldLnbr}, + USE_STRNGHLD_HO = #{useStrnghldHo}, + USE_STRNGHLD_ADRES_NM = #{useStrnghldAdresNm}, + USE_STRNGHLD_ROAD_NM_CODE = #{useStrnghldRoadNmCode}, + USGSRHLD_UNDGRND_BULD_SE_CODE = #{usgsrhldUndgrndBuldSeCode}, + USE_STRNGHLD_BULD_MAIN_NO = #{useStrnghldBuldMainNo}, + USE_STRNGHLD_BULD_SUB_NO = #{useStrnghldBuldSubNo}, + USGSRHLD_ADRES_FULL = #{usgsrhldAdresFull}, + MBER_SE_CODE = #{mberSeCode}, + MBER_SE_NO = #{mberSeNo}, + TELNO = #{telno}, + OWNER_LEGALDONG_CODE = #{ownerLegaldongCode}, + OWNER_ADSTRD_CODE = #{ownerAdstrdCode}, + OWNER_MNTN = #{ownerMntn}, + OWNER_LNBR = #{ownerLnbr}, + OWNER_HO = #{ownerHo}, + OWNER_ADRES_NM = #{ownerAdresNm}, + OWNER_ROAD_NM_CODE = #{ownerRoadNmCode}, + OWNER_UNDGRND_BULD_SE_CODE = #{ownerUndgrndBuldSeCode}, + OWNER_BULD_MAIN_NO = #{ownerBuldMainNo}, + OWNER_BULD_SUB_NO = #{ownerBuldSubNo}, + OWNR_WHOLADDR = #{ownrWholaddr}, + AFTR_VHRNO = #{aftrVhrno}, + USE_FUEL_CODE = #{useFuelCode}, + PRPOS_SE_CODE = #{prposSeCode}, + MTRS_FOM_NM = #{mtrsFomNm}, + FRNT_VHRNO = #{frntVhrno}, + VHCLNO = #{vhclno}, + VIN = #{vin}, + CNM = #{cnm}, + VHCLE_TOT_WT = #{vhcleTotWt}, + CAAG_ENDDE = #{caagEndde}, + CHANGE_DE = #{changeDe}, + VHCTY_ASORT_CODE = #{vhctyAsortCode}, + VHCTY_TY_CODE = #{vhctyTyCode}, + VHCTY_SE_CODE = #{vhctySeCode}, + MXMM_LDG = #{mxmmLdg}, + VHCTY_ASORT_NM = #{vhctyAsortNm}, + VHCTY_TY_NM = #{vhctyTyNm}, + VHCTY_SE_NM = #{vhctySeNm}, + FRST_REGIST_DE = #{frstRegistDe}, + FOM_NM = #{fomNm}, + ACQS_DE = #{acqsDe}, + ACQS_END_DE = #{acqsEndDe}, + YBL_MD = #{yblMd}, + TRANSR_REGIST_DE = #{transrRegistDe}, + SPCF_REGIST_STTUS_CODE = #{spcfRegistSttusCode}, + COLOR_NM = #{colorNm}, + MRTG_CO = #{mrtgCo}, + SEIZR_CO = #{seizrCo}, + STMD_CO = #{stmdCo}, + NMPL_CSDY_AT = #{nmplCsdyAt}, + NMPL_CSDY_REMNR_DE = #{nmplCsdyRemnrDe}, + ORIGIN_SE_CODE = #{originSeCode}, + NMPL_STNDRD_CODE = #{nmplStndrdCode}, + ACQS_AMOUNT = #{acqsAmount}, + INSPT_VALID_PD_BGNDE = #{insptValidPdBgnde}, + INSPT_VALID_PD_ENDDE = #{insptValidPdEndde}, + USE_STRNGHLD_GRC_CODE = #{useStrnghldGrcCode}, + TKCAR_PSCAP_CO = #{tkcarPscapCo}, + SPMNNO = #{spmnno}, + TRVL_DSTNC = #{trvlDstnc}, + FRST_REGIST_RQRCNO = #{frstRegistRqrcno}, + VLNT_ERSR_PRVNTC_NTICE_DE = #{vlntErsrPrvntcNticeDe}, + REGIST_INSTT_NM = #{registInsttNm}, + PROCESS_IMPRTY_RESN_CODE = #{processImprtyResnCode}, + PROCESS_IMPRTY_RESN_DTLS = #{processImprtyResnDtls}, + CBD_LT = #{cbdLt}, + CBD_BT = #{cbdBt}, + CBD_HG = #{cbdHg}, + FRST_MXMM_LDG = #{frstMxmmLdg}, + FUEL_CNSMP_RT = #{fuelCnsmpRt}, + ELCTY_CMPND_FUEL_CNSMP_RT = #{elctyCmpndFuelCnsmpRt}, + MBER_NM = #{mberNm}, + + WHERE CAR_BASS_MATTER_INQIRE = #{carBassMatterInqire} + + + + + + diff --git a/src/main/resources/mybatis/mapper/sample_maria.xml b/src/main/resources/mybatis/mapper/sample_maria.xml new file mode 100644 index 0000000..b1a4c7f --- /dev/null +++ b/src/main/resources/mybatis/mapper/sample_maria.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + diff --git a/src/main/resources/mybatis/mybatis-config.xml b/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 0000000..2f288d1 --- /dev/null +++ b/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +