Use application home for runtime data paths

This commit is contained in:
张家训
2026-08-03 14:58:21 +08:00
parent 931b964226
commit de565541e1
8 changed files with 115 additions and 47 deletions
@@ -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);
@@ -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() {
@@ -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");
}
/**
@@ -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) {
+3 -3
View File
@@ -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"
@@ -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
@@ -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());
}
}
@@ -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());
}
}