大家好,我是田哥
前面我跟大家分享小伙伴正在用它来练练手,其中,有个非常聪明的小伙伴发现了个问题:大量类似的代码,差不多一样的CRUD.
医院项目里确实存在大量的CRUD,其实嘛,医院项目毕竟还是重点偏向于后台管理系统,不过,我前段时间增加了外网预约挂号功能,但再怎么说,也还是偏向于后台,比较主要的工作量都在后台。做过后台系统的都知道,后台系统会存在大量的CRUD。但话又说回来,不是后台系统也存在大部分功能都是CRUD的。
于是,来找我能不能搞一个就可以了,其他的复制过去,再改改就成了。
想想也是哈,那么多重复的CRUD,只是字段不同而已。
但,也别小看这个改字段的过程,或许因为你改错一个、少改一个字段,你需要花时间去排查的。
所以,我觉得很有必要搞一个代码生成工具,一次性把controller、service、mappper代码生成,甚至把HTML、数据导出、权限控制、swagger文档都给生成出来。
演示
我们只要创建好我们的业务表,即可搞定代码生成。比如:我们来建个表:sys_test,三个字段,主键自增。
CREATE TABLE `sys_test` (
`id` int NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
然后到代码生成页面,把系统数据库表全部查出来。
点击生成
按钮,或者可以选多张表,一次性全部生成好。
技术栈:Spring Boot +shiro+thymeleaf+MyBatis-plus+MySQL
我们来看看,这个工具生成出来的代码长什么样。
controller层:页面跳转、swagger API 、分页查询....
@Controller
@RequestMapping("/")
public class SysTestController {
@Autowired
private SysTestService sysTestService;
/**
* 跳转到页面
*/
@GetMapping("/index/sysTest")
public String sysTest() {
return "systest/list";
}
@ApiOperation(value = "新增")
@PostMapping("sysTest/add")
@RequiresPermissions("sysTest:add")
@ResponseBody
public DataResult add(@RequestBody SysTestEntity sysTest){
sysTestService.save(sysTest);
return DataResult.success();
}
@ApiOperation(value = "删除")
@DeleteMapping("sysTest/delete")
@RequiresPermissions("sysTest:delete")
@ResponseBody
public DataResult delete(@RequestBody @ApiParam(value = "id集合") List<String> ids){
sysTestService.removeByIds(ids);
return DataResult.success();
}
@ApiOperation(value = "更新")
@PutMapping("sysTest/update")
@RequiresPermissions("sysTest:update")
@ResponseBody
public DataResult update(@RequestBody SysTestEntity sysTest){
sysTestService.updateById(sysTest);
return DataResult.success();
}
@ApiOperation(value = "查询分页数据")
@PostMapping("sysTest/listByPage")
@RequiresPermissions("sysTest:list")
@ResponseBody
public DataResult findListByPage(@RequestBody SysTestEntity sysTest){
Page page = new Page(sysTest.getPage(), sysTest.getLimit());
LambdaQueryWrapper<SysTestEntity> queryWrapper = Wrappers.lambdaQuery();
//查询条件示例
//queryWrapper.eq(SysTestEntity::getId, sysTest.getId());
IPage<SysTestEntity> iPage = sysTestService.page(page, queryWrapper);
return DataResult.success(iPage);
}
}
entity代码:
@Data
@TableName("sys_test")
public class SysTestEntity extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
@TableId("id")
private Integer id;
/**
*
*/
@TableField("name")
private String name;
/**
*
*/
@TableField("create_time")
private Date createTime;
}
mapper代码:
/**
*
* @author tiange
* @date 2022-11-15 22:15:56
*/
public interface SysTestMapper extends BaseMapper<SysTestEntity> {
}
service接口层代码:
public interface SysTestService extends IService<SysTestEntity> {
}
service实现层代码:
@Service("sysTestService")
public class SysTestServiceImpl extends ServiceImpl<SysTestMapper, SysTestEntity> implements SysTestService {
}
SysTestMapper.xml代码:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.company.project.mapper.SysTestMapper">
<!-- 可根据自己的需求,是否要使用 -->
<resultMap type="com.company.project.entity.SysTestEntity" id="sysTestMap">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="createTime" column="create_time"/>
</resultMap>
</mapper>
html代码:
<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" th:href="@{/layui/css/layui.css}"/>
<link rel="stylesheet" th:href="@{/css/custom.form.css}">
<style id="layuimini-bg-color">
</style>
<body>
<div class="panel panel-default operation" hidden>
<div class="panel-heading title"></div>
<div class="layui-card-body">
<form class="layui-form " action="" lay-filter="info" style="width: 700px;margin-top: 10px">
<input name="id" hidden/>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<input type="name" name="name" placeholder="请输入" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<input type="createTime" name="createTime" placeholder="请输入" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button type="submit" class="layui-btn" lay-submit="" lay-filter="submit">保存</button>
<button class="layui-btn layui-btn-primary" id="btn_cancel">返回</button>
</div>
</div>
</form>
</div>
</div>
<div class="table_div">
<div id="searchParam" shiro:hasPermission="sysTest:list">
<form class="layui-form ">
<div class="layui-form-item">
<div class="layui-input-inline">
<input type="text" id="key" class="layui-input" autocomplete="off" placeholder="请输入">
</div>
<div class="layui-input-inline ">
<button class="layui-btn" onclick="search()" id="search">查询</button>
<button class="layui-btn" id="export">导出全部</button>
</div>
</div>
</form>
</div>
<table class="layui-table" id="showTable" lay-filter="showTable" ></table>
</div>
<script type="text/html" id="toolbar">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="add" shiro:hasPermission="sysTest:add">添加</button>
<button class="layui-btn layui-btn-sm" lay-event="batchDeleted" shiro:hasPermission="sysTest:delete">删除</button>
</div>
</script>
<script type="text/html" id="tool">
<a class="layui-btn layui-btn-xs" lay-event="edit" shiro:hasPermission="sysTest:update">编辑</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del" shiro:hasPermission="sysTest:delete">删除</a>
</script>
</body>
</html>
<script th:inline="javascript" type="text/javascript">
var ctx = '[[@{/}]]'.replaceAll("\"", "").replace('//', '/');
</script>
<script th:src="@{/layui/layui.all.js}"></script>
<script th:src="@{/js/core.util.js}"></script>
<script>
//获取token
var token = CoreUtil.getData("access_token");
//地址栏转义token中的#号
var tokenQuery = token.replace("#", "%23");
var tableIns1;
var table = layui.table;
var form = layui.form;
var layer = layui.layer;
var $ = jQuery = layui.jquery;
var laydate = layui.laydate;
layui.use(['table', 'layer', 'laydate'], function () {
//加载table
tableIns1 = table.render({
elem: '#showTable'
, contentType: 'application/json'
, headers: {"authorization": token}
, page: true //开启分页
, url: ctx + 'sysTest/listByPage' //数据接口
, method: 'POST'
, parseData: function (res) { //将原始数据解析成 table 组件所规定的数据
return {
"code": res.code, //解析接口状态
"msg": res.msg, //解析提示文本
"count": CoreUtil.isEmpty(res.data) ? 0 : res.data.total, //解析数据长度
"data": CoreUtil.isEmpty(res.data) ? null : res.data.records //解析数据列表
}
}
, cols: [
[
{type: 'checkbox', fixed: 'left'},
{field: 'id', title: '', sort: true},
{field: 'name', title: '', sort: true},
{field: 'createTime', title: '', sort: true},
{width: 120, toolbar: "#tool", title: '操作'}
]
]
, toolbar: '#toolbar'
});
//表头工具
table.on('toolbar(showTable)', function(obj){
switch(obj.event){
case 'batchDeleted':
var checkStatus = table.checkStatus(obj.config.id);
var data = checkStatus.data;
if(data.length==0){
layer.msg("请选择要批量删除的列");
}else {
var ids = [];
$(data).each(function (index,item) {
ids.push(item.id);
});
tipDialog(ids);
}
break;
case 'add':
$(".table_div").hide();
$(".operation").show();
$(".title").html("新增");
setTimeout(function () {
form.val('info', {
"test": "test"
, "id": ""
, "name": ""
, "createTime": ""
});
}, 200);
break;
};
});
//列操作
table.on('tool(showTable)',function (obj) {
var data = obj.data;
switch (obj.event) {
case 'del':
var ids=[];
ids.push(data.id);
tipDialog(ids);
break;
case 'edit':
$(".table_div").hide();
$(".operation").show();
$(".title").html("编辑");
setTimeout(function () {
form.val('info', {
"test": "test"
, "id": data.id
, "name": data.name
, "createTime": data.createTime
});
}, 200);
break;
}
});
//导出
$('#export').on('click', function () {
//原先分页limit
var exportParams = {
limit: 10000,
key: $("#key").val()
};
CoreUtil.sendPost(ctx + "sysTest/listByPage", exportParams, function (res) {
//初始化渲染数据
if (res.data != null && res.data.records != null) {
table.exportFile(tableIns1.config.id, res.data.records, 'xls');
}
});
});
//删除
var tipDialog=function (ids) {
layer.open({
content: "确定要删除么?",
yes: function(index, layero){
layer.close(index); //如果设定了yes回调,需进行手工关闭
CoreUtil.sendDelete(ctx + "sysTest/delete",ids,function (res) {
layer.msg(res.msg, {time:1000},function () {
search();
});
});
}
});
};
//返回
$("#btn_cancel").click(function() {
$(".table_div").show();
$(".operation").hide();
return false;
});
//监听保存
form.on('submit(submit)', function(data){
if(data.field.id===undefined || data.field.id===null || data.field.id===""){
CoreUtil.sendPost(ctx + "sysTest/add",data.field,function (res) {
$(".table_div").show();
$(".operation").hide();
search();
});
}else {
CoreUtil.sendPut(ctx + "sysTest/update",data.field,function (res) {
$(".table_div").show();
$(".operation").hide();
search();
});
}
return false;
});
});
//执行查询
function search() {
//这里以搜索为例
tableIns1.reload({
where: { //设定异步数据接口的额外参数,任意设
key: $("#key").val()
}
, page: {
curr: 1 //重新从第 1 页开始
}
});
};
</script>
以上代码,既然代码生成了,我们接着就是把这些代码放到对应的包目录下就完事了,一个CRUD就搞定了。
不需要咱们动手写一行代码,是不是很爽?
我们在回头去看看医院项目,其实不管是医院项目,很多后台管理项目都是这个样,大部分功能都是CRUD。另外,这个代码生成还有其他功能:导出Excel功能、权限控制、swagger API文档。
关于后台项目,我们只要把以上这些功能搞定,剩下的工作量还能有多少?
所以嘛,如果你每天还在搞后台管理这种系统,那你完全可以使用这种方式来节约自己的时间,把更多时间留给自己学习其他技术。
按照这种方法,不说50张表,就算是100张表,你也可以轻松搞定的。
再说几句
以上说的代码生成,只能说实现了一些简单的CRUD,因为业务中肯定没那么简单的,很多一个增加功能可能会关联很多表。
比如说:挂号功能,哪个科室?挂号类型?医生是否在岗、每天每个时间段医生预约人数是否已满?等问题。
最后来说说医院项目,医院项目的外网预约挂号功能设计和开发已经完成。
同时,医院项目拆分成多个项目,也就是很多人所谓的分布式项目,目前项目基本结构已搞定。
新医院项目技术栈:
Spring Boot + Dubbo +Nacos +RabbitMQ +Redis +MyBatis+MySQL+Thmeleaf+shiro
项目整体结构,暂定,后期可能会变动:
后面继续迭代,争取把分库分表、分布式事务也落实到这个项目中来。