1. 重构模块与服务的生成器,使用模板的方式进行支持

This commit is contained in:
chenbowen
2025-09-02 17:58:48 +08:00
parent 81f3387195
commit 7c45ea5a19
29 changed files with 1980 additions and 316 deletions

View File

@@ -56,5 +56,10 @@
<groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 -->
<artifactId>podam</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,70 @@
package cn.iocoder.yudao.framework.test.core.ut;
import java.util.Arrays;
import java.util.stream.Collectors;
/**
* 代码生成器通用工具类
*
* @author ZT
*/
public class GeneratorUtils {
/**
* 首字母大写
*
* @param str 字符串
* @return 首字母大写后的字符串
*/
public static String capitalize(String str) {
if (str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
/**
* 将小驼峰命名转换为短横线分割的命名
* 例如orderManagement -> order-management
*
* @param camelCase 驼峰命名的字符串
* @return 短横线分割的字符串
*/
public static String camelToKebabCase(String camelCase) {
if (camelCase == null || camelCase.isEmpty()) {
return camelCase;
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < camelCase.length(); i++) {
char c = camelCase.charAt(i);
if (Character.isUpperCase(c) && i > 0) {
result.append('-');
}
result.append(Character.toLowerCase(c));
}
return result.toString();
}
/**
* 将 kebab-case 转换为 PascalCase
* 例如demo-server -> DemoServer
*
* @param kebabCase 短横线分割的字符串
* @return 帕斯卡命名的字符串
*/
public static String toPascalCase(String kebabCase) {
if (kebabCase == null || kebabCase.isEmpty()) {
return "";
}
return Arrays.stream(kebabCase.split("-"))
.map(s -> {
if (s.isEmpty()) {
return "";
}
return s.substring(0, 1).toUpperCase() + s.substring(1);
})
.collect(Collectors.joining());
}
}

View File

@@ -0,0 +1,65 @@
package cn.iocoder.yudao.framework.test.core.ut;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.engine.velocity.VelocityEngine;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
/**
* 模板引擎工具类,用于代码生成
*
* @author ZT
*/
public class TemplateEngine {
private final cn.hutool.extra.template.TemplateEngine velocityEngine;
public TemplateEngine() {
TemplateConfig config = new TemplateConfig();
config.setResourceMode(TemplateConfig.ResourceMode.CLASSPATH);
this.velocityEngine = new VelocityEngine(config);
}
/**
* 渲染模板
*
* @param templatePath 模板路径(相对于 resources 目录)
* @param variables 变量映射
* @return 渲染后的内容
*/
public String render(String templatePath, Map<String, Object> variables) {
return velocityEngine.getTemplate(templatePath).render(variables);
}
/**
* 渲染模板并写入文件
*
* @param templatePath 模板路径(相对于 resources 目录)
* @param outputPath 输出文件路径
* @param variables 变量映射
* @throws IOException IO异常
*/
public void renderToFile(String templatePath, Path outputPath, Map<String, Object> variables) throws IOException {
String content = render(templatePath, variables);
Files.createDirectories(outputPath.getParent());
Files.write(outputPath, content.getBytes());
}
/**
* 检查模板文件是否存在
*
* @param templatePath 模板路径
* @return 是否存在
*/
public boolean templateExists(String templatePath) {
try {
velocityEngine.getTemplate(templatePath);
return true;
} catch (Exception e) {
return false;
}
}
}

View File

@@ -0,0 +1,29 @@
package ${basePackage}.module.${packageName}.controller.admin.${baseName};
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ${basePackage}.framework.common.pojo.CommonResult;
import static ${basePackage}.framework.common.pojo.CommonResult.success;
/**
* ${capitalizedModuleName} 控制器
*
* @author ${author}
*/
@Tag(name = "管理后台 - ${capitalizedModuleName}")
@RestController
@RequestMapping("/admin/${dashModuleName}/${baseName}")
public class ${capitalizedModuleName}Controller {
@GetMapping("/hello")
@Operation(summary = "Hello ${capitalizedModuleName}")
public CommonResult<String> hello() {
return success("Hello, ${capitalizedModuleName}!");
}
}

View File

@@ -0,0 +1,17 @@
package ${basePackage}.module.${packageName}.enums;
import ${basePackage}.framework.common.exception.ErrorCode;
/**
* ${dashModuleName} 错误码枚举类
*
* ${dashModuleName} 系统,使用 1-xxx-xxx-xxx 段
*
* @author ${author}
*/
public interface ErrorCodeConstants {
// ========== 示例模块 1-001-000-000 ==========
ErrorCode EXAMPLE_NOT_EXISTS = new ErrorCode(1_001_000_001, "示例不存在");
}

View File

@@ -0,0 +1,42 @@
package ${basePackage}.module.${packageName}.framework.security.config;
import ${basePackage}.framework.security.config.AuthorizeRequestsCustomizer;
import ${basePackage}.module.infra.enums.ApiConstants;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
/**
* ${capitalizedModuleName} 模块的 Security 配置
*
* @author ${author}
*/
@Configuration(proxyBeanMethods = false)
public class SecurityConfiguration {
@Bean
public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
return new AuthorizeRequestsCustomizer() {
@Override
public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
// Swagger 接口文档
registry.requestMatchers("/v3/api-docs/**").permitAll()
.requestMatchers("/webjars/**").permitAll()
.requestMatchers("/swagger-ui").permitAll()
.requestMatchers("/swagger-ui/**").permitAll();
// Druid 监控
registry.requestMatchers("/druid/**").permitAll();
// Spring Boot Actuator 的安全配置
registry.requestMatchers("/actuator").permitAll()
.requestMatchers("/actuator/**").permitAll();
// RPC 服务的安全配置
registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
}
};
}
}

View File

@@ -0,0 +1,18 @@
package ${basePackage}.module.${packageName};
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* ${moduleDescription}的启动类
*
* @author ${author}
*/
@SpringBootApplication
public class ${applicationClassName} {
public static void main(String[] args) {
SpringApplication.run(${applicationClassName}.class, args);
}
}

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>yudao-module-${dashModuleName}</artifactId>
<groupId>cn.iocoder.cloud</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-module-${dashModuleName}-api</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
暴露给其它模块调用
</description>
<dependencies>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-common</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>org.springdoc</groupId> <!-- 接口文档:使用最新版本的 Swagger 模型 -->
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

Some files were not shown because too many files have changed in this diff Show More