[Schedule]

- Created schedule worker. (schedules are only in sys-rest-api)
[HcmClient]
- Added get system token to call hcm-rest-api
This commit is contained in:
Hyojin Ahn 2026-01-14 13:39:22 -05:00
parent 923b6d5aca
commit 6a7882a028
10 changed files with 221 additions and 185 deletions

View File

@ -14,6 +14,7 @@ import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import com.goi.erp.token.PermissionAuthenticationToken; import com.goi.erp.token.PermissionAuthenticationToken;
import com.goi.erp.token.SystemTokenProvider;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -24,54 +25,41 @@ import java.util.UUID;
public class HcmEmployeeClient { public class HcmEmployeeClient {
private final RestTemplate restTemplate; private final RestTemplate restTemplate;
private final SystemTokenProvider systemTokenProvider;
@Value("${hcm.api.base-url}") @Value("${hcm.api.base-url}")
private String hcmBaseUrl; private String hcmBaseUrl;
public Long getEmpIdFromExternalId(String externalSource, String externalId) { public Long getEmpIdFromExternalId(String externalSource, String externalId) {
String url = hcmBaseUrl + "/employee/external" + "?solutionType=" + externalSource + "&externalId=" + externalId; String url = hcmBaseUrl + "/employee/external"
+ "?solutionType=" + externalSource
+ "&externalId=" + externalId;
try { try {
// set token in header HttpEntity<Void> entity = new HttpEntity<>(systemHeaders());
String jwt = getCurrentJwt();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + jwt);
HttpEntity<Void> entity = new HttpEntity<>(headers);
// GET
ResponseEntity<Map<String, Object>> response = ResponseEntity<Map<String, Object>> response =
restTemplate.exchange( restTemplate.exchange(
url, url,
HttpMethod.GET, HttpMethod.GET,
entity, entity,
new ParameterizedTypeReference<Map<String, Object>>() {} new ParameterizedTypeReference<>() {}
); );
Map<String, Object> body = response.getBody(); Map<String, Object> body = response.getBody();
//System.out.println("RESPONSE ➜ " + body); if (body != null && body.get("eexEmpId") instanceof Number n) {
return n.longValue();
if (body != null && body.get("eexEmpId") != null) {
Object raw = body.get("eexEmpId");
if (raw instanceof Number) {
return ((Number) raw).longValue(); // 🔥 모든 숫자를 Long 변환
}
// 예상 타입일 경우
} }
return null; return null;
} catch (Exception e) { } catch (Exception e) {
log.error( log.error("[HCM][GET] externalId lookup error", e);
"[EXTERNAL][GET] externalId lookup error: {}",
e.getMessage()
);
return null; return null;
} }
} }
public Long getEmpIdFromUuid(UUID uuid) { public Long getEmpIdFromUuid(UUID uuid) {
String url = hcmBaseUrl + "/employee/" + uuid; String url = hcmBaseUrl + "/employee/" + uuid;
@ -125,6 +113,12 @@ public class HcmEmployeeClient {
return null; return null;
} }
private HttpHeaders systemHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(systemTokenProvider.getToken());
return headers;
}
} }

View File

