当前位置: 首页 > ai >正文

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.xdnf.cn/news/18022.html

相关文章:

  • ansible管理变量和事实
  • Chrome插件开发实战:todoList 插件
  • 影刀初级B级考试大题2
  • Java ArraysParallelSortHelpers 并行排序
  • PyTorch 面试题及详细答案120题(01-05)-- 基础概念与安装
  • 深度学习-计算机视觉-数据增广/图像增广
  • AMBA-AXI and ACE协议详解(三)
  • TDengine IDMP 运维指南(1. 部署规划)
  • 基于飞算JavaAI的可视化数据分析集成系统项目实践:从需求到落地的全流程解析
  • 学习游戏制作记录(玩家掉落系统,删除物品功能和独特物品)8.17
  • Vue深入组件:Props 详解2
  • LINUX学习笔记
  • [RCTF2015]EasySQL
  • 11.苹果ios逆向-FridaHook-ios中的算法-CC_SHA1(sha1算法)
  • maxwell安装部署
  • 裸机框架:按键模组
  • PCA 实现多向量压缩:首个主成分的深层意义
  • 网络通信的基本概念与设备
  • 链路聚合与软件网桥
  • Android面试指南(二)
  • 记SpringBoot3.x + Thymeleaf 项目实现(MVC架构模式)
  • 校园综合数据分析可视化大屏 -Vue纯前端静态页面项目
  • Ugit使用记录
  • Git 入门指南:核心概念与常用命令全解析
  • Docker-14.项目部署-DockerCompose
  • 【Jenkins】02 - 自动化部署配置
  • 【Linux系列】如何在 Linux 服务器上快速获取公网
  • PAT 1068 Find More Coins
  • 补充:用信号量实现前驱关系
  • 【架构师干货】数据库管理系统