Compare commits
2 Commits
25f46606fc
...
ad5bc29cb3
Author | SHA1 | Date |
---|---|---|
|
ad5bc29cb3 | |
|
464a419d9a |
470
README.md
470
README.md
|
@ -1,90 +1,402 @@
|
||||||
# Bunny Code Generator 🚀
|
# 🚀 Bunny Code Generator 代码生成器系统文档
|
||||||
|
|
||||||
[](LICENSE)[]()[]()
|
[](LICENSE)[]()[]()
|
||||||
|
|
||||||
Bunny Code Generator 是一个高效的数据库表结构到代码的生成工具,帮助开发者快速生成高质量的服务端代码,显著提升开发效率。
|
## 1. 系统架构 🏗️
|
||||||
|
|
||||||
## ✨ 核心特性
|
### 1.1 架构图
|
||||||
|
|
||||||
- **多数据源支持** - 支持通过数据库连接或直接解析SQL语句生成代码
|
```mermaid
|
||||||
- **模板引擎自由** - 基于Velocity模板引擎,支持完全自定义代码模板
|
graph TD
|
||||||
- **灵活输出方式** - 支持即时代码预览和ZIP打包下载两种输出模式
|
A[🖥️ 前端界面] -->|HTTP请求| B[🌐 WebController]
|
||||||
- **智能命名转换** - 自动将表名转换为大/小驼峰命名的类名
|
A -->|API调用| C[⚙️ GeneratorController]
|
||||||
- **动态模板选择** - 前端可交互式选择需要生成的模板文件组合
|
A -->|SQL解析| D[🔍 SqlParserController]
|
||||||
- **注释保留** - 自动将表字段注释带入生成的代码中
|
A -->|元数据查询| E[🗃️ TableController]
|
||||||
|
A -->|模板管理| F[📂 VmsController]
|
||||||
|
|
||||||
|
B -->|页面跳转| G[📝 Thymeleaf模板]
|
||||||
|
C -->|生成请求| H[🏭 GeneratorService]
|
||||||
|
D -->|SQL解析| I[✂️ SqlMetadataProvider]
|
||||||
|
E -->|数据库查询| J[🔗 DatabaseMetadataProvider]
|
||||||
|
F -->|模板扫描| K[🗃️ VmsService]
|
||||||
|
|
||||||
|
H -->|模板渲染| L[🌀 Velocity引擎]
|
||||||
|
H -->|打包下载| M[📦 ZipFileUtil]
|
||||||
|
I & J -->|数据源| N[💾 MySQL数据库]
|
||||||
|
J -->|连接池| O[🛟 HikariCP]
|
||||||
|
```
|
||||||
|
|
||||||
## 🚀 快速入门
|
### 1.2 核心分层 🔍
|
||||||
|
|
||||||
### 前置要求
|
| 层级 | 组件 | 技术实现 |
|
||||||
|
| ------------ | --------------- | -------------------- |
|
||||||
- JDK 1.8 或更高版本
|
| **接入层** 🚪 | Controllers | Spring Web, Swagger |
|
||||||
- MySQL 5.7+ (或其他支持的数据库)
|
| **业务层** ⚙️ | Services | 并行流, 设计模式 |
|
||||||
- Spring Boot 2.7+
|
| **核心层** 🧠 | 模板生成/元数据 | Velocity, JSqlParser |
|
||||||
|
| **数据层** 💾 | 数据源/连接池 | HikariCP, JDBC |
|
||||||
### 安装步骤
|
|
||||||
|
|
||||||
1. 克隆仓库:
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/yourusername/bunny-code-generator.git
|
|
||||||
```
|
|
||||||
|
|
||||||
2. 配置数据库连接 (`application.yml`):
|
|
||||||
```yaml
|
|
||||||
bunny:
|
|
||||||
master:
|
|
||||||
database: your_database
|
|
||||||
spring:
|
|
||||||
datasource:
|
|
||||||
url: jdbc:mysql://localhost:3306/your_database
|
|
||||||
username: your_username
|
|
||||||
password: your_password
|
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
||||||
```
|
|
||||||
|
|
||||||
3. 启动应用:
|
|
||||||
```bash
|
|
||||||
mvn spring-boot:run
|
|
||||||
```
|
|
||||||
|
|
||||||
4. 访问 `http://localhost:8080` 开始使用
|
|
||||||
|
|
||||||
## 🛠️ 模板变量参考
|
|
||||||
|
|
||||||
模板中可使用以下预定义变量:
|
|
||||||
|
|
||||||
| 变量名 | 描述 | 示例值 |
|
|
||||||
| -------------------- | ------------------ | ------------------ |
|
|
||||||
| `tableName` | 原始表名 | `user_info` |
|
|
||||||
| `classLowercaseName` | 小驼峰类名 | `userInfo` |
|
|
||||||
| `classUppercaseName` | 大驼峰类名 | `UserInfo` |
|
|
||||||
| `columnInfoList` | 列信息列表 | `List<ColumnInfo>` |
|
|
||||||
| `baseColumnList` | SQL列名字符串 | `id,name,age` |
|
|
||||||
| `package` | 包名 | `com.example.demo` |
|
|
||||||
| `author` | 作者名 | `YourName` |
|
|
||||||
| `date` | 当前日期 | `2023-07-20` |
|
|
||||||
| `comment` | 表注释 | `用户信息表` |
|
|
||||||
| `requestMapping` | Controller请求前缀 | `/api/user` |
|
|
||||||
|
|
||||||
## 🤝 参与贡献
|
|
||||||
|
|
||||||
我们欢迎各种形式的贡献!请阅读 [贡献指南](CONTRIBUTING.md) 了解如何参与项目开发。
|
|
||||||
|
|
||||||
1. Fork 项目仓库
|
|
||||||
2. 创建您的特性分支 (`git checkout -b feature/AmazingFeature`)
|
|
||||||
3. 提交您的更改 (`git commit -m 'Add some AmazingFeature'`)
|
|
||||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
|
||||||
5. 发起 Pull Request
|
|
||||||
|
|
||||||
## 📄 许可证
|
|
||||||
|
|
||||||
本项目采用 MIT 许可证 - 详情请参阅 [LICENSE](LICENSE) 文件。
|
|
||||||
|
|
||||||
## ☕ 支持项目
|
|
||||||
|
|
||||||
如果这个项目对您有帮助,可以考虑支持我们:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2. 核心控制器详解 🎯
|
||||||
|
|
||||||
|
### 2.1 GeneratorController ⚡
|
||||||
|
|
||||||
|
**核心业务流:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
A[接收VmsArgumentDto] --> B{判断生成方式}
|
||||||
|
B -->|SQL生成| C[调用generateCodeBySql]
|
||||||
|
B -->|数据库生成| D[调用generateCodeByDatabase]
|
||||||
|
C/D --> E[模板渲染]
|
||||||
|
E --> F{是否打包下载}
|
||||||
|
F -->|是| G[生成ZIP响应]
|
||||||
|
F -->|否| H[返回代码预览]
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键方法说明:**
|
||||||
|
|
||||||
|
1. `generator()` 方法:
|
||||||
|
|
||||||
|
- 🔄 根据`sql`参数判断生成方式
|
||||||
|
|
||||||
|
- 📊 使用`VmsArgumentDto`接收模板选择、包名等参数
|
||||||
|
|
||||||
|
- 🌟 核心逻辑:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Strings.isEmpty(sql)
|
||||||
|
? generatorService.generateCodeByDatabase(dto)
|
||||||
|
: generatorService.generateCodeBySql(dto);
|
||||||
|
```
|
||||||
|
|
||||||
|
2. `downloadByZip()` 方法:
|
||||||
|
|
||||||
|
- 🗜️ 使用`ZipFileUtil`进行内存压缩
|
||||||
|
|
||||||
|
- ⚡ 响应头设置:
|
||||||
|
|
||||||
|
```java
|
||||||
|
headers.add("Content-Disposition", "attachment; filename=" + zipFilename);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 TableController 🗃️
|
||||||
|
|
||||||
|
**元数据查询体系:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
TableController --> TableService : 依赖
|
||||||
|
TableService --> DatabaseMetadataProvider : 调用
|
||||||
|
DatabaseMetadataProvider --> HikariDataSource : 使用
|
||||||
|
|
||||||
|
class TableController{
|
||||||
|
+databaseInfoMetaData() 数据库基础信息
|
||||||
|
+databaseTableList() 表清单
|
||||||
|
+tableMetaData() 单表结构
|
||||||
|
+tableColumnInfo() 列详情
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**典型调用链:**
|
||||||
|
|
||||||
|
1. 获取数据库信息:
|
||||||
|
|
||||||
|
```java
|
||||||
|
DatabaseMetaData metaData = connection.getMetaData();
|
||||||
|
return DatabaseInfoMetaData.builder()
|
||||||
|
.databaseProductName(metaData.getDatabaseProductName())
|
||||||
|
.driverVersion(metaData.getDriverVersion())
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 查询表结构:
|
||||||
|
|
||||||
|
```java
|
||||||
|
ResultSet tables = metaData.getTables(dbName, null, "%", new String[]{"TABLE"});
|
||||||
|
while(tables.next()){
|
||||||
|
// 构建TableMetaData对象
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 关键技术实现 🔧
|
||||||
|
|
||||||
|
### 3.1 模板生成流程 🌀
|
||||||
|
|
||||||
|
**抽象模板方法模式:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public abstract class AbstractTemplateGenerator {
|
||||||
|
// 模板方法
|
||||||
|
public final StringWriter generateCode() {
|
||||||
|
prepareContext(); // 准备数据
|
||||||
|
return mergeTemplate(); // 渲染模板
|
||||||
|
}
|
||||||
|
protected abstract void addContext(); // 子类实现
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Velocity上下文示例:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
context.put("classLowercaseName", "userInfo");
|
||||||
|
context.put("columns", columnList);
|
||||||
|
context.put("date", "2023-01-01");
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 类型转换策略 🔄
|
||||||
|
|
||||||
|
**MySQL类型映射:**
|
||||||
|
|
||||||
|
| 数据库类型 | Java类型 | 转换方法 |
|
||||||
|
| ---------- | --------------- | ------------------------------------------ |
|
||||||
|
| `varchar` | `String` | `MysqlTypeConvertUtil.convertToJavaType()` |
|
||||||
|
| `datetime` | `LocalDateTime` | 同上 |
|
||||||
|
| `tinyint` | `Integer` | 同上 |
|
||||||
|
|
||||||
|
**命名转换示例:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 下划线转驼峰
|
||||||
|
convertToCamelCase("user_name", true); // → "UserName"
|
||||||
|
convertToCamelCase("user_name", false); // → "userName"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 核心业务逻辑说明
|
||||||
|
|
||||||
|
### 4.1 代码生成流程
|
||||||
|
|
||||||
|
#### 4.1.1 简洁流畅
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant F as 前端
|
||||||
|
participant C as Controller
|
||||||
|
participant S as Service
|
||||||
|
participant T as Template
|
||||||
|
|
||||||
|
F->>C: 提交生成请求(VmsArgumentDto)
|
||||||
|
C->>S: 调用generateCodeBy[Sql/Database]
|
||||||
|
S->>T: 准备Velocity上下文
|
||||||
|
T->>S: 返回渲染结果
|
||||||
|
S->>C: 返回Map<String, List<GeneratorVo>>
|
||||||
|
C->>F: 返回结果或ZIP包
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.1.2 详细流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant 前端
|
||||||
|
participant GeneratorController
|
||||||
|
participant GeneratorService
|
||||||
|
participant MetadataProvider
|
||||||
|
participant TemplateEngine
|
||||||
|
|
||||||
|
前端->>GeneratorController: 提交生成请求
|
||||||
|
GeneratorController->>GeneratorService: 调用生成方法
|
||||||
|
GeneratorService->>MetadataProvider: 获取表/列元数据
|
||||||
|
MetadataProvider-->>GeneratorService: 返回元数据
|
||||||
|
GeneratorService->>TemplateEngine: 填充模板
|
||||||
|
TemplateEngine-->>GeneratorService: 生成代码
|
||||||
|
GeneratorService-->>GeneratorController: 返回生成结果
|
||||||
|
GeneratorController-->>前端: 返回代码/ZIP包
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 关键类协作
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class GeneratorController {
|
||||||
|
+generateCode()
|
||||||
|
+downloadByZip()
|
||||||
|
}
|
||||||
|
|
||||||
|
class GeneratorService {
|
||||||
|
+generateCodeByDatabase()
|
||||||
|
+generateCodeBySql()
|
||||||
|
}
|
||||||
|
|
||||||
|
class AbstractTemplateGenerator {
|
||||||
|
+generateCode()
|
||||||
|
#addContext()
|
||||||
|
#templateMerge()
|
||||||
|
}
|
||||||
|
|
||||||
|
class VmsTBaseTemplateGenerator {
|
||||||
|
+addContext()
|
||||||
|
+templateMerge()
|
||||||
|
}
|
||||||
|
|
||||||
|
GeneratorController --> GeneratorService
|
||||||
|
GeneratorService --> AbstractTemplateGenerator
|
||||||
|
AbstractTemplateGenerator <|-- VmsTBaseTemplateGenerator
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 扩展指南 🛠️
|
||||||
|
|
||||||
|
### 5.1 添加新数据库支持
|
||||||
|
|
||||||
|
1. 实现`DatabaseDialect`接口 ✏️
|
||||||
|
|
||||||
|
2. 配置新的`IMetadataProvider` ⚙️
|
||||||
|
|
||||||
|
3. 示例代码结构:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Component
|
||||||
|
public class OracleDialect implements DatabaseDialect {
|
||||||
|
// 实现提取注释等方法
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 依赖管理 📦
|
||||||
|
|
||||||
|
**关键依赖说明:**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- SQL解析 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.jsqlparser</groupId>
|
||||||
|
<artifactId>jsqlparser</artifactId>
|
||||||
|
<version>4.9</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 模板引擎 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.velocity</groupId>
|
||||||
|
<artifactId>velocity-engine-core</artifactId>
|
||||||
|
<version>2.2</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 注意事项 ⚠️
|
||||||
|
|
||||||
|
### 6.1 性能优化点 🚀
|
||||||
|
|
||||||
|
- `parallelStream()` 用于批量生成
|
||||||
|
|
||||||
|
- `HikariCP` 配置:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
hikari:
|
||||||
|
maximum-pool-size: 20
|
||||||
|
connection-timeout: 30000
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 版本兼容性 🔗
|
||||||
|
|
||||||
|
| 组件 | 已验证版本 |
|
||||||
|
| ----------- | ---------- |
|
||||||
|
| MySQL | 8.0+ |
|
||||||
|
| JDK | 17+ |
|
||||||
|
| Spring Boot | 3.4.3 |
|
||||||
|
|
||||||
|
### 6.3 文件名重复问题解决方案⚠️
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[用户选择模板] --> B{是否包含$className}
|
||||||
|
B -->|是| C[替换为表名的小驼峰格式]
|
||||||
|
B -->|否| D[保持原文件名]
|
||||||
|
C --> E[生成唯一文件路径]
|
||||||
|
D --> E
|
||||||
|
```
|
||||||
|
|
||||||
|
**核心逻辑实现**(位于`VmsGeneratorPathHelper.java`):
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static String processVmPath(VmsArgumentDto dto, String path, String tableName) {
|
||||||
|
String className = removeTablePrefixes(dto, tableName);
|
||||||
|
String lowerCamelCase = MysqlTypeConvertUtil.convertToCamelCase(tableName, false);
|
||||||
|
// 关键替换逻辑👇
|
||||||
|
String[] pathParts = path.replace("$className", lowerCamelCase).split("/");
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**必须遵守的规则**:
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
>
|
||||||
|
> 如果不想使用`$className`可自己修改源码,进行更改。
|
||||||
|
|
||||||
|
1. 前端文件必须使用`$className`作为动态目录名(如:`web/$className/api.ts`)
|
||||||
|
2. 相同基础名称的文件必须放在不同目录下
|
||||||
|
3. Java/XML文件会自动添加类型后缀(如`UserController.java`)
|
||||||
|
|
||||||
|
### 6.4 文件命名冲突场景示例
|
||||||
|
|
||||||
|
| 错误案例 | 正确方案 | 原因 |
|
||||||
|
| ----------------------- | -------------------------- | -------------------------- |
|
||||||
|
| `web/index.vue` | `web/$className/index.vue` | 多表生成时会冲突 |
|
||||||
|
| `mapper/UserMapper.xml` | 自动处理 | 系统会自动添加表名前缀 |
|
||||||
|
| `service/Service.java` | 自动处理 | 会转换为`UserService.java` |
|
||||||
|
|
||||||
|
## 7. 代码质量评估 🔍
|
||||||
|
|
||||||
|
### 7.1 后端代码评估(85/100)
|
||||||
|
|
||||||
|
**优势**:
|
||||||
|
|
||||||
|
- ✅ 清晰的层次划分(Controller/Service/Provider)
|
||||||
|
- ✅ 合理使用设计模式(模板方法模式)
|
||||||
|
- ✅ 完善的异常处理体系
|
||||||
|
- ✅ 类型转换工具类封装良好
|
||||||
|
|
||||||
|
**待改进**:
|
||||||
|
|
||||||
|
- ⚠️ 部分SQL解析逻辑可抽取为策略模式
|
||||||
|
- ⚠️ 元数据提供者接口可进一步抽象
|
||||||
|
- ⚠️ ZIP打包逻辑与业务耦合稍紧
|
||||||
|
|
||||||
|
**坏味道检测**:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
pie
|
||||||
|
title 后端代码坏味道分布
|
||||||
|
"重复代码" : 15
|
||||||
|
"过长方法" : 10
|
||||||
|
"过度耦合" : 5
|
||||||
|
"其他" : 70
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 前端代码评估(78/100)
|
||||||
|
|
||||||
|
**亮点**:
|
||||||
|
|
||||||
|
- ✅ 组件化设计合理(表单/表格分离)
|
||||||
|
- ✅ 响应式状态管理有效
|
||||||
|
- ✅ 良好的用户交互反馈
|
||||||
|
- ✅ 类型提示完善
|
||||||
|
|
||||||
|
**问题点**:
|
||||||
|
|
||||||
|
- ⚠️ 部分表单验证逻辑重复
|
||||||
|
- ⚠️ 表格分页逻辑可抽取为独立组件
|
||||||
|
|
||||||
|
**复杂度分析**:
|
||||||
|
|
||||||
|
| 文件 | 方法数 | 平均行数 | 复杂度 |
|
||||||
|
| ------------------- | ------ | -------- | ------ |
|
||||||
|
| DatabaseTable.js | 12 | 18 | 中等 |
|
||||||
|
| DatabaseForm.js | 25 | 12 | 较高 |
|
||||||
|
| AppGeneratorPage.js | 8 | 15 | 低 |
|
||||||
|
|
||||||
|
### 7.3 总结评价
|
||||||
|
|
||||||
|
**整体评分**:82/100
|
||||||
|
|
||||||
|
**可维护性**:⭐️⭐️⭐️⭐️
|
||||||
|
|
||||||
|
**扩展性**:⭐️⭐️⭐️⭐️
|
||||||
|
|
||||||
|
**代码规范**:⭐️⭐️⭐️⭐️⭐️
|
||||||
|
|
||||||
|
## 8. 支持项目☕
|
||||||
|
|
||||||
|
如果这个项目对您有帮助,可以考虑支持我们:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
**Happy Coding!** 🎉
|
**Happy Coding!** 🎉
|
|
@ -39,7 +39,10 @@ public class GeneratorController {
|
||||||
@Operation(summary = "生成代码", description = "根据SQL或数据库表生成代码")
|
@Operation(summary = "生成代码", description = "根据SQL或数据库表生成代码")
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public Result<Map<String, List<GeneratorVo>>> generator(@Valid @RequestBody VmsArgumentDto dto) {
|
public Result<Map<String, List<GeneratorVo>>> generator(@Valid @RequestBody VmsArgumentDto dto) {
|
||||||
Map<String, List<GeneratorVo>> result = Strings.isEmpty(dto.getSql())
|
// 判断当前是使用 SQL语句 生成还是 数据库生成
|
||||||
|
String sql = dto.getSql();
|
||||||
|
|
||||||
|
Map<String, List<GeneratorVo>> result = Strings.isEmpty(sql)
|
||||||
? generatorService.generateCodeByDatabase(dto)
|
? generatorService.generateCodeByDatabase(dto)
|
||||||
: generatorService.generateCodeBySql(dto);
|
: generatorService.generateCodeBySql(dto);
|
||||||
return Result.success(result);
|
return Result.success(result);
|
||||||
|
@ -54,7 +57,10 @@ public class GeneratorController {
|
||||||
@Operation(summary = "打包下载", description = "将生成的代码打包为ZIP文件下载")
|
@Operation(summary = "打包下载", description = "将生成的代码打包为ZIP文件下载")
|
||||||
@PostMapping("downloadByZip")
|
@PostMapping("downloadByZip")
|
||||||
public ResponseEntity<byte[]> downloadByZip(@Valid @RequestBody VmsArgumentDto dto) {
|
public ResponseEntity<byte[]> downloadByZip(@Valid @RequestBody VmsArgumentDto dto) {
|
||||||
return Strings.isEmpty(dto.getSql())
|
// 判断当前是使用 SQL语句 生成还是 数据库生成
|
||||||
|
String sql = dto.getSql();
|
||||||
|
|
||||||
|
return Strings.isEmpty(sql)
|
||||||
? generatorService.downloadByZipByDatabase(dto)
|
? generatorService.downloadByZipByDatabase(dto)
|
||||||
: generatorService.downloadByZipBySqL(dto);
|
: generatorService.downloadByZipBySqL(dto);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,23 +18,23 @@ import java.util.List;
|
||||||
public class VmsArgumentDto {
|
public class VmsArgumentDto {
|
||||||
|
|
||||||
@Schema(name = "author", description = "作者名称")
|
@Schema(name = "author", description = "作者名称")
|
||||||
String author;
|
String author = "";
|
||||||
|
|
||||||
@Schema(name = "packageName", description = "包名称")
|
@Schema(name = "packageName", description = "包名称")
|
||||||
@NotBlank(message = "包名不能为空")
|
@NotBlank(message = "包名不能为空")
|
||||||
String packageName;
|
String packageName;
|
||||||
|
|
||||||
@Schema(name = "requestMapping", description = "requestMapping 名称")
|
@Schema(name = "requestMapping", description = "requestMapping 名称")
|
||||||
String requestMapping;
|
String requestMapping = "";
|
||||||
|
|
||||||
@Schema(name = "tableNames", description = "表名列表")
|
@Schema(name = "tableNames", description = "表名列表")
|
||||||
private List<String> tableNames;
|
private List<String> tableNames;
|
||||||
|
|
||||||
@Schema(name = "simpleDateFormat", description = "时间格式")
|
@Schema(name = "simpleDateFormat", description = "时间格式")
|
||||||
private String simpleDateFormat;
|
private String simpleDateFormat = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
|
||||||
@Schema(name = "tablePrefixes", description = "去除表前缀")
|
@Schema(name = "tablePrefixes", description = "去除表前缀")
|
||||||
private String tablePrefixes;
|
private String tablePrefixes = "";
|
||||||
|
|
||||||
@Schema(name = "path", description = "路径")
|
@Schema(name = "path", description = "路径")
|
||||||
@NotEmpty(message = "表名称不能为空")
|
@NotEmpty(message = "表名称不能为空")
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
package cn.bunny.service.helper;
|
|
||||||
|
|
||||||
import cn.bunny.core.template.VmsTBaseTemplateGenerator;
|
|
||||||
import cn.bunny.domain.dto.VmsArgumentDto;
|
|
||||||
import cn.bunny.domain.entity.ColumnMetaData;
|
|
||||||
import cn.bunny.domain.entity.TableMetaData;
|
|
||||||
import cn.bunny.domain.vo.GeneratorVo;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 代码生成服务辅助类
|
|
||||||
* 提供生成器和响应实体的构建方法
|
|
||||||
*/
|
|
||||||
public class GeneratorServiceImplHelper {
|
|
||||||
|
|
||||||
private static final String ZIP_FILE_PREFIX = "code-";
|
|
||||||
private static final String ZIP_FILE_SUFFIX = ".zip";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取生成器流
|
|
||||||
*
|
|
||||||
* @param dto 生成参数
|
|
||||||
* @param tableMeta 表元数据
|
|
||||||
* @param columns 列信息
|
|
||||||
* @return 生成器流
|
|
||||||
*/
|
|
||||||
public static Stream<GeneratorVo> getGeneratorStream(VmsArgumentDto dto, TableMetaData tableMeta, List<ColumnMetaData> columns) {
|
|
||||||
return dto.getPath().parallelStream().map(path -> {
|
|
||||||
VmsTBaseTemplateGenerator generator = new VmsTBaseTemplateGenerator(dto, path, tableMeta);
|
|
||||||
String code = generator.generateCode(tableMeta, columns).toString();
|
|
||||||
|
|
||||||
return GeneratorVo.builder()
|
|
||||||
.id(UUID.randomUUID().toString())
|
|
||||||
.code(code)
|
|
||||||
.comment(tableMeta.getComment())
|
|
||||||
.tableName(tableMeta.getTableName())
|
|
||||||
.path(VmsGeneratorPathHelper.processVmPath(dto, path, tableMeta.getTableName()))
|
|
||||||
.build();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建ZIP下载响应实体
|
|
||||||
*
|
|
||||||
* @param zipBytes ZIP文件字节数组
|
|
||||||
* @return 响应实体
|
|
||||||
*/
|
|
||||||
public static ResponseEntity<byte[]> getResponseEntity(byte[] zipBytes) {
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.add("Content-Disposition", "attachment; filename=" + generateZipFilename());
|
|
||||||
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
||||||
headers.add("Pragma", "no-cache");
|
|
||||||
headers.add("Expires", "0");
|
|
||||||
|
|
||||||
return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成ZIP文件名
|
|
||||||
*/
|
|
||||||
private static String generateZipFilename() {
|
|
||||||
return ZIP_FILE_PREFIX + UUID.randomUUID().toString().split("-")[0] + ZIP_FILE_SUFFIX;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +1,24 @@
|
||||||
package cn.bunny.service.impl;
|
package cn.bunny.service.impl;
|
||||||
|
|
||||||
import cn.bunny.core.ZipFileService;
|
|
||||||
import cn.bunny.core.provider.IMetadataProvider;
|
import cn.bunny.core.provider.IMetadataProvider;
|
||||||
|
import cn.bunny.core.template.VmsTBaseTemplateGenerator;
|
||||||
import cn.bunny.domain.dto.VmsArgumentDto;
|
import cn.bunny.domain.dto.VmsArgumentDto;
|
||||||
import cn.bunny.domain.entity.ColumnMetaData;
|
import cn.bunny.domain.entity.ColumnMetaData;
|
||||||
import cn.bunny.domain.entity.TableMetaData;
|
import cn.bunny.domain.entity.TableMetaData;
|
||||||
import cn.bunny.domain.vo.GeneratorVo;
|
import cn.bunny.domain.vo.GeneratorVo;
|
||||||
import cn.bunny.service.GeneratorService;
|
import cn.bunny.service.GeneratorService;
|
||||||
import cn.bunny.service.helper.GeneratorServiceImplHelper;
|
import cn.bunny.service.helper.VmsGeneratorPathHelper;
|
||||||
|
import cn.bunny.utils.ZipFileUtil;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 代码生成服务实现类
|
* 代码生成服务实现类
|
||||||
|
@ -29,7 +30,6 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||||
|
|
||||||
private final IMetadataProvider databaseMetadataProvider;
|
private final IMetadataProvider databaseMetadataProvider;
|
||||||
private final IMetadataProvider sqlMetadataProvider;
|
private final IMetadataProvider sqlMetadataProvider;
|
||||||
private final ZipFileService zipFileService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 代码生成方法---数据库生成
|
* 代码生成方法---数据库生成
|
||||||
|
@ -43,7 +43,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||||
.flatMap(tableName -> {
|
.flatMap(tableName -> {
|
||||||
TableMetaData tableMeta = databaseMetadataProvider.getTableMetadata(tableName);
|
TableMetaData tableMeta = databaseMetadataProvider.getTableMetadata(tableName);
|
||||||
List<ColumnMetaData> columns = databaseMetadataProvider.getColumnInfoList(tableName);
|
List<ColumnMetaData> columns = databaseMetadataProvider.getColumnInfoList(tableName);
|
||||||
return GeneratorServiceImplHelper.getGeneratorStream(dto, tableMeta, columns);
|
return getGeneratorStream(dto, tableMeta, columns);
|
||||||
})
|
})
|
||||||
.collect(Collectors.groupingBy(GeneratorVo::getTableName));
|
.collect(Collectors.groupingBy(GeneratorVo::getTableName));
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||||
TableMetaData tableMeta = sqlMetadataProvider.getTableMetadata(sql);
|
TableMetaData tableMeta = sqlMetadataProvider.getTableMetadata(sql);
|
||||||
List<ColumnMetaData> columns = sqlMetadataProvider.getColumnInfoList(sql);
|
List<ColumnMetaData> columns = sqlMetadataProvider.getColumnInfoList(sql);
|
||||||
|
|
||||||
List<GeneratorVo> generatorVoList = GeneratorServiceImplHelper.getGeneratorStream(dto, tableMeta, columns).toList();
|
List<GeneratorVo> generatorVoList = getGeneratorStream(dto, tableMeta, columns).toList();
|
||||||
|
|
||||||
Map<String, List<GeneratorVo>> map = new HashMap<>();
|
Map<String, List<GeneratorVo>> map = new HashMap<>();
|
||||||
map.put(tableMeta.getTableName(), generatorVoList);
|
map.put(tableMeta.getTableName(), generatorVoList);
|
||||||
|
@ -87,12 +87,47 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||||
*/
|
*/
|
||||||
private ResponseEntity<byte[]> downloadByZip(VmsArgumentDto dto,
|
private ResponseEntity<byte[]> downloadByZip(VmsArgumentDto dto,
|
||||||
Function<VmsArgumentDto, Map<String, List<GeneratorVo>>> generator) {
|
Function<VmsArgumentDto, Map<String, List<GeneratorVo>>> generator) {
|
||||||
|
// 调用生成函数
|
||||||
List<GeneratorVo> generatorVoList = generator.apply(dto)
|
List<GeneratorVo> generatorVoList = generator.apply(dto)
|
||||||
.values().stream()
|
.values().stream()
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
byte[] zipBytes = zipFileService.createZipFile(generatorVoList);
|
// 创建Zip文件
|
||||||
return GeneratorServiceImplHelper.getResponseEntity(zipBytes);
|
byte[] zipBytes = ZipFileUtil.createZipFile(generatorVoList);
|
||||||
|
|
||||||
|
// 设置返回给前端的文件名
|
||||||
|
String zipFilename = "code-" + UUID.randomUUID().toString().split("-")[0] + ".zip";
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.add("Content-Disposition", "attachment; filename=" + zipFilename);
|
||||||
|
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||||
|
headers.add("Pragma", "no-cache");
|
||||||
|
headers.add("Expires", "0");
|
||||||
|
|
||||||
|
return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取生成器流
|
||||||
|
*
|
||||||
|
* @param dto 生成参数
|
||||||
|
* @param tableMeta 表元数据
|
||||||
|
* @param columns 列信息
|
||||||
|
* @return 生成器流
|
||||||
|
*/
|
||||||
|
public Stream<GeneratorVo> getGeneratorStream(VmsArgumentDto dto, TableMetaData tableMeta, List<ColumnMetaData> columns) {
|
||||||
|
return dto.getPath().parallelStream().map(path -> {
|
||||||
|
VmsTBaseTemplateGenerator generator = new VmsTBaseTemplateGenerator(dto, path, tableMeta);
|
||||||
|
String code = generator.generateCode(tableMeta, columns).toString();
|
||||||
|
|
||||||
|
return GeneratorVo.builder()
|
||||||
|
.id(UUID.randomUUID().toString())
|
||||||
|
.code(code)
|
||||||
|
.comment(tableMeta.getComment())
|
||||||
|
.tableName(tableMeta.getTableName())
|
||||||
|
.path(VmsGeneratorPathHelper.processVmPath(dto, path, tableMeta.getTableName()))
|
||||||
|
.build();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package cn.bunny.core;
|
package cn.bunny.utils;
|
||||||
|
|
||||||
import cn.bunny.domain.vo.GeneratorVo;
|
import cn.bunny.domain.vo.GeneratorVo;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -16,9 +14,7 @@ import java.util.zip.ZipOutputStream;
|
||||||
* <p>
|
* <p>
|
||||||
* 提供将生成的代码模板打包为ZIP文件并支持下载的功能
|
* 提供将生成的代码模板打包为ZIP文件并支持下载的功能
|
||||||
*/
|
*/
|
||||||
@Service
|
public class ZipFileUtil {
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ZipFileService {
|
|
||||||
|
|
||||||
private static final String FILE_EXTENSION = ".vm";
|
private static final String FILE_EXTENSION = ".vm";
|
||||||
private static final String UTF_8 = StandardCharsets.UTF_8.name();
|
private static final String UTF_8 = StandardCharsets.UTF_8.name();
|
||||||
|
@ -30,7 +26,7 @@ public class ZipFileService {
|
||||||
* @return ZIP文件字节数组
|
* @return ZIP文件字节数组
|
||||||
* @throws RuntimeException 打包失败时抛出
|
* @throws RuntimeException 打包失败时抛出
|
||||||
*/
|
*/
|
||||||
public byte[] createZipFile(List<GeneratorVo> generatorVoList) {
|
public static byte[] createZipFile(List<GeneratorVo> generatorVoList) {
|
||||||
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream, StandardCharsets.UTF_8)) {
|
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream, StandardCharsets.UTF_8)) {
|
||||||
|
|
||||||
|
@ -48,7 +44,7 @@ public class ZipFileService {
|
||||||
* @param generatorVo 代码生成结果对象,包含文件路径和内容
|
* @param generatorVo 代码生成结果对象,包含文件路径和内容
|
||||||
* @throws RuntimeException 当文件添加失败时抛出,包含失败文件路径信息
|
* @throws RuntimeException 当文件添加失败时抛出,包含失败文件路径信息
|
||||||
*/
|
*/
|
||||||
private void addToZip(ZipOutputStream zipOutputStream, GeneratorVo generatorVo) {
|
private static void addToZip(ZipOutputStream zipOutputStream, GeneratorVo generatorVo) {
|
||||||
try {
|
try {
|
||||||
String entryPath = generatorVo.getPath().replace(FILE_EXTENSION, "");
|
String entryPath = generatorVo.getPath().replace(FILE_EXTENSION, "");
|
||||||
zipOutputStream.putNextEntry(new ZipEntry(entryPath));
|
zipOutputStream.putNextEntry(new ZipEntry(entryPath));
|
|
@ -14,4 +14,7 @@ spring:
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
url: jdbc:mysql://${bunny.master.host}:${bunny.master.port}/${bunny.master.database}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
|
url: jdbc:mysql://${bunny.master.host}:${bunny.master.port}/${bunny.master.database}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
|
||||||
username: ${bunny.master.username}
|
username: ${bunny.master.username}
|
||||||
password: ${bunny.master.password}
|
password: ${bunny.master.password}
|
||||||
|
hikari:
|
||||||
|
maximum-pool-size: 20
|
||||||
|
connection-timeout: 30000
|
Loading…
Reference in New Issue