# 案例演示

# 案例概述

在代码生成器章节中已经详细说讲解了单个模块的生成方式,包括自动生成手动生成两种,根据自己的喜好选择一种即可,本章节中将具体的举例说明生成好的模块如何去优化及相关细节调整以便达到高质量开发模块的要去,首先我们要知道单个模块中具体包括哪些个文件,文件有:模块常量控制器实体对象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.annotation.Log;
import com.javaweb.common.enums.LogType;
import com.javaweb.common.utils.JsonResult;
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.web.bind.annotation.*;

import com.javaweb.system.common.BaseController;

/**
 * <p>
 * 职级表 前端控制器
 * </p>
 *
 * @author 鲲鹏
 * @since 2020-11-02
 */
@RestController
@RequestMapping("/level")
public class LevelController extends BaseController {

    @Autowired
    private ILevelService levelService;

    /**
     * 获取职级列表
     *
     * @param levelQuery 查询条件
     * @return
     */
    @GetMapping("/index")
    @RequiresPermissions("sys:level:index")
    public JsonResult index(LevelQuery levelQuery) {
        return levelService.getList(levelQuery);
    }

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

    /**
     * 编辑职级
     *
     * @param entity 实体对象
     * @return
     */
    @Log(title = "职级管理", logType = LogType.UPDATE)
    @PutMapping("/edit")
    @RequiresPermissions("sys:level:edit")
    public JsonResult edit(@RequestBody Level entity) {
        return levelService.edit(entity);
    }

    /**
     * 删除职级
     *
     * @param levelIds 职级ID
     * @return
     */
    @Log(title = "职级管理", logType = LogType.DELETE)
    @DeleteMapping("/delete/{levelIds}")
    @RequiresPermissions("sys:level:delete")
    public JsonResult delete(@PathVariable("levelIds") Integer[] levelIds) {
        return levelService.deleteByIds(levelIds);
    }

    /**
     * 设置状态
     *
     * @param entity 实体对象
     * @return
     */
    @Log(title = "职级管理", logType = LogType.STATUS)
    @PutMapping("/status")
    @RequiresPermissions("sys:level:status")
    public JsonResult status(@RequestBody Level entity) {
        return levelService.setStatus(entity);
    }

    /**
     * 获取职级列表
     *
     * @return
     */
    @GetMapping("/getLevelList")
    public JsonResult getLevelList() {
        return levelService.getLevelList();
    }

}

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

总结:

  • 代码生成器在生成代码的时候动态解析模板,生成了权限节点控制参数,如:@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.javaweb.system.common.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableName;

import java.time.LocalDateTime;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

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

    private static final long serialVersionUID = 1L;

    /**
     * 职级名称
     */
    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

# 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-11-02
 */
public interface LevelMapper extends BaseMapper<Level> {

}


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

# 查询文件(LevelQuery.java)

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

package com.javaweb.system.query;

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

/**
 * 职级查询条件
 */
@Data
public class LevelQuery extends BaseQuery {

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

}


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

# 接口文件(ILevelService.java)

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

package com.javaweb.system.service;

import com.javaweb.common.utils.JsonResult;
import com.javaweb.system.common.IBaseService;
import com.javaweb.system.entity.Level;
import com.baomidou.mybatisplus.extension.service.IService;

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

    /**
     * 获取职级列表
     *
     * @return
     */
    JsonResult getLevelList();

}

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

# 接口实现文件(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.utils.JsonResult;
import com.javaweb.common.utils.StringUtils;
import com.javaweb.system.common.BaseQuery;
import com.javaweb.system.common.BaseServiceImpl;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 职级表 服务实现类
 * </p>
 *
 * @author 鲲鹏
 * @since 2020-11-02
 */
