AutoScript 代码生成Saas工具

AutoScript 代码生成Saas 创建模版手册

前言

为了满足用户自定义代码模版需求,特意编写该手册,方便用户使用AutoScript 工具来快速配置模版。

该工具基于FreeMarker 技术,如果首次接触,可以先了解附录的FreeMarker语法,以便迅速地编写模版。某些功能FreeMarker不能很好支持,需要平台自定义一些函数来补充完善代码生成需要。

模版工程,工程参数,模版关系如下图:
在这里插入图片描述
一个模版工程只需定义一组工程参数,可以若干个模版,一个模版通常对应一组文件

本人也是做开发多年,深感IT民工“搬砖”辛苦,想想如何减轻天下开发人员工作量负担,酝酿良久。多版改造,终于出一版BS结构代码生成器!我知道任何系统都不是完美,必经迭代改造,希望用户使用过程中,多提宝贵意见,逐步完善该工具。实现初心:让天下程序员脱离繁复工作!能按点上下班,多抽出时间陪伴家人,保养好身体,为社会做出更大贡献。

系统特点
ü 该系统只是提供模版配置,所以它具有平台无关性,语言无关性。能生成任何平台,任何框架,任何语言代码及相关配置

ü 一键生成,支持批量生成;一个功能生成一般都是秒级生成,一个项目一次生成也是分钟级别而已!

ü 支持从设计到代码,一气呵成!目前设计支持pdm格式,如果还有其他,可以提出。应网友要求支持sql表结构导入。

ü BS结构,本地无需任何安装,直接访问云服务器即可。系统升级服务就升级。

ü 支持定制页面,可能有些客户需求不同,比如直接连接云上系统,挂接菜单等。

编写模版次序
1.数据库设计,目前只支持pdm及SQL文件格式

2.根据业务需求,归纳总结系统模版,比如单表,主子表等。

3.根据模版做出比较通用的功能,切勿加有业务的功能,因为生成代码是统一个“模”生成的,必须具有通用性。

4.着手编写模版。编写模版时,通常一个模版功能分为前后端,一般模版也分为前端模版和后端模版。便于开发人员使用。为了加快开发人员入门,本文以生成bean vo 为例子,详细说明如何创建模版。

这个例子熟练后,可以参考代码生成平台的其他工程创建其他模版。

本文以bean:AsKnowledgeBase.java为例,示范如何配置模版
在这里插入图片描述

package top.tangyh.lamp.learnpm.entity;

 

import java.time.LocalDateTime;

 

 

/**

 * <p>

 * 知识库实体类

 * </p>

 *

 * @author lsb

 * @date 2022-06-26

 */

@Data

public class AsKnowledgeBase  {

 

    private static final long serialVersionUID = 1L;

 

    /**

     * 类型

     */

    private String kbType;

    /**

     * 关键字

     */

    private String kbKeyword;

    /**

     * 语法

     */

    private String kbGrammar;

    /**

     * 描述

     */

    private String desc;

 

}

 

新建工程
在浏览器(推荐用谷歌浏览器)地址栏录入:http://jz-fw.com:8081/,如果地址栏有变化,发邮件及短信通知
在这里插入图片描述
首次使用,需要注册用户
在这里插入图片描述
填写上面各项,手机号和邮箱为了忘记密码,系统变更时发通知用,不会对外暴露公开,请放心注册。

登陆进去后,点击菜单:模版维护->模版列表
在这里插入图片描述
点击新增后,出现如下界面:
在这里插入图片描述
工程名称:录入一个有意义的名称,通常为带有业务含义,比如XXX平台单表模版,工程名称不能与系统现有工程名称相同。

字符集:即jdk支持字符集名称。通常为UTF-8

是否选数据源:是否需要上传表结构(pdm文件等),通常用于创建新项目模版。生成功能代码,一般选择是,如图绿色;新建项目,选否,开关往左拨。变红色。

自定义运行页面URL:用于定制生成代码页面,需要联系平台定制,然后才能配进去。

备注:对于工程的普通描述。

填完,点击保存按钮。

配置工程参数
工程参数通常用于配置一些公共参数,比如根包名,作者,根目录,数据源名称等等。需要根据平台框架需要配置。

在工程列表点击六边形图标,进行参数配置,如下图:
在这里插入图片描述
点击六边形图标后,进入下图:
在这里插入图片描述
点击新增按钮,进入下图:
在这里插入图片描述
在这里插入图片描述
各项说明如下:

参数类型:是生成代码时,配置工程参数页面参数界面控件是什么类型,通常类型有如下图:
在这里插入图片描述
单行输入框,多行输入框(通常说的大文本框),日期,时间,数字输入框,单选框,多选框等

参数标题:在界面控件前面标题,提示界面控件的作用。

宽度:界面控件占用宽度,通常为50%,100%

验证规则:通常填必填验证规则。如果该参数不是必填,可以为空

选择内容:用于下拉框选择,下拉框有那些选项

参数名:这个非常重要,用于模版中如何得到参数输入的值,就是靠这个参数名来获取。同一个模版工程中,参数名唯一。

占位符:就是提示使用者文本,比如请输入xxxx之类

最大长度:指定参数能录入的最大长度

CS风格:目前尚不支持

初始化值:就是参数默认值

最大值:用于数字输入框的最大值

最小值:用于数字输入框的最小值

小数位:用于数字输入框的能录入小数位数

步长:目前尚未支持

最大行数:用于多行输入框的最多输入行数

最小行数:用于多行输入框的最小输入行数

初始行数:用于多行输入框的初始行数(一开始就显示有多少行)

初始列数:用于多行输入框的初始列数(一开始就显示有多少列)

下面以单行输入框和下拉框为例子

单行输入框:
在这里插入图片描述
在这里插入图片描述
下拉框:

在这里插入图片描述
在这里插入图片描述
选择内容截图看不全,下面复制出文本:

[{“value”:“DsConstant.DEFAULTS”,“label”:“缺省数据源”},{“value”:“DsConstant.BASE_TENANT”,“label”:“动态租户base库”}, {“value”:“DsConstant.EXTEND_TENANT”,“label”:“动态租户extend库”}]

它是一个JSON数组,每一项都包含value,label,其他label为显示名称,value为对应值

有多少项,就构造多少个{}

我们观察一下demo的Bean,会发现文件根位置,根包名和作者是可以提取成参数,如下图:
在这里插入图片描述
在这里插入图片描述
我们创建三个输入框的参数即可,如下图:
在这里插入图片描述
【注意】:目录必须以/作为文件分隔符
--------------------------------根包名参数--------------------------
在这里插入图片描述
--------------------------------作者--------------------------
在这里插入图片描述
全部录入完,如下图:
在这里插入图片描述

新建模版

配置完模版后,可以正式进入编制模版阶段了,点击如下按钮:
在这里插入图片描述
点击上图图标,进入模版维护列表
在这里插入图片描述
点击新增,进入模版编辑界面
在这里插入图片描述
模版名称可以任意起,同一个模版工程里不能重复,这个例子我们可以起bean作为模版名称;后面到关键模版内容配置地方,在开始配置之前,先观察实际的bean内容,如下图:
在这里插入图片描述
可以看到需要两层循环才能完成bean vo生成
循环一个表写法如下:

<#list rootnode.getChildNodesByPath(“Table”) as tableNode>

</#list>

<#list …> 表示循环开始

</#list> 表示循环结束

循环一个表字段写法如下:

<#list tableNode.getChildNodesByPath(“Column”) as columnNode>

</#list>

循环问题搞掂后,还有一些细节入下图:
在这里插入图片描述

上图还漏了,创建文件及根包名

 

取出表中文名写法:

 ${tableNode.getAttributeVal("name")}

 

取作者写法:

${author}

author是配置工程参数的参数名

 

取当前时间写法:

 ${currentdate("yyyy-MM-dd")}

Yyyy-MM-dd 是日期格式

 

表名转换为类名是一个宏,调用如下:

<#assign tablenametrimmodule><@getTableName tableName='${tableNode.getAttributeVal("code")}' />

 

字段名转属性,字段类型转java类型都是宏,在下面的模版内容逐一标出

 

取字段中文名写法:

${columnNode.getAttributeVal("name")}

 

创建文件写法

@{createTextFile("xxx",false)}

文件内容

@{closeFile()}

 

xxx表示文件名,可以动态或静态

文件内容是将写入文件内容

@{closeFile()}表示文件结束

 

通过上面几种命令组合,可以配置如下:

<#--

转换表名为类名 首字母大写

-->

<#macro getTableName tableName>

${wordfirstupper(replace(wordfirstupper(lowercase(tableName),"_"),"_",""),"_")}<#t>

</#macro>

 

<#--

转换字段类型为java类型

-->

<#macro toJavaType fieldType>

<#if startswithignorecase(fieldType,"char")||startswithignorecase(fieldType,"varchar")||

     startswithignorecase(fieldType,"blob")||startswithignorecase(fieldType,"clob")||

     startswithignorecase(fieldType,"nchar")||startswithignorecase(fieldType,"nvarchar")||

     startswithignorecase(fieldType,"nblob")||startswithignorecase(fieldType,"nclob")||

     lowercase(fieldType)=="text"||lowercase(fieldType)=="longtext"||

     lowercase(fieldType)=="mediumtext" ||lowercase(fieldType)=="json">

String<#t>

<#elseif startswithignorecase(fieldType,"datetime") >

Date<#t>

<#elseif startswithignorecase(fieldType,"date") >

Date<#t>

<#elseif startswithignorecase(fieldType,"float") >

Float<#t>

<#elseif startswithignorecase(fieldType,"integer") || startswithignorecase(fieldType,"int")||startswithignorecase(fieldType,"tinyint")>

Integer<#t>

<#elseif startswithignorecase(fieldType,"long")||lowercase(fieldType)=="bigint" >

Long<#t>

<#elseif startswithignorecase(fieldType,"timestamp") >

Timestamp<#t>

<#elseif (startswithignorecase(fieldType,"number") && index(fieldType,",")>=0) || startswithignorecase(fieldType,"decimal")|| startswithignorecase(fieldType,"numeric")>

BigDecimal<#t>

<#elseif (startswithignorecase(fieldType,"number") && index(fieldType,",")==-1) >

Long<#t>

<#else>

String<#t>

</#if>

</#macro>

<#--

转换字段名为属性

-->

<#macro toPropertyName fieldName>

${wordfirstlower(replace(wordfirstupper(lowercase(fieldName),"_"),"_",""),"_")}<#t>

</#macro>

 

<#list rootnode.getChildNodesByPath("Table") as tableNode>

<#assign tablenametrimmodule><@getTableName tableName='${tableNode.getAttributeVal("code")}' /></#assign>

<#assign fileName>${tablenametrimmodule}.java</#assign>

 

@{createTextFile("${rootPath}/entity/${fileName}",false)}

package ${rootPack}.entity;

 

import java.time.LocalDateTime;

 

/**

 * <p>

 * ${tableNode.getAttributeVal("name")}实体类

 * </p>

 *

 * @author ${author}

 * @date ${currentdate("yyyy-MM-dd")}

 */

@Data

public class ${tablenametrimmodule} {

 

    private static final long serialVersionUID = 1L;

 

    <#list tableNode.getChildNodesByPath("Column") as columnNode>  

  <#if (lowercase(columnNode.getAttributeVal("code"))!="id")>

    /**

     * ${columnNode.getAttributeVal("name")}

     */

    <#assign javaType><@toJavaType fieldType='${columnNode.getAttributeVal("datatype")}' /></#assign>

    private ${javaType}   <@toPropertyName fieldName='${columnNode.getAttributeVal("code")}'/>;

  </#if>

    </#list>

}

@{closeFile()}

</#list>

 

模版文件结构:前面是几个宏,宏可以理解为函数,供后面调用

格式为:

<#macro …>

</#macro>

凡是<#开头的都是freemarker语句,具体见附录

