diff --git a/demo-server/Dockerfile b/demo-server/Dockerfile new file mode 100644 index 00000000..10275d6f --- /dev/null +++ b/demo-server/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:17-jre-slim + +# 设置应用目录 +WORKDIR /app + +# 复制应用文件 +COPY target/demo-server.jar /app/demo-server.jar + +# 暴露端口 +EXPOSE 48100 + +# 运行应用 +ENTRYPOINT ["java", "-jar", "/app/demo-server.jar"] diff --git a/demo-server/pom.xml b/demo-server/pom.xml new file mode 100644 index 00000000..c9a48511 --- /dev/null +++ b/demo-server/pom.xml @@ -0,0 +1,99 @@ + + + + cn.iocoder.cloud + yudao + ${revision} + + 4.0.0 + demo-server + jar + + demo-server + Demo 服务器 + + + + cn.iocoder.cloud + yudao-module-system-server + ${revision} + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + cn.iocoder.cloud + yudao-module-infra-server + ${revision} + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-protection + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-rpc + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + + + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + + + diff --git a/demo-server/src/main/java/cn/iocoder/yudao/demoserver/DemoServerApplication.java b/demo-server/src/main/java/cn/iocoder/yudao/demoserver/DemoServerApplication.java new file mode 100644 index 00000000..f371b001 --- /dev/null +++ b/demo-server/src/main/java/cn/iocoder/yudao/demoserver/DemoServerApplication.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.demoserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Demo 服务器的启动类 + * + * @author chenbw + */ +@SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${yudao.info.base-package} +@SpringBootApplication(scanBasePackages = {"${yudao.info.base-package}.demoserver", "${yudao.info.base-package}.module"}, + excludeName = {}) +public class DemoServerApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoServerApplication.class, args); + } + +} diff --git a/demo-server/src/main/java/cn/iocoder/yudao/demoserver/controller/demo/DemoController.java b/demo-server/src/main/java/cn/iocoder/yudao/demoserver/controller/demo/DemoController.java new file mode 100644 index 00000000..6cc74aa2 --- /dev/null +++ b/demo-server/src/main/java/cn/iocoder/yudao/demoserver/controller/demo/DemoController.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.demoserver.controller.demo; + +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 cn.iocoder.yudao.framework.common.pojo.CommonResult; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +/** + * demo 控制器 + * + * @author chenbw + */ +@Tag(name = "demo") +@RestController +@RequestMapping("/demo") +public class DemoController { + + @GetMapping("/hello") + @Operation(summary = "Hello demo") + public CommonResult hello() { + return success("Hello, demo!"); + } + +} diff --git a/demo-server/src/main/resources/application-dev.yml b/demo-server/src/main/resources/application-dev.yml new file mode 100644 index 00000000..68d3e40e --- /dev/null +++ b/demo-server/src/main/resources/application-dev.yml @@ -0,0 +1,118 @@ +server: + servlet: + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true + +--- +spring: + # 数据源配置项 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 5 # 初始连接数 + min-idle: 10 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + # 设置默认的数据源或者数据源组,默认 master + primary: master + datasource: + # 主库 + master: + url: jdbc:mysql://127.0.0.1:3306/${dbName}?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: + # 从库 + slave: + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://127.0.0.1:3306/${dbName}?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 127.0.0.1 # 地址 + port: 6379 # 端口 + database: 1 # 数据库索引 +# password: # 密码,建议生产环境开启 + +--- +xxl: + job: + enabled: false # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址 + +--- +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: 127.0.0.1:9876 # RocketMQ Namesrv + +spring: + # RabbitMQ 配置项,对应 RabbitProperties 配置类 + rabbitmq: + host: 127.0.0.1 # RabbitMQ 服务的地址 + port: 5672 # RabbitMQ 服务的端口 + username: guest # RabbitMQ 服务的账号 + password: guest # RabbitMQ 服务的密码 + # Kafka 配置项,对应 KafkaProperties 配置类 + kafka: + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 + +--- +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# 日志文件配置 +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + level: + # 配置自己写的 MyBatis Mapper 打印日志 + cn.iocoder.yudao.demoserver.dal.mysql: debug + +--- +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + demo: false # 开启演示模式 + # 附件加密相关配置 + AES: + key: "0123456789abcdef0123456789abcdef" diff --git a/demo-server/src/main/resources/application-local.yml b/demo-server/src/main/resources/application-local.yml new file mode 100644 index 00000000..23924880 --- /dev/null +++ b/demo-server/src/main/resources/application-local.yml @@ -0,0 +1,117 @@ +server: + servlet: + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true + +--- +spring: + # 数据源配置项 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + # 设置默认的数据源或者数据源组,默认 master + primary: master + datasource: + master: + url: jdbc:dm://localhost:5236?schema=SYSDBA + username: SYSDBA + password: P@ssword25 + slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:dm://localhost:5236?schema=SYSDBA + username: SYSDBA + password: P@ssword25 + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: localhost # 地址 + port: 6379 # 端口 + database: 1 # 数据库索引 +# password: # 密码,建议生产环境开启 + +--- +xxl: + job: + enabled: false # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址 + +--- +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: localhost:9876 # RocketMQ Namesrv + +spring: + # RabbitMQ 配置项,对应 RabbitProperties 配置类 + rabbitmq: + host: 127.0.0.1 # RabbitMQ 服务的地址 + port: 5672 # RabbitMQ 服务的端口 + username: rabbit # RabbitMQ 服务的账号 + password: rabbit # RabbitMQ 服务的密码 + # Kafka 配置项,对应 KafkaProperties 配置类 + kafka: + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 + +--- +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# 日志文件配置 +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + level: + # 配置自己写的 MyBatis Mapper 打印日志 + cn.iocoder.yudao.demoserver.dal.mysql: debug + root: info + +--- +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + demo: false # 开启演示模式 + # 附件加密相关配置 + AES: + key: "0123456789abcdef0123456789abcdef" diff --git a/demo-server/src/main/resources/application.yml b/demo-server/src/main/resources/application.yml new file mode 100644 index 00000000..b4b802eb --- /dev/null +++ b/demo-server/src/main/resources/application.yml @@ -0,0 +1,109 @@ +server: + port: 48100 + +spring: + application: + name: demo-server + main: + allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 + profiles: + active: local + + # Servlet 配置 + servlet: + # 文件上传相关配置项 + multipart: + max-file-size: 16MB # 单个文件大小 + max-request-size: 32MB # 设置总上传的文件大小 + + # Jackson 配置项 + jackson: + serialization: + write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳 + write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 + fail-on-empty-beans: false # 允许序列化无属性的 Bean + + # Cache 配置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 小时 + +--- +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui + default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: true + setting: + language: zh_cn + +# MyBatis Plus 的配置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # "智能"模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制台的 Banner 打印 + type-aliases-package: cn.iocoder.yudao.demoserver.dal.dataobject + +# Spring Data Redis 配置 +spring: + data: + redis: + repositories: + enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度 + +--- +yudao: + info: + version: 1.0.0 + base-package: cn.iocoder.yudao + author: chenbw + description: Demo 服务器 + web: + admin-ui: + url: http://localhost:3000 # Admin 管理后台 UI 的地址 + xss: + enable: false + security: + permit-all_urls: [] + websocket: + enable: true # websocket的开关 + path: /infra/ws # 路径 + sender-type: local # 消息发送的类型,可选值为 local、redis、rocketmq、kafka、rabbitmq + sender-rocketmq: + topic: ${spring.application.name}-websocket # 消息发送的 RocketMQ Topic + consumer-group: ${spring.application.name}-websocket-consumer # 消息发送的 RocketMQ Consumer Group + sender-rabbitmq: + exchange: ${spring.application.name}-websocket-exchange # 消息发送的 RabbitMQ Exchange + queue: ${spring.application.name}-websocket-queue # 消息发送的 RabbitMQ Queue + sender-kafka: + topic: ${spring.application.name}-websocket # 消息发送的 Kafka Topic + consumer-group: ${spring.application.name}-websocket-consumer # 消息发送的 Kafka Consumer Group + swagger: + title: demo-server + description: Demo 服务器 + version: ${yudao.info.version} + email: xingyu4j@vip.qq.com + license: MIT + license-url: https://gitee.com/zhijiantianya/ruoyi-vue-pro/blob/master/LICENSE + codegen: + base-package: cn.iocoder.yudao.demoserver + db-schemas: ${spring.datasource.dynamic.datasource.master.name} + front-type: 20 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类 + vo-type: 10 # VO 的类型,参见 CodegenVOTypeEnum 枚举类 + delete-batch-enable: true # 是否生成批量删除接口 + unit-test-enable: false # 是否生成单元测试 + +debug: false diff --git a/yudao-framework/yudao-spring-boot-starter-test/pom.xml b/yudao-framework/yudao-spring-boot-starter-test/pom.xml index 05e5c681..d12e94d3 100644 --- a/yudao-framework/yudao-spring-boot-starter-test/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-test/pom.xml @@ -56,5 +56,10 @@ uk.co.jemos.podam podam + + + org.apache.velocity + velocity-engine-core + diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/GeneratorUtils.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/GeneratorUtils.java new file mode 100644 index 00000000..9195f2cc --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/GeneratorUtils.java @@ -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()); + } +} diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/ModuleGenerator.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/ModuleGenerator.java index e6022b3c..7935f102 100644 --- a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/ModuleGenerator.java +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/ModuleGenerator.java @@ -4,11 +4,15 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; import java.util.Scanner; -import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.test.core.ut.GeneratorUtils.camelToKebabCase; +import static cn.iocoder.yudao.framework.test.core.ut.GeneratorUtils.capitalize; /** - * Yudao 模块代码生成器 + * Yudao 模块代码生成器 - 使用 VM 模板 * * 使用方法: * 1. 在 IDE 中,右键运行 {@link #main(String[])} 方法 @@ -19,8 +23,7 @@ import java.util.stream.Stream; */ public class ModuleGenerator { - private static final String TEMPLATE_PATH = "yudao-module-template"; - private static final String BASE_PACKAGE = "cn.iocoder.yudao.module."; + private static final TemplateEngine templateEngine = new TemplateEngine(); public static void main(String[] args) throws IOException { // 1. 获取用户输入 @@ -38,12 +41,6 @@ public class ModuleGenerator { // 2. 定义项目根路径 Path projectRoot = Paths.get("").toAbsolutePath(); - Path templateDir = projectRoot.resolve(TEMPLATE_PATH); - - if (!Files.exists(templateDir)) { - System.err.println("错误:模板目录不存在: " + templateDir); - return; - } // 3. 批量创建模块 for (int i = 0; i < modules.length; i++) { @@ -56,7 +53,7 @@ public class ModuleGenerator { int modulePort = startPort + i; System.out.println("\n=== 开始创建模块: " + moduleName + " (端口: " + modulePort + ") ==="); - createSingleModule(templateDir, projectRoot, moduleName, author, modulePort); + createSingleModule(projectRoot, moduleName, author, modulePort); } System.out.println("\n所有模块创建完成!"); @@ -70,12 +67,13 @@ public class ModuleGenerator { } } - private static void createSingleModule(Path templateDir, Path projectRoot, String moduleName, String author, int port) throws IOException { - String packageName = moduleName.replace("-", ""); - String capitalizedModuleName = capitalize(packageName); - + private static void createSingleModule(Path projectRoot, String moduleName, String author, int port) throws IOException { // 将小驼峰转换为短横线分割的模块名 String dashModuleName = camelToKebabCase(moduleName); + + // packageName 应该是去掉短横线的版本,用于 Java 包名 + String packageName = dashModuleName.replace("-", ""); + String capitalizedModuleName = capitalize(packageName); // 定义新模块路径 Path newModuleDir = projectRoot.resolve("yudao-module-" + dashModuleName); @@ -87,214 +85,98 @@ public class ModuleGenerator { System.out.println("将在以下位置创建新模块: " + newModuleDir); - // 复制并处理文件 - copyAndProcessDirectory(templateDir, newModuleDir, dashModuleName, packageName, capitalizedModuleName, author, port); + // 准备模板变量 + Map variables = new HashMap<>(); + variables.put("moduleName", moduleName); + variables.put("dashModuleName", dashModuleName); + variables.put("packageName", packageName); + variables.put("capitalizedModuleName", capitalizedModuleName); + variables.put("baseName", dashModuleName.toLowerCase()); // 使用 dashModuleName 的小写版本 + variables.put("applicationClassName", capitalizedModuleName + "ServerApplication"); + variables.put("basePackage", "cn.iocoder.yudao"); + variables.put("author", author); + variables.put("port", port); + variables.put("moduleDescription", capitalizedModuleName + " 模块"); - // 创建启动类 - createApplicationClass(newModuleDir, dashModuleName, packageName, capitalizedModuleName, author); - - // 创建配置文件 - createApplicationYml(newModuleDir, dashModuleName, port, author); + // 创建模块结构 + createModuleStructure(newModuleDir, variables); System.out.println("模块 '" + dashModuleName + "' 创建成功!"); } - private static void copyAndProcessDirectory(Path sourceDir, Path targetDir, String moduleName, String packageName, String capitalizedModuleName, String author, int port) throws IOException { - try (Stream stream = Files.walk(sourceDir)) { - stream.filter(path -> shouldIncludePath(sourceDir.relativize(path))) - .forEach(sourcePath -> { - try { - Path relativePath = sourceDir.relativize(sourcePath); - Path targetPath = targetDir.resolve(relativePath); - String targetPathStr = targetPath.toString(); + private static void createModuleStructure(Path moduleDir, Map variables) throws IOException { + String dashModuleName = (String) variables.get("dashModuleName"); + String packageName = (String) variables.get("packageName"); + String basePackage = (String) variables.get("basePackage"); + String capitalizedModuleName = (String) variables.get("capitalizedModuleName"); + String applicationClassName = (String) variables.get("applicationClassName"); + String baseName = (String) variables.get("baseName"); - // 替换路径中的 'template' - targetPathStr = targetPathStr.replace("template", moduleName); - // 替换包名路径 - targetPathStr = targetPathStr.replace(java.io.File.separator + "template" + java.io.File.separator, java.io.File.separator + packageName + java.io.File.separator); - targetPath = Paths.get(targetPathStr); + // 创建主模块目录 + Files.createDirectories(moduleDir); - if (Files.isDirectory(sourcePath)) { - Files.createDirectories(targetPath); - } else { - // 确保父目录存在 - Files.createDirectories(targetPath.getParent()); - String content = new String(Files.readAllBytes(sourcePath)); - String newContent = processFileContent(content, sourcePath.getFileName().toString(), moduleName, packageName, capitalizedModuleName, author, port); - Files.write(targetPath, newContent.getBytes()); - } - } catch (IOException e) { - throw new RuntimeException("处理文件失败: " + sourcePath, e); - } - }); - } - } + // 1. 创建主模块 pom.xml + templateEngine.renderToFile("generator/module/pom.xml.vm", + moduleDir.resolve("pom.xml"), variables); - private static boolean shouldIncludePath(Path relativePath) { - String pathStr = relativePath.toString(); + // 2. 创建 API 模块 + Path apiDir = moduleDir.resolve("yudao-module-" + dashModuleName + "-api"); + Files.createDirectories(apiDir); - // 排除 target 目录 - if (pathStr.contains("target")) { - return false; - } + // API 模块的 Java 源码目录 + Path apiJavaDir = apiDir.resolve("src/main/java") + .resolve(basePackage.replace(".", "/")) + .resolve("module") + .resolve(packageName); + Files.createDirectories(apiJavaDir); - // 排除 .flattened-pom.xml 文件 - if (pathStr.contains(".flattened-pom.xml")) { - return false; - } + // API 模块的错误码目录 + Path enumsDir = apiJavaDir.resolve("enums"); + Files.createDirectories(enumsDir); - // 如果是根目录或者包含 example 的路径,则包含 - if (relativePath.getNameCount() <= 3) { // 根目录和基础包结构 - return true; - } - - // 只包含 example 相关的文件和目录 - return pathStr.contains("example") || pathStr.contains("ErrorCodeConstants"); - } + templateEngine.renderToFile("generator/module/api-pom.xml.vm", + apiDir.resolve("pom.xml"), variables); + templateEngine.renderToFile("generator/module/ErrorCodeConstants.java.vm", + enumsDir.resolve("ErrorCodeConstants.java"), variables); - 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); - } + // 3. 创建 Server 模块 + Path serverDir = moduleDir.resolve("yudao-module-" + dashModuleName + "-server"); + Files.createDirectories(serverDir); - // 处理配置文件 - if (fileName.endsWith(".yml") || fileName.endsWith(".yaml")) { - String newContent = content; - - // 替换应用名称 - newContent = newContent.replace("name: yudao-module-template-server", "name: yudao-module-" + moduleName + "-server"); - - // 处理端口配置 - if (newContent.contains("port:")) { - // 使用更精确的正则表达式来匹配端口行 - newContent = newContent.replaceAll("(?m)^(\\s*)port:\\s*\\d+\\s*$", "$1port: " + port); - } else if (newContent.contains("server:")) { - // 如果有server配置但没有port,在server下添加port - newContent = newContent.replaceAll("(?m)^(\\s*)server:\\s*$", "$1server:\n$1 port: " + port); - } else { - // 如果完全没有server配置,在文件开头添加 - newContent = "server:\n port: " + port + "\n\n" + newContent; - } - - return newContent; - } + // Server 模块的 Java 源码目录 + Path serverJavaDir = serverDir.resolve("src/main/java") + .resolve(basePackage.replace(".", "/")) + .resolve("module") + .resolve(packageName); + Files.createDirectories(serverJavaDir); - return content.replace("yudao-module-template", "yudao-module-" + moduleName) - .replace("cn.iocoder.yudao.module.template", BASE_PACKAGE + packageName) - .replace("TemplateServerApplication", capitalizedModuleName + "ServerApplication") - .replace("样例模块", moduleName + "模块") - .replace("周迪", author) // 替换作者 - .replace("template", moduleName) // 最后替换纯 "template" - .replace("Example", capitalize(moduleName) + "Example"); - } - - private static String processErrorCodeConstants(String content, String moduleName, String packageName, String author) { - // 构建新的 ErrorCodeConstants 内容,只保留基本结构,移除具体的错误码常量 - StringBuilder sb = new StringBuilder(); - sb.append("package ").append(BASE_PACKAGE).append(packageName).append(".enums;\n\n"); - sb.append("import cn.iocoder.yudao.framework.common.exception.ErrorCode;\n\n"); - sb.append("/**\n"); - sb.append(" * ").append(moduleName).append(" 错误码枚举类\n"); - sb.append(" *\n"); - sb.append(" * ").append(moduleName).append(" 系统,使用 1-xxx-xxx-xxx 段\n"); - sb.append(" *\n"); - sb.append(" * @author ").append(author).append("\n"); - sb.append(" */\n"); - sb.append("public interface ErrorCodeConstants {\n\n"); - sb.append(" // ========== 示例模块 1-001-000-000 ==========\n"); - sb.append(" // ErrorCode EXAMPLE_NOT_EXISTS = new ErrorCode(1_001_000_001, \"示例不存在\");\n\n"); - sb.append("}\n"); + // Controller 目录 + Path controllerDir = serverJavaDir.resolve("controller/admin").resolve(baseName); + Files.createDirectories(controllerDir); - return sb.toString(); - } - - /** - * 创建启动类 - */ - private static void createApplicationClass(Path moduleDir, String dashModuleName, String packageName, String capitalizedModuleName, String author) throws IOException { - // 创建启动类目录 - Path applicationDir = moduleDir.resolve("yudao-module-" + dashModuleName + "-server") - .resolve("src/main/java/cn/iocoder/yudao/module/" + packageName); - Files.createDirectories(applicationDir); - - // 创建启动类文件 - Path applicationFile = applicationDir.resolve(capitalizedModuleName + "ServerApplication.java"); + // Security 配置目录 + Path securityConfigDir = serverJavaDir.resolve("framework/security/config"); + Files.createDirectories(securityConfigDir); - StringBuilder sb = new StringBuilder(); - sb.append("package ").append(BASE_PACKAGE).append(packageName).append(";\n\n"); - sb.append("import org.springframework.boot.SpringApplication;\n"); - sb.append("import org.springframework.boot.autoconfigure.SpringBootApplication;\n\n"); - sb.append("/**\n"); - sb.append(" * ").append(dashModuleName).append(" 模块的启动类\n"); - sb.append(" *\n"); - sb.append(" * @author ").append(author).append("\n"); - sb.append(" */\n"); - sb.append("@SpringBootApplication\n"); - sb.append("public class ").append(capitalizedModuleName).append("ServerApplication {\n\n"); - sb.append(" public static void main(String[] args) {\n"); - sb.append(" SpringApplication.run(").append(capitalizedModuleName).append("ServerApplication.class, args);\n"); - sb.append(" }\n\n"); - sb.append("}\n"); - - Files.write(applicationFile, sb.toString().getBytes()); - 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"); + // 资源目录 + Path resourcesDir = serverDir.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"); + templateEngine.renderToFile("generator/module/server-pom.xml.vm", + serverDir.resolve("pom.xml"), variables); + templateEngine.renderToFile("generator/module/ServerApplication.java.vm", + serverJavaDir.resolve(applicationClassName + ".java"), variables); + templateEngine.renderToFile("generator/module/Controller.java.vm", + controllerDir.resolve(capitalizedModuleName + "Controller.java"), variables); + templateEngine.renderToFile("generator/module/SecurityConfiguration.java.vm", + securityConfigDir.resolve("SecurityConfiguration.java"), variables); + templateEngine.renderToFile("generator/module/application.yml.vm", + resourcesDir.resolve("application.yml"), variables); + templateEngine.renderToFile("generator/module/application-dev.yml.vm", + resourcesDir.resolve("application-dev.yml"), variables); + templateEngine.renderToFile("generator/module/application-local.yml.vm", + resourcesDir.resolve("application-local.yml"), variables); - Files.write(configFile, sb.toString().getBytes()); - System.out.println("创建配置文件: " + configFile); } - private static String capitalize(String str) { - if (str == null || str.isEmpty()) { - return str; - } - return str.substring(0, 1).toUpperCase() + str.substring(1); - } - - /** - * 将小驼峰命名转换为短横线分割的命名 - * 例如:orderManagement -> order-management - */ - private 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(); - } } diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/ServerGenerator.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/ServerGenerator.java index 498e8055..5e5f8384 100644 --- a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/ServerGenerator.java +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/ServerGenerator.java @@ -4,13 +4,15 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.Scanner; -import java.util.stream.Collectors; -import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.test.core.ut.GeneratorUtils.capitalize; +import static cn.iocoder.yudao.framework.test.core.ut.GeneratorUtils.toPascalCase; /** - * Yudao Server 代码生成器 + * Yudao Server 代码生成器 - 使用 VM 模板 * * 使用方法: * 1. 在 IDE 中,右键运行 {@link #main(String[])} 方法 @@ -21,8 +23,8 @@ import java.util.stream.Stream; */ public class ServerGenerator { - private static final String TEMPLATE_PATH = "yudao-server"; private static final String BASE_PACKAGE_PREFIX = "cn.iocoder.yudao."; + private static final TemplateEngine templateEngine = new TemplateEngine(); public static void main(String[] args) throws IOException { // 1. 获取用户输入 @@ -39,15 +41,7 @@ public class ServerGenerator { String[] servers = baseNames.split(","); // 2. 定义项目根路径 - // 注意:请在项目的根目录(例如 ztcloud)下运行该生成器 Path projectRoot = Paths.get("").toAbsolutePath(); - Path templateDir = projectRoot.resolve(TEMPLATE_PATH); - - if (!Files.exists(templateDir)) { - System.err.println("错误:模板目录 '" + templateDir + "' 不存在。"); - System.err.println("请确保在项目根目录下运行此生成器。"); - return; - } // 3. 批量创建服务器 for (int i = 0; i < servers.length; i++) { @@ -61,7 +55,7 @@ public class ServerGenerator { int serverPort = startPort + i; System.out.println("\n=== 开始创建 Server: " + serverName + " (端口: " + serverPort + ") ==="); - createSingleServer(templateDir, projectRoot, serverName, author, serverPort); + createSingleServer(projectRoot, serverName, baseName, author, serverPort); } System.out.println("\n所有服务器创建完成!"); @@ -75,9 +69,10 @@ public class ServerGenerator { } } - private static void createSingleServer(Path templateDir, Path projectRoot, String serverName, String author, int port) throws IOException { + private static void createSingleServer(Path projectRoot, String serverName, String baseName, String author, int port) throws IOException { String packageName = serverName.replace("-", ""); String capitalizedName = toPascalCase(serverName); + String capitalizedBaseName = capitalize(baseName); // 定义新 Server 路径 Path newServerDir = projectRoot.resolve(serverName); @@ -89,108 +84,72 @@ public class ServerGenerator { System.out.println("将在以下位置创建新 Server: " + newServerDir); - // 复制并处理文件 - copyAndProcessDirectory(templateDir, newServerDir, serverName, packageName, capitalizedName, author, port); + // 准备模板变量 + Map variables = new HashMap<>(); + variables.put("serverName", serverName); + variables.put("baseName", baseName); + variables.put("capitalizedBaseName", capitalizedBaseName); + variables.put("packageName", packageName); + variables.put("applicationClassName", capitalizedName + "Application"); + variables.put("basePackage", BASE_PACKAGE_PREFIX.substring(0, BASE_PACKAGE_PREFIX.length() - 1)); // 去掉末尾的点 + variables.put("author", author); + variables.put("port", port); + variables.put("serverDescription", capitalizedBaseName + " 服务器"); + + // 创建目录结构和文件 + createServerStructure(newServerDir, variables); } - private static void copyAndProcessDirectory(Path sourceDir, Path targetDir, String serverName, String packageName, String capitalizedName, String author, int port) throws IOException { - try (Stream stream = Files.walk(sourceDir)) { - stream.forEach(sourcePath -> { - try { - Path relativePath = sourceDir.relativize(sourcePath); - if (!shouldIncludePath(relativePath)) { - return; - } + private static void createServerStructure(Path serverDir, Map variables) throws IOException { + String packageName = (String) variables.get("packageName"); + String basePackage = (String) variables.get("basePackage"); + String baseName = (String) variables.get("baseName"); + String applicationClassName = (String) variables.get("applicationClassName"); - // 处理路径和文件名 - String javaPackagePath = "src" + java.io.File.separator + "main" + java.io.File.separator + "java" + java.io.File.separator - + "cn" + java.io.File.separator + "iocoder" + java.io.File.separator + "yudao" + java.io.File.separator; - String targetPathStr = relativePath.toString() - .replace(javaPackagePath + "server", javaPackagePath + packageName) - .replace("YudaoServerApplication.java", capitalizedName + "Application.java"); + // 创建基础目录结构 + Files.createDirectories(serverDir); + + // 创建 Java 源码目录结构 + Path javaSourceDir = serverDir.resolve("src/main/java") + .resolve(basePackage.replace(".", "/")) + .resolve(packageName); + Files.createDirectories(javaSourceDir); + + // 创建控制器目录 + Path controllerDir = javaSourceDir.resolve("controller").resolve(baseName); + Files.createDirectories(controllerDir); + + // 创建资源目录 + Path resourcesDir = serverDir.resolve("src/main/resources"); + Files.createDirectories(resourcesDir); - Path targetPath = targetDir.resolve(targetPathStr); + // 生成文件 + // 1. pom.xml + templateEngine.renderToFile("generator/server/pom.xml.vm", + serverDir.resolve("pom.xml"), variables); + + // 2. 启动类 + templateEngine.renderToFile("generator/server/Application.java.vm", + javaSourceDir.resolve(applicationClassName + ".java"), variables); + + // 3. 示例控制器 + String controllerClassName = variables.get("capitalizedBaseName") + "Controller"; + templateEngine.renderToFile("generator/server/Controller.java.vm", + controllerDir.resolve(controllerClassName + ".java"), variables); + + // 4. 配置文件 + templateEngine.renderToFile("generator/server/application.yml.vm", + resourcesDir.resolve("application.yml"), variables); + templateEngine.renderToFile("generator/server/application-dev.yml.vm", + resourcesDir.resolve("application-dev.yml"), variables); + templateEngine.renderToFile("generator/server/application-local.yml.vm", + resourcesDir.resolve("application-local.yml"), variables); + + // 5. Dockerfile + templateEngine.renderToFile("generator/server/Dockerfile.vm", + serverDir.resolve("Dockerfile"), variables); - if (Files.isDirectory(sourcePath)) { - Files.createDirectories(targetPath); - } else { - Files.createDirectories(targetPath.getParent()); - String content = new String(Files.readAllBytes(sourcePath)); - String newContent = processFileContent(content, sourcePath.getFileName().toString(), serverName, packageName, capitalizedName, author, port); - Files.write(targetPath, newContent.getBytes()); - } - } catch (IOException e) { - throw new RuntimeException("处理文件失败: " + sourcePath, e); - } - }); - } + System.out.println("Server '" + variables.get("serverName") + "' 创建成功!"); } - private static boolean shouldIncludePath(Path relativePath) { - String pathStr = relativePath.toString(); - // 排除 target, .idea, .git 等目录和 .iml, .flattened-pom.xml 等文件 - return !pathStr.startsWith("target") - && !pathStr.startsWith(".idea") - && !pathStr.startsWith(".git") - && !pathStr.endsWith(".iml") - && !pathStr.contains(".flattened-pom.xml"); - } - - 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) { - case "pom.xml": - // 替换 artifactId - newContent = newContent.replace("yudao-server", "" + serverName + ""); - // 移除对 yudao-module-xxx-server 的依赖,但保留 system-server 和 infra-server - return newContent.replaceAll("(?m)^\\s*\\s*cn\\.iocoder\\.cloud\\s*yudao-module-(?!system-server|infra-server).*-server[\\s\\S]*?\\s*", ""); - case "application.yaml": - case "application-dev.yaml": - case "application-prod.yaml": - case "application-test.yaml": - case "application-local.yaml": - // 替换应用名称 - newContent = newContent.replace("name: yudao-server", "name: " + serverName); - - // 处理端口配置 - 更精确的替换逻辑 - if (newContent.contains("port:")) { - // 使用更精确的正则表达式来匹配端口行 - newContent = newContent.replaceAll("(?m)^(\\s*)port:\\s*\\d+\\s*$", "$1port: " + port); - } else if (newContent.contains("server:")) { - // 如果有server配置但没有port,在server下添加port - newContent = newContent.replaceAll("(?m)^(\\s*)server:\\s*$", "$1server:\n$1 port: " + port); - } else { - // 如果完全没有server配置,在文件开头添加 - 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"); - case "Dockerfile": - return newContent.replace("yudao-server.jar", serverName + ".jar") - .replace("/yudao-server", "/" + serverName); - default: - return newContent; - } - } - - /** - * 将 kebab-case 转换为 PascalCase - * 例如:demo-server -> DemoServer - */ - private 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()); - } } diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/TemplateEngine.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/TemplateEngine.java new file mode 100644 index 00000000..faefc36d --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/TemplateEngine.java @@ -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 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 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; + } + } +} diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/Controller.java.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/Controller.java.vm new file mode 100644 index 00000000..5035c688 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/Controller.java.vm @@ -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 hello() { + return success("Hello, ${capitalizedModuleName}!"); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/ErrorCodeConstants.java.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/ErrorCodeConstants.java.vm new file mode 100644 index 00000000..18f421f4 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/ErrorCodeConstants.java.vm @@ -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, "示例不存在"); + +} diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/SecurityConfiguration.java.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/SecurityConfiguration.java.vm new file mode 100644 index 00000000..45b96570 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/SecurityConfiguration.java.vm @@ -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.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(); + } + + }; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/ServerApplication.java.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/ServerApplication.java.vm new file mode 100644 index 00000000..195a2fe4 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/ServerApplication.java.vm @@ -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); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/api-pom.xml.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/api-pom.xml.vm new file mode 100644 index 00000000..70ac7841 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/api-pom.xml.vm @@ -0,0 +1,46 @@ + + + + yudao-module-${dashModuleName} + cn.iocoder.cloud + ${revision} + + 4.0.0 + yudao-module-${dashModuleName}-api + jar + + ${project.artifactId} + + 暴露给其它模块调用 + + + + + cn.iocoder.cloud + yudao-common + + + + org.springdoc + springdoc-openapi-starter-webmvc-api + provided + + + + + org.springframework.boot + spring-boot-starter-validation + true + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + true + + + + diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/application-dev.yml.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/application-dev.yml.vm new file mode 100644 index 00000000..30abeecd --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/application-dev.yml.vm @@ -0,0 +1,107 @@ +spring: + # 数据源配置项 + autoconfigure: + exclude: + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 5 # 初始连接数 + min-idle: 10 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:dm://172.16.46.247:1050?schema=RUOYI-VUE-PRO + username: SYSDBA + password: pgbsci6ddJ6Sqj@e + slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:dm://172.16.46.247:1050?schema=RUOYI-VUE-PRO + username: SYSDBA + password: pgbsci6ddJ6Sqj@e + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 172.16.46.63 # 地址 + port: 30379 # 端口 + database: 0 # 数据库索引 +# password: 123456 # 密码,建议生产环境开启 + +xxl: + job: + admin: + addresses: http://172.16.46.63:30082/xxl-job-admin # 调度中心部署跟地址 + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# 日志文件配置 +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + + +justauth: + enabled: true + type: + DINGTALK: # 钉钉 + client-id: dingvrnreaje3yqvzhxg + client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI + ignore-check-redirect-uri: true + WECHAT_ENTERPRISE: # 企业微信 + client-id: wwd411c69a39ad2e54 + client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw + agent-id: 1000004 + ignore-check-redirect-uri: true + # noinspection SpringBootApplicationYaml + WECHAT_MINI_PROGRAM: # 微信小程序 + client-id: ${dollar}{wx.miniapp.appid} + client-secret: ${dollar}{wx.miniapp.secret} + ignore-check-redirect-uri: true + ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验 + WECHAT_MP: # 微信公众号 + client-id: ${dollar}{wx.mp.app-id} + client-secret: ${dollar}{wx.mp.secret} + ignore-check-redirect-uri: true + cache: + type: REDIS + prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: + timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 + diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/application-local.yml.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/application-local.yml.vm new file mode 100644 index 00000000..3f8706d8 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/application-local.yml.vm @@ -0,0 +1,98 @@ +#set($dollar = '$') +spring: + # 数据源配置项 + autoconfigure: + # noinspection SpringBootApplicationYaml + exclude: + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:dm://172.16.46.247:1050?schema=RUOYI-VUE-PRO + username: SYSDBA + password: pgbsci6ddJ6Sqj@e + slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:dm://172.16.46.247:1050?schema=RUOYI-VUE-PRO + username: SYSDBA + password: pgbsci6ddJ6Sqj@e + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 172.16.46.63 # 地址 + port: 30379 # 端口 + database: 0 # 数据库索引 +# password: 123456 # 密码,建议生产环境开启 + +xxl: + job: + admin: + addresses: http://172.16.46.63:30082/xxl-job-admin # 调度中心部署跟地址 + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# 日志文件配置 +logging: + level: + # 配置自己写的 MyBatis Mapper 打印日志 + ${basePackage}.module.${packageName}.dal.mysql: debug + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR + +mybatis-plus: + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + env: # 多环境的配置项 + tag: ${dollar}{HOSTNAME} + security: + mock-enable: true + access-log: # 访问日志的配置项 + enable: true + diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/application.yml.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/application.yml.vm new file mode 100644 index 00000000..0f5deffe --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/application.yml.vm @@ -0,0 +1,124 @@ +#set($dollar = '$') +spring: + application: + name: ${dashModuleName}-server + + profiles: + active: ${dollar}{env.name} + #统一nacos配置,使用 profile 管理 + cloud: + nacos: + server-addr: ${dollar}{config.server-addr} # Nacos 服务器地址 + username: ${dollar}{config.username} # Nacos 账号 + password: ${dollar}{config.password} # Nacos 密码 + discovery: # 【配置中心】配置项 + namespace: ${dollar}{config.namespace} # 命名空间。这里使用 maven Profile 资源过滤进行动态替换 + group: ${dollar}{config.group} # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # 服务实例的版本号,可用于灰度发布 + config: # 【注册中心】配置项 + namespace: ${dollar}{config.namespace} # 命名空间。这里使用 maven Profile 资源过滤进行动态替换 + group: ${dollar}{config.group} # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + main: + allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 + allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务 + + config: + import: + - optional:classpath:application-${dollar}{spring.profiles.active}.yaml # 加载【本地】配置 + - optional:nacos:${dollar}{spring.application.name}-${dollar}{spring.profiles.active}.yaml # 加载【Nacos】的配置 + + # Servlet 配置 + servlet: + # 文件上传相关配置项 + multipart: + max-file-size: 16MB # 单个文件大小 + max-request-size: 32MB # 设置总上传的文件大小 + + # Jackson 配置项 + jackson: + serialization: + write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 + write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 + fail-on-empty-beans: false # 允许序列化无属性的 Bean + time-zone: Asia/Shanghai + + # Cache 配置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 小时 + +server: + port: ${port} + +logging: + file: + name: ${dollar}{user.home}/logs/${dollar}{spring.application.name}.log # 日志文件名,全路径 + +springdoc: + api-docs: + enabled: true # 1. 是否开启 Swagger 接文档的元数据 + path: /v3/api-docs + swagger-ui: + enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面 + path: /swagger-ui.html + default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: true # 2.2 是否开启 Swagger 文档的 Knife4j UI 界面 + setting: + language: zh_cn + +# MyBatis Plus 的配置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 + # id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库 + # id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库 + # id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制台的 Banner 打印 + type-aliases-package: ${basePackage}.module.*.dal.dataobject + encryptor: + password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成 + +mybatis-plus-join: + banner: false # 关闭控制台的 Banner 打印 + +# VO 转换(数据翻译)相关 +easy-trans: + is-enable-global: false # 启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口 + +xxl: + job: + executor: + appname: ${dollar}{spring.application.name} # 执行器 AppName + logpath: ${dollar}{user.home}/logs/xxl-job/${dollar}{spring.application.name} # 执行器运行日志文件存储磁盘路径 + accessToken: default_token # 执行器通讯TOKEN + +yudao: + info: + version: 1.0.0 + base-package: ${basePackage}.module.${packageName} + web: + admin-ui: + url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址 + xss: + enable: false + exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 + - ${dollar}{spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 + - ${dollar}{management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 + swagger: + title: 管理后台 + description: 提供管理员管理的所有功能 + version: ${dollar}{yudao.info.version} + tenant: # 多租户相关配置项 + enable: true + +debug: false diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/pom.xml.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/pom.xml.vm new file mode 100644 index 00000000..729ddd91 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/pom.xml.vm @@ -0,0 +1,24 @@ + + + + yudao + cn.iocoder.cloud + ${revision} + + + yudao-module-${dashModuleName}-api + yudao-module-${dashModuleName}-server + + 4.0.0 + + yudao-module-${dashModuleName} + pom + + ${project.artifactId} + + ${moduleDescription}。 + + + diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/server-pom.xml.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/server-pom.xml.vm new file mode 100644 index 00000000..5acd236a --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/module/server-pom.xml.vm @@ -0,0 +1,151 @@ + + + + yudao-module-${dashModuleName} + cn.iocoder.cloud + ${revision} + + 4.0.0 + jar + + yudao-module-${dashModuleName}-server + + ${project.artifactId} + + ${moduleDescription}。 + + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-env + + + + + cn.iocoder.cloud + yudao-module-system-api + ${revision} + + + cn.iocoder.cloud + yudao-module-infra-api + ${revision} + + + + cn.iocoder.cloud + yudao-module-${dashModuleName}-api + ${revision} + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-data-permission + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-tenant + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-web + + + + cn.iocoder.cloud + yudao-spring-boot-starter-security + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-mybatis + + + + cn.iocoder.cloud + yudao-spring-boot-starter-redis + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-rpc + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-job + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-mq + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-test + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-excel + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-monitor + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-business + ${revision} + + + + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + + + diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/Application.java.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/Application.java.vm new file mode 100644 index 00000000..0f91e56f --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/Application.java.vm @@ -0,0 +1,21 @@ +#set($dollar = '$') +package ${basePackage}.${packageName}; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * ${serverDescription}的启动类 + * + * @author ${author} + */ +@SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${dollar}{yudao.info.base-package} +@SpringBootApplication(scanBasePackages = {"${dollar}{yudao.info.base-package}.${packageName}", "${dollar}{yudao.info.base-package}.module"}, + excludeName = {}) +public class ${applicationClassName} { + + public static void main(String[] args) { + SpringApplication.run(${applicationClassName}.class, args); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/Controller.java.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/Controller.java.vm new file mode 100644 index 00000000..f3181a3b --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/Controller.java.vm @@ -0,0 +1,29 @@ +package ${basePackage}.${packageName}.controller.${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; + +/** + * ${baseName} 控制器 + * + * @author ${author} + */ +@Tag(name = "${baseName}") +@RestController +@RequestMapping("/${baseName}") +public class ${capitalizedBaseName}Controller { + + @GetMapping("/hello") + @Operation(summary = "Hello ${baseName}") + public CommonResult hello() { + return success("Hello, ${baseName}!"); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/Dockerfile.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/Dockerfile.vm new file mode 100644 index 00000000..baee3299 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/Dockerfile.vm @@ -0,0 +1,13 @@ +FROM openjdk:17-jre-slim + +# 设置应用目录 +WORKDIR /app + +# 复制应用文件 +COPY target/${serverName}.jar /app/${serverName}.jar + +# 暴露端口 +EXPOSE ${port} + +# 运行应用 +ENTRYPOINT ["java", "-jar", "/app/${serverName}.jar"] diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/application-dev.yml.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/application-dev.yml.vm new file mode 100644 index 00000000..d31287be --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/application-dev.yml.vm @@ -0,0 +1,124 @@ +server: + servlet: + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 5 # 初始连接数 + min-idle: 10 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + # 设置默认的数据源或者数据源组,默认 master + primary: master + datasource: + # 主库 + master: + url: jdbc:mysql://127.0.0.1:3306/${dbName}?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: + # 从库 + slave: + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://127.0.0.1:3306/${dbName}?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 127.0.0.1 # 地址 + port: 6379 # 端口 + database: 1 # 数据库索引 +# password: # 密码,建议生产环境开启 + +--- #################### 定时任务相关配置 #################### + +xxl: + job: + enabled: false # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址 + +--- #################### 消息队列相关 #################### + +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: 127.0.0.1:9876 # RocketMQ Namesrv + +spring: + # RabbitMQ 配置项,对应 RabbitProperties 配置类 + rabbitmq: + host: 127.0.0.1 # RabbitMQ 服务的地址 + port: 5672 # RabbitMQ 服务的端口 + username: guest # RabbitMQ 服务的账号 + password: guest # RabbitMQ 服务的密码 + # Kafka 配置项,对应 KafkaProperties 配置类 + kafka: + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# 日志文件配置 +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + level: + # 配置自己写的 MyBatis Mapper 打印日志 + ${basePackage}.${packageName}.dal.mysql: debug + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + demo: false # 开启演示模式 + # 附件加密相关配置 + AES: + key: "0123456789abcdef0123456789abcdef" diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/application-local.yml.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/application-local.yml.vm new file mode 100644 index 00000000..99f2bfd9 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/application-local.yml.vm @@ -0,0 +1,125 @@ +server: + servlet: + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + # 设置默认的数据源或者数据源组,默认 master + primary: master + datasource: + # 主库 + master: + url: jdbc:mysql://127.0.0.1:3306/${dbName}?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: + # 从库 + slave: + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://127.0.0.1:3306/${dbName}?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: localhost # 地址 + port: 6379 # 端口 + database: 1 # 数据库索引 +# password: # 密码,建议生产环境开启 + +--- #################### 定时任务相关配置 #################### + +xxl: + job: + enabled: false # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址 + +--- #################### 消息队列相关 #################### + +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: localhost:9876 # RocketMQ Namesrv + +spring: + # RabbitMQ 配置项,对应 RabbitProperties 配置类 + rabbitmq: + host: 127.0.0.1 # RabbitMQ 服务的地址 + port: 5672 # RabbitMQ 服务的端口 + username: rabbit # RabbitMQ 服务的账号 + password: rabbit # RabbitMQ 服务的密码 + # Kafka 配置项,对应 KafkaProperties 配置类 + kafka: + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# 日志文件配置 +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + level: + # 配置自己写的 MyBatis Mapper 打印日志 + ${basePackage}.${packageName}.dal.mysql: debug + root: info + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + demo: false # 开启演示模式 + # 附件加密相关配置 + AES: + key: "0123456789abcdef0123456789abcdef" diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/application.yml.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/application.yml.vm new file mode 100644 index 00000000..9b5c6241 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/application.yml.vm @@ -0,0 +1,111 @@ +server: + port: ${port} + +spring: + application: + name: ${serverName} + main: + allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 + profiles: + active: local + + # Servlet 配置 + servlet: + # 文件上传相关配置项 + multipart: + max-file-size: 16MB # 单个文件大小 + max-request-size: 32MB # 设置总上传的文件大小 + + # Jackson 配置项 + jackson: + serialization: + write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳 + write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 + fail-on-empty-beans: false # 允许序列化无属性的 Bean + + # Cache 配置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 小时 + +--- #################### 接口文档配置 #################### + +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui + default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: true + setting: + language: zh_cn + +# MyBatis Plus 的配置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # "智能"模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制台的 Banner 打印 + type-aliases-package: ${basePackage}.${packageName}.dal.dataobject + +# Spring Data Redis 配置 +spring: + data: + redis: + repositories: + enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度 + +--- #################### 芋道相关配置 #################### + +yudao: + info: + version: 1.0.0 + base-package: ${basePackage} + author: ${author} + description: ${serverDescription} + web: + admin-ui: + url: http://localhost:3000 # Admin 管理后台 UI 的地址 + xss: + enable: false + security: + permit-all_urls: [] + websocket: + enable: true # websocket的开关 + path: /infra/ws # 路径 + sender-type: local # 消息发送的类型,可选值为 local、redis、rocketmq、kafka、rabbitmq + sender-rocketmq: + topic: ${spring.application.name}-websocket # 消息发送的 RocketMQ Topic + consumer-group: ${spring.application.name}-websocket-consumer # 消息发送的 RocketMQ Consumer Group + sender-rabbitmq: + exchange: ${spring.application.name}-websocket-exchange # 消息发送的 RabbitMQ Exchange + queue: ${spring.application.name}-websocket-queue # 消息发送的 RabbitMQ Queue + sender-kafka: + topic: ${spring.application.name}-websocket # 消息发送的 Kafka Topic + consumer-group: ${spring.application.name}-websocket-consumer # 消息发送的 Kafka Consumer Group + swagger: + title: ${serverName} + description: ${serverDescription} + version: ${yudao.info.version} + email: xingyu4j@vip.qq.com + license: MIT + license-url: https://gitee.com/zhijiantianya/ruoyi-vue-pro/blob/master/LICENSE + codegen: + base-package: ${basePackage}.${packageName} + db-schemas: ${spring.datasource.dynamic.datasource.master.name} + front-type: 20 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类 + vo-type: 10 # VO 的类型,参见 CodegenVOTypeEnum 枚举类 + delete-batch-enable: true # 是否生成批量删除接口 + unit-test-enable: false # 是否生成单元测试 + +debug: false diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/pom.xml.vm b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/pom.xml.vm new file mode 100644 index 00000000..ca55513d --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/resources/generator/server/pom.xml.vm @@ -0,0 +1,99 @@ + + + + cn.iocoder.cloud + yudao + ${revision} + + 4.0.0 + ${serverName} + jar + + ${serverName} + ${serverDescription} + + + + cn.iocoder.cloud + yudao-module-system-server + ${revision} + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + cn.iocoder.cloud + yudao-module-infra-server + ${revision} + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-protection + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-rpc + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + + + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + + +