@chenpbh
2018-10-31T02:45:53.000000Z
字数 10356
阅读 600
公共文档| 版本 | 日期 | 人员 | 变更内容 |
|---|---|---|---|
| 1.1 | 2017-08-02 | 陈鹏 | 版本初定义 |
| 1.2 | 2017-09-06 | 陈鹏 | 增加字典格式化 |
| 1.3 | 2017-11-22 | 陈鹏 | 增加名称格式化 |
| 1.4 | 2018-03-05 | 陈鹏 | 增加开发规范章节,介绍了权限控制、缓存处理、日志采集 |
| 1.6 | 2018-03-06 | 陈鹏 | 增加系统自动化工具章节 |
| 1.7 | 2018-04-10 | 陈鹏 | 增加导出文件的规范和流程 |
框架基于Spring Boot进行开发,目前主要使用了如下的相关组件:

需要注意几点:
在此与test_demo表为例,子系统名即为:test,相关类的目录规范如下
二次开发自定义编写的脚本,样式,还有引用的图片,必须按规定放在/resources/static/extend目录。引用的第三方前端组件,按规定放到/resources/static/frame目录

表名和字段名,统一采用小写+下划线,并且应该是有意义而且易于理解的,最好是能够表达字段含义的英文字母。
注意,所有的表和字段都必须填写相关注释。
base_user,则表示基础子系统下的用户表。u将对应mapper.xml中的aliasid varchar(36)name,长度视具体情况而定
框架支持生成搜索条件的注释,比如注释字段为用户名|action:search#lis实体类需要继承TailBean类来实现混合模型(实体+Map)的特性。关于混合模型使用的规则,数据表字段直接在实体中定义,联表查询的字段,用map获取。
混合模型需要Mapper.xml的支持,需要配置resultMap
u.get("roleIds")获取roleIds的值


