瀏覽代碼

fix(backend): 数据库备份优雅处理内存数据库和缺失文件

wangkangyjy 1 周之前
父節點
當前提交
6816ba4b25

+ 25 - 4
backend/src/main/java/com/yimeng/okr/service/DatabaseBackupService.java

@@ -28,9 +28,16 @@ public class DatabaseBackupService {
     }
 
     public Path createBackup(String label) {
-        Path dbPath = resolveSqlitePath(datasourceUrl);
-        if (!Files.exists(dbPath)) {
-            throw new BusinessException("数据库文件不存在,无法备份: " + dbPath);
+        Path dbPath;
+        try {
+            dbPath = resolveSqlitePath(datasourceUrl);
+        } catch (Exception e) {
+            // In-memory or non-file databases — skip backup
+            return null;
+        }
+        if (!Files.exists(dbPath) || !Files.isRegularFile(dbPath)) {
+            // In-memory or non-existent — skip backup silently
+            return null;
         }
         try {
             Files.createDirectories(Path.of(backupDir));
@@ -48,6 +55,20 @@ public class DatabaseBackupService {
         if (url == null || !url.startsWith("jdbc:sqlite:")) {
             throw new BusinessException("仅支持 jdbc:sqlite 数据库备份");
         }
-        return Path.of(url.substring("jdbc:sqlite:".length())).normalize();
+        String path = url.substring("jdbc:sqlite:".length());
+        // Strip query parameters
+        int queryIdx = path.indexOf('?');
+        if (queryIdx >= 0) {
+            path = path.substring(0, queryIdx);
+        }
+        // Handle file: prefix for shared cache mode
+        if (path.startsWith("file:")) {
+            path = path.substring(5);
+        }
+        // :memory: is not a file
+        if (path.contains(":memory:")) {
+            throw new BusinessException("内存数据库,无需备份");
+        }
+        return Path.of(path).normalize();
     }
 }

+ 11 - 3
backend/src/test/java/com/yimeng/okr/service/DatabaseBackupServiceTest.java

@@ -26,13 +26,21 @@ class DatabaseBackupServiceTest {
     }
 
     @Test
-    void createBackupRejectsMissingDatabaseFile() throws Exception {
+    void createBackupReturnsNullForMissingDatabaseFile() throws Exception {
         Path temp = Files.createTempDirectory("okr-backup-test");
         DatabaseBackupService service = new DatabaseBackupService(
                 "jdbc:sqlite:" + temp.resolve("missing.db"), temp.resolve("backups").toString());
 
-        BusinessException ex = assertThrows(BusinessException.class, service::createBackup);
+        Path backup = service.createBackup();
+        assertNull(backup, "Should return null for missing file instead of throwing");
+    }
+
+    @Test
+    void createBackupReturnsNullForInMemoryDatabase() {
+        DatabaseBackupService service = new DatabaseBackupService(
+                "jdbc:sqlite:file:test?mode=memory&cache=shared", "./data/backups");
 
-        assertTrue(ex.getMessage().contains("数据库文件不存在"));
+        Path backup = service.createBackup();
+        assertNull(backup, "Should return null for in-memory database");
     }
 }