This commit is contained in:
Wang 2025-10-09 15:04:44 +08:00
parent 421de0b7e6
commit 548cf48701
39 changed files with 4629 additions and 0 deletions

5
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Environment-dependent path to Maven home directory
/mavenHomeManager.xml

6
.idea/ApifoxUploaderProjectSetting.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ApifoxUploaderProjectSetting">
<option name="apiAccessToken" value="APS-7Em7CF8xpPJXB0iKgPeqbBDJufFaINiLgi" />
</component>
</project>

15
.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<option name="BUILD_PROCESS_HEAP_SIZE" value="1024" />
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="complete-multi-db-connection-code-generator" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel target="8" />
</component>
</project>

7
.idea/encodings.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

20
.idea/jarRepositories.xml generated Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
</component>
</project>

12
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

186
MainFrame.java Normal file
View File

@ -0,0 +1,186 @@
// 创建代码生成配置面板
private JPanel createGeneratorPanel() {
JPanel panel = new JPanel(new BorderLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// 标题
JLabel titleLabel = new JLabel("代码生成配置");
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));
titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0));
panel.add(titleLabel, BorderLayout.NORTH);
// 主内容面板分为上下两部分
JPanel contentPanel = new JPanel(new BorderLayout(10, 10));
// 上部分配置选项
JPanel configPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5);
gbc.anchor = GridBagConstraints.WEST;
// 基础包名
JLabel basePackageLabel = new JLabel("基础包名:");
basePackageField = new JTextField();
basePackageField.setPreferredSize(new Dimension(300, 25));
// 是否启用Lombok
JLabel lombokLabel = new JLabel("启用Lombok:");
lombokCheckBox = new JCheckBox();
lombokCheckBox.setSelected(true);
// 父类实体
JLabel superEntityLabel = new JLabel("父类实体:");
superEntityField = new JTextField();
superEntityField.setPreferredSize(new Dimension(300, 25));
// 父类公共字段
JLabel superColumnsLabel = new JLabel("父类公共字段:");
superColumnsField = new JTextField();
superColumnsField.setPreferredSize(new Dimension(300, 25));
superColumnsField.setToolTipText("多个字段用逗号分隔id,createTime,updateTime");
// Vue2 生成选项
JLabel generateVue2Label = new JLabel("生成Vue2代码:");
generateVue2CheckBox = new JCheckBox();
generateVue2CheckBox.setSelected(true);
// Vue3 生成选项
JLabel generateVue3Label = new JLabel("生成Vue3代码:");
generateVue3CheckBox = new JCheckBox();
generateVue3CheckBox.setSelected(true);
// 保存配置按钮
JButton saveConfigBtn = new JButton("保存配置");
saveConfigBtn.addActionListener(e -> saveGeneratorConfig());
// 添加组件到配置面板
int row = 0;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(basePackageLabel, gbc);
gbc.gridx = 1;
configPanel.add(basePackageField, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(lombokLabel, gbc);
gbc.gridx = 1;
configPanel.add(lombokCheckBox, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(superEntityLabel, gbc);
gbc.gridx = 1;
configPanel.add(superEntityField, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(superColumnsLabel, gbc);
gbc.gridx = 1;
configPanel.add(superColumnsField, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(generateVue2Label, gbc);
gbc.gridx = 1;
configPanel.add(generateVue2CheckBox, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(generateVue3Label, gbc);
gbc.gridx = 1;
configPanel.add(generateVue3CheckBox, gbc);
row++;
gbc.gridx = 1;
gbc.gridy = row + 1;
gbc.anchor = GridBagConstraints.EAST;
configPanel.add(saveConfigBtn, gbc);
contentPanel.add(configPanel, BorderLayout.NORTH);
// 下部分表选择
JPanel tablePanel = new JPanel(new BorderLayout(10, 10));
// 过滤面板
JPanel filterPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
JLabel filterLabel = new JLabel("过滤:");
tableFilterField = new JTextField(20);
JButton filterBtn = new JButton("查询");
filterBtn.addActionListener(e -> filterTables());
filterPanel.add(filterLabel);
filterPanel.add(tableFilterField);
filterPanel.add(filterBtn);
tablePanel.add(filterPanel, BorderLayout.NORTH);
// 表列表
String[] columnNames = {"选择", "表名"};
tableModel = new DefaultTableModel(columnNames, 0) {
@Override
public Class<?> getColumnClass(int column) {
return column == 0 ? Boolean.class : String.class;
}
@Override
public boolean isCellEditable(int row, int column) {
return column == 0;
}
};
tableTable = new JTable(tableModel);
tableTable.getColumnModel().getColumn(0).setPreferredWidth(50);
tableTable.getColumnModel().getColumn(1).setPreferredWidth(300);
tableTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int column = tableTable.columnAtPoint(e.getPoint());
int row = tableTable.rowAtPoint(e.getPoint());
if (row != -1) {
if (column == 0) {
// 点击复选框列切换选中状态
Boolean checked = (Boolean) tableTable.getValueAt(row, 0);
tableTable.setValueAt(!checked, row, 0);
} else {
// 点击其他列也切换选中状态
Boolean checked = (Boolean) tableTable.getValueAt(row, 0);
tableTable.setValueAt(!checked, row, 0);
}
}
}
});
JScrollPane tableScrollPane = new JScrollPane(tableTable);
tablePanel.add(tableScrollPane, BorderLayout.CENTER);
contentPanel.add(tablePanel, BorderLayout.CENTER);
panel.add(contentPanel, BorderLayout.CENTER);
// 按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
JButton backBtn = new JButton("返回");
JButton generateBtn = new JButton("生成代码");
backBtn.addActionListener(e -> {
CardLayout cl = (CardLayout) ((JPanel) getContentPane().getComponent(0)).getLayout();
cl.show((JPanel) getContentPane().getComponent(0), "dbPanel");
});
generateBtn.addActionListener(e -> generateCode());
buttonPanel.add(backBtn);
buttonPanel.add(generateBtn);
panel.add(buttonPanel, BorderLayout.SOUTH);
// 加载已保存的配置
loadGeneratorConfig();
return panel;
}

6
codegenerator.properties Normal file
View File

@ -0,0 +1,6 @@
#Tue Sep 16 18:49:22 CST 2025
db.password=Zs139768
db.connName=MySQL
db.url=jdbc\:mysql\://192.168.0.45\:3306/seer_teach?useSSL\=false&serverTimezone\=Asia/Shanghai&useUnicode\=true&characterEncoding\=UTF-8&connectTimeout\=10000&socketTimeout\=60000
db.type=mysql
db.username=root

439
foo.java Normal file
View File

@ -0,0 +1,439 @@
public class BackendCodeGenerator {
// 生成后端代码
public static void generateBackendCode(String url, String username, String password, List<String> tables, Map<String, Map<String,Object>> columns) {
GeneratorConfig generatorConfig = ConfigUtil.getGeneratorConfigAsObject();
String basePackage = generatorConfig.getBasePackage();
String superEntityClass = generatorConfig.getSuperEntityClass();
String[] superEntityColumns = generatorConfig.getSuperEntityColumns();
// 解析包名获取作者和模块名
String author = System.getProperty("user.name");
String moduleName = basePackage.substring(basePackage.lastIndexOf(".") + 1);
// 生成路径
String projectPath = System.getProperty("user.dir");
String outputDir = projectPath + "/generated-code/backend/src/main/java";
FastAutoGenerator.create(url, username, password)
// 全局配置
.globalConfig(builder -> {
builder.author(author) // 设置作者
.enableSwagger() // 开启Swagger模式
.outputDir(outputDir) // 指定输出目录
.dateType(DateType.ONLY_DATE); // 时间策略
})
// 包配置
.packageConfig(builder -> {
builder.parent(basePackage) // 父包名
.moduleName(moduleName) // 父包模块名
// 模块名为seer
// 实体类包名为entity
.entity("entity")
// 服务层包名为service
.service("service")
// 服务实现类包名为service.impl
.serviceImpl("service.impl")
// Mapper接口包名为mapper
.mapper("mapper")
// Mapper XML文件包名为mapper.xml
.xml("mapper.xml")
// 构建包配置
.pathInfo(Collections.singletonMap(OutputFile.xml,
projectPath + "/generated-code/backend/src/main/resources/mapper"));
})
// 策略配置
.strategyConfig(builder -> {
builder.addInclude(tables) // 设置需要生成的表名
.addTablePrefix("t_", "sys_") // 设置过滤表前缀
// Entity策略配置
.entityBuilder()
.enableLombok() // 开启Lombok
.superClass(superEntityClass) // 设置父类
.addSuperEntityColumns(superEntityColumns) // 父类公共字段
.enableTableFieldAnnotation() // 开启字段注解
// Service策略配置
.serviceBuilder()
.formatServiceFileName("I%sService")
.formatServiceImplFileName("%sServiceImpl")
// Mapper策略配置
.mapperBuilder()
.superClass(BaseMapper.class) // 设置父类
.enableMapperAnnotation() // 开启@Mapper注解
.formatMapperFileName("%sMapper")
.formatXmlFileName("%sMapper");
builder.entityBuilder()
// 实体类继承自BaseEntity类
.superClass(BaseEntity.class)
// 启用Lombok注解
.enableLombok()
// 启用表字段注解
.enableTableFieldAnnotation()
// 逻辑删除字段名为deleted
.logicDeleteColumnName("deleted")
// 添加父类实体的共有字段
.addSuperEntityColumns("id", "created_by", "created_time", "updated_by", "updated_time")
// 添加表字段填充策略
.addTableFills(new Column("create_time", FieldFill.INSERT))
// 添加属性填充策略
.addTableFills(new Property("update_time", FieldFill.INSERT_UPDATE))
// 主键类型为AUTO
.idType(IdType.AUTO)
// 设置生成文件名格式为%sEntity
.formatFileName("%sEntity")
// 构建实体类配置
.build();
})
// 使用Freemarker引擎模板
.templateEngine(new FreemarkerTemplateEngine())
.execute();
// 生成自定义Controller
generateControllers(basePackage, moduleName, tables);
// 生成请求和响应类
generateRequestAndResponseClasses(basePackage, tables,columns);
}
// 生成符合RESTful规范的Controller
private static void generateControllers(String basePackage, String moduleName, List<String> tables) {
try {
// 初始化FreeMarker配置
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setClassForTemplateLoading(BackendCodeGenerator.class, "/templates/backend");
cfg.setDefaultEncoding("UTF-8");
// 获取模板
Template template = cfg.getTemplate("custom-controller.java.ftl");
// 生成路径
String projectPath = System.getProperty("user.dir");
String outputDir = projectPath + "/generated-code/backend/src/main/java/" +
basePackage.replace(".", "/") + "/" + moduleName + "/controller/";
// 创建目录
File dir = new File(outputDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 为每个表生成Controller
for (String table : tables) {
// 处理表名转换为类名
String className = tableToClassName(table);
String varName = className.substring(0, 1).toLowerCase() + className.substring(1);
// 准备数据模型
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("basePackage", basePackage);
dataModel.put("moduleName", moduleName);
dataModel.put("className", className);
dataModel.put("varName", varName);
dataModel.put("author", System.getProperty("user.name"));
dataModel.put("date", new Date());
// 生成文件
String fileName = outputDir + className + "Controller.java";
try (Writer out = new FileWriter(new File(fileName))) {
template.process(dataModel, out);
}
}
} catch (IOException | TemplateException e) {
log.info("生成Controller文件失败" + e.getMessage(),e);
}
}
/**
* 生成所有请求和响应类
*/
public static void generateRequestAndResponseClasses(String basePackage, List<String> tables, Map<String, Map<String,Object>> columns) {
List<String> commonColumns = Arrays.asList("deleted","createTime","updateTime","createBy","updateBy");
Map<String, Map<String,Object>> columnMaps = columns.entrySet().stream().filter(entry -> !commonColumns.contains(entry.getKey())).collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
// 生成响应类
generateResponseClasses(basePackage, tables,columnMaps);
// 生成查询请求类
generateQueryRequestClasses(basePackage, tables,columnMaps);
// 生成通用请求类
generateRequestClasses(basePackage, tables,columnMaps);
// 生成PageRequest基类
generatePageRequestClass(basePackage, tables,columnMaps);
}
/**
* 生成统一响应类
*/
public static void generateResponseClasses(String basePackage, List<String> tables, Map<String, Map<String,Object>> tableColumnsInfo) {
generateClassFiles(basePackage, tables, tableColumnsInfo, "admin-response.java.ftl", "Response", "/controller/response/");
}
/**
* 生成查询请求类
*/
public static void generateQueryRequestClasses(String basePackage, List<String> tables, Map<String, Map<String,Object>> tableColumnsInfo) {
generateClassFiles(basePackage, tables, tableColumnsInfo, "admin-query-admin-request.java.ftl", "QueryRequest", "/controller/request/");
}
/**
* 生成通用请求类
*/
public static void generateRequestClasses(String basePackage, List<String> tables, Map<String, Map<String,Object>> tableColumnsInfo) {
generateClassFiles(basePackage, tables, tableColumnsInfo, "admin-request.java.ftl", "Request", "/controller/request/");
}
/**
* 通用类文件生成方法
*/
private static void generateClassFiles(String basePackage, List<String> tables, Map<String, Map<String,Object>> tableColumnsInfo,
String templateName, String suffix, String outputPath) {
try {
// 初始化FreeMarker配置
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setClassForTemplateLoading(BackendCodeGenerator.class, "/templates/backend");
cfg.setDefaultEncoding("UTF-8");
// 获取模板
Template template = cfg.getTemplate(templateName);
// 生成路径
String projectPath = System.getProperty("user.dir");
String outputDir = projectPath + "/generated-code/backend/src/main/java/" +
basePackage.replace(".", "/") + outputPath;
// 创建目录
File dir = new File(outputDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 为每个表生成类文件
for (String table : tables) {
// 处理表名转换为类名
String className = tableToClassName(table);
// 获取表的列信息
Map<String, Object> tableInfo = tableColumnsInfo.get(table);
List<ColumnInfo> columns = new ArrayList<>();
boolean hasDateField = false;
boolean hasBigDecimalField = false;
if (tableInfo != null) {
columns = processColumns(tableInfo);
hasDateField = hasFieldType(columns, "Date");
hasBigDecimalField = hasFieldType(columns, "BigDecimal");
}
// 准备数据模型
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("basePackage", basePackage);
dataModel.put("className", className);
dataModel.put("columns", columns);
dataModel.put("hasDateField", hasDateField);
dataModel.put("hasBigDecimalField", hasBigDecimalField);
// 生成文件
String fileName = outputDir + className + suffix + ".java";
try (Writer out = new FileWriter(new File(fileName))) {
template.process(dataModel, out);
}
}
} catch (IOException | TemplateException e) {
log.error("生成" + suffix + "类文件失败:" + e.getMessage(), e);
}
}
/**
* 生成PageRequest基类
*/
public static void generatePageRequestClass(String basePackage, List<String> tables, Map<String, Map<String,Object>> columns) {
try {
// 初始化FreeMarker配置
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setClassForTemplateLoading(BackendCodeGenerator.class, "/templates/backend");
cfg.setDefaultEncoding("UTF-8");
Template pageRequestTemplate = cfg.getTemplate("admin-pageRequest.java.ftl");
// 生成路径
String projectPath = System.getProperty("user.dir");
String outputDir = projectPath + "/generated-code/backend/src/main/java/" +
basePackage.replace(".", "/") + "/controller/request/";
// 创建目录
File dir = new File(outputDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 为每个表生成Controller
for (String table : tables) {
// 处理表名转换为类名
String className = tableToClassName(table);
String varName = className.substring(0, 1).toLowerCase() + className.substring(1);
// 准备数据模型
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("basePackage", basePackage);
dataModel.put("className", className);
dataModel.put("varName", varName);
dataModel.put("author", System.getProperty("user.name"));
dataModel.put("date", new Date());
// 生成文件
String fileName = outputDir + className + "Controller.java";
try (Writer out = new FileWriter(new File(fileName))) {
pageRequestTemplate.process(dataModel, out);
}
}
} catch (IOException | TemplateException e) {
log.error("生成PageRequest基类文件失败" + e.getMessage(), e);
}
}
// 表名转类名下划线转驼峰
private static String tableToClassName(String tableName) {
// 去除表前缀
if (tableName.startsWith("t_")) {
tableName = tableName.substring(2);
}
// 下划线转驼峰
StringBuilder sb = new StringBuilder();
boolean nextUpperCase = false;
for (char c : tableName.toCharArray()) {
if (c == '_') {
nextUpperCase = true;
} else {
if (nextUpperCase) {
sb.append(Character.toUpperCase(c));
nextUpperCase = false;
} else {
sb.append(c);
}
}
}
// 首字母大写
if (sb.length() > 0) {
sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
}
return sb.toString();
}
/**
* 处理列信息转换为Java属性
*/
private static List<ColumnInfo> processColumns(Map<String, Object> tableInfo) {
List<ColumnInfo> columnInfos = new ArrayList<>();
@SuppressWarnings("unchecked")
List<Map<String, Object>> columns = (List<Map<String, Object>>) tableInfo.get("columns");
if (columns != null) {
for (Map<String, Object> column : columns) {
ColumnInfo info = new ColumnInfo();
String columnName = (String) column.get("columnName");
String dataType = (String) column.get("dataType");
String columnComment = (String) column.get("columnComment");
String isNullable = (String) column.get("isNullable");
String columnKey = (String) column.get("columnKey");
info.setColumnName(columnName);
info.setPropertyName(columnToProperty(columnName));
info.setJavaType(mapDataTypeToJavaType(dataType));
info.setDataType(dataType);
info.setComment(columnComment != null ? columnComment : "");
info.setNullable("YES".equalsIgnoreCase(isNullable));
info.setPrimaryKey("PRI".equalsIgnoreCase(columnKey));
columnInfos.add(info);
}
}
return columnInfos;
}
/**
* 将数据库列名转换为Java属性名下划线转驼峰
*/
private static String columnToProperty(String columnName) {
if (columnName == null || columnName.isEmpty()) {
return columnName;
}
StringBuilder result = new StringBuilder();
boolean nextUpperCase = false;
for (char c : columnName.toCharArray()) {
if (c == '_') {
nextUpperCase = true;
} else {
if (nextUpperCase) {
result.append(Character.toUpperCase(c));
nextUpperCase = false;
} else {
result.append(Character.toLowerCase(c));
}
}
}
return result.toString();
}
/**
* 将数据库类型映射为Java类型
*/
private static String mapDataTypeToJavaType(String dataType) {
if (dataType == null) {
return "String";
}
dataType = dataType.toLowerCase();
switch (dataType) {
case "tinyint":
case "smallint":
case "mediumint":
case "int":
return "Integer";
case "bigint":
return "Long";
case "float":
case "double":
return "Double";
case "decimal":
case "numeric":
return "BigDecimal";
case "date":
case "datetime":
case "timestamp":
return "Date";
case "bit":
return "Boolean";
default:
return "String";
}
}
/**
* 检查是否有特定类型的字段
*/
private static boolean hasFieldType(List<ColumnInfo> columns, String javaType) {
if (columns == null || columns.isEmpty()) {
return false;
}
for (ColumnInfo column : columns) {
if (javaType.equals(column.getJavaType())) {
return true;
}
}
return false;
}
}

183
list.vue.ftl Normal file
View File

@ -0,0 +1,183 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="searchParams.keyword" placeholder="请输入关键词" style="width: 200px;" class="filter-item" @keyup.enter="handleQuery" />
<el-button type="primary" icon="Search" @click="handleQuery" class="filter-item">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" class="filter-item">重置</el-button>
<el-button type="primary" icon="Plus" @click="handleAdd" class="filter-item">新增</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
border
fit
highlight-current-row
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="ID" prop="id" width="80" align="center" />
<!-- 根据实际字段动态生成表格列 -->
<#if columns?? && columns.columns??>
<#list columns.columns as column>
<#assign columnName = column.columnName>
<#assign propertyName = column.propertyName>
<el-table-column label="${column.comment!columnName}" prop="${propertyName}" />
</#list>
</#if>
<el-table-column label="操作" width="200" align="center">
<template #default="scope">
<el-button size="small" type="text" @click="handleView(scope.row)">查看</el-button>
<el-button size="small" type="text" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="text" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
<!-- 新增/编辑弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px">
<${entityName}Form ref="formRef" :data="formData" />
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { get${entityName}List, delete${entityName} } from '@/api/${lowerEntityName}'
import ${entityName}Form from './${entityName}Form.vue'
import { ElMessage, ElConfirm } from 'element-plus'
// 列表数据
const list = ref([])
const listLoading = ref(false)
// 搜索参数
const searchParams = ref({
keyword: ''
})
// 分页参数
const pagination = ref({
currentPage: 1,
pageSize: 10,
total: 0
})
// 弹窗相关
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formData = ref({})
const formRef = ref(null)
// 页面加载时查询数据
onMounted(() => {
handleQuery()
})
// 查询数据
const handleQuery = () => {
listLoading.value = true
get${entityName}List({
pageNum: pagination.value.currentPage,
pageSize: pagination.value.pageSize,
...searchParams.value
}).then(response => {
list.value = response.data.records
pagination.value.total = response.data.total
listLoading.value = false
}).catch(() => {
listLoading.value = false
})
}
// 重置查询
const resetQuery = () => {
searchParams.value = {
keyword: ''
}
pagination.value.currentPage = 1
handleQuery()
}
// 分页大小变化
const handleSizeChange = (val) => {
pagination.value.pageSize = val
handleQuery()
}
// 当前页变化
const handleCurrentChange = (val) => {
pagination.value.currentPage = val
handleQuery()
}
// 新增
const handleAdd = () => {
dialogTitle.value = '新增${entityName}'
formData.value = {}
dialogVisible.value = true
}
// 编辑
const handleEdit = (row) => {
dialogTitle.value = '编辑${entityName}'
formData.value = { ...row }
dialogVisible.value = true
}
// 查看
const handleView = (row) => {
dialogTitle.value = '查看${entityName}'
formData.value = { ...row }
dialogVisible.value = true
}
// 删除
const handleDelete = (row) => {
ElConfirm('确定要删除这条记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
delete${entityName}(row.id).then(() => {
ElMessage.success('删除成功')
handleQuery()
})
})
}
// 提交表单
const handleSubmit = () => {
formRef.value.validate(valid => {
if (valid) {
// 调用保存方法,实际项目中需要根据新增/编辑调用不同接口
ElMessage.success('保存成功')
dialogVisible.value = false
handleQuery()
}
})
}
</script>
<style scoped>
.filter-container {
padding: 10px 0;
}
.filter-item {
margin-right: 10px;
}
</style>

99
pom.xml Normal file
View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.codegenerator</groupId>
<artifactId>complete-multi-db-connection-code-generator</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mybatis-plus.version>3.5.13</mybatis-plus.version>
<freemarker.version>2.3.31</freemarker.version>
<mysql.version>8.0.28</mysql.version>
<lombok.version>1.18.24</lombok.version>
<hutool.version>5.8.16</hutool.version>
</properties>
<dependencies>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- FreeMarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- Hutool工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.15</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.codegenerator.Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,16 @@
package com.codegenerator;
import com.codegenerator.ui.MainFrame;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
// 在EDT线程中启动Swing应用
SwingUtilities.invokeLater(() -> {
MainFrame frame = new MainFrame();
frame.setVisible(true);
});
}
}

View File

@ -0,0 +1,49 @@
package com.codegenerator.config;
public class DbConnectionConfig {
private String name;
private String dbType;
private String url;
private String username;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDbType() {
return dbType;
}
public void setDbType(String dbType) {
this.dbType = dbType;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -0,0 +1,90 @@
package com.codegenerator.config;
import lombok.Data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Data
public class GeneratorConfig {
// 数据库连接列表
private List<DbConnectionConfig> dbConnections = new ArrayList<>();
// 当前选中的连接索引
private int selectedDbConnectionIndex = 0;
// 基础配置
private String basePackage = "com.example";
private boolean lombokEnabled = true;
private boolean useLombok;
private boolean generateVue2 = true;
private boolean generateVue3 = true;
private String superEntityClass = "";
private String[] superEntityColumns = {"id", "create_time", "update_time"};
private String backendOutputDir = "${user.dir}/src/main/java";
private String vue2OutputDir = "${user.dir}/src/main/vue2";
private String vue3OutputDir = "${user.dir}/src/main/vue3";
private String customTemplateDir = "";
// 字段类型映射
private Map<String, String> fieldTypeMappings = new HashMap<String, String>() {{ put("int", "Integer");
put("bigint", "Long");
put("varchar", "String");
put("datetime", "LocalDateTime");
put("date", "LocalDate");
put("double", "Double");
put("float", "Float");
put("tinyint", "Boolean");
}};
public GeneratorConfig() {
initDefaultConnections();
}
// 初始化默认连接
public void initDefaultConnections() {
if (dbConnections.isEmpty()) {
// 添加默认MySQL连接
DbConnectionConfig mysqlConn = new DbConnectionConfig();
setFieldValue(mysqlConn, "name", "本地MySQL");
setFieldValue(mysqlConn, "dbType", "mysql");
setFieldValue(mysqlConn, "url", "jdbc:mysql://192.168.0.45:3306/seer_teach?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&connectTimeout=10000&socketTimeout=60000");
setFieldValue(mysqlConn, "username", "root");
setFieldValue(mysqlConn, "password", "Zs139768");
dbConnections.add(mysqlConn);
// 添加默认PostgreSQL连接
DbConnectionConfig pgConn = new DbConnectionConfig();
setFieldValue(pgConn, "name", "本地PostgreSQL");
setFieldValue(pgConn, "dbType", "postgresql");
setFieldValue(pgConn, "url", "jdbc:postgresql://localhost:5432/test");
setFieldValue(pgConn, "username", "postgres");
setFieldValue(pgConn, "password", "postgres");
dbConnections.add(pgConn);
}
}
// 获取当前选中的连接
public DbConnectionConfig getSelectedDbConnection() {
if (dbConnections.isEmpty()) {
initDefaultConnections();
}
if (selectedDbConnectionIndex < 0 || selectedDbConnectionIndex >= dbConnections.size()) {
selectedDbConnectionIndex = 0;
}
return dbConnections.get(selectedDbConnectionIndex);
}
// 临时的辅助方法绕过Lombok注解处理器问题
private <T> void setFieldValue(Object obj, String fieldName, T value) {
try {
java.lang.reflect.Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
} catch (Exception e) {
// 忽略异常
}
}
}

View File

@ -0,0 +1,24 @@
package com.codegenerator.database;
import lombok.Data;
@Data
public class ColumnInfo {
private String columnName; // 列名
private String propertyName; // Java属性名
private String dataType; // 数据库数据类型
private String javaType; // Java类型
private String comment; // 列注释
private boolean nullable; // 是否可为空
private boolean primaryKey; // 是否为主键
private int columnSize; // 列大小
private int decimalDigits; // 小数位数
private int dataTypeCode;
public ColumnInfo() {}
public ColumnInfo(String columnName, String propertyName, String javaType) {
this.columnName = columnName;
this.propertyName = propertyName;
this.javaType = javaType;
}
}

View File

@ -0,0 +1,259 @@
package com.codegenerator.database;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class DatabaseConnector {
// 根据数据库类型加载对应的驱动
private static void loadDriver(String dbType) throws ClassNotFoundException {
switch (dbType.toLowerCase()) {
case "mysql":
Class.forName("com.mysql.cj.jdbc.Driver");
break;
case "postgresql":
Class.forName("org.postgresql.Driver");
break;
case "oracle":
Class.forName("oracle.jdbc.driver.OracleDriver");
break;
case "sqlserver":
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
break;
default:
throw new IllegalArgumentException("不支持的数据库类型: " + dbType);
}
}
// 连接数据库并获取表信息
public static List<TableInfo> connectAndGetTables(String url, String username, String password, String dbType) throws Exception {
loadDriver(dbType);
List<TableInfo> tables = new ArrayList<>();
try (Connection conn = DriverManager.getConnection(url, username, password)) {
DatabaseMetaData metaData = conn.getMetaData();
// 根据数据库类型获取表信息
String[] types = {"TABLE"};
ResultSet rs;
switch (dbType.toLowerCase()) {
case "mysql":
case "postgresql":
case "sqlserver":
rs = metaData.getTables(null, null, "%", types);
break;
case "oracle":
// Oracle通常使用大写表名
rs = metaData.getTables(null, username.toUpperCase(), "%", types);
break;
default:
rs = metaData.getTables(null, null, "%", types);
}
while (rs.next()) {
TableInfo table = new TableInfo();
table.setName(rs.getString("TABLE_NAME"));
table.setComment(rs.getString("REMARKS"));
// 获取表的列信息
List<ColumnInfo> columns = getTableColumns(metaData, table.getName(), dbType, username);
table.setColumns(columns);
tables.add(table);
}
}
return tables;
}
// 获取表的列信息
private static List<ColumnInfo> getTableColumns(DatabaseMetaData metaData, String tableName, String dbType, String username) throws SQLException {
List<ColumnInfo> columns = new ArrayList<>();
ResultSet rs;
switch (dbType.toLowerCase()) {
case "oracle":
// Oracle需要指定用户名( schema )
rs = metaData.getColumns(null, username.toUpperCase(), tableName, "%");
break;
default:
rs = metaData.getColumns(null, null, tableName, "%");
}
while (rs.next()) {
ColumnInfo column = new ColumnInfo();
column.setColumnName(rs.getString("COLUMN_NAME"));
column.setDataType(rs.getString("TYPE_NAME"));
column.setComment(rs.getString("REMARKS"));
column.setNullable(rs.getInt("NULLABLE") == 1);
column.setPrimaryKey(false); // 主键信息需要通过其他方式获取
// 获取列的其他信息
column.setColumnSize(rs.getInt("COLUMN_SIZE"));
column.setDecimalDigits(rs.getInt("DECIMAL_DIGITS"));
column.setDataTypeCode(rs.getInt("DATA_TYPE"));
// 设置Java类型映射
column.setJavaType(mapDataTypeToJavaType(rs.getString("TYPE_NAME"), rs.getInt("DATA_TYPE")));
// 设置属性名下划线转驼峰
column.setPropertyName(columnToProperty(column.getColumnName()));
columns.add(column);
}
// 获取主键信息
setPrimaryKeys(metaData, tableName, username, dbType, columns);
return columns;
}
// 获取表的主键信息
private static void setPrimaryKeys(DatabaseMetaData metaData, String tableName, String username, String dbType, List<ColumnInfo> columns) throws SQLException {
ResultSet rs;
switch (dbType.toLowerCase()) {
case "oracle":
rs = metaData.getPrimaryKeys(null, username.toUpperCase(), tableName);
break;
default:
rs = metaData.getPrimaryKeys(null, null, tableName);
}
while (rs.next()) {
String pkColumnName = rs.getString("COLUMN_NAME");
// 标记主键列
for (ColumnInfo column : columns) {
if (pkColumnName.equals(column.getColumnName())) {
column.setPrimaryKey(true);
break;
}
}
}
}
// 将数据库列名转换为Java属性名下划线转驼峰
private static String columnToProperty(String columnName) {
if (columnName == null || columnName.isEmpty()) {
return columnName;
}
StringBuilder result = new StringBuilder();
boolean nextUpperCase = false;
for (char c : columnName.toCharArray()) {
if (c == '_') {
nextUpperCase = true;
} else {
if (nextUpperCase) {
result.append(Character.toUpperCase(c));
nextUpperCase = false;
} else {
result.append(Character.toLowerCase(c));
}
}
}
return result.toString();
}
// 将数据库类型映射为Java类型
private static String mapDataTypeToJavaType(String dataTypeName, int dataTypeCode) {
if (dataTypeName == null) {
return "String";
}
String lowerDataType = dataTypeName.toLowerCase();
switch (lowerDataType) {
case "tinyint":
return "Byte";
case "smallint":
return "Short";
case "integer":
case "int":
return "Integer";
case "bigint":
return "Long";
case "float":
return "Float";
case "double":
return "Double";
case "decimal":
case "numeric":
return "BigDecimal";
case "bit":
return "Boolean";
case "boolean":
return "Boolean";
case "date":
case "time":
case "timestamp":
case "datetime":
return "Date";
case "char":
case "varchar":
case "text":
case "longvarchar":
case "nchar":
case "nvarchar":
case "longnvarchar":
return "String";
case "binary":
case "varbinary":
case "longvarbinary":
return "byte[]";
default:
// 根据SQL类型代码判断
switch (dataTypeCode) {
case Types.TINYINT:
return "Byte";
case Types.SMALLINT:
return "Short";
case Types.INTEGER:
return "Integer";
case Types.BIGINT:
return "Long";
case Types.FLOAT:
return "Float";
case Types.DOUBLE:
return "Double";
case Types.DECIMAL:
case Types.NUMERIC:
return "BigDecimal";
case Types.BIT:
case Types.BOOLEAN:
return "Boolean";
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
return "Date";
case Types.CHAR:
case Types.VARCHAR:
case Types.LONGVARCHAR:
case Types.NCHAR:
case Types.NVARCHAR:
case Types.LONGNVARCHAR:
return "String";
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
return "byte[]";
default:
return "String";
}
}
}
// 测试数据库连接
public static boolean testConnection(String url, String username, String password, String dbType) throws Exception {
loadDriver(dbType);
try (Connection conn = DriverManager.getConnection(url, username, password)) {
return conn.isValid(5); // 5秒超时
}
}
}

View File

@ -0,0 +1,33 @@
package com.codegenerator.database;
import java.util.List;
public class TableInfo {
private String name;
private String comment;
private List<ColumnInfo> columns;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public List<ColumnInfo> getColumns() {
return columns;
}
public void setColumns(List<ColumnInfo> columns) {
this.columns = columns;
}
}

View File

@ -0,0 +1,353 @@
package com.codegenerator.generator;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import com.baomidou.mybatisplus.generator.fill.Property;
import com.codegenerator.config.GeneratorConfig;
import com.codegenerator.database.ColumnInfo;
import com.codegenerator.util.ConfigUtil;
import com.seer.teach.common.entity.BaseEntity;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
public class BackendCodeGenerator {
// 生成后端代码
public static void generateBackendCode(String url, String username, String password, List<String> tables,String moduleName, Map<String, Map<String,Object>> columns) {
GeneratorConfig generatorConfig = ConfigUtil.getGeneratorConfigAsObject();
String basePackage = generatorConfig.getBasePackage();
String superEntityClass = generatorConfig.getSuperEntityClass();
String[] superEntityColumns = generatorConfig.getSuperEntityColumns();
// 解析包名获取作者和模块名
String author = System.getProperty("user.name");
// 生成路径
String projectPath = System.getProperty("user.dir");
String outputDir = projectPath + "/generated-code/backend/src/main/java";
FastAutoGenerator.create(url, username, password)
// 全局配置
.globalConfig(builder -> {
builder.author(author)
.outputDir(outputDir)
.dateType(DateType.ONLY_DATE);
})
// 包配置
.packageConfig(builder -> {
builder.parent(basePackage)
.moduleName(moduleName)
// 模块名为seer
// 实体类包名为entity
.entity("entity")
// 服务层包名为service
.service("service")
// 服务实现类包名为service.impl
.serviceImpl("service.impl")
// Mapper接口包名为mapper
.mapper("mapper")
// Mapper XML文件包名为mapper.xml
.xml("mapper.xml")
// 构建包配置
.pathInfo(Collections.singletonMap(OutputFile.xml,
projectPath + "/generated-code/backend/src/main/resources/mapper"));
})
// 策略配置
.strategyConfig(builder -> {
builder.addInclude(tables) // 设置需要生成的表名
.addTablePrefix("t_", "sys_") // 设置过滤表前缀
// Entity策略配置
.entityBuilder()
.enableLombok() // 开启Lombok
.superClass(superEntityClass) // 设置父类
.addSuperEntityColumns(superEntityColumns) // 父类公共字段
.enableTableFieldAnnotation() // 开启字段注解
// Service策略配置
.serviceBuilder()
.formatServiceFileName("I%sService")
.formatServiceImplFileName("%sServiceImpl")
// Mapper策略配置
.mapperBuilder()
.superClass(BaseMapper.class) // 设置父类
.enableMapperAnnotation() // 开启@Mapper注解
.formatMapperFileName("%sMapper")
.formatXmlFileName("%sMapper");
builder.entityBuilder()
// 实体类继承自BaseEntity类
.superClass(BaseEntity.class)
// 启用Lombok注解
.enableLombok()
// 启用表字段注解
.enableTableFieldAnnotation()
// 逻辑删除字段名为deleted
.logicDeleteColumnName("deleted")
// 添加父类实体的共有字段
.addSuperEntityColumns("id", "created_by", "created_time", "updated_by", "updated_time")
// 添加表字段填充策略
.addTableFills(new Column("create_time", FieldFill.INSERT))
// 添加属性填充策略
.addTableFills(new Property("update_time", FieldFill.INSERT_UPDATE))
// 主键类型为AUTO
.idType(IdType.AUTO)
// 设置生成文件名格式为%sEntity
.formatFileName("%sEntity")
// 构建实体类配置
.build();
})
// 使用Freemarker引擎模板
.templateEngine(new FreemarkerTemplateEngine())
.execute();
List<String> commonColumns = Arrays.asList("deleted","createTime","updateTime","createBy","updateBy");
Map<String, Map<String,Object>> columnMaps = columns.entrySet().stream().filter(entry -> !commonColumns.contains(entry.getKey())).collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
String path = System.getProperty("user.dir") + "/generated-code/backend/src/main/java/" + basePackage.replace(".", "/") + "/" + moduleName ;
generate( moduleName, basePackage,tables,columnMaps, path);
}
private static void generate(String moduleName,String basePackage, List<String> tables, Map<String, Map<String,Object>> columnMaps,String path) {
generateClass(moduleName,basePackage, tables,columnMaps,"admin-service.java.ftl",path + "/service/","Service","Admin");
generateClass(moduleName,basePackage, tables,columnMaps,"admin-convert.java.ftl",path + "/convert/","Convert","Admin");
// 生成自定义Controller
generateClass(moduleName,basePackage, tables,columnMaps,"admin-custom-controller.java.ftl",path + "/controller/","Controller","Admin");
// 生成响应类
generateClass(moduleName,basePackage, tables,columnMaps,"admin-response.java.ftl",path + "/controller/response/","Resp","Admin");
// 生成创建和更新的请求类
generateClass(moduleName,basePackage, tables,columnMaps,"admin-request.java.ftl",path + "/controller/request/","Req","Admin");
// 生成查询请求类
generateClass(moduleName,basePackage, tables,columnMaps,"admin-query-request.java.ftl",path + "/controller/request/","QueryReq","Admin");
// 生成PageRequest基类
generateClass(moduleName,basePackage, tables,columnMaps,"admin-pageRequest.java.ftl",path + "/controller/request/","PageReq","Admin");
}
private static void generateClass(String moduleName,String basePackage, List<String> tables, Map<String, Map<String,Object>> tableColumnsInfo,String templateName, String path,String suffix,String prefix){
try {
// 初始化FreeMarker配置
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setClassForTemplateLoading(BackendCodeGenerator.class, "/templates/backend/" + prefix.toLowerCase());
cfg.setDefaultEncoding("UTF-8");
Template pageRequestTemplate = cfg.getTemplate(templateName);
// 创建目录
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
// 为每个表生成通用请求类
for (String table : tables) {
// 处理表名转换为类名
String className = tableToClassName(table);
String varName = tableToValName(className);
// 获取表的列信息
Map<String, Object> tableInfo = tableColumnsInfo.get(table);
List<ColumnInfo> columns = new ArrayList<>();
boolean hasDateField = false;
boolean hasBigDecimalField = false;
if (tableInfo != null) {
columns = processColumns(tableInfo);
hasDateField = hasFieldType(columns, "Date");
hasBigDecimalField = hasFieldType(columns, "BigDecimal");
}
// 准备数据模型
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("basePackage", basePackage);
dataModel.put("className", className);
dataModel.put("varName", varName);
dataModel.put("prefix", prefix);
dataModel.put("suffix", suffix);
dataModel.put("moduleName", moduleName);
dataModel.put("columns", columns);
dataModel.put("hasDateField", hasDateField);
dataModel.put("hasBigDecimalField", hasBigDecimalField);
dataModel.put("author", System.getProperty("user.name"));
dataModel.put("date", new Date());
// 生成文件
String fileName = path + prefix + className + suffix + ".java";
try (Writer out = new FileWriter(new File(fileName))) {
pageRequestTemplate.process(dataModel, out);
}
}
} catch (IOException | TemplateException e) {
log.error("生成文件失败:" + e.getMessage(), e);
}
}
// 表名转类名下划线转驼峰
private static String tableToClassName(String tableName) {
// 去除表前缀
if (tableName.startsWith("t_")) {
tableName = tableName.substring(2);
}
// 下划线转驼峰
StringBuilder sb = new StringBuilder();
boolean nextUpperCase = false;
for (char c : tableName.toCharArray()) {
if (c == '_') {
nextUpperCase = true;
} else {
if (nextUpperCase) {
sb.append(Character.toUpperCase(c));
nextUpperCase = false;
} else {
sb.append(c);
}
}
}
// 首字母大写
if (sb.length() > 0) {
sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
}
return sb.toString();
}
private static String tableToValName(String tableName) {
return tableName.substring(0,1).toLowerCase() + tableName.substring(1);
}
/**
* 处理列信息转换为Java属性
*/
private static List<ColumnInfo> processColumns(Map<String, Object> tableInfo) {
List<ColumnInfo> columnInfos = new ArrayList<>();
@SuppressWarnings("unchecked")
List<Map<String, Object>> columns = (List<Map<String, Object>>) tableInfo.get("columns");
if (columns != null) {
for (Map<String, Object> column : columns) {
ColumnInfo info = new ColumnInfo();
String columnName = (String) column.get("columnName");
String dataType = (String) column.get("dataType");
String columnComment = (String) column.get("columnComment");
String isNullable = (String) column.get("isNullable");
String columnKey = (String) column.get("columnKey");
info.setColumnName(columnName);
info.setPropertyName(columnToProperty(columnName));
info.setJavaType(mapDataTypeToJavaType(dataType));
info.setDataType(dataType);
info.setComment(columnComment != null ? columnComment : "");
info.setNullable("YES".equalsIgnoreCase(isNullable));
info.setPrimaryKey("PRI".equalsIgnoreCase(columnKey));
columnInfos.add(info);
}
}
return columnInfos;
}
/**
* 将数据库列名转换为Java属性名下划线转驼峰
*/
private static String columnToProperty(String columnName) {
if (columnName == null || columnName.isEmpty()) {
return columnName;
}
StringBuilder result = new StringBuilder();
boolean nextUpperCase = false;
for (char c : columnName.toCharArray()) {
if (c == '_') {
nextUpperCase = true;
} else {
if (nextUpperCase) {
result.append(Character.toUpperCase(c));
nextUpperCase = false;
} else {
result.append(Character.toLowerCase(c));
}
}
}
return result.toString();
}
/**
* 将数据库类型映射为Java类型
*/
private static String mapDataTypeToJavaType(String dataType) {
if (dataType == null) {
return "String";
}
dataType = dataType.toLowerCase();
switch (dataType) {
case "tinyint":
case "smallint":
case "mediumint":
case "int":
return "Integer";
case "bigint":
return "Long";
case "float":
case "double":
return "Double";
case "decimal":
case "numeric":
return "BigDecimal";
case "date":
case "datetime":
case "timestamp":
return "Date";
case "bit":
return "Boolean";
default:
return "String";
}
}
/**
* 检查是否有特定类型的字段
*/
private static boolean hasFieldType(List<ColumnInfo> columns, String javaType) {
if (columns == null || columns.isEmpty()) {
return false;
}
for (ColumnInfo column : columns) {
if (javaType.equals(column.getJavaType())) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,342 @@
package com.codegenerator.generator;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FrontendCodeGenerator {
// 生成Vue2代码
public static void generateVue2Code(List<String> tables,Map<String, Map<String,Object>> columns) {
generateVueCode(tables, "vue2",columns);
}
// 生成Vue3代码
public static void generateVue3Code(List<String> tables,Map<String, Map<String,Object>> columns) {
generateVueCode(tables, "vue3",columns);
}
// 生成request.js文件
private static void generateRequestFile(Template requestTemplate, String utilsOutputDir) throws IOException, TemplateException {
// 创建utils目录
File utilsDir = new File(utilsOutputDir);
if (!utilsDir.exists()) {
utilsDir.mkdirs();
}
// 准备数据模型这里不需要特别的数据模型
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("token", "");
// 生成request.js文件
String requestFileName = utilsOutputDir + "request.js";
try (Writer requestOut = new FileWriter(new File(requestFileName))) {
requestTemplate.process(dataModel, requestOut);
}
}
// 生成Vue代码
private static void generateVueCode(List<String> tables, String version,Map<String, Map<String,Object>> columns) {
try {
// 初始化FreeMarker配置
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setClassForTemplateLoading(FrontendCodeGenerator.class, "/templates/frontend");
cfg.setDefaultEncoding("UTF-8");
// 获取模板
Template listTemplate = cfg.getTemplate(version + "/list.vue.ftl");
Template formTemplate = cfg.getTemplate(version + "/form.vue.ftl");
Template requestTemplate = cfg.getTemplate("api/api.js.ftl");
// 生成路径
String projectPath = System.getProperty("user.dir");
String baseOutputDir = projectPath + "/generated-code/frontend/" + version + "/src/views/";
String apiOutputDir = projectPath + "/generated-code/frontend/" + version + "/src/api/";
String utilsOutputDir = projectPath + "/generated-code/frontend/" + version + "/src/utils/";
// 生成request.js工具文件
generateRequestFile(requestTemplate, utilsOutputDir);
// 为每个表生成Vue组件
for (String table : tables) {
// 处理表名转换为类名和路径
String className = tableToClassName(table);
String varName = className.substring(0, 1).toLowerCase() + className.substring(1);
String outputDir = baseOutputDir + varName + "/";
// 创建目录
File dir = new File(outputDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 准备数据模型
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("entityName", className);
dataModel.put("lowerEntityName", varName);
dataModel.put("className", className);
dataModel.put("varName", varName);
dataModel.put("tableName", table);
dataModel.put("version", version);
// 添加列信息到数据模型
if (columns.containsKey(table)) {
dataModel.put("columns", columns.get(table));
}
// 生成列表组件
String listFileName = outputDir + "List.vue";
Writer listOut = new FileWriter(new File(listFileName));
listTemplate.process(dataModel, listOut);
listOut.close();
// 生成表单组件
String formFileName = outputDir + "Form.vue";
Writer formOut = new FileWriter(new File(formFileName));
formTemplate.process(dataModel, formOut);
formOut.close();
// 生成API请求文件
generateApiFile(dataModel, apiOutputDir, varName);
}
} catch (IOException | TemplateException e) {
e.printStackTrace();
}
}
// 生成API请求文件
private static void generateApiFile(Map<String, Object> dataModel, String apiOutputDir, String varName) throws IOException {
// 创建API目录
File apiDir = new File(apiOutputDir);
if (!apiDir.exists()) {
apiDir.mkdirs();
}
// 准备API文件内容
String className = (String) dataModel.get("className");
StringBuilder content = new StringBuilder();
// 导入请求库
content.append("import request from '@/utils/request'\n\n");
// 生成API方法
content.append("// 获取分页数据\nexport const get").append(className).append("Page = (params) => {\n");
content.append(" return request({\n");
content.append(" url: '/api/").append(varName).append("/page',\n");
content.append(" method: 'get',\n");
content.append(" params\n");
content.append(" })\n}");
content.append("\n\n");
content.append("// 通过ID获取详情\nexport const get").append(className).append(" = (id) => {\n");
content.append(" return request({\n");
content.append(" url: '/api/").append(varName).append("/' + id,\n");
content.append(" method: 'get'\n");
content.append(" })\n}");
content.append("\n\n");
content.append("// 新增\nexport const save").append(className).append(" = (data) => {\n");
content.append(" return request({\n");
content.append(" url: '/api/").append(varName).append("',\n");
content.append(" method: 'post',\n");
content.append(" data\n");
content.append(" })\n}");
content.append("\n\n");
content.append("// 更新\nexport const update").append(className).append(" = (data) => {\n");
content.append(" return request({\n");
content.append(" url: '/api/").append(varName).append("',\n");
content.append(" method: 'put',\n");
content.append(" data\n");
content.append(" })\n}");
content.append("\n\n");
content.append("// 删除\nexport const delete").append(className).append(" = (id) => {\n");
content.append(" return request({\n");
content.append(" url: '/api/").append(varName).append("/' + id,\n");
content.append(" method: 'delete'\n");
content.append(" })\n}");
// 写入文件
String apiFileName = apiOutputDir + varName + ".js";
try (FileWriter apiOut = new FileWriter(new File(apiFileName))) {
apiOut.write(content.toString());
}
}
// 表名转类名下划线转驼峰
private static String tableToClassName(String tableName) {
// 去除表前缀
if (tableName.startsWith("t_")) {
tableName = tableName.substring(2);
}
// 下划线转驼峰
StringBuilder sb = new StringBuilder();
boolean nextUpperCase = false;
for (char c : tableName.toCharArray()) {
if (c == '_') {
nextUpperCase = true;
} else {
if (nextUpperCase) {
sb.append(Character.toUpperCase(c));
nextUpperCase = false;
} else {
sb.append(c);
}
}
}
// 首字母大写
if (sb.length() > 0) {
sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
}
return sb.toString();
}
// 测试生成的代码
public static Map<String, Object> testGeneratedCode() {
Map<String, Object> testResult = new HashMap<>();
List<String> successFiles = new ArrayList<>();
List<String> errorFiles = new ArrayList<>();
boolean isSuccess = true;
try {
String projectPath = System.getProperty("user.dir");
// 检查后端代码
testResult.put("backendTest", checkBackendCode(projectPath, successFiles, errorFiles));
// 检查Vue2前端代码
testResult.put("vue2Test", checkFrontendCode(projectPath, "vue2", successFiles, errorFiles));
// 检查Vue3前端代码
testResult.put("vue3Test", checkFrontendCode(projectPath, "vue3", successFiles, errorFiles));
// 综合结果
if (!errorFiles.isEmpty()) {
isSuccess = false;
}
} catch (Exception e) {
errorFiles.add("测试过程中发生异常: " + e.getMessage());
isSuccess = false;
}
testResult.put("success", isSuccess);
testResult.put("successFiles", successFiles);
testResult.put("errorFiles", errorFiles);
return testResult;
}
// 检查后端代码
private static boolean checkBackendCode(String projectPath, List<String> successFiles, List<String> errorFiles) {
boolean isSuccess = true;
String backendDirPath = projectPath + "/generated-code/backend";
File backendDir = new File(backendDirPath);
if (!backendDir.exists() || backendDir.listFiles() == null || backendDir.listFiles().length == 0) {
errorFiles.add("后端代码目录不存在或为空: " + backendDirPath);
return false;
}
// 检查Controller文件是否符合RESTful规范
File controllerDir = new File(backendDirPath + "/src/main/java/com/example/controller");
if (controllerDir.exists() && controllerDir.isDirectory()) {
File[] controllerFiles = controllerDir.listFiles((dir, name) -> name.endsWith("Controller.java"));
if (controllerFiles != null && controllerFiles.length > 0) {
for (File file : controllerFiles) {
try {
String content = new String(java.nio.file.Files.readAllBytes(file.toPath()));
// 检查是否包含RESTful注解
if (content.contains("@RestController") && content.contains("@RequestMapping") &&
content.contains("@GetMapping") && content.contains("@PostMapping") &&
content.contains("@PutMapping") && content.contains("@DeleteMapping")) {
successFiles.add("后端Controller文件符合RESTful规范: " + file.getName());
} else {
errorFiles.add("后端Controller文件不符合RESTful规范: " + file.getName());
isSuccess = false;
}
} catch (IOException e) {
errorFiles.add("读取Controller文件失败: " + file.getName() + ", 错误: " + e.getMessage());
isSuccess = false;
}
}
}
}
return isSuccess;
}
// 检查前端代码
private static boolean checkFrontendCode(String projectPath, String version, List<String> successFiles, List<String> errorFiles) {
boolean isSuccess = true;
String frontendDirPath = projectPath + "/generated-code/frontend/" + version;
File frontendDir = new File(frontendDirPath);
if (!frontendDir.exists() || frontendDir.listFiles() == null || frontendDir.listFiles().length == 0) {
errorFiles.add(version + "前端代码目录不存在或为空: " + frontendDirPath);
return false;
}
// 检查Vue组件文件
File viewsDir = new File(frontendDirPath + "/src/views");
if (viewsDir.exists() && viewsDir.isDirectory()) {
File[] viewDirs = viewsDir.listFiles(File::isDirectory);
if (viewDirs != null && viewDirs.length > 0) {
for (File dir : viewDirs) {
File listVueFile = new File(dir, "List.vue");
File formVueFile = new File(dir, "Form.vue");
if (listVueFile.exists()) {
successFiles.add(version + "列表组件文件存在: " + listVueFile.getPath());
} else {
errorFiles.add(version + "列表组件文件不存在: " + listVueFile.getPath());
isSuccess = false;
}
if (formVueFile.exists()) {
successFiles.add(version + "表单组件文件存在: " + formVueFile.getPath());
} else {
errorFiles.add(version + "表单组件文件不存在: " + formVueFile.getPath());
isSuccess = false;
}
}
}
}
// 检查API文件
File apiDir = new File(frontendDirPath + "/src/api");
if (apiDir.exists() && apiDir.isDirectory()) {
File[] apiFiles = apiDir.listFiles((dir, name) -> name.endsWith(".js"));
if (apiFiles != null && apiFiles.length > 0) {
for (File file : apiFiles) {
successFiles.add(version + "API文件存在: " + file.getName());
}
} else {
errorFiles.add(version + "API文件不存在: " + apiDir.getPath());
isSuccess = false;
}
}
// 检查request.js工具文件
File utilsDir = new File(frontendDirPath + "/src/utils");
File requestFile = new File(utilsDir, "request.js");
if (requestFile.exists()) {
successFiles.add(version + "请求工具文件存在: " + requestFile.getPath());
} else {
errorFiles.add(version + "请求工具文件不存在: " + requestFile.getPath());
isSuccess = false;
}
return isSuccess;
}
}

View File

@ -0,0 +1,143 @@
package com.codegenerator.ui;
import com.codegenerator.config.DbConnectionConfig;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
public class DbConnectionEditorDialog extends JDialog {
private DbConnectionConfig connection;
private JTextField nameField;
private JComboBox<String> dbTypeCombo;
private JTextField urlField;
private JTextField usernameField;
private JPasswordField passwordField;
public DbConnectionEditorDialog(Dialog parent, DbConnectionConfig conn) {
super(parent, conn == null ? "新增数据库连接" : "编辑数据库连接", true);
this.connection = conn != null ? conn : new DbConnectionConfig();
initUI();
if (conn != null) {
loadConnectionData();
}
setSize(500, 300);
setLocationRelativeTo(parent);
}
private void initUI() {
JPanel mainPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5);
gbc.anchor = GridBagConstraints.WEST;
gbc.fill = GridBagConstraints.HORIZONTAL;
int row = 0;
// 连接名称
gbc.gridx = 0;
gbc.gridy = row++;
mainPanel.add(new JLabel("连接名称:"), gbc);
gbc.gridx = 1;
nameField = new JTextField(25);
mainPanel.add(nameField, gbc);
// 数据库类型
gbc.gridx = 0;
gbc.gridy = row++;
mainPanel.add(new JLabel("数据库类型:"), gbc);
gbc.gridx = 1;
dbTypeCombo = new JComboBox<>(new String[]{"mysql", "postgresql", "oracle", "sqlserver"});
mainPanel.add(dbTypeCombo, gbc);
// URL
gbc.gridx = 0;
gbc.gridy = row++;
mainPanel.add(new JLabel("连接URL:"), gbc);
gbc.gridx = 1;
urlField = new JTextField(25);
mainPanel.add(urlField, gbc);
// 用户名
gbc.gridx = 0;
gbc.gridy = row++;
mainPanel.add(new JLabel("用户名:"), gbc);
gbc.gridx = 1;
usernameField = new JTextField(25);
mainPanel.add(usernameField, gbc);
// 密码
gbc.gridx = 0;
gbc.gridy = row++;
mainPanel.add(new JLabel("密码:"), gbc);
gbc.gridx = 1;
passwordField = new JPasswordField(25);
mainPanel.add(passwordField, gbc);
// 按钮
gbc.gridx = 0;
gbc.gridy = row++;
gbc.gridwidth = 2;
gbc.anchor = GridBagConstraints.CENTER;
JPanel buttonPanel = new JPanel();
JButton saveButton = new JButton("保存");
saveButton.addActionListener(this::saveConnection);
JButton cancelButton = new JButton("取消");
cancelButton.addActionListener(e -> dispose());
buttonPanel.add(saveButton);
buttonPanel.add(cancelButton);
mainPanel.add(buttonPanel, gbc);
add(mainPanel);
}
private void loadConnectionData() {
nameField.setText(connection.getName());
dbTypeCombo.setSelectedItem(connection.getDbType());
urlField.setText(connection.getUrl());
usernameField.setText(connection.getUsername());
passwordField.setText(connection.getPassword());
}
private void saveConnection(ActionEvent e) {
String name = nameField.getText().trim();
String dbType = (String) dbTypeCombo.getSelectedItem();
String url = urlField.getText().trim();
String username = usernameField.getText().trim();
String password = new String(passwordField.getPassword());
// 简单验证
if (name.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入连接名称");
return;
}
if (url.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入连接URL");
return;
}
if (username.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入用户名");
return;
}
// 保存数据
connection.setName(name);
connection.setDbType(dbType);
connection.setUrl(url);
connection.setUsername(username);
connection.setPassword(password);
dispose();
}
public DbConnectionConfig getConnection() {
return connection;
}
}

View File

@ -0,0 +1,331 @@
package com.codegenerator.ui;
import com.codegenerator.config.DbConnectionConfig;
import com.codegenerator.config.GeneratorConfig;
import com.codegenerator.util.ConfigUtil;
import com.codegenerator.util.DatabaseUtil;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
public class DbConnectionManagerDialog extends JDialog {
private JTable table;
private DefaultTableModel tableModel;
private List<DbConnectionConfig> connections;
private GeneratorConfig config;
private MainFrame parentFrame;
public DbConnectionManagerDialog(MainFrame parent, GeneratorConfig config) {
super(parent, "数据库连接管理", true);
this.parentFrame = parent;
// 加载已保存的配置如果没有则使用传入的配置
this.config = ConfigUtil.loadDbConnections();
this.connections = config.getDbConnections();
initUI();
}
private void initUI() {
setSize(600, 400);
setLocationRelativeTo(getParent());
setLayout(new BorderLayout());
// 创建表格模型
tableModel = new DefaultTableModel(new String[] {"连接名称", "数据库类型", "连接URL", "用户名"}, 0) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
table = new JTable(tableModel);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
editConnection(selectedRow);
}
}
}
});
// 创建主面板
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.add(new JScrollPane(table), BorderLayout.CENTER);
// 创建按钮面板
JPanel buttonPanel = new JPanel();
JButton addButton = new JButton("添加");
JButton editButton = new JButton("编辑");
JButton deleteButton = new JButton("删除");
JButton testButton = new JButton("测试连接");
JButton selectButton = new JButton("选择");
addButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
addConnection();
// 保存配置
ConfigUtil.saveDbConnections(connections, getSelectedDbConnectionIndex(config));
}
});
editButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
editConnection(selectedRow);
} else {
JOptionPane.showMessageDialog(DbConnectionManagerDialog.this, "请先选择一个连接");
}
}
});
deleteButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int selectedRow = table.getSelectedRow();
deleteConnection(selectedRow);
// 保存配置
ConfigUtil.saveDbConnections(connections, getSelectedDbConnectionIndex(config));
}
});
testButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int selectedRow = table.getSelectedRow();
testConnection(selectedRow);
}
});
selectButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
setSelectedDbConnectionIndex(config, selectedRow);
// 保存配置
ConfigUtil.saveDbConnections(connections, selectedRow);
// 更新MainFrame的UI
DbConnectionConfig selectedConn = connections.get(selectedRow);
// 将选中的连接信息设置到MainFrame的UI中
try {
java.lang.reflect.Field field = parentFrame.getClass().getDeclaredField("connNameField");
field.setAccessible(true);
JTextField connNameField = (JTextField) field.get(parentFrame);
connNameField.setText(getConnectionName(selectedConn));
field = parentFrame.getClass().getDeclaredField("dbTypeCombo");
field.setAccessible(true);
JComboBox<String> dbTypeCombo = (JComboBox<String>) field.get(parentFrame);
dbTypeCombo.setSelectedItem(getConnectionDbType(selectedConn));
field = parentFrame.getClass().getDeclaredField("urlField");
field.setAccessible(true);
JTextField urlField = (JTextField) field.get(parentFrame);
urlField.setText(getConnectionUrl(selectedConn));
field = parentFrame.getClass().getDeclaredField("usernameField");
field.setAccessible(true);
JTextField usernameField = (JTextField) field.get(parentFrame);
usernameField.setText(getConnectionUsername(selectedConn));
field = parentFrame.getClass().getDeclaredField("passwordField");
field.setAccessible(true);
JPasswordField passwordField = (JPasswordField) field.get(parentFrame);
passwordField.setText(getConnectionPassword(selectedConn));
// 也保存单连接配置以便兼容旧的代码
ConfigUtil.saveDbConfig(
getConnectionName(selectedConn),
getConnectionDbType(selectedConn),
getConnectionUrl(selectedConn),
getConnectionUsername(selectedConn),
getConnectionPassword(selectedConn)
);
} catch (Exception ex) {
ex.printStackTrace();
}
dispose();
} else {
JOptionPane.showMessageDialog(DbConnectionManagerDialog.this, "请先选择一个连接");
}
}
});
buttonPanel.add(addButton);
buttonPanel.add(editButton);
buttonPanel.add(deleteButton);
buttonPanel.add(testButton);
buttonPanel.add(selectButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
add(mainPanel);
loadConnections();
}
private void loadConnections() {
// 清空表格
tableModel.setRowCount(0);
// 添加连接到表格
for (DbConnectionConfig conn : connections) {
tableModel.addRow(new Object[] {
getConnectionName(conn),
getConnectionDbType(conn),
getConnectionUrl(conn),
getConnectionUsername(conn)
});
}
}
private void addConnection() {
DbConnectionEditorDialog dialog = new DbConnectionEditorDialog(this, new DbConnectionConfig());
dialog.setVisible(true);
loadConnections();
}
private void editConnection(int selectedRow) {
if (selectedRow >= 0 && selectedRow < connections.size()) {
DbConnectionEditorDialog dialog = new DbConnectionEditorDialog(
this, connections.get(selectedRow));
dialog.setVisible(true);
loadConnections();
} else {
JOptionPane.showMessageDialog(this, "请先选择一个连接");
}
}
private void deleteConnection(int selectedRow) {
if (selectedRow >= 0 && selectedRow < connections.size()) {
// 不能删除最后一个连接
if (connections.size() <= 1) {
JOptionPane.showMessageDialog(this, "至少保留一个数据库连接");
return;
}
int confirm = JOptionPane.showConfirmDialog(this,
"确定要删除选中的连接吗?", "确认删除", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
connections.remove(selectedRow);
// 如果删除的是当前选中的连接自动切换到第一个
if (selectedRow == getSelectedDbConnectionIndex(config)) {
setSelectedDbConnectionIndex(config, 0);
parentFrame.updateConnectionUI();
}
loadConnections();
}
} else {
JOptionPane.showMessageDialog(this, "请先选择一个连接");
}
}
private void testConnection(int selectedRow) {
if (selectedRow >= 0 && selectedRow < connections.size()) {
DbConnectionConfig conn = connections.get(selectedRow);
try {
// 测试连接的代码
if (DatabaseUtil.testConnection(
getConnectionUrl(conn),
getConnectionUsername(conn),
getConnectionPassword(conn))) {
JOptionPane.showMessageDialog(this, "连接测试成功!");
} else {
JOptionPane.showMessageDialog(this, "连接测试失败!");
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(this, "连接测试失败: " + ex.getMessage(),
"错误", JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(this, "请先选择一个连接");
}
}
// 临时的辅助方法绕过Lombok注解处理器问题
private String getConnectionName(DbConnectionConfig conn) {
try {
java.lang.reflect.Field field = conn.getClass().getDeclaredField("name");
field.setAccessible(true);
return (String) field.get(conn);
} catch (NoSuchFieldException | IllegalAccessException e) {
return "";
}
}
private String getConnectionDbType(DbConnectionConfig conn) {
try {
java.lang.reflect.Field field = conn.getClass().getDeclaredField("dbType");
field.setAccessible(true);
return (String) field.get(conn);
} catch (NoSuchFieldException | IllegalAccessException e) {
return "mysql";
}
}
private String getConnectionUrl(DbConnectionConfig conn) {
try {
java.lang.reflect.Field field = conn.getClass().getDeclaredField("url");
field.setAccessible(true);
return (String) field.get(conn);
} catch (NoSuchFieldException | IllegalAccessException e) {
return "";
}
}
private String getConnectionUsername(DbConnectionConfig conn) {
try {
java.lang.reflect.Field field = conn.getClass().getDeclaredField("username");
field.setAccessible(true);
return (String) field.get(conn);
} catch (NoSuchFieldException | IllegalAccessException e) {
return "";
}
}
private String getConnectionPassword(DbConnectionConfig conn) {
try {
java.lang.reflect.Field field = conn.getClass().getDeclaredField("password");
field.setAccessible(true);
return (String) field.get(conn);
} catch (NoSuchFieldException | IllegalAccessException e) {
return "";
}
}
private int getSelectedDbConnectionIndex(GeneratorConfig config) {
try {
java.lang.reflect.Field field = config.getClass().getDeclaredField("selectedDbConnectionIndex");
field.setAccessible(true);
return field.getInt(config);
} catch (Exception e) {
return 0;
}
}
private void setSelectedDbConnectionIndex(GeneratorConfig config, int index) {
try {
java.lang.reflect.Field field = config.getClass().getDeclaredField("selectedDbConnectionIndex");
field.setAccessible(true);
field.setInt(config, index);
} catch (Exception e) {
e.printStackTrace();
}
}
public void addConnection(DbConnectionConfig conn) {
connections.add(conn);
}
}

View File

@ -0,0 +1,587 @@
package com.codegenerator.ui;
import com.codegenerator.generator.BackendCodeGenerator;
import com.codegenerator.generator.FrontendCodeGenerator;
import com.codegenerator.util.CodeTestUtil;
import com.codegenerator.util.ConfigUtil;
import com.codegenerator.util.DatabaseUtil;
import com.codegenerator.config.GeneratorConfig;
import com.codegenerator.config.DbConnectionConfig;
import lombok.extern.slf4j.Slf4j;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
public class MainFrame extends JFrame {
// 数据库连接相关组件
private JComboBox<String> dbTypeCombo;
private JTextField connNameField;
private JTextField urlField;
private JTextField usernameField;
private JPasswordField passwordField;
// 代码生成配置相关组件
private JTextField basePackageField;
private JCheckBox lombokCheckBox;
private JTextField superEntityField;
private JTextField superColumnsField;
private JTextField moduleNameField;
// 表选择相关组件
private JTextField tableFilterField;
private JTable tableTable;
private DefaultTableModel tableModel;
private List<String> allTables;
private Map<String, Map<String,Object>> columnInfoMap;
private JCheckBox generateVue2CheckBox;
private JCheckBox generateVue3CheckBox;
public MainFrame() {
initUI();
loadConfig();
}
private void initUI() {
setTitle("代码生成器");
setSize(900, 700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// 创建主面板使用卡片布局
JPanel mainPanel = new JPanel(new CardLayout());
// 1. 数据库连接面板
JPanel dbPanel = createDbPanel();
// 2. 代码生成配置面板
JPanel generatorPanel = createGeneratorPanel();
mainPanel.add(dbPanel, "dbPanel");
mainPanel.add(generatorPanel, "generatorPanel");
add(mainPanel);
// 显示数据库连接面板
((CardLayout) mainPanel.getLayout()).show(mainPanel, "dbPanel");
}
// 创建数据库连接面板
private JPanel createDbPanel() {
JPanel panel = new JPanel(new BorderLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// 标题
JLabel titleLabel = new JLabel("数据库连接设置");
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));
titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0));
panel.add(titleLabel, BorderLayout.NORTH);
// 表单面板
JPanel formPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5);
gbc.anchor = GridBagConstraints.WEST;
// 数据库类型
JLabel dbTypeLabel = new JLabel("数据库类型:");
dbTypeCombo = new JComboBox<>(new String[]{"mysql"});
dbTypeCombo.setPreferredSize(new Dimension(300, 25));
// 连接名
JLabel connNameLabel = new JLabel("连接名称:");
connNameField = new JTextField();
connNameField.setPreferredSize(new Dimension(300, 25));
connNameField.setText("dev");
// URL
JLabel urlLabel = new JLabel("连接URL:");
urlField = new JTextField();
urlField.setPreferredSize(new Dimension(300, 25));
urlField.setText("jdbc:mysql://192.168.0.45:3306/seer_teach?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&connectTimeout=10000&socketTimeout=60000");
// 用户名
JLabel usernameLabel = new JLabel("用户名:");
usernameField = new JTextField();
usernameField.setPreferredSize(new Dimension(300, 25));
usernameField.setText("root");
// 密码
JLabel passwordLabel = new JLabel("密码:");
passwordField = new JPasswordField();
passwordField.setPreferredSize(new Dimension(300, 25));
passwordField.setText("Zs139768");
// 添加组件到表单
int row = 0;
gbc.gridx = 0;
gbc.gridy = row;
formPanel.add(dbTypeLabel, gbc);
gbc.gridx = 1;
formPanel.add(dbTypeCombo, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
formPanel.add(connNameLabel, gbc);
gbc.gridx = 1;
formPanel.add(connNameField, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
formPanel.add(urlLabel, gbc);
gbc.gridx = 1;
formPanel.add(urlField, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
formPanel.add(usernameLabel, gbc);
gbc.gridx = 1;
formPanel.add(usernameField, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
formPanel.add(passwordLabel, gbc);
gbc.gridx = 1;
formPanel.add(passwordField, gbc);
// 滚动面板包裹表单
JScrollPane scrollPane = new JScrollPane(formPanel);
panel.add(scrollPane, BorderLayout.CENTER);
// 按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
JButton testBtn = new JButton("测试连接");
JButton connectBtn = new JButton("连接");
JButton manageBtn = new JButton("管理连接");
testBtn.addActionListener(e -> {
String connName = connNameField.getText();
String dbType = (String) dbTypeCombo.getSelectedItem();
String url = urlField.getText();
String username = usernameField.getText();
String password = new String(passwordField.getPassword());
if (url.isEmpty() || username.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写必要的连接信息!", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
if (DatabaseUtil.testConnection(url, username, password)) {
// 如果连接名称为空使用默认名称
if (connName.isEmpty()) {
connName = "数据库连接_" + System.currentTimeMillis();
connNameField.setText(connName);
}
// 保存配置
ConfigUtil.saveDbConfig(connName, dbType, url, username, password);
JOptionPane.showMessageDialog(this, "连接成功!配置已保存。");
} else {
JOptionPane.showMessageDialog(this, "连接失败,请检查配置!", "错误", JOptionPane.ERROR_MESSAGE);
}
});
connectBtn.addActionListener(e -> {
String connName = connNameField.getText();
String dbType = (String) dbTypeCombo.getSelectedItem();
String url = urlField.getText();
String username = usernameField.getText();
String password = new String(passwordField.getPassword());
if (connName.isEmpty() || url.isEmpty() || username.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写必要的连接信息!", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
if (DatabaseUtil.testConnection(url, username, password)) {
// 保存配置
ConfigUtil.saveDbConfig(connName, dbType, url, username, password);
// 切换到代码生成配置面板
CardLayout cl = (CardLayout) ((JPanel) getContentPane().getComponent(0)).getLayout();
cl.show((JPanel) getContentPane().getComponent(0), "generatorPanel");
// 加载表列表
loadTables(url, username, password);
} else {
JOptionPane.showMessageDialog(this, "连接失败,请检查配置!", "错误", JOptionPane.ERROR_MESSAGE);
}
});
manageBtn.addActionListener(e -> {
GeneratorConfig generatorConfig = new GeneratorConfig();
DbConnectionManagerDialog dialog = new DbConnectionManagerDialog(MainFrame.this, generatorConfig);
dialog.setVisible(true);
});
buttonPanel.add(testBtn);
buttonPanel.add(connectBtn);
buttonPanel.add(manageBtn);
panel.add(buttonPanel, BorderLayout.SOUTH);
return panel;
}
// 创建代码生成配置面板
private JPanel createGeneratorPanel() {
JPanel panel = new JPanel(new BorderLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// 标题
JLabel titleLabel = new JLabel("代码生成配置");
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));
titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0));
panel.add(titleLabel, BorderLayout.NORTH);
// 主内容面板分为上下两部分
JPanel contentPanel = new JPanel(new BorderLayout(10, 10));
// 上部分配置选项
JPanel configPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5);
gbc.anchor = GridBagConstraints.WEST;
// 基础包名
JLabel basePackageLabel = new JLabel("基础包名:");
basePackageField = new JTextField();
basePackageField.setPreferredSize(new Dimension(300, 25));
// 是否启用Lombok
JLabel lombokLabel = new JLabel("启用Lombok:");
lombokCheckBox = new JCheckBox();
lombokCheckBox.setSelected(true);
// 父类实体
JLabel superEntityLabel = new JLabel("父类实体:");
superEntityField = new JTextField();
superEntityField.setPreferredSize(new Dimension(300, 25));
// 父类公共字段
JLabel superColumnsLabel = new JLabel("父类公共字段:");
superColumnsField = new JTextField();
superColumnsField.setPreferredSize(new Dimension(300, 25));
superColumnsField.setToolTipText("多个字段用逗号分隔id,createTime,updateTime");
// Vue2 生成选项
JLabel generateVue2Label = new JLabel("生成Vue2代码:");
generateVue2CheckBox = new JCheckBox();
generateVue2CheckBox.setSelected(true);
// Vue3 生成选项
JLabel generateVue3Label = new JLabel("生成Vue3代码:");
generateVue3CheckBox = new JCheckBox();
generateVue3CheckBox.setSelected(true);
JLabel moduleNameLabel = new JLabel("模块名:");
moduleNameField = new JTextField();
moduleNameField.setPreferredSize(new Dimension(300, 25));
moduleNameField.setToolTipText("模块名,用于生成代码的包名");
// 保存配置按钮
JButton saveConfigBtn = new JButton("保存配置");
saveConfigBtn.addActionListener(e -> saveGeneratorConfig());
// 添加组件到配置面板
int row = 0;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(basePackageLabel, gbc);
gbc.gridx = 1;
configPanel.add(basePackageField, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(lombokLabel, gbc);
gbc.gridx = 1;
configPanel.add(lombokCheckBox, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(superEntityLabel, gbc);
gbc.gridx = 1;
configPanel.add(superEntityField, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(superColumnsLabel, gbc);
gbc.gridx = 1;
configPanel.add(superColumnsField, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(generateVue2Label, gbc);
gbc.gridx = 1;
configPanel.add(generateVue2CheckBox, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(generateVue3Label, gbc);
gbc.gridx = 1;
configPanel.add(generateVue3CheckBox, gbc);
row++;
gbc.gridx = 0;
gbc.gridy = row;
configPanel.add(moduleNameLabel, gbc);
gbc.gridx = 1;
configPanel.add(moduleNameField, gbc);
row++;
gbc.gridx = 1;
gbc.gridy = row + 1;
gbc.anchor = GridBagConstraints.EAST;
configPanel.add(saveConfigBtn, gbc);
contentPanel.add(configPanel, BorderLayout.NORTH);
// 下部分表选择
JPanel tablePanel = new JPanel(new BorderLayout(10, 10));
// 过滤面板
JPanel filterPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
JLabel filterLabel = new JLabel("过滤:");
tableFilterField = new JTextField(20);
JButton filterBtn = new JButton("查询");
filterBtn.addActionListener(e -> filterTables());
filterPanel.add(filterLabel);
filterPanel.add(tableFilterField);
filterPanel.add(filterBtn);
tablePanel.add(filterPanel, BorderLayout.NORTH);
// 表列表
String[] columnNames = {"选择", "表名"};
tableModel = new DefaultTableModel(columnNames, 0) {
@Override
public Class<?> getColumnClass(int column) {
return column == 0 ? Boolean.class : String.class;
}
@Override
public boolean isCellEditable(int row, int column) {
return column == 0;
}
};
tableTable = new JTable(tableModel);
tableTable.getColumnModel().getColumn(0).setPreferredWidth(50);
tableTable.getColumnModel().getColumn(1).setPreferredWidth(300);
tableTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int column = tableTable.columnAtPoint(e.getPoint());
int row = tableTable.rowAtPoint(e.getPoint());
if (row != -1) {
if (column == 0) {
// 点击复选框列切换选中状态
Boolean checked = (Boolean) tableTable.getValueAt(row, 0);
tableTable.setValueAt(!checked, row, 0);
} else {
// 点击其他列也切换选中状态
Boolean checked = (Boolean) tableTable.getValueAt(row, 0);
tableTable.setValueAt(!checked, row, 0);
}
}
}
});
JScrollPane tableScrollPane = new JScrollPane(tableTable);
tablePanel.add(tableScrollPane, BorderLayout.CENTER);
contentPanel.add(tablePanel, BorderLayout.CENTER);
panel.add(contentPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
JButton backBtn = new JButton("返回");
JButton generateBtn = new JButton("生成代码");
backBtn.addActionListener(e -> {
CardLayout cl = (CardLayout) ((JPanel) getContentPane().getComponent(0)).getLayout();
cl.show((JPanel) getContentPane().getComponent(0), "dbPanel");
});
generateBtn.addActionListener(e -> generateCode());
buttonPanel.add(backBtn);
buttonPanel.add(generateBtn);
panel.add(buttonPanel, BorderLayout.SOUTH);
// 加载已保存的配置
loadGeneratorConfig();
return panel;
}
// 加载配置
private void loadConfig() {
try {
// 先尝试从多连接配置加载
GeneratorConfig dbConfig = ConfigUtil.loadDbConnections();
if (!dbConfig.getDbConnections().isEmpty()) {
DbConnectionConfig selectedConn = dbConfig.getSelectedDbConnection();
if (selectedConn != null) {
connNameField.setText(selectedConn.getName());
dbTypeCombo.setSelectedItem(selectedConn.getDbType());
urlField.setText(selectedConn.getUrl());
usernameField.setText(selectedConn.getUsername());
passwordField.setText(selectedConn.getPassword());
return;
}
}
} catch (Exception e) {
log.error("Failed to load db config", e);
}
// 如果多连接配置加载失败则使用旧的单连接配置
Map<String, String> dbConfig = ConfigUtil.getDbConfig();
connNameField.setText(dbConfig.get("connName"));
dbTypeCombo.setSelectedItem(dbConfig.get("dbType"));
urlField.setText(dbConfig.get("url"));
usernameField.setText(dbConfig.get("username"));
passwordField.setText(dbConfig.get("password"));
// 加载生成器配置
loadGeneratorConfig();
}
// 加载生成器配置
private void loadGeneratorConfig() {
GeneratorConfig generatorConfig = ConfigUtil.getGeneratorConfigAsObject();
basePackageField.setText(generatorConfig.getBasePackage());
lombokCheckBox.setSelected(generatorConfig.isUseLombok());
superEntityField.setText(generatorConfig.getSuperEntityClass());
String[] superColumns = generatorConfig.getSuperEntityColumns();
if (superColumns != null && superColumns.length > 0) {
superColumnsField.setText(String.join(",", superColumns));
}
generateVue2CheckBox.setSelected(generatorConfig.isGenerateVue2());
generateVue3CheckBox.setSelected(generatorConfig.isGenerateVue3());
}
// 保存生成器配置
private void saveGeneratorConfig() {
String basePackage = basePackageField.getText();
boolean useLombok = lombokCheckBox.isSelected();
String superEntityClass = superEntityField.getText();
String[] superEntityColumns = superColumnsField.getText().split(",");
boolean generateVue2 = generateVue2CheckBox.isSelected();
boolean generateVue3 = generateVue3CheckBox.isSelected();
if (basePackage.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写基础包名!", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
ConfigUtil.saveGeneratorConfig(basePackage, useLombok, superEntityClass, superEntityColumns, generateVue2, generateVue3);
JOptionPane.showMessageDialog(this, "配置保存成功!");
}
// 加载数据库表
private void loadTables(String url, String username, String password) {
// 清空表格
tableModel.setRowCount(0);
// 获取所有表
allTables = DatabaseUtil.getTables(url, username, password);
columnInfoMap = DatabaseUtil.getTableColumns(url, username, password, allTables);
// 添加到表格
for (String table : allTables) {
tableModel.addRow(new Object[]{false, table});
}
}
// 过滤表
private void filterTables() {
String filter = tableFilterField.getText().trim().toLowerCase();
// 清空表格
tableModel.setRowCount(0);
// 添加符合条件的表
for (String table : allTables) {
if (filter.isEmpty() || table.toLowerCase().contains(filter)) {
tableModel.addRow(new Object[]{false, table});
}
}
}
// 生成代码
private void generateCode() {
// 获取选中的表
List<String> selectedTables = getSelectedTables();
if (selectedTables.isEmpty()) {
JOptionPane.showMessageDialog(this, "请至少选择一个表", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
try {
// 获取数据库连接信息
String url = urlField.getText();
String username = usernameField.getText();
String password = new String(passwordField.getPassword());
String moduleName = moduleNameField.getText();
String basePackage = basePackageField.getText();
// 设置默认值
if (moduleName == null || moduleName.trim().isEmpty()) {
moduleName = "seer";
}
if (basePackage == null || basePackage.trim().isEmpty()) {
basePackage = "com.example";
}
// 获取表的列信息
Map<String, Map<String, Object>> tableColumns = DatabaseUtil.getTableColumns(url, username, password, selectedTables);
// 生成后端代码
BackendCodeGenerator.generateBackendCode(url, username, password, selectedTables, moduleName, tableColumns);
// 生成前端Vue2代码
if (generateVue2CheckBox.isSelected()) {
FrontendCodeGenerator.generateVue2Code(selectedTables, tableColumns);
}
// 生成前端Vue3代码
if (generateVue3CheckBox.isSelected()) {
FrontendCodeGenerator.generateVue3Code(selectedTables, tableColumns);
}
JOptionPane.showMessageDialog(this, "代码生成完成", "提示", JOptionPane.INFORMATION_MESSAGE);
} catch (Exception ex) {
JOptionPane.showMessageDialog(this, "代码生成失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
ex.printStackTrace();
}
}
// 获取选中的表
private List<String> getSelectedTables() {
return java.util.stream.IntStream.range(0, tableModel.getRowCount())
.filter(i -> (Boolean) tableModel.getValueAt(i, 0))
.mapToObj(i -> (String) tableModel.getValueAt(i, 1))
.collect(java.util.stream.Collectors.toList());
}
// 更新连接UI在数据库连接配置变更时调用
public void updateConnectionUI() {
// 重新加载配置
loadConfig();
}
}

View File

@ -0,0 +1,151 @@
package com.codegenerator.util;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CodeTestUtil {
// 测试生成的代码
public static Map<String, Object> testGeneratedCode(boolean generateVue2,boolean generateVue3) {
Map<String, Object> testResult = new HashMap<>();
List<String> successFiles = new ArrayList<>();
List<String> errorFiles = new ArrayList<>();
boolean isSuccess = true;
try {
String projectPath = System.getProperty("user.dir");
// 检查后端代码
testResult.put("backendTest", checkBackendCode(projectPath, successFiles, errorFiles));
if (generateVue2){
// 检查Vue2前端代码
testResult.put("vue2Test", checkFrontendCode(projectPath, "vue2", successFiles, errorFiles));
}
if (generateVue3) {
// 检查Vue3前端代码
testResult.put("vue3Test", checkFrontendCode(projectPath, "vue3", successFiles, errorFiles));
}
// 综合结果
if (!errorFiles.isEmpty()) {
isSuccess = false;
}
} catch (Exception e) {
errorFiles.add("测试过程中发生异常: " + e.getMessage());
isSuccess = false;
}
testResult.put("success", isSuccess);
testResult.put("successFiles", successFiles);
testResult.put("errorFiles", errorFiles);
return testResult;
}
// 检查后端代码
private static boolean checkBackendCode(String projectPath, List<String> successFiles, List<String> errorFiles) {
boolean isSuccess = true;
String backendDirPath = projectPath + "/generated-code/backend";
File backendDir = new File(backendDirPath);
if (!backendDir.exists() || backendDir.listFiles() == null || backendDir.listFiles().length == 0) {
errorFiles.add("后端代码目录不存在或为空: " + backendDirPath);
return false;
}
// 检查Controller文件是否符合RESTful规范
File controllerDir = new File(backendDirPath + "/src/main/java/com/example/controller");
if (controllerDir.exists() && controllerDir.isDirectory()) {
File[] controllerFiles = controllerDir.listFiles((dir, name) -> name.endsWith("Controller.java"));
if (controllerFiles != null && controllerFiles.length > 0) {
for (File file : controllerFiles) {
try {
String content = new String(java.nio.file.Files.readAllBytes(file.toPath()));
// 检查是否包含RESTful注解
if (content.contains("@RestController") && content.contains("@RequestMapping") &&
content.contains("@GetMapping") && content.contains("@PostMapping") &&
content.contains("@PutMapping") && content.contains("@DeleteMapping")) {
successFiles.add("后端Controller文件符合RESTful规范: " + file.getName());
} else {
errorFiles.add("后端Controller文件不符合RESTful规范: " + file.getName());
isSuccess = false;
}
} catch (IOException e) {
errorFiles.add("读取Controller文件失败: " + file.getName() + ", 错误: " + e.getMessage());
isSuccess = false;
}
}
}
}
return isSuccess;
}
// 检查前端代码
private static boolean checkFrontendCode(String projectPath, String version, List<String> successFiles, List<String> errorFiles) {
boolean isSuccess = true;
String frontendDirPath = projectPath + "/generated-code/frontend/" + version;
File frontendDir = new File(frontendDirPath);
if (!frontendDir.exists() || frontendDir.listFiles() == null || frontendDir.listFiles().length == 0) {
errorFiles.add(version + "前端代码目录不存在或为空: " + frontendDirPath);
return false;
}
// 检查Vue组件文件
File viewsDir = new File(frontendDirPath + "/src/views");
if (viewsDir.exists() && viewsDir.isDirectory()) {
File[] viewDirs = viewsDir.listFiles(File::isDirectory);
if (viewDirs != null && viewDirs.length > 0) {
for (File dir : viewDirs) {
File listVueFile = new File(dir, "List.vue");
File formVueFile = new File(dir, "Form.vue");
if (listVueFile.exists()) {
successFiles.add(version + "列表组件文件存在: " + listVueFile.getPath());
} else {
errorFiles.add(version + "列表组件文件不存在: " + listVueFile.getPath());
isSuccess = false;
}
if (formVueFile.exists()) {
successFiles.add(version + "表单组件文件存在: " + formVueFile.getPath());
} else {
errorFiles.add(version + "表单组件文件不存在: " + formVueFile.getPath());
isSuccess = false;
}
}
}
}
// 检查API文件
File apiDir = new File(frontendDirPath + "/src/api");
if (apiDir.exists() && apiDir.isDirectory()) {
File[] apiFiles = apiDir.listFiles((dir, name) -> name.endsWith(".js"));
if (apiFiles != null && apiFiles.length > 0) {
for (File file : apiFiles) {
successFiles.add(version + "API文件存在: " + file.getName());
}
} else {
errorFiles.add(version + "API文件不存在: " + apiDir.getPath());
isSuccess = false;
}
}
// 检查request.js工具文件
File utilsDir = new File(frontendDirPath + "/src/utils");
File requestFile = new File(utilsDir, "request.js");
if (requestFile.exists()) {
successFiles.add(version + "请求工具文件存在: " + requestFile.getPath());
} else {
errorFiles.add(version + "请求工具文件不存在: " + requestFile.getPath());
isSuccess = false;
}
return isSuccess;
}
}

View File

@ -0,0 +1,161 @@
package com.codegenerator.util;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.setting.dialect.Props;
import com.codegenerator.config.DbConnectionConfig;
import com.codegenerator.config.GeneratorConfig;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ConfigUtil {
private static final String CONFIG_FILE = "codegenerator.properties";
private static Props props;
static {
init();
}
private static void init() {
File file = new File(CONFIG_FILE);
if (!file.exists()) {
FileUtil.touch(file);
}
props = new Props(file);
}
// 保存数据库连接配置
public static void saveDbConfig(String connName, String dbType, String url, String username, String password) {
props.setProperty("db.connName", connName);
props.setProperty("db.type", dbType);
props.setProperty("db.url", url);
props.setProperty("db.username", username);
props.setProperty("db.password", password);
props.store(CONFIG_FILE);
}
// 保存多个数据库连接配置
public static void saveDbConnections(List<DbConnectionConfig> connections, int selectedIndex) {
// 清除旧的连接配置
for (int i = 0; i < 100; i++) { // 最大100个连接
String prefix = "db.connections." + i + ".";
if (!props.containsKey(prefix + "name")) {
break;
}
props.remove(prefix + "name");
props.remove(prefix + "dbType");
props.remove(prefix + "url");
props.remove(prefix + "username");
props.remove(prefix + "password");
}
// 保存新的连接配置
for (int i = 0; i < connections.size(); i++) {
DbConnectionConfig conn = connections.get(i);
String prefix = "db.connections." + i + ".";
props.setProperty(prefix + "name", conn.getName());
props.setProperty(prefix + "dbType", conn.getDbType());
props.setProperty(prefix + "url", conn.getUrl());
props.setProperty(prefix + "username", conn.getUsername());
props.setProperty(prefix + "password", conn.getPassword());
}
// 保存当前选中的连接索引
props.setProperty("db.selectedIndex", String.valueOf(selectedIndex));
props.store(CONFIG_FILE);
}
// 加载多个数据库连接配置
public static GeneratorConfig loadDbConnections() {
GeneratorConfig config = new GeneratorConfig();
config.setDbConnections(new ArrayList<>());
// 加载连接列表
for (int i = 0; i < 100; i++) { // 最大100个连接
String prefix = "db.connections." + i + ".";
if (!props.containsKey(prefix + "name")) {
break;
}
DbConnectionConfig conn = new DbConnectionConfig();
conn.setName(props.getStr(prefix + "name", ""));
conn.setDbType(props.getStr(prefix + "dbType", "mysql"));
conn.setUrl(props.getStr(prefix + "url", ""));
conn.setUsername(props.getStr(prefix + "username", ""));
conn.setPassword(props.getStr(prefix + "password", ""));
config.getDbConnections().add(conn);
}
// 加载选中的连接索引
int selectedIndex = props.getInt("db.selectedIndex", 0);
config.setSelectedDbConnectionIndex(selectedIndex);
// 如果没有连接初始化默认连接
if (config.getDbConnections().isEmpty()) {
config.initDefaultConnections();
}
return config;
}
// 获取数据库连接配置
public static Map<String, String> getDbConfig() {
Map<String, String> config = new HashMap<>();
config.put("connName", props.getStr("db.connName", ""));
config.put("dbType", props.getStr("db.type", "mysql"));
config.put("url", props.getStr("db.url", ""));
config.put("username", props.getStr("db.username", ""));
config.put("password", props.getStr("db.password", ""));
return config;
}
// 保存代码生成配置
public static void saveGeneratorConfig(String basePackage, boolean useLombok,
String superEntityClass, String[] superEntityColumns,boolean generateVue2,boolean generateVue3) {
props.setProperty("codegenerator.basePackage", basePackage);
props.setProperty("codegenerator.useLombok", String.valueOf(useLombok));
props.setProperty("codegenerator.superEntityClass", superEntityClass);
props.setProperty("codegenerator.superEntityColumns", StrUtil.join(",", superEntityColumns));
props.setProperty("codegenerator.generateVue2", generateVue2);
props.setProperty("codegenerator.generateVue3", generateVue3);
props.store(CONFIG_FILE);
}
public static void main(String[] args) {
saveGeneratorConfig("com.seer.teach", true, "com.seer.teach.common.BaseEntity", new String[]{"id", "createTime", "updateTime"},true,true);
System.out.println(getGeneratorConfig());
}
// 获取代码生成配置 - 返回配置对象
public static GeneratorConfig getGeneratorConfigAsObject() {
GeneratorConfig config = new GeneratorConfig();
Map<String, Object> generatorConfig = getGeneratorConfig();
config.setBasePackage((String) generatorConfig.get("basePackage"));
config.setUseLombok((Boolean) generatorConfig.get("useLombok"));
config.setSuperEntityClass((String) generatorConfig.get("superEntityClass"));
config.setSuperEntityColumns((String[]) generatorConfig.get("superEntityColumns"));
config.setGenerateVue2((Boolean) generatorConfig.get("generateVue2"));
config.setGenerateVue3((Boolean) generatorConfig.get("generateVue3"));
return config;
}
// 获取代码生成配置
public static Map<String, Object> getGeneratorConfig() {
Map<String, Object> config = new HashMap<>();
config.put("basePackage", props.getStr("codegenerator.basePackage", "com.seer.teach"));
config.put("useLombok", props.getBool("codegenerator.useLombok", true));
config.put("superEntityClass", props.getStr("codegenerator.superEntityClass", "com.seer.teach.common.BaseEntity"));
config.put("superEntityColumns", props.getStr("codegenerator.superEntityColumns", "id,createTime,updateTime").split(","));
config.put("generateVue3", props.getBool("codegenerator.generateVue3", true));
config.put("generateVue2", props.getBool("codegenerator.generateVue2", true));
return config;
}
}

View File

@ -0,0 +1,195 @@
package com.codegenerator.util;
import com.mysql.cj.jdbc.Driver;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DatabaseUtil {
private static Connection connection;
// 测试数据库连接
public static boolean testConnection(String url, String username, String password) {
try {
Class.forName(Driver.class.getName());
connection = java.sql.DriverManager.getConnection(url, username, password);
return connection.isValid(3000);
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
// 获取数据库连接
public static Connection getConnection(String url, String username, String password) throws SQLException, ClassNotFoundException {
Class.forName(Driver.class.getName());
return java.sql.DriverManager.getConnection(url, username, password);
}
// 获取所有非系统表
public static List<String> getTables(String url, String username, String password) {
List<String> tables = new ArrayList<>();
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = getConnection(url, username, password);
// MySQL查询非系统表
String sql = "SELECT table_name FROM information_schema.tables " +
"WHERE table_schema = (SELECT database()) " +
"AND table_type = 'BASE TABLE' " +
"AND table_name NOT LIKE 'sys_%' " +
"AND table_name NOT LIKE 'information_schema_%' " +
"AND table_name NOT LIKE 'mysql_%' " +
"AND table_name NOT LIKE 'performance_schema_%'";
// 使用传统JDBC方法执行查询
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
// 处理结果集
while (rs.next()) {
tables.add(rs.getString("table_name"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
if (rs != null) {
try { rs.close(); } catch (SQLException e) { e.printStackTrace(); }
}
if (stmt != null) {
try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); }
}
if (conn != null) {
try { conn.close(); } catch (SQLException e) { e.printStackTrace(); }
}
}
return tables;
}
/**
* 获取表的列信息
* @param url 数据库连接URL
* @param username 用户名
* @param password 密码
* @param tableNames 表名列表
* @return 表的列信息
*/
public static Map<String, Map<String, Object>> getTableColumns(String url, String username, String password, List<String> tableNames) {
Map<String, Map<String, Object>> tableColumnsInfo = new HashMap<>();
// 创建表名列表的SQL字符串
StringBuilder tableNamesList = new StringBuilder();
for (int i = 0; i < tableNames.size(); i++) {
if (i > 0) {
tableNamesList.append(",");
}
tableNamesList.append("'").append(tableNames.get(i)).append("'");
}
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = getConnection(url, username, password);
// 一次性查询所有表的列信息
String sql = "SELECT " +
"TABLE_NAME, " +
"COLUMN_NAME, " +
"DATA_TYPE, " +
"IS_NULLABLE, " +
"COLUMN_DEFAULT, " +
"COLUMN_COMMENT, " +
"ORDINAL_POSITION, " +
"CHARACTER_MAXIMUM_LENGTH, " +
"NUMERIC_PRECISION, " +
"NUMERIC_SCALE, " +
"COLUMN_KEY " +
"FROM information_schema.COLUMNS " +
"WHERE TABLE_SCHEMA = (SELECT DATABASE()) " +
"AND TABLE_NAME IN (" + tableNamesList.toString() + ") " +
"ORDER BY TABLE_NAME, ORDINAL_POSITION";
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
// 按表名分组处理结果
String currentTable = null;
Map<String, Object> currentTableInfo = null;
List<Map<String, Object>> currentColumns = null;
while (rs.next()) {
String tableName = rs.getString("TABLE_NAME");
// 如果是新表初始化表信息
if (!tableName.equals(currentTable)) {
if (currentTable != null) {
// 保存上一个表的信息
currentTableInfo.put("columns", currentColumns);
tableColumnsInfo.put(currentTable, currentTableInfo);
}
// 初始化新表的信息
currentTable = tableName;
currentTableInfo = new HashMap<>();
currentColumns = new ArrayList<>();
}
// 添加列信息
Map<String, Object> column = new HashMap<>();
column.put("columnName", rs.getString("COLUMN_NAME"));
column.put("dataType", rs.getString("DATA_TYPE"));
column.put("isNullable", rs.getString("IS_NULLABLE"));
column.put("columnDefault", rs.getString("COLUMN_DEFAULT"));
column.put("columnComment", rs.getString("COLUMN_COMMENT"));
column.put("ordinalPosition", rs.getInt("ORDINAL_POSITION"));
column.put("characterMaximumLength", rs.getObject("CHARACTER_MAXIMUM_LENGTH"));
column.put("numericPrecision", rs.getObject("NUMERIC_PRECISION"));
column.put("numericScale", rs.getObject("NUMERIC_SCALE"));
column.put("columnKey", rs.getString("COLUMN_KEY"));
currentColumns.add(column);
}
// 保存最后一个表的信息
if (currentTable != null) {
currentTableInfo.put("columns", currentColumns);
tableColumnsInfo.put(currentTable, currentTableInfo);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
if (rs != null) {
try { rs.close(); } catch (SQLException e) { e.printStackTrace(); }
}
if (stmt != null) {
try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); }
}
if (conn != null) {
try { conn.close(); } catch (SQLException e) { e.printStackTrace(); }
}
}
return tableColumnsInfo;
}
}

View File

@ -0,0 +1 @@
package com.seer.teach.common.entity; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import lombok.Data; import lombok.ToString; import java.time.LocalDateTime; /** * @Author: Captain * @Autograph: 安稳 * @Description: * @Date: 2023-07-20 16:44:26 */ @Data @ToString public class BaseEntity { /** * 主键 */ @TableId(value = "id", type = IdType.AUTO) private Integer id; /** * 是否删除,0-正常1-删除 */ @TableField("deleted") @TableLogic private Integer deleted=0; /** * 更新时间 */ @TableField(value = "update_time", fill = FieldFill.UPDATE) private LocalDateTime updateTime; /** * 创建人 */ @TableField(value = "create_by", fill = FieldFill.INSERT) private String createBy; /** * 创建时间 */ @TableField(value = "create_time", fill = FieldFill.INSERT) private LocalDateTime createTime; /** * 修改人 */ @TableField(value = "update_by", fill = FieldFill.UPDATE) private String updateBy; /** * 租户ID */ @TableField(value = "tenant_id") private String tenantId; }

View File

@ -0,0 +1,21 @@
package ${basePackage}.${moduleName}.convert;
import ${basePackage}.${moduleName}.admin.controller.request.${className}Req;
import ${basePackage}.${moduleName}.admin.controller.response.${className}Resp;
import ${basePackage}.${moduleName}.entity.${className}Entity;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface ${prefix}${className}Convert {
${prefix}${className}Convert INSTANCE = Mappers.getMapper(${prefix}${className}Convert.class);
${prefix}${className}Resp entityToResponse(${className}Entity ${varName}Entity);
List<${prefix}${className}Resp> entityListToResponseList(List<${className}Entity> ${varName}EntityList);
${className}Entity reqToEntity(${prefix}${className}Req ${varName}Request);
}

View File

@ -0,0 +1,74 @@
package ${basePackage}.controller;
import com.seer.teach.common.PageListBean;
import com.seer.teach.common.ResultBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.annotation.SaCheckPermission;
import ${basePackage}.${moduleName}.admin.controller.request.Admin${className}Req;
import ${basePackage}.${moduleName}.admin.controller.response.Admin${className}Resp;
import ${basePackage}.${moduleName}.service.Admin${className}Service;
import java.util.List;
/**
* ${className}控制器
*
* @author ${author}
* @since ${date?string("yyyy-MM-dd")}
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/${moduleName}/${className}")
@Api(tags = "${className}管理")
public class ${prefix}${className}Controller {
private final ${prefix}${className}Service admin${className}Service;
@GetMapping("/paged")
@ApiOperation("分页查询${className}")
public ResultBean<PageListBean<${prefix}${className}Resp>> pageList(@RequestBody Admin${className}PageReq pageRequest) {
return ResultBean.success(admin${className}Service.pageList(pageRequest));
}
@GetMapping("/list")
@ApiOperation("获取${className}列表")
public ResultBean<List<${prefix}${className}Resp>> list(Admin${className}QueryReq queryRequest) {
return ResultBean.success(admin${className}Service.list(queryRequest));
}
@GetMapping("/{id}")
@ApiOperation("根据ID获取${className}")
public ResultBean<${prefix}${className}Resp> getById(@PathVariable Integer id) {
return ResultBean.success(admin${className}Service.getDetail(id));
}
@PostMapping
@ApiOperation("新增${className}")
public ResultBean<Boolean> save(@RequestBody Admin${className}Req requestParam) {
return ResultBean.success(admin${className}Service.save(requestParam));
}
@PutMapping
@ApiOperation("更新${className}")
public ResultBean<Boolean> update(@RequestBody Admin${className}Req requestParam) {
return ResultBean.success(admin${className}Service.updateById(requestParam));
}
@DeleteMapping("/{id}")
@ApiOperation("删除${className}")
public ResultBean<Boolean> delete(@PathVariable Integer id) {
return ResultBean.success(admin${className}Service.deleteById(id));
}
}

View File

@ -0,0 +1,37 @@
package ${basePackage}.${moduleName}.admin.controller.request;
import com.seer.teach.common.request.PageRequest;
import lombok.Data;
<#if hasDateField>
import java.util.Date;
</#if>
<#if hasBigDecimalField>
import java.math.BigDecimal;
</#if>
/**
* 分页请求基类
*/
@Data
public class Admin${className}PageReq extends PageRequest {
<#if columns??>
<#list columns as column>
<#assign javaType = column.javaType>
<#if javaType == "Date">
private Date ${column.propertyName};
<#elseif javaType == "BigDecimal">
private BigDecimal ${column.propertyName};
<#elseif javaType == "Boolean">
private Boolean ${column.propertyName};
<#elseif javaType == "Integer">
private Integer ${column.propertyName};
<#elseif javaType == "Long">
private Long ${column.propertyName};
<#elseif javaType == "Double">
private Double ${column.propertyName};
<#else>
private String ${column.propertyName};
</#if>
</#list>
</#if>
}

View File

@ -0,0 +1,36 @@
package ${basePackage}.${moduleName}.admin.controller.request;
import lombok.Data;
<#if hasDateField>
import java.util.Date;
</#if>
<#if hasBigDecimalField>
import java.math.BigDecimal;
</#if>
/**
* ${className}查询请求类
*/
@Data
public class Admin${className}QueryReq {
<#if columns??>
<#list columns as column>
<#assign javaType = column.javaType>
<#if javaType == "Date">
private Date ${column.propertyName};
<#elseif javaType == "BigDecimal">
private BigDecimal ${column.propertyName};
<#elseif javaType == "Boolean">
private Boolean ${column.propertyName};
<#elseif javaType == "Integer">
private Integer ${column.propertyName};
<#elseif javaType == "Long">
private Long ${column.propertyName};
<#elseif javaType == "Double">
private Double ${column.propertyName};
<#else>
private String ${column.propertyName};
</#if>
</#list>
</#if>
}

View File

@ -0,0 +1,36 @@
package ${basePackage}.controller.request;
import lombok.Data;
<#if hasDateField>
import java.util.Date;
</#if>
<#if hasBigDecimalField>
import java.math.BigDecimal;
</#if>
/**
* ${className}请求类
*/
@Data
public class Admin${className}Req {
<#if columns??>
<#list columns as column>
<#assign javaType = column.javaType>
<#if javaType == "Date">
private Date ${column.propertyName};
<#elseif javaType == "BigDecimal">
private BigDecimal ${column.propertyName};
<#elseif javaType == "Boolean">
private Boolean ${column.propertyName};
<#elseif javaType == "Integer">
private Integer ${column.propertyName};
<#elseif javaType == "Long">
private Long ${column.propertyName};
<#elseif javaType == "Double">
private Double ${column.propertyName};
<#else>
private String ${column.propertyName};
</#if>
</#list>
</#if>
}

View File

@ -0,0 +1,36 @@
package ${basePackage}.${moduleName}.admin.controller.response;
import lombok.Data;
<#if hasDateField>
import java.util.Date;
</#if>
<#if hasBigDecimalField>
import java.math.BigDecimal;
</#if>
/**
* ${className}响应类
*/
@Data
public class Admin${className}Resp {
<#if columns??>
<#list columns as column>
<#assign javaType = column.javaType>
<#if javaType == "Date">
private Date ${column.propertyName};
<#elseif javaType == "BigDecimal">
private BigDecimal ${column.propertyName};
<#elseif javaType == "Boolean">
private Boolean ${column.propertyName};
<#elseif javaType == "Integer">
private Integer ${column.propertyName};
<#elseif javaType == "Long">
private Long ${column.propertyName};
<#elseif javaType == "Double">
private Double ${column.propertyName};
<#else>
private String ${column.propertyName};
</#if>
</#list>
</#if>
}

View File

@ -0,0 +1,78 @@
package ${basePackage}.${moduleName}.admin.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.seer.teach.common.PageListBean;
import com.seer.teach.common.utils.PageConverterUtils;
import ${basePackage}.${moduleName}.controller.request.${className}PageRequest;
import ${basePackage}.${moduleName}.controller.request.${className}Req;
import ${basePackage}.${moduleName}.controller.response.${className}Response;
import ${basePackage}.${moduleName}.convert.${className}Convert;
import ${basePackage}.entity.${className}Entity;
import ${basePackage}.service.I${className}Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
@Slf4j
@RequiredArgsConstructor
@Service
public class Admin${className}Service {
private final I${className}Service ${varName}Service;
public PageListBean<${prefix}${className}Resp> pageList(${prefix}${className}PageReq pageRequest) {
IPage<${className}Entity> page = new Page<>(pageRequest.getPageNo(), pageRequest.getPageSize());
IPage<${className}Entity> result = ${varName}Service.page(page, new LambdaQueryWrapper<${className}Entity>()
<#if columns?? && columns?size gt 0>
<#list columns as column>
.eq(Objects.nonNull(pageRequest.get${column.propertyName?cap_first}()), ${className}Entity::get${column.propertyName?cap_first}, pageRequest.get${column.propertyName?cap_first}())
</#list>
</#if>
);
return PageConverterUtils.convertPageListBean(result, ${prefix}${className}Convert.INSTANCE::entityListToResponseList);
}
public List<${prefix}${className}Resp> list(${prefix}${className}QueryReq queryParam) {
List<${className}Entity> result = ${varName}Service.list(new LambdaQueryWrapper<${className}Entity>()
<#if columns?? && columns?size gt 0>
<#list columns as column>
.eq(Objects.nonNull(queryParam.get${column.propertyName?cap_first}()), ${className}Entity::get${column.propertyName?cap_first}, queryParam.get${column.propertyName?cap_first}())
</#list>
</#if>
);
return ${prefix}${className}Convert.INSTANCE.entityListToResponseList(result);
}
public ${prefix}${className}Resp getDetail(Integer id) {
${className}Entity result = ${varName}Service.getById(id);
log.info("getById:{}", result);
return ${prefix}${className}Convert.INSTANCE.entityToResponse(result);
}
public boolean save(${prefix}${className}Req ${varName}) {
${className}Entity entity = ${prefix}${className}Convert.INSTANCE.reqToEntity(${varName});
boolean saved = ${varName}Service.save(entity);
log.info("save:{}", saved);
return saved;
}
public boolean updateById(${prefix}${className}Req ${varName}) {
${className}Entity entity = ${prefix}${className}Convert.INSTANCE.reqToEntity(${varName});
boolean updated = ${varName}Service.updateById(entity);
log.info("updateById:{}", updated);
return updated;
}
public boolean deleteById(Integer id) {
boolean updated = ${varName}Service.removeById(id);
log.info("deleteById:{}", updated);
return updated;
}
}

View File

@ -0,0 +1,47 @@
import axios from 'axios'
// 创建axios实例
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
timeout: 5000 // 请求超时时间
})
// 请求拦截器
service.interceptors.request.use(
config => {
// 在发送请求之前做些什么
// 如果有token就添加到请求头中
if ([token]) {
config.headers['Authorization'] = [token]
}
return config
},
error => {
// 对请求错误做些什么
console.log(error)
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data
// 如果返回的状态码不是200则判断为错误
if (res.code !== 200) {
// 处理错误信息
console.log('Error: ' + res.message)
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
error => {
// 对响应错误做些什么
console.log('err' + error)
return Promise.reject(error)
}
)
export default service

View File

@ -0,0 +1,112 @@
<template>
<el-dialog
:title="title"
:visible.sync="visible"
width="500px"
:close-on-click-modal="false"
>
<el-form
ref="form"
:model="model"
:rules="rules"
label-width="100px"
>
<!-- 这里可以根据实际表结构动态生成表单字段 -->
<el-form-item label="ID" v-if="model.id">
<el-input v-model="model.id" disabled></el-input>
</el-form-item>
<#if columns?? && columns.columns??>
<#list columns.columns as column>
<#assign columnName = column.columnName>
<#assign propertyName = column.propertyName>
<#assign comment = column.comment!columnName>
<#if !["id", "createTime", "updateTime", "createBy", "updateBy", "deleted"].contains(propertyName)>
<el-form-item label="${comment}" prop="${propertyName}">
<#if column.javaType == "String">
<el-input v-model="model.${propertyName}" placeholder="请输入${comment}" />
<#elseif column.javaType == "Integer" || column.javaType == "Long" || column.javaType == "Double">
<el-input-number v-model="model.${propertyName}" placeholder="请输入${comment}" />
<#elseif column.javaType == "Boolean">
<el-switch v-model="model.${propertyName}" />
<#elseif column.javaType == "Date">
<el-date-picker v-model="model.${propertyName}" type="date" placeholder="请选择${comment}" />
<#else>
<el-input v-model="model.${propertyName}" placeholder="请输入${comment}" />
</#if>
</el-form-item>
</#if>
</#list>
</#if>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</div>
</el-dialog>
</template>
<script>
import { save${className}, update${className} } from '@/api/${varName}'
export default {
name: '${className}Form',
props: {
visible: {
type: Boolean,
default: false
},
model: {
type: Object,
default: () => ({})
}
},
data() {
return {
rules: {
// 这里可以根据实际表结构动态生成表单验证规则
<#if columns?? && columns.columns??>
<#list columns.columns as column>
<#assign propertyName = column.propertyName>
</#if>
</#list>
</#if>
}
}
},
computed: {
title() {
return this.model.id ? '编辑${className}' : '新增${className}'
}
},
methods: {
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
const isEdit = !!this.model.id
const request = isEdit ? update${className}(this.model) : save${className}(this.model)
request.then(response => {
if (response.code === 200) {
this.$message.success(isEdit ? '更新成功' : '新增成功')
this.$emit('success')
} else {
this.$message.error(response.msg)
}
}).catch(error => {
this.$message.error(isEdit ? '更新失败' : '新增失败')
console.error(error)
})
}
})
},
handleCancel() {
this.$emit('close')
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,165 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="searchParams.keyword" placeholder="请输入关键词" style="width: 200px;" class="filter-item" @keyup.enter.native="handleQuery" />
<el-button type="primary" icon="el-icon-search" @click="handleQuery" class="filter-item">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery" class="filter-item">重置</el-button>
<el-button type="primary" icon="el-icon-plus" @click="handleAdd" class="filter-item">新增</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
border
fit
highlight-current-row
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="ID" prop="id" width="80" align="center" />
<#if columns?? && columns.columns??>
<#list columns.columns as column>
<#assign columnName = column.columnName>
<#assign propertyName = column.propertyName>
<#assign comment = column.comment!columnName>
<el-table-column label="${comment}" prop="${propertyName}" />
</#list>
</#if>
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="handleView(scope.row)">查看</el-button>
<el-button size="mini" type="text" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="mini" type="text" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
<!-- 新增/编辑弹窗 -->
<el-dialog :visible.sync="dialogVisible" :title="dialogTitle" width="500px">
<${entityName}Form ref="form" :data="formData" />
<div slot="footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { get${entityName}List, delete${entityName} } from '@/api/${lowerEntityName}'
import ${entityName}Form from './${entityName}Form'
export default {
name: '${entityName}List',
components: { ${entityName}Form },
data() {
return {
list: [],
listLoading: false,
searchParams: {
keyword: ''
},
pagination: {
currentPage: 1,
pageSize: 10,
total: 0
},
dialogVisible: false,
dialogTitle: '',
formData: {}
}
},
created() {
this.handleQuery()
},
methods: {
handleQuery() {
this.listLoading = true
get${entityName}List({
pageNum: this.pagination.currentPage,
pageSize: this.pagination.pageSize,
...this.searchParams
}).then(response => {
this.list = response.data.records
this.pagination.total = response.data.total
this.listLoading = false
}).catch(() => {
this.listLoading = false
})
},
resetQuery() {
this.searchParams = {
keyword: ''
}
this.pagination.currentPage = 1
this.handleQuery()
},
handleSizeChange(val) {
this.pagination.pageSize = val
this.handleQuery()
},
handleCurrentChange(val) {
this.pagination.currentPage = val
this.handleQuery()
},
handleAdd() {
this.dialogTitle = '新增${entityName}'
this.formData = {}
this.dialogVisible = true
},
handleEdit(row) {
this.dialogTitle = '编辑${entityName}'
this.formData = { ...row }
this.dialogVisible = true
},
handleView(row) {
this.dialogTitle = '查看${entityName}'
this.formData = { ...row }
this.dialogVisible = true
},
handleDelete(row) {
this.$confirm('确定要删除这条记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
delete${entityName}(row.id).then(() => {
this.$message.success('删除成功')
this.handleQuery()
})
})
},
handleSubmit() {
this.$refs.form.validate(valid => {
if (valid) {
// 调用保存方法,实际项目中需要根据新增/编辑调用不同接口
this.$message.success('保存成功')
this.dialogVisible = false
this.handleQuery()
}
})
}
}
}
</script>
<style scoped>
.filter-container {
padding: 10px 0;
}
.filter-item {
margin-right: 10px;
}
</style>
</file5>

View File

@ -0,0 +1,92 @@
<template>
<el-dialog
:title="title"
v-model="visible"
width="500px"
:close-on-click-modal="false"
>
<el-form
ref="formRef"
:model="model"
:rules="rules"
label-width="100px"
>
<!-- 这里可以根据实际表结构动态生成表单字段 -->
<el-form-item label="ID" v-if="model.id">
<el-input v-model="model.id" disabled></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, computed, defineProps, defineEmits } from 'vue'
import { save${className}, update${className} } from '@/api/${varName}'
import { ElMessage } from 'element-plus'
// 定义属性
const props = defineProps({
visible: {
type: Boolean,
default: false
},
model: {
type: Object,
default: () => ({})
}
})
// 定义事件
const emit = defineEmits(['success', 'close'])
// 表单引用
const formRef = ref(null)
// 表单数据
const formModel = ref({ ...props.model })
// 验证规则
const rules = ref({
// 这里可以根据实际表结构动态生成表单验证规则
})
// 标题
const title = computed(() => {
return formModel.value.id ? '编辑${className}' : '新增${className}'
})
// 提交表单
const handleSubmit = async () => {
try {
await formRef.value.validate()
const isEdit = !!formModel.value.id
const request = isEdit ? update${className}(formModel.value) : save${className}(formModel.value)
const response = await request
if (response.code === 200) {
ElMessage.success(isEdit ? '更新成功' : '新增成功')
emit('success')
} else {
ElMessage.error(response.msg)
}
} catch (error) {
console.error('表单验证失败:', error)
}
}
// 取消
const handleCancel = () => {
emit('close')
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,176 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="searchParams.keyword" placeholder="请输入关键词" style="width: 200px;" class="filter-item" @keyup.enter="handleQuery" />
<el-button type="primary" icon="Search" @click="handleQuery" class="filter-item">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" class="filter-item">重置</el-button>
<el-button type="primary" icon="Plus" @click="handleAdd" class="filter-item">新增</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
border
fit
highlight-current-row
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="ID" prop="id" width="80" align="center" />
<!-- 这里可以根据实际字段动态生成表格列 -->
<el-table-column label="操作" width="200" align="center">
<template #default="scope">
<el-button size="small" type="text" @click="handleView(scope.row)">查看</el-button>
<el-button size="small" type="text" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="text" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
<!-- 新增/编辑弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px">
<${entityName}Form ref="formRef" :data="formData" />
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { get${entityName}List, delete${entityName} } from '@/api/${lowerEntityName}'
import ${entityName}Form from './${entityName}Form.vue'
import { ElMessage, ElConfirm } from 'element-plus'
// 列表数据
const list = ref([])
const listLoading = ref(false)
// 搜索参数
const searchParams = ref({
keyword: ''
})
// 分页参数
const pagination = ref({
currentPage: 1,
pageSize: 10,
total: 0
})
// 弹窗相关
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formData = ref({})
const formRef = ref(null)
// 页面加载时查询数据
onMounted(() => {
handleQuery()
})
// 查询数据
const handleQuery = () => {
listLoading.value = true
get${entityName}List({
pageNum: pagination.value.currentPage,
pageSize: pagination.value.pageSize,
...searchParams.value
}).then(response => {
list.value = response.data.records
pagination.value.total = response.data.total
listLoading.value = false
}).catch(() => {
listLoading.value = false
})
}
// 重置查询
const resetQuery = () => {
searchParams.value = {
keyword: ''
}
pagination.value.currentPage = 1
handleQuery()
}
// 分页大小变化
const handleSizeChange = (val) => {
pagination.value.pageSize = val
handleQuery()
}
// 当前页变化
const handleCurrentChange = (val) => {
pagination.value.currentPage = val
handleQuery()
}
// 新增
const handleAdd = () => {
dialogTitle.value = '新增${entityName}'
formData.value = {}
dialogVisible.value = true
}
// 编辑
const handleEdit = (row) => {
dialogTitle.value = '编辑${entityName}'
formData.value = { ...row }
dialogVisible.value = true
}
// 查看
const handleView = (row) => {
dialogTitle.value = '查看${entityName}'
formData.value = { ...row }
dialogVisible.value = true
}
// 删除
const handleDelete = (row) => {
ElConfirm('确定要删除这条记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
delete${entityName}(row.id).then(() => {
ElMessage.success('删除成功')
handleQuery()
})
})
}
// 提交表单
const handleSubmit = () => {
formRef.value.validate(valid => {
if (valid) {
// 调用保存方法,实际项目中需要根据新增/编辑调用不同接口
ElMessage.success('保存成功')
dialogVisible.value = false
handleQuery()
}
})
}
</script>
<style scoped>
.filter-container {
padding: 10px 0;
}
.filter-item {
margin-right: 10px;
}
</style>