diff --git a/ruoyi-admin/src/test/java/org/dromara/test/MavenModuleRefactorTest.java b/ruoyi-admin/src/test/java/org/dromara/test/MavenModuleRefactorTest.java new file mode 100644 index 000000000..635cd6ea1 --- /dev/null +++ b/ruoyi-admin/src/test/java/org/dromara/test/MavenModuleRefactorTest.java @@ -0,0 +1,2276 @@ +package org.dromara.test; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.*; +import java.nio.charset.Charset; + +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.Comparator; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * Maven模块重构工具 + * 基于Maven项目结构自动分析和重构,输出到指定目录 + *

+ * 使用说明: + * 1. 修改 refactor-config.properties 配置文件 + * 2. 运行 refactorMavenModules() 方法执行重构 + * + * @author whj + */ +@DisplayName("Maven模块重构工具") +public class MavenModuleRefactorTest { + + // 配置文件路径 + private static final String CONFIG_FILE = "refactor-config.properties"; + + // 配置属性 + private Properties config; + + // 项目内部模块信息缓存 + private final Set internalPackages = new HashSet<>(); + private final Set internalArtifacts = new HashSet<>(); + private final Map artifactToPackageMap = new HashMap<>(); + + // 排除的依赖包缓存 + private final Set excludePackages = new HashSet<>(); + + // 跨平台配置缓存 + private String pathSeparator; + private String lineSeparator; + private String fileEncoding; + + /** + * 加载配置文件 + */ + private void loadConfig() { + config = new Properties(); + try (InputStream input = getClass().getClassLoader().getResourceAsStream(CONFIG_FILE)) { + if (input == null) { + throw new RuntimeException("无法找到配置文件: " + CONFIG_FILE); + } + config.load(input); + System.out.println("配置文件加载成功: " + CONFIG_FILE); + + // 初始化跨平台配置 + initializeCrossPlatformConfig(); + + // 加载排除依赖配置 + loadExcludePackages(); + + } catch (IOException e) { + throw new RuntimeException("加载配置文件失败: " + e.getMessage(), e); + } + } + + /** + * 获取配置值 + */ + private String getConfig(String key) { + return config.getProperty(key); + } + + /** + * 获取布尔配置值 + */ + private boolean getBooleanConfig(String key) { + return Boolean.parseBoolean(config.getProperty(key, "false")); + } + + /** + * 初始化跨平台配置 + */ + private void initializeCrossPlatformConfig() { + // 路径分隔符配置 + String pathSepStrategy = getConfig("path.separator.strategy"); + if ("auto".equals(pathSepStrategy) || pathSepStrategy == null) { + this.pathSeparator = File.separator; + } else if ("unix".equals(pathSepStrategy)) { + this.pathSeparator = "/"; + } else if ("windows".equals(pathSepStrategy)) { + this.pathSeparator = "\\"; + } else { + this.pathSeparator = File.separator; + } + + // 文件编码配置 + this.fileEncoding = getConfig("file.encoding"); + if (this.fileEncoding == null || this.fileEncoding.trim().isEmpty()) { + this.fileEncoding = "UTF-8"; + } + + // 换行符配置 + String lineSepStrategy = getConfig("line.separator.strategy"); + if ("auto".equals(lineSepStrategy) || lineSepStrategy == null) { + this.lineSeparator = System.lineSeparator(); + } else if ("unix".equals(lineSepStrategy)) { + this.lineSeparator = "\n"; + } else if ("windows".equals(lineSepStrategy)) { + this.lineSeparator = "\r\n"; + } else { + this.lineSeparator = System.lineSeparator(); + } + + System.out.println("跨平台配置初始化完成:"); + System.out.println(" 路径分隔符: " + this.pathSeparator); + System.out.println(" 文件编码: " + this.fileEncoding); + System.out.println(" 换行符: " + (this.lineSeparator.equals("\n") ? "LF" : "CRLF")); + } + + /** + * 加载排除依赖配置 + */ + private void loadExcludePackages() { + excludePackages.clear(); + + // 加载排除的依赖包配置 + String excludeConfig = getConfig("exclude.packages"); + if (excludeConfig != null && !excludeConfig.trim().isEmpty()) { + String[] packages = excludeConfig.split(","); + for (String pkg : packages) { + String trimmedPkg = pkg.trim(); + if (!trimmedPkg.isEmpty()) { + excludePackages.add(trimmedPkg); + System.out.println(" 排除依赖: " + trimmedPkg); + } + } + } + + System.out.println("排除依赖配置加载完成,共 " + excludePackages.size() + " 个排除规则"); + } + + @Test + @DisplayName("Maven模块重构") + public void refactorMavenModules() { + try { + // 加载配置 + loadConfig(); + + System.out.println("开始Maven模块重构..."); + + // 验证配置 + validateConfig(); + + // 分析项目依赖关系 + analyzeProjectDependencies(); + + // 执行重构renamePackageDirectories + executeRefactoring(); + + System.out.println("\n=== 重构完成!==="); + System.out.println("重构后的项目位于: " + getConfig("output.project.root")); + System.out.println("请在新目录中验证项目是否正常工作"); + + } catch (Exception e) { + System.err.println("重构过程中出现错误: " + e.getMessage()); + e.printStackTrace(); + } + } + + + + /** + * 验证配置 + */ + private void validateConfig() { + String sourceRoot = getConfig("source.project.root"); + String outputRoot = getConfig("output.project.root"); + String oldGroupId = getConfig("old.groupId"); + String newGroupId = getConfig("new.groupId"); + + if (sourceRoot == null || sourceRoot.trim().isEmpty()) { + throw new RuntimeException("source.project.root 配置不能为空"); + } + + if (outputRoot == null || outputRoot.trim().isEmpty()) { + throw new RuntimeException("output.project.root 配置不能为空"); + } + + if (!Files.exists(Paths.get(sourceRoot))) { + throw new RuntimeException("源项目目录不存在: " + sourceRoot); + } + + if (oldGroupId == null || oldGroupId.trim().isEmpty()) { + throw new RuntimeException("old.groupId 配置不能为空"); + } + + if (newGroupId == null || newGroupId.trim().isEmpty()) { + throw new RuntimeException("new.groupId 配置不能为空"); + } + + if (oldGroupId.equals(newGroupId)) { + throw new RuntimeException("新旧GroupId不能相同"); + } + + System.out.println("配置验证通过"); + } + + /** + * 分析项目依赖关系 + */ + private void analyzeProjectDependencies() { + try { + System.out.println("\n=== 分析项目依赖关系 ==="); + + // 清空缓存 + internalPackages.clear(); + internalArtifacts.clear(); + artifactToPackageMap.clear(); + + // 收集所有pom.xml文件 + List pomFiles = collectPomFiles(); + System.out.println("找到 " + pomFiles.size() + " 个pom.xml文件"); + + // 分析每个pom文件 + for (Path pomFile : pomFiles) { + analyzePomFile(pomFile); + } + + // 如果没有分析出内部包,进行额外的包扫描 + if (internalPackages.isEmpty()) { + System.out.println("未从POM文件分析出内部包,开始扫描Java源码..."); + scanJavaSourcesForInternalPackages(); + } + + System.out.println("分析完成:"); + System.out.println("- 内部模块: " + internalArtifacts.size() + " 个"); + System.out.println("- 内部包前缀: " + internalPackages.size() + " 个"); + if (!internalPackages.isEmpty()) { + System.out.println("- 内部包列表: " + internalPackages); + } + + } catch (Exception e) { + System.err.println("依赖分析失败: " + e.getMessage()); + throw new RuntimeException("依赖分析失败", e); + } + } + + /** + * 执行重构 + */ + private void executeRefactoring() throws IOException { + String sourceRoot = getConfig("source.project.root"); + String outputRoot = getConfig("output.project.root"); + + Path sourcePath = Paths.get(sourceRoot); + Path outputPath = Paths.get(outputRoot); + + // 创建输出目录 + if (Files.exists(outputPath)) { + System.out.println("输出目录已存在,将清空: " + outputPath); + deleteDirectory(outputPath); + } + Files.createDirectories(outputPath); + + // 复制项目到输出目录 + System.out.println("\n=== 复制项目到输出目录 ==="); + copyProject(sourcePath, outputPath); + + // 修改输出目录中的文件 + System.out.println("\n=== 修改项目文件 ==="); + modifyProjectInOutputDirectory(outputPath); + } + + /** + * 复制项目到输出目录 + */ + private void copyProject(Path source, Path target) throws IOException { + Set excludeDirs = getExcludeDirectories(); + Set excludeFiles = getExcludeFiles(); + + Files.walkFileTree(source, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + // 添加空值检查,防止根目录导致的空指针异常 + Path fileName = dir.getFileName(); + if (fileName != null) { + String dirName = fileName.toString(); + if (excludeDirs.contains(dirName)) { + return FileVisitResult.SKIP_SUBTREE; + } + } + + Path targetDir = target.resolve(source.relativize(dir)); + Files.createDirectories(targetDir); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + // 添加空值检查,防止特殊文件导致的空指针异常 + Path fileName = file.getFileName(); + if (fileName == null) { + return FileVisitResult.CONTINUE; + } + + String fileNameStr = fileName.toString(); + + // 检查是否为排除文件 + if (shouldExcludeFile(fileNameStr, excludeFiles)) { + System.out.println("跳过排除文件: " + fileNameStr); + return FileVisitResult.CONTINUE; + } + + Path targetFile = target.resolve(source.relativize(file)); + Files.copy(file, targetFile, StandardCopyOption.REPLACE_EXISTING); + return FileVisitResult.CONTINUE; + } + }); + + System.out.println("项目复制完成"); + } + + /** + * 修改输出目录中的项目文件 + */ + private void modifyProjectInOutputDirectory(Path projectRoot) throws IOException { + // 1. 修改所有pom.xml文件 + List pomFiles = collectPomFilesInDirectory(projectRoot); + System.out.println("修改 " + pomFiles.size() + " 个pom.xml文件"); + modifyPomFiles(pomFiles); + + // 2. 修改聚合POM文件中的modules引用 + System.out.println("修改聚合POM文件中的modules引用..."); + modifyAggregatorPomModules(pomFiles); + + // 3. 修改所有Java源文件 + List javaFiles = collectJavaFilesInDirectory(projectRoot); + System.out.println("修改 " + javaFiles.size() + " 个Java文件"); + modifyJavaFiles(javaFiles); + + // 4. 修改配置文件 + List configFiles = collectConfigFilesInDirectory(projectRoot); + System.out.println("修改Spring Boot配置文件..."); + modifyConfigFiles(configFiles); + + // 5. 重命名目录结构 + System.out.println("重命名目录结构"); + renameDirectoryStructure(projectRoot); + + // 6. 清理旧的包目录结构 + System.out.println("清理旧的包目录结构..."); + cleanupOldPackageDirectories(projectRoot); + + // 7. 多轮清理空目录 + if (getBooleanConfig("cleanup.empty.directories")) { + System.out.println("开始多轮清理空目录..."); + + // 进行多轮清理,确保彻底清除空目录 + for (int round = 1; round <= 3; round++) { + System.out.println("第 " + round + " 轮清理空目录..."); + int deletedCount = cleanupEmptyDirectories(projectRoot); + + if (deletedCount == 0) { + System.out.println("第 " + round + " 轮未发现空目录,清理完成"); + break; + } + } + + // 额外清理可能遗留的空包目录结构 + System.out.println("清理遗留的空包目录结构..."); + cleanupEmptyPackageStructure(projectRoot); + } + } + + /** + * 收集pom.xml文件 + */ + private List collectPomFiles() throws IOException { + return collectPomFilesInDirectory(Paths.get(getConfig("source.project.root"))); + } + + /** + * 在指定目录收集pom.xml文件 + */ + private List collectPomFilesInDirectory(Path rootPath) throws IOException { + List pomFiles = new ArrayList<>(); + Set excludeDirs = getExcludeDirectories(); + + Files.walkFileTree(rootPath, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + // 添加空值检查,防止根目录导致的空指针异常 + Path fileName = dir.getFileName(); + if (fileName != null) { + String dirName = fileName.toString(); + if (excludeDirs.contains(dirName)) { + return FileVisitResult.SKIP_SUBTREE; + } + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + // 添加空值检查,防止特殊文件导致的空指针异常 + Path fileName = file.getFileName(); + if (fileName != null && fileName.toString().equals("pom.xml")) { + pomFiles.add(file); + } + return FileVisitResult.CONTINUE; + } + }); + + return pomFiles; + } + + /** + * 在指定目录收集Java文件 + */ + private List collectJavaFilesInDirectory(Path rootPath) throws IOException { + List javaFiles = new ArrayList<>(); + Set excludeDirs = getExcludeDirectories(); + Set excludeFiles = getExcludeFiles(); + + Files.walkFileTree(rootPath, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + // 添加空值检查,防止根目录导致的空指针异常 + Path fileName = dir.getFileName(); + if (fileName != null) { + String dirName = fileName.toString(); + if (excludeDirs.contains(dirName)) { + return FileVisitResult.SKIP_SUBTREE; + } + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + // 添加空值检查,防止特殊文件导致的空指针异常 + Path fileName = file.getFileName(); + if (fileName != null) { + String fileNameStr = fileName.toString(); + + // 检查文件是否在排除列表中 + if (shouldExcludeFile(fileNameStr, excludeFiles)) { + return FileVisitResult.CONTINUE; + } + + if (fileNameStr.endsWith(".java")) { + javaFiles.add(file); + } + } + return FileVisitResult.CONTINUE; + } + }); + + return javaFiles; + } + + /** + * 收集配置文件 + */ + private List collectConfigFilesInDirectory(Path directory) throws IOException { + List configFiles = new ArrayList<>(); + Set excludeDirs = getExcludeDirectories(); + Set excludeFiles = getExcludeFiles(); + + Files.walkFileTree(directory, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + // 添加空值检查,防止根目录导致的空指针异常 + Path fileName = dir.getFileName(); + if (fileName != null) { + String dirName = fileName.toString(); + if (excludeDirs.contains(dirName)) { + return FileVisitResult.SKIP_SUBTREE; + } + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + // 添加空值检查,防止特殊文件导致的空指针异常 + Path fileName = file.getFileName(); + if (fileName == null) { + return FileVisitResult.CONTINUE; + } + + String fileNameStr = fileName.toString(); + String filePath = file.toString(); + + // 检查文件是否在排除列表中 + if (shouldExcludeFile(fileNameStr, excludeFiles)) { + return FileVisitResult.CONTINUE; + } + + // 收集Spring Boot自动配置文件和其他需要处理包名的文件 + if (fileNameStr.equals("org.springframework.boot.autoconfigure.AutoConfiguration.imports") || + fileNameStr.equals("spring.factories") || + fileNameStr.equals("application.yml") || + fileNameStr.equals("application.yaml") || + fileNameStr.equals("application.properties") || + fileNameStr.equals("bootstrap.yml") || + fileNameStr.equals("bootstrap.yaml") || + fileNameStr.equals("bootstrap.properties") || + fileNameStr.endsWith(".vm") || // 添加Velocity模板文件 + fileNameStr.endsWith(".json") || // 添加JSON文件 + fileNameStr.endsWith(".xml") || // 添加XML文件(除了pom.xml) + fileNameStr.endsWith(".ftl") || // 添加FreeMarker模板文件 + fileNameStr.endsWith(".sql") || // 添加SQL文件 + filePath.contains("META-INF") && (fileNameStr.endsWith(".imports") || fileNameStr.endsWith(".factories"))) { + // 排除pom.xml文件,因为它们有专门的处理方法 + if (!fileNameStr.equals("pom.xml")) { + configFiles.add(file); + } + } + return FileVisitResult.CONTINUE; + } + }); + + return configFiles; + } + + /** + * 分析单个pom文件 + */ + private void analyzePomFile(Path pomFile) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(pomFile.toFile()); + doc.getDocumentElement().normalize(); + + // 获取当前模块的groupId和artifactId + String groupId = getElementText(doc, "groupId"); + String artifactId = getElementText(doc, "artifactId"); + + // 如果当前模块没有groupId,尝试从parent获取 + if (groupId == null || groupId.trim().isEmpty()) { + Element parent = getFirstElement(doc, "parent"); + if (parent != null) { + groupId = getElementText(parent, "groupId"); + } + } + + if (groupId != null && artifactId != null) { + // 检查是否是项目内部模块 + String oldGroupId = getConfig("old.groupId"); + String oldArtifactPrefix = getConfig("old.artifactPrefix"); + + if (groupId.equals(oldGroupId) || + (oldArtifactPrefix != null && artifactId.startsWith(oldArtifactPrefix))) { + + internalArtifacts.add(artifactId); + + // 推断包名 + String packageName = inferPackageName(pomFile, groupId, artifactId); + if (packageName != null) { + internalPackages.add(packageName); + artifactToPackageMap.put(artifactId, packageName); + } + } + } + + } catch (Exception e) { + System.err.println("解析pom文件失败: " + pomFile + ", 错误: " + e.getMessage()); + } + } + + /** + * 推断模块的包名 + */ + private String inferPackageName(Path pomFile, String groupId, String artifactId) { + try { + // 查找src/main/java目录 + Path moduleDir = pomFile.getParent(); + Path srcMainJava = moduleDir.resolve("src" + getPathSeparator() + "main" + getPathSeparator() + "java"); + + Set foundPackages = new HashSet<>(); + + if (Files.exists(srcMainJava)) { + // 查找所有Java文件并分析其包声明 + Files.walk(srcMainJava) + .filter(path -> path.toString().endsWith(".java")) + .limit(10) // 限制扫描文件数量,提高性能 + .forEach(javaFile -> { + String packageName = extractPackageFromJavaFile(javaFile); + if (packageName != null && packageName.startsWith(groupId)) { + foundPackages.add(packageName); + } + }); + + // 如果找到了包,返回最短的包名(通常是根包) + if (!foundPackages.isEmpty()) { + return foundPackages.stream() + .min(Comparator.comparing(String::length)) + .orElse(groupId); + } + } + + // 如果找不到Java文件,使用groupId作为包名 + return groupId; + + } catch (Exception e) { + System.err.println("推断包名失败: " + pomFile + ", 错误: " + e.getMessage()); + return groupId; + } + } + + /** + * 扫描Java源码以识别内部包 + * 当POM文件分析无法识别内部包时使用此方法 + */ + private void scanJavaSourcesForInternalPackages() { + try { + Path sourceRoot = Paths.get(getConfig("source.project.root")); + String oldPackagePrefix = getConfig("old.packagePrefix"); + Set foundPackages = new HashSet<>(); + + // 查找所有Java源码目录 + Files.walkFileTree(sourceRoot, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + // 添加空值检查,防止根目录导致的空指针异常 + Path fileName = dir.getFileName(); + if (fileName != null) { + String dirName = fileName.toString(); + Set excludeDirs = getExcludeDirectories(); + if (excludeDirs.contains(dirName)) { + return FileVisitResult.SKIP_SUBTREE; + } + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + if (file.toString().endsWith(".java")) { + String packageName = extractPackageFromJavaFile(file); + if (packageName != null && packageName.startsWith(oldPackagePrefix)) { + // 提取包的根前缀 + String[] parts = packageName.split("\\."); + if (parts.length >= 3) { + // 通常取前3段作为基础包名,如 com.ruoyi.xxx + String basePackage = String.join(".", Arrays.copyOf(parts, Math.min(3, parts.length))); + foundPackages.add(basePackage); + } else { + foundPackages.add(packageName); + } + } + } + return FileVisitResult.CONTINUE; + } + }); + + // 将找到的包添加到内部包集合中 + internalPackages.addAll(foundPackages); + + System.out.println("从Java源码扫描到 " + foundPackages.size() + " 个内部包前缀: " + foundPackages); + + } catch (Exception e) { + System.err.println("扫描Java源码失败: " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * 从Java文件中提取包名 + */ + private String extractPackageFromJavaFile(Path javaFile) { + try { + String content = Files.readString(javaFile, Charset.forName(getFileEncoding())); + Pattern pattern = Pattern.compile("package\\s+([a-zA-Z0-9_.]+)\\s*;"); + Matcher matcher = pattern.matcher(content); + if (matcher.find()) { + return matcher.group(1); + } + } catch (Exception e) { + // 忽略错误 + } + return null; + } + + /** + * 修改pom文件 + */ + private void modifyPomFiles(List pomFiles) { + String oldGroupId = getConfig("old.groupId"); + String newGroupId = getConfig("new.groupId"); + String oldArtifactPrefix = getConfig("old.artifactPrefix"); + String newArtifactPrefix = getConfig("new.artifactPrefix"); + + int modifiedCount = 0; + + for (Path pomFile : pomFiles) { + try { + String content = Files.readString(pomFile, Charset.forName(getFileEncoding())); + String originalContent = content; + String modifiedContent = content; + + // 修改groupId + modifiedContent = modifiedContent.replaceAll( + "" + Pattern.quote(oldGroupId) + "", + "" + newGroupId + "" + ); + + // 修改artifactId中的前缀 + if (oldArtifactPrefix != null && newArtifactPrefix != null) { + Pattern artifactPattern = Pattern.compile("(" + Pattern.quote(oldArtifactPrefix) + "[^<]*)"); + Matcher artifactMatcher = artifactPattern.matcher(modifiedContent); + StringBuffer sb = new StringBuffer(); + + while (artifactMatcher.find()) { + String oldArtifactId = artifactMatcher.group(1); + String newArtifactId = oldArtifactId.replace(oldArtifactPrefix, newArtifactPrefix); + artifactMatcher.appendReplacement(sb, "" + newArtifactId + ""); + } + artifactMatcher.appendTail(sb); + modifiedContent = sb.toString(); + + // 修改依赖中的artifactId前缀 + modifiedContent = modifiedContent.replaceAll( + "" + Pattern.quote(oldArtifactPrefix), + "" + newArtifactPrefix + ); + } + + // 只有内容发生变化时才写回文件 + if (!originalContent.equals(modifiedContent)) { + Files.writeString(pomFile, modifiedContent, Charset.forName(getFileEncoding())); + modifiedCount++; + } + + } catch (IOException e) { + System.err.println("修改pom文件失败: " + pomFile + ", 错误: " + e.getMessage()); + } + } + + System.out.println("POM文件修改完成,共修改了 " + modifiedCount + " 个文件"); + } + + /** + * 修改Java文件 + */ + private void modifyJavaFiles(List javaFiles) { + String oldPackagePrefix = getConfig("old.packagePrefix"); + String newPackagePrefix = getConfig("new.packagePrefix"); + + int modifiedCount = 0; + + for (Path javaFile : javaFiles) { + try { + String content = Files.readString(javaFile, Charset.forName(getFileEncoding())); + String originalContent = content; + String modifiedContent = modifyJavaFileIntelligently(content, oldPackagePrefix, newPackagePrefix); + + // 如果内容有变化,写回文件 + if (!originalContent.equals(modifiedContent)) { + Files.writeString(javaFile, modifiedContent, Charset.forName(getFileEncoding())); + modifiedCount++; + } + + } catch (IOException e) { + System.err.println("修改Java文件失败: " + javaFile + ", 错误: " + e.getMessage()); + } + } + + System.out.println("Java文件修改完成,共修改了 " + modifiedCount + " 个文件"); + } + + /** + * 修改配置文件 + */ + private void modifyConfigFiles(List configFiles) { + String oldPackagePrefix = getConfig("old.packagePrefix"); + String newPackagePrefix = getConfig("new.packagePrefix"); + String oldGroupId = getConfig("old.groupId"); + String newGroupId = getConfig("new.groupId"); + String oldArtifactPrefix = getConfig("old.artifactPrefix"); + String newArtifactPrefix = getConfig("new.artifactPrefix"); + + int modifiedCount = 0; + + for (Path configFile : configFiles) { + try { + String content = Files.readString(configFile, Charset.forName(getFileEncoding())); + String originalContent = content; + String modifiedContent = content; + + // 修改包名引用 + if (oldPackagePrefix != null && newPackagePrefix != null) { + // 修改包名,只修改项目内部包 + modifiedContent = modifyConfigPackageReferences(modifiedContent, oldPackagePrefix, newPackagePrefix); + } + + // 修改groupId引用 + if (oldGroupId != null && newGroupId != null) { + modifiedContent = modifiedContent.replace(oldGroupId, newGroupId); + } + + // 修改artifactId引用 + if (oldArtifactPrefix != null && newArtifactPrefix != null) { + modifiedContent = modifiedContent.replaceAll(oldArtifactPrefix + "([a-zA-Z0-9-]*)", newArtifactPrefix + "$1"); + } + + // 如果内容有变化,写回文件 + if (!originalContent.equals(modifiedContent)) { + Files.writeString(configFile, modifiedContent, Charset.forName(getFileEncoding())); + modifiedCount++; + System.out.println(" 修改配置文件: " + configFile.getFileName()); + } + + } catch (IOException e) { + System.err.println("修改配置文件失败: " + configFile + ", 错误: " + e.getMessage()); + } + } + + System.out.println("配置文件修改完成,共修改了 " + modifiedCount + " 个文件"); + } + + /** + * 修改配置文件中的包名引用 + */ + private String modifyConfigPackageReferences(String content, String oldPackagePrefix, String newPackagePrefix) { + String[] lines = content.split("\n"); + StringBuilder result = new StringBuilder(); + + for (String line : lines) { + String trimmedLine = line.trim(); + String modifiedLine = line; + + // 处理Spring Boot自动配置文件中的类名 + if (trimmedLine.startsWith(oldPackagePrefix) && isInternalPackage(trimmedLine)) { + modifiedLine = applyGlobalPackageReplacementToLine(line, oldPackagePrefix, newPackagePrefix); + } + // 处理包含包名的行 + else if (trimmedLine.contains(oldPackagePrefix)) { + // 对于配置文件,检查是否是包名配置 + if (isPackageConfiguration(trimmedLine, oldPackagePrefix)) { + modifiedLine = applyGlobalPackageReplacementToLine(line, oldPackagePrefix, newPackagePrefix); + } + // 对于其他文件类型(如.vm、.json、.xml等),直接应用包名映射 + else { + modifiedLine = applyPackageMappingToLine(line, oldPackagePrefix, newPackagePrefix); + } + } + + result.append(modifiedLine); + if (!modifiedLine.equals(lines[lines.length - 1])) { + result.append("\n"); + } + } + + return result.toString(); + } + + /** + * 判断是否是包名配置 + */ + /** + * 对单行内容应用包名映射 + */ + private String applyPackageMappingToLine(String line, String oldPackagePrefix, String newPackagePrefix) { + String modifiedLine = line; + + // 使用正则表达式匹配所有包名 + Pattern packagePattern = Pattern.compile("\\b" + Pattern.quote(oldPackagePrefix) + "(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)*\\b"); + Matcher matcher = packagePattern.matcher(modifiedLine); + StringBuffer sb = new StringBuffer(); + + while (matcher.find()) { + String foundPackage = matcher.group(); + boolean shouldExclude = false; + + // 检查是否应该排除这个包名 + for (String excludePackage : excludePackages) { + if (foundPackage.equals(excludePackage) || foundPackage.startsWith(excludePackage + ".")) { + shouldExclude = true; + break; + } + } + + if (shouldExclude) { + // 保持原样,不替换 + matcher.appendReplacement(sb, Matcher.quoteReplacement(foundPackage)); + } else if (isInternalPackage(foundPackage)) { + // 应用通用前缀替换 + String newPackage = foundPackage.replace(oldPackagePrefix, newPackagePrefix); + matcher.appendReplacement(sb, Matcher.quoteReplacement(newPackage)); + } else { + matcher.appendReplacement(sb, Matcher.quoteReplacement(foundPackage)); + } + } + matcher.appendTail(sb); + + return sb.toString(); + } + + private boolean isPackageConfiguration(String line, String packagePrefix) { + String trimmed = line.trim(); + + // 跳过注释行(支持多种注释格式) + if (trimmed.startsWith("#") || trimmed.startsWith("//") || + trimmed.startsWith("/*") || trimmed.startsWith("