@ -33,6 +33,7 @@ public class SecurityConfig {
.authorizeHttpRequests(auth -> auth .authorizeHttpRequests(auth -> auth
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.requestMatchers("/ext/**").permitAll() .requestMatchers("/ext/**").permitAll()
.requestMatchers("/scheduler/**").permitAll()
.anyRequest().authenticated() .anyRequest().authenticated()
) // 요청 권한 설정 ) // 요청 권한 설정
.addFilterBefore(new CorsFilter(corsConfigurationSource()), UsernamePasswordAuthenticationFilter.class) // JWT 필터 전에 CorsFilter 등록 .addFilterBefore(new CorsFilter(corsConfigurationSource()), UsernamePasswordAuthenticationFilter.class) // JWT 필터 전에 CorsFilter 등록

View File

@ -1,38 +0,0 @@
package com.goi.erp.controller;
import com.goi.erp.service.ExtSamsaraInspectionProcessor;
import com.goi.erp.service.VehicleDispatchAutoCloseService;
import lombok.RequiredArgsConstructor;
import java.time.LocalDate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
@RequiredArgsConstructor
public class TestController {
private final ExtSamsaraInspectionProcessor processor;
private final VehicleDispatchAutoCloseService autoCloseService;
@PostMapping("/process-samsara-inspection")
public String process() {
processor.processUnprocessed();
return "OK";
}
/**
* 차량 배차 자동 종료 테스트 (오늘 날짜 기준)
*/
@PostMapping("/auto-close-dispatch")
public String autoCloseDispatch() {
autoCloseService.processAutoClose(LocalDate.now());
return "AUTO CLOSE OK";
}
}

View File

@ -11,7 +11,6 @@ import lombok.NoArgsConstructor;
@Builder @Builder
public class ExtIngestResult { public class ExtIngestResult {
private String source; private String source;
private String recordType;
private int received; private int received;
private int inserted; private int inserted;
private int updated; private int updated;

View File

@ -14,7 +14,6 @@ import java.util.List;
@Builder @Builder
public class ExtSamsaraInspectionIngestCommand { public class ExtSamsaraInspectionIngestCommand {
private String source; private String source;
private String recordType;
private LocalDateTime fetchedAt; private LocalDateTime fetchedAt;
private List<ExtSamsaraInspectionRecordDto> records; private List<ExtSamsaraInspectionRecordDto> records;
} }

View File

@ -49,9 +49,6 @@ public class ExtSamsaraRawInspection {
@Column(name = "esri_source", nullable = false, length = 20) @Column(name = "esri_source", nullable = false, length = 20)
private String esriSource; // SAMSARA private String esriSource; // SAMSARA
@Column(name = "esri_record_type", nullable = false, length = 20)
private String esriRecordType; // DVIR
@Column(name = "esri_external_id", nullable = false, length = 50) @Column(name = "esri_external_id", nullable = false, length = 50)
private String esriExternalId; // inspection id private String esriExternalId; // inspection id

View File

@ -50,7 +50,6 @@ public class ExtSamsaraInspectionIngestService {
return ExtIngestResult.builder() return ExtIngestResult.builder()
.source(command.getSource()) .source(command.getSource())
.recordType(command.getRecordType())
.received(command.getRecords().size()) .received(command.getRecords().size())
.inserted(inserted) .inserted(inserted)
.updated(updated) .updated(updated)
@ -90,7 +89,6 @@ public class ExtSamsaraInspectionIngestService {
ExtSamsaraRawInspection entity = ExtSamsaraRawInspection.builder() ExtSamsaraRawInspection entity = ExtSamsaraRawInspection.builder()
.esriSource(command.getSource()) .esriSource(command.getSource())
.esriRecordType(command.getRecordType())
.esriExternalId(record.getExternalId()) .esriExternalId(record.getExternalId())
.esriVehicleExtId(record.getVehicleExternalId()) .esriVehicleExtId(record.getVehicleExternalId())
.esriDriverExtId(record.getDriverExternalId()) .esriDriverExtId(record.getDriverExternalId())

View File

@ -1,6 +1,7 @@
package com.goi.erp.service; package com.goi.erp.service;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.goi.erp.dto.ProcessResultDto;
import com.goi.erp.dto.VehicleInspectionDefectDto; import com.goi.erp.dto.VehicleInspectionDefectDto;
import com.goi.erp.dto.VehicleInspectionDto; import com.goi.erp.dto.VehicleInspectionDto;
import com.goi.erp.entity.ExtSamsaraRawInspection; import com.goi.erp.entity.ExtSamsaraRawInspection;
@ -11,6 +12,7 @@ import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -25,43 +27,61 @@ public class ExtSamsaraInspectionProcessor {
private final ExtSamsaraRawInspectionRepository rawRepo; private final ExtSamsaraRawInspectionRepository rawRepo;
private final VehicleInspectionService vehicleInspectionService; private final VehicleInspectionService vehicleInspectionService;
@Transactional public ProcessResultDto processUnprocessed() {
public void processUnprocessed() {
List<ExtSamsaraRawInspection> raws = rawRepo.findByEsriProcessedFalse();
if (raws.isEmpty()) {
log.info("[DVIR_PROCESSOR] no unprocessed raw inspections");
return;
}
// log
int processed = 0; int processed = 0;
int failed = 0; int failed = 0;
List<ExtSamsaraRawInspection> raws = rawRepo.findByEsriProcessedFalseAndEsriFetchedDate(LocalDate.now());
if (raws.isEmpty()) {
log.info("[INSPECTION_PROCESSOR] no unprocessed raw inspections");
return new ProcessResultDto(0, 0);
}
for (ExtSamsaraRawInspection raw : raws) { for (ExtSamsaraRawInspection raw : raws) {
try { try {
processSingle(raw); processSingleTx(raw);
processed++; processed++;
} catch (Exception e) { } catch (Exception e) {
failed++; failed++;
log.error( // markFailed(raw, e);
"[DVIR_PROCESSOR] failed rawId={} externalId={} msg={}",
raw.getEsriId(),
raw.getEsriExternalId(),
e.getMessage(),
e
);
} }
} }
log.info("[DVIR_PROCESSOR] done processed={} failed={}", processed, failed); log.info("[INSPECTION_PROCESSOR] done processed={} failed={}", processed, failed);
return new ProcessResultDto(processed, failed);
} }
/**
* raw 1건 단위 트랜잭션
*/
@Transactional
protected void processSingleTx(ExtSamsaraRawInspection raw) {
processSingle(raw);
raw.setEsriProcessed(true);
raw.setEsriProcessedAt(LocalDateTime.now());
rawRepo.save(raw);
}
// private void markFailed(ExtSamsaraRawInspection raw, Exception e) {
// log.error(
// "[INSPECTION_PROCESSOR] failed rawId={} externalId={} msg={}",
// raw.getEsriId(),
// raw.getEsriExternalId(),
// e.getMessage(),
// e
// );
//
// raw.setEsriProcessError(e.getMessage());
// raw.setEsriProcessFailedAt(LocalDateTime.now());
// rawRepo.save(raw);
// }
private void processSingle(ExtSamsaraRawInspection raw) { private void processSingle(ExtSamsaraRawInspection raw) {
JsonNode payload = raw.getEsriPayload(); JsonNode payload = raw.getEsriPayload();
// Inspection
VehicleInspectionDto dto = new VehicleInspectionDto(); VehicleInspectionDto dto = new VehicleInspectionDto();
dto.setSource("SAMSARA"); dto.setSource("SAMSARA");
dto.setSourceId(Long.valueOf(raw.getEsriExternalId())); dto.setSourceId(Long.valueOf(raw.getEsriExternalId()));
@ -80,14 +100,7 @@ public class ExtSamsaraInspectionProcessor {
dto.setResult(payload.path("safetyStatus").asText(null)); dto.setResult(payload.path("safetyStatus").asText(null));
dto.setOdometer(payload.path("odometerMeters").asLong(0L)); dto.setOdometer(payload.path("odometerMeters").asLong(0L));
log.info( // defects
"[DVIR] rawId={} odometerMeters node={} value={}",
raw.getEsriExternalId(),
payload.get("odometerMeters"),
payload.path("odometerMeters").asLong(-1)
);
// Defects
JsonNode defectsNode = payload.path("vehicleDefects"); JsonNode defectsNode = payload.path("vehicleDefects");
List<VehicleInspectionDefectDto> defectDtos = new ArrayList<>(); List<VehicleInspectionDefectDto> defectDtos = new ArrayList<>();
@ -98,33 +111,22 @@ public class ExtSamsaraInspectionProcessor {
defectDto.setDefectType(d.path("defectType").asText(null)); defectDto.setDefectType(d.path("defectType").asText(null));
defectDto.setComment(d.path("comment").asText(null)); defectDto.setComment(d.path("comment").asText(null));
defectDto.setIsResolved(d.path("isResolved").asBoolean(false)); defectDto.setIsResolved(d.path("isResolved").asBoolean(false));
JsonNode resolvedBy = d.path("resolvedBy"); JsonNode resolvedBy = d.path("resolvedBy");
if (!resolvedBy.isMissingNode()) { if (!resolvedBy.isMissingNode()) {
defectDto.setResolvedByExternalId(resolvedBy.path("id").asText(null)); defectDto.setResolvedByExternalId(resolvedBy.path("id").asText(null));
} }
defectDto.setResolvedAt(DateTimeUtil.parse(d.path("resolvedAtTime"))); defectDto.setResolvedAt(DateTimeUtil.parse(d.path("resolvedAtTime")));
defectDto.setCreatedAt(DateTimeUtil.parse(d.path("createdAtTime"))); defectDto.setCreatedAt(DateTimeUtil.parse(d.path("createdAtTime")));
defectDtos.add(defectDto); defectDtos.add(defectDto);
} }
} }
dto.setDefects(defectDtos); dto.setDefects(defectDtos);
dto.setHasDefect(!defectDtos.isEmpty()); dto.setHasDefect(!defectDtos.isEmpty());
log.info(
"[DVIR DTO] sourceId={} result={} odometer={} hasDefect={} defects={}",
dto.getSourceId(),
dto.getResult(),
dto.getOdometer(),
dto.getHasDefect(),
dto.getDefects() != null ? dto.getDefects().size() : null
);
// inspection + defect 저장은 Service 에서
vehicleInspectionService.saveInspection(dto); vehicleInspectionService.saveInspection(dto);
// raw 처리 완료
raw.setEsriProcessed(true);
raw.setEsriProcessedAt(LocalDateTime.now());
rawRepo.save(raw);
} }
} }

View File

@ -1,8 +1,9 @@
package com.goi.erp.service; package com.goi.erp.service;
import com.goi.erp.client.SamsaraVehicleStatGpsClient; import com.goi.erp.client.SamsaraVehicleStatGpsClient;
import com.goi.erp.client.SysConfigClient;
import com.goi.erp.common.util.GeoUtil; import com.goi.erp.common.util.GeoUtil;
import com.goi.erp.dto.ProcessResultDto;
import com.goi.erp.dto.ScheduleWorkerRequestDto;
import com.goi.erp.dto.VehicleStatGpsResponseDto; import com.goi.erp.dto.VehicleStatGpsResponseDto;
import com.goi.erp.entity.VehicleDispatch; import com.goi.erp.entity.VehicleDispatch;
import com.goi.erp.repository.VehicleDispatchRepository; import com.goi.erp.repository.VehicleDispatchRepository;
@ -35,22 +36,38 @@ public class VehicleDispatchAutoCloseService {
private final VehicleDispatchRepository vehicleDispatchRepository; private final VehicleDispatchRepository vehicleDispatchRepository;
private final VehicleExternalMapService vehicleExternalMapService; private final VehicleExternalMapService vehicleExternalMapService;
private final SamsaraVehicleStatGpsClient vehicleStatClient; private final SamsaraVehicleStatGpsClient vehicleStatClient;
private final SysConfigClient sysConfigClient;
@Transactional @Transactional
public void processAutoClose(LocalDate dispatchDate) { public ProcessResultDto processAutoClose(ScheduleWorkerRequestDto request) {
// config
BigDecimal homeLat = sysConfigClient.getDecimal("SYS", "HOME_LATITUDE");
BigDecimal homeLon = sysConfigClient.getDecimal("SYS", "HOME_LONGITUDE");
Integer radiusMeters = sysConfigClient.getInt("OPR", "HOME_RADIUS_METERS");
Integer pausedMinutes = sysConfigClient.getInt("OPR", "PAUSED_CLOSE_MINUTES");
// 0. auto open check 먼저 int processed = 0;
processAutoOpen(dispatchDate, homeLat, homeLon, radiusMeters); int failed = 0;
// 1. 대상 vehicle_dispatch 조회 (C 제외) // config
Map<String, Map<String, String>> cfg = request.getConfig();
BigDecimal homeLat = new BigDecimal(cfg.get("SYS").get("HOME_LATITUDE"));
BigDecimal homeLon = new BigDecimal(cfg.get("SYS").get("HOME_LONGITUDE"));
Integer radiusMeters = Integer.valueOf(cfg.get("OPR").get("HOME_RADIUS_METERS"));
Integer pausedCloseMinutes = Integer.valueOf(cfg.get("OPR").getOrDefault("PAUSED_CLOSE_MINUTES", "30"));
LocalDate dispatchDate = request.getFrom().toLocalDate();
// 0. auto open
try {
processAutoOpen(dispatchDate, homeLat, homeLon, radiusMeters);
} catch (Exception e) {
log.error("[AUTO-CLOSE] auto-open failed", e);
failed++;
}
// 1. 대상 조회
List<VehicleDispatch> targets = vehicleDispatchRepository.findAllNotClosed(dispatchDate); List<VehicleDispatch> targets = vehicleDispatchRepository.findAllNotClosed(dispatchDate);
if (targets.isEmpty()) return;
if (targets.isEmpty()) {
log.info("[AUTO-CLOSE] no dispatch to process");
return new ProcessResultDto(0, 0);
}
// 2. external vehicleIds 수집 // 2. external vehicleIds 수집
List<Long> vehicleIds = targets.stream() List<Long> vehicleIds = targets.stream()
@ -58,8 +75,10 @@ public class VehicleDispatchAutoCloseService {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.distinct() .distinct()
.toList(); .toList();
if (vehicleIds.isEmpty()) return; if (vehicleIds.isEmpty()) {
log.info("[AUTO-CLOSE] no vehicles to process");
return new ProcessResultDto(0, 0);
}
// 3. internal vehicleId -> samsara externalId 매핑 bulk 조회 // 3. internal vehicleId -> samsara externalId 매핑 bulk 조회
Map<Long, String> vehicleIdToExternalId = vehicleExternalMapService.findExternalIdsByVehicleIds("SAMSARA", vehicleIds); Map<Long, String> vehicleIdToExternalId = vehicleExternalMapService.findExternalIdsByVehicleIds("SAMSARA", vehicleIds);
@ -68,11 +87,17 @@ public class VehicleDispatchAutoCloseService {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.distinct() .distinct()
.toList(); .toList();
if (externalIds.isEmpty()) return; if (externalIds.isEmpty()) {
log.info("[AUTO-CLOSE] no external vehicles to process");
return new ProcessResultDto(0, 0);
}
// 4. call // 4. call
List<VehicleStatGpsResponseDto> stats = vehicleStatClient.fetchVehicleStats(externalIds); List<VehicleStatGpsResponseDto> stats = vehicleStatClient.fetchVehicleStats(externalIds);
if (stats.isEmpty()) return; if (stats.isEmpty()) {
log.info("[AUTO-CLOSE] no stats to process");
return new ProcessResultDto(0, 0);
}
// 5. response // 5. response
Map<String, VehicleStatGpsResponseDto> statMap = stats.stream() Map<String, VehicleStatGpsResponseDto> statMap = stats.stream()
@ -82,63 +107,116 @@ public class VehicleDispatchAutoCloseService {
(a, b) -> a (a, b) -> a
)); ));
// 6. dispatch별 처리
for (VehicleDispatch dispatch : targets) { for (VehicleDispatch dispatch : targets) {
try {
Long internalVehicleId = dispatch.getVedVehId(); boolean changed = processSingleDispatch(
if (internalVehicleId == null) continue; dispatch,
vehicleIdToExternalId,
String externalId = vehicleIdToExternalId.get(internalVehicleId); statMap,
if (externalId == null) continue; homeLat,
homeLon,
VehicleStatGpsResponseDto stat = statMap.get(externalId); radiusMeters,
if (stat == null) continue; pausedCloseMinutes
);
// 관측값 계산 if (changed) {
boolean engineOn = Boolean.TRUE.equals(stat.getVesEngineOn()); processed++;
double distanceMeters = GeoUtil.distanceMeters(
homeLat.doubleValue(),
homeLon.doubleValue(),
stat.getVesLatitude().doubleValue(),
stat.getVesLongitude().doubleValue()
);
boolean isAtHome = distanceMeters <= radiusMeters;
boolean isStoppedAtHome = !engineOn && isAtHome;
// 상태 변경
String status = dispatch.getVedStatus();
// P O (다시 출발)
if ("P".equals(status) && !isStoppedAtHome) {
transitionToOnRoute(dispatch);
continue;
}
// O -> P (도착 정차)
if ("O".equals(status) && isStoppedAtHome) {
transitionToPaused(dispatch);
continue;
}
// P -> C (장시간 정차)
if ("P".equals(status) && isStoppedAtHome) {
if (dispatch.getVedPausedAt() == null) {
continue;
}
long pausedMin = Duration.between(
dispatch.getVedPausedAt(),
LocalDateTime.now()
).toMinutes();
if (pausedMin >= pausedMinutes) {
transitionToClosed(dispatch, "AUTO_CLOSE_HOME_IDLE");
} }
} catch (Exception e) {
failed++;
log.error(
"[AUTO-CLOSE] failed dispatchId={} msg={}",
dispatch.getVedId(),
e.getMessage(),
e
);
} }
} }
log.info("[AUTO-CLOSE] done processed={} failed={}", processed, failed);
return new ProcessResultDto(processed, failed);
}
private boolean processSingleDispatch(
VehicleDispatch dispatch,
Map<Long, String> vehicleIdToExternalId,
Map<String, VehicleStatGpsResponseDto> statMap,
BigDecimal homeLat,
BigDecimal homeLon,
Integer radiusMeters,
Integer pausedCloseMinutes
) {
Long internalVehicleId = dispatch.getVedVehId();
if (internalVehicleId == null) {
return false;
}
// internal external vehicleId
String externalId = vehicleIdToExternalId.get(internalVehicleId);
if (externalId == null) {
return false;
}
// 외부 GPS 상태
VehicleStatGpsResponseDto stat = statMap.get(externalId);
if (stat == null) {
return false;
}
boolean engineOn = Boolean.TRUE.equals(stat.getVesEngineOn());
double distanceMeters = GeoUtil.distanceMeters(
homeLat.doubleValue(),
homeLon.doubleValue(),
stat.getVesLatitude().doubleValue(),
stat.getVesLongitude().doubleValue()
);
boolean isAtHome = distanceMeters <= radiusMeters;
boolean isStoppedAtHome = !engineOn && isAtHome;
String status = dispatch.getVedStatus();
/*
* 상태 전이 규칙
*
* O P : HOME 근접 + engine OFF
* P O : HOME 이탈 or engine ON
* P C : HOME 근접 + engine OFF + paused_at N분
*/
// P O (다시 출발)
if ("P".equals(status) && !isStoppedAtHome) {
transitionToOnRoute(dispatch);
return true;
}
// O P (도착 정차)
if ("O".equals(status) && isStoppedAtHome) {
transitionToPaused(dispatch);
return true;
}
// P C (장시간 정차)
if ("P".equals(status) && isStoppedAtHome) {
if (dispatch.getVedPausedAt() == null) {
return false;
}
long pausedMinutes = Duration.between(
dispatch.getVedPausedAt(),
LocalDateTime.now()
).toMinutes();
if (pausedMinutes >= pausedCloseMinutes) {
transitionToClosed(dispatch, "AUTO_CLOSE_HOME_IDLE");
return true;
}
}
return false;
} }
private void processAutoOpen( private void processAutoOpen(

View File

@ -22,6 +22,9 @@ application:
expiration: 86400000 # a day expiration: 86400000 # a day
refresh-token: refresh-token:
expiration: 604800000 # 7 days expiration: 604800000 # 7 days
system:
client-id: opr-rest-api
client-secret: ${SYSTEM_CLIENT_SECRET}
pagination: pagination:
default-page: 0 default-page: 0
default-size: 20 default-size: 20
@ -33,6 +36,9 @@ server:
# ================================ # ================================
# ADD THIS # ADD THIS
# ================================ # ================================
auth:
api:
base-url: http://localhost:8080/auth-service
hcm: hcm:
api: api:
base-url: http://localhost:8081/hcm-rest-api base-url: http://localhost:8081/hcm-rest-api