diff --git a/src/main/java/com/example/workutils/WorkUtilsApplication.java b/src/main/java/com/example/workutils/WorkUtilsApplication.java index 0d6f4f7..368c533 100644 --- a/src/main/java/com/example/workutils/WorkUtilsApplication.java +++ b/src/main/java/com/example/workutils/WorkUtilsApplication.java @@ -67,8 +67,7 @@ public class WorkUtilsApplication implements CommandLineRunner { "damaged_repos.cache", "mybatis-generator.properties", "workutils.config", "plugins.config" }; - java.nio.file.Path dataDir = java.nio.file.Paths.get( - System.getProperty("user.dir"), "data"); + java.nio.file.Path dataDir = properties.getWorkutilsConfigFilePath().getParent(); StringBuilder sb = new StringBuilder("data 目录文件状态:\n"); for (String f : files) { java.nio.file.Path p = dataDir.resolve(f); diff --git a/src/main/java/com/example/workutils/biz/gitreview/GitReviewService.java b/src/main/java/com/example/workutils/biz/gitreview/GitReviewService.java index 4dc87ad..663d7f2 100644 --- a/src/main/java/com/example/workutils/biz/gitreview/GitReviewService.java +++ b/src/main/java/com/example/workutils/biz/gitreview/GitReviewService.java @@ -1,6 +1,7 @@ package com.example.workutils.biz.gitreview; import com.example.workutils.system.config.WorkUtilsProperties; +import com.example.workutils.system.support.ApplicationPaths; import com.example.workutils.system.support.FileOperationHelper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -265,7 +266,7 @@ public class GitReviewService { if (parent != null) { return parent; } - return Paths.get(System.getProperty("user.dir")).resolve("data"); + return ApplicationPaths.current().resolve("data"); } private Path historyFile() { diff --git a/src/main/java/com/example/workutils/system/config/WorkUtilsProperties.java b/src/main/java/com/example/workutils/system/config/WorkUtilsProperties.java index 5fadb56..37c7361 100644 --- a/src/main/java/com/example/workutils/system/config/WorkUtilsProperties.java +++ b/src/main/java/com/example/workutils/system/config/WorkUtilsProperties.java @@ -1,5 +1,6 @@ package com.example.workutils.system.config; +import com.example.workutils.system.support.ApplicationPaths; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -42,6 +43,16 @@ import java.util.jar.JarFile; @Data public class WorkUtilsProperties { + private final ApplicationPaths applicationPaths; + + public WorkUtilsProperties() { + this(ApplicationPaths.current()); + } + + WorkUtilsProperties(ApplicationPaths applicationPaths) { + this.applicationPaths = applicationPaths; + } + /** 基础目录 */ private String baseDirectory; @@ -105,17 +116,17 @@ public class WorkUtilsProperties { void snapshotDefaults() { // 基础目录:未配置则默认取运行目录(user.dir) if (baseDirectory == null || baseDirectory.trim().isEmpty()) { - baseDirectory = System.getProperty("user.dir"); + baseDirectory = applicationPaths.home().toString(); log.info("基础目录未配置,默认取运行目录: {}", baseDirectory); } // 导出目录:未配置则默认 {user.dir}/export if (exportDirectory == null || exportDirectory.trim().isEmpty()) { - exportDirectory = System.getProperty("user.dir") + "/export"; + exportDirectory = applicationPaths.resolve("export").toString(); log.info("导出目录未配置,默认: {}", exportDirectory); ensureExportDirExists(); } else if (!Paths.get(exportDirectory).isAbsolute()) { // 相对路径 → 解析到 user.dir 下 - exportDirectory = Paths.get(System.getProperty("user.dir"), exportDirectory).toString(); + exportDirectory = applicationPaths.resolveConfigured(exportDirectory).toString(); ensureExportDirExists(); } else { ensureExportDirExists(); @@ -234,11 +245,7 @@ public class WorkUtilsProperties { * 文件不存在时自动创建空文件。 */ public Path getProjectPathsFilePath() { - Path p = Paths.get(projectPathsFile != null ? projectPathsFile : "data/project_paths.txt"); - if (p.isAbsolute()) { - return p; - } - return Paths.get(System.getProperty("user.dir")).resolve(p); + return applicationPaths.resolveConfigured(projectPathsFile, "data", "project_paths.txt"); } /** @@ -249,66 +256,42 @@ public class WorkUtilsProperties { * 否则解析到 user.dir 下。 */ public Path getDamagedReposFilePath() { - Path p = Paths.get(damagedReposFile != null ? damagedReposFile : "data/damaged_repos.cache"); - if (p.isAbsolute()) { - return p; - } - return Paths.get(System.getProperty("user.dir")).resolve(p); + return applicationPaths.resolveConfigured(damagedReposFile, "data", "damaged_repos.cache"); } /** * 数据源配置文件的实际路径 */ public Path getDatasourcesFilePath() { - Path p = Paths.get(datasourcesFile != null ? datasourcesFile : "data/datasources.json"); - if (p.isAbsolute()) { - return p; - } - return Paths.get(System.getProperty("user.dir")).resolve(p); + return applicationPaths.resolveConfigured(datasourcesFile, "data", "datasources.json"); } /** * 外部配置文件(workutils.config)的实际路径 */ public Path getWorkutilsConfigFilePath() { - Path p = Paths.get(workutilsConfigFile != null ? workutilsConfigFile : "data/workutils.config"); - if (p.isAbsolute()) { - return p; - } - return Paths.get(System.getProperty("user.dir")).resolve(p); + return applicationPaths.resolveConfigured(workutilsConfigFile, "data", "workutils.config"); } /** * MyBatis 生成器配置文件的实际路径 */ public Path getMybatisGeneratorFilePath() { - Path p = Paths.get(mybatisGeneratorFile != null ? mybatisGeneratorFile : "data/mybatis-generator.properties"); - if (p.isAbsolute()) { - return p; - } - return Paths.get(System.getProperty("user.dir")).resolve(p); + return applicationPaths.resolveConfigured(mybatisGeneratorFile, "data", "mybatis-generator.properties"); } /** * 插件状态配置文件的实际路径 */ public Path getPluginsConfigFilePath() { - Path p = Paths.get(pluginsConfigFile != null ? pluginsConfigFile : "data/plugins.config"); - if (p.isAbsolute()) { - return p; - } - return Paths.get(System.getProperty("user.dir")).resolve(p); + return applicationPaths.resolveConfigured(pluginsConfigFile, "data", "plugins.config"); } /** * API 基类配置文件的实际路径 */ public Path getApiBaseClassesFilePath() { - Path p = Paths.get(apiBaseClassesFile != null ? apiBaseClassesFile : "data/api_base_classes.json"); - if (p.isAbsolute()) { - return p; - } - return Paths.get(System.getProperty("user.dir")).resolve(p); + return applicationPaths.resolveConfigured(apiBaseClassesFile, "data", "api_base_classes.json"); } /** diff --git a/src/main/java/com/example/workutils/system/support/HistoryPersistence.java b/src/main/java/com/example/workutils/system/support/HistoryPersistence.java index 89664d6..d9232a2 100644 --- a/src/main/java/com/example/workutils/system/support/HistoryPersistence.java +++ b/src/main/java/com/example/workutils/system/support/HistoryPersistence.java @@ -22,7 +22,11 @@ public class HistoryPersistence { * @param name 文件名(不含路径),如 "scanner_history.txt" */ public HistoryPersistence(String name) { - Path dataDir = Paths.get(System.getProperty("user.dir"), "data"); + this(name, ApplicationPaths.current()); + } + + public HistoryPersistence(String name, ApplicationPaths applicationPaths) { + Path dataDir = applicationPaths.resolve("data"); try { Files.createDirectories(dataDir); } catch (IOException ignored) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index fa8ebc8..c8697ca 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,11 +3,11 @@ spring: name: workutils workutils: - # base dir, empty = defaults to user.dir + # base dir, empty = defaults to the WorkUtils application home base-directory: "" - # export dir, empty = defaults to {user.dir}/export (auto-created on startup) + # export dir, empty = defaults to {application-home}/export (auto-created on startup) export-directory: "" - # data file paths (relative -> under user.dir/data/, absolute -> as-is) + # data file paths (relative -> under application-home/data/, absolute -> as-is) project-paths-file: "data/project_paths.txt" damaged-repos-file: "data/damaged_repos.cache" datasources-file: "data/datasources.json" @@ -22,4 +22,4 @@ logging: com.example.workutils: INFO org.springframework: WARN pattern: - console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" \ No newline at end of file + console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" diff --git a/src/main/resources/templates/workutils.config b/src/main/resources/templates/workutils.config index c3313cb..ffe4ef9 100644 --- a/src/main/resources/templates/workutils.config +++ b/src/main/resources/templates/workutils.config @@ -5,7 +5,7 @@ base-directory= export-directory= -# ===== 数据文件路径(相对于 user.dir)===== +# ===== 数据文件路径(相对于 WorkUtils 应用根目录)===== project-paths-file=data/project_paths.txt damaged-repos-file=data/damaged_repos.cache diff --git a/src/test/java/com/example/workutils/system/config/WorkUtilsPropertiesPathTest.java b/src/test/java/com/example/workutils/system/config/WorkUtilsPropertiesPathTest.java new file mode 100644 index 0000000..178c3c3 --- /dev/null +++ b/src/test/java/com/example/workutils/system/config/WorkUtilsPropertiesPathTest.java @@ -0,0 +1,54 @@ +package com.example.workutils.system.config; + +import com.example.workutils.system.support.ApplicationPaths; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class WorkUtilsPropertiesPathTest { + + @TempDir + Path tempDirectory; + + @Test + void relativeRuntimeFilesUseApplicationHome() { + WorkUtilsProperties properties = new WorkUtilsProperties(ApplicationPaths.of(tempDirectory)); + + assertEquals(tempDirectory.resolve(Path.of("data", "project_paths.txt")), + properties.getProjectPathsFilePath()); + assertEquals(tempDirectory.resolve(Path.of("data", "damaged_repos.cache")), + properties.getDamagedReposFilePath()); + assertEquals(tempDirectory.resolve(Path.of("data", "datasources.json")), + properties.getDatasourcesFilePath()); + assertEquals(tempDirectory.resolve(Path.of("data", "workutils.config")), + properties.getWorkutilsConfigFilePath()); + assertEquals(tempDirectory.resolve(Path.of("data", "mybatis-generator.properties")), + properties.getMybatisGeneratorFilePath()); + assertEquals(tempDirectory.resolve(Path.of("data", "plugins.config")), + properties.getPluginsConfigFilePath()); + assertEquals(tempDirectory.resolve(Path.of("data", "api_base_classes.json")), + properties.getApiBaseClassesFilePath()); + } + + @Test + void absoluteConfiguredFileIsPreserved() { + WorkUtilsProperties properties = new WorkUtilsProperties(ApplicationPaths.of(tempDirectory)); + Path absolute = tempDirectory.resolve("外部 配置.json").toAbsolutePath().normalize(); + properties.setDatasourcesFile(absolute.toString()); + + assertEquals(absolute, properties.getDatasourcesFilePath()); + } + + @Test + void snapshotDefaultsUsesApplicationHomeForBaseAndExportDirectories() { + WorkUtilsProperties properties = new WorkUtilsProperties(ApplicationPaths.of(tempDirectory)); + + properties.snapshotDefaults(); + + assertEquals(tempDirectory.toString(), properties.getBaseDirectory()); + assertEquals(tempDirectory.resolve("export"), properties.getExportBasePath()); + } +} diff --git a/src/test/java/com/example/workutils/system/support/HistoryPersistencePathTest.java b/src/test/java/com/example/workutils/system/support/HistoryPersistencePathTest.java new file mode 100644 index 0000000..cdfc350 --- /dev/null +++ b/src/test/java/com/example/workutils/system/support/HistoryPersistencePathTest.java @@ -0,0 +1,27 @@ +package com.example.workutils.system.support; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Path; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class HistoryPersistencePathTest { + + @TempDir + Path tempDirectory; + + @Test + void historyIsSavedUnderApplicationHomeWithUnicodeAndSpaces() { + HistoryPersistence persistence = new HistoryPersistence( + "扫描 历史.txt", ApplicationPaths.of(tempDirectory)); + + persistence.save(List.of("一条历史")); + + assertEquals(tempDirectory.resolve(Path.of("data", "扫描 历史.txt")).toString(), + persistence.getFilePath()); + assertEquals(List.of("一条历史"), persistence.load()); + } +}