# 代码生成器使用指南 ## 代码生成器概述 ### 功能特性 - **前后端代码生成**: 一键生成 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 createCodegenList(Long dataSourceConfigId, List tableNames) { List 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 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 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 execute(Long tableId) { // 校验表和字段的有效性 CodegenTableDO table = codegenTableService.getCodegenTable(tableId); List columns = codegenColumnService.getCodegenColumnListByTableId(tableId); // 执行生成 return execute0(table, columns); } public Map execute0(CodegenTableDO table, List columns) { // 创建生成上下文 Map bindingMap = buildBindingMap(table, columns); // 获得生成模板 List templates = getCodegenTemplateList(table.getTemplateType()); // 执行生成 Map result = new LinkedHashMap<>(); for (CodegenTemplateDO template : templates) { String content = templateEngine.getTemplate(template.getContent()).execute(bindingMap); result.put(template.getFilePath(), content); } return result; } /** * 构建模板上下文 */ private Map buildBindingMap(CodegenTableDO table, List columns) { Map 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 createColumns = filterList(columns, CodegenColumnDO::getCreateOperation); List updateColumns = filterList(columns, CodegenColumnDO::getUpdateOperation); List listColumns = filterList(columns, CodegenColumnDO::getListOperation); List 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 buildImportPackages(CodegenTableDO table, List columns) { Set 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 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 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> 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 ``` ## 高级配置 ### 主子表生成 ```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 getTemplateList(Integer templateType) { if (Objects.equals(templateType, CustomTemplateTypeEnum.CUSTOM.getType())) { return loadCustomTemplates(); } return null; } private List 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 columns) { // 自定义预处理逻辑 log.info("开始生成代码: {}", table.getClassName()); } /** * 生成后处理 */ public void afterGenerate(CodegenTableDO table, Map result) { // 自定义后处理逻辑 log.info("代码生成完成: {}, 文件数: {}", table.getClassName(), result.size()); // 可以在这里执行: // 1. 代码格式化 // 2. 文件写入磁盘 // 3. Git 提交 // 4. 发送通知 } } ```