diff --git a/src/main/java/com/example/workutils/WorkUtilsApplication.java b/src/main/java/com/example/workutils/WorkUtilsApplication.java index a642777..c8aca9b 100644 --- a/src/main/java/com/example/workutils/WorkUtilsApplication.java +++ b/src/main/java/com/example/workutils/WorkUtilsApplication.java @@ -1,8 +1,7 @@ package com.example.workutils; -import com.example.workutils.system.menu.MenuRunner; import com.example.workutils.system.tool.Tool; -import com.example.workutils.system.tool.ToolRegistry; +import com.example.workutils.system.tool.Registry; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; @@ -17,12 +16,10 @@ import java.util.Scanner; @Slf4j public class WorkUtilsApplication implements CommandLineRunner { - private final MenuRunner menuRunner; - private final ToolRegistry toolRegistry; + private final Registry registry; - public WorkUtilsApplication(MenuRunner menuRunner, ToolRegistry toolRegistry) { - this.menuRunner = menuRunner; - this.toolRegistry = toolRegistry; + public WorkUtilsApplication(Registry registry) { + this.registry = registry; } public static void main(String[] args) { @@ -40,7 +37,7 @@ public class WorkUtilsApplication implements CommandLineRunner { private void showInteractiveMenu() { Scanner scanner = new Scanner(System.in); - menuRunner.run(scanner); + registry.runMainMenu(scanner); } /** @@ -50,7 +47,7 @@ public class WorkUtilsApplication implements CommandLineRunner { private void processCommand(String... args) throws Exception { String command = args[0].toLowerCase(); - Optional toolOpt = toolRegistry.getToolByCommand(command); + Optional toolOpt = registry.getToolByCommand(command); if (toolOpt.isPresent()) { Tool tool = toolOpt.get(); try { diff --git a/src/main/java/com/example/workutils/biz/git/menu/GitMenu.java b/src/main/java/com/example/workutils/biz/git/menu/GitMenu.java index 4ca5f9c..988508d 100644 --- a/src/main/java/com/example/workutils/biz/git/menu/GitMenu.java +++ b/src/main/java/com/example/workutils/biz/git/menu/GitMenu.java @@ -2,7 +2,7 @@ package com.example.workutils.biz.git.menu; import com.example.workutils.biz.git.service.GitService; import com.example.workutils.biz.git.util.GitCommandExecutor; -import com.example.workutils.system.tool.AbstractTool; +import com.example.workutils.system.tool.Tool; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -19,7 +19,7 @@ import java.util.stream.Collectors; @Slf4j @Component @RequiredArgsConstructor -public class GitMenu extends AbstractTool { +public class GitMenu implements Tool { private static final String[] MENU_OPTIONS = { "1. 检测所有仓库健康状态", @@ -45,7 +45,7 @@ public class GitMenu extends AbstractTool { } @Override - protected void handleChoice(int choice, Scanner scanner) throws Exception { + public void handleChoice(int choice, Scanner scanner) throws Exception { switch (choice) { case 1: checkAllReposHealth(scanner); @@ -61,13 +61,13 @@ public class GitMenu extends AbstractTool { private void checkAllReposHealth(Scanner scanner) { printMenuTitle("开始检测所有仓库"); - info("开始检测所有仓库健康状态"); + log.info("开始检测所有仓库健康状态"); List results = gitService.checkCorruptedRepos(scanner); if (results.isEmpty()) { System.out.println("所有仓库状态正常!"); - info("所有仓库状态正常"); + log.info("所有仓库状态正常"); } else { System.out.println("\n发现 " + results.size() + " 个损坏的仓库:"); for (int i = 0; i < results.size(); i++) { @@ -76,7 +76,7 @@ public class GitMenu extends AbstractTool { extractProjectName(result.getRepoPath()), result.getCorruptionReason()); } - info("检测完成,发现 {} 个损坏仓库", results.size()); + log.info("检测完成,发现 {} 个损坏仓库", results.size()); } } @@ -108,11 +108,11 @@ public class GitMenu extends AbstractTool { String confirm = scanner.nextLine().trim(); if (!"y".equalsIgnoreCase(confirm)) { System.out.println("已取消修复操作。"); - info("用户取消修复操作"); + log.info("用户取消修复操作"); return; } - info("开始修复 {} 个仓库", selectedIndices.size()); + log.info("开始修复 {} 个仓库", selectedIndices.size()); int successCount = 0; int failCount = 0; for (Integer index : selectedIndices) { @@ -132,7 +132,7 @@ public class GitMenu extends AbstractTool { printMenuTitle("修复完成"); System.out.println("成功: " + successCount); System.out.println("失败: " + failCount); - info("修复完成,成功: {},失败: {}", successCount, failCount); + log.info("修复完成,成功: {},失败: {}", successCount, failCount); } private List parseSelectedIndices(String input, int repoCount) { diff --git a/src/main/java/com/example/workutils/biz/mybatis/menu/MyBatisGeneratorMenu.java b/src/main/java/com/example/workutils/biz/mybatis/menu/MyBatisGeneratorMenu.java index da2a8ed..acdfe28 100644 --- a/src/main/java/com/example/workutils/biz/mybatis/menu/MyBatisGeneratorMenu.java +++ b/src/main/java/com/example/workutils/biz/mybatis/menu/MyBatisGeneratorMenu.java @@ -1,7 +1,7 @@ package com.example.workutils.biz.mybatis.menu; import com.example.workutils.biz.mybatis.generator.MyBatisGeneratorService; -import com.example.workutils.system.tool.AbstractTool; +import com.example.workutils.system.tool.Tool; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -17,7 +17,7 @@ import java.util.Scanner; @Slf4j @Component @RequiredArgsConstructor -public class MyBatisGeneratorMenu extends AbstractTool { +public class MyBatisGeneratorMenu implements Tool { private static final String[] MENU_OPTIONS = { "1. 根据表名生成(单表)", @@ -46,7 +46,7 @@ public class MyBatisGeneratorMenu extends AbstractTool { } @Override - protected void handleChoice(int choice, Scanner scanner) throws Exception { + public void handleChoice(int choice, Scanner scanner) throws Exception { switch (choice) { case 1: generateBySingleTable(scanner); @@ -78,7 +78,7 @@ public class MyBatisGeneratorMenu extends AbstractTool { return; } - info("开始生成表 [{}] 的Mapper文件", tableName); + log.info("开始生成表 [{}] 的Mapper文件", tableName); String result = generatorService.generateByTableNames(Arrays.asList(tableName), true); System.out.println(result); printSuccess("生成完成"); @@ -98,7 +98,7 @@ public class MyBatisGeneratorMenu extends AbstractTool { tableNames.set(i, tableNames.get(i).trim()); } - info("开始生成 {} 个表的Mapper文件", tableNames.size()); + log.info("开始生成 {} 个表的Mapper文件", tableNames.size()); String result = generatorService.generateByTableNames(tableNames, true); System.out.println(result); printSuccess("生成完成"); @@ -131,7 +131,7 @@ public class MyBatisGeneratorMenu extends AbstractTool { return; } - info("开始根据通配符 [{}] 生成 {} 张表的Mapper文件", pattern, matchedTables.size()); + log.info("开始根据通配符 [{}] 生成 {} 张表的Mapper文件", pattern, matchedTables.size()); String result = generatorService.generateByTableNames(matchedTables, true); System.out.println(result); printSuccess("生成完成"); diff --git a/src/main/java/com/example/workutils/biz/mybatis/menu/MyBatisMenu.java b/src/main/java/com/example/workutils/biz/mybatis/menu/MyBatisMenu.java index 79c1aca..384d81e 100644 --- a/src/main/java/com/example/workutils/biz/mybatis/menu/MyBatisMenu.java +++ b/src/main/java/com/example/workutils/biz/mybatis/menu/MyBatisMenu.java @@ -1,7 +1,7 @@ package com.example.workutils.biz.mybatis.menu; import com.example.workutils.biz.mybatis.export.MyBatisExportService; -import com.example.workutils.system.tool.AbstractTool; +import com.example.workutils.system.tool.Tool; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -16,7 +16,7 @@ import java.util.Scanner; @Slf4j @Component @RequiredArgsConstructor -public class MyBatisMenu extends AbstractTool { +public class MyBatisMenu implements Tool { private static final String[] MENU_OPTIONS = { "1. 导出所有SQL", @@ -85,7 +85,7 @@ public class MyBatisMenu extends AbstractTool { } @Override - protected void handleChoice(int choice, Scanner scanner) throws Exception { + public void handleChoice(int choice, Scanner scanner) throws Exception { switch (choice) { case 1: exportAllSql(); @@ -110,7 +110,7 @@ public class MyBatisMenu extends AbstractTool { private void exportAllSql() throws Exception { printMenuTitle("导出所有SQL"); - info("开始导出所有SQL"); + log.info("开始导出所有SQL"); myBatisExportService.exportSQL(); printSuccess("导出所有SQL"); } @@ -122,7 +122,7 @@ public class MyBatisMenu extends AbstractTool { System.out.println("表名不能为空"); return; } - info("按表名导出SQL: {}", tableName); + log.info("按表名导出SQL: {}", tableName); myBatisExportService.exportSQLByTable(tableName); printSuccess("按表名导出SQL"); } @@ -134,7 +134,7 @@ public class MyBatisMenu extends AbstractTool { System.out.println("表名不能为空"); return; } - info("按多个表名导出SQL: {}", tableNames); + log.info("按多个表名导出SQL: {}", tableNames); myBatisExportService.exportSQLByTables(Arrays.asList(tableNames.split(","))); printSuccess("按多个表名导出SQL"); } @@ -146,7 +146,7 @@ public class MyBatisMenu extends AbstractTool { System.out.println("关键字不能为空"); return; } - info("按关键字导出SQL: {}", keyword); + log.info("按关键字导出SQL: {}", keyword); myBatisExportService.exportSQLByKeyword(keyword); printSuccess("按关键字导出SQL"); } @@ -158,7 +158,7 @@ public class MyBatisMenu extends AbstractTool { System.out.println("标签ID不能为空"); return; } - info("按标签ID导出SQL: {}", tagId); + log.info("按标签ID导出SQL: {}", tagId); myBatisExportService.exportSQLByTagId(tagId); printSuccess("按标签ID导出SQL"); } diff --git a/src/main/java/com/example/workutils/biz/output/menu/BatchMenu.java b/src/main/java/com/example/workutils/biz/output/menu/BatchMenu.java index 8580bfb..2b7bf3b 100644 --- a/src/main/java/com/example/workutils/biz/output/menu/BatchMenu.java +++ b/src/main/java/com/example/workutils/biz/output/menu/BatchMenu.java @@ -2,7 +2,7 @@ package com.example.workutils.biz.output.menu; import com.example.workutils.biz.mybatis.export.MyBatisExportService; import com.example.workutils.biz.scanner.service.ScanService; -import com.example.workutils.system.tool.AbstractTool; +import com.example.workutils.system.tool.Tool; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -18,7 +18,7 @@ import java.util.Scanner; @Slf4j @Component @RequiredArgsConstructor -public class BatchMenu extends AbstractTool { +public class BatchMenu implements Tool { @FunctionalInterface public interface ExceptionRunnable { @@ -63,13 +63,13 @@ public class BatchMenu extends AbstractTool { @Override public void executeCommand(String... args) throws Exception { - info("命令行模式: 开始执行所有检查"); + log.info("命令行模式: 开始执行所有检查"); executeAllChecks(); System.out.println("批量处理完成!"); } @Override - protected void handleChoice(int choice, Scanner scanner) throws Exception { + public void handleChoice(int choice, Scanner scanner) throws Exception { switch (choice) { case 1: executeAllChecks(); @@ -85,7 +85,7 @@ public class BatchMenu extends AbstractTool { private void executeAllChecks() throws Exception { printMenuTitle("执行所有检查"); - info("开始执行所有检查"); + log.info("开始执行所有检查"); executeStep("1. 导出SQL...", () -> myBatisExportService.exportSQL()); executeStep("2. 扫描Dubbo接口...", @@ -94,7 +94,7 @@ public class BatchMenu extends AbstractTool { () -> scanService.scanRedisKeysWithDefaultName("redis\\.opsFor")); printSuccess("所有检查"); - info("所有检查执行完成"); + log.info("所有检查执行完成"); } private void executeCustomChecks(Scanner scanner) throws Exception { @@ -105,7 +105,7 @@ public class BatchMenu extends AbstractTool { String choices = scanner.nextLine().trim(); List choiceList = parseCommaSeparatedInput(choices); - info("用户选择执行检查项: {}", choiceList); + log.info("用户选择执行检查项: {}", choiceList); for (String choice : choiceList) { try { @@ -132,7 +132,7 @@ public class BatchMenu extends AbstractTool { break; } } catch (Exception e) { - error("执行检查项失败: " + choice, e); + log.error("执行检查项失败: " + choice, e); System.out.println("检查项 " + choice + " 执行失败: " + e.getMessage()); } } diff --git a/src/main/java/com/example/workutils/biz/scanner/menu/ScanMenu.java b/src/main/java/com/example/workutils/biz/scanner/menu/ScanMenu.java index 98b6917..fb36db5 100644 --- a/src/main/java/com/example/workutils/biz/scanner/menu/ScanMenu.java +++ b/src/main/java/com/example/workutils/biz/scanner/menu/ScanMenu.java @@ -1,7 +1,7 @@ package com.example.workutils.biz.scanner.menu; import com.example.workutils.biz.scanner.service.ScanService; -import com.example.workutils.system.tool.AbstractTool; +import com.example.workutils.system.tool.Tool; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -15,7 +15,7 @@ import java.util.Scanner; @Slf4j @Component @RequiredArgsConstructor -public class ScanMenu extends AbstractTool { +public class ScanMenu implements Tool { private static final String[] MENU_OPTIONS = { "1. 关键词扫描", @@ -75,7 +75,7 @@ public class ScanMenu extends AbstractTool { } @Override - protected void handleChoice(int choice, Scanner scanner) throws Exception { + public void handleChoice(int choice, Scanner scanner) throws Exception { switch (choice) { case 1: scanByKeyword(scanner); @@ -88,7 +88,7 @@ public class ScanMenu extends AbstractTool { private void scanByKeyword(Scanner scanner) throws Exception { printMenuTitle("关键词扫描"); - info("开始关键词扫描"); + log.info("开始关键词扫描"); System.out.print("请输入关键词: "); String keyword = scanner.nextLine().trim(); @@ -98,7 +98,7 @@ public class ScanMenu extends AbstractTool { return; } - info("扫描关键词: {}", keyword); + log.info("扫描关键词: {}", keyword); scanService.scanKeywordsWithDefaultName(keyword); printSuccess("关键词扫描"); diff --git a/src/main/java/com/example/workutils/system/menu/MenuRunner.java b/src/main/java/com/example/workutils/system/menu/MenuRunner.java deleted file mode 100644 index 7cf0442..0000000 --- a/src/main/java/com/example/workutils/system/menu/MenuRunner.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.example.workutils.system.menu; - -import com.example.workutils.system.ConsoleHelper; -import com.example.workutils.system.tool.Tool; -import com.example.workutils.system.tool.ToolRegistry; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.Scanner; - -/** - * 菜单运行器 - * 从ToolRegistry动态构建主菜单,无需硬编码Menu引用 - */ -@Slf4j -@Component -public class MenuRunner { - - private static final int EXIT_CODE = 0; - - private final ToolRegistry toolRegistry; - - public MenuRunner(ToolRegistry toolRegistry) { - this.toolRegistry = toolRegistry; - } - - /** - * 运行主菜单循环 - */ - public void run(Scanner scanner) { - log.info("启动主菜单"); - while (true) { - ConsoleHelper.printMenuTitle("主菜单"); - displayMainMenu(); - - int choice = ConsoleHelper.readChoice(scanner); - scanner.nextLine(); - - if (choice == EXIT_CODE) { - log.info("用户退出系统"); - System.out.println("感谢使用,再见!"); - break; - } - - handleChoice(choice, scanner); - } - } - - /** - * 动态构建主菜单 - */ - private void displayMainMenu() { - System.out.println("请选择操作:"); - List tools = toolRegistry.getEnabledTools(); - for (int i = 0; i < tools.size(); i++) { - System.out.println((i + 1) + ". " + tools.get(i).getName()); - } - System.out.println(EXIT_CODE + ". 退出系统"); - } - - /** - * 处理用户选择,工具隔离 - * 单个工具异常不影响平台和其他工具 - */ - private void handleChoice(int choice, Scanner scanner) { - List tools = toolRegistry.getEnabledTools(); - int index = choice - 1; - - if (index >= 0 && index < tools.size()) { - Tool tool = tools.get(index); - try { - tool.execute(scanner); - } catch (Exception e) { - log.error("工具执行失败: {} [{}]", tool.getName(), tool.getId(), e); - System.out.println("工具执行失败: " + e.getMessage()); - } - } else { - ConsoleHelper.printInvalidOption(); - } - } -} diff --git a/src/main/java/com/example/workutils/system/tool/AbstractTool.java b/src/main/java/com/example/workutils/system/tool/AbstractTool.java deleted file mode 100644 index 2ff75a3..0000000 --- a/src/main/java/com/example/workutils/system/tool/AbstractTool.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.example.workutils.system.tool; - -import com.example.workutils.system.ConsoleHelper; -import lombok.extern.slf4j.Slf4j; - -import java.util.Scanner; - -/** - * 工具基类 - * 提供共享的菜单循环模板方法,消除各Menu中的重复代码 - * 子类只需实现handleChoice()定义具体交互逻辑 - * - * 控制台I/O方法委托给ConsoleHelper,日志方法保留在此类 - */ -@Slf4j -public abstract class AbstractTool implements Tool { - - /** - * 模板方法:标准菜单交互循环 - * displayMenu → readChoice → if 0 return → handleChoice → repeat - */ - @Override - public void execute(Scanner scanner) { - while (true) { - ConsoleHelper.printMenuTitle(getName()); - ConsoleHelper.displayMenu(getMenuOptions()); - - int choice = ConsoleHelper.readChoice(scanner); - scanner.nextLine(); - - if (choice == 0) { - info("返回上级菜单"); - return; - } - - try { - handleChoice(choice, scanner); - } catch (Exception e) { - error(getName() + "执行失败", e); - ConsoleHelper.printError(e.getMessage()); - } - } - } - - /** - * 处理子菜单选择,子类实现具体switch-case逻辑 - */ - protected abstract void handleChoice(int choice, Scanner scanner) throws Exception; - - // ============ 日志方法 ============ - - protected void info(String message) { - log.info(message); - } - - protected void info(String message, Object... args) { - log.info(message, args); - } - - protected void error(String message) { - log.error(message); - } - - protected void error(String message, Exception e) { - log.error(message, e); - } - - // ============ 控制台I/O方法(委托给ConsoleHelper,子类可直接调用) ============ - - protected void printMenuTitle(String title) { - ConsoleHelper.printMenuTitle(title); - } - - protected void displayMenu(String[] options) { - ConsoleHelper.displayMenu(options); - } - - protected void printSuccess(String operation) { - ConsoleHelper.printSuccess(operation); - } - - protected void printError(String message) { - ConsoleHelper.printError(message); - } - - protected void printInvalidOption() { - ConsoleHelper.printInvalidOption(); - } -} diff --git a/src/main/java/com/example/workutils/system/tool/ToolRegistry.java b/src/main/java/com/example/workutils/system/tool/Registry.java similarity index 59% rename from src/main/java/com/example/workutils/system/tool/ToolRegistry.java rename to src/main/java/com/example/workutils/system/tool/Registry.java index c216982..854e410 100644 --- a/src/main/java/com/example/workutils/system/tool/ToolRegistry.java +++ b/src/main/java/com/example/workutils/system/tool/Registry.java @@ -1,5 +1,6 @@ package com.example.workutils.system.tool; +import com.example.workutils.system.ConsoleHelper; import com.example.workutils.system.config.WorkUtilsProperties; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -10,11 +11,14 @@ import java.util.stream.Collectors; /** * 工具注册中心 - * 自动发现Spring容器中的Tool Bean,按配置过滤启用/禁用 + * 自动发现 Spring 容器中的 Tool Bean,按配置过滤启用/禁用 + * 同时负责主菜单循环(从 MenuRunner 合并) */ @Slf4j @Component -public class ToolRegistry { +public class Registry { + + private static final int EXIT_CODE = 0; private final List allTools; private final WorkUtilsProperties properties; @@ -22,11 +26,7 @@ public class ToolRegistry { private Map toolByIdMap; private Map toolByCommandMap; - /** - * Spring自动注入所有Tool Bean - * 新增工具只需创建@Component类实现Tool接口,无需修改此类 - */ - public ToolRegistry(List tools, WorkUtilsProperties properties) { + public Registry(List tools, WorkUtilsProperties properties) { this.allTools = tools; this.properties = properties; } @@ -52,7 +52,6 @@ public class ToolRegistry { log.info("跳过已禁用工具: {} [{}]", tool.getName(), tool.getId()); } } catch (Exception e) { - // 工具隔离:单个工具初始化异常不影响平台和其他工具 log.error("工具初始化失败: {} [{}]", tool.getName(), tool.getId(), e); } } @@ -60,33 +59,66 @@ public class ToolRegistry { log.info("工具注册完成: {}/{} 已启用", enabledTools.size(), allTools.size()); } - /** - * 获取所有已启用工具 - */ + // ==================== 主菜单(从 MenuRunner 合并) ==================== + + public void runMainMenu(Scanner scanner) { + log.info("启动主菜单"); + while (true) { + ConsoleHelper.printMenuTitle("主菜单"); + displayMainMenu(); + + int choice = ConsoleHelper.readChoice(scanner); + scanner.nextLine(); + + if (choice == EXIT_CODE) { + log.info("用户退出系统"); + System.out.println("感谢使用,再见!"); + break; + } + + dispatchTool(choice, scanner); + } + } + + private void displayMainMenu() { + System.out.println("请选择操作:"); + for (int i = 0; i < enabledTools.size(); i++) { + System.out.println((i + 1) + ". " + enabledTools.get(i).getName()); + } + System.out.println(EXIT_CODE + ". 退出系统"); + } + + private void dispatchTool(int choice, Scanner scanner) { + int index = choice - 1; + if (index >= 0 && index < enabledTools.size()) { + Tool tool = enabledTools.get(index); + try { + tool.execute(scanner); + } catch (Exception e) { + log.error("工具执行失败: {} [{}]", tool.getName(), tool.getId(), e); + System.out.println("工具执行失败: " + e.getMessage()); + } + } else { + ConsoleHelper.printInvalidOption(); + } + } + + // ==================== 查找方法 ==================== + public List getEnabledTools() { return Collections.unmodifiableList(enabledTools); } - /** - * 按ID查找工具 - */ public Optional getToolById(String id) { return Optional.ofNullable(toolByIdMap.get(id)); } - /** - * 按命令行命令名查找工具 - */ public Optional getToolByCommand(String command) { return Optional.ofNullable(toolByCommandMap.get(command)); } - /** - * 根据配置解析启用的工具ID集合 - * 优先级: enabled白名单 > disabled黑名单 > 全部启用 - * - * @return null表示全部启用,否则为启用的ID集合 - */ + // ==================== 启用/禁用解析 ==================== + private Set resolveEnabledIds() { WorkUtilsProperties.ToolsConfig toolsConfig = properties.getTools(); if (toolsConfig == null) { diff --git a/src/main/java/com/example/workutils/system/tool/Tool.java b/src/main/java/com/example/workutils/system/tool/Tool.java index ba875af..a1b7f8a 100644 --- a/src/main/java/com/example/workutils/system/tool/Tool.java +++ b/src/main/java/com/example/workutils/system/tool/Tool.java @@ -1,53 +1,90 @@ package com.example.workutils.system.tool; +import com.example.workutils.system.ConsoleHelper; + import java.util.Scanner; /** * 工具策略接口 - * 所有可插拔工具实现此接口,Spring自动发现Tool Bean + * 所有可插拔工具实现此接口,Spring 自动发现 Tool Bean + *

+ * 提供交互式菜单(execute)和命令行(executeCommand)两种执行模式。 + * 交互模式由默认 execute() 模板方法驱动,子类实现 handleChoice() 定义选择逻辑。 */ public interface Tool { - /** - * 工具唯一标识(用于配置启用/禁用) - * 示例: "scan", "mybatis", "batch", "git-health", "mybatis-generator" - */ + /** 工具唯一标识(如 "scan", "mybatis") */ String getId(); - /** - * 菜单显示名称 - * 示例: "关键词扫描", "MyBatis导出" - */ + /** 菜单显示名称(如 "关键词扫描", "MyBatis SQL导出") */ String getName(); - /** - * 子菜单选项 - * 返回空数组表示无子菜单 - */ + /** 子菜单选项,返回空数组表示无子菜单 */ String[] getMenuOptions(); - /** - * 命令行模式下的命令名,如 "sql", "scan", "batch" - * 返回空字符串表示不支持命令行模式 - */ + /** 命令行命令名(如 "sql", "scan"),返回空字符串表示不支持命令行模式 */ default String getCommandName() { return ""; } - /** - * 执行工具的交互会话 - * - * @param scanner 控制台输入 - */ - void execute(Scanner scanner); + // ==================== 交互模式(模板方法) ==================== /** - * 命令行模式执行(无交互) - * 默认实现打印不支持提示 - * - * @param args 命令行参数(不含命令名本身) + * 交互式菜单循环(默认实现,从 AbstractTool 合并而来) + * displayMenu → readChoice → if 0 return → handleChoice → repeat + */ + default void execute(Scanner scanner) { + while (true) { + ConsoleHelper.printMenuTitle(getName()); + ConsoleHelper.displayMenu(getMenuOptions()); + + int choice = ConsoleHelper.readChoice(scanner); + scanner.nextLine(); + + if (choice == 0) { + return; + } + + try { + handleChoice(choice, scanner); + } catch (Exception e) { + ConsoleHelper.printError(e.getMessage()); + } + } + } + + /** 处理子菜单选择,子类实现具体分发逻辑 */ + default void handleChoice(int choice, Scanner scanner) throws Exception { + } + + // ==================== 命令行模式 ==================== + + /** + * 命令行模式执行(无交互),默认打印不支持提示 */ default void executeCommand(String... args) throws Exception { System.out.println("工具 [" + getName() + "] 不支持命令行模式"); } + + // ==================== 控制台输出辅助方法 ==================== + + default void printMenuTitle(String title) { + ConsoleHelper.printMenuTitle(title); + } + + default void displayMenu(String[] options) { + ConsoleHelper.displayMenu(options); + } + + default void printSuccess(String operation) { + ConsoleHelper.printSuccess(operation); + } + + default void printError(String message) { + ConsoleHelper.printError(message); + } + + default void printInvalidOption() { + ConsoleHelper.printInvalidOption(); + } }