RuoYi-Vue-Plus (Velocity 代码生成使用以及代码逻辑)

一、配置文件

1-配置文件: src/main/resources/generator.yml 

2-配置生成表的基础信息:包路径 前缀规则等

# 代码生成
gen: 
  # 作者
  author: ruoyi
  # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
  packageName: com.ruoyi.system
  # 自动去除表前缀,默认是false
  autoRemovePre: false
  # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
  tablePrefix: sys_

  配置类:GenConfig

 路径 : src/main/java/com/ruoyi/generator/config/GenConfig.java

package com.ruoyi.generator.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

/**
 * 读取代码生成相关配置
 *
 * @author ruoyi
 */
@Component
@ConfigurationProperties(prefix = "gen")
@PropertySource(value = {"classpath:generator.yml"}, encoding = "UTF-8")
public class GenConfig {

    /**
     * 作者
     */
    public static String author;

    /**
     * 生成包路径
     */
    public static String packageName;

    /**
     * 自动去除表前缀,默认是false
     */
    public static boolean autoRemovePre;

    /**
     * 表前缀(类名不会包含表前缀)
     */
    public static String tablePrefix;

    public static String getAuthor() {
        return author;
    }

    @Value("${author}")
    public void setAuthor(String author) {
        GenConfig.author = author;
    }

    public static String getPackageName() {
        return packageName;
    }

  //省略。。。。。。。。
}

二、导入表逻辑

功能:导入需要生成的表数据到以下2张表

          gen_table 导入的表

         gen_table_column  导入的表列名

页面位置:src/views/tool/gen/importTable.vue

2-1 导入按钮:

上面 导入按钮组件 ,在index页面中

按钮逻辑:

       1-点击导入按钮,调用弹框页面show方法

        2-show 展示导入选表页面,渲染所有表列表

