diff --git a/src/main/java/com/cisd/tms/modules/backup/service/impl/ResourceTaskQueryServiceImpl.java b/src/main/java/com/cisd/tms/modules/backup/service/impl/ResourceTaskQueryServiceImpl.java index d7c148f..9861820 100644 --- a/src/main/java/com/cisd/tms/modules/backup/service/impl/ResourceTaskQueryServiceImpl.java +++ b/src/main/java/com/cisd/tms/modules/backup/service/impl/ResourceTaskQueryServiceImpl.java @@ -10,6 +10,8 @@ import com.cisd.tms.modules.backup.service.ResourceTaskQueryService; import java.nio.file.Path; import java.time.DateTimeException; import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; import java.util.Comparator; import java.util.Optional; @@ -41,6 +43,7 @@ public class ResourceTaskQueryServiceImpl implements ResourceTaskQueryService { RestoreStatusSnapshot status = restoreStatusFileReader.read(resolveTaskDir(active.getRunnerStatePath())); String latestStatus = defaultIfBlank(status.status(), active.getStatus()); repairMissingTask(active, status, latestStatus); + syncExistingTask(active.getTaskId(), status, latestStatus); ResourceRestoreLatestResponse response = new ResourceRestoreLatestResponse(); response.setTaskId(active.getTaskId()); response.setBackupId(active.getBackupId()); @@ -65,6 +68,7 @@ public class ResourceTaskQueryServiceImpl implements ResourceTaskQueryService { } RestoreStatusSnapshot status = restoreStatusFileReader.read(resolveTaskDir(entity)); String latestStatus = defaultIfBlank(status.status(), entity.getStatus()); + syncExistingTask(entity, status, latestStatus); response.setTaskId(entity.getTaskId()); response.setBackupId(entity.getBackupId()); response.setStatus(latestStatus); @@ -109,6 +113,25 @@ public class ResourceTaskQueryServiceImpl implements ResourceTaskQueryService { resourceRestoreTaskRepository.save(entity); } + private void syncExistingTask(String taskId, RestoreStatusSnapshot status, String latestStatus) { + if (trim(taskId).isEmpty()) { + return; + } + resourceRestoreTaskRepository.findByTaskId(taskId) + .ifPresent(entity -> syncExistingTask(entity, status, latestStatus)); + } + + private void syncExistingTask(ResourceRestoreTaskEntity entity, RestoreStatusSnapshot status, String latestStatus) { + if (entity == null || !isTerminal(latestStatus) || latestStatus.equalsIgnoreCase(trim(entity.getStatus()))) { + return; + } + entity.setStatus(latestStatus); + entity.setErrorMessage(status.message()); + entity.setFinishTime(parseLocalDateTime(status.updatedAt(), entity.getFinishTime())); + entity.setUpdateTime(LocalDateTime.now()); + resourceRestoreTaskRepository.update(entity); + } + private Path resolveStatePath(String statusFilePath) { return trim(statusFilePath).isEmpty() ? null : Path.of(statusFilePath).normalize(); } @@ -157,7 +180,11 @@ public class ResourceTaskQueryServiceImpl implements ResourceTaskQueryService { try { return LocalDateTime.parse(text); } catch (DateTimeException ex) { - return fallback; + try { + return OffsetDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ")).toLocalDateTime(); + } catch (DateTimeException ignored) { + return fallback; + } } } diff --git a/src/test/java/com/cisd/tms/modules/backup/service/ResourceTaskQueryServiceTest.java b/src/test/java/com/cisd/tms/modules/backup/service/ResourceTaskQueryServiceTest.java index 368ac18..db82df3 100644 --- a/src/test/java/com/cisd/tms/modules/backup/service/ResourceTaskQueryServiceTest.java +++ b/src/test/java/com/cisd/tms/modules/backup/service/ResourceTaskQueryServiceTest.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -61,6 +62,41 @@ class ResourceTaskQueryServiceTest { )); } + @Test + void shouldSyncExistingRestoreTaskWhenStatusFileIsTerminal() throws Exception { + Path taskDir = tempDir.resolve("RRST-TASK-20260429-000001"); + Files.createDirectories(taskDir); + Files.writeString(taskDir.resolve("status.json"), """ + { + "status":"SUCCESS", + "phase":"DONE", + "message":"restore applied", + "updatedAt":"2026-05-22T16:16:01+0800" + } + """); + ResourceRestoreTaskEntity entity = restoreTask(taskDir); + ResourceRestoreTaskRepository repository = Mockito.mock(ResourceRestoreTaskRepository.class); + Mockito.when(repository.findAll()).thenReturn(List.of(entity)); + ResourceTaskQueryService service = new ResourceTaskQueryServiceImpl( + repository, + new RestoreStatusFileReader(new ObjectMapper()), + new ActiveRestoreStateStore(resourceBackupProperties(), new ObjectMapper()) + ); + + ResourceRestoreLatestResponse latest = service.getLatestRestore(); + + Assertions.assertEquals("SUCCESS", latest.getStatus()); + Assertions.assertEquals("DONE", latest.getPhase()); + Assertions.assertEquals("restore applied", latest.getMessage()); + Assertions.assertEquals("2026-05-22T16:16:01+0800", latest.getFinishedAt()); + Mockito.verify(repository).update(Mockito.argThat(updated -> + "RRST-TASK-20260429-000001".equals(updated.getTaskId()) + && "SUCCESS".equals(updated.getStatus()) + && "restore applied".equals(updated.getErrorMessage()) + && updated.getFinishTime() != null + )); + } + private static ResourceRestoreTaskEntity restoreTask(Path taskDir) { ResourceRestoreTaskEntity entity = new ResourceRestoreTaskEntity(); entity.setTaskId("RRST-TASK-20260429-000001");