云尚办公系统是一套自动办公系统,系统主要包含:管理端和员工端
管理端包含:权限管理、审批管理、公众号菜单管理
员工端采用微信公众号操作,包含:办公审批、微信授权登录、消息推送等功能
项目服务器端架构:SpringBoot + MyBatisPlus + SpringSecurity + Redis + Activiti+ MySQL
前端架构:vue-admin-template + Node.js + Npm + Vue + ElementUI + Axios
开发工具: IDEA2021 VS code
基础框架:SpringBoot |
数据缓存:Redis |
数据库:MySQL |
权限控制:SpringSecurity |
工作流引擎:Activiti |
前端技术:vue-admin-template + Node.js + Npm + Vue + ElementUI + Axios |
微信公众号:公众号菜单 + 微信授权登录 + 消息推送 |
最终服务器端架构模块
guigu-oa-parent:根目录,管理子模块:
common:公共类父模块
common-util:核心工具类
service-util:service模块工具类
spring-security:spring-security业务模块
model:实体类模块
service-oa:系统服务模块
数据库从资料文件中获取,导入数据库,Activiti表后续自动导入,数据库表如下:
如:实体类、前端项目模板等都在资料文件夹中,实体类直接复制到model模块,后续不做说明。
目前先搭建“云尚办公系统“项目模块。
管理子模块及依赖
GroupId:com.atguigu
ArtifactId:guigu-oa-parent
新建项目(父模块 ) 如图所示:
删除src目录,用来管理子模块所以不需要src目录
工具类父模块
GroupId:com.atguigu
ArtifactId:common
第一步:右键点击“guigu-oa-parent”新建"module" src删除
核心工具类
GroupId:com.atguigu
ArtifactId:common-util
第一步:右键点击“common”新建"common-util"
第二步:修改路径记得是在父模块common下新建模块common-util
核心工具类
GroupId:com.atguigu
ArtifactId:service-util
第一步:右键点击“common”新建"service-util"
第二步:修改路径记得是在父模块common下新建模块service-util
核心工具类
GroupId:com.atguigu
ArtifactId:model
第一步:和common同一级别模块model
核心工具类
GroupId:com.atguigu
ArtifactId:service-oa
第一步:和common同一级别模块service-oa
修改guigu-oa-parent模块pom.xml文件
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.6.RELEASE
com.atguigu
guigu-oa-parent
pom
1.0-SNAPSHOT
common
model
service-oa
1.8
3.4.1
8.0.30
3.0.3
0.9.1
2.0.21
com.baomidou
mybatis-plus-boot-starter
${mybatis-plus.version}
mysql
mysql-connector-java
${mysql.version}
com.github.xiaoymin
knife4j-spring-boot-starter
${knife4j.version}
io.jsonwebtoken
jjwt
${jwt.version}
com.alibaba
fastjson
${fastjson.version}
org.apache.maven.plugins
maven-compiler-plugin
3.1
1.8
common公共父模块pom.xml文件配置(如果出现报错重新导入依赖)
guigu-oa-parent
com.atguigu
1.0-SNAPSHOT
4.0.0
common
pom
service-util
common-util
common
com.atguigu
1.0-SNAPSHOT
4.0.0
common-util
jar
org.springframework.boot
spring-boot-starter-web
provided
io.jsonwebtoken
jjwt
org.projectlombok
lombok
com.alibaba
fastjson
common
com.atguigu
1.0-SNAPSHOT
4.0.0
service-util
com.atguigu
common-util
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
com.baomidou
mybatis-plus-boot-starter
mysql
mysql-connector-java
guigu-oa-parent
com.atguigu
1.0-SNAPSHOT
4.0.0
model
org.projectlombok
lombok
com.github.xiaoymin
knife4j-spring-boot-starter
provided
com.baomidou
mybatis-plus-boot-starter
provided
从资源文件夹中导入实体类
(1) 因为目前采用idea2021版本,Lombok在2020.2开始不再更新,导致高版本IDEA(2021及之后版本)无法在Plugins中搜索到Lombok插件,需要手动安装,或者也可以不安装插件,在实体类里面手动生成get、set和构造方法
(2) 如果搜不到上图lombok插件那么使用以下方法:
https://plugins.jetbrains.com/plugin/6317-lombok/versions
选择最新版本
修改jar包里面文件内容,可以使用压缩工具打开jar包
修改当前idea版本
idea的File -> Settings -> Plugins下选中修改参数后的安装包进行安装,安装后重启idea
guigu-oa-parent
com.atguigu
1.0-SNAPSHOT
4.0.0
service-oa
jar
com.atguigu
model
1.0-SNAPSHOT
com.atguigu
service-util
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-test
test
${project.artifactId}
org.springframework.boot
spring-boot-maven-plugin
官网:https://baomidou.com/
MyBatis-Plus(简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
com.baomidou
mybatis-plus-boot-starter
3.4.1
前面介绍了MyBatis-Plus,当前就以角色管理为例讲解MyBatis-Plus的使用
配置 MySQL 数据库的相关配置及Mybatis-Plus日志,在service-oa模块中配置
application.yml
spring:
application:
name: service-oa #配置文件所在模块名称
profiles:
active: dev #在开发过程快速切换环境配置,以及如何使一个部署适配各种不同的环境。如果我们的部署包可以不用修改配置文件
application-dev.yml
server:
port: 8800
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看日志
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource # Springboot默认数据源配置HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/guigu-oa?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8
username: root
password: 123
在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:
package com.atguigu;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication //springboot项目启动类
@ComponentScan("com.atguigu") //根据定义的扫描路径,把符合扫描规则的类装配到spring容器中
@MapperScan("com.atguigu.*.mapper") //扫描mapper包
public class ServiceAuthApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceAuthApplication.class, args);
}
}
前面已引入,mybatis-plus中的实体类注解说明:
实体类注解详细文档:https://baomidou.com/pages/223848/
@TableName:表名注解,标识实体类对应的表
@TableId:主键注解,type = IdType.AUTO(数据库 ID 自增)
@TableField:字段注解(非主键)
@TableLogic:逻辑删除
package com.atguigu.model.system;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.atguigu.model.base.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "角色")// 前面API接口
@TableName("sys_role") //表名注解,标识实体类对应的表
public class SysRole extends BaseEntity {
private static final long serialVersionUID = 1L;
//@NotBlank(message = "角色名称不能为空")
@ApiModelProperty(value = "角色名称")
@TableField("role_name")
private String roleName;
@ApiModelProperty(value = "角色编码")
@TableField("role_code")
private String roleCode;
@ApiModelProperty(value = "描述")
@TableField("description")
private String description;
}
package com.atguigu.auth.mapper;
import com.atguigu.model.system.SysRole;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository //@Repository用于标注数据访问组件,即DAO组件
public interface SysRoleMapper extends BaseMapper {
}
com.baomidou.mybatisplus.core.mapper.BaseMapper这是Mybatis-Plus提供的默认Mapper接口。
package com.baomidou.mybatisplus.core.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
public interface BaseMapper extends Mapper {
int insert(T entity);
int deleteById(Serializable id);
int deleteByMap(@Param("cm") Map columnMap);
int delete(@Param("ew") Wrapper queryWrapper);
int deleteBatchIds(@Param("coll") Collection extends Serializable> idList);
int updateById(@Param("et") T entity);
int update(@Param("et") T entity, @Param("ew") Wrapper updateWrapper);
T selectById(Serializable id);
List selectBatchIds(@Param("coll") Collection extends Serializable> idList);
List selectByMap(@Param("cm") Map columnMap);
T selectOne(@Param("ew") Wrapper queryWrapper);
Integer selectCount(@Param("ew") Wrapper queryWrapper);
List selectList(@Param("ew") Wrapper queryWrapper);
List
package com.atguigu;
import com.atguigu.auth.mapper.SysRoleMapper;
import com.atguigu.model.system.SysRole;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)//测试类是需要@RunWith的,作用是告诉java你这个类通过用什么运行环境运行,
@SpringBootTest
public class SysRoleMapperTest {
@Autowired
private SysRoleMapper sysRoleMapper;
@Test
public void testSelectList() {
System.out.println(("----- selectAll method test ------"));
//UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper
//所以不填写就是无任何条件
List users = sysRoleMapper.selectList(null);
users.forEach(System.out::println);
}
}
IDEA在sysRoleMapper处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执行。
为了避免报错,可以在 mapper 层 的接口上添加 @Repository 或直接使用 @Resource 代替 @Autowired。
运行后控制台输出:
@Test
public void testInsert(){
SysRole sysRole = new SysRole();
sysRole.setRoleName("角色管理员");
sysRole.setRoleCode("role");
sysRole.setDescription("角色管理员");
int result = sysRoleMapper.insert(sysRole);
System.out.println(result); //影响的行数
System.out.println(sysRole); //id自动回填
}
MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID
要想主键自增需要配置如下主键策略
需要在创建数据表的时候设置主键自增
实体字段中配置 @TableId(type = IdType.AUTO)
@TableId(type = IdType.AUTO)
private Long id;
(3)其他主键策略分析: IdType 源码可知
public enum IdType {
/**
* 数据库ID自增
*/
AUTO(0),
/**
* 该类型为未设置主键类型
*/
NONE(1),
/**
* 用户输入ID
* 该类型可以通过自己注册自动填充插件进行填充
*/
INPUT(2),
/**
* 全局唯一ID
*/
ASSIGN_ID(3),
/**
* 全局唯一ID (UUID)
*/
ASSIGN_UUID(4),
/** @deprecated */
@Deprecated
ID_WORKER(3),
/** @deprecated */
@Deprecated
ID_WORKER_STR(3),
/** @deprecated */
@Deprecated
UUID(4);
private final int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
@Test
public void testUpdateById(){
SysRole sysRole = new SysRole();
sysRole.setId(1L);
sysRole.setRoleName("角色管理员1");
int result = sysRoleMapper.updateById(sysRole);
System.out.println(result);
}
/**
* application-dev.yml 加入配置
* 此为默认值,如果你的默认值和mp默认的一样,则不需要该配置
* mybatis-plus:
* global-config:
* db-config:
* logic-delete-value: 1
* logic-not-delete-value: 0
*/
@Test
public void testDeleteById(){
int result = sysRoleMapper.deleteById(2L);
System.out.println(result);
}
删除成功,数据不会被删除,可以从后面表中字段is_deleted的数字增加
@Test
public void testDeleteBatchIds() {
int result = sysRoleMapper.deleteBatchIds(Arrays.asList(1, 1));
System.out.println(result);
}
Wrapper : 条件构造抽象类,最顶端父类
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper : Entity 对象封装操作类,不是用lambda语法
UpdateWrapper : Update 条件封装,用于Entity对象更新操作
AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper : Lambda 更新封装Wrapper
示例实现如下:
@Test
public void testSelect1() {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("role_code", "role");
List users = sysRoleMapper.selectList(queryWrapper);
System.out.println(users);
}
@Test
public void testSelect2() {
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysRole::getRoleCode, "role");
List users = sysRoleMapper.selectList(queryWrapper);
System.out.println(users);
}
package com.atguigu.auth.service;
import com.atguigu.model.system.SysRole;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface SysRoleService extends IService {
}
package com.baomidou.mybatisplus.extension.service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.kotlin.KtQueryChainWrapper;
import com.baomidou.mybatisplus.extension.kotlin.KtUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.transaction.annotation.Transactional;
public interface IService {
int DEFAULT_BATCH_SIZE = 1000;
default boolean save(T entity) {
return SqlHelper.retBool(this.getBaseMapper().insert(entity));
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean saveBatch(Collection entityList) {
return this.saveBatch(entityList, 1000);
}
boolean saveBatch(Collection entityList, int batchSize);
@Transactional(
rollbackFor = {Exception.class}
)
default boolean saveOrUpdateBatch(Collection entityList) {
return this.saveOrUpdateBatch(entityList, 1000);
}
boolean saveOrUpdateBatch(Collection entityList, int batchSize);
default boolean removeById(Serializable id) {
return SqlHelper.retBool(this.getBaseMapper().deleteById(id));
}
default boolean removeByMap(Map columnMap) {
Assert.notEmpty(columnMap, "error: columnMap must not be empty", new Object[0]);
return SqlHelper.retBool(this.getBaseMapper().deleteByMap(columnMap));
}
default boolean remove(Wrapper queryWrapper) {
return SqlHelper.retBool(this.getBaseMapper().delete(queryWrapper));
}
default boolean removeByIds(Collection extends Serializable> idList) {
return CollectionUtils.isEmpty(idList) ? false : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(idList));
}
default boolean updateById(T entity) {
return SqlHelper.retBool(this.getBaseMapper().updateById(entity));
}
default boolean update(Wrapper updateWrapper) {
return this.update((Object)null, updateWrapper);
}
default boolean update(T entity, Wrapper updateWrapper) {
return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper));
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean updateBatchById(Collection entityList) {
return this.updateBatchById(entityList, 1000);
}
boolean updateBatchById(Collection entityList, int batchSize);
boolean saveOrUpdate(T entity);
default T getById(Serializable id) {
return this.getBaseMapper().selectById(id);
}
default List listByIds(Collection extends Serializable> idList) {
return this.getBaseMapper().selectBatchIds(idList);
}
default List listByMap(Map columnMap) {
return this.getBaseMapper().selectByMap(columnMap);
}
default T getOne(Wrapper queryWrapper) {
return this.getOne(queryWrapper, true);
}
T getOne(Wrapper queryWrapper, boolean throwEx);
Map getMap(Wrapper queryWrapper);
V getObj(Wrapper queryWrapper, Function super Object, V> mapper);
default int count() {
return this.count(Wrappers.emptyWrapper());
}
default int count(Wrapper queryWrapper) {
return SqlHelper.retCount(this.getBaseMapper().selectCount(queryWrapper));
}
default List list(Wrapper queryWrapper) {
return this.getBaseMapper().selectList(queryWrapper);
}
default List list() {
return this.list(Wrappers.emptyWrapper());
}
default > E page(E page, Wrapper queryWrapper) {
return this.getBaseMapper().selectPage(page, queryWrapper);
}
default > E page(E page) {
return this.page(page, Wrappers.emptyWrapper());
}
default List
package com.atguigu.auth.service.impl;
import com.atguigu.auth.mapper.SysRoleMapper;
import com.atguigu.auth.service.SysRoleService;
import com.atguigu.model.system.SysRole;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@Service
public class SysRoleServiceImpl extends ServiceImpl implements SysRoleService {
@Autowired
private SysRoleMapper sysRoleMapper;
}
新建一个SysRoleServiceTest代码如下:
package com.atguigu;
import com.atguigu.auth.service.SysRoleService;
import com.atguigu.model.system.SysRole;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SysRoleServiceTest {
@Autowired
private SysRoleService sysRoleService;
@Test
public void testSelectList() {
System.out.println(("----- selectAll method test ------"));
//UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper
//所以不填写就是无任何条件
List users = sysRoleService.list();
users.forEach(System.out::println);
}
@Test
public void testInsert(){
SysRole sysRole = new SysRole();
sysRole.setRoleName("角色管理员");
sysRole.setRoleCode("role");
sysRole.setDescription("角色管理员");
boolean result = sysRoleService.save(sysRole);
System.out.println(result); //影响的行数
System.out.println(sysRole); //id自动回填
}
@Test
public void testUpdateById(){
SysRole sysRole = new SysRole();
sysRole.setId(1L);
sysRole.setRoleName("角色管理员1");
boolean result = sysRoleService.updateById(sysRole);
System.out.println(result);
}
@Test
public void testDeleteById(){
boolean result = sysRoleService.removeById(2L);
System.out.println(result);
}
@Test
public void testSelect1() {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.ge("role_code", "role");
List users = sysRoleService.list(queryWrapper);
System.out.println(users);
}
@Test
public void testSelect2() {
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.ge(SysRole::getRoleCode, "role");
List users = sysRoleService.list(queryWrapper);
System.out.println(users);
}
}
package com.atguigu.auth.controller;
import com.atguigu.auth.service.SysRoleService;
import com.atguigu.model.system.SysRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/admin/system/sysRole")
public class SysRoleController {
@Autowired
private SysRoleService sysRoleService;
@GetMapping("findAll") //查询全部
public List findAll() {
List roleList = sysRoleService.list();
return roleList;
}
}
运行springboot启动类,输入下列路径测试:
http://localhost:8800/admin/system/sysRole/findAll
项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。
一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容
例如,我们的系统要求返回的基本数据格式如下:
列表:
{
"code": 200,
"message": "成功",
"data": [
{
"id": 2, "roleName": "系统管理员"
}
],
"ok": true
}
分页:
{
"code": 200,
"message": "成功",
"data": {
"records": [
{
"id": 2,
"roleName": "系统管理员"
},
{
"id": 3,
"name": "普通管理员"
}
],
"total": 10,
"size": 3,
"current": 1,
"orders": [],
"hitCount": false,
"searchCount": true,
"pages": 2
},
"ok": true
}
没有返回数据:
{
"code": 200,
"message": "成功",
"data": null,
"ok": true
}
失败:
{
"code": 201,
"message": "失败",
"data": null,
"ok": false
}
操作模块:common-util
后续其他模块也会用到,故抽取到common-util模块
Result类,代码如下: 报错先不急.先往下看
package com.atguigu.common.result;
import lombok.Data;
/**
* 全局统一返回结果类
*
*/
@Data
public class Result {
//返回码
private Integer code;
//返回消息
private String message;
//返回数据
private T data;
public Result(){}
// 返回数据
protected static Result build(T data) {
Result result = new Result();
if (data != null)
result.setData(data);
return result;
}
public static Result build(T body, Integer code, String message) {
Result result = build(body);
result.setCode(code);
result.setMessage(message);
return result;
}
public static Result build(T body, ResultCodeEnum resultCodeEnum) {
Result result = build(body);
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
return result;
}
public static Result ok(){
return Result.ok(null);
}
/**
* 操作成功
* @param data baseCategory1List
* @param
* @return
*/
public static Result ok(T data){
Result result = build(data);
return build(data, ResultCodeEnum.SUCCESS);
}
public static Result fail(){
return Result.fail(null);
}
/**
* 操作失败
* @param data
* @param
* @return
*/
public static Result fail(T data){
Result result = build(data);
return build(data, ResultCodeEnum.FAIL);
}
public Result message(String msg){
this.setMessage(msg);
return this;
}
public Result code(Integer code){
this.setCode(code);
return this;
}
}
统一返回结果状态信息类:
下面的状态后续都会用到,所以直接引入了
ResultCodeEnum类,代码如下:
package com.atguigu.common.result;
import lombok.Getter;
/**
* 统一返回结果状态信息类
*
*/
@Getter
public enum ResultCodeEnum {
SUCCESS(200,"成功"),
FAIL(201, "失败"),
SERVICE_ERROR(2012, "服务异常"),
DATA_ERROR(204, "数据异常"),
LOGIN_AUTH(208, "未登陆"),
PERMISSION(209, "没有权限")
;
private Integer code;
private String message;
private ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
@GetMapping("findAll")
public Result> findAll() {
List roleList = sysRoleService.list();
return Result.ok(roleList);
}
http://localhost:8800/admin/system/sysRole/findAll
文档地址:https://doc.xiaominfo.com/
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。
前后端分离开发模式中,api文档是最好的沟通方式。
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
1、及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)
2、规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)
3、一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)
4、可测性 (直接在接口文档上进行测试,以方便理解业务)
knife4j属于service模块公共资源,因此我们集成到service-uitl模块
操作模块:service-uitl导入依赖:
com.github.xiaoymin
knife4j-spring-boot-starter
操作模块:service-uitl
package com.atguigu.common.config.knife4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
import java.util.ArrayList;
import java.util.List;
/**
* knife4j配置信息
*/
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfig {
@Bean
public Docket adminApiConfig(){
List pars = new ArrayList<>();
ParameterBuilder tokenPar = new ParameterBuilder();
tokenPar.name("zhousir")
.description("用户zhousir")
.defaultValue("")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build();
pars.add(tokenPar.build());
//添加head参数end
Docket adminApi = new Docket(DocumentationType.SWAGGER_2)
.groupName("adminApi")
.apiInfo(adminApiInfo())
.select()
//只显示admin路径下的页面
.apis(RequestHandlerSelectors.basePackage("com.atguigu"))
.paths(PathSelectors.regex("/admin/.*"))
.build()
.globalOperationParameters(pars);
return adminApi;
}
private ApiInfo adminApiInfo(){
return new ApiInfoBuilder()
.title("云尚办公后台管理系统-API文档")
.description("本文档描述了后台管理系统微服务接口定义")
.version("1.0")
.contact(new Contact("atguigu", "http://atguigu.com", "[email protected]"))
.build();
}
}
package com.atguigu.system.controller;
import com.atguigu.auth.service.SysRoleService;
import com.atguigu.common.result.Result;
import com.atguigu.model.system.SysRole;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Api(tags = "角色管理") //Api访问名称
@RestController
@RequestMapping("/admin/system/sysRole")
public class SysRoleController {
@Autowired
private SysRoleService sysRoleService;
@ApiOperation(value = "获取全部角色列表")//接口的访问地址名称
@GetMapping("findAll")
public Result> findAll() {
List roleList = sysRoleService.list();
return Result.ok(roleList);
}
}
http://localhost:8800/doc.html
操作模块:service-uitl (com.atguigu.common.config)新建包mp下创建类
说明:我们将@MapperScan("com.atguigu.auth.mapper")提取到该配置类上面,统一管理,启动类就不需要了。
package com.atguigu.common.config.mp;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration// 配置文件注解
@MapperScan("com.atguigu.auth.mapper")
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false
避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
package com.atguigu.auth.controller;
import com.atguigu.auth.service.SysRoleService;
import com.atguigu.common.result.Result;
import com.atguigu.model.system.SysRole;
import com.atguigu.vo.system.SysRoleQueryVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Api(tags = "角色管理") //Api访问名称
@RestController
@RequestMapping("/admin/system/sysRole")
public class SysRoleController {
@Autowired
private SysRoleService sysRoleService;
@ApiOperation(value = "获取全部角色列表")//接口的访问地址名称
@GetMapping("findAll")
public Result> findAll() {
List roleList = sysRoleService.list();
return Result.ok(roleList);
}
//条件分页查询
//page 当前页 limit 每页显示记录数
//SysRoleQueryVo 条件对象
@ApiOperation("条件分页查询")
@GetMapping("{page}/{limit}")
public Result pageQueryRole(@PathVariable Long page,
@PathVariable Long limit,
SysRoleQueryVo sysRoleQueryVo) {
//调用service的方法实现
//1 创建Page对象,传递分页相关参数
//page 当前页 limit 每页显示记录数
Page pageParam = new Page<>(page,limit);
//2 封装条件,判断条件是否为空,不为空进行封装
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
String roleName = sysRoleQueryVo.getRoleName();
if(!StringUtils.isEmpty(roleName)) {
//封装 like模糊查询
wrapper.like(SysRole::getRoleName,roleName);
}
//3 调用方法实现
IPage pageModel = sysRoleService.page(pageParam, wrapper);
return Result.ok(pageModel);
}
}
说明:通过knife4j测试接口
@ApiOperation(value = "获取")
@GetMapping("get/{id}")
public Result get(@PathVariable Long id) {
SysRole role = sysRoleService.getById(id);
return Result.ok(role);
}
@ApiOperation(value = "新增角色")
@PostMapping("save")
public Result save(@RequestBody @Validated SysRole role) {
sysRoleService.save(role);
return Result.ok();
}
@ApiOperation(value = "修改角色")
@PutMapping("update")
public Result updateById(@RequestBody SysRole role) {
sysRoleService.updateById(role);
return Result.ok();
}
@ApiOperation(value = "删除角色")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
sysRoleService.removeById(id);
return Result.ok();
}
@ApiOperation(value = "根据id列表删除")
@DeleteMapping("batchRemove")
public Result batchRemove(@RequestBody List idList) {
sysRoleService.removeByIds(idList);
return Result.ok();
}
配置日期时间格式
application-dev.yml添加以下内容
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
除以0
int a = 10/0;
我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要统一异常处理。
操作模块:service-util
package com.atguigu.common.config.exception;
import com.atguigu.common.result.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 全局异常处理类
*
*/
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public Result error(Exception e){
e.printStackTrace();
return Result.fail();
}
}
操作模块:service-util com.atguigu.config.exception
GlobalExceptionHandler.java中添加以下代码:
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public Result error(ArithmeticException e){
e.printStackTrace();
return Result.fail().message("执行了特定异常处理");
}
操作模块:service-util com.atguigu.config.exception
package com.atguigu.common.config.exception;
import com.atguigu.common.result.ResultCodeEnum;
import lombok.Data;
/**
* 自定义全局异常类
*
*/
@Data
public class GuiguException extends RuntimeException {
private Integer code;//状态码
private String msg;//描述信息
public GuiguException(Integer code,String msg) {
super(msg);
this.code = code;
this.msg = msg;
}
/**
* 接收枚举类型对象
* @param resultCodeEnum
*/
public GuiguException(ResultCodeEnum resultCodeEnum) {
super(resultCodeEnum.getMessage());
this.code = resultCodeEnum.getCode();
this.msg = resultCodeEnum.getMessage();
}
@Override
public String toString() {
return "GuliException{" +
"code=" + code +
", message=" + this.getMessage() +
'}';
}
}
try {
int a = 10/0;
}catch(Exception e) {
throw new GuiguException(20001,"出现自定义异常");
}
GlobalExceptionHandler.java中添加
@ExceptionHandler(GuiguException.class)
@ResponseBody
public Result error(GuiguException e){
e.printStackTrace();
return Result.fail().message(e.getMsg()).code(e.getCode());
}