优化仓库路径配置

This commit is contained in:
张家训
2026-06-17 16:08:56 +08:00
parent 711193a503
commit 0e95bf30a0
13 changed files with 296 additions and 129 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
</parent>
<groupId>com.example</groupId>
<artifactId>workutils</artifactId>
<version>0.0.2-SNAPSHOT</version>
<version>1.0.1-SNAPSHOT</version>
<name>workutils</name>
<description>workutils</description>
<properties>
@@ -11,6 +11,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;
@@ -62,28 +64,38 @@ public class MyBatisGeneratorPlugin implements Plugin {
return "MyBatis 外部配置中数据库 URL 未设置。\n请通过「系统设置 > MyBatis 数据库」标签页补充。";
}
// 2. 回退检查内置 classpath 配置
// 2. 回退检查 data/ 外部配置 → classpath 内置配置
Properties props = new Properties();
// 2a. 先尝试 classpath 内置值(作为基线)
try (InputStream is = getClass().getClassLoader()
.getResourceAsStream("mybatis-generator.properties")) {
if (is == null) {
return "MyBatis 生成器配置文件不存在: mybatis-generator.properties\n"
+ "请在 src/main/resources/ 下创建此文件,完成后重新打包。\n"
+ "或通过「系统设置 > MyBatis 数据库」标签页配置数据库连接。";
}
Properties props = new Properties();
if (is != null) {
props.load(is);
}
} catch (Exception e) {
log.warn("无法读取 classpath 内置配置", e);
}
// 2b. 用 data/ 外部配置覆盖
Path dataFile = properties.getMybatisGeneratorFilePath();
if (Files.exists(dataFile)) {
try (InputStream is = new java.io.FileInputStream(dataFile.toFile())) {
Properties externalProps = new Properties();
externalProps.load(is);
props.putAll(externalProps);
} catch (Exception e) {
log.warn("无法读取 data/ MyBatis 配置", e);
}
}
String dbUrl = props.getProperty("db_url", "");
String dbUser = props.getProperty("db_username", "");
if (dbUrl.isEmpty() || dbUser.isEmpty()) {
return "MyBatis 生成器数据库配置不完整:\n"
+ " db_url = " + (dbUrl.isEmpty() ? "(未设置)" : dbUrl) + "\n"
+ " db_username = " + (dbUser.isEmpty() ? "(未设置)" : dbUser) + "\n"
+ "请编辑 src/main/resources/mybatis-generator.properties 后重新打包,\n"
+ "请在 data/mybatis-generator.properties 中编辑数据库连接信息,\n"
+ "或通过「系统设置 > MyBatis 数据库」标签页配置。";
}
} catch (Exception e) {
return "无法读取 MyBatis 生成器配置文件:\n错误: " + e.getMessage();
}
return null;
}
@@ -448,9 +448,19 @@ public class MyBatisGeneratorWindow {
private void loadDefaultDirs() {
Properties p = new Properties();
// 1. classpath 内置默认值
try (InputStream is = getClass().getClassLoader().getResourceAsStream("mybatis-generator.properties")) {
if (is != null) p.load(is);
} catch (Exception ignored) {}
// 2. data/ 外部配置覆盖
Path dataFile = properties.getMybatisGeneratorFilePath();
if (java.nio.file.Files.exists(dataFile)) {
try (InputStream is = new java.io.FileInputStream(dataFile.toFile())) {
Properties externalProps = new Properties();
externalProps.load(is);
p.putAll(externalProps);
} catch (Exception ignored) {}
}
setDirField(modelDirField, p.getProperty("model_target_project", "./generator"));
setDirField(xmlDirField, p.getProperty("sqlmapper_target_project", "./generator"));
@@ -2,6 +2,7 @@ package com.example.workutils.biz.mybatisgenerator.service.generator;
import com.example.workutils.system.config.DatasourceConfig;
import com.example.workutils.system.config.DatasourceConfigManager;
import com.example.workutils.system.config.WorkUtilsProperties;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
@@ -11,6 +12,8 @@ import org.springframework.stereotype.Service;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
@@ -25,14 +28,16 @@ import java.util.Properties;
public class MyBatisGeneratorService {
private static final String CONFIG_FILE = "mybatis-generator.xml";
private static final String PROPERTIES_FILE = "mybatis-generator.properties";
private static final String CLASSPATH_PROPERTIES = "mybatis-generator.properties";
private final DatasourceConfigManager datasourceMgr;
/** 当前选中的数据源名称(null = 使用外部配置或内置默认配置) */
private final WorkUtilsProperties workUtilsProperties;
/** 当前选中的数据源名称(null = 使用 data/ 外部配置或内置默认配置) */
private String currentDatasourceName;
public MyBatisGeneratorService(DatasourceConfigManager datasourceMgr) {
public MyBatisGeneratorService(DatasourceConfigManager datasourceMgr, WorkUtilsProperties workUtilsProperties) {
this.datasourceMgr = datasourceMgr;
this.workUtilsProperties = workUtilsProperties;
}
// ==================== 数据源切换 ====================
@@ -142,8 +147,8 @@ public class MyBatisGeneratorService {
if (ds != null) {
return new DbConnection(ds.getUrl(), ds.getUsername(), ds.getPassword());
}
// 回退 classpath 配置
Properties props = getClasspathProperties();
// 回退: data/ 外部配置 → classpath 内置配置
Properties props = loadMybatisProperties();
return new DbConnection(
props.getProperty("db_url", ""),
props.getProperty("db_username", ""),
@@ -250,6 +255,15 @@ public class MyBatisGeneratorService {
content = sb.toString();
}
// 将 classpath 资源引用替换为 data/ 外部文件的 file:// URL
Path propsFile = workUtilsProperties.getMybatisGeneratorFilePath();
if (Files.exists(propsFile)) {
String fileUrl = propsFile.toUri().toString();
content = content.replace(
"<properties resource=\"" + CLASSPATH_PROPERTIES + "\" />",
"<properties url=\"" + fileUrl + "\" />");
}
// 截掉 </generatorConfiguration> 之后的内容(防止尾随垃圾导致 XML 解析失败)
int endIdx = content.lastIndexOf("</generatorConfiguration>");
if (endIdx >= 0) {
@@ -390,10 +404,10 @@ public class MyBatisGeneratorService {
}
/**
* 获取配置属性(优先使用选中的数据源,回退到classpath)
* 获取配置属性(优先: 选中数据源 > data/ 外部配置 > classpath 内置配置)
*/
public Properties getConfigurationProperties() {
Properties props = getClasspathProperties();
Properties props = loadMybatisProperties();
// 如果选中了数据源,覆盖连接信息
DatasourceConfig ds = getCurrentDatasource();
if (ds != null) {
@@ -405,12 +419,30 @@ public class MyBatisGeneratorService {
return props;
}
private Properties getClasspathProperties() {
/**
* 加载 MyBatis 属性:优先从 data/ 目录,回退 classpath
*/
private Properties loadMybatisProperties() {
Properties props = new Properties();
try {
props.load(getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE));
} catch (Exception e) {
log.warn("无法加载配置属性文件", e);
// 1. 先从 classpath 加载内置默认值
try (InputStream is = getClass().getClassLoader().getResourceAsStream(CLASSPATH_PROPERTIES)) {
if (is != null) {
props.load(is);
}
} catch (IOException e) {
log.warn("无法加载 classpath 内置属性文件", e);
}
// 2. 再用 data/ 目录的外部配置覆盖
Path dataFile = workUtilsProperties.getMybatisGeneratorFilePath();
if (Files.exists(dataFile)) {
try (InputStream is = new FileInputStream(dataFile.toFile())) {
Properties externalProps = new Properties();
externalProps.load(is);
props.putAll(externalProps); // 外部配置覆盖内置值
log.debug("已从 data/ 加载 MyBatis 属性: {}", dataFile.toAbsolutePath());
} catch (IOException e) {
log.warn("无法加载 data/ MyBatis 属性文件: {}", e.getMessage());
}
}
return props;
}
@@ -418,24 +450,24 @@ public class MyBatisGeneratorService {
public String getDatabaseUrl() {
DatasourceConfig ds = getCurrentDatasource();
if (ds != null) return ds.getUrl();
return getClasspathProperties().getProperty("db_url", "");
return loadMybatisProperties().getProperty("db_url", "");
}
public String getDatabaseUsername() {
DatasourceConfig ds = getCurrentDatasource();
if (ds != null) return ds.getUsername();
return getClasspathProperties().getProperty("db_username", "");
return loadMybatisProperties().getProperty("db_username", "");
}
public String getModelPackage() {
return getClasspathProperties().getProperty("model_package", "");
return loadMybatisProperties().getProperty("model_package", "");
}
public String getMapperPackage() {
return getClasspathProperties().getProperty("dao_package", "");
return loadMybatisProperties().getProperty("dao_package", "");
}
public String getSqlMapperPackage() {
return getClasspathProperties().getProperty("sqlmapper_package", "");
return loadMybatisProperties().getProperty("sqlmapper_package", "");
}
}
@@ -7,9 +7,13 @@ import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* 配置初始化器 —— Spring 启动完成后检查并合并外部配置
* 配置初始化器 —— Spring 启动完成后加载外部配置
* <p>
* 优先级: workutils.config > application.yml > 默认值
* data/ 目录下的配置文件由 WorkUtilsProperties 在 @PostConstruct 阶段统一确保存在
* (不存在时自动从 classpath:templates/ 复制)。
* 本初始化器负责加载 workutils.config 并合并到运行属性中。
* <p>
* 优先级: data/workutils.config > application.yml > 默认值
*/
@Slf4j
@Component
@@ -22,13 +26,10 @@ public class ConfigInitializer implements ApplicationListener<ApplicationReadyEv
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
if (configManager.exists()) {
log.info("发现外部配置文件: {}", configManager.getConfigFile().toAbsolutePath());
log.info("加载外部配置文件: {}", configManager.getConfigFile().toAbsolutePath());
properties.mergeExternal(configManager.load());
} else {
log.info("未找到外部配置文件 workutils.config,正在使用 application.yml 默认值生成...");
configManager.saveSectioned(configManager.generateDefaults(properties));
properties.mergeExternal(configManager.load());
log.info("已生成默认外部配置文件: {}", configManager.getConfigFile().toAbsolutePath());
log.info("无外部配置文件,使用 application.yml 默认值");
}
}
}
@@ -9,60 +9,39 @@ import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
/**
* 多数据源配置管理器
* <p>
* 从 data/datasources.json 加载数据源列表,供 MyBatis 逆向工程插件选择使用。
* 首次运行时自动从 classpath 模板复制到 data/ 目录。
* 配置文件的路径和首次复制由 WorkUtilsProperties 统一管理。
*/
@Slf4j
@Component
public class DatasourceConfigManager {
private static final String DATASOURCE_FILE = "data/datasources.json";
private static final String TEMPLATE_PATH = "templates/datasources.json";
private final WorkUtilsProperties properties;
private final List<DatasourceConfig> datasources = new ArrayList<>();
private Path configFile;
public DatasourceConfigManager(WorkUtilsProperties properties) {
this.properties = properties;
}
@PostConstruct
void init() {
configFile = Paths.get(System.getProperty("user.dir"), DATASOURCE_FILE);
ensureConfigExists();
// data/ 目录下的文件已由 WorkUtilsProperties 统一确保存在
reload();
}
/** 确保数据源配置文件存在 */
private void ensureConfigExists() {
if (Files.exists(configFile)) return;
try {
Files.createDirectories(configFile.getParent());
try (InputStream is = getClass().getClassLoader().getResourceAsStream(TEMPLATE_PATH)) {
if (is != null) {
Files.copy(is, configFile);
log.info("已从模板复制数据源配置: {}", configFile.toAbsolutePath());
return;
}
}
} catch (IOException e) {
log.warn("复制数据源模板失败: {}", e.getMessage());
}
// 兜底:创建空数组
try {
Files.write(configFile, "[]".getBytes(StandardCharsets.UTF_8));
log.info("已创建空数据源配置: {}", configFile.toAbsolutePath());
} catch (IOException e) {
log.error("创建数据源配置文件失败", e);
}
}
/** 重新加载数据源配置 */
public synchronized void reload() {
datasources.clear();
if (!Files.exists(configFile)) return;
Path configFile = getConfigFile();
if (!Files.exists(configFile)) {
log.warn("数据源配置文件不存在: {}", configFile.toAbsolutePath());
return;
}
try {
String json = new String(Files.readAllBytes(configFile), StandardCharsets.UTF_8);
List<DatasourceConfig> loaded = JSON.parseArray(json, DatasourceConfig.class);
@@ -93,6 +72,6 @@ public class DatasourceConfigManager {
/** 获取配置文件的路径(供设置窗口打开编辑) */
public Path getConfigFile() {
return configFile;
return properties.getDatasourcesFilePath();
}
}
@@ -7,7 +7,6 @@ import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
@@ -15,9 +14,9 @@ import java.util.Properties;
/**
* 外部配置文件管理器
* <p>
* 管理 exe 同级目录下的 workutils.config 文件:
* 管理 data/ 目录下的 workutils.config 文件:
* <ul>
* <li>首次运行自动生成带注释的分段式默认配置</li>
* <li>首次运行时由 WorkUtilsProperties 从 templates/ 复制到 data/</li>
* <li>运行时读写,优先级高于 application.yml</li>
* <li>支持热重载</li>
* <li>手动写入避免中文 Unicode 转义</li>
@@ -27,7 +26,11 @@ import java.util.Properties;
@Component
public class ExternalConfigManager {
static final String CONFIG_FILE_NAME = "data/workutils.config";
private final WorkUtilsProperties properties;
public ExternalConfigManager(WorkUtilsProperties properties) {
this.properties = properties;
}
// ---- 系统路径 ----
public static final String KEY_BASE_DIR = "base-directory";
@@ -78,9 +81,9 @@ public class ExternalConfigManager {
});
}
/** 获取配置文件路径(exe 所在目录) */
/** 获取配置文件路径 */
public Path getConfigFile() {
return Paths.get(System.getProperty("user.dir"), CONFIG_FILE_NAME);
return properties.getWorkutilsConfigFilePath();
}
public boolean exists() {
@@ -155,39 +158,4 @@ public class ExternalConfigManager {
}
}
/**
* 根据 application.yml 生成默认配置(数据文件、config文件均为 user.dir 同级目录)
*/
public Properties generateDefaults(WorkUtilsProperties appProps) {
Properties props = new Properties();
props.setProperty(KEY_BASE_DIR, nvl(appProps.getBaseDirectory()));
props.setProperty(KEY_EXPORT_DIR, nvl(appProps.getExportDirectory()));
// 数据文件用 user.dir 下的绝对路径
props.setProperty(KEY_PROJECT_PATHS, appProps.getProjectPathsFilePath().toAbsolutePath().toString());
props.setProperty(KEY_DAMAGED_REPOS, appProps.getDamagedReposFilePath().toAbsolutePath().toString());
// MyBatis 留空让用户填
String[] mybatisKeys = {
KEY_MYBATIS_DRIVER, KEY_MYBATIS_URL, KEY_MYBATIS_USERNAME,
KEY_MYBATIS_PASSWORD,
KEY_MYBATIS_MODEL_PKG, KEY_MYBATIS_DAO_PKG, KEY_MYBATIS_SQLMAPPER_PKG
};
for (String k : mybatisKeys) {
props.setProperty(k, "");
}
// 字体大小默认值
props.setProperty(KEY_FONT_SECTION_TITLE, String.valueOf(appProps.getFontSectionTitle()));
props.setProperty(KEY_FONT_FIELD_LABEL, String.valueOf(appProps.getFontFieldLabel()));
props.setProperty(KEY_FONT_BUTTON, String.valueOf(appProps.getFontButton()));
props.setProperty(KEY_FONT_BODY, String.valueOf(appProps.getFontBody()));
props.setProperty(KEY_FONT_MONO, String.valueOf(appProps.getFontMono()));
return props;
}
private static String nvl(String s) {
return s == null ? "" : s;
}
}
@@ -16,6 +16,7 @@ import javafx.stage.Stage;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
@@ -233,7 +234,7 @@ public class SettingsWindow {
wrapper.getChildren().add(grid);
Button btnLoadBuiltin = new Button("从内置 mybatis-generator.properties 加载");
Button btnLoadBuiltin = new Button("从 data/mybatis-generator.properties 加载");
btnLoadBuiltin.setFont(font(fontButton));
btnLoadBuiltin.setStyle("-fx-padding: 6 14;");
allButtons.add(btnLoadBuiltin);
@@ -564,7 +565,8 @@ public class SettingsWindow {
private void loadMyBatisFromClasspath() {
Properties builtin = loadBuiltinMyBatisProps();
if (builtin.isEmpty()) {
showAlert(AlertType.WARNING, "未找到内置 mybatis-generator.properties 文件。");
showAlert(AlertType.WARNING, "未找到 mybatis-generator.properties 配置文件。"
+ "\n请检查 data/mybatis-generator.properties 是否存在。");
return;
}
tfDbDriver.setText(builtin.getProperty("db_driver", ""));
@@ -579,6 +581,7 @@ public class SettingsWindow {
private Properties loadBuiltinMyBatisProps() {
Properties props = new Properties();
// 1. 先从 classpath 加载内置默认值
try (InputStream is = getClass().getClassLoader()
.getResourceAsStream("mybatis-generator.properties")) {
if (is != null) {
@@ -586,6 +589,16 @@ public class SettingsWindow {
}
} catch (IOException ignored) {
}
// 2. 再用 data/ 外部配置覆盖
Path dataFile = properties.getMybatisGeneratorFilePath();
if (Files.exists(dataFile)) {
try (InputStream is = new FileInputStream(dataFile.toFile())) {
Properties externalProps = new Properties();
externalProps.load(is);
props.putAll(externalProps);
} catch (IOException ignored) {
}
}
return props;
}
@@ -50,6 +50,15 @@ public class WorkUtilsProperties {
/** 损坏仓库缓存文件(相对路径时相对于 user.dir) */
private String damagedReposFile;
/** 数据源配置文件(相对路径时相对于 user.dir) */
private String datasourcesFile;
/** 外部配置文件(相对路径时相对于 user.dir) */
private String workutilsConfigFile;
/** MyBatis 生成器配置文件(相对路径时相对于 user.dir) */
private String mybatisGeneratorFile;
/** 工具启用/禁用配置 */
private ToolsConfig tools;
@@ -92,8 +101,8 @@ public class WorkUtilsProperties {
defaults.put(ExternalConfigManager.KEY_EXPORT_DIR, exportDirectory);
defaults.put(ExternalConfigManager.KEY_PROJECT_PATHS, projectPathsFile);
defaults.put(ExternalConfigManager.KEY_DAMAGED_REPOS, damagedReposFile);
// 首次运行时确保数据文件存在于 user.dir 同级目录
ensureDataFilesExist();
// 首次运行时确保所有数据文件存在于 user.dir 同级 data/ 目录
ensureAllDataFilesExist();
}
/** 确保导出目录存在 */
@@ -202,7 +211,7 @@ public class WorkUtilsProperties {
* 否则解析到 user.dir 下。
*/
public Path getDamagedReposFilePath() {
Path p = Paths.get(damagedReposFile != null ? damagedReposFile : "damaged_repos.cache");
Path p = Paths.get(damagedReposFile != null ? damagedReposFile : "data/damaged_repos.cache");
if (p.isAbsolute()) {
return p;
}
@@ -210,16 +219,50 @@ public class WorkUtilsProperties {
}
/**
* 确保数据文件存在于 user.dir 同级目录。
* 不存在时从 classpath:config/ 下复制模板内容(保留原始内容),
* 无法读取模板时则创建空文件。
* 数据源配置文件的实际路径
*/
private void ensureDataFilesExist() {
ensureDataFile(getProjectPathsFilePath(), TEMPLATES_PATH + "project_paths.txt", "项目路径文件");
ensureDataFile(getDamagedReposFilePath(), TEMPLATES_PATH + "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);
}
private static final String TEMPLATES_PATH = "templates/";
/**
* 外部配置文件(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);
}
/**
* 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);
}
/**
* 确保所有数据文件存在于 user.dir 同级 data/ 目录。
* 启动时统一检查:不存在则从 classpath:templates/ 下复制模板。
* 这是所有运行时数据文件的唯一入口。
*/
private void ensureAllDataFilesExist() {
ensureDataFile(getWorkutilsConfigFilePath(), "templates/workutils.config", "外部配置文件");
ensureDataFile(getDatasourcesFilePath(), "templates/datasources.json", "数据源配置");
ensureDataFile(getProjectPathsFilePath(), "templates/project_paths.txt", "项目路径文件");
ensureDataFile(getDamagedReposFilePath(), "templates/damaged_repos.cache", "损坏缓存文件");
ensureDataFile(getMybatisGeneratorFilePath(), "templates/mybatis-generator.properties", "MyBatis生成器配置");
}
private void ensureDataFile(Path target, String classpathResource, String label) {
try {
+3
View File
@@ -10,6 +10,9 @@ workutils:
# data file paths (relative -> under user.dir/data/, absolute -> as-is)
project-paths-file: "data/project_paths.txt"
damaged-repos-file: "data/damaged_repos.cache"
datasources-file: "data/datasources.json"
workutils-config-file: "data/workutils.config"
mybatis-generator-file: "data/mybatis-generator.properties"
logging:
charset:
@@ -0,0 +1,11 @@
db_driver=com.mysql.jdbc.Driver
db_url=jdbc:mysql://localhost:3306/disburse?useUnicode=true&characterEncoding=utf8&useSSL=false
db_username=root
db_password=1Qaz@4772
model_package=model
model_target_project=./generator
sqlmapper_package=mapperxml
sqlmapper_target_project=./generator
dao_package=mapper
dao_target_project=./generator
@@ -1,3 +1,70 @@
# 项目路径配置文件
# 格式: 本地路径=Git仓库URL(每行一个项目)
# 示例: D:/workspace/my-project=https://user@code.example.com/org/repo.git
D:/bestpay-srdcloud/ft-compensatory-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-creative-prod=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-credit-prod=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-customer-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-file-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-finflux-gw-adapter=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-lending-management-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-loancapital-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-loan-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-organmng-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-orgsupport-weixin=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-protocol-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-repay-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-risk-control-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-settlement-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-wechatstage-prod=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-agent-admin=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-batch-creditpayment=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-batch-creditpayment-plus=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-blockchain-prod=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-boss=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-clrstl-fee-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-clrstl-report-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-codeutils=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-collateral-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-commodity-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-commonservice=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-consumestage-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-consumestage-mall=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-credit-bizfront=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-credit-bizfront-plus=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-credit-front=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-credit-prod=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-credit-prod-manage=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-creditstagescore=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-credituse-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-ctcorp-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-customer-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-file-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-govent-prod=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-grant-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-info-collection=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-instalment-admin=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-instalment-batch=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-invoice-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-lending-management-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-loancapital-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-marketing-manage=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-marketing-manage-plus=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-onlinestage-prod=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-organmng-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-precredit-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-productcfg-core-plus=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-protocol-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-risktool=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-scheduler=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-search=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-settlement-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-thirdpartybiz-prod=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-trade-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/cf-wechatstage-prod=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-approval-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-asset-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-blockchain-prod=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-collateral-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-commodity-core=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
D:/bestpay-srdcloud/ft-commonservice=https://srd17321104074@code.srdcloud.cn/bestpay-cf/cf/
@@ -0,0 +1,28 @@
# ====== workutils 外部配置文件 ======
# 编辑后通过「系统设置 > 重新加载」生效,无需重启
# ===== 系统路径 =====
base-directory=
export-directory=
# ===== 数据文件路径(相对于 user.dir)=====
project-paths-file=data/project_paths.txt
damaged-repos-file=data/damaged_repos.cache
# ===== MyBatis 数据库连接(覆盖内置值)=====
mybatis.db.driver=
mybatis.db.url=
mybatis.db.username=
mybatis.db.password=
# ===== MyBatis 代码生成包路径 =====
mybatis.model.package=
mybatis.dao.package=
mybatis.sqlmapper.package=
# ===== 字体大小(显示设置)=====
font.size.section-title=20
font.size.field-label=18
font.size.button=18
font.size.body=18
font.size.mono=18