ruoyi-vue(十一)——代码生成

大部分项目里其实有很多代码都是重复的,几乎每个基础模块的代码都有增删改查的功能,而这些功能都是大同小异, 如果这些功能都要自己去写,将会大大浪费我们的精力降低效率。所以这种重复性的代码可以使用代码生成。

一 代码生成使用

ruoyi-generator中的resources目录下的generator.yml,可以自己根据实际情况调整默认配置。

# 代码生成
gen:# 作者author: ruoyi# 默认生成包路径 system 需改成自己的模块名称 如 system monitor toolpackageName: com.ruoyi.system# 自动去除表前缀,默认是falseautoRemovePre: false# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)tablePrefix: sys_# 是否允许生成文件覆盖到本地(自定义路径),默认不允许allowOverwrite: false

1、单表

在数据库中创建一张表

drop table if exists sys_student;
create table sys_student (student_id           int(11)         auto_increment    comment '编号',student_name         varchar(30)     default ''        comment '学生名称',student_age          int(3)          default null      comment '年龄',student_hobby        varchar(30)     default ''        comment '爱好(0代码 1音乐 2电影)',student_sex          char(1)         default '0'       comment '性别(0男 1女 2未知)',student_status       char(1)         default '0'       comment '状态(0正常 1停用)',student_birthday     datetime                          comment '生日',primary key (student_id)
) engine=innodb auto_increment=1 comment = '学生信息表';

导入该表
在这里插入图片描述
在这里插入图片描述
点击预览可查看生成的代码,点击生成代码即为下载代码文件。
在这里插入图片描述
通过编辑进行修改
在这里插入图片描述
在这里插入图片描述
提交并下载代码
在这里插入图片描述
sql文件内容,新增菜单,按钮数据

-- 菜单 SQL
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('学生信息', '3', '1', 'student', 'system/student/index', 1, 0, 'C', '0', '0', 'system:student:list', '#', 'admin', sysdate(), '', null, '学生信息菜单');-- 按钮父菜单ID
SELECT @parentId := LAST_INSERT_ID();-- 按钮 SQL
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('学生信息查询', @parentId, '1',  '#', '', 1, 0, 'F', '0', '0', 'system:student:query',        '#', 'admin', sysdate(), '', null, '');insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('学生信息新增', @parentId, '2',  '#', '', 1, 0, 'F', '0', '0', 'system:student:add',          '#', 'admin', sysdate(), '', null, '');insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('学生信息修改', @parentId, '3',  '#', '', 1, 0, 'F', '0', '0', 'system:student:edit',         '#', 'admin', sysdate(), '', null, '');insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('学生信息删除', @parentId, '4',  '#', '', 1, 0, 'F', '0', '0', 'system:student:remove',       '#', 'admin', sysdate(), '', null, '');insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('学生信息导出', @parentId, '5',  '#', '', 1, 0, 'F', '0', '0', 'system:student:export',       '#', 'admin', sysdate(), '', null, '');

将代码文件导入到项目中
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
导入前端代码
在这里插入图片描述
重启项目查看效果
在这里插入图片描述
因为在插入菜单数据时将学生信息的parent_id插入的值为3(系统工具),所以就在系统工具菜单下

2、树表

新建表树表

drop table if exists sys_product;
create table sys_product (product_id        bigint(20)      not null auto_increment    comment '产品id',parent_id         bigint(20)      default 0                  comment '父产品id',product_name      varchar(30)     default ''                 comment '产品名称',order_num         int(4)          default 0                  comment '显示顺序',status            char(1)         default '0'                comment '产品状态(0正常 1停用)',primary key (product_id)
) engine=innodb auto_increment=1 comment = '产品表';

其它步骤一样,在修改配置这里多了一个其他信息
在这里插入图片描述

提交,生成代码并导入到项目中,重启项目查看效果
在这里插入图片描述

3、主子表

新建主子表

-- ----------------------------
-- 客户表
-- ----------------------------
drop table if exists sys_customer;
create table sys_customer (customer_id           bigint(20)      not null auto_increment    comment '客户id',customer_name         varchar(30)     default ''                 comment '客户姓名',phonenumber           varchar(11)     default ''                 comment '手机号码',sex                   varchar(20)     default null               comment '客户性别',birthday              datetime                                   comment '客户生日',remark                varchar(500)    default null               comment '客户描述',primary key (customer_id)
) engine=innodb auto_increment=1 comment = '客户表';-- ----------------------------
-- 商品表
-- ----------------------------
drop table if exists sys_goods;
create table sys_goods (goods_id           bigint(20)      not null auto_increment    comment '商品id',customer_id        bigint(20)      not null                   comment '客户id',name               varchar(30)     default ''                 comment '商品名称',weight             int(5)          default null               comment '商品重量',price              decimal(6,2)    default null               comment '商品价格',date               datetime                                   comment '商品时间',type               char(1)         default null               comment '商品种类',primary key (goods_id)
) engine=innodb auto_increment=1 comment = '商品表';

导入客户表和商品表
在这里插入图片描述

编辑客户表,这里以客户表为主表配置信息
在这里插入图片描述

下载客户表代码(客户表生成的代码中包含商品表domain),导入项目。重启后查看效果
在这里插入图片描述
在这里插入图片描述

二 代码生成实现详解

从数据表sys_menu中可以看到代码生成tool/gen/index中
在这里插入图片描述
前端
代码生成页面入口

