# 案例演示

# 文件概述

在代码生成器章节中已经详细说讲解了单个模块的生成方式,包括自动生成手动生成两种,根据自己的喜好选择一种即可,本章节中将具体的举例说明生成好的模块如何去优化及相关细节调整以便达到高质量开发模块的要去,首先我们要知道单个模块中具体包括哪些个文件,文件有:模块常量控制器实体对象Mapper文件Query查询文件IService接口文件IServiceImpl接口实现文件列表ListVo文件Index.html列表文件edit.html编辑文件JS文件,我们以职级(level)来举例说明:

# 常量文件(LevelConstant.java)

代码生成时程序会分析数据表中是否存在枚举的字段,如果存在则会解析参数并生成枚举配置参数,如下所述:状态:1正常 2停用

package com.javaweb.system.constant;

import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 职级 模块常量
 * </p>
 *
 * @author 鲲鹏
 * @since 2020-04-20
 */
public class LevelConstant {

    /**
     * 状态
     */
    public static Map<Integer, String> LEVEL_STATUS_LIST = new HashMap<Integer, String>() {
        {
            put(1, "正常");
            put(2, "停用");
        }
    };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 控制器文件(LevelController.java)

在代码生成时,程序会动态解析自定义模板标签,替换参数,生成单模块管理中所需的所有方法,如:列表方法(list)添加方法(add)编辑方法(update)详情方法(edit)删除方法(delete)等等,程序模板会动态解析,在审核模块管理中,很多会有状态(status)的字段,此时如果有此字段,会动态生成对应的设置状态的方法设置状态(setStatus),详情如下:

package com.javaweb.system.controller;


import com.javaweb.common.utils.JsonResult;
import com.javaweb.common.annotation.Log;
import com.javaweb.common.enums.BusinessType;
import com.javaweb.system.entity.Level;
import com.javaweb.system.query.LevelQuery;
import com.javaweb.system.service.ILevelService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import com.javaweb.common.common.BaseController;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 职级 控制器
 * </p>
 *
 * @author 鲲鹏
 * @since 2020-04-20
 */
@Controller
@RequestMapping("/level")
public class LevelController extends BaseController {

    @Autowired
    private ILevelService levelService;

    /**
     * 获取数据列表
     *
     * @param query 查询条件
     * @return
     */
    @RequiresPermissions("sys:level:list")
    @ResponseBody
    @PostMapping("/list")
    public JsonResult list(LevelQuery query) {
        return levelService.getList(query);
    }

    /**
     * 添加记录
     *
     * @param entity 实体对象
     * @return
     */
    @RequiresPermissions("sys:level:add")
    @Log(title = "职级", businessType = BusinessType.INSERT)
    @ResponseBody
    @PostMapping("/add")
    public JsonResult add(@RequestBody Level entity) {
        return levelService.edit(entity);
    }

    /**
     * 修改记录
     *
     * @param entity 实体对象
     * @return
     */
    @RequiresPermissions("sys:level:update")
    @Log(title = "职级", businessType = BusinessType.UPDATE)
    @ResponseBody
    @PostMapping("/update")
    public JsonResult update(@RequestBody Level entity) {
        return levelService.edit(entity);
    }

    /**
     * 获取记录详情
     *
     * @param id    记录ID
     * @param model 模型
     * @return
     */
    @Override
    public String edit(Integer id, Model model) {
        Map<String, Object> info = new HashMap<>();
        if (id != null && id > 0) {
            info = levelService.info(id);
        }
        model.addAttribute("info", info);
        return super.edit(id, model);
    }

    /**
     * 删除记录
     *
     * @param id 记录ID
     * @return
     */
    @RequiresPermissions("sys:level:delete")
    @Log(title = "职级", businessType = BusinessType.DELETE)
    @Override
    public JsonResult delete(Integer id) {
        return levelService.deleteById(id);
    }

