前言
一款基于SpringBoot+Bootstrap的极速后台开发框架。
经典技术组合(Spring Boot、Apache Shiro、MyBatis、Thymeleaf、Bootstrap)
经典技术组合(Spring Boot、Apache Shiro、MyBatis、vue + element)
内置模块如:机构管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、通知公告等。
在线定时任务配置;支持集群,支持多数据源。
技术选型
系统环境
- jdk/javaee8
- apache maven
主框架
- Spring Boot 2.0
- Apache Shiro 1.4
持久层
- Apache MyBatisPlus
视图层
- vue2.x
- element
内置
支持CRUD下载
管理工具:
登录日志:系统登录日志记录查询包含登录异常。
操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
在线用户:当前系统中活跃用户状态监控。
系统管理:
用户管理:用户是系统操作者,该功能主要完成系统用户配置。
部门管理:系统组织机构(公司、部门、小组),树结构展现支持数据权限。
岗位管理:配置系统用户所属担任职务。
角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
菜单管理:配置系统菜单,操作权限,按钮权限标识等。
开发工具:
参数管理:对系统动态配置常用参数。
字典管理:对系统中经常使用的一些较为固定的数据进行维护。
代码生成(开发规范)
定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
集成:
本地nacos服务监控(集成)
http://127.0.0.1:8848/nacos/#/login
nacos/nacos
本地sentinel服务监控(集成)
http://127.0.0.1:8718/
根据业务代码自动生成相关的api接口文档(集成)
layout.vue
<template>
<div>
<el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="100px">
<el-row gutter="15">
</el-row>
<el-form-item size="large">
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
components: {},
props: [],
data() {
return {
formData: {},
rules: {},
}
},
computed: {},
watch: {},
created() {},
mounted() {},
methods: {
submitForm() {
this.$refs['elForm'].validate(valid => {
if (!valid) return
// TODO 提交表单
})
},
resetForm() {
this.$refs['elForm'].resetFields()
},
}
}
</script>
<style>
</style>
select.vue
<template>
<div>
<el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="100px">
<el-form-item label="下拉选择" prop="field116">
<el-select v-model="formData.field116" placeholder="请选择下拉选择" clearable :style="{width: '100%'}">
<el-option v-for="(item, index) in field116Options" :key="index" :label="item.label"
:value="item.value" :disabled="item.disabled"></el-option>
</el-select>
</el-form-item>
<el-form-item label="级联选择" prop="field117">
<el-cascader v-model="formData.field117" :options="field117Options" :props="field117Props"
:style="{width: '100%'}" placeholder="请选择级联选择" clearable></el-cascader>
</el-form-item>
<el-form-item label="单选框组" prop="field118">
<el-radio-group v-model="formData.field118" size="medium">
<el-radio v-for="(item, index) in field118Options" :key="index" :label="item.value"
:disabled="item.disabled">{{item.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="多选框组" prop="field119">
<el-checkbox-group v-model="formData.field119" size="medium">
<el-checkbox v-for="(item, index) in field119Options" :key="index" :label="item.value"
:disabled="item.disabled">{{item.label}}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="上传" prop="field124" required>
<el-upload ref="field124" :file-list="field124fileList" :action="field124Action"
:before-upload="field124BeforeUpload">
<el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
</el-upload>
</el-form-item>
<el-form-item size="large">
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
components: {},
props: [],
data() {
return {
formData: {
field116: undefined,
field117: [1, 2],
field118: undefined,
field119: [],
field124: null,
},
rules: {
field116: [{
required: true,
message: '请选择下拉选择',
trigger: 'change'
}],
field117: [{
required: true,
type: 'array',
message: '请至少选择一个field117',
trigger: 'change'
}],
field118: [{
required: true,
message: '单选框组不能为空',
trigger: 'change'
}],
field119: [{
required: true,
type: 'array',
message: '请至少选择一个field119',
trigger: 'change'
}],
},
field124Action: 'https://jsonplaceholder.typicode.com/posts/',
field124fileList: [],
field116Options: [{
"label": "选项一",
"value": 1
}, {
"label": "选项二",
"value": 2
}],
field117Options: [],
field118Options: [{
"label": "选项一",
"value": 1
}, {
"label": "选项二",
"value": 2
}],
field119Options: [{
"label": "选项一",
"value": 1
}, {
"label": "选项二",
"value": 2
}],
field117Props: {
"multiple": false
},
}
},
computed: {},
watch: {},
created() {},
mounted() {},
methods: {
submitForm() {
this.$refs['elForm'].validate(valid => {
if (!valid) return
// TODO 提交表单
})
},
resetForm() {
this.$refs['elForm'].resetFields()
},
getField117Options() {
// TODO 发起请求获取数据
this.field117Options
},
field124BeforeUpload(file) {
let isRightSize = file.size / 1024 / 1024 < 2
if (!isRightSize) {
this.$message.error('文件大小超过 2MB')
}
return isRightSize
},
}
}
</script>
<style>
.el-upload__tip {
line-height: 1.2;
}
</style>
form.vue
<template>
<div>
<el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="100px">
<el-form-item label="单行文本" prop="field110">
<el-input v-model="formData.field110" placeholder="请输入单行文本" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="下拉选择" prop="field111">
<el-select v-model="formData.field111" placeholder="请选择下拉选择" clearable :style="{width: '100%'}">
<el-option v-for="(item, index) in field111Options" :key="index" :label="item.label"
:value="item.value" :disabled="item.disabled"></el-option>
</el-select>
</el-form-item>
<el-form-item label="时间选择" prop="field112">
<el-time-picker v-model="formData.field112" format="HH:mm:ss" value-format="HH:mm:ss"
:picker-options='{"selectableRange":"00:00:00-23:59:59"}' :style="{width: '100%'}"
placeholder="请选择时间选择" clearable></el-time-picker>
</el-form-item>
<el-form-item label="时间范围" prop="field113">
<el-time-picker v-model="formData.field113" is-range format="HH:mm:ss" value-format="HH:mm:ss"
:style="{width: '100%'}" start-placeholder="开始时间" end-placeholder="结束时间" range-separator="至"
clearable></el-time-picker>
</el-form-item>
<el-form-item label="日期选择" prop="field114">
<el-date-picker v-model="formData.field114" format="yyyy-MM-dd" value-format="yyyy-MM-dd"
:style="{width: '100%'}" placeholder="请选择日期选择" clearable></el-date-picker>
</el-form-item>
<el-form-item label="日期范围" prop="field115">
<el-date-picker type="daterange" v-model="formData.field115" format="yyyy-MM-dd"
value-format="yyyy-MM-dd" :style="{width: '100%'}" start-placeholder="开始日期" end-placeholder="结束日期"
range-separator="至" clearable></el-date-picker>
</el-form-item>
<el-form-item size="large">
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
components: {},
props: [],
data() {
return {
formData: {
field110: undefined,
field111: undefined,
field112: null,
field113: null,
field114: null,
field115: null,
},
rules: {
field110: [{
required: true,
message: '请输入单行文本',
trigger: 'blur'
}],
field111: [{
required: true,
message: '请选择下拉选择',
trigger: 'change'
}],
field112: [{
required: true,
message: '请选择时间选择',
trigger: 'change'
}],
field113: [{
required: true,
message: '时间范围不能为空',
trigger: 'change'
}],
field114: [{
required: true,
message: '请选择日期选择',
trigger: 'change'
}],
field115: [{
required: true,
message: '日期范围不能为空',
trigger: 'change'
}],
},
field111Options: [{
"label": "选项一",
"value": 1
}, {
"label": "选项二",
"value": 2
}],
}
},
computed: {},
watch: {},
created() {},
mounted() {},
methods: {
submitForm() {
this.$refs['elForm'].validate(valid => {
if (!valid) return
// TODO 提交表单
})
},
resetForm() {
this.$refs['elForm'].resetFields()
},
}
}
</script>
<style>
</style>
web开发规范
- css 基础语法
- scss 语法
- yml 语法
- svg 图标 icons
- png 图片
- image 图片
- gif 动图
vue 2.x src 开发目录
- src\assets static 资源
- src\mixins mixins.js 混入
- src\directive 指令.js
- src\components 组件.vue
- src\router 路由.js
- src\api
- src\views
编码规范
3.1使用swagger 必须 补充完整好注解, 每一example个参数必须要有方便以后测试
3.2 单表操作 请使用 mybatisplus 的方法
3.3 不允许在代码中直接出现数字赋值的情况请使用(枚举/常量)
3.4 在进行对比的时候请使用(枚举/常量)
3.5 字段校验请使用spring validate,需要校验的字段请务必加好注解并补充好规则,以及提示信息
3.5 入参超过三个必须建立一个实体类接收
3.6 出参统一格式是用AjaxResult包装实体类 泛型必须写清楚(返回数据为空的除外)
3.7 每一个方法都必须要有注释
3.8 如果是feign 接口请使用 .inside 结尾
Mybatis Plus
MyBatis:一种操作数据库的框架,提供一种Mapper类,支持让你用java代码进行增删改查的数据库操作,省去了每次都要手写sql语句的麻烦。但是!有一个前提,你得先在xml中写好sql语句,是不是很麻烦?于是有下面的↓
Mybatis Generator:自动为Mybatis生成简单的增删改查sql语句的工具,省去一大票时间,两者配合使用,开发速度快到飞起。至于标题说的↓
Mybatis Plus:国人团队苞米豆在Mybatis的基础上开发的框架,在Mybatis基础上扩展了许多功能,荣获了2018最受欢迎国产开源软件第5名,当然也有配套的↓
Mybatis Plus Generator:同样为苞米豆开发,比Mybatis Generator更加强大,支持功能更多,自动生成Entity、Mapper、Service、Controller等
总结:
数据库框架:Mybatis Plus > Mybatis
代码生成器:Mybatis Plus Generator > Mybatis Generator
如何在你的项目中集成MP
- 它也提供了一些很有意思的插件,比如SQL性能监控、乐观锁、执行分析等
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-annotation</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
MP代码生成器
《需求详细设计》
- 需求调研文档(可以解决什么问题,提供什么基础服务)
- 需求分析(细化流程) 【产品/架构各有侧重】
- 原型交互(进一步沟通)【产品s】
- UI设计(细化页面风格)【ui进场】
《系统技术方案》
- 数据库设计【架构】
《开发文档》
- 接口分析(细化接口参数)
- 详细设计【开发】
- 接口交互文档【开发】
《开发文档》
- src\main\java\com\copote\system\domain\vo db-mapper 【entity】
- \src\main\java\com\copote\system\mapper xml-mapper 【dao】
- src\main\resources\mapper 【mapper】
- src\main\java\com\copote\system\service\ 【定义接口】
- src\main\java\com\copote\system\service\impl 【接口实现】
- src\main\java\com\copote\system\controller 【api 提供】
《常见且重要spring 注解》
- @PreAuthorize 服务鉴权 菜单表配置
查询
/**
* 查询终端配置列表
*/
@PreAuthorize("@ss.hasPermi('system:client:list')")
@GetMapping("/list")
public TableDataInfo list(SysClientDetails sysClientDetails){
startPage();
List<SysClientDetails> list = sysClientDetailsService.selectSysClientDetailsList(sysClientDetails);
return getDataTable(list);
}
新增
/**
* 新增终端配置
*/
@PreAuthorize("@ss.hasPermi('system:client:add')")
@Log(title = "终端配置", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysClientDetails sysClientDetails){
String clientId = sysClientDetails.getClientId();
if (StringUtils.isNotNull(sysClientDetailsService.selectSysClientDetailsById(clientId))){
return AjaxResult.error("新增终端'" + clientId + "'失败,编号已存在");
}
sysClientDetails.setClientSecret(SecurityUtils.encryptPassword(sysClientDetails.getClientSecret()));
return toAjax(sysClientDetailsService.insertSysClientDetails(sysClientDetails));
}
修改
/**
* 修改终端配置
*/
@PreAuthorize("@ss.hasPermi('system:client:edit')")
@Log(title = "终端配置", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody SysClientDetails sysClientDetails){
sysClientDetails.setClientSecret(SecurityUtils.encryptPassword(sysClientDetails.getClientSecret()));
return toAjax(sysClientDetailsService.updateSysClientDetails(sysClientDetails));
}
删除
/**
* 删除终端配置
*/
@PreAuthorize("@ss.hasPermi('system:client:remove')")
@Log(title = "终端配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{clientIds}")
public AjaxResult remove(@PathVariable String[] clientIds){
return toAjax(sysClientDetailsService.deleteSysClientDetailsByIds(clientIds));
}