<template><div class="app-container"><el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"><el-form-item label="表名称" prop="tableName"><el-inputv-model="queryParams.tableName"placeholder="请输入表名称"clearablestyle="width: 200px"@keyup.enter="handleQuery"/></el-form-item><el-form-item label="表描述" prop="tableComment"><el-inputv-model="queryParams.tableComment"placeholder="请输入表描述"clearablestyle="width: 200px"@keyup.enter="handleQuery"/></el-form-item><el-form-item label="创建时间" style="width: 308px"><el-date-pickerv-model="dateRange"value-format="YYYY-MM-DD"type="daterange"range-separator="-"start-placeholder="开始日期"end-placeholder="结束日期"></el-date-picker></el-form-item><el-form-item><el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button><el-button icon="Refresh" @click="resetQuery">重置</el-button></el-form-item></el-form><el-row :gutter="10" class="mb8"><el-col :span="1.5"><el-buttontype="primary"plainicon="Download":disabled="multiple"@click="handleGenTable"v-hasPermi="['tool:gen:code']">生成</el-button></el-col><el-col :span="1.5"><el-buttontype="primary"plainicon="Plus"@click="openCreateTable"v-hasRole="['admin']">创建</el-button></el-col><el-col :span="1.5"><el-buttontype="info"plainicon="Upload"@click="openImportTable"v-hasPermi="['tool:gen:import']">导入</el-button></el-col><el-col :span="1.5"><el-buttontype="success"plainicon="Edit":disabled="single"@click="handleEditTable"v-hasPermi="['tool:gen:edit']">修改</el-button></el-col><el-col :span="1.5"><el-buttontype="danger"plainicon="Delete":disabled="multiple"@click="handleDelete"v-hasPermi="['tool:gen:remove']">删除</el-button></el-col><!-- 查询生成表数据 --><right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar></el-row><el-table ref="genRef" v-loading="loading" :data="tableList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange"><el-table-column type="selection" align="center" width="55"></el-table-column><el-table-column label="序号" type="index" width="50" align="center"><template #default="scope"><span>{{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}</span></template></el-table-column><el-table-column label="表名称" align="center" prop="tableName" :show-overflow-tooltip="true" /><el-table-column label="表描述" align="center" prop="tableComment" :show-overflow-tooltip="true" /><el-table-column label="实体" align="center" prop="className" :show-overflow-tooltip="true" /><el-table-column label="创建时间" align="center" prop="createTime" width="160" sortable="custom" :sort-orders="['descending', 'ascending']" /><el-table-column label="更新时间" align="center" prop="updateTime" width="160" sortable="custom" :sort-orders="['descending', 'ascending']" /><el-table-column label="操作" align="center" width="330" class-name="small-padding fixed-width"><template #default="scope"><el-tooltip content="预览" placement="top"><el-button link type="primary" icon="View" @click="handlePreview(scope.row)" v-hasPermi="['tool:gen:preview']"></el-button></el-tooltip><el-tooltip content="编辑" placement="top"><el-button link type="primary" icon="Edit" @click="handleEditTable(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button></el-tooltip><el-tooltip content="删除" placement="top"><el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['tool:gen:remove']"></el-button></el-tooltip><el-tooltip content="同步" placement="top"><el-button link type="primary" icon="Refresh" @click="handleSynchDb(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button></el-tooltip><el-tooltip content="生成代码" placement="top"><el-button link type="primary" icon="Download" @click="handleGenTable(scope.row)" v-hasPermi="['tool:gen:code']"></el-button></el-tooltip></template></el-table-column></el-table><paginationv-show="total>0":total="total"v-model:page="queryParams.pageNum"v-model:limit="queryParams.pageSize"@pagination="getList"/><!-- 预览界面 --><el-dialog :title="preview.title" v-model="preview.open" width="80%" top="5vh" append-to-body class="scrollbar"><el-tabs v-model="preview.activeName"><el-tab-panev-for="(value, key) in preview.data":label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))":name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))":key="value"><el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right">&nbsp;复制</el-link><pre>{{ value }}</pre></el-tab-pane></el-tabs></el-dialog><import-table ref="importRef" @ok="handleQuery" /><create-table ref="createRef" @ok="handleQuery" /></div>
</template><script setup name="Gen">
import { listTable, previewTable, delTable, genCode, synchDb } from "@/api/tool/gen"
import router from "@/router"
import importTable from "./importTable"
import createTable from "./createTable"const route = useRoute()
const { proxy } = getCurrentInstance()const tableList = ref([])
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const tableNames = ref([])
const dateRange = ref([])
const uniqueId = ref("")
const defaultSort = ref({ prop: "createTime", order: "descending" })const data = reactive({queryParams: {pageNum: 1,pageSize: 10,tableName: undefined,tableComment: undefined,orderByColumn: defaultSort.value.prop,isAsc: defaultSort.value.order},preview: {open: false,title: "代码预览",data: {},activeName: "domain.java"}
})const { queryParams, preview } = toRefs(data)onActivated(() => {const time = route.query.tif (time != null && time != uniqueId.value) {uniqueId.value = timequeryParams.value.pageNum = Number(route.query.pageNum)dateRange.value = []proxy.resetForm("queryForm")getList()}
})/** 查询表集合 */
function getList() {loading.value = truelistTable(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {tableList.value = response.rowstotal.value = response.totalloading.value = false})
}/** 搜索按钮操作 */
function handleQuery() {queryParams.value.pageNum = 1getList()
}/** 生成代码操作 */
function handleGenTable(row) {const tbNames = row.tableName || tableNames.valueif (tbNames == "") {proxy.$modal.msgError("请选择要生成的数据")return}if (row.genType === "1") {genCode(row.tableName).then(response => {proxy.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath)})} else {proxy.$download.zip("/tool/gen/batchGenCode?tables=" + tbNames, "ruoyi.zip")}
}/** 同步数据库操作 */
function handleSynchDb(row) {const tableName = row.tableNameproxy.$modal.confirm('确认要强制同步"' + tableName + '"表结构吗?').then(function () {return synchDb(tableName)}).then(() => {proxy.$modal.msgSuccess("同步成功")}).catch(() => {})
}/** 打开导入表弹窗 */
function openImportTable() {proxy.$refs["importRef"].show()
}/** 打开创建表弹窗 */
function openCreateTable() {proxy.$refs["createRef"].show()
}/** 重置按钮操作 */
function resetQuery() {dateRange.value = []proxy.resetForm("queryRef")queryParams.value.pageNum = 1proxy.$refs["genRef"].sort(defaultSort.value.prop, defaultSort.value.order)
}/** 预览按钮 */
function handlePreview(row) {previewTable(row.tableId).then(response => {preview.value.data = response.datapreview.value.open = truepreview.value.activeName = "domain.java"})
}/** 复制代码成功 */
function copyTextSuccess() {proxy.$modal.msgSuccess("复制成功")
}// 多选框选中数据
function handleSelectionChange(selection) {ids.value = selection.map(item => item.tableId)tableNames.value = selection.map(item => item.tableName)single.value = selection.length != 1multiple.value = !selection.length
}/** 排序触发事件 */
function handleSortChange(column, prop, order) {queryParams.value.orderByColumn = column.propqueryParams.value.isAsc = column.ordergetList()
}/** 修改按钮操作 */
function handleEditTable(row) {const tableId = row.tableId || ids.value[0]const tableName = row.tableName || tableNames.value[0]const params = { pageNum: queryParams.value.pageNum }proxy.$tab.openPage("修改[" + tableName + "]生成配置", '/tool/gen-edit/index/' + tableId, params)
}/** 删除按钮操作 */
function handleDelete(row) {const tableIds = row.tableId || ids.valueproxy.$modal.confirm('是否确认删除表编号为"' + tableIds + '"的数据项?').then(function () {return delTable(tableIds)}).then(() => {getList()proxy.$modal.msgSuccess("删除成功")}).catch(() => {})
}getList()
</script>

模板部分
1.搜索表单区域

<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
  • 表名称和表描述的输入框,支持回车搜索
  • 时间范围选择器,用于筛选创建时间
  • 搜索和重置按钮

2.操作按钮区域

<el-row :gutter="10" class="mb8">
  • 生成代码按钮(需要选中表)
  • 创建表按钮(仅管理员)
  • 导入表按钮
  • 修改表按钮(需要选中一个表)
  • 删除表按钮(需要选中表)
  • 显示/隐藏搜索区域的工具栏

3.数据表

<el-table ref="genRef" v-loading="loading" :data="tableList" ...>
  • 显示表信息:序号、表名、表描述、实体类名、创建时间、更新时间
  • 支持多选、排序
  • 每行操作包括:预览、编辑、删除、同步、生成代码

4.代码预览对话框

<el-dialog :title="preview.title" v-model="preview.open" ...>
  • 以标签页形式展示生成的各类代码文件
  • 支持复制代码功能

5.子组件

<import-table ref="importRef" @ok="handleQuery" />
<create-table ref="createRef" @ok="handleQuery" />
  • 导入表和创建表的弹窗组件

脚本部分
1.导入和初始化

import { listTable, previewTable, delTable, genCode, synchDb } from "@/api/tool/gen"
  • 导入相关API接口
  • 导入子组件

2.响应式数据定义

