longz
2026-04-21 c4db0a4526f61758272c92396aa788d84f098102
backend/src/main/java/com/sinata/appointment/service/ExportApplicationService.java
@@ -1,8 +1,17 @@
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;
@@ -14,22 +23,137 @@
@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);
        }
    }
}