@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());
        }
        queryWrapper.eq("mark", 1);
        queryWrapper.orderByAsc("sort");

        // 查询分页数据
        IPage<Level> page = new Page<>(levelQuery.getPage(), levelQuery.getLimit());
        IPage<Level> pageData = levelMapper.selectPage(page, queryWrapper);
        return JsonResult.success(pageData);
    }

    /**
     * 获取职级列表
     *
     * @return
     */
    @Override
    public JsonResult getLevelList() {
        QueryWrapper<Level> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", 1);
        queryWrapper.eq("mark", 1);
        queryWrapper.orderByAsc("sort");
        List<Level> levelList = list(queryWrapper);
        return JsonResult.success(levelList);
    }
}

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

# 列表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

# 职级Vue前端(index.vue)

代码生成器生成时会根据表结构自动生成前端Vue部分,框架会动态解析表字段以及注释信息,完整的生成后端Java服务器代码的同时自动生成匹配的前端Vue代码部分,Vue代码如下:

<template>
  <div class="ele-body">
    <el-card shadow="never">
      <!-- 搜索表单 -->
      <el-form :model="table.where" label-width="77px" class="ele-form-search"
               @keyup.enter.native="$refs.table.reload()" @submit.native.prevent>
        <el-row :gutter="15">
          <el-col :md="6" :sm="12">
            <el-form-item label="职级名称:">
              <el-input v-model="table.where.name" placeholder="请输入职级名称" clearable/>
            </el-form-item>
          </el-col>
          <el-col :md="6" :sm="12">
            <div class="ele-form-actions">
              <el-button type="primary" @click="$refs.table.reload()" icon="el-icon-search" class="ele-btn-icon">查询
              </el-button>
              <el-button @click="(table.where={})&&$refs.table.reload()">重置</el-button>
            </div>
          </el-col>
        </el-row>
      </el-form>
      <!-- 操作按钮 -->
      <div class="ele-table-tool ele-table-tool-default">
        <el-button @click="showEdit=true" type="primary" icon="el-icon-plus" class="ele-btn-icon" size="small">添加
        </el-button>
        <el-button @click="remove()" type="danger" icon="el-icon-delete" class="ele-btn-icon" size="small">批量删除
        </el-button>
      </div>
      <!-- 数据表格 -->
      <ele-data-table ref="table" :config="table" :choose.sync="choose" height="calc(100vh - 315px)" highlight-current-row>
        <template slot-scope="{index}">
          <el-table-column type="selection" width="45" align="center" fixed="left"/>
          <el-table-column type="index" :index="index" label="编号" width="60" align="center" fixed="left" show-overflow-tooltip/>
          <el-table-column prop="name" label="职级名称" sortable="custom" show-overflow-tooltip min-width="250"/>
          <el-table-column prop="status" label="职级状态" sortable="custom" :resizable="false" min-width="120">
            <template slot-scope="{row}">
              <el-switch v-model="row.status" @change="editStatus(row)" :active-value="1" :inactive-value="2"/>
            </template>
          </el-table-column>
          <el-table-column prop="sort" label="排序" sortable="custom" show-overflow-tooltip/>
          <el-table-column label="创建时间" sortable="custom" show-overflow-tooltip min-width="160">
            <template slot-scope="{row}">{{ row.createTime | toDateString }}</template>
          </el-table-column>
          <el-table-column label="更新时间" sortable="custom" show-overflow-tooltip min-width="160">
            <template slot-scope="{row}">{{ row.updateTime | toDateString }}</template>
          </el-table-column>
          <el-table-column label="操作" width="130px" align="center" :resizable="false"  fixed="right">
            <template slot-scope="{row}">
              <el-link @click="edit(row)" icon="el-icon-edit" type="primary" :underline="false">修改</el-link>
              <el-popconfirm title="确定要删除此职级吗?" @confirm="remove(row)" class="ele-action">
                <el-link slot="reference" icon="el-icon-delete" type="danger" :underline="false">删除</el-link>
              </el-popconfirm>
            </template>
          </el-table-column>
        </template>
      </ele-data-table>
    </el-card>
    <!-- 编辑弹窗 -->
    <el-dialog :title="editForm.id?'修改职级':'修改职级'" :visible.sync="showEdit" width="400px"
               @closed="editForm={}" :destroy-on-close="true" :lock-scroll="false">
      <el-form :model="editForm" ref="editForm" :rules="editRules" label-width="82px">
        <el-form-item label="职级名称:" prop="name">
          <el-input v-model="editForm.name" placeholder="请输入职级名称" clearable/>
        </el-form-item>
        <el-form-item label="职级状态:">
          <el-radio-group v-model="editForm.status">
            <el-radio :label="1">正常</el-radio>
            <el-radio :label="2">禁用</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="排序号:" prop="sort">
          <el-input-number v-model="editForm.sort" controls-position="right" :min="0"
                            placeholder="请输入排序号" class="ele-fluid ele-text-left"/>
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="showEdit=false">取消</el-button>
        <el-button type="primary" @click="save">保存</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "SysLevel",
  data() {
    return {
      table: {url: '/level/index', where: {}},  // 表格配置
      choose: [],  // 表格选中数据
      showEdit: false,  // 是否显示表单弹窗
      editForm: {},  // 表单数据
      editRules: {  // 表单验证规则
        name: [
          {required: true, message: '请输入职级名称', trigger: 'blur'}
        ],
        sort: [
          {required: true, message: '请输入排序', trigger: 'blur'}
        ],
      },
    }
  },
  mounted() {
  },
  methods: {
    /* 显示编辑 */
    edit(row) {
      this.editForm = Object.assign({}, row);
      this.showEdit = true;
    },
    /* 保存编辑 */
    save() {
      this.$refs['editForm'].validate((valid) => {
        if (valid) {
          const loading = this.$loading({lock: true});
          this.$http[this.editForm.id ? 'put' : 'post'](this.editForm.id ? '/level/edit' : '/level/add', this.editForm).then(res => {
            loading.close();
            if (res.data.code === 0) {
              this.showEdit = false;
              this.$message({type: 'success', message: res.data.msg});
              this.$refs.table.reload();
            } else {
              this.$message.error(res.data.msg);
            }
          }).catch(e => {
            loading.close();
            this.$message.error(e.message);
          });
        } else {
          return false;
        }
      });
    },
    /* 删除 */
    remove(row) {
      if (!row) {  // 批量删除
        if (this.choose.length === 0) return this.$message.error('请至少选择一条数据');
        let ids = this.choose.map(d => d.id);
        this.$confirm('确定要删除选中的职级吗?', '提示', {type: 'warning'}).then(() => {
          const loading = this.$loading({lock: true});
          this.$http.delete('/level/delete/' + ids).then(res => {
            loading.close();
            if (res.data.code === 0) {
              this.$message({type: 'success', message: res.data.msg});
              this.$refs.table.reload();
            } else {
              this.$message.error(res.data.msg);
            }
          }).catch(e => {
            loading.close();
            this.$message.error(e.message);
          });
        }).catch(() => 0);
      } else {  // 单个删除
        const loading = this.$loading({lock: true});
        this.$http.delete('/level/delete/' + [row.id]).then(res => {
          loading.close();
          if (res.data.code === 0) {
            this.$message({type: 'success', message: res.data.msg});
            this.$refs.table.reload();
          } else {
            this.$message.error(res.data.msg);
          }
        }).catch(e => {
          loading.close();
          this.$message.error(e.message);
        });
      }
    },
    /* 更改状态 */
    editStatus(row) {
      const loading = this.$loading({lock: true});
      //let params = new FormData();
      // params.append('status', row.status);
      let params = Object.assign({}, row);
      this.$http.put('/level/status', params).then(res => {
        loading.close();
        if (res.data.code === 0) {
          this.$message({type: 'success', message: res.data.msg});
        } else {
          row.status = !row.status ? 2 : 1;
          this.$message.error(res.data.msg);
        }
      }).catch(e => {
        loading.close();
        this.$message.error(e.message);
      });
    },
  }
}
</script>

<style scoped>
.ele-block >>> .el-upload, .ele-block >>> .el-upload-dragger {
  width: 100%;
}
</style>
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

# 效果展示

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

  • 职级列表

    mixureSecure

  • 职级添加/编辑

    mixureSecure

  • 职级删除

    mixureSecure