1. 替换原始 code 签名

2. 服务与模块生成器,支持指定起始端口批量端口分配

(cherry picked from commit e99da38fd4)
This commit is contained in:
chenbowen
2025-09-02 10:15:16 +08:00
committed by chenbowen
parent 9929ae8a39
commit 1e1216c150
778 changed files with 876 additions and 801 deletions

View File

@@ -12,7 +12,7 @@ import java.io.IOException;
/**
* Redis 测试 Configuration主要实现内嵌 Redis 的启动
*
* @author 芋道源码
* @author ZT
*/
@Configuration(proxyBeanMethods = false)
@Lazy(false) // 禁止延迟加载

View File

@@ -21,7 +21,7 @@ import javax.sql.DataSource;
* 因为我们在单元测试会使用 spring.main.lazy-initialization 为 true开启延迟加载。此时会导致 DataSourceInitializationConfiguration 初始化
* 不过呢,当前类的实现代码,基本是复制 DataSourceInitializationConfiguration 的哈!
*
* @author 芋道源码
* @author ZT
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(AbstractScriptDatabaseInitializer.class)

View File

@@ -24,7 +24,7 @@ import org.springframework.test.context.jdbc.Sql;
*
* 相比 {@link BaseDbUnitTest} 来说,额外增加了内存 Redis
*
* @author 芋道源码
* @author ZT
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisUnitTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件

View File

@@ -22,7 +22,7 @@ import org.springframework.test.context.jdbc.Sql;
*
* 注意Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法
*
* @author 芋道源码
* @author ZT
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件

View File

@@ -8,7 +8,7 @@ import org.springframework.boot.test.mock.mockito.MockBean;
/**
* 纯 Mockito 的单元测试
*
* @author 芋道源码
* @author ZT
*/
@ExtendWith(MockitoExtension.class)
public class BaseMockitoUnitTest {

View File

@@ -17,7 +17,7 @@ import org.springframework.test.context.ActiveProfiles;
*
* 相比 {@link BaseDbUnitTest} 来说,从内存 DB 改成了内存 Redis
*
* @author 芋道源码
* @author ZT
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisUnitTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件

View File

@@ -15,7 +15,7 @@ import java.util.stream.Stream;
* 2. 根据提示,输入模块名,例如 "order"
* 3. 根据提示,输入作者名,例如 "yudao"
*
* @author 芋道源码
* @author ZT
*/
public class ModuleGenerator {
@@ -29,6 +29,8 @@ public class ModuleGenerator {
String moduleNames = scanner.nextLine();
System.out.print("请输入作者名称例如yudao: ");
String author = scanner.nextLine();
System.out.print("请输入起始端口号例如8080: ");
int startPort = Integer.parseInt(scanner.nextLine());
scanner.close();
// 分割模块名
@@ -44,14 +46,17 @@ public class ModuleGenerator {
}
// 3. 批量创建模块
for (String moduleName : modules) {
moduleName = moduleName.trim(); // 去除空格
for (int i = 0; i < modules.length; i++) {
String moduleName = modules[i].trim(); // 去除空格
if (moduleName.isEmpty()) {
continue;
}
System.out.println("\n=== 开始创建模块: " + moduleName + " ===");
createSingleModule(templateDir, projectRoot, moduleName, author);
// 计算当前模块的端口号:起始端口 + 当前模块索引
int modulePort = startPort + i;
System.out.println("\n=== 开始创建模块: " + moduleName + " (端口: " + modulePort + ") ===");
createSingleModule(templateDir, projectRoot, moduleName, author, modulePort);
}
System.out.println("\n所有模块创建完成!");
@@ -65,7 +70,7 @@ public class ModuleGenerator {
}
}
private static void createSingleModule(Path templateDir, Path projectRoot, String moduleName, String author) throws IOException {
private static void createSingleModule(Path templateDir, Path projectRoot, String moduleName, String author, int port) throws IOException {
String packageName = moduleName.replace("-", "");
String capitalizedModuleName = capitalize(packageName);
@@ -83,15 +88,18 @@ public class ModuleGenerator {
System.out.println("将在以下位置创建新模块: " + newModuleDir);
// 复制并处理文件
copyAndProcessDirectory(templateDir, newModuleDir, dashModuleName, packageName, capitalizedModuleName, author);
copyAndProcessDirectory(templateDir, newModuleDir, dashModuleName, packageName, capitalizedModuleName, author, port);
// 创建启动类
createApplicationClass(newModuleDir, dashModuleName, packageName, capitalizedModuleName, author);
// 创建配置文件
createApplicationYml(newModuleDir, dashModuleName, port, author);
System.out.println("模块 '" + dashModuleName + "' 创建成功!");
}
private static void copyAndProcessDirectory(Path sourceDir, Path targetDir, String moduleName, String packageName, String capitalizedModuleName, String author) throws IOException {
private static void copyAndProcessDirectory(Path sourceDir, Path targetDir, String moduleName, String packageName, String capitalizedModuleName, String author, int port) throws IOException {
try (Stream<Path> stream = Files.walk(sourceDir)) {
stream.filter(path -> shouldIncludePath(sourceDir.relativize(path)))
.forEach(sourcePath -> {
@@ -112,7 +120,7 @@ public class ModuleGenerator {
// 确保父目录存在
Files.createDirectories(targetPath.getParent());
String content = new String(Files.readAllBytes(sourcePath));
String newContent = processFileContent(content, sourcePath.getFileName().toString(), moduleName, packageName, capitalizedModuleName, author);
String newContent = processFileContent(content, sourcePath.getFileName().toString(), moduleName, packageName, capitalizedModuleName, author, port);
Files.write(targetPath, newContent.getBytes());
}
} catch (IOException e) {
@@ -144,7 +152,7 @@ public class ModuleGenerator {
return pathStr.contains("example") || pathStr.contains("ErrorCodeConstants");
}
private static String processFileContent(String content, String fileName, String moduleName, String packageName, String capitalizedModuleName, String author) {
private static String processFileContent(String content, String fileName, String moduleName, String packageName, String capitalizedModuleName, String author, int port) {
// 如果是 ErrorCodeConstants 文件,特殊处理
if (fileName.equals("ErrorCodeConstants.java")) {
return processErrorCodeConstants(content, moduleName, packageName, author);
@@ -211,6 +219,35 @@ public class ModuleGenerator {
System.out.println("创建启动类: " + applicationFile);
}
/**
* 创建配置文件 application.yml
*/
private static void createApplicationYml(Path moduleDir, String dashModuleName, int port, String author) throws IOException {
// 创建配置文件目录
Path resourcesDir = moduleDir.resolve("yudao-module-" + dashModuleName + "-server")
.resolve("src/main/resources");
Files.createDirectories(resourcesDir);
// 创建配置文件
Path configFile = resourcesDir.resolve("application.yml");
StringBuilder sb = new StringBuilder();
sb.append("server:\n");
sb.append(" port: ").append(port).append("\n\n");
sb.append("spring:\n");
sb.append(" application:\n");
sb.append(" name: yudao-module-").append(dashModuleName).append("-server\n\n");
sb.append("# 模块配置\n");
sb.append("yudao:\n");
sb.append(" info:\n");
sb.append(" version: 1.0.0\n");
sb.append(" base-package: cn.iocoder.yudao.module.").append(dashModuleName.replace("-", "")).append("\n");
sb.append(" author: ").append(author).append("\n");
Files.write(configFile, sb.toString().getBytes());
System.out.println("创建配置文件: " + configFile);
}
private static String capitalize(String str) {
if (str == null || str.isEmpty()) {
return str;

View File

@@ -17,7 +17,7 @@ import java.util.stream.Stream;
* 2. 根据提示,输入新 Server 的名称,例如 "demo-server"
* 3. 根据提示,输入作者名,例如 "yudao"
*
* @author 芋道源码
* @author ZT
*/
public class ServerGenerator {
@@ -27,13 +27,20 @@ public class ServerGenerator {
public static void main(String[] args) throws IOException {
// 1. 获取用户输入
Scanner scanner = new Scanner(System.in);
System.out.print("请输入新 Server 的基础名称(小写短横线例如demo: ");
String baseName = scanner.nextLine();
String serverName = baseName + "-server";
System.out.print("请输入新 Server 的基础名称(支持多个服务器用逗号分割例如demo,admin,api: ");
String baseNames = scanner.nextLine();
System.out.print("请输入作者名称例如yudao: ");
String author = scanner.nextLine();
System.out.print("请输入起始端口号例如8080: ");
int startPort = Integer.parseInt(scanner.nextLine());
scanner.close();
// 分割服务器名
String[] servers = baseNames.split(",");
// 分割服务器名
String[] servers = baseNames.split(",");
// 2. 定义项目根路径
// 注意:请在项目的根目录(例如 ztcloud下运行该生成器
Path projectRoot = Paths.get("").toAbsolutePath();
@@ -45,15 +52,33 @@ public class ServerGenerator {
return;
}
System.out.println("\n=== 开始创建 Server: " + serverName + " ===");
createSingleServer(templateDir, projectRoot, serverName, author);
System.out.println("\nServer '" + serverName + "' 创建成功!");
// 3. 批量创建服务器
for (int i = 0; i < servers.length; i++) {
String baseName = servers[i].trim(); // 去除空格
if (baseName.isEmpty()) {
continue;
}
String serverName = baseName + "-server";
// 计算当前服务器的端口号:起始端口 + 当前服务器索引
int serverPort = startPort + i;
System.out.println("\n=== 开始创建 Server: " + serverName + " (端口: " + serverPort + ") ===");
createSingleServer(templateDir, projectRoot, serverName, author, serverPort);
}
System.out.println("\n所有服务器创建完成!");
System.out.println("请手动将以下模块添加到根 pom.xml 的 <modules> 标签中:");
System.out.println("<module>" + serverName + "</module>");
for (String baseName : servers) {
baseName = baseName.trim();
if (!baseName.isEmpty()) {
String serverName = baseName + "-server";
System.out.println("<module>" + serverName + "</module>");
}
}
}
private static void createSingleServer(Path templateDir, Path projectRoot, String serverName, String author) throws IOException {
private static void createSingleServer(Path templateDir, Path projectRoot, String serverName, String author, int port) throws IOException {
String packageName = serverName.replace("-", "");
String capitalizedName = toPascalCase(serverName);
@@ -68,10 +93,10 @@ public class ServerGenerator {
System.out.println("将在以下位置创建新 Server: " + newServerDir);
// 复制并处理文件
copyAndProcessDirectory(templateDir, newServerDir, serverName, packageName, capitalizedName, author);
copyAndProcessDirectory(templateDir, newServerDir, serverName, packageName, capitalizedName, author, port);
}
private static void copyAndProcessDirectory(Path sourceDir, Path targetDir, String serverName, String packageName, String capitalizedName, String author) throws IOException {
private static void copyAndProcessDirectory(Path sourceDir, Path targetDir, String serverName, String packageName, String capitalizedName, String author, int port) throws IOException {
try (Stream<Path> stream = Files.walk(sourceDir)) {
stream.forEach(sourcePath -> {
try {
@@ -94,7 +119,7 @@ public class ServerGenerator {
} else {
Files.createDirectories(targetPath.getParent());
String content = new String(Files.readAllBytes(sourcePath));
String newContent = processFileContent(content, sourcePath.getFileName().toString(), serverName, packageName, capitalizedName, author);
String newContent = processFileContent(content, sourcePath.getFileName().toString(), serverName, packageName, capitalizedName, author, port);
Files.write(targetPath, newContent.getBytes());
}
} catch (IOException e) {
@@ -114,7 +139,7 @@ public class ServerGenerator {
&& !pathStr.contains(".flattened-pom.xml");
}
private static String processFileContent(String content, String fileName, String serverName, String packageName, String capitalizedName, String author) {
private static String processFileContent(String content, String fileName, String serverName, String packageName, String capitalizedName, String author, int port) {
String newContent = content.replace("芋道源码", author);
switch (fileName) {
@@ -124,7 +149,20 @@ public class ServerGenerator {
// 移除对 yudao-module-xxx-server 的依赖,但保留 system-server 和 infra-server
return newContent.replaceAll("(?m)^\\s*<dependency>\\s*<groupId>cn\\.iocoder\\.cloud</groupId>\\s*<artifactId>yudao-module-(?!system-server|infra-server).*-server</artifactId>[\\s\\S]*?</dependency>\\s*", "");
case "application.yaml":
return newContent.replace("name: yudao-server", "name: " + serverName);
case "application.yml":
// 替换应用名称和端口号
newContent = newContent.replace("name: yudao-server", "name: " + serverName);
// 如果有端口配置,替换端口号,如果没有则添加端口配置
if (newContent.contains("server:")) {
if (newContent.contains("port:")) {
newContent = newContent.replaceAll("(?m)^\\s*port:.*$", " port: " + port);
} else {
newContent = newContent.replace("server:", "server:\n port: " + port);
}
} else {
newContent = "server:\n port: " + port + "\n\n" + newContent;
}
return newContent;
case "YudaoServerApplication.java":
return newContent.replace("package cn.iocoder.yudao.server;", "package " + BASE_PACKAGE_PREFIX + packageName + ";")
.replace("YudaoServerApplication", capitalizedName + "Application");

View File

@@ -17,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* 单元测试assert 断言工具类
*
* @author 芋道源码
* @author ZT
*/
public class AssertUtils {

View File

@@ -21,7 +21,7 @@ import java.util.stream.Stream;
/**
* 随机工具类
*
* @author 芋道源码
* @author ZT
*/
public class RandomUtils {