| 序号 | 属性名 | 描述 | 是否必须 | 默认值 | 示例 |
|---|---|---|---|---|---|
| 1 | name | select选择框名称 | 是 | 无 | status |
| 2 | ds | 数据源,dict:字典,sql:sql | 是 | 无 | sql |
| 3 | key | 键值,若ds=dict,key对应字典管理里的编码,或ds=sql,对应xml里的sqlid | 是 | 无 | base.RoleMapper.roles |
| 4 | value | 当前值 | 是 | ||
| 5 | verify | 验证规则名 | 否 |
如果ds=sql,那么sql应该固定返回val和name字段,比如select id as val,name as name from xxxx
示例
<div class="layui-form-item"><label class="layui-form-label">状态</label><div class="layui-input-block"><@core.select name="status" ds="dict" key="USER_STATE" value="1" /></div></div>
| 序号 | 属性名 | 描述 | 是否必须 | 默认值 | 示例 |
|---|---|---|---|---|---|
| 1 | name | radio名称 | 是 | 无 | status |
| 2 | ds | 数据源,dict:字典,sql:sql | 是 | 无 | sql |
| 3 | key | 键值,若ds=dict,key对应字典管理里的编码,或ds=sql,对应xml里的sqlid | 是 | 无 | base.RoleMapper.roles |
| 4 | value | 当前值 | 是 | ||
| 5 | verify | 验证规则名 | 否 |
如果ds=sql,那么sql应该固定返回val和name字段,比如select id as val,name as name from xxxx
| 序号 | 属性名 | 描述 | 是否必须 | 默认值 | 示例 |
|---|---|---|---|---|---|
| 1 | name | checkbox名称 | 是 | 无 | status |
| 2 | ds | 数据源,dict:字典,sql:sql | 是 | 无 | sql |
| 3 | key | 键值,若ds=dict,key对应字典管理里的编码,或ds=sql,对应xml里的sqlid | 是 | 无 | base.RoleMapper.roles |
| 4 | value | 当前值,支持多个值,用逗号“,”分隔 | 是 | ||
| 5 | verify | 验证规则名 | 否 |
如果ds=sql,那么sql应该固定返回val和name字段,比如select id as val,name as name from xxxx
| 序号 | 属性名 | 描述 | 是否必须 | 默认值 | 示例 |
|---|---|---|---|---|---|
| 1 | name | select选择框名称 | 是 | status | |
| 2 | value | 当前值,支持多个值 | 是 | 1,2 | |
| 3 | display | 显示文本 | 否 | 程序员,前端工程师 | |
| 4 | refer | 引用页面dom的ID,在页面为隐藏域,开发人员不需要手动增加隐藏域 | 是 | roleIds | |
| 5 | key | sql,对应xml里的sqlid | 是 | base.RoleMapper.roles | |
| 6 | args | 附加参数 | 是 | k1:v1,k2:v2 | |
| 7 | title | 标题 | 是 | 请选择角色 | |
| 8 | width | 长度,单位为px | 否 | 500px | 500px |
| 9 | height | 高度,单位为px | 否 | 600px | 600px |
| 11 | multiple | 是否多选 | 否 | false | false |
| 12 | verify | 验证规则名 | 否 | ||
| 13 | queryParam | 明文传参到ftl页面 | 否 |
sql应该固定返回id和name字段,比如select id as id,name as name from xxxx
<div class="layui-form-item"><label class="layui-form-label">角色</label><div class="layui-input-block"><@core.dialog name="roleNames" key="base.RoleMapper.treeNodes" value="" multiple=true refer="roleIds" title="请选择角色" height="600px" /></div></div>
| 序号 | 属性名 | 描述 | 是否必须 | 默认值 | 示例 |
|---|---|---|---|---|---|
| 1 | name | select选择框名称 | 是 | status | |
| 2 | value | 当前值,支持多个值 | 是 | 1,2 | |
| 3 | display | 显示文本 | 否 | 程序员,前端工程师 | |
| 4 | refer | 引用页面dom的ID,在页面为隐藏域,开发人员不需要手动增加隐藏域 | 是 | roleIds | |
| 5 | key | sql,对应xml里的sqlid | 是 | base.RoleMapper.roles | |
| 6 | args | 附加参数 | 是 | ||
| 7 | title | 标题 | 是 | 请选择角色 | |
| 8 | width | 长度,单位为px | 否 | 500px | 500px |
| 9 | height | 高度,单位为px | 否 | 600px | 600px |
| 11 | multiple | 是否多选 | 否 | false | false |
| 12 | verify | 验证规则名 | 否 |
sql应该固定返回id和name字段,比如select id as id,name as name from xxxx
<div class="layui-form-item"><label class="layui-form-label">父节点</label><div class="layui-input-block"><@core.tree name="parentName" key="base.AreaMapper.treeNodes" value="${(obj.parentId)!}" display="${(obj.tails.parentName)!}" multiple=true refer="parentId" title="请选择上级区域"/></div></div>
| 序号 | 属性名 | 是否必填 | 描述 | 示例 |
| 1 | lngName | 是 | 经度html元素名称 | lng |
| 2 | latName | 是 | 纬度html元素名称 | lat |
| 3 | lng | 否 | 当前经度值 | 0 |
| 4 | lng | 否 | 当前纬度值 | 0 |
| 5 | verifyLngName | 否 | 经度校验规则名称 | |
| 6 | verifyLatName | 否 | 纬度校验规则名称 |
<div class="layui-form-item"><@core.lngLat lngName="lng" latName="lat" lng="${(obj.lng)!0}" lat="${(obj.lat)!0}" addressFrom="address"/><div>

| 序号 | 属性名 | 描述 | 是否必须 | 默认值 | 示例 |
|---|---|---|---|---|---|
| 1 | code | 字典编码 | 是 | 无 | BASE_EMPLOYEE_IS_USE |
| 2 | key | 字段名,对应column的field名 | 是 | 无 | inUser |
示例
<@formatter.dict code='BASE_EMPLOYEE_IS_USE' key='isUse'/>#表格{field: 'isUse', title: '权限状态', width: 120, templet:"#isUse"},