还有一些扩展函数意义,也参见附录

模版配置完如下:
在这里插入图片描述

运行

找到刚才配置模版,点击运行图标,即可生成代码了!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
大体步骤,点击运行按钮,进入运行界面,填工程参数,选择表,点击生成按钮。生成成功自动下载结果文件

在这里插入图片描述

模版优化


如果每个页面都把宏定义一遍,显得臃肿,不好维护。可以把公共的宏抽出来,放到一个模版里头,其他模版公用。类似java jar

假设抽取后的模版名称为:pubmacro,则它内容如下:

<#--

转换表名为类名 首字母大写

-->

<#macro getTableName tableName>

${wordfirstupper(replace(wordfirstupper(lowercase(tableName),"_"),"_",""),"_")}<#t>

</#macro>

 

<#--

转换字段类型为java类型

-->

<#macro toJavaType fieldType>

<#if startswithignorecase(fieldType,"char")||startswithignorecase(fieldType,"varchar")||

     startswithignorecase(fieldType,"blob")||startswithignorecase(fieldType,"clob")||

     startswithignorecase(fieldType,"nchar")||startswithignorecase(fieldType,"nvarchar")||

     startswithignorecase(fieldType,"nblob")||startswithignorecase(fieldType,"nclob")||

     lowercase(fieldType)=="text"||lowercase(fieldType)=="longtext"||

     lowercase(fieldType)=="mediumtext" ||lowercase(fieldType)=="json">

String<#t>

<#elseif startswithignorecase(fieldType,"datetime") >

Date<#t>

<#elseif startswithignorecase(fieldType,"date") >

Date<#t>

<#elseif startswithignorecase(fieldType,"float") >

Float<#t>

<#elseif startswithignorecase(fieldType,"integer") || startswithignorecase(fieldType,"int")||startswithignorecase(fieldType,"tinyint")>

Integer<#t>

<#elseif startswithignorecase(fieldType,"long")||lowercase(fieldType)=="bigint" >

Long<#t>

<#elseif startswithignorecase(fieldType,"timestamp") >

Timestamp<#t>

<#elseif (startswithignorecase(fieldType,"number") && index(fieldType,",")>=0) || startswithignorecase(fieldType,"decimal")|| startswithignorecase(fieldType,"numeric")>

BigDecimal<#t>

<#elseif (startswithignorecase(fieldType,"number") && index(fieldType,",")==-1) >

Long<#t>

<#else>

String<#t>

</#if>

</#macro>

<#--

转换字段名为属性

-->

<#macro toPropertyName fieldName>

${wordfirstlower(replace(wordfirstupper(lowercase(fieldName),"_"),"_",""),"_")}<#t>

</#macro>

而bean模版内容,则调整为:

<#include "pubmacro.ftl" encoding="UTF-8" parse=true>

 

<#list rootnode.getChildNodesByPath("Table") as tableNode>

<#assign tablenametrimmodule><@getTableName tableName='${tableNode.getAttributeVal("code")}' /></#assign>

<#assign fileName>${tablenametrimmodule}.java</#assign>

 

@{createTextFile("${rootPath}/entity/${fileName}",false)}

package ${rootPack}.entity;

 

import java.time.LocalDateTime;

 

/**

 * <p>

 * ${tableNode.getAttributeVal("name")}实体类

 * </p>

 *

 * @author ${author}

 * @date ${currentdate("yyyy-MM-dd")}

 */

@Data

public class ${tablenametrimmodule} {

 

    private static final long serialVersionUID = 1L;

 

    <#list tableNode.getChildNodesByPath("Column") as columnNode>  

  <#if (lowercase(columnNode.getAttributeVal("code"))!="id")>

    /**

     * ${columnNode.getAttributeVal("name")}

     */

    <#assign javaType><@toJavaType fieldType='${columnNode.getAttributeVal("datatype")}' /></#assign>

    private ${javaType}   <@toPropertyName fieldName='${columnNode.getAttributeVal("code")}'/>;

  </#if>

    </#list>

}

@{closeFile()}

</#list>

 

 

