parent
7a95540634
commit
425dd2c44d
@ -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 추상화 인터페이스.
|
||||
*
|
||||
* <p>외부 정부 시스템과의 통신 계약을 명확히 하여 테스트 용이성과
|
||||
* 추후 교체 가능성을 높입니다.</p>
|
||||
*/
|
||||
public interface GovernmentApi {
|
||||
|
||||
ResponseEntity<Envelope<BasicResponse>> callBasic(Envelope<BasicRequest> envelope);
|
||||
|
||||
ResponseEntity<Envelope<LedgerResponse>> callLedger(Envelope<LedgerRequest> envelope);
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package com.vmis.interfaceapp.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 샘플 MyBatis Mapper 인터페이스
|
||||
*
|
||||
* <p>이 인터페이스는 MyBatis 설정 테스트 및 예제 용도입니다.
|
||||
* 실제 프로젝트에서는 비즈니스 요구사항에 맞는 Mapper를 작성하세요.</p>
|
||||
*
|
||||
* <p>@Mapper 어노테이션을 사용하면 Spring이 자동으로 구현체를 생성합니다.
|
||||
* XML 파일의 namespace와 이 인터페이스의 경로가 일치해야 합니다.</p>
|
||||
*/
|
||||
@Mapper
|
||||
public interface SampleMapper {
|
||||
|
||||
/**
|
||||
* 데이터베이스 연결 테스트용 쿼리
|
||||
*
|
||||
* <p>현재 시간을 조회하여 데이터베이스 연결이 정상적으로
|
||||
* 작동하는지 확인할 수 있습니다.</p>
|
||||
*
|
||||
* @return 현재 시간 문자열
|
||||
*/
|
||||
String selectCurrentTime();
|
||||
}
|
||||
@ -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<Envelope<BasicResponse>> basic(Envelope<BasicRequest> 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<Envelope<BasicResponse>> 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<Envelope<LedgerResponse>> ledger(Envelope<LedgerRequest> 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<BasicResponse> 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;
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<!--
|
||||
샘플 MyBatis Mapper XML 파일
|
||||
|
||||
실제 사용 시에는 테이블명과 컬럼명을 프로젝트에 맞게 수정하세요.
|
||||
이 파일은 설정 테스트 및 예제 용도입니다.
|
||||
-->
|
||||
<mapper namespace="com.vmis.interfaceapp.mapper.SampleMapper">
|
||||
|
||||
<!-- 샘플 쿼리: 데이터베이스 연결 테스트 -->
|
||||
<select id="selectCurrentTime" resultType="String">
|
||||
SELECT NOW() AS current_time
|
||||
</select>
|
||||
|
||||
<!--
|
||||
추가 쿼리 예제:
|
||||
|
||||
<select id="selectById" parameterType="Long" resultType="com.vmis.interfaceapp.model.SampleEntity">
|
||||
SELECT id, name, created_at
|
||||
FROM sample_table
|
||||
WHERE id = #{id}
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="com.vmis.interfaceapp.model.SampleEntity">
|
||||
INSERT INTO sample_table (name, created_at)
|
||||
VALUES (#{name}, NOW())
|
||||
</insert>
|
||||
|
||||
<update id="update" parameterType="com.vmis.interfaceapp.model.SampleEntity">
|
||||
UPDATE sample_table
|
||||
SET name = #{name}
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
<delete id="delete" parameterType="Long">
|
||||
DELETE FROM sample_table
|
||||
WHERE id = #{id}
|
||||
</delete>
|
||||
-->
|
||||
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue