1. 替换原始 code 签名
2. 服务与模块生成器,支持指定起始端口批量端口分配
This commit is contained in:
@@ -12,7 +12,7 @@ import java.io.IOException;
|
||||
/**
|
||||
* Redis 测试 Configuration,主要实现内嵌 Redis 的启动
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @author ZT
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Lazy(false) // 禁止延迟加载
|
||||
|
||||
@@ -21,7 +21,7 @@ import javax.sql.DataSource;
|
||||
* 因为我们在单元测试会使用 spring.main.lazy-initialization 为 true,开启延迟加载。此时,会导致 DataSourceInitializationConfiguration 初始化
|
||||
* 不过呢,当前类的实现代码,基本是复制 DataSourceInitializationConfiguration 的哈!
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @author ZT
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(AbstractScriptDatabaseInitializer.class)
|
||||
|
||||
@@ -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 配置文件
|
||||
|
||||
@@ -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 配置文件
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
/**
|
||||
* 纯 Mockito 的单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @author ZT
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class BaseMockitoUnitTest {
|
||||
|
||||
@@ -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 配置文件
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -17,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
/**
|
||||
* 单元测试,assert 断言工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @author ZT
|
||||
*/
|
||||
public class AssertUtils {
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.stream.Stream;
|
||||
/**
|
||||
* 随机工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @author ZT
|
||||
*/
|
||||
public class RandomUtils {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user