这样一来,一个宏文件调整,所有引用地方就调整,方便统一维护

附录

FreeMarker语法
指令含义及语法
<#if判断语句 <#if condition> … <#elseif condition2> … <#elseif condition3> … … <#else> … </#if>
<#switch多路判断 <#switch value> <#case refValue1> … <#break> <#case refValue2> … <#break> … <#case refValueN> … <#break> <#default> … </#switch>
<#list循环 <#list sequence as item> … <#if item = “spring”><#break></#if> … </#list>
<#include引入另一个模版 <#include filename encoding=“GBK” parse=true>引用公共模板.<#include filename options> options包含两个属性 encoding=”GBK” 编码格式 parse=true 是否作为ftl语法解析,默认是true,false就是以文本方式引入.注意在ftl文件里布尔值都是直接赋值的如parse=true,而不是parse=”true”
<#import引入宏 <#import path as hash>类似于java里的import,它导入文件,然后就可以在当前文件里使用被导入文件里的宏组件 用例 假设mylib.ftl里定义了宏copyright那么我们在其他模板页面里可以这样使用 <#import “/libs/mylib.ftl” as my> <@my.copyright date=“1999-2002”/> "my"在freemarker里被称作namespace
<#compress>用来压缩空白空间和空白的行 <#compress> … </#compress>用来压缩空白空间和空白的行.<#compress> 1 2 3 4 5 ${moo} test only I said, test only </#compress> 输出 (1 2 3 4 5 moo test only I said, test only) escape, noescape
<#escape主要使用在相似的字符串变量输出,比如某一个模块的所有字符串输出都必须是html安全的,这个时候就可以使用该表达式 <#escape identifier as expression> … <#noescape>…</#noescape> … </#escape> <#escape x as x?html> First name: ${firstName} <#noescape>Last name: ${lastName}</#noescape> Maiden name: ${maidenName} </#escape> 相同表达式 First name: ${firstName?html} Last name: ${lastName } Maiden name: ${maidenName?html}
<#assign赋值语句 给变量赋值。语法 <#assign name=value> or <#assign name1=value1 name2=value2 … nameN=valueN> or <#assign same as above… in namespacehash> or <#assign name> capture this </#assign> or <#assign name in namespacehash> capture this </#assign>
<#global全局赋值语法,利用这个语法给变量赋值,那么这个变量在所有的namespace中是可见的,如果这个变量被当前的assign语法覆盖 如<#global x=2> <#assign x=1> 在当前页面里x=2将被隐藏,或者通过${global.x}来访问 语法 <#global name=value> or <#global name1=value1 name2=value2 … nameN=valueN> or <#global name> capture this </#global>
<#setting用来设置整个系统的一个环境. 用来设置整个系统的一个环境 locale number_format boolean_format date_format, time_format, datetime_format time_zone classic_compatible 用例 假如当前是匈牙利的设置,然后修改成美国 ${1.2} <#setting locale=“en_US”> ${1.2} 输出 1,2 1.2 因为匈牙利是采用“,”作为十进制的分隔符,美国是用“.”
<#macro宏定义 宏定义.用例 <#macro test foo bar=“Bar” baaz=-1> Test text, and the params: ${foo}, ${bar}, ${baaz} </#macro> <@test foo=“a” bar=“b” baaz=55-2/> <@test foo=“a” bar=“b”/> <@test foo=“a” baaz=55-2/> <@test foo=“a”/> 输出 Test text, and the params: a, b, 23 Test text, and the params: a, b, -1 Test text, and the params: a, Bar, 23 Test text, and the params: a, Bar, -1
<#t>去掉左右空白和回车换行 xxxxx<#t>
<#lt>去掉左边空白和回车换行xxxx<#lt>
<#rt>去掉右边空白和回车换行 xxxx<#rt>
<#nt>取消<#t>,<#lt>,<#rt>的效果 xxxx<#t><#nt>
${expression}${expression}计算expression并输出
#{ expression }#{ expression }数字计算#{ expression ;format}安格式输出数字format为M和m M表示小数点后最多的位数,m表示小数点后最少的位数如#{121.2322;m2M2}输出121.23
扩展函数
函数名含义及语法
startswith判断字符串是否以指定的字符串开头startswith(“abc","”)返回布尔型
currentdate获取当前日期时间调用举例currentdate(‘yyyy-MM-dd’) 参数为java 日期格式
dateformat日期格式化 dateformat(date,‘yyyy/MM/dd’) date为java.util.Date类型 返回字符串
wordfirstlower多个单词首字符变小写 调用举例:wordfirstlower(‘Word1_Word2’,‘_’) 第二个参数为单词分隔符
wordfirstupper多个单词首字符变大写 调用举例:wordfirstupper(‘Word1_Word2’,‘_’) 第二个参数为单词分隔符
uppercase字符串转换为大写 调用举例:uppercase(“test”)
endswith判断字符串以什么结束 调用示例:endwith(“xxxx”,“x”) 返回布尔型
endswithignorecase忽略大小写判断字符串以什么结束 调用示例:endswithignorecase(“xxxx”,“x”) 返回布尔型
startswithignorecase忽略大小写判断字符串以什么开始调用示例:startswithignorecase(“xxxx”,“x”) 返回布尔型
substring字符串截断 调用示例:substring(“abc”,1)返回"bc",substring(“abcd”,1,2)返回"bc"
index字符串查找函数 调用示例:index(“abc”,“a”)返回0,index(“abc”,“bc”)返回1
splitstring字符串拆分函数调用示例:splitstring(“a,b,c”,“,”)返回list对象,包含a,b,c三个元素
filecontentfind文件内容查找函数 调用示例:filecontentfind(“c:\1.txt”,“abc”) 存在返回true;否则返回false
trim截断字符串前后空格和回车函数 调用示例:trim(" abc “)返回"abc”
randomnumber产生随机数 调用示例:randomnumber(8,10)返回31302931;randomnumber(8,16)返回A7C2F7C2 第一个参数指定返回随机数位数,第二个参数为进位
createmap新建map对象 调用示例:createmap()
mapcontain检查哈希表是否包含指定的key 调用示例:<#if mapcontain(map,“key”)> </#if>
mapget根据key获取哈希表值调用示例:<#assign value = mapget(map,“key”)/>
mapkeys获取哈希表key列表 ,调用示例:<#list mapkeys(map) as key> @{createTextFile(“${xwlrootpath}${key}\page\folder.json”,false)} <#assign indexContent=mapget(map,key)> {“title”:“”,“index”:[${indexContent?substring(1,indexContent?length)}],“hidden”:false,“iconCls”:“”}</#assign> @{closeFile()} </#list>
stringformat字符串格式化 用法:stringformat(参数格式,对象1,对象2,…),例子:stringformat(“%s%d”,“测试”,1)
toint字符串转整型 用法:toint(字符串)
getshortuuid获取短的唯一数用法:getshortuuid(10) 10表示产生随机数位数 如果随机数位数为10位,10万两次 没重复,大于10万次没验证
fileisexist判断文件是否存在 用法:fileIsexist(文件名)
readtextfile读文件 ${readtextfile(‘filename’,‘UTF-8’)}
stringinsert字符插入 用法:stringInsert(字符串,插入字符串,插入位置)
stringreverse字符倒查 用法: stringreverse(字符串,被搜字符串,结束位置) 返回:找到,返回>=0位置;否则返回-1
writetextfile写入文本文件 用法:wirtetextfile(文件名,内容,字符集)
createdir创建目录 用法:createdir(‘c:\temp’)
httpget发起http get请求 例子:httpget(url)
httppost发起一个http post 请求例子:httppost(url,postDataJson),参数postDataJson为body的,格式为json

Demo例子工程下载
这个demo是上面的例子导出,开发者可以导入平台,练手。

导入操作如下:
在这里插入图片描述
这个工具理论上可以生成任意平台代码(只要是文本格式的),如果你觉得配置模版麻烦,可以联系我(lita2lz)帮您配置! 祝您使用愉快!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值