此标签主要用于列表格式化显示,另外`DataLoader.loadNames(Object)`方法可用于在编辑、查看页面,将外键关联的名称显示出来,而不必通过关联表查询。同样在处理导出时,调用此方法,也可以将相关数据查询出来。
| 序号 | 属性名 | 描述 | 是否必须 | 默认值 | 示例 |
|---|---|---|---|---|---|
| 1 | key | 字段名,对应column的field名 | 是 | 无 | roleIds |
| 2 | url | 点击弹出页面,一般为查看详情页面,若url为空,width和height可为空 | 否 | 无 | /role/view |
| 3 | width | 窗口宽度 | 否 | 无 | 500px |
| 4 | height | 窗口高度 | 否 | 无 | 500px |
1、 在domain实体的相应get方法增加@LinkName注解
@LinkName(table = "base_role")public String getRoleIds() {return roleIds;}
2、 在service层显示调用DataLoader.loadNames(Object)方法(目前传参支持List、LayModel和TailBean的子类)
LayModel layModel = new LayModel();ayModel.setCount(pm.getTotal());layModel.setData(pm.getRows());DataLoader.loadNames(layModel);return layModel;
3、在前端页面使用标签
<@formatter.name key='roleIds' url='/role/update' width="600px" height='500px'/><script>#表格列定义{field: 'roleNames', title: '角色',templet: '#roleIds'},</script>

此章节主要约定了开发过程中的规范,避免开发人员在开发过程中,出现遗漏和不规范的情况。
该框架采用 Apache Shiro实现用户访问权限的控制,支持前后端的权限控制。其中前端采用的是Freemarker标签方式,而后台是需要在控制器方法中,增加注解。而权限控制的核心元素,则是资源编码(对应模块管理中的编码)。另外,建议编码的命名为业务大模块_功能模块_动作,比如新增用户,命名为BASE_USER_ADD
通过代码生成器生成的代码,已经生成了对应的资源信息。如果是单独开发的,需要必须手工增加资源信息,并在前后端代码中,加以控制。我们约定,在下面章节点,我们将以新增用户的权限处理来举例说明,权限是如何控制的

前端通过freemarker标签进行控制,常用的标签包括:
- hasPermission: 验证当前用户是否拥有该权限
- authenticated: 验证用户是否已经登录系统
- lacksPermission:验证当前用户不拥有某种权限,与hasPermission标签是相对的
1、 为了对新增用户进行权限控制,首先我们需要在前台页面/base/user/list.ftl页面中,html的新增按钮元素用freemarker标签已经包括起来
<@shiro.hasPermission name="BASE_USER_ADD"><a class="layui-btn btn-add btn-default" id="btn-add">新增</a></@shiro.hasPermission>
2、然后,出于安全和隐私控制,我们同时将新增相关的JS代码也进行控制
<@shiro.hasPermission name="BASE_USER_ADD">//绑定新增按钮事件bindAdd('/base/user/add','新增',"540px","530px",gridObj);</@shiro.hasPermission>
在控制器通过注解@RequiresPermissions控制权限(注意,功能关联的其他请求,也应该加上注解进行控制,常用的就是包括页面和Ajax调用)
/*** 新增页面* @param model* @return*/@GetMapping(value = "/add")@RequiresPermissions("BASE_USER_ADD")@SLog(action = "新增页面")public String add(Model model) {return BASE+"add";}/*** 保存* @param job* @return*/@PostMapping(value = "/add")@ResponseBody@RequiresPermissions("BASE_USER_ADD")@SLog(action = "新增用户")public AppBean add(User user){userService.insert(user);return new AppBean("增加成功");}
系统日志目前是通过提供自定义的注解方式实现,提供的注解为下:
* Module: 模块名,比如用户管理
* SLog: 动作名,比如新增
下面我们以新增用户举例:
1、需要在控制类声明上加上@Module注解
/*** 用户管理* @author 陈鹏* @Time 2017-07-04 18:29:52*/@Module(name = "用户管理")@Controller@RequestMapping(value = "/user")
2、在新增方法加上注解SLog注解,由于新增涉及到两个请求,所以此处在这两个方法都加上SLog注解
/*** 新增页面* @param model* @return*/@GetMapping(value = "/add")@RequiresPermissions("BASE_USER_ADD")@SLog(action = "新增页面")public String add(Model model) {return BASE+"add";}/*** 保存* @param job* @return*/@PostMapping(value = "/add")@ResponseBody@RequiresPermissions("BASE_USER_ADD")@SLog(action = "新增用户")public AppBean add(User user){userService.insert(user);return new AppBean("增加成功");}
3、日志采集情况可以通过日志管理中查看。