//0-引入导入页面组件
import importTable from "./importTable";
export default {
  name: "Gen",
  components: { importTable },



//1-点击导入按钮,触发 openImportTable方法
  <el-button
          type="info"
          plain
          icon="el-icon-upload"
          size="mini"
          @click="openImportTable"
          v-hasPermi="['tool:gen:import']"
        >导入</el-button>


//2-js方法:openImportTable
   /** 打开导入表弹窗 调用了import组件里面的show方法 */
    openImportTable() {
      this.$refs.import.show();
    },



//3-import组件中show方法:调用后台接口: /db/list 查询数据,并展示弹框页面
 show() {
      this.getList();
      this.visible = true;
    },
 
    // 查询表数据
    getList() {
      listDbTable(this.queryParams).then(res => {
        if (res.code === 200) {
          this.dbTableList = res.rows;
          this.total = res.total;
        }
      });
    },

后台查询列表中

根据  <if>条件 判断数据库类型,执行对应sql,查询数据库所有表

<if test="@com.ruoyi.common.helper.DataBaseHelper@isMySql()">:判断是myql

<if test="@com.ruoyi.common.helper.DataBaseHelper@isOracle()">:判断oracle查询对应库博

<select id="selectPageDbTableList" resultMap="GenTableResult">
        <if test="@com.ruoyi.common.helper.DataBaseHelper@isMySql()">
            select table_name, table_comment, create_time, update_time
            from information_schema.tables
            where table_schema = (select database())
            AND table_name NOT LIKE 'xxl_job_%' AND table_name NOT LIKE 'gen_%'
            AND table_name NOT IN (select table_name from gen_table)
            <if test="genTable.tableName != null and genTable.tableName != ''">
                AND lower(table_name) like lower(concat('%', #{genTable.tableName}, '%'))
            </if>
            <if test="genTable.tableComment != null and genTable.tableComment != ''">
                AND lower(table_comment) like lower(concat('%', #{genTable.tableComment}, '%'))
            </if>
            order by create_time desc
        </if>
        <if test="@com.ruoyi.common.helper.DataBaseHelper@isOracle()">
            select lower(dt.table_name) as table_name, dtc.comments as table_comment, uo.created as create_time, uo.last_ddl_time as update_time
            from user_tables dt, user_tab_comments dtc, user_objects uo
            where dt.table_name = dtc.table_name
            and dt.table_name = uo.object_name
            and uo.object_type = 'TABLE'
            AND dt.table_name NOT LIKE 'XXL_JOB_%' AND dt.table_name NOT LIKE 'GEN_%'
            AND lower(dt.table_name) NOT IN (select table_name from gen_table)
            <if test="genTable.tableName != null and genTable.tableName != ''">
                AND lower(dt.table_name) like lower(concat(concat('%', #{genTable.tableName}), '%'))
            </if>
            <if test="genTable.tableComment != null and genTable.tableComment != ''">
                AND lower(dtc.comments) like lower(concat(concat('%', #{genTable.tableComment}), '%'))
            </if>
            order by create_time desc
        </if>
        
    </select>

2-2 确认按钮

功能:插入选中表,以及表字段到代码生成数据库。

前端逻辑:

确定按钮调用了:handleImportTable  JS方法,并请求后台接口 /tool/gen/importTable 保存数据,

成功: this.$emit("ok"); 通知父组件的 ok方法,触发 handleQuery渲染保存数据

子组件  src/views/tool/gen/importTable.vue 方法:

  /** 导入按钮操作 */
    handleImportTable() {
      const tableNames = this.tables.join(",");
      if (tableNames == "") {
        this.$modal.msgError("请选择要导入的表");
        return;
      }
      importTable({ tables: tableNames }).then(res => {
        this.$modal.msgSuccess(res.msg);
        if (res.code === 200) {
          this.visible = false;
          this.$emit("ok");
        }
      });
    }


//调用后台接口
export function importTable(data) {
  return request({
    headers: { 'datasource': localStorage.getItem("dataName") },
    url: '/tool/gen/importTable',
    method: 'post',
    params: data
  })
}

父组件:handleQuery

重新渲染上面插入的数据,到列表

    <import-table ref="import" @ok="handleQuery" />


   /** 搜索按钮操作 */
    handleQuery() {
      localStorage.setItem("dataName", this.queryParams.dataName);
      this.queryParams.pageNum = 1;
      this.getList();
    },
后台逻辑:

请看注释,已经详细写了每一步:

主要是 :初始化表信息 实体类驼峰,以及java实体类类型

                生成页面的表单 、列表、 查询栏等显示

   /**
     * 导入表结构(保存)
     *
     * @param tables 表名串
     */
    @SaCheckPermission("tool:gen:import")
    @Log(title = "代码生成", businessType = BusinessType.IMPORT)
    @PostMapping("/importTable")
    public R<Void> importTableSave(String tables) {
        String[] tableNames = Convert.toStrArray(tables);
        //1- 根据传入的表名称,查询information_schema.tables表信息
        List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);
        //2- 保存表信息到GenTable 表存标字段信息到GenTableColumn
        genTableService.importGenTable(tableList);
        return R.ok();
    }

service层:

@Transactional(rollbackFor = Exception.class)
    @Override
    public void importGenTable(List<GenTable> tableList) {
        String operName = LoginHelper.getUsername();
        try {
            for (GenTable table : tableList) {
                //1-得到表名称
                String tableName = table.getTableName();
                //2-初始化表信息  (表面转类名称 去除表前缀f_转驼峰  设置包名称 创建人 就是yml 配置)
                GenUtils.initTable(table, operName);
                //3-插入表信息
                int row = baseMapper.insert(table);
                if (row > 0) {
                    //4- 查询表字段信息
                    List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
                    List<GenTableColumn> saveColumns = new ArrayList<>();
                    for (GenTableColumn column : genTableColumns) {
                        //5-初始化表字段,数据库字段类型设置对应java字段类型,以及页面字段类型,查询条件,上传文件 等
                        GenUtils.initColumnField(column, table);
                        saveColumns.add(column);
                    }
                    if (CollUtil.isNotEmpty(saveColumns)) {
                        //5-插入 列表
                        genTableColumnMapper.insertBatch(saveColumns);
                    }
                }
            }
        } catch (Exception e) {
            throw new ServiceException("导入失败:" + e.getMessage());
        }

上面步骤4: //4- 查询表字段信息

调用selectDbTableColumnsByName 方法, 执行sql如下:

select column_name, (case when (is_nullable = 'no' && column_key != 'PRI') then '1' else '0' end) as is_required, (case when column_key = 'PRI' then '1' else '0' end) as is_pk, ordinal_position as sort, column_comment, (case when extra = 'auto_increment' then '1' else '0' end) as is_increment, column_type from information_schema.columns where table_schema = (select database()) and table_name = ('test_demo') order by ordinal_position

结果:

最后插入到表 gen_table_column 中

三、修改操作 

3.1页面以及逻辑展示

功能:

1-定义代码生成的实体类

2-定义前端泰马是否显示

编辑页面,前端路径:src/views/tool/gen/editTable.vue

功能:页面会初始化字段信息,以及基础数据下拉

  created() {
    const tableId = this.$route.params && this.$route.params.tableId;
    if (tableId) {
      // 获取表详细信息
      getGenTable(tableId).then(res => {
        this.columns = res.data.rows;
        this.info = res.data.info;
        this.tables = res.data.tables;
      });
      /** 查询字典下拉列表 */
      getDictOptionselect().then(response => {
        this.dictOptions = response.data;
      });
      /** 查询菜单下拉列表 */
      getMenuTreeselect().then(response => {
        this.menus = this.handleTree(response.data, "menuId");
      });
    }
  },

提交按钮逻辑:

校验表单数据,成功则组装表单数据发送后台保存

 /** 提交按钮 */
    submitForm() {
      const basicForm = this.$refs.basicInfo.$refs.basicInfoForm;
      const genForm = this.$refs.genInfo.$refs.genInfoForm;
      Promise.all([basicForm, genForm].map(this.getFormPromise)).then(res => {
        // 校验表单basicForm、genForm 成功后执行
        const validateResult = res.every(item => !!item);
        if (validateResult) {
          //basicForm.model, genForm.model  设置到 {}中得到 genTable对象
          const genTable = Object.assign({}, basicForm.model, genForm.model);
          genTable.columns = this.columns;
          genTable.params = {
            treeCode: genTable.treeCode,
            treeName: genTable.treeName,
            treeParentCode: genTable.treeParentCode,
            parentMenuId: genTable.parentMenuId
          };
          //请求后台 修改代码生成信息
          updateGenTable(genTable).then(res => {
            this.$modal.msgSuccess(res.msg);
            if (res.code === 200) {
              this.close();
            }
          });
        } else {
          this.$modal.msgError("表单校验未通过,请重新检查提交内容");
        }
      });
    }
 3.2 树表代码生成

如图对比表字段

四、预览(Velocity生成预览

4.1功能展示

点击预览展示生成的前后端代码

 如图:

可以预览前后端页面代码,以及生成测试数据sql

4.2预览页面前端代码:

点击 src/views/tool/gen/index.vue 页面的预览按钮:

 /** 预览按钮 */
    handlePreview(row) {
      previewTable(row.tableId).then(response => {
        this.preview.data = response.data;
        this.preview.open = true;
        this.preview.activeName = "domain.java";
      });
    },

,然后渲染弹框页面如上图 4.1展示。

 渲染页面展示代码:

   <el-dialog :title="preview.title" :visible.sync="preview.open" width="80%" top="5vh" append-to-body class="scrollbar">
      <el-tabs v-model="preview.activeName">
        <el-tab-pane
          v-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="key"
        >
          <el-link :underline="false" icon="el-icon-document-copy" v-clipboard:copy="value" v-clipboard:success="clipboardSuccess" style="float:right">复制</el-link>
          <!--  <pre> 标签 保留所有空格换行        -->
          <pre><code class="hljs" v-html="highlightedCode(value, key)"></code></pre>
        </el-tab-pane>
      </el-tabs>
    </el-dialog>

 备注渲染代码:

1-展示代码用到 :<pre> 标签 保留所有空格换行,文本格式原样子输出

2-代码高亮展示 :highlightedCode 方法,更具语言高亮展示代码:
import hljs from "highlight.js/lib/highlight";
import "highlight.js/styles/github-gist.css";
/** 高亮显示 */
highlightedCode(code, key) {
  const vmName = key.substring(key.lastIndexOf("/") + 1, key.indexOf(".vm"));
  var language = vmName.substring(vmName.indexOf(".") + 1, vmName.length);
  const result = hljs.highlight(language, code || "", true);
  return result.value || '&nbsp;';
},
 4.3 后端代码
后端使用Velocity生成预览的模版

请求了:@GetMapping("/preview/{tableId}") 接口,其实现类主要逻辑如下:

1-拿到要生成的表和标数据

2-合并表数据和Velocity模版

private void generatorCode(String tableName, ZipOutputStream zip) {
        // 查询表信息
        GenTable table = baseMapper.selectGenTableByName(tableName);
        List<Long> menuIds = new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            menuIds.add(identifierGenerator.nextId(null).longValue());
        }
        table.setMenuIds(menuIds);
        // 设置主子表信息
        setSubTable(table);
        // 设置主键列信息
        setPkColumn(table);

        /**
         * 生成Velocity模版步骤
         */

        //1-初始化Velocity
        VelocityInitializer.initVelocity();
        //2-准备Velocity上下文
        VelocityContext context = VelocityUtils.prepareContext(table);

        //3-获取模板列表
        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            //4-根据路径拿到模板
            Template tpl = Velocity.getTemplate(template, Constants.UTF8);
            //5-StringWriter流,合并上下文和模版
            tpl.merge(context, sw);
            //6-生成压缩包
            try {
                // 添加到zip
                zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));
                IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString());
                IoUtil.close(sw);
                zip.flush();
                zip.closeEntry();
            } catch (IOException e) {
                log.error("渲染模板失败,表名:" + table.getTableName(), e);
            }
        }
    }

上面getTemplateList方法,对应的vm模版位置:

实体类 模版展示:

具体Velocity生成代码学习可以看我文章:

RuoYi-Vue-Plus (代码生成、Velocity模板引擎)_vue plus-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/javaxueba/article/details/139993004

package ${packageName}.domain;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;

#foreach ($import in $importList)
import ${import};
#end
#if($table.crud || $table.sub)
import com.ruoyi.common.core.domain.BaseEntity;
#elseif($table.tree)
import com.ruoyi.common.core.domain.TreeEntity;
#end

/**
 * ${functionName}对象 ${tableName}
 *
 * @author ${author}
 * @date ${datetime}
 */
#if($table.crud || $table.sub)
    #set($Entity="BaseEntity")
#elseif($table.tree)
    #set($Entity="TreeEntity<${ClassName}>")
#end
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("${tableName}")
public class ${ClassName} extends ${Entity} {

    private static final long serialVersionUID=1L;

#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField))
    /**
     * $column.columnComment
     */
#if($column.javaField=='delFlag')
    @TableLogic
#end
#if($column.javaField=='version')
    @Version
#end
#if($column.pk)
    @TableId(value = "$column.columnName")
#end
    private $column.javaType $column.javaField;
#end
#end

}
4.3下载压缩包

选择zip压缩包下载

逻辑和4.3预览一样,多了转为字节流下载

 /**
     * 批量生成代码
     *
     * @param tables 表名串
     */
    @SaCheckPermission("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");
        IoUtil.write(response.getOutputStream(), false, data);
    }
}

service层

 /**
     * 批量生成代码(下载方式)
     *
     * @param tableNames 表数组
     * @return 数据
     */
    @Override
    public byte[] downloadCode(String[] tableNames) {
        //创建字节数组输出流
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        //根据字节数组输出流,创建ZipOutputStream输出流
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        //生成模版
        for (String tableName : tableNames) {
            generatorCode(tableName, zip);
        }
        //关闭流
        IoUtil.close(zip);
        //转为字节数组
        return outputStream.toByteArray();
    }

前端:

判断是二进制数据,进行下载

    /** 生成代码操作 */
    handleGenTable(row) {
      const tableNames = row.tableName || this.tableNames;
      if (tableNames == "") {
        this.$modal.msgError("请选择要生成的数据");
        return;
      }
      if(row.genType === "1") {
        genCode(row.tableName).then(response => {
          this.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath);
        });
      } else {
        this.$download.zip("/tool/gen/batchGenCode?tables=" + tableNames, "ruoyi.zip");
      }
    },


  zip(url, name) {
    var url = baseURL + url
    downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
    axios({
      method: 'get',
      url: url,
      responseType: 'blob',
      headers: {
        'Authorization': 'Bearer ' + getToken(),
        'datasource': localStorage.getItem("dataName")
      }
    }).then((res) => {
        //是否是二进制
      const isBlob = blobValidate(res.data);
      if (isBlob) {
        const blob = new Blob([res.data], { type: 'application/zip' })
        this.saveAs(blob, name)
      } else {
        this.printErrMsg(res.data);
      }
      downloadLoadingInstance.close();
    }).catch((r) => {
      console.error(r)
      Message.error('下载文件出现错误,请联系管理员!')
      downloadLoadingInstance.close();
    })
  },

五、同步功能

数据库字段改动点击同步,会按照最新字段生成代码,保留之前的未修改字段逻辑

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值