239 lines
8.7 KiB
Java
239 lines
8.7 KiB
Java
package com.goi.erp.service;
|
|
|
|
import com.goi.erp.dto.VehicleInspectionDefectDto;
|
|
import com.goi.erp.dto.VehicleInspectionDto;
|
|
import com.goi.erp.entity.VehicleDispatch;
|
|
import com.goi.erp.entity.VehicleInspection;
|
|
import com.goi.erp.entity.VehicleInspectionDefect;
|
|
import com.goi.erp.repository.VehicleDispatchRepository;
|
|
import com.goi.erp.repository.VehicleInspectionDefectRepository;
|
|
import com.goi.erp.repository.VehicleInspectionRepository;
|
|
|
|
import jakarta.transaction.Transactional;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.math.BigDecimal;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
|
|
@Slf4j
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
public class VehicleInspectionService {
|
|
|
|
private final VehicleInspectionRepository inspectionRepository;
|
|
private final VehicleInspectionDefectRepository defectRepository;
|
|
private final VehicleDispatchRepository dispatchRepository;
|
|
private final HcmEmployeeClient hcmEmployeeClient;
|
|
private final VehicleExternalMapService vehicleExternalMapService;
|
|
private final VehicleDispatchService vehicleDispatchService;
|
|
// private final VehicleService vehicleService;
|
|
|
|
/**
|
|
* Create or Update Vehicle Inspection (idempotent)
|
|
* -> defects upsert
|
|
* -> evaluate dispatch close by inspection
|
|
* -> create dispatch (preTrip) if needed
|
|
*/
|
|
@Transactional
|
|
public VehicleInspection saveInspection(VehicleInspectionDto dto) {
|
|
VehicleInspection inspection = inspectionRepository
|
|
.findByVeiSourceAndVeiSourceId(dto.getSource(), dto.getSourceId())
|
|
.orElseGet(VehicleInspection::new);
|
|
|
|
// ===== ID resolve =====
|
|
Long driverId = resolveDriverId(dto);
|
|
Long subDriverId = resolveSubDriverId(dto);
|
|
Long vehicleId = resolveVehicleId(dto);
|
|
|
|
log.info(
|
|
"[INSPECTION] start source={} sourceId={} type={} vehId={} driverId={}",
|
|
dto.getSource(),
|
|
dto.getSourceId(),
|
|
dto.getInspectionType(),
|
|
vehicleId,
|
|
driverId
|
|
);
|
|
|
|
// ===== Inspection =====
|
|
inspection.setVeiUuid(UUID.randomUUID());
|
|
inspection.setVeiVehId(vehicleId);
|
|
inspection.setVeiDriverId(driverId);
|
|
inspection.setVeiSubDriverId(subDriverId);
|
|
inspection.setVeiInspectionDate(dto.getInspectionDate());
|
|
inspection.setVeiInspectionType(dto.getInspectionType());
|
|
inspection.setVeiStartAt(dto.getStartAt());
|
|
inspection.setVeiEndAt(dto.getEndAt());
|
|
inspection.setVeiResult(dto.getResult());
|
|
inspection.setVeiOdometer(dto.getOdometer());
|
|
inspection.setVeiHasDefect(dto.getHasDefect());
|
|
inspection.setVeiSource(dto.getSource());
|
|
inspection.setVeiSourceId(dto.getSourceId());
|
|
|
|
inspection = inspectionRepository.save(inspection);
|
|
|
|
// ===== Defects =====
|
|
if (dto.getDefects() != null) {
|
|
saveDefects(inspection.getVeiId(), dto.getDefects());
|
|
}
|
|
|
|
// ===== Dispatch close evaluation (Rule 1~3) =====
|
|
// - postTrip이면 여기서 closeDispatch 호출됨
|
|
// - preTrip new driver/vehicle 조건도 여기서 기존 open close됨
|
|
vehicleDispatchService.evaluateDispatchCloseByInspection(inspection);
|
|
|
|
// ===== Dispatch create (preTrip) =====
|
|
// - 이미 open dispatch가 있으면 생성하지 않음
|
|
// - odometerStart = inspection.odometer (가능하면)
|
|
if (shouldCreateDispatch(inspection)) {
|
|
// open 여부
|
|
boolean hasOpenDispatch = dispatchRepository.findOpenDispatchByVehId(inspection.getVeiVehId()).isPresent();
|
|
|
|
if (!hasOpenDispatch) {
|
|
|
|
BigDecimal odometerStart =
|
|
(inspection.getVeiOdometer() != null)
|
|
? BigDecimal.valueOf(inspection.getVeiOdometer())
|
|
: null;
|
|
|
|
VehicleDispatch dispatch =
|
|
vehicleDispatchService.createFromPreTripInspection(
|
|
inspection.getVeiVehId(),
|
|
inspection.getVeiDriverId(),
|
|
inspection.getVeiSubDriverId(),
|
|
inspection.getVeiInspectionDate(),
|
|
inspection.getVeiStartAt(),
|
|
odometerStart,
|
|
"AUTO"
|
|
);
|
|
|
|
// inspection에 dispatch 연결
|
|
inspection.setVeiDispatchId(dispatch.getVedId());
|
|
inspection = inspectionRepository.save(inspection);
|
|
} else {
|
|
log.info(
|
|
"[DISPATCH][CREATE] skipped. open dispatch already exists. vehId={}",
|
|
inspection.getVeiVehId()
|
|
);
|
|
}
|
|
} else {
|
|
log.debug(
|
|
"[DISPATCH][CREATE] skip. reason=shouldCreateDispatch=false inspectionId={}",
|
|
inspection.getVeiId()
|
|
);
|
|
}
|
|
|
|
return inspection;
|
|
}
|
|
|
|
/**
|
|
* defects 저장 (externalDefectId 기준 upsert)
|
|
* - externalDefectId가 null이면 "수기"로 간주 → 그냥 매번 INSERT 하면 중복 가능성이 커짐
|
|
* => 수기는 (veiId + defectType + comment + createdAt) 조합으로 찾아
|
|
*/
|
|
private void saveDefects(Long veiId, List<VehicleInspectionDefectDto> defects) {
|
|
// 없으면
|
|
if (defects == null || defects.isEmpty()) return;
|
|
|
|
// defect 별
|
|
for (VehicleInspectionDefectDto d : defects) {
|
|
|
|
VehicleInspectionDefect defect;
|
|
|
|
// 외부 defect id가 있으면 그걸로 idempotent
|
|
if (d.getVidExternalDefectId() != null && !d.getVidExternalDefectId().isBlank()) {
|
|
defect = defectRepository
|
|
.findByVidVeiIdAndVidExternalDefectId(veiId, d.getVidExternalDefectId())
|
|
.orElseGet(VehicleInspectionDefect::new);
|
|
} else {
|
|
// 수기 defect: 최소한의 중복 방지 (원하면 repository 메서드로 더 정교하게)
|
|
defect = new VehicleInspectionDefect();
|
|
}
|
|
|
|
defect.setVidVeiId(veiId);
|
|
defect.setVidExternalDefectId(d.getVidExternalDefectId());
|
|
defect.setVidDefectType(d.getDefectType());
|
|
defect.setVidComment(d.getComment());
|
|
defect.setVidIsResolved(d.getIsResolved());
|
|
defect.setVidCreatedAt(d.getCreatedAt());
|
|
defect.setVidResolvedAt(d.getResolvedAt());
|
|
defect.setVidResolvedBy(resolveEmpId(d));
|
|
|
|
defectRepository.save(defect);
|
|
}
|
|
}
|
|
|
|
private Long resolveDriverId(VehicleInspectionDto dto) {
|
|
|
|
if (dto.getDriverId() != null) return dto.getDriverId();
|
|
|
|
if (dto.getExternalDriverId() != null) {
|
|
return hcmEmployeeClient.getEmpIdFromExternalId(
|
|
"SAMSARA",
|
|
dto.getExternalDriverId()
|
|
);
|
|
}
|
|
|
|
if (dto.getDriverUuid() != null) {
|
|
return hcmEmployeeClient.getEmpIdFromUuid(dto.getDriverUuid());
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private Long resolveSubDriverId(VehicleInspectionDto dto) {
|
|
|
|
if (dto.getSubDriverId() != null) return dto.getSubDriverId();
|
|
|
|
if (dto.getExternalSubDriverId() != null) {
|
|
return hcmEmployeeClient.getEmpIdFromExternalId(
|
|
"SAMSARA",
|
|
dto.getExternalSubDriverId()
|
|
);
|
|
}
|
|
|
|
if (dto.getSubDriverUuid() != null) {
|
|
return hcmEmployeeClient.getEmpIdFromUuid(dto.getSubDriverUuid());
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private Long resolveVehicleId(VehicleInspectionDto dto) {
|
|
|
|
if (dto.getVehId() != null) return dto.getVehId();
|
|
|
|
if (dto.getExternalVehicleId() != null) {
|
|
return vehicleExternalMapService
|
|
.findMapping("SAMSARA", dto.getExternalVehicleId())
|
|
.getVexVehicleId();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private Long resolveEmpId(VehicleInspectionDefectDto dto) {
|
|
|
|
if (dto.getResolvedBy() != null) return dto.getResolvedBy();
|
|
|
|
if (dto.getResolvedByExternalId() != null) {
|
|
return hcmEmployeeClient.getEmpIdFromExternalId(
|
|
"SAMSARA",
|
|
dto.getResolvedByExternalId()
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private boolean shouldCreateDispatch(VehicleInspection inspection) {
|
|
return "preTrip".equalsIgnoreCase(inspection.getVeiInspectionType())
|
|
&& inspection.getVeiDispatchId() == null
|
|
&& inspection.getVeiVehId() != null
|
|
&& inspection.getVeiDriverId() != null;
|
|
}
|
|
}
|