框架对于Mybatis二级缓存进行扩展,由Mybatis默认的内存改为了Redis。使用方式如下:
1、配置文件,在application.yml的redis配置中,增加mybatis缓存配置spring.redis.mybatis.index,即指定二级缓存放的redis的db
spring:redis:database: 12host: 192.168.6.141port: 6380password: 123456mybatis:index: 13
2、需要在mybatis的mapper的xml开启二级缓存支持,即cache的type为com.bitnei.cloud.common.cache.MybatisRedisCache
<mapper namespace="com.bitnei.cloud.base.mapper.JobMapper"><cache type="com.bitnei.cloud.common.cache.MybatisRedisCache" eviction="LRU" flushInterval="6000000" size="1024" readOnly="false"/>...........</mapper>
3、缓存管理,为了避免出现开发人员使用不当导致内存不同步的情况,框架提供了mybatis二级缓存的管理功能,支持查看和清理等操作。

在这里,以导出系统日志为示例:
1、编写mysql语句: 正常情况下,我们可以直接使用数据列表的sql语句,如下
<!-- 分页查询 --><select id="pagerModel" resultMap="tailResults" parameterType="java.util.HashMap">select<include refid="moreColumns"/>frombase_log logwhere1=1<if test="username != null">and log.username like "%"#{username}"%"</if><if test="moudle != null">and log.moudle like "%"#{moudle}"%"</if><if test="action != null">and log.action like "%"#{action}"%"</if>ORDER BY log.TIME DESC</select>
2、业务层采集数据: 如果模块代码是通过代码生成工具生成的,正常业务层代码是不需要改动的
@Overridepublic void export() {List list = findBySqlId("pagerModel",ServletUtil.getQueryParams());String srcBase = RequestContext.class.getResource("/templates/").getFile();String srcFile = srcBase +"module/base/log/export.xls";ExcelData ed = new ExcelData();ed.setTitle("日志管理");ed.setExportTime(DateUtil.getNow());ed.setData(list);String outName = String.format("%s-导出-%s.xls", "日志管理", DateUtil.getShortDate());EasyExcel.renderResponse(srcFile,outName,ed);}
3、配置excel模板: 框架的excel模板为export.xls,excel文件中共有两个sheet,一个为列定义,一个参数配置。详细的excel配置,将在下面章节进行描述。

列定义采用Freemarker实现,支持freemarker指令表达式。在这里,框架默认对象名为obj,比如Log实体定义为
public class Job extends TailBean {/** **/private String id;/** 任务名称 **/private String name;/** 类名 **/private String className;/** 方法名 **/private String methodName;/** Cron表达式 **/private String cron;/** 上次执行时间 **/private String lastJobTime;/** 最后异常信息 **/private String lastException;/** 启用状态 **/private Integer status;/** 耗时 **/private Integer takeTime;/** 创建时间 **/private String createTime;/** 创建人 **/private String createBy;/** 更新时间 **/private String updateTime;/** 更新人 **/private String updateBy;/** 备注 **/private String note;....getter/setter........}
然后在excel中就可以根据实现情况,进行配置模板。另外,导出文件的样式,也可以直接调整。导出文件,样式和模板的保持一致。
目前框架支持的参数项如下:
日志管理-20180410154150.xls或日志管理-20180410154150.zip代码生成依赖于数据库表的注释,详细注释规则参见# 2 数据库规范。
