status-update log 추가

This commit is contained in:
2026-03-11 22:26:01 +09:00
parent 141b735ccc
commit 4322e46e3a
20 changed files with 144 additions and 15 deletions

View File

@@ -25,46 +25,105 @@ public class ExternalHttpClient {
public <T> ExternalCallResult<T> call(
String url, HttpMethod method, Object body, HttpHeaders headers, Class<T> responseType) {
long start = System.currentTimeMillis();
log.info("[API-REQ] method={}, url={}", method, url);
// responseType 기반으로 Accept 동적 세팅
HttpHeaders resolvedHeaders = resolveHeaders(headers, responseType);
log.debug("[API-REQ] headers={}", resolvedHeaders);
logRequestBody(body);
HttpEntity<Object> entity = new HttpEntity<>(body, resolvedHeaders);
try {
// String: raw bytes -> UTF-8 string
if (responseType == String.class) {
ResponseEntity<byte[]> res = restTemplate.exchange(url, method, entity, byte[].class);
String raw =
(res.getBody() == null) ? null : new String(res.getBody(), StandardCharsets.UTF_8);
long elapsed = System.currentTimeMillis() - start;
log.info(
"[API-RES] method={}, url={}, status={}, elapsed={}ms",
method,
url,
res.getStatusCodeValue(),
elapsed);
log.debug("[API-RES] body={}", raw);
@SuppressWarnings("unchecked")
T casted = (T) raw;
return new ExternalCallResult<>(res.getStatusCodeValue(), true, casted, null);
}
// byte[]: raw bytes로 받고, JSON이면 에러로 처리
if (responseType == byte[].class) {
ResponseEntity<byte[]> res = restTemplate.exchange(url, method, entity, byte[].class);
long elapsed = System.currentTimeMillis() - start;
log.info(
"[API-RES] method={}, url={}, status={}, elapsed={}ms",
method,
url,
res.getStatusCodeValue(),
elapsed);
MediaType ct = res.getHeaders().getContentType();
byte[] bytes = res.getBody();
if (isJsonLike(ct)) {
String err = (bytes == null) ? null : new String(bytes, StandardCharsets.UTF_8);
log.warn("[API-RES] JSON error body={}", err);
return new ExternalCallResult<>(res.getStatusCodeValue(), false, null, err);
}
@SuppressWarnings("unchecked")
T casted = (T) bytes;
return new ExternalCallResult<>(res.getStatusCodeValue(), true, casted, null);
}
// DTO 등: 일반 역직렬화
ResponseEntity<T> res = restTemplate.exchange(url, method, entity, responseType);
long elapsed = System.currentTimeMillis() - start;
log.info(
"[API-RES] method={}, url={}, status={}, elapsed={}ms",
method,
url,
res.getStatusCodeValue(),
elapsed);
log.debug("[API-RES] body={}", res.getBody());
return new ExternalCallResult<>(res.getStatusCodeValue(), true, res.getBody(), null);
} catch (HttpStatusCodeException e) {
long elapsed = System.currentTimeMillis() - start;
log.error(
"[API-ERROR] method={}, url={}, status={}, elapsed={}ms, body={}",
method,
url,
e.getStatusCode().value(),
elapsed,
e.getResponseBodyAsString());
return new ExternalCallResult<>(
e.getStatusCode().value(), false, null, e.getResponseBodyAsString());
}

View File

@@ -15,8 +15,8 @@ public class GukYuinPnuCntUpdateJobCoreService {
}
@Transactional
public void updateGukYuinContListPnuUpdateCnt() {
gukYuinPnuCntUpdateRepository.updateGukYuinContListPnuUpdateCnt();
public int updateGukYuinContListPnuUpdateCnt() {
return gukYuinPnuCntUpdateRepository.updateGukYuinContListPnuUpdateCnt();
}
@Transactional

View File

@@ -2,7 +2,7 @@ package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
public interface GukYuinPnuCntUpdateJobRepositoryCustom {
void updateGukYuinContListPnuUpdateCnt();
int updateGukYuinContListPnuUpdateCnt();
void updateGukYuinApplyStatus(String uid, String status);
}

View File

@@ -20,7 +20,7 @@ public class GukYuinPnuCntUpdateJobRepositoryImpl
@PersistenceContext private EntityManager em;
@Override
public void updateGukYuinContListPnuUpdateCnt() {
public int updateGukYuinContListPnuUpdateCnt() {
String sql =
"""
update tb_map_sheet_anal_data_inference_geom p
@@ -33,7 +33,7 @@ public class GukYuinPnuCntUpdateJobRepositoryImpl
where p.geo_uid = c_count.geo_uid and p.pnu != c_count.actual_count;
""";
jdbcTemplate.update(sql);
return jdbcTemplate.update(sql);
}
@Override

View File

@@ -23,11 +23,6 @@ public class GukYuinApiStatusJobService {
@Value("${spring.profiles.active}")
private String profile;
/**
* 실행중인 profile
*
* @return
*/
private boolean isLocalProfile() {
return "local".equalsIgnoreCase(profile);
}
@@ -35,34 +30,109 @@ public class GukYuinApiStatusJobService {
/** 매일 00시에 pnu cnt 업데이트 */
public void findGukYuinPnuCntUpdate() {
long jobStart = System.currentTimeMillis();
log.info("[JOB-START] GukYuin PNU CNT Update Job start profile={}", profile);
log.info("추론 learn 테이블의 apply_status = IN_PROGRESS 인 회차 조회");
log.info("국유인 연동 시작 상태 : IN_PROGRESS 로 업데이트 된다.");
log.info("국유인에서 shp 파일 모두 다운로드 되는지 확인 후 100%가 되면 PNU_COMPLETED 로 업데이트 한다.");
List<LearnKeyDto> list =
gukYuinJobCoreService.findGukyuinApplyStatusUidList(
List.of(GukYuinStatus.IN_PROGRESS.getId()));
if (list.isEmpty()) {
int total = list == null ? 0 : list.size();
log.info("[Step 1] 대상 추론 회차 UID 갯수 count={}", total);
if (list == null || list.isEmpty()) {
log.info("[JOB-END] 처리 대상 없음");
return;
}
int success = 0;
int skip = 0;
int fail = 0;
for (LearnKeyDto dto : list) {
long itemStart = System.currentTimeMillis();
try {
log.info("[Step 2] 처리 시작 uid={}, chnDtctMstId={}", dto.getUid(), dto.getChnDtctMstId());
log.info("[Step 3] 국유인 API 호출 시작 uid={}", dto.getUid());
ChngDetectMastDto.ResultDto result = gukYuinApiService.listChnDtctId(dto.getUid(), "Y");
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
log.warn("[GUKYUIN] empty result chnDtctMstId={}", dto.getChnDtctMstId());
if (result == null) {
log.warn("[Step 3] API result NULL uid={}", dto.getUid());
skip++;
continue;
}
if (result.getResult() == null || result.getResult().isEmpty()) {
log.warn("[Step 3] API result empty chnDtctMstId={}", dto.getChnDtctMstId());
skip++;
continue;
}
log.info("[Step 3] API result size={}", result.getResult().size());
ChngDetectMastDto.Basic basic = result.getResult().getFirst();
log.debug("[Step 4] API first result={}", basic);
Integer progress =
basic.getExcnPgrt() == null ? null : Integer.parseInt(basic.getExcnPgrt().trim());
log.info("[Step 4] 실행 진행률 progress={}", progress);
if (progress != null && progress == 100) {
gukYuinPnuCntUpdateJobCoreService.updateGukYuinContListPnuUpdateCnt();
log.info("[Step 5] progress 100 확인 → PNU CNT update 실행");
log.info(
"=== tb_pnu 에 insert 된 데이터 중 tb_map_sheet_anal_data_inference_geom 의 pnu (cnt)값이 다른 것 update");
int updateCnt = gukYuinPnuCntUpdateJobCoreService.updateGukYuinContListPnuUpdateCnt();
log.info("[Step 5] PNU CNT update 완료 updatedRows={}", updateCnt);
log.info(
"[Step 6] 추론 learn 테이블 apply_status={} 로 업데이트", GukYuinStatus.PNU_COMPLETED.getId());
gukYuinPnuCntUpdateJobCoreService.updateGukYuinApplyStatus(
dto.getUid(), GukYuinStatus.PNU_COMPLETED.getId());
log.info(
"[Step 6] apply_status 변경 완료 uid={}, status={}",
dto.getUid(),
GukYuinStatus.PNU_COMPLETED.getId());
success++;
} else {
log.info("[Step 5] progress != 100 skip uid={}, progress={}", dto.getUid(), progress);
skip++;
}
log.info(
"[ITEM-END] uid={} elapsed={}ms", dto.getUid(), System.currentTimeMillis() - itemStart);
} catch (Exception e) {
log.error("[GUKYUIN] failed uid={}", dto.getChnDtctMstId(), e);
fail++;
log.error(
"[ITEM-ERROR] uid={} elapsed={}ms",
dto.getChnDtctMstId(),
System.currentTimeMillis() - itemStart,
e);
}
}
log.info(
"[JOB-END] total={}, success={}, skip={}, fail={}, elapsed={}ms",
total,
success,
skip,
fail,
System.currentTimeMillis() - jobStart);
}
}