| | |
| | | package com.sinata.appointment.service; |
| | | |
| | | import com.sinata.appointment.constants.IntegrationConstant; |
| | | import com.sinata.appointment.dto.ExportTaskStatusVo; |
| | | import java.io.IOException; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeParseException; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | import java.util.UUID; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.amqp.rabbit.core.RabbitTemplate; |
| | |
| | | @RequiredArgsConstructor |
| | | public class ExportApplicationService { |
| | | |
| | | private static final String STATUS_QUEUED = "QUEUED"; |
| | | private static final String STATUS_RUNNING = "RUNNING"; |
| | | private static final String STATUS_COMPLETED = "COMPLETED"; |
| | | private static final String STATUS_FAILED = "FAILED"; |
| | | private static final String APPOINTMENT_EXPORT_TYPE = "appointment"; |
| | | |
| | | private final ObjectProvider<RabbitTemplate> rabbitTemplateProvider; |
| | | private final AppointmentApplicationService appointmentApplicationService; |
| | | private final LocalExportAsyncService localExportAsyncService; |
| | | |
| | | private final ConcurrentHashMap<String, ExportTaskSnapshot> taskStore = new ConcurrentHashMap<>(); |
| | | |
| | | public String enqueueExport(String exportType, Map<String, Object> criteria) { |
| | | String normalizedType = normalizeType(exportType); |
| | | String taskId = UUID.randomUUID().toString().replace("-", ""); |
| | | Map<String, Object> safeCriteria = criteria == null ? Map.of() : new HashMap<>(criteria); |
| | | RabbitTemplate rabbitTemplate = rabbitTemplateProvider.getIfAvailable(); |
| | | if (rabbitTemplate == null) { |
| | | log.warn( |
| | | "RabbitMQ not configured, export task accepted locally only taskId={} type={}", |
| | | taskId, |
| | | exportType); |
| | | taskStore.put(taskId, ExportTaskSnapshot.queued(taskId, normalizedType)); |
| | | localExportAsyncService.process(taskId, normalizedType, safeCriteria); |
| | | return taskId; |
| | | } |
| | | log.info("Enqueue export taskId={} type={}", taskId, exportType); |
| | | rabbitTemplate.convertAndSend( |
| | | IntegrationConstant.EXPORT_TASK_QUEUE, |
| | | Map.of("taskId", taskId, "type", exportType, "criteria", criteria)); |
| | | taskStore.put(taskId, ExportTaskSnapshot.queued(taskId, normalizedType)); |
| | | log.info("Enqueue export taskId={} type={}", taskId, normalizedType); |
| | | Map<String, Object> payload = new HashMap<>(); |
| | | payload.put("taskId", taskId); |
| | | payload.put("type", normalizedType); |
| | | payload.put("criteria", safeCriteria); |
| | | rabbitTemplate.convertAndSend(IntegrationConstant.EXPORT_TASK_QUEUE, payload); |
| | | return taskId; |
| | | } |
| | | |
| | | public ExportTaskStatusVo getTaskStatus(String taskId) { |
| | | ExportTaskSnapshot snapshot = taskStore.get(taskId); |
| | | if (snapshot == null) { |
| | | return null; |
| | | } |
| | | ExportTaskStatusVo vo = new ExportTaskStatusVo(); |
| | | vo.setTaskId(snapshot.taskId()); |
| | | vo.setType(snapshot.type()); |
| | | vo.setStatus(snapshot.status()); |
| | | vo.setFileName(snapshot.fileName()); |
| | | vo.setErrorMessage(snapshot.errorMessage()); |
| | | return vo; |
| | | } |
| | | |
| | | public Path getExportFilePath(String taskId) { |
| | | ExportTaskSnapshot snapshot = taskStore.get(taskId); |
| | | if (snapshot == null || snapshot.filePath() == null || !Files.exists(snapshot.filePath())) { |
| | | return null; |
| | | } |
| | | return snapshot.filePath(); |
| | | } |
| | | |
| | | void processLocalExport(String taskId, String exportType, Map<String, Object> criteria) { |
| | | taskStore.computeIfPresent(taskId, (_k, v) -> v.toRunning()); |
| | | try { |
| | | if (!APPOINTMENT_EXPORT_TYPE.equals(normalizeType(exportType))) { |
| | | throw new IllegalArgumentException("unsupported export type: " + exportType); |
| | | } |
| | | byte[] bytes = |
| | | appointmentApplicationService.exportExcel( |
| | | asString(criteria.get("visitedUserName")), |
| | | asLong(criteria.get("visitedDeptId")), |
| | | asString(criteria.get("accessArea")), |
| | | asString(criteria.get("localUnit")), |
| | | asLocalDate(criteria.get("visitDateStart")), |
| | | asLocalDate(criteria.get("visitDateEnd")), |
| | | asString(criteria.get("displayStatus")), |
| | | asString(criteria.get("approvalStatus")), |
| | | asString(criteria.get("visitorKeyword")), |
| | | asString(criteria.get("visitReasonCode"))); |
| | | Path exportDir = Paths.get(System.getProperty("java.io.tmpdir"), "reservation-mini-program-exports"); |
| | | Files.createDirectories(exportDir); |
| | | Path filePath = exportDir.resolve(taskId + ".xlsx"); |
| | | Files.write(filePath, bytes); |
| | | taskStore.put( |
| | | taskId, |
| | | new ExportTaskSnapshot(taskId, normalizeType(exportType), STATUS_COMPLETED, filePath, filePath.getFileName().toString(), null)); |
| | | } catch (Exception ex) { |
| | | log.error("Local export failed taskId={} type={}", taskId, exportType, ex); |
| | | taskStore.put( |
| | | taskId, |
| | | new ExportTaskSnapshot(taskId, normalizeType(exportType), STATUS_FAILED, null, null, ex.getMessage())); |
| | | } |
| | | } |
| | | |
| | | private String normalizeType(String exportType) { |
| | | return exportType == null || exportType.isBlank() ? APPOINTMENT_EXPORT_TYPE : exportType.trim(); |
| | | } |
| | | |
| | | private String asString(Object value) { |
| | | return value == null ? null : value.toString().trim(); |
| | | } |
| | | |
| | | private Long asLong(Object value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | return Long.parseLong(value.toString().trim()); |
| | | } catch (NumberFormatException ex) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private LocalDate asLocalDate(Object value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | return LocalDate.parse(value.toString().trim()); |
| | | } catch (DateTimeParseException ex) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private record ExportTaskSnapshot( |
| | | String taskId, |
| | | String type, |
| | | String status, |
| | | Path filePath, |
| | | String fileName, |
| | | String errorMessage) { |
| | | |
| | | private static ExportTaskSnapshot queued(String taskId, String type) { |
| | | return new ExportTaskSnapshot(taskId, type, STATUS_QUEUED, null, null, null); |
| | | } |
| | | |
| | | private ExportTaskSnapshot toRunning() { |
| | | return new ExportTaskSnapshot(taskId, type, STATUS_RUNNING, filePath, fileName, errorMessage); |
| | | } |
| | | } |
| | | } |