package com.svnlog.web.service; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.springframework.stereotype.Service; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import com.svnlog.web.model.TaskInfo; import com.svnlog.web.model.TaskStatus; @Service public class TaskPersistenceService { private final Gson gson = new GsonBuilder().setPrettyPrinting().create(); public List load(Path storePath) throws IOException { if (storePath == null || !Files.exists(storePath) || !Files.isRegularFile(storePath)) { return new ArrayList(); } try (Reader reader = Files.newBufferedReader(storePath, StandardCharsets.UTF_8)) { List persisted = gson.fromJson( reader, new TypeToken>() { }.getType() ); if (persisted == null) { return new ArrayList(); } List result = new ArrayList(); for (PersistedTaskInfo item : persisted) { result.add(toTaskInfo(item)); } return result; } } public void save(Path storePath, Collection tasks) throws IOException { if (storePath == null) { return; } if (storePath.getParent() != null) { Files.createDirectories(storePath.getParent()); } List persisted = new ArrayList(); if (tasks != null) { for (TaskInfo task : tasks) { persisted.add(fromTaskInfo(task)); } } try (Writer writer = Files.newBufferedWriter(storePath, StandardCharsets.UTF_8)) { gson.toJson(persisted, writer); } } private PersistedTaskInfo fromTaskInfo(TaskInfo task) { PersistedTaskInfo info = new PersistedTaskInfo(); info.taskId = task.getTaskId(); info.type = task.getType(); info.status = task.getStatus() == null ? null : task.getStatus().name(); info.progress = task.getProgress(); info.message = task.getMessage(); info.error = task.getError(); info.aiReasoningText = task.getAiReasoningText(); info.aiAnswerText = task.getAiAnswerText(); info.aiStreamStatus = task.getAiStreamStatus(); info.createdAt = toString(task.getCreatedAt()); info.updatedAt = toString(task.getUpdatedAt()); info.files = new ArrayList(task.getFiles()); return info; } private TaskInfo toTaskInfo(PersistedTaskInfo persisted) { TaskInfo task = new TaskInfo(); task.setTaskId(persisted.taskId); task.setType(persisted.type); task.setStatus(parseStatus(persisted.status)); task.setProgress(persisted.progress); task.setMessage(persisted.message); task.setError(persisted.error); task.setAiReasoningText(persisted.aiReasoningText); task.setAiAnswerText(persisted.aiAnswerText); task.setAiStreamStatus(persisted.aiStreamStatus); task.setCreatedAt(parseInstant(persisted.createdAt)); task.setUpdatedAt(parseInstant(persisted.updatedAt)); if (persisted.files != null) { task.getFiles().addAll(persisted.files); } return task; } private Instant parseInstant(String value) { if (value == null || value.trim().isEmpty()) { return Instant.now(); } try { return Instant.parse(value); } catch (Exception e) { return Instant.now(); } } private String toString(Instant value) { return value == null ? Instant.now().toString() : value.toString(); } private TaskStatus parseStatus(String value) { if (value == null || value.trim().isEmpty()) { return TaskStatus.FAILED; } try { return TaskStatus.valueOf(value); } catch (Exception e) { return TaskStatus.FAILED; } } private static class PersistedTaskInfo { private String taskId; private String type; private String status; private int progress; private String message; private String error; private String aiReasoningText; private String aiAnswerText; private String aiStreamStatus; private String createdAt; private String updatedAt; private List files; } }