Add comprehensive documentation for system architecture and best practices

This commit is contained in:
Cursor Agent
2025-06-18 08:04:08 +00:00
parent bbf6135e39
commit d8faa09372
9 changed files with 5748 additions and 0 deletions

View File

@@ -0,0 +1,916 @@
# 代码生成器使用指南
## 代码生成器概述
### 功能特性
- **前后端代码生成**: 一键生成 Java + Vue 完整代码
- **支持单表、树表、主子表**: 多种表结构类型
- **CRUD 操作**: 增删改查的完整实现
- **权限控制**: 自动生成权限注解
- **接口文档**: 自动生成 Swagger 文档
- **单元测试**: 生成完整的单元测试代码
### 生成内容
**后端 Java 代码**:
- Controller 控制器
- Service 业务逻辑层
- Mapper 数据访问层
- DO 数据对象
- VO 视图对象
- Convert 对象转换器
- 单元测试类
**前端 Vue 代码**:
- 列表页面
- 新增/编辑弹窗
- 搜索表单
- API 接口调用
**SQL 脚本**:
- 菜单权限 SQL
- 按钮权限 SQL
## 代码生成器表结构
### 生成表配置
```sql
-- 代码生成表定义
CREATE TABLE infra_codegen_table (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '编号',
data_source_config_id BIGINT NOT NULL COMMENT '数据源配置的编号',
scene TINYINT NOT NULL DEFAULT 1 COMMENT '生成场景',
table_name VARCHAR(200) NOT NULL DEFAULT '' COMMENT '表名称',
table_comment VARCHAR(500) NOT NULL DEFAULT '' COMMENT '表描述',
remark VARCHAR(255) DEFAULT NULL COMMENT '备注',
module_name VARCHAR(30) NOT NULL COMMENT '模块名',
business_name VARCHAR(30) NOT NULL COMMENT '业务名',
class_name VARCHAR(100) NOT NULL DEFAULT '' COMMENT '类名称',
class_comment VARCHAR(50) NOT NULL COMMENT '类描述',
author VARCHAR(50) NOT NULL COMMENT '作者',
template_type TINYINT NOT NULL DEFAULT 1 COMMENT '模板类型',
front_type TINYINT NOT NULL COMMENT '前端类型',
parent_menu_id BIGINT DEFAULT NULL COMMENT '父菜单编号',
master_table_id BIGINT DEFAULT NULL COMMENT '主表的编号',
sub_join_column_name VARCHAR(30) DEFAULT NULL COMMENT '子表关联主表的字段名',
sub_join_many BIT DEFAULT NULL COMMENT '主表与子表是否一对多',
tree_parent_column_name VARCHAR(30) DEFAULT NULL COMMENT '树表的父字段名',
tree_name_column_name VARCHAR(30) DEFAULT NULL COMMENT '树表的名字字段名',
creator VARCHAR(64) DEFAULT '' COMMENT '创建者',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updater VARCHAR(64) DEFAULT '' COMMENT '更新者',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除'
);
-- 代码生成表字段定义
CREATE TABLE infra_codegen_column (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '编号',
table_id BIGINT NOT NULL COMMENT '表编号',
column_name VARCHAR(200) NOT NULL COMMENT '字段名',
data_type VARCHAR(100) NOT NULL COMMENT '字段类型',
column_comment VARCHAR(500) NOT NULL COMMENT '字段描述',
nullable BIT NOT NULL COMMENT '是否允许为空',
primary_key BIT NOT NULL COMMENT '是否主键',
ordinal_position INT NOT NULL COMMENT '排序',
java_type VARCHAR(32) NOT NULL COMMENT 'Java 属性类型',
java_field VARCHAR(64) NOT NULL COMMENT 'Java 属性名',
dict_type VARCHAR(200) DEFAULT '' COMMENT '字典类型',
example VARCHAR(64) DEFAULT NULL COMMENT '数据示例',
create_operation BIT NOT NULL COMMENT '是否为 Create 创建操作的字段',
update_operation BIT NOT NULL COMMENT '是否为 Update 更新操作的字段',
list_operation BIT NOT NULL COMMENT '是否为 List 查询操作的字段',
list_operation_condition VARCHAR(32) NOT NULL DEFAULT '=' COMMENT 'List 查询操作的条件类型',
list_operation_result BIT NOT NULL COMMENT '是否为 List 查询操作的返回字段',
html_type VARCHAR(32) NOT NULL COMMENT '显示类型',
creator VARCHAR(64) DEFAULT '' COMMENT '创建者',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updater VARCHAR(64) DEFAULT '' COMMENT '更新者',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除'
);
```
## 代码生成服务
### 表导入服务
```java
@Service
public class CodegenTableServiceImpl implements CodegenTableService {
@Resource
private DatabaseTableService databaseTableService;
@Resource
private CodegenTableMapper codegenTableMapper;
@Resource
private CodegenColumnMapper codegenColumnMapper;
@Override
@Transactional
public List<Long> createCodegenList(Long dataSourceConfigId, List<String> tableNames) {
List<Long> ids = new ArrayList<>();
// 遍历添加每个表
tableNames.forEach(tableName -> {
Long id = createCodegen(dataSourceConfigId, tableName);
ids.add(id);
});
return ids;
}
private Long createCodegen(Long dataSourceConfigId, String tableName) {
// 从数据库中,获得数据库表结构
DatabaseTableRespDTO table = databaseTableService.getDatabaseTable(dataSourceConfigId, tableName);
List<DatabaseColumnRespDTO> columns = databaseTableService.getDatabaseColumnList(dataSourceConfigId, tableName);
// 导入数据库表结构
CodegenTableDO codegenTable = convertTable(table);
codegenTable.setDataSourceConfigId(dataSourceConfigId);
codegenTable.setScene(CodegenSceneEnum.ADMIN.getScene()); // 默认管理后台
codegenTable.setAuthor(CodegenConstants.DEFAULT_AUTHOR);
initTable(codegenTable);
codegenTableMapper.insert(codegenTable);
// 导入数据库字段结构
List<CodegenColumnDO> codegenColumns = convertColumns(codegenTable.getId(), columns);
// 初始化字段的默认值
codegenColumns.forEach(column -> initColumn(column, codegenTable));
codegenColumnMapper.insertBatch(codegenColumns);
return codegenTable.getId();
}
/**
* 初始化表的配置
*/
private void initTable(CodegenTableDO table) {
// 设置模块名
table.setModuleName(CodegenConstants.DEFAULT_MODULE_NAME);
// 设置业务名
table.setBusinessName(StrUtil.toCamelCase(table.getTableName().replace(table.getModuleName() + "_", "")));
// 设置类名
table.setClassName(StrUtil.upperFirst(StrUtil.toCamelCase(table.getBusinessName())));
// 设置类描述
if (StrUtil.isBlank(table.getClassComment())) {
table.setClassComment(table.getTableComment());
}
// 设置模板类型
table.setTemplateType(CodegenTemplateTypeEnum.CRUD.getType());
// 设置前端类型
table.setFrontType(CodegenFrontTypeEnum.VUE3.getType());
}
/**
* 初始化字段的配置
*/
private void initColumn(CodegenColumnDO column, CodegenTableDO table) {
// 设置 Java 属性类型
column.setJavaType(getJavaType(column.getDataType()));
// 设置 Java 属性名
column.setJavaField(StrUtil.toCamelCase(column.getColumnName()));
// 设置字典类型
column.setDictType(getDictType(column.getColumnName(), column.getColumnComment()));
// 设置操作字段
if (isBaseColumn(column.getColumnName())) {
column.setCreateOperation(false);
column.setUpdateOperation(false);
column.setListOperation(false);
column.setListOperationResult(false);
} else {
column.setCreateOperation(true);
column.setUpdateOperation(true);
column.setListOperation(true);
column.setListOperationResult(true);
}
// 设置 HTML 类型
column.setHtmlType(getHtmlType(column));
// 设置查询条件
column.setListOperationCondition(getListOperationCondition(column));
}
}
```
### 代码生成服务
```java
@Service
public class CodegenEngineServiceImpl implements CodegenEngineService {
@Resource
private CodegenTableService codegenTableService;
@Resource
private CodegenColumnService codegenColumnService;
@Override
public Map<String, String> execute(Long tableId) {
// 校验表和字段的有效性
CodegenTableDO table = codegenTableService.getCodegenTable(tableId);
List<CodegenColumnDO> columns = codegenColumnService.getCodegenColumnListByTableId(tableId);
// 执行生成
return execute0(table, columns);
}
public Map<String, String> execute0(CodegenTableDO table, List<CodegenColumnDO> columns) {
// 创建生成上下文
Map<String, Object> bindingMap = buildBindingMap(table, columns);
// 获得生成模板
List<CodegenTemplateDO> templates = getCodegenTemplateList(table.getTemplateType());
// 执行生成
Map<String, String> result = new LinkedHashMap<>();
for (CodegenTemplateDO template : templates) {
String content = templateEngine.getTemplate(template.getContent()).execute(bindingMap);
result.put(template.getFilePath(), content);
}
return result;
}
/**
* 构建模板上下文
*/
private Map<String, Object> buildBindingMap(CodegenTableDO table, List<CodegenColumnDO> columns) {
Map<String, Object> bindingMap = new HashMap<>();
// 全局变量
bindingMap.put("basePackage", CodegenConstants.BASE_PACKAGE);
bindingMap.put("baseFrameworkPackage", CodegenConstants.BASE_FRAMEWORK_PACKAGE);
bindingMap.put("dateTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
bindingMap.put("author", table.getAuthor());
// 表信息
bindingMap.put("table", table);
bindingMap.put("columns", columns);
// 主键信息
CodegenColumnDO primaryColumn = CollUtil.findOne(columns, CodegenColumnDO::getPrimaryKey);
bindingMap.put("primaryColumn", primaryColumn);
// 分类字段
List<CodegenColumnDO> createColumns = filterList(columns, CodegenColumnDO::getCreateOperation);
List<CodegenColumnDO> updateColumns = filterList(columns, CodegenColumnDO::getUpdateOperation);
List<CodegenColumnDO> listColumns = filterList(columns, CodegenColumnDO::getListOperation);
List<CodegenColumnDO> listOperationResultColumns = filterList(columns, CodegenColumnDO::getListOperationResult);
bindingMap.put("createColumns", createColumns);
bindingMap.put("updateColumns", updateColumns);
bindingMap.put("listColumns", listColumns);
bindingMap.put("listOperationResultColumns", listOperationResultColumns);
// 导入包
bindingMap.put("importPackages", buildImportPackages(table, columns));
return bindingMap;
}
/**
* 构建导入包
*/
private Set<String> buildImportPackages(CodegenTableDO table, List<CodegenColumnDO> columns) {
Set<String> packages = new HashSet<>();
// 根据字段类型,添加对应的包
for (CodegenColumnDO column : columns) {
if (JavaTypeEnum.DATE.getType().equals(column.getJavaType())
|| JavaTypeEnum.LOCAL_DATE_TIME.getType().equals(column.getJavaType())) {
packages.add("java.time.LocalDateTime");
}
if (JavaTypeEnum.BIG_DECIMAL.getType().equals(column.getJavaType())) {
packages.add("java.math.BigDecimal");
}
}
// 移除 java.lang 包下的类
packages.removeIf(packageName -> packageName.startsWith("java.lang."));
return packages;
}
}
```
## 代码模板
### Java Controller 模板
```java
package ${basePackage}.module.${table.moduleName}.controller.admin;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.annotations.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.*;
import ${baseFrameworkPackage}.common.pojo.PageResult;
import ${baseFrameworkPackage}.common.pojo.CommonResult;
import ${baseFrameworkPackage}.common.util.object.BeanUtils;
import static ${baseFrameworkPackage}.common.pojo.CommonResult.success;
import ${baseFrameworkPackage}.excel.core.util.ExcelUtils;
import ${baseFrameworkPackage}.operatelog.core.annotations.OperateLog;
import static ${baseFrameworkPackage}.operatelog.core.enums.OperateTypeEnum.*;
import ${basePackage}.module.${table.moduleName}.controller.admin.${table.className}Controller;
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.className}DO;
import ${basePackage}.module.${table.moduleName}.service.${table.className}Service;
import ${basePackage}.module.${table.moduleName}.controller.admin.vo.${table.businessName}.*;
@Api(tags = "管理后台 - ${table.classComment}")
@RestController
@RequestMapping("/admin-api/${table.moduleName}/${table.businessName}")
@Validated
public class ${table.className}Controller {
@Resource
private ${table.className}Service ${table.businessName}Service;
@PostMapping("/create")
@ApiOperation("创建${table.classComment}")
@PreAuthorize("@ss.hasPermission('${table.moduleName}:${table.businessName}:create')")
public CommonResult<${primaryColumn.javaType}> create${table.className}(@Valid @RequestBody ${table.className}CreateReqVO createReqVO) {
return success(${table.businessName}Service.create${table.className}(createReqVO));
}
@PutMapping("/update")
@ApiOperation("更新${table.classComment}")
@PreAuthorize("@ss.hasPermission('${table.moduleName}:${table.businessName}:update')")
public CommonResult<Boolean> update${table.className}(@Valid @RequestBody ${table.className}UpdateReqVO updateReqVO) {
${table.businessName}Service.update${table.className}(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@ApiOperation("删除${table.classComment}")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('${table.moduleName}:${table.businessName}:delete')")
public CommonResult<Boolean> delete${table.className}(@RequestParam("id") ${primaryColumn.javaType} id) {
${table.businessName}Service.delete${table.className}(id);
return success(true);
}
@GetMapping("/get")
@ApiOperation("获得${table.classComment}")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('${table.moduleName}:${table.businessName}:query')")
public CommonResult<${table.className}RespVO> get${table.className}(@RequestParam("id") ${primaryColumn.javaType} id) {
${table.className}DO ${table.businessName} = ${table.businessName}Service.get${table.className}(id);
return success(BeanUtils.toBean(${table.businessName}, ${table.className}RespVO.class));
}
@GetMapping("/page")
@ApiOperation("获得${table.classComment}分页")
@PreAuthorize("@ss.hasPermission('${table.moduleName}:${table.businessName}:query')")
public CommonResult<PageResult<${table.className}RespVO>> get${table.className}Page(@Valid ${table.className}PageReqVO pageReqVO) {
PageResult<${table.className}DO> pageResult = ${table.businessName}Service.get${table.className}Page(pageReqVO);
return success(BeanUtils.toBean(pageResult, ${table.className}RespVO.class));
}
@GetMapping("/export-excel")
@ApiOperation("导出${table.classComment} Excel")
@PreAuthorize("@ss.hasPermission('${table.moduleName}:${table.businessName}:export')")
@OperateLog(type = EXPORT)
public void export${table.className}Excel(@Valid ${table.className}PageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<${table.className}DO> list = ${table.businessName}Service.get${table.className}Page(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "${table.classComment}.xls", "数据", ${table.className}RespVO.class,
BeanUtils.toBean(list, ${table.className}RespVO.class));
}
}
```
### Vue 列表页面模板
```vue
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form
:model="queryParams"
ref="queryForm"
size="small"
:inline="true"
v-show="showSearch"
label-width="68px"
>
#foreach ($column in $listColumns)
#if ($column.listOperation)
<el-form-item label="${column.columnComment}" prop="${column.javaField}">
#if ($column.htmlType == "input")
<el-input
v-model="queryParams.${column.javaField}"
placeholder="请输入${column.columnComment}"
clearable
@keyup.enter.native="handleQuery"
/>
#elseif ($column.htmlType == "select" || $column.htmlType == "radio")
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnComment}" clearable>
<el-option
v-for="dict in ${column.javaField}DictDatas"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
#elseif ($column.htmlType == "datetime")
<el-date-picker
v-model="queryParams.${column.javaField}"
style="width: 240px"
value-format="yyyy-MM-dd HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
/>
#end
</el-form-item>
#end
#end
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['${table.moduleName}:${table.businessName}:create']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['${table.moduleName}:${table.businessName}:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 数据列表 -->
<el-table v-loading="loading" :data="${table.businessName}List">
#foreach ($column in $listOperationResultColumns)
<el-table-column label="${column.columnComment}" align="center" prop="${column.javaField}" />
#end
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['${table.moduleName}:${table.businessName}:update']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['${table.moduleName}:${table.businessName}:delete']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNo"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
#foreach ($column in $createColumns)
<el-form-item label="${column.columnComment}" prop="${column.javaField}">
#if ($column.htmlType == "input")
<el-input v-model="form.${column.javaField}" placeholder="请输入${column.columnComment}" />
#elseif ($column.htmlType == "select")
<el-select v-model="form.${column.javaField}" placeholder="请选择${column.columnComment}">
<el-option
v-for="dict in ${column.javaField}DictDatas"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
#elseif ($column.htmlType == "radio")
<el-radio-group v-model="form.${column.javaField}">
<el-radio
v-for="dict in ${column.javaField}DictDatas"
:key="dict.value"
:label="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
#elseif ($column.htmlType == "datetime")
<el-date-picker clearable
v-model="form.${column.javaField}"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择${column.columnComment}">
</el-date-picker>
#elseif ($column.htmlType == "textarea")
<el-input v-model="form.${column.javaField}" type="textarea" placeholder="请输入内容" />
#end
</el-form-item>
#end
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { list${table.className}, get${table.className}, del${table.className}, add${table.className}, update${table.className} } from "@/api/${table.moduleName}/${table.businessName}";
export default {
name: "${table.className}",
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// ${table.classComment}表格数据
${table.businessName}List: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
#foreach ($column in $listColumns)
#if ($column.listOperation)
${column.javaField}: null,
#end
#end
},
// 表单参数
form: {},
// 表单校验
rules: {
#foreach ($column in $createColumns)
#if (!$column.nullable && !${column.primaryKey})
${column.javaField}: [{ required: true, message: "${column.columnComment}不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }],
#end
#end
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
list${table.className}(this.queryParams).then(response => {
this.${table.businessName}List = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
#foreach ($column in $createColumns)
${column.javaField}: null,
#end
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加${table.classComment}";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const ${primaryColumn.javaField} = row.${primaryColumn.javaField}
get${table.className}(${primaryColumn.javaField}).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改${table.classComment}";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.${primaryColumn.javaField} != null) {
update${table.className}(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
add${table.className}(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ${primaryColumn.javaField}s = row.${primaryColumn.javaField};
this.$modal.confirm('是否确认删除${table.classComment}编号为"' + ${primaryColumn.javaField}s + '"的数据项?').then(function() {
return del${table.className}(${primaryColumn.javaField}s);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
this.download('${table.moduleName}/${table.businessName}/export-excel', {
...this.queryParams
}, `${table.businessName}_#{new Date().getTime()}.xls`)
}
}
};
</script>
```
## 高级配置
### 主子表生成
```java
// 主表配置
CodegenTableDO masterTable = new CodegenTableDO();
masterTable.setTemplateType(CodegenTemplateTypeEnum.MASTER_INNER.getType());
// 子表配置
CodegenTableDO subTable = new CodegenTableDO();
subTable.setMasterTableId(masterTable.getId());
subTable.setSubJoinColumnName("master_id");
subTable.setSubJoinMany(true);
subTable.setTemplateType(CodegenTemplateTypeEnum.SUB.getType());
```
### 树表生成
```java
CodegenTableDO treeTable = new CodegenTableDO();
treeTable.setTemplateType(CodegenTemplateTypeEnum.TREE.getType());
treeTable.setTreeParentColumnName("parent_id");
treeTable.setTreeNameColumnName("name");
```
### 字段高级配置
```java
// 字典类型设置
CodegenColumnDO column = new CodegenColumnDO();
column.setDictType("common_status");
// HTML 类型设置
column.setHtmlType("select"); // 下拉框
column.setHtmlType("radio"); // 单选框
column.setHtmlType("checkbox"); // 复选框
column.setHtmlType("textarea"); // 文本域
column.setHtmlType("datetime"); // 日期时间
// 查询条件设置
column.setListOperationCondition("LIKE"); // 模糊查询
column.setListOperationCondition("BETWEEN"); // 范围查询
column.setListOperationCondition("IN"); // 包含查询
```
## 自定义模板
### 模板引擎
代码生成器使用 **Velocity** 模板引擎,支持自定义模板。
### 模板变量
```velocity
## 表信息
$table.tableName ## 表名
$table.className ## 类名
$table.classComment ## 类注释
$table.moduleName ## 模块名
$table.businessName ## 业务名
## 字段信息
#foreach($column in $columns)
$column.columnName ## 字段名
$column.javaField ## Java 属性名
$column.javaType ## Java 类型
$column.columnComment ## 字段注释
$column.htmlType ## HTML 类型
#end
## 工具方法
$tool.firstLower($table.className) ## 首字母小写
$tool.firstUpper($table.businessName) ## 首字母大写
```
### 自定义模板示例
```velocity
package ${basePackage}.module.${table.moduleName}.enums;
import ${baseFrameworkPackage}.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* ${table.classComment} 枚举类
*
* @author ${author}
* @date ${dateTime}
*/
@AllArgsConstructor
@Getter
public enum ${table.className}Enum implements IntArrayValuable {
#foreach($column in $columns)
#if($column.dictType && $column.dictType != "")
// TODO: 根据字典类型 ${column.dictType} 补充枚举值
#end
#end
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(${table.className}Enum::getValue).toArray();
/**
* 值
*/
private final Integer value;
/**
* 名字
*/
private final String name;
@Override
public int[] array() {
return ARRAYS;
}
}
```
## 代码生成最佳实践
### 数据库表设计规范
1. **表名规范**: 使用模块前缀,如 `system_user`
2. **字段规范**: 包含基础字段id, create_time, update_time 等)
3. **注释完整**: 表和字段都要有详细注释
4. **类型选择**: 合理选择字段类型和长度
### 生成前配置检查
1. **模块名**: 确认模块名正确
2. **业务名**: 设置有意义的业务名
3. **类注释**: 完善类注释信息
4. **字段配置**: 检查字段的操作权限和显示类型
5. **字典类型**: 为枚举字段设置字典类型
### 生成后代码优化
1. **业务逻辑**: 添加具体的业务校验逻辑
2. **权限菜单**: 导入生成的菜单权限 SQL
3. **前端优化**: 根据业务需求调整前端页面
4. **测试验证**: 运行单元测试验证代码正确性
### 版本控制
1. **代码备份**: 生成前备份现有代码
2. **分支管理**: 在独立分支中生成代码
3. **合并策略**: 谨慎合并生成的代码到主分支
4. **冲突解决**: 手工解决代码冲突
## 扩展开发
### 自定义模板类型
```java
@Component
public class CustomCodegenTemplateLoader implements CodegenTemplateLoader {
@Override
public List<CodegenTemplateDO> getTemplateList(Integer templateType) {
if (Objects.equals(templateType, CustomTemplateTypeEnum.CUSTOM.getType())) {
return loadCustomTemplates();
}
return null;
}
private List<CodegenTemplateDO> loadCustomTemplates() {
// 加载自定义模板
return Arrays.asList(
buildTemplate("custom/controller.vm", "${table.moduleName}/${table.businessName}/controller/${table.className}Controller.java"),
buildTemplate("custom/service.vm", "${table.moduleName}/${table.businessName}/service/${table.className}Service.java")
);
}
}
```
### 自定义字段类型映射
```java
@Component
public class CustomColumnTypeMapping implements ColumnTypeMapping {
@Override
public String getJavaType(String columnType) {
if ("json".equalsIgnoreCase(columnType)) {
return "String"; // JSON 字段映射为 String
}
return null;
}
@Override
public String getHtmlType(CodegenColumnDO column) {
if ("json".equalsIgnoreCase(column.getDataType())) {
return "textarea"; // JSON 字段使用文本域
}
return null;
}
}
```
### 代码生成插件
```java
@Component
public class CodegenPlugin {
/**
* 生成前处理
*/
public void beforeGenerate(CodegenTableDO table, List<CodegenColumnDO> columns) {
// 自定义预处理逻辑
log.info("开始生成代码: {}", table.getClassName());
}
/**
* 生成后处理
*/
public void afterGenerate(CodegenTableDO table, Map<String, String> result) {
// 自定义后处理逻辑
log.info("代码生成完成: {}, 文件数: {}", table.getClassName(), result.size());
// 可以在这里执行:
// 1. 代码格式化
// 2. 文件写入磁盘
// 3. Git 提交
// 4. 发送通知
}
}
```