diff --git a/pom.xml b/pom.xml index 1a04d70..8c0315b 100644 --- a/pom.xml +++ b/pom.xml @@ -32,12 +32,6 @@ - - org.mybatis - mybatis - 3.5.7 - - org.mybatis.generator @@ -64,12 +58,6 @@ 4.5 - - xerces - xercesImpl - 2.12.2 - - org.springframework.boot spring-boot-starter @@ -100,53 +88,10 @@ 2.11.0 - - org.apache.dubbo - dubbo - 2.7.12 - - - - - org.apache.commons - commons-csv - 1.9.0 - - - - - org.eclipse.jgit - org.eclipse.jgit - 6.8.0.202311291450-r - - org.projectlombok lombok - - - - com.google.guava - guava - 33.0.0-jre - - - - org.dom4j - dom4j - 2.1.3 - - - - jaxen - jaxen - 1.2.0 - - - - com.alibaba - fastjson - 1.2.83 + provided @@ -166,18 +111,6 @@ com.alibaba easyexcel 3.1.1 - - - - redis.clients - jedis - 3.7.0 - - - - org.apache.poi - poi-ooxml - 5.2.3 org.apache.logging.log4j @@ -217,6 +150,15 @@ org.springframework.boot spring-boot-maven-plugin + + com.example.workutils.WorkUtilsApplication + + + org.projectlombok + lombok + + + diff --git a/scripts/build_release.bat b/scripts/build_release.bat index 3e1a8a2..13882a6 100644 --- a/scripts/build_release.bat +++ b/scripts/build_release.bat @@ -19,29 +19,29 @@ echo ========== WorkUtils 生产构建 ========== echo. :: 1. 杀掉可能占用 target 的进程 -echo [1/4] 清理残留进程... +echo [1/4] Clean stale processes... taskkill /f /im java.exe 2>nul taskkill /f /im workutils.exe 2>nul :: 2. Maven 打包 echo. -echo [2/4] Maven 编译打包... -call "%PROJECT_DIR%mvnw.cmd" clean package -DskipTests -q +echo [2/4] Maven build... +call "%PROJECT_DIR%\mvnw.cmd" clean package -DskipTests -q if %errorlevel% neq 0 ( - echo [ERROR] Maven 打包失败! + echo [ERROR] Maven build failed! pause exit /b 1 ) :: 3. 提取版本号 -for /f "tokens=2 delims=>" %%i in ('findstr /c:"" "%PROJECT_DIR%pom.xml" ^| findstr /v "xml" ^| findstr /n "1" ^| findstr "1:"') do ( +for /f "tokens=2 delims=>" %%i in ('findstr /c:"" "%PROJECT_DIR%\pom.xml" ^| findstr /v "xml" ^| findstr /n "1" ^| findstr "1:"') do ( for /f "tokens=1 delims=<" %%v in ("%%i") do set VERSION=%%v ) if "%VERSION%"=="" set VERSION=0.0.1-SNAPSHOT :: 4. 准备发布目录 echo. -echo [3/4] 组装发布包... +echo [3/4] Assemble distribution... if exist "%DIST%" rd /s /q "%DIST%" mkdir "%DIST%\workutils" 2>nul @@ -63,9 +63,9 @@ powershell -Command "Compress-Archive -Path '%DIST%\workutils' -DestinationPath :: 5. 完成 echo. -echo [4/4] 构建完成! +echo [4/4] Build complete! echo ============================================ -echo 产物位置: +echo Output: echo exe : target\workutils.exe echo jar : target\workutils-%VERSION%.jar echo zip : target\dist\%ZIPNAME%.zip diff --git a/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorPlugin.java b/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorPlugin.java index d161a41..ddf7077 100644 --- a/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorPlugin.java +++ b/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorPlugin.java @@ -132,6 +132,18 @@ public class MyBatisGeneratorPlugin implements Plugin { public String generate(List tables, String modelDir, String xmlDir, String mapperDir) throws Exception { return generatorService.generateByTableNames(tables, true, modelDir, xmlDir, mapperDir); } + @Override + public List getDatasourceNames() { + return generatorService.getDatasourceNames(); + } + @Override + public void switchDatasource(String name) { + generatorService.switchDatasource(name); + } + @Override + public String getCurrentDatasourceName() { + return generatorService.getCurrentDatasourceName(); + } }; javafx.application.Platform.runLater(() -> { diff --git a/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java b/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java index 759f06f..13ea394 100644 --- a/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java +++ b/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java @@ -53,6 +53,9 @@ public class MyBatisGeneratorWindow { private TextField mapperDirField; private TreeView projectTree; private ToggleGroup fillTargetGroup; + private ComboBox datasourceCmb; + private Label dbUrlLabel; + private Label dbUserLabel; /** 表项(含复选框) */ public static class TableItem { @@ -79,6 +82,12 @@ public class MyBatisGeneratorWindow { String getDbUrl(); String getDbUsername(); String generate(List tables, String modelDir, String xmlDir, String mapperDir) throws Exception; + /** 可用的数据源名称列表 */ + List getDatasourceNames(); + /** 切换数据源 */ + void switchDatasource(String name); + /** 当前选中的数据源名称 */ + String getCurrentDatasourceName(); } public MyBatisGeneratorWindow(GeneratorCallback callback, FileOperationHelper fileOperationHelper, @@ -177,19 +186,69 @@ public class MyBatisGeneratorWindow { } private HBox createDbInfoSection() { - HBox box = new HBox(20); + HBox box = new HBox(15); box.setPadding(new Insets(8, 12, 8, 12)); box.setStyle("-fx-background-color: #e8f0fe; -fx-background-radius: 4;"); + box.setAlignment(Pos.CENTER_LEFT); - Label urlLabel = new Label("数据库: " + callback.getDbUrl()); - urlLabel.setFont(Font.font("Consolas", fsBody())); - Label userLabel = new Label("用户: " + callback.getDbUsername()); - userLabel.setFont(Font.font("Microsoft YaHei", fsBody())); + Label dsLabel = new Label("数据源:"); + dsLabel.setFont(Font.font("Microsoft YaHei", fsBody())); - box.getChildren().addAll(urlLabel, userLabel); + datasourceCmb = new ComboBox<>(); + datasourceCmb.setPrefWidth(160); + datasourceCmb.setStyle("-fx-font-size: " + fsBody() + "px;"); + + // 加载数据源列表 + refreshDatasourceList(); + datasourceCmb.setOnAction(e -> { + String selected = datasourceCmb.getValue(); + if (selected != null) { + callback.switchDatasource("默认配置".equals(selected) ? null : selected); + updateDbInfo(); + } + }); + + dbUrlLabel = new Label(); + dbUrlLabel.setFont(Font.font("Consolas", fsBody() - 1)); + dbUserLabel = new Label(); + dbUserLabel.setFont(Font.font("Microsoft YaHei", fsBody() - 1)); + updateDbInfo(); + + Button refreshDsBtn = new Button("刷新"); + refreshDsBtn.setStyle("-fx-font-size: " + (fsBody() - 1) + "px; -fx-padding: 2 8;"); + refreshDsBtn.setOnAction(e -> { + refreshDatasourceList(); + updateDbInfo(); + }); + + VBox infoBox = new VBox(3, dbUrlLabel, dbUserLabel); + + box.getChildren().addAll(dsLabel, datasourceCmb, refreshDsBtn, infoBox); return box; } + private void refreshDatasourceList() { + String current = datasourceCmb.getValue(); + datasourceCmb.getItems().clear(); + datasourceCmb.getItems().add("默认配置"); + datasourceCmb.getItems().addAll(callback.getDatasourceNames()); + if (current != null && datasourceCmb.getItems().contains(current)) { + datasourceCmb.setValue(current); + } else { + String dsName = callback.getCurrentDatasourceName(); + if (dsName != null && datasourceCmb.getItems().contains(dsName)) { + datasourceCmb.setValue(dsName); + } else { + datasourceCmb.setValue("默认配置"); + } + } + } + + private void updateDbInfo() { + dbUrlLabel.setText("数据库: " + callback.getDbUrl()); + dbUserLabel.setText("用户: " + callback.getDbUsername()); + } + // ==================== 左侧:项目路径树 ==================== private VBox createProjectTreePanel() { diff --git a/src/main/java/com/example/workutils/biz/mybatisgenerator/service/generator/MyBatisGeneratorService.java b/src/main/java/com/example/workutils/biz/mybatisgenerator/service/generator/MyBatisGeneratorService.java index 9c08dd7..fae1bcc 100644 --- a/src/main/java/com/example/workutils/biz/mybatisgenerator/service/generator/MyBatisGeneratorService.java +++ b/src/main/java/com/example/workutils/biz/mybatisgenerator/service/generator/MyBatisGeneratorService.java @@ -1,5 +1,7 @@ package com.example.workutils.biz.mybatisgenerator.service.generator; +import com.example.workutils.system.config.DatasourceConfig; +import com.example.workutils.system.config.DatasourceConfigManager; import lombok.extern.slf4j.Slf4j; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; @@ -25,12 +27,40 @@ public class MyBatisGeneratorService { private static final String CONFIG_FILE = "mybatis-generator.xml"; private static final String PROPERTIES_FILE = "mybatis-generator.properties"; + private final DatasourceConfigManager datasourceMgr; + /** 当前选中的数据源名称(null = 使用外部配置或内置默认配置) */ + private String currentDatasourceName; + + public MyBatisGeneratorService(DatasourceConfigManager datasourceMgr) { + this.datasourceMgr = datasourceMgr; + } + + // ==================== 数据源切换 ==================== + + public List getDatasourceNames() { + return datasourceMgr.getDatasourceNames(); + } + + public DatasourceConfig getCurrentDatasource() { + if (currentDatasourceName != null) { + return datasourceMgr.findByName(currentDatasourceName); + } + return null; + } + + public String getCurrentDatasourceName() { + return currentDatasourceName; + } + + public void switchDatasource(String name) { + this.currentDatasourceName = name; + log.info("已切换数据源: {}", name != null ? name : "默认配置"); + } + + // ==================== 生成入口 ==================== + /** * 根据表名列表生成Mapper文件 - * - * @param tableNames 表名列表 - * @param overwrite 是否覆盖已有文件 - * @return 生成结果信息 */ public String generateByTableNames(List tableNames, boolean overwrite) throws Exception { return generateInternal(tableNames, overwrite, null, null, null); @@ -46,40 +76,27 @@ public class MyBatisGeneratorService { /** * 根据表名通配符生成Mapper文件 - * 先查询数据库中匹配的表,再进行逆向工程 - * - * @param pattern 表名通配符(如 user_%) - * @param overwrite 是否覆盖已有文件 - * @return 生成结果信息 */ public String generateByPattern(String pattern, boolean overwrite) throws Exception { List matchedTables = queryTablesByPattern(pattern); - if (matchedTables.isEmpty()) { return "未找到匹配通配符 [" + pattern + "] 的表"; } - log.info("通配符 [{}] 匹配到 {} 张表: {}", pattern, matchedTables.size(), matchedTables); return generateInternal(matchedTables, overwrite, null, null, null); } + // ==================== 数据库查询 ==================== + /** * 根据通配符查询数据库中的表 - * - * @param pattern 表名通配符(支持 % 和 _ 通配符) - * @return 匹配的表名列表 */ public List queryTablesByPattern(String pattern) throws Exception { List tables = new ArrayList<>(); - Properties props = getConfigurationProperties(); - - String url = props.getProperty("db_url"); - String username = props.getProperty("db_username"); - String password = props.getProperty("db_password"); - - String dbName = extractDatabaseName(url); - - try (Connection conn = DriverManager.getConnection(url, username, password)) { + DbConnection db = getDbConnection(); + String dbName = extractDatabaseName(db.url); + + try (Connection conn = DriverManager.getConnection(db.url, db.username, db.password)) { String sql = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE ?"; try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, dbName); @@ -91,26 +108,18 @@ public class MyBatisGeneratorService { } } } - return tables; } /** * 查询数据库中所有表 - * - * @return 所有表名列表 */ public List queryAllTables() throws Exception { List tables = new ArrayList<>(); - Properties props = getConfigurationProperties(); - - String url = props.getProperty("db_url"); - String username = props.getProperty("db_username"); - String password = props.getProperty("db_password"); - - String dbName = extractDatabaseName(url); - - try (Connection conn = DriverManager.getConnection(url, username, password)) { + DbConnection db = getDbConnection(); + String dbName = extractDatabaseName(db.url); + + try (Connection conn = DriverManager.getConnection(db.url, db.username, db.password)) { String sql = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? ORDER BY TABLE_NAME"; try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, dbName); @@ -121,10 +130,39 @@ public class MyBatisGeneratorService { } } } - return tables; } + /** + * 获取当前生效的数据库连接信息 + * 优先级: 选中的数据源 > 外部配置(workutils.config) > 内置classpath属性 + */ + private DbConnection getDbConnection() { + DatasourceConfig ds = getCurrentDatasource(); + if (ds != null) { + return new DbConnection(ds.getUrl(), ds.getUsername(), ds.getPassword()); + } + // 回退 classpath 配置 + Properties props = getClasspathProperties(); + return new DbConnection( + props.getProperty("db_url", ""), + props.getProperty("db_username", ""), + props.getProperty("db_password", "") + ); + } + + /** 内部数据类:数据库连接信息 */ + private static class DbConnection { + final String url; + final String username; + final String password; + DbConnection(String url, String username, String password) { + this.url = url; + this.username = username; + this.password = password; + } + } + /** * 从JDBC URL中提取数据库名 */ @@ -352,9 +390,22 @@ public class MyBatisGeneratorService { } /** - * 获取配置属性 + * 获取配置属性(优先使用选中的数据源,回退到classpath) */ public Properties getConfigurationProperties() { + Properties props = getClasspathProperties(); + // 如果选中了数据源,覆盖连接信息 + DatasourceConfig ds = getCurrentDatasource(); + if (ds != null) { + if (ds.getDriver() != null) props.setProperty("db_driver", ds.getDriver()); + if (ds.getUrl() != null) props.setProperty("db_url", ds.getUrl()); + if (ds.getUsername() != null) props.setProperty("db_username", ds.getUsername()); + if (ds.getPassword() != null) props.setProperty("db_password", ds.getPassword()); + } + return props; + } + + private Properties getClasspathProperties() { Properties props = new Properties(); try { props.load(getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE)); @@ -364,38 +415,27 @@ public class MyBatisGeneratorService { return props; } - /** - * 获取数据库URL - */ public String getDatabaseUrl() { - return getConfigurationProperties().getProperty("db_url", ""); + DatasourceConfig ds = getCurrentDatasource(); + if (ds != null) return ds.getUrl(); + return getClasspathProperties().getProperty("db_url", ""); } - /** - * 获取数据库用户名 - */ public String getDatabaseUsername() { - return getConfigurationProperties().getProperty("db_username", ""); + DatasourceConfig ds = getCurrentDatasource(); + if (ds != null) return ds.getUsername(); + return getClasspathProperties().getProperty("db_username", ""); } - /** - * 获取Model包路径 - */ public String getModelPackage() { - return getConfigurationProperties().getProperty("model_package", ""); + return getClasspathProperties().getProperty("model_package", ""); } - /** - * 获取Mapper包路径 - */ public String getMapperPackage() { - return getConfigurationProperties().getProperty("dao_package", ""); + return getClasspathProperties().getProperty("dao_package", ""); } - /** - * 获取SQL Mapper包路径 - */ public String getSqlMapperPackage() { - return getConfigurationProperties().getProperty("sqlmapper_package", ""); + return getClasspathProperties().getProperty("sqlmapper_package", ""); } } diff --git a/src/main/java/com/example/workutils/system/config/DatasourceConfig.java b/src/main/java/com/example/workutils/system/config/DatasourceConfig.java new file mode 100644 index 0000000..5a7538a --- /dev/null +++ b/src/main/java/com/example/workutils/system/config/DatasourceConfig.java @@ -0,0 +1,25 @@ +package com.example.workutils.system.config; + +import lombok.Data; + +/** + * 单条数据源配置 + */ +@Data +public class DatasourceConfig { + + /** 显示名称(如"开发环境"、"SIT环境") */ + private String name; + + /** JDBC 驱动类 */ + private String driver = "com.mysql.jdbc.Driver"; + + /** JDBC URL */ + private String url; + + /** 用户名 */ + private String username; + + /** 密码 */ + private String password; +} diff --git a/src/main/java/com/example/workutils/system/config/DatasourceConfigManager.java b/src/main/java/com/example/workutils/system/config/DatasourceConfigManager.java new file mode 100644 index 0000000..32ee62c --- /dev/null +++ b/src/main/java/com/example/workutils/system/config/DatasourceConfigManager.java @@ -0,0 +1,98 @@ +package com.example.workutils.system.config; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +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.*; + +/** + * 多数据源配置管理器 + *

+ * 从 data/datasources.json 加载数据源列表,供 MyBatis 逆向工程插件选择使用。 + * 首次运行时自动从 classpath 模板复制到 data/ 目录。 + */ +@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 List datasources = new ArrayList<>(); + private Path configFile; + + @PostConstruct + void init() { + configFile = Paths.get(System.getProperty("user.dir"), DATASOURCE_FILE); + ensureConfigExists(); + 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; + try { + String json = new String(Files.readAllBytes(configFile), StandardCharsets.UTF_8); + List loaded = JSON.parseArray(json, DatasourceConfig.class); + if (loaded != null) { + datasources.addAll(loaded); + } + log.info("已加载 {} 套数据源配置", datasources.size()); + } catch (IOException e) { + log.error("解析数据源配置失败: {}", e.getMessage()); + } + } + + /** 获取所有数据源名称列表 */ + public List getDatasourceNames() { + List names = new ArrayList<>(); + for (DatasourceConfig ds : datasources) { + names.add(ds.getName()); + } + return names; + } + + /** 根据名称查找数据源配置 */ + public DatasourceConfig findByName(String name) { + return datasources.stream() + .filter(ds -> ds.getName().equals(name)) + .findFirst().orElse(null); + } + + /** 获取配置文件的路径(供设置窗口打开编辑) */ + public Path getConfigFile() { + return configFile; + } +} diff --git a/src/main/resources/templates/datasources.json b/src/main/resources/templates/datasources.json new file mode 100644 index 0000000..89299b8 --- /dev/null +++ b/src/main/resources/templates/datasources.json @@ -0,0 +1,23 @@ +[ + { + "name": "开发环境", + "driver": "com.mysql.jdbc.Driver", + "url": "jdbc:mysql://localhost:3306/disburse?useUnicode=true&characterEncoding=utf8&useSSL=false", + "username": "root", + "password": "1Qaz@4772" + }, + { + "name": "SIT环境", + "driver": "com.mysql.jdbc.Driver", + "url": "jdbc:mysql://sit-server:3306/disburse?useUnicode=true&characterEncoding=utf8&useSSL=false", + "username": "sit_user", + "password": "" + }, + { + "name": "生产环境(只读)", + "driver": "com.mysql.jdbc.Driver", + "url": "jdbc:mysql://prod-server:3306/disburse?useUnicode=true&characterEncoding=utf8&useSSL=false", + "username": "readonly", + "password": "" + } +]