    /**
     * 设置状态
     *
     * @param entity 实体对象
     * @return
     */
    @RequiresPermissions("sys:level:status")
    @Log(title = "职级", businessType = BusinessType.STATUS)
    @ResponseBody
    @PostMapping("/setStatus")
    public JsonResult setStatus(@RequestBody Level entity) {
        return levelService.setStatus(entity);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

总结:

  • 代码生成器在生成代码的时候动态解析模板,生成了权限节点控制参数,如:@RequiresPermissions("sys:level:status"),其中的参数sys:level:status就是菜单管理模块中权限标识,需要在添加菜单时给每个节点添加相应的权限标识,以便实现正常的权限访问控制。
  • AOP日志注解:此处需要说明的是@Log注解是框架中自定义的操作日志注解,基于AOP切面思想编写的类似插件的东西,操作日志框架会已线程异步的方式去创建并入库,以便遇到问题后进行溯源排查问题。
  • 这里需要重点说明的是setStatus方法并不是每个模块都有,只有数据表有status字段来标识记录状态时,生成代码时才会根据参数分析创建这个方法,反之模块不会出现设置状态这一方法,常规模块中只有列表添加编辑删除等方法,如需额外的方法实现业务,需要开发人员自定义方法去实现相应的功能;

# 实体文件(Level.java)

实体类继承了BaseEntity基类文件,基类文件中包括记录(ID)创建人(createUser)创建时间(createTime)更新人(updateUser)更新时间(updateTime)等基本常规字段,避免每个实体重复抒写。

package com.javaweb.system.entity;

import com.baomidou.mybatisplus.annotation.TableName;

import java.math.BigDecimal;
import java.util.Date;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.javaweb.common.common.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;


/**
 * <p>
 * 职级
 * </p>
 *
 * @author 鲲鹏
 * @since 2020-04-20
 */
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("sys_level")
public class Level extends BaseEntity {

    /**
     * 职级名称
     */
    private String name;

    /**
     * 状态:1正常 2停用
     */
    private Integer status;

    /**
     * 显示顺序
     */
    private Integer sort;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# Mapper文件(LevelMapper.java)

Mapper类继承了BaseMapper基类,Mapper层中提供了大量API接口可供调用,简化了开发,提高了开发效率。

package com.javaweb.system.mapper;

import com.javaweb.system.entity.Level;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * <p>
 * 职级 Mapper 接口
 * </p>
 *
 * @author 鲲鹏
 * @since 2020-04-20
 */
public interface LevelMapper extends BaseMapper<Level> {

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 查询文件(LevelQuery.java)

很多数据列表中会有各种各样的查询条件,程序在创建文件时会动态分析数据表中的常用筛选字段以及一些枚举字段进行动态生成查询条件,比如:名称(name)标题(title)类型(type)状态(status)等等。

package com.javaweb.system.query;

import com.javaweb.common.common.BaseQuery;
import lombok.Data;

/**
 * <p>
 * 职级查询条件
 * </p>
 *
 * @author 鲲鹏
 * @since 2020-04-20
 */
@Data
public class LevelQuery extends BaseQuery {

    /**
     * 职级名称
     */
    private String name;

    /**
     * 状态:1正常 2停用
     */
    private Integer status;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 接口文件(ILevelService.java)

接口文件集成基类IBaseService方法,在基类中封装了模块管理中常用的方法,前面章节中已经做了详细的说明,在此不做过多的累赘描述。

package com.javaweb.system.service;

import com.javaweb.system.entity.Level;
import com.javaweb.common.common.IBaseService;

/**
 * <p>
 * 职级 服务类
 * </p>
 *
 * @author 鲲鹏
 * @since 2020-04-20
 */
public interface ILevelService extends IBaseService<Level> {

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 接口实现文件(LevelServiceImpl.java)

接口实现中主要包括了增删改查一系列功能方法的具体实现,比如列表添加修改删除查询状态设置(需要时才会有此方法)等等,此文件集成基类BaseServiceImpl,前面章节有详细的说明,有不理解请看前面章节。

package com.javaweb.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.javaweb.common.common.BaseQuery;
import com.javaweb.shiro.common.BaseServiceImpl;
import com.javaweb.common.utils.JsonResult;
import com.javaweb.common.utils.StringUtils;
import com.javaweb.system.constant.LevelConstant;
import com.javaweb.system.entity.Level;
import com.javaweb.system.mapper.LevelMapper;
import com.javaweb.system.query.LevelQuery;
import com.javaweb.system.service.ILevelService;
import com.javaweb.system.utils.AdminUtils;
import com.javaweb.system.vo.LevelListVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 职级 服务实现类
 * </p>
 *
 * @author 鲲鹏
 * @since 2020-04-20
 */
@Service
public class LevelServiceImpl extends BaseServiceImpl<LevelMapper, Level> implements ILevelService {

    @Autowired
    private LevelMapper levelMapper;

    /**
     * 获取数据列表
     *
     * @param query 查询条件
     * @return
     */
    @Override
    public JsonResult getList(BaseQuery query) {
        LevelQuery levelQuery = (LevelQuery) query;
        // 查询条件
        QueryWrapper<Level> queryWrapper = new QueryWrapper<>();
        // 职级名称
        if (!StringUtils.isEmpty(levelQuery.getName())) {
            queryWrapper.like("name", levelQuery.getName());
        }
        // 状态:1正常 2停用
        if (levelQuery.getStatus() != null) {
            queryWrapper.eq("status", levelQuery.getStatus());
        }
        queryWrapper.eq("mark", 1);
        queryWrapper.orderByDesc("id");

        // 查询数据
        IPage<Level> page = new Page<>(levelQuery.getPage(), levelQuery.getLimit());
        IPage<Level> data = levelMapper.selectPage(page, queryWrapper);
        List<Level> levelList = data.getRecords();
        List<LevelListVo> levelListVoList = new ArrayList<>();
        if (!levelList.isEmpty()) {
            levelList.forEach(item -> {
                LevelListVo levelListVo = new LevelListVo();
                // 拷贝属性
                BeanUtils.copyProperties(item, levelListVo);
                // 状态描述
                if (levelListVo.getStatus() != null && levelListVo.getStatus() > 0) {
                    levelListVo.setStatusName(LevelConstant.LEVEL_STATUS_LIST.get(levelListVo.getStatus()));
                }
                // 添加人名称
                if (levelListVo.getCreateUser() > 0) {
                    levelListVo.setCreateUserName(AdminUtils.getName((levelListVo.getCreateUser())));
                }
                // 更新人名称
                if (levelListVo.getUpdateUser() > 0) {
                    levelListVo.setUpdateUserName(AdminUtils.getName((levelListVo.getUpdateUser())));
                }
                levelListVoList.add(levelListVo);
            });
        }
        return JsonResult.success("操作成功", levelListVoList, data.getTotal());
    }

    /**
     * 获取记录详情
     *
     * @param id 记录ID
     * @return
     */
    @Override
    public Object getInfo(Serializable id) {
        Level entity = (Level) super.getInfo(id);
        return entity;
    }

    /**
     * 添加或编辑记录
     *
     * @param entity 实体对象
     * @return
     */
    @Override
    public JsonResult edit(Level entity) {
        return super.edit(entity);
    }

    /**
     * 删除记录
     *
     * @param id 记录ID
     * @return
     */
    @Override
    public JsonResult deleteById(Integer id) {
        if (id == null || id == 0) {
            return JsonResult.error("记录ID不能为空");
        }
        Level entity = this.getById(id);
        if (entity == null) {
            return JsonResult.error("记录不存在");
        }
        return super.delete(entity);
    }

    /**
     * 设置状态
     *
     * @param entity 实体对象
     * @return
     */
    @Override
    public JsonResult setStatus(Level entity) {
        if (entity.getId() == null || entity.getId() <= 0) {
            return JsonResult.error("记录ID不能为空");
        }
        if (entity.getStatus() == null) {
            return JsonResult.error("记录状态不能为空");
        }
        return super.setStatus(entity);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

# 列表ListVo文件(LevelListVo.java)

列表ListVo文件主要是后端返回给前端UI页面的一系列字段信息,通常情况下字段信息跟实体Entity类中字段相同,为了保持实体类型的干净,所以独立一个Vo类,如需返回其他字段可以直接在Vo类中进行添加,这些字段信息都会返回给前端的JS文件进行动态解析,下面我们会详细的说明javaweb_level.js具体内容。

package com.javaweb.system.vo;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.math.BigDecimal;
import java.util.Date;

/**
 * <p>
 * 职级列表Vo
 * </p>
 *
 * @author 鲲鹏
 * @since 2020-04-20
 */
@Data
public class LevelListVo {

    /**
     * 职级ID
     */
    private Integer id;

    /**
     * 职级名称
     */
    private String name;

    /**
     * 状态:1正常 2停用
     */
    private Integer status;

    /**
     * 状态描述
     */
    private String statusName;

    /**
     * 显示顺序
     */
    private Integer sort;

    /**
     * 添加人
     */
    private Integer createUser;

    /**
     * 添加人名称
     */
    private String createUserName;

    /**
     * 创建时间
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="GMT+8")
    private Date createTime;

    /**
     * 更新人
     */
    private Integer updateUser;

    /**
     * 更新人名称
     */
    private String updateUserName;

    /**
     * 更新时间
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="GMT+8")
    private Date updateTime;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

# 数据列表文件(index.html)

前端数据列表文件也是生成器动态生成,生成时会动态解析各种所需组件,有关组件内容请看前面章节,这里需要特别说明的是状态(status)这个下拉选择的组件参数来源问题,前面常量文件中我们有提到status这个字段生成的枚举配置文件,这里就用到,文件刚生成时状态下拉选择形式是<widget:singleSelect name="status|0|状态|name|id" data="1:正常,2=停用" value="0"/>,那么为什么会变成现在这个样子呢?肯定是我们认为改的,这就是前面所说的优化和细节调整了,这里的参数${LEVEL_STATUS_LIST}就是前面常量中的枚举参数了,当前如何让这个参数被程序自动解析,我们还需要在ThymeleafConfig模板配置文件中加入一行代码vars.put("LEVEL_STATUS_LIST", LevelConstant.LEVEL_STATUS_LIST);,在入口加入配置文件解析后,这个参数才会生效,如果不明白原理的小伙伴可以找度娘,这里所用到的组件有,如下:

  • <widget:singleSelect name="status|0|状态|name|id" th:data="${LEVEL_STATUS_LIST}" value="0"/>
  • <widget:btnQuery name="查询"/>
  • <widget:btnAdd name="添加职级"/>
  • <widget:btnDAll name="批量删除"/>
  • <widget:btnEdit name="编辑"/>
  • <widget:btnDel name="删除"/> 组件这里大家有不明白的地方请看前面的章节《UI组件》
<html layout:decorator="public/layout" xmlns:miguo="http://www.w3.org/1999/html">
<div layout:fragment="content">

    <!-- 表格工具栏 -->
    <form class="layui-form toolbar">
        <div class="layui-form-item">
            <div class="layui-inline">
                <div class="layui-input-inline">
                    <input type="text" name="name" placeholder="请输入职级名称" autocomplete="off" class="layui-input">
                </div>
            </div>
            <!-- 状态下拉单选 -->
            <div class="layui-inline">
                <div class="layui-input-inline">
                    <!-- 下拉单选 -->
                    <widget:singleSelect name="status|0|状态|name|id" th:data="${LEVEL_STATUS_LIST}" value="0"/>
                </div>
            </div>
            <div class="layui-inline">
                <div class="layui-input-inline" style="width: auto;">
                    <widget:btnQuery name="查询"/>
                    <widget:btnAdd name="添加职级"/>
                    <widget:btnDAll name="批量删除"/>
                </div>
            </div>
        </div>
    </form>

    <!-- 数据表格 -->
    <table class="layui-hide" id="tableList" lay-filter="tableList"></table>

    <!-- 表格操作列 -->
    <script type="text/html" id="toolBar">
        <widget:btnEdit name="编辑"/>
        <widget:btnDel name="删除"/>
    </script>

    <!-- 状态 -->
    <script type="text/html" id="statusTpl">
        <input type="checkbox" name="status" value="{{ d.id }}" lay-skin="switch" lay-text="正常|停用" lay-filter="status" {{ d.status == 1 ? 'checked' : '' }} >
    </script>
</div>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# 编辑表单文件(edit.html)

此文件为表单编辑页,代码生成时会动态解析根据字段及字段类型生成相应的输入框、下拉选择框、开关、图片上传等等一系列表单元素,比较好理解,此处不做过多说明。

<html layout:decorator="public/form" xmlns:miguo="http://www.w3.org/1999/html">
<div layout:fragment="content">
	<form class="layui-form model-form" action="">
		<input name="id" type="hidden" th:value="${info['id']}?:0">
		<div class="layui-form-item">
			<label class="layui-form-label">职级名称:</label>
			<div class="layui-input-block">
				<input name="name" th:value="${info['name']}" lay-verify="required" autocomplete="off" placeholder="请输入职级名称" class="layui-input" type="text">
			</div>
		</div>
		<div class="layui-form-item">
			<label class="layui-form-label">状态:</label>
			<div class="layui-input-block">
				<widget:switchCheck name="status" data="正常|停用" th:value="${info['status']} ?: 1"/>
			</div>
		</div>
		<div class="layui-form-item">
			<label class="layui-form-label">显示顺序:</label>
			<div class="layui-input-block">
				<input name="sort" th:value="${info['sort']}" lay-verify="required|number" autocomplete="off" placeholder="请输入显示顺序" class="layui-input" type="text">
			</div>
		</div>
		<widget:btnSubmit name="submit|立即保存,close|关闭"/>
	</form>
</div>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 模块JS文件(javaweb_level.js)

模块JS文件是比较重要的一个模块,这里集成了JS组件,完成了当前模块的列表展示、数据查询、添加编辑弹窗、表单提交、状态设置等一系列操作,可能感觉代码不能,就是调用几个方法,确实如此,因为大量的工作都交由JS组件去完成了,在模块中只做简单的调用,动态传入当前模块的一些参数,是不是非常的方便简洁。

/**
 * 职级
 * @auth 鲲鹏
 * @date 2020-04-20
 */
layui.use(['func'], function () {

    //声明变量
    var func = layui.func
        , $ = layui.$;

    if (A == 'index') {
        //【TABLE列数组】
        var cols = [
            {type: 'checkbox', fixed: 'left'}
            , {field: 'id', width: 80, title: 'ID', align: 'center', sort: true, fixed: 'left'}
            , {field: 'name', width: 200, title: '职级名称', align: 'center'}
            , {field: 'status', width: 100, title: '状态', align: 'center', templet: '#statusTpl'}
            , {field: 'sort', width: 100, title: '显示顺序', align: 'center'}
            , {field: 'createUserName', width: 100, title: '添加人', align: 'center'}
            , {field: 'createTime', width: 180, title: '创建时间', align: 'center'}
            , {field: 'updateUserName', width: 100, title: '更新人', align: 'center'}
            , {field: 'updateTime', width: 180, title: '更新时间', align: 'center'}
            , {fixed: 'right', width: 150, title: '功能操作', align: 'center', toolbar: '#toolBar'}
        ];

        //【渲染TABLE】
        func.tableIns(cols, "tableList");

        //【设置弹框】
        func.setWin("职级", 500, 300);

        //【设置状态】
        func.formSwitch('status', null, function (data, res) {
            console.log("开关回调成功");
        });
    }
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

总结:var cols = [];这个参数中的内容出自Layui的列数据字段展示,此处尊重Layui规范,可以根据自己的需求更改字段名称及列宽度、是否居中显示等等。

# 效果展示

至此我们的模块可以正式可以运行预览效果了,增删改查整个模块的所有功能全部完毕,下面我们直接看效果吧,请看下图:

  • 职级列表

    mixureSecure

  • 职级添加/编辑

    mixureSecure

  • 职级删除

    mixureSecure