const tableList = ref([])          // 表数据列表
const loading = ref(true)          // 加载状态
const showSearch = ref(true)       // 是否显示搜索区域
const ids = ref([])                // 选中项ID列表
const single = ref(true)           // 是否只选中一项
const multiple = ref(true)         // 是否选中多项
const total = ref(0)               // 数据总数
const dateRange = ref([])          // 时间范围
const defaultSort = ref({ prop: "createTime", order: "descending" }) // 默认排序

3.查询参数和预览数据

const data = reactive({queryParams: {                   // 查询参数pageNum: 1,pageSize: 10,tableName: undefined,tableComment: undefined,orderByColumn: defaultSort.value.prop,isAsc: defaultSort.value.order},preview: {                      // 预览相关数据open: false,title: "代码预览",data: {},activeName: "domain.java"}
})

4.主要功能函数

查询相关:
getList(): 获取表列表数据
handleQuery(): 处理搜索操作
resetQuery(): 重置查询条件
handleSortChange(): 处理排序变化
表操作:
handleGenTable(): 生成代码
handleSynchDb(): 同步数据库表结构
handleEditTable(): 编辑表配置
handleDelete(): 删除表
openImportTable(): 打开导入表弹窗
openCreateTable(): 打开创建表弹窗
其他功能:
handlePreview(): 预览生成的代码
copyTextSuccess(): 复制代码成功提示
handleSelectionChange(): 处理表格选择变化

5.声明周期钩子

onActivated(() => {// 组件激活时的处理逻辑
})

6.权限控制
使用了自定义指令进行权限控制:

  • v-hasPermi: 检查用户是否有特定权限
  • v-hasRole: 检查用户角色

后端
我们直接看ruoyi-generator模块com.ruoyi.generator.controller下的GenController

package com.ruoyi.generator.controller;import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
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 com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.sql.SqlUtil;
import com.ruoyi.generator.config.GenConfig;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import com.ruoyi.generator.service.IGenTableColumnService;
import com.ruoyi.generator.service.IGenTableService;/*** 代码生成 操作处理* * @author ruoyi*/
@RestController
@RequestMapping("/tool/gen")
public class GenController extends BaseController
{@Autowiredprivate IGenTableService genTableService;@Autowiredprivate IGenTableColumnService genTableColumnService;/*** 查询代码生成列表*/@PreAuthorize("@ss.hasPermi('tool:gen:list')")@GetMapping("/list")public TableDataInfo genList(GenTable genTable){// 设置分页startPage();// 查询代码生成列表并返回List<GenTable> list = genTableService.selectGenTableList(genTable);return getDataTable(list);}/*** 获取代码生成信息*/@PreAuthorize("@ss.hasPermi('tool:gen:query')")@GetMapping(value = "/{tableId}")// 将 URL 路径中 {tableId} 占位符的值提取出来,自动转换为 Long 类型,绑定到方法参数 tableId 上,比如请求 /tool/gen/123,tableId 参数值就是 123。public AjaxResult getInfo(@PathVariable Long tableId){// 通过table_id 关联表gen_table与表gen_table_column查询代码生成信息GenTable table = genTableService.selectGenTableById(tableId);List<GenTable> tables = genTableService.selectGenTableAll();List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(tableId);Map<String, Object> map = new HashMap<String, Object>();map.put("info", table);map.put("rows", list);map.put("tables", tables);return success(map);}/*** 查询数据库列表*/@PreAuthorize("@ss.hasPermi('tool:gen:list')")@GetMapping("/db/list")public TableDataInfo dataList(GenTable genTable){startPage();// 查询数据库里所有表信息List<GenTable> list = genTableService.selectDbTableList(genTable);return getDataTable(list);}/*** 查询数据表字段列表*/@PreAuthorize("@ss.hasPermi('tool:gen:list')")@GetMapping(value = "/column/{tableId}")public TableDataInfo columnList(Long tableId){TableDataInfo dataInfo = new TableDataInfo();// 从表gen_table_column中查询指定表的所有字段List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(tableId);dataInfo.setRows(list);dataInfo.setTotal(list.size());return dataInfo;}/*** 导入表结构(保存)*/@PreAuthorize("@ss.hasPermi('tool:gen:import')")@Log(title = "代码生成", businessType = BusinessType.IMPORT)@PostMapping("/importTable")public AjaxResult importTableSave(String tables){String[] tableNames = Convert.toStrArray(tables);// 查询数据库中所有业务表信息List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);// 导入表结构genTableService.importGenTable(tableList, SecurityUtils.getUsername());return success();}/*** 创建表结构(保存)*/@PreAuthorize("@ss.hasRole('admin')")@Log(title = "创建表", businessType = BusinessType.OTHER)@PostMapping("/createTable")public AjaxResult createTableSave(String sql){try{// 防止sql注入SqlUtil.filterKeyword(sql);// 使用阿里巴巴的Druid库SQLUtils.parseStatements(sql, DbType.mysql)将SQL语句解析为SQLStatement对象列表List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql);List<String> tableNames = new ArrayList<>();// 遍历sqlStatementsfor (SQLStatement sqlStatement : sqlStatements){// 如果sqlStatement是MySqlCreateTableStatement类型 mysql建表语句类型if (sqlStatement instanceof MySqlCreateTableStatement){// 类型转换MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement;// 如果建表成功if (genTableService.createTable(createTableStatement.toString())){// 获取建表成功的表名String tableName = createTableStatement.getTableName().replaceAll("`", "");// add进tableNames中tableNames.add(tableName);}}}// 查询出tableNames中所有表信息List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames.toArray(new String[tableNames.size()]));// 获取操作人名称String operName = SecurityUtils.getUsername();// 导入tableNames中所有表genTableService.importGenTable(tableList, operName);return AjaxResult.success();}catch (Exception e){logger.error(e.getMessage(), e);return AjaxResult.error("创建表结构异常");}}/*** 修改保存代码生成业务*/@PreAuthorize("@ss.hasPermi('tool:gen:edit')")@Log(title = "代码生成", businessType = BusinessType.UPDATE)@PutMappingpublic AjaxResult editSave(@Validated @RequestBody GenTable genTable){// 参数校验genTableService.validateEdit(genTable);// 修改业务genTableService.updateGenTable(genTable);return success();}/*** 删除代码生成*/@PreAuthorize("@ss.hasPermi('tool:gen:remove')")@Log(title = "代码生成", businessType = BusinessType.DELETE)@DeleteMapping("/{tableIds}")public AjaxResult remove(@PathVariable Long[] tableIds){genTableService.deleteGenTableByIds(tableIds);return success();}/*** 预览代码*/@PreAuthorize("@ss.hasPermi('tool:gen:preview')")@GetMapping("/preview/{tableId}")public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException{Map<String, String> dataMap = genTableService.previewCode(tableId);return success(dataMap);}/*** 生成代码(下载方式)*/@PreAuthorize("@ss.hasPermi('tool:gen:code')")@Log(title = "代码生成", businessType = BusinessType.GENCODE)@GetMapping("/download/{tableName}")public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException{byte[] data = genTableService.downloadCode(tableName);genCode(response, data);}/*** 生成代码(自定义路径)*/@PreAuthorize("@ss.hasPermi('tool:gen:code')")@Log(title = "代码生成", businessType = BusinessType.GENCODE)@GetMapping("/genCode/{tableName}")public AjaxResult genCode(@PathVariable("tableName") String tableName){if (!GenConfig.isAllowOverwrite()){return AjaxResult.error("【系统预设】不允许生成文件覆盖到本地");}genTableService.generatorCode(tableName);return success();}/*** 同步数据库*/@PreAuthorize("@ss.hasPermi('tool:gen:edit')")@Log(title = "代码生成", businessType = BusinessType.UPDATE)@GetMapping("/synchDb/{tableName}")public AjaxResult synchDb(@PathVariable("tableName") String tableName){genTableService.synchDb(tableName);return success();}/*** 批量生成代码*/@PreAuthorize("@ss.hasPermi('tool:gen:code')")@Log(title = "代码生成", businessType = BusinessType.GENCODE)@GetMapping("/batchGenCode")public void batchGenCode(HttpServletResponse response, String tables) throws IOException{String[] tableNames = Convert.toStrArray(tables);byte[] data = genTableService.downloadCode(tableNames);genCode(response, data);}/*** 生成zip文件*/private void genCode(HttpServletResponse response, byte[] data) throws IOException{response.reset();response.addHeader("Access-Control-Allow-Origin", "*");response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\"");response.addHeader("Content-Length", "" + data.length);response.setContentType("application/octet-stream; charset=UTF-8");IOUtils.write(data, response.getOutputStream());}
}

再来看实现类

package com.ruoyi.generator.service;import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.core.text.CharsetKit;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import com.ruoyi.generator.mapper.GenTableColumnMapper;
import com.ruoyi.generator.mapper.GenTableMapper;
import com.ruoyi.generator.util.GenUtils;
import com.ruoyi.generator.util.VelocityInitializer;
import com.ruoyi.generator.util.VelocityUtils;/*** 业务 服务层实现* * @author ruoyi*/
@Service
public class GenTableServiceImpl implements IGenTableService
{private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class);@Autowiredprivate GenTableMapper genTableMapper;@Autowiredprivate GenTableColumnMapper genTableColumnMapper;/*** 查询业务信息* * @param id 业务ID* @return 业务信息*/@Overridepublic GenTable selectGenTableById(Long id){GenTable genTable = genTableMapper.selectGenTableById(id);setTableFromOptions(genTable);return genTable;}/*** 查询业务列表* * @param genTable 业务信息* @return 业务集合*/@Overridepublic List<GenTable> selectGenTableList(GenTable genTable){return genTableMapper.selectGenTableList(genTable);}/*** 查询据库列表* * @param genTable 业务信息* @return 数据库表集合*/@Overridepublic List<GenTable> selectDbTableList(GenTable genTable){return genTableMapper.selectDbTableList(genTable);}/*** 查询据库列表* * @param tableNames 表名称组* @return 数据库表集合*/@Overridepublic List<GenTable> selectDbTableListByNames(String[] tableNames){return genTableMapper.selectDbTableListByNames(tableNames);}/*** 查询所有表信息* * @return 表信息集合*/@Overridepublic List<GenTable> selectGenTableAll(){return genTableMapper.selectGenTableAll();}/*** 修改业务* * @param genTable 业务信息* @return 结果*/@Override@Transactionalpublic void updateGenTable(GenTable genTable){String options = JSON.toJSONString(genTable.getParams());genTable.setOptions(options);// 更新gen_table表信息int row = genTableMapper.updateGenTable(genTable);if (row > 0){// 更新gen_table_column中修改表的列信息for (GenTableColumn genTableColumn : genTable.getColumns()){genTableColumnMapper.updateGenTableColumn(genTableColumn);}}}/*** 删除业务对象* * @param tableIds 需要删除的数据ID* @return 结果*/@Override@Transactionalpublic void deleteGenTableByIds(Long[] tableIds){// 从gen_table表中删除表信息genTableMapper.deleteGenTableByIds(tableIds);// 从gen_table_column中删除表列信息genTableColumnMapper.deleteGenTableColumnByIds(tableIds);}/*** 创建表** @param sql 创建表语句* @return 结果*/@Overridepublic boolean createTable(String sql){return genTableMapper.createTable(sql) == 0;}/*** 导入表结构* * @param tableList 导入表列表*/@Override@Transactionalpublic void importGenTable(List<GenTable> tableList, String operName){try{// 遍历导入表列表for (GenTable table : tableList){// 获取表名String tableName = table.getTableName();// 初始化表信息GenUtils.initTable(table, operName);// 将初始化信息插入到表gen_table中int row = genTableMapper.insertGenTable(table);if (row > 0){// 保存列信息  查询表的列信息并插入到表gen_table_column中List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);for (GenTableColumn column : genTableColumns){// 初始化列属性字段GenUtils.initColumnField(column, table);// 插入表到gen_table_column中genTableColumnMapper.insertGenTableColumn(column);}}}}catch (Exception e){throw new ServiceException("导入失败:" + e.getMessage());}}/*** 预览代码* * @param tableId 表编号* @return 预览数据列表*/@Overridepublic Map<String, String> previewCode(Long tableId){Map<String, String> dataMap = new LinkedHashMap<>();// 查询表信息GenTable table = genTableMapper.selectGenTableById(tableId);// 设置主子表信息setSubTable(table);// 设置主键列信息setPkColumn(table);// 初始化Velocity模板引擎VelocityInitializer.initVelocity();// 准备模板上下文数据VelocityContext context = VelocityUtils.prepareContext(table);// 获取模板列表List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType());// 将生成的代码以模板路径为key、代码内容为value存入Map返for (String template : templates){// 使用Velocity模板引擎生成代码// 渲染模板StringWriter sw = new StringWriter();// Velocity引擎加载指定的模板文件,使用UTF-8编码Template tpl = Velocity.getTemplate(template, Constants.UTF8);// 将上下文数据(context)与模板(tpl)合并,渲染生成实际代码内容tpl.merge(context, sw);// 将生成的代码内容转换为字符串,并以模板路径为key存入dataMap中dataMap.put(template, sw.toString());}return dataMap;}/*** 生成代码(下载方式)* * @param tableName 表名称* @return 数据*/@Overridepublic byte[] downloadCode(String tableName){ByteArrayOutputStream outputStream = new ByteArrayOutputStream();ZipOutputStream zip = new ZipOutputStream(outputStream);generatorCode(tableName, zip);IOUtils.closeQuietly(zip);return outputStream.toByteArray();}/*** 生成代码(自定义路径)* * @param tableName 表名称*/@Overridepublic void generatorCode(String tableName){// 查询表信息GenTable table = genTableMapper.selectGenTableByName(tableName);// 设置主子表信息setSubTable(table);// 设置主键列信息setPkColumn(table);VelocityInitializer.initVelocity();VelocityContext context = VelocityUtils.prepareContext(table);// 获取模板列表List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType());for (String template : templates){if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")){// 渲染模板StringWriter sw = new StringWriter();Template tpl = Velocity.getTemplate(template, Constants.UTF8);tpl.merge(context, sw);try{String path = getGenPath(table, template);// 生成代码文件到指定路径FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8);}catch (IOException e){throw new ServiceException("渲染模板失败,表名:" + table.getTableName());}}}}/*** 同步数据库* 查询当前表信息和数据库实际表结构* 对比两者的列信息,更新已存在的列配置* 保留用户自定义的查询方式、字典类型、必填等配置* 插入新增的列信息* 删除已不存在的列信息* 用于保持代码生成器配置与实际数据库表结构一致。* @param tableName 表名称*/@Override@Transactionalpublic void synchDb(String tableName){GenTable table = genTableMapper.selectGenTableByName(tableName);List<GenTableColumn> tableColumns = table.getColumns();Map<String, GenTableColumn> tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity()));List<GenTableColumn> dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);if (StringUtils.isEmpty(dbTableColumns)){throw new ServiceException("同步数据失败,原表结构不存在");}List<String> dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList());dbTableColumns.forEach(column -> {GenUtils.initColumnField(column, table);if (tableColumnMap.containsKey(column.getColumnName())){GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName());column.setColumnId(prevColumn.getColumnId());if (column.isList()){// 如果是列表,继续保留查询方式/字典类型选项column.setDictType(prevColumn.getDictType());column.setQueryType(prevColumn.getQueryType());}if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk()&& (column.isInsert() || column.isEdit())&& ((column.isUsableColumn()) || (!column.isSuperColumn()))){// 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项column.setIsRequired(prevColumn.getIsRequired());column.setHtmlType(prevColumn.getHtmlType());}genTableColumnMapper.updateGenTableColumn(column);}else{genTableColumnMapper.insertGenTableColumn(column);}});List<GenTableColumn> delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList());if (StringUtils.isNotEmpty(delColumns)){genTableColumnMapper.deleteGenTableColumns(delColumns);}}/*** 批量生成代码(下载方式)* * @param tableNames 表数组* @return 数据*/@Overridepublic byte[] downloadCode(String[] tableNames){// 创建ByteArrayOutputStream和ZipOutputStream用于生成压缩包ByteArrayOutputStream outputStream = new ByteArrayOutputStream();// 遍历表名数组,为每个表调用generatorCode方法生成代码并写入zipZipOutputStream zip = new ZipOutputStream(outputStream);for (String tableName : tableNames){generatorCode(tableName, zip);}// 关闭zip流并返回压缩包的字节数组IOUtils.closeQuietly(zip);return outputStream.toByteArray();}/*** 查询表信息并生成代码*/private void generatorCode(String tableName, ZipOutputStream zip){// 查询表信息GenTable table = genTableMapper.selectGenTableByName(tableName);// 设置主子表信息setSubTable(table);// 设置主键列信息setPkColumn(table);// 初始化 Velocity模板VelocityInitializer.initVelocity();VelocityContext context = VelocityUtils.prepareContext(table);// 获取模板列表List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType());for (String template : templates){// 渲染模板StringWriter sw = new StringWriter();Template tpl = Velocity.getTemplate(template, Constants.UTF8);tpl.merge(context, sw);try{// 添加到zipzip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));IOUtils.write(sw.toString(), zip, Constants.UTF8);IOUtils.closeQuietly(sw);zip.flush();zip.closeEntry();}catch (IOException e){log.error("渲染模板失败,表名:" + table.getTableName(), e);}}}/*** 修改保存参数校验* * @param genTable 业务信息*/@Overridepublic void validateEdit(GenTable genTable){// 当模板类型为树表(TPL_TREE)时,校验树编码、树父编码、树名称字段是否为空if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())){String options = JSON.toJSONString(genTable.getParams());JSONObject paramsObj = JSON.parseObject(options);if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))){throw new ServiceException("树编码字段不能为空");}else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))){throw new ServiceException("树父编码字段不能为空");}else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))){throw new ServiceException("树名称字段不能为空");}}// 当模板类型为子表关联(TPL_SUB)时,校验关联子表名和外键名是否为空else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())){if (StringUtils.isEmpty(genTable.getSubTableName())){throw new ServiceException("关联子表的表名不能为空");}else if (StringUtils.isEmpty(genTable.getSubTableFkName())){throw new ServiceException("子表关联的外键名不能为空");}}}/*** 设置主键列信息* * @param table 业务表信息*/public void setPkColumn(GenTable table){for (GenTableColumn column : table.getColumns()){if (column.isPk()){table.setPkColumn(column);break;}}if (StringUtils.isNull(table.getPkColumn())){table.setPkColumn(table.getColumns().get(0));}if (GenConstants.TPL_SUB.equals(table.getTplCategory())){for (GenTableColumn column : table.getSubTable().getColumns()){if (column.isPk()){table.getSubTable().setPkColumn(column);break;}}if (StringUtils.isNull(table.getSubTable().getPkColumn())){table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0));}}}/*** 设置主子表信息* * @param table 业务表信息*/public void setSubTable(GenTable table){String subTableName = table.getSubTableName();if (StringUtils.isNotEmpty(subTableName)){table.setSubTable(genTableMapper.selectGenTableByName(subTableName));}}/*** 设置代码生成其他选项值* * @param genTable 设置后的生成对象*/public void setTableFromOptions(GenTable genTable){JSONObject paramsObj = JSON.parseObject(genTable.getOptions());if (StringUtils.isNotNull(paramsObj)){String treeCode = paramsObj.getString(GenConstants.TREE_CODE);String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE);String treeName = paramsObj.getString(GenConstants.TREE_NAME);Long parentMenuId = paramsObj.getLongValue(GenConstants.PARENT_MENU_ID);String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME);genTable.setTreeCode(treeCode);genTable.setTreeParentCode(treeParentCode);genTable.setTreeName(treeName);genTable.setParentMenuId(parentMenuId);genTable.setParentMenuName(parentMenuName);}}/*** 获取代码生成地址* * @param table 业务表信息* @param template 模板文件路径* @return 生成地址*/public static String getGenPath(GenTable table, String template){String genPath = table.getGenPath();if (StringUtils.equals(genPath, "/")){return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table);}return genPath + File.separator + VelocityUtils.getFileName(template, table);}
}

代码生成器工具类

package com.ruoyi.generator.util;import java.util.Arrays;
import org.apache.commons.lang3.RegExUtils;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.generator.config.GenConfig;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;/*** 代码生成器 工具类* * @author ruoyi*/
public class GenUtils
{/*** 初始化表信息*/public static void initTable(GenTable genTable, String operName){// 将表名转换为类名genTable.setClassName(convertClassName(genTable.getTableName()));// set默认信息genTable.setPackageName(GenConfig.getPackageName());genTable.setModuleName(getModuleName(GenConfig.getPackageName()));genTable.setBusinessName(getBusinessName(genTable.getTableName()));genTable.setFunctionName(replaceText(genTable.getTableComment()));genTable.setFunctionAuthor(GenConfig.getAuthor());genTable.setCreateBy(operName);}/*** 初始化列属性字段*/public static void initColumnField(GenTableColumn column, GenTable table){String dataType = getDbType(column.getColumnType());String columnName = column.getColumnName();column.setTableId(table.getTableId());column.setCreateBy(table.getCreateBy());// 设置java字段名column.setJavaField(StringUtils.toCamelCase(columnName));// 设置默认类型column.setJavaType(GenConstants.TYPE_STRING);column.setQueryType(GenConstants.QUERY_EQ);if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)){// 字符串长度超过500设置为文本域Integer columnLength = getColumnLength(column.getColumnType());String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;column.setHtmlType(htmlType);}else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)){column.setJavaType(GenConstants.TYPE_DATE);column.setHtmlType(GenConstants.HTML_DATETIME);}else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)){column.setHtmlType(GenConstants.HTML_INPUT);// 如果是浮点型 统一用BigDecimalString[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0){column.setJavaType(GenConstants.TYPE_BIGDECIMAL);}// 如果是整形else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10){column.setJavaType(GenConstants.TYPE_INTEGER);}// 长整形else{column.setJavaType(GenConstants.TYPE_LONG);}}// 插入字段(默认所有字段都需要插入)column.setIsInsert(GenConstants.REQUIRE);// 编辑字段if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()){column.setIsEdit(GenConstants.REQUIRE);}// 列表字段if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()){column.setIsList(GenConstants.REQUIRE);}// 查询字段if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()){column.setIsQuery(GenConstants.REQUIRE);}// 查询字段类型if (StringUtils.endsWithIgnoreCase(columnName, "name")){column.setQueryType(GenConstants.QUERY_LIKE);}// 状态字段设置单选框if (StringUtils.endsWithIgnoreCase(columnName, "status")){column.setHtmlType(GenConstants.HTML_RADIO);}// 类型&性别字段设置下拉框else if (StringUtils.endsWithIgnoreCase(columnName, "type")|| StringUtils.endsWithIgnoreCase(columnName, "sex")){column.setHtmlType(GenConstants.HTML_SELECT);}// 图片字段设置图片上传控件else if (StringUtils.endsWithIgnoreCase(columnName, "image")){column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD);}// 文件字段设置文件上传控件else if (StringUtils.endsWithIgnoreCase(columnName, "file")){column.setHtmlType(GenConstants.HTML_FILE_UPLOAD);}// 内容字段设置富文本控件else if (StringUtils.endsWithIgnoreCase(columnName, "content")){column.setHtmlType(GenConstants.HTML_EDITOR);}}/*** 校验数组是否包含指定值* * @param arr 数组* @param targetValue 值* @return 是否包含*/public static boolean arraysContains(String[] arr, String targetValue){return Arrays.asList(arr).contains(targetValue);}/*** 获取模块名* * @param packageName 包名* @return 模块名*/public static String getModuleName(String packageName){int lastIndex = packageName.lastIndexOf(".");int nameLength = packageName.length();return StringUtils.substring(packageName, lastIndex + 1, nameLength);}/*** 获取业务名* * @param tableName 表名* @return 业务名*/public static String getBusinessName(String tableName){int lastIndex = tableName.lastIndexOf("_");int nameLength = tableName.length();return StringUtils.substring(tableName, lastIndex + 1, nameLength);}/*** 表名转换成Java类名* * @param tableName 表名称* @return 类名*/public static String convertClassName(String tableName){boolean autoRemovePre = GenConfig.getAutoRemovePre();String tablePrefix = GenConfig.getTablePrefix();if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)){String[] searchList = StringUtils.split(tablePrefix, ",");tableName = replaceFirst(tableName, searchList);}return StringUtils.convertToCamelCase(tableName);}/*** 批量替换前缀* * @param replacementm 替换值* @param searchList 替换列表* @return*/public static String replaceFirst(String replacementm, String[] searchList){String text = replacementm;for (String searchString : searchList){if (replacementm.startsWith(searchString)){text = replacementm.replaceFirst(searchString, "");break;}}return text;}/*** 关键字替换* * @param text 需要被替换的名字* @return 替换后的名字*/public static String replaceText(String text){return RegExUtils.replaceAll(text, "(?:表|若依)", "");}/*** 获取数据库类型字段* * @param columnType 列类型* @return 截取后的列类型*/public static String getDbType(String columnType){if (StringUtils.indexOf(columnType, "(") > 0){return StringUtils.substringBefore(columnType, "(");}else{return columnType;}}/*** 获取字段长度* * @param columnType 列类型* @return 截取后的列类型*/public static Integer getColumnLength(String columnType){if (StringUtils.indexOf(columnType, "(") > 0){String length = StringUtils.substringBetween(columnType, "(", ")");return Integer.valueOf(length);}else{return 0;}}
}

VelocityEngine工厂
Velocity模板引擎的初始化工具类

package com.ruoyi.generator.util;import java.util.Properties;
import org.apache.velocity.app.Velocity;
import com.ruoyi.common.constant.Constants;/*** VelocityEngine工厂* * @author ruoyi*/
public class VelocityInitializer
{/*** 初始化vm方法*/public static void initVelocity(){Properties p = new Properties();try{// 加载classpath目录下的vm文件p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");// 定义字符集p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8);// 初始化Velocity引擎,指定配置PropertiesVelocity.init(p);}catch (Exception e){throw new RuntimeException(e);}}
}

Velocity模板处理工具类

package com.ruoyi.generator.util;import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.velocity.VelocityContext;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;/*** 模板处理工具类* * @author ruoyi*/
public class VelocityUtils
{/** 项目空间路径 */private static final String PROJECT_PATH = "main/java";/** mybatis空间路径 */private static final String MYBATIS_PATH = "main/resources/mapper";/** 默认上级菜单,系统工具 */private static final String DEFAULT_PARENT_MENU_ID = "3";/*** 设置模板变量信息* 用于准备Velocity模板的上下文变量:* 从GenTable对象中提取模块名、业务名、包名等信息* 创建VelocityContext并设置各种模板变量,包括表名、类名、包名、作者、日期等* 根据模板类型(树形或子表)添加相应的特殊变量* 返回包含所有上下文信息的VelocityContext对象* 为代码生成模板提供所需的数据变量。** @return 模板列表*/public static VelocityContext prepareContext(GenTable genTable){String moduleName = genTable.getModuleName();String businessName = genTable.getBusinessName();String packageName = genTable.getPackageName();String tplCategory = genTable.getTplCategory();String functionName = genTable.getFunctionName();VelocityContext velocityContext = new VelocityContext();velocityContext.put("tplCategory", genTable.getTplCategory());velocityContext.put("tableName", genTable.getTableName());velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】");velocityContext.put("ClassName", genTable.getClassName());velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName()));velocityContext.put("moduleName", genTable.getModuleName());velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName()));velocityContext.put("businessName", genTable.getBusinessName());velocityContext.put("basePackage", getPackagePrefix(packageName));velocityContext.put("packageName", packageName);velocityContext.put("author", genTable.getFunctionAuthor());velocityContext.put("datetime", DateUtils.getDate());velocityContext.put("pkColumn", genTable.getPkColumn());velocityContext.put("importList", getImportList(genTable));velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));velocityContext.put("columns", genTable.getColumns());velocityContext.put("table", genTable);velocityContext.put("dicts", getDicts(genTable));setMenuVelocityContext(velocityContext, genTable);if (GenConstants.TPL_TREE.equals(tplCategory)){setTreeVelocityContext(velocityContext, genTable);}if (GenConstants.TPL_SUB.equals(tplCategory)){setSubVelocityContext(velocityContext, genTable);}return velocityContext;}public static void setMenuVelocityContext(VelocityContext context, GenTable genTable){String options = genTable.getOptions();JSONObject paramsObj = JSON.parseObject(options);String parentMenuId = getParentMenuId(paramsObj);context.put("parentMenuId", parentMenuId);}public static void setTreeVelocityContext(VelocityContext context, GenTable genTable){String options = genTable.getOptions();JSONObject paramsObj = JSON.parseObject(options);String treeCode = getTreecode(paramsObj);String treeParentCode = getTreeParentCode(paramsObj);String treeName = getTreeName(paramsObj);context.put("treeCode", treeCode);context.put("treeParentCode", treeParentCode);context.put("treeName", treeName);context.put("expandColumn", getExpandColumn(genTable));if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)){context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE));}if (paramsObj.containsKey(GenConstants.TREE_NAME)){context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME));}}public static void setSubVelocityContext(VelocityContext context, GenTable genTable){GenTable subTable = genTable.getSubTable();String subTableName = genTable.getSubTableName();String subTableFkName = genTable.getSubTableFkName();String subClassName = genTable.getSubTable().getClassName();String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName);context.put("subTable", subTable);context.put("subTableName", subTableName);context.put("subTableFkName", subTableFkName);context.put("subTableFkClassName", subTableFkClassName);context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName));context.put("subClassName", subClassName);context.put("subclassName", StringUtils.uncapitalize(subClassName));context.put("subImportList", getImportList(genTable.getSubTable()));}/*** 获取模板信息* 用于获取代码生成所需的模板文件列表:* 根据前端类型确定使用的Vue模板版本(默认或element-plus v3)* 添加通用的后端模板(domain、mapper、service等)* 根据模板类别(CRUD、树形、主子表)添加对应的前端Vue模板* 主子表类型额外添加子表domain模板* 返回完整的模板路径列表用于代码生成。* @param tplCategory 生成的模板* @param tplWebType 前端类型* @return 模板列表*/public static List<String> getTemplateList(String tplCategory, String tplWebType){String useWebType = "vm/vue";if ("element-plus".equals(tplWebType)){useWebType = "vm/vue/v3";}List<String> templates = new ArrayList<String>();// ruoyi-generator模块下的resources下的模板文件templates.add("vm/java/domain.java.vm");templates.add("vm/java/mapper.java.vm");templates.add("vm/java/service.java.vm");templates.add("vm/java/serviceImpl.java.vm");templates.add("vm/java/controller.java.vm");templates.add("vm/xml/mapper.xml.vm");templates.add("vm/sql/sql.vm");templates.add("vm/js/api.js.vm");if (GenConstants.TPL_CRUD.equals(tplCategory)){templates.add(useWebType + "/index.vue.vm");}else if (GenConstants.TPL_TREE.equals(tplCategory)){templates.add(useWebType + "/index-tree.vue.vm");}else if (GenConstants.TPL_SUB.equals(tplCategory)){templates.add(useWebType + "/index.vue.vm");templates.add("vm/java/sub-domain.java.vm");}return templates;}/*** 获取文件名*/public static String getFileName(String template, GenTable genTable){// 文件名称String fileName = "";// 包路径String packageName = genTable.getPackageName();// 模块名String moduleName = genTable.getModuleName();// 大写类名String className = genTable.getClassName();// 业务名称String businessName = genTable.getBusinessName();String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/");String mybatisPath = MYBATIS_PATH + "/" + moduleName;String vuePath = "vue";if (template.contains("domain.java.vm")){fileName = StringUtils.format("{}/domain/{}.java", javaPath, className);}if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())){fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName());}else if (template.contains("mapper.java.vm")){fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);}else if (template.contains("service.java.vm")){fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className);}else if (template.contains("serviceImpl.java.vm")){fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className);}else if (template.contains("controller.java.vm")){fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className);}else if (template.contains("mapper.xml.vm")){fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className);}else if (template.contains("sql.vm")){fileName = businessName + "Menu.sql";}else if (template.contains("api.js.vm")){fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName);}else if (template.contains("index.vue.vm")){fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);}else if (template.contains("index-tree.vue.vm")){fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);}return fileName;}/*** 获取包前缀** @param packageName 包名称* @return 包前缀名称*/public static String getPackagePrefix(String packageName){int lastIndex = packageName.lastIndexOf(".");return StringUtils.substring(packageName, 0, lastIndex);}/*** 根据列类型获取导入包* * @param genTable 业务表对象* @return 返回需要导入的包列表*/public static HashSet<String> getImportList(GenTable genTable){List<GenTableColumn> columns = genTable.getColumns();GenTable subGenTable = genTable.getSubTable();HashSet<String> importList = new HashSet<String>();if (StringUtils.isNotNull(subGenTable)){importList.add("java.util.List");}for (GenTableColumn column : columns){if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())){importList.add("java.util.Date");importList.add("com.fasterxml.jackson.annotation.JsonFormat");}else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())){importList.add("java.math.BigDecimal");}}return importList;}/*** 根据列类型获取字典组* * @param genTable 业务表对象* @return 返回字典组*/public static String getDicts(GenTable genTable){List<GenTableColumn> columns = genTable.getColumns();Set<String> dicts = new HashSet<String>();addDicts(dicts, columns);if (StringUtils.isNotNull(genTable.getSubTable())){List<GenTableColumn> subColumns = genTable.getSubTable().getColumns();addDicts(dicts, subColumns);}return StringUtils.join(dicts, ", ");}/*** 添加字典列表* * @param dicts 字典列表* @param columns 列集合*/public static void addDicts(Set<String> dicts, List<GenTableColumn> columns){for (GenTableColumn column : columns){if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny(column.getHtmlType(),new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })){dicts.add("'" + column.getDictType() + "'");}}}/*** 获取权限前缀** @param moduleName 模块名称* @param businessName 业务名称* @return 返回权限前缀*/public static String getPermissionPrefix(String moduleName, String businessName){return StringUtils.format("{}:{}", moduleName, businessName);}/*** 获取上级菜单ID字段** @param paramsObj 生成其他选项* @return 上级菜单ID字段*/public static String getParentMenuId(JSONObject paramsObj){if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID)&& StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))){return paramsObj.getString(GenConstants.PARENT_MENU_ID);}return DEFAULT_PARENT_MENU_ID;}/*** 获取树编码** @param paramsObj 生成其他选项* @return 树编码*/public static String getTreecode(JSONObject paramsObj){if (paramsObj.containsKey(GenConstants.TREE_CODE)){return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE));}return StringUtils.EMPTY;}/*** 获取树父编码** @param paramsObj 生成其他选项* @return 树父编码*/public static String getTreeParentCode(JSONObject paramsObj){if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)){return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE));}return StringUtils.EMPTY;}/*** 获取树名称** @param paramsObj 生成其他选项* @return 树名称*/public static String getTreeName(JSONObject paramsObj){if (paramsObj.containsKey(GenConstants.TREE_NAME)){return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME));}return StringUtils.EMPTY;}/*** 获取需要在哪一列上面显示展开按钮** @param genTable 业务表对象* @return 展开按钮列序号*/public static int getExpandColumn(GenTable genTable){String options = genTable.getOptions();JSONObject paramsObj = JSON.parseObject(options);String treeName = paramsObj.getString(GenConstants.TREE_NAME);int num = 0;for (GenTableColumn column : genTable.getColumns()){if (column.isList()){num++;String columnName = column.getColumnName();if (columnName.equals(treeName)){break;}}}return num;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/93611.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/93611.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

neo4j导入导出方法

在 Neo4j 中&#xff0c;如果需要将数据从 一个环境导出&#xff0c;再 导入到另一个环境&#xff08;如从开发环境迁移到生产环境&#xff09;&#xff0c;可以通过以下方法实现&#xff1a;方法 1&#xff1a;使用 neo4j-admin 导出和导入&#xff08;完整数据库迁移&#xf…

Diamond基础2:开发流程之LedDemo

文章目录1.关联VS Code2.Diamond工程目录3.Led Demo开发流程4.烧写bit文件5.传送门1.关联VS Code 和Vivado一样&#xff0c;Diamond也可以使用第三方的编辑器&#xff0c;VS Code编辑器因为可以安装各种插件&#xff0c;并且对verilog开发的支持也算完善&#xff0c;所以很受欢…

Golang 后台技术面试套题 1

文章目录1.网络1.1 浏览器从输入网址到展示页面&#xff0c;描述下整个过程&#xff1f;1.2 HTTP 502&#xff0c;503 和 504 是什么含义&#xff1f;区别以及如何排查&#xff1f;1.3 HTTPS 通信过程为什么要约定加密密钥 code&#xff0c;用非对称加密不行吗&#xff1f;1.4 …

【科研绘图系列】R语言绘制蝶形条形图蝶形柱状堆积图

文章目录 介绍 加载R包 数据下载 导入数据 数据预处理 画图 系统信息 参考 介绍 【科研绘图系列】R语言绘制蝶形条形图&蝶形柱状堆积图 加载R包 library(tidyverse) library(ggsignif) library(RColorBrewer) library(dplyr) library(reshape2) library(grid

Jeecg后端经验汇总

Jeecg是一个不错的低代码平台&#xff0c;极大的降低了很多开发人员接私活的难度&#xff0c;也极大的降低了开发全套功能的难度。但是一码归一码&#xff0c;开发人员的水平很一般&#xff0c;如下&#xff1a;&#xff08;1&#xff09;普通用户可以修改管理员密码&#xff0…

ethernet_input到应用层处理简单分析

1、驱动层&#xff1a;从硬件读取数据并构造pbuf中断触发后&#xff0c;驱动层的接收任务&#xff08;或轮询函数&#xff09;会从网卡硬件读取数据&#xff0c;并将其封装为 LWIP 可识别的pbuf结构体&#xff08;LWIP 的数据缓冲区&#xff09;。关键函数&#xff1a;驱动自定…

C#WPF实战出真汁05--左侧导航

1、左侧导航设计要点清晰的信息架构 确保导航结构层次分明&#xff0c;主分类与子分类逻辑清晰&#xff0c;避免过度嵌套。使用分组、缩进或分隔线区分不同层级&#xff0c;保持视觉可读性。直观的图标与标签 为每个导航项搭配简洁的图标&#xff0c;强化视觉识别。标签文字需简…

大模拟 Major

题目来源&#xff1a;2025 Wuhan University of Technology Programming Contest 比赛链接&#xff1a;Dashboard - 2025 Wuhan University of Technology Programming Contest - Codeforces 题目大意&#xff1a; 模拟 16 支队伍的瑞士轮比赛结果&#xff0c;规则太多&…

【手撕JAVA多线程】1.从设计初衷去看JAVA的线程操作

目录 前言 概述 主动阻塞/唤醒 代码示例 实现 为什么必须在同步块中使用 计时等待是如何实现的 被动阻塞/唤醒 为什么要有被动阻塞/唤醒 实现&#xff08;锁升级&#xff09; 前言 JAVA多线程相关的内容很多很杂&#xff0c;但工作中用到的频率不高&#xff0c;用到…

UE5多人MOBA+GAS 46、制作龙卷风技能

文章目录创建龙卷风GA创建蒙太奇创捷一系列GE添加数据表添加到角色中创建龙卷风GA GA_Tornado 添加标签 // 龙卷风冷却CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Tornado_Cooldown)// 通用技能伤害CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Generic_Dama…

如何在ubuntu下安装libgpiod库

以下是关于如何在ubuntu下安装libgpiod库的两种安装方式以及遇到ubuntu存在多个工具链导致编译失败的解决方法。如果想要自由选择使用不同版本的libgpiod&#xff0c;可以选择手动编译安装方式&#xff0c;系统安装默认1.6.3版本(ubuntu22.04)。手动编译安装1、在github上下载要…

qt vs2019编译QXlsx

1、安装ActivePerl2、打开pro文件&#xff0c;直接编译即可第一个简单实例&#xff1a;#include "xlsxcellrange.h" #include "xlsxchart.h" #include "xlsxchartsheet.h" #include "xlsxdocument.h" #include "xlsxrichstring.h…

计算机存储器分类和层次结构详解

存储器是计算机系统的核心部件之一&#xff0c;其核心功能是存储程序&#xff08;指令&#xff09;和数据&#xff0c;是冯诺依曼体系结构“存储程序”概念的物质基础。它直接关系到计算机系统的性能、容量和成本。 存储器核心内容总览表分类维度存储器层级技术实现速度容量成本…

通过rss订阅小红书,程序员将小红书同步到自己的github主页

title: 通过rss订阅小红书&#xff0c;程序员将小红书同步到自己的github主页 tags: 个人成长 categories:杂谈最近在做一些新的尝试&#xff0c;把文本的内容转化为漫画和图片&#xff0c;方便大众阅读&#xff0c;恰好小红书很适合分发这些内容&#xff0c;于是我开通了小红书…

麒麟KylinOS V10-SP3 安装FastGPT

1. 操作系统环境CPU&#xff1a;20核 Xeon(R) Platinum 8457C 内存&#xff1a;64GB GPU&#xff1a;4090 操作系统&#xff1a;KylinOS-V10-SP32. 安装docker、docker-compose、fastgpt下载安装docker、docker-compose1. 下载docker docker 下载地址&#xff1a; https://do…

前端/在vscode中创建Vue3项目

Contenthtml input元素添加css样式使用js添加交互按钮点击提示输入框字符计数使用 npm 来管理项目包安装 Node.js初始化项目安装依赖包创建一个基于 Vite 的 Vue 项目创建项目进入项目目录安装依赖调用代码格式化工具启动开发服务器在浏览器中访问html input元素 <input ty…

HiSmartPerf使用WIFI方式连接Android机显示当前设备0.0.0.0无法ping通!设备和电脑连接同一网络,将设备保持亮屏重新尝试

在使用HiSmartPerf使用WIFI方式连接Android机时&#xff0c;如果出现无法ping通0.0.0.0的情况&#xff0c;可以尝试以下步骤解决问题&#xff1a;问了一下AI&#xff0c;给出的解答如下&#xff1a; 检查网络连接 &#xff1a;确保设备和电脑连接到同一局域网的Wi-Fi。可以在手…

SpringWeb是什么东西?

SpringWeb是个什么东西&#xff1f;SpringWeb是一个Java开发Web项目时的Web层框架。所谓Web层&#xff0c;就是直接和用户打交道的框架&#xff0c;用户(User)也就是顾客&#xff0c;顾客就是上帝&#xff0c;我们说是Web项目&#xff0c;通常也就是说B/S架构的项目&#xff0c…

docker+nginx+keepalived+openappsec+web ui+crowdsec部署安全代理

docker+nginx+keepalived+openappsec+web ui+crowdsec部署安全代理 一、环境介绍 二、基础环境安装 1、优化系统参数 2、安装docker 3、创建容器网络 4、安装测试容器(可选) 三、安装nginx 1、拉取镜像 2、创建映射目录 3、准备默认配置文件 4、证书文件准备 5、启动nginx容器…

自动驾驶中安全相关机器学习功能的可靠性定义方法

摘要当前标准无法涵盖高自动化驾驶中基于机器学习功能的安全需求。由于神经网络的不透明性&#xff0c;一些自动驾驶功能无法按照 V 模型进行开发。这些功能需要对标准进行扩展。本文聚焦这一空白&#xff0c;为这类功能定义了功能可靠性&#xff0c;以帮助未来的标准控制基于机…