- 注重版权,转载请注明原作者和原文链接
- 作者:Yuan-Programmer
- 主页:https://blog.csdn.net/weixin_47971206/article/details/121368075?spm=1001.2014.3001.5502
- 进来的小伙伴点点赞呀
demo地址预览(域名正在审核,将就ip访问):http://43.138.223.178/user-manager
花了几个小时做了一个SpringBoot+Vue的简单用户管理demo项目,适合新手教程,项目已在Gitee上开源,Gitee开源地址:https://gitee.com/yuandewei/Yuan-SpringBoot/tree/master
Gitee上开源的代码跟本次的案例的代码有些区别,本次案例稍微改了一点点,不过不影响Gitee上的项目运行,大致效果如下,功能可以访问demo地址测试哦
前言
开发环境
开发工具就不多介绍啦,就IDEA做后端,VSCode做前端,用其他的也都可以
技术
本次后端用到的技术呢: 主要就两个,SpringBoot + MyBatisPlus
前端的技术用到的技术: Vue,结合脚手架以及element ui框架开发前端
表设计
既然是用户管理嘛,肯定有用户表,我们先来设计表结构
这里说明一点,这次案例是新手教程,着重讲解功能的实现,所以用户信息参数方面就没有那么严格的校验,一般像号码这种字段肯定是设置为 char(11) 并且后端要校验的
创建Maven工程
创建一个空的Maven项目,大家应该都会了吧,还不会的小伙伴看之前的其他项目教程哦(我个人习惯创建maven工程,你喜欢直接创建springboot项目也可以,)
我这里创建好了一个 user-manager的maven项目,创建好项目,点击右下角选择自动导入,没有弹出来也没关系
引入依赖
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.1.RELEASE</version><relativePath/></parent><properties><java.version>1.8</java.version></properties><dependencies><!-- MyBatis-Plus依赖 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.0</version></dependency><!-- 数据库驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- Web启动依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- SpringBoot测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- 处理JSON的 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency></dependencies>
基本配置
创建 com.xiaoyuan.usermanager 目录,新建一个启动类 UserManagerApplication
@SpringBootApplicationpublicclassUserManagerApplication{publicstaticvoidmain(String[] args){SpringApplication.run(UserManagerApplication.class, args);}}
在 resources 资源目录下新建 application.yml 配置文件
server:
# 端口
port: 8081
spring:
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///l_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
username: xiaoyuan
password: root
mybatis-plus:
# mapper文件映射路径
mapper-locations: classpath*:mapper/*.xml
configuration:
# 打印SQL语句
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
db层
entity实体类
新建 db 包,在 db 包下新建 entity 和 mapper 两个包,在 entity 包下新建一个 User 类
注意一下,图片里 describe 有个注解忘记加上了,以下面代码为准
@DatapublicclassUser{// 主键ID@TableId(value ="id", type =IdType.AUTO)privateInteger id;// 用户名privateString username;// 昵称privateString nickname;// 密码privateString password;// 号码privateString phone;// 性别privateCharacter sex;// 描述@TableField(value ="`describe`")// describe属于数据库关键字,加上``区分privateString describe;// 创建时间@TableField(fill =FieldFill.INSERT)// insert操作时自动注入时间privateDate gmtCreate;}
mapper数据访问层
在 mapper 包下新建一个 UserMapper,继承MyBatisPlus的 BaseMapper 类,作为DAO层操作数据
MyBaitsPlus配置
这里创建的两个包都与db包同级目录
config配置类
创建一个config包,新建一个MyBatisPlusConfig 类
@ConfigurationpublicclassMyBatisPlusConfig{/* 分页插件 */@BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptor interceptor =newMybatisPlusInterceptor();// 开启
interceptor.addInnerInterceptor(newPaginationInnerInterceptor());return interceptor;}}
handler处理
新建一个 handler 包,包下新建一个 MyMetaObjectHandler 类,实现 MetaObjectHandler 类,改类有两个方法,一个insert…在向数据库插入数据的时候,会自动插入我们设置的值
@ComponentpublicclassMyMetaObjectHandlerimplementsMetaObjectHandler{/**
* 新增数据执行
*
* insert插入数据到数据库操作时执行
*/@OverridepublicvoidinsertFill(MetaObject metaObject){// 配置初始创建时间this.setFieldValByName("gmtCreate",newDate(), metaObject);}/**
* 修改数据执行
*
* update修改数据库数据操作时执行
*/@OverridepublicvoidupdateFill(MetaObject metaObject){}}
这里的 this.setFi… 第一个参数对应的是User实体类的名字,不是表中的字段名,第二个参数的默认值
Vo对象
新建一个 vo 包,用于和前端交互数据的类
统一结果返回类
vo 包下新建一个 R 类,作为我们统一返回给前端数据的类,
@DatapublicclassR{privateBoolean success;privateInteger code;privateString message;privateMap<String,Object> data =newHashMap<>();// 把构造方法私有化privateR(){}// 成功静态方法publicstaticRok(){R r =newR();
r.setSuccess(true);
r.setCode(200);
r.setMessage("成功");return r;}// 失败静态方法publicstaticRerror(){R r =newR();
r.setSuccess(false);
r.setCode(201);
r.setMessage("失败");return r;}publicRsuccess(Boolean success){this.setSuccess(success);returnthis;}publicRmessage(String message){this.setMessage(message);returnthis;}publicRcode(Integer code){this.setCode(code);returnthis;}publicRdata(String key,Object value){this.data.put(key, value);returnthis;}}
vo 包下新建 QueryParam 类
@DatapublicclassQueryParam{// 用户名privateString username;// 昵称privateString nickname;// 号码privateString phone;// 性别privateString sex;// 创建时间privateString time;}
这里讲一下吧,这个类是用来干嘛的呢?我们在效果展示的时候,是不是在上面看到有5个筛选条件,这5个筛选条件参数刚好对应类中的5个属性,我们统一封装起来
service业务层
新建一个 service 包,包下新建一个 UserService 接口类,继承MyBatisPlus的 IService 类
service 包下新建 impl 包,新建一个 UserServiceImpl 实现类,继承MyBatisPlus的 ServiceImpl 类,实现我们自己的 UserService 类
我们先在 UserService 接口类定义五个方法,接下来我们一一实现这五个功能
publicinterfaceUserServiceextendsIService<User>{/**
* 添加用户
* @param user
* @return
*/RinsertUser(User user);/**
* 删除单个用户
* @param id 用户编号
* @return
*/RdeleteUser(Integer id);/**
* 删除多个用户
* @param ids 用户编号集合
* @return
*/RdeleteUserMore(List<Integer> ids);/**
* 编辑用户
* @param user
* @return
*/RmodifyUser(User user);/**
* 分页查询用户列表
* @param index 当前页
* @param size 每页显示数量
* @param queryParam 筛选条件对象
* @return
*/RfindUserList(Integer index,Integer size,QueryParam queryParam);/**
* 查询单个用户详细信息
* @param id 用户编号
* @return
*/RgetUserInfoById(Integer id);}
添加用户
UserServiceImpl 实现类里实现添加用户方法,这里只做了简单的非空判断,其他参数的非法性校验可以自己额外完善
@OverridepublicRinsertUser(User user){if(user ==null)returnR.error().message("参数错误");// 用户名String username = user.getUsername();// 构建条件对象, 查询是否已经存在用户名QueryWrapper<User> wrapper =newQueryWrapper<>();
wrapper.select("id");
wrapper.eq("username", username);
wrapper.last("limit 1");// 查询判断, 如果查询出来有数据, 则不为nullif(this.baseMapper.selectOne(wrapper)!=null)R.error().message("该用户名已存在");// 执行插入数据操作returnthis.baseMapper.insert(user)==0?R.error().message("添加用户失败"):R.ok();}
删除用户
删除用户就比较简单啦,肯定有人会问,前端做了非空校验,后端怎么还要做参数校验校验呢?
其实前后端都做是最好的,有绕过前端发送请求的,就比如我们自己测试接口时用的postman, apifox,后端多做一层校验,避免直接操作数据库,我这里也是比较简单的做了校验
@OverridepublicRdeleteUser(Integer id){if(id ==null|| id <=0)returnR.error().message("参数错误");returnthis.baseMapper.deleteById(id)==0?R.error().message("删除失败"):R.ok();}
一键删除多个用户
删除多个用户也没难度,将多个用户的编号放到一个集合中,一次删除多个
@OverridepublicRdeleteUserMore(List<Integer> ids){if(ids.size()==0)returnR.error().message("参数错误");returnthis.baseMapper.deleteBatchIds(ids)!= ids.size()?R.error().message("删除失败"):R.ok();}
编辑用户
这个也没什么难度,做个简单校验,然后根据ID更新用户信息(参数其他合法性校验可以自己额外做哦)
@OverridepublicRmodifyUser(User user){if(user ==null|| user.getId()==null|| user.getId()<=0)returnR.error().message("参数错误");returnthis.baseMapper.updateById(user)==0?R.error().message("编辑用户失败"):R.ok();}
获取单个用户信息
先实现这个吧,这个也很简单,直接通过用户编号查询用户的信息返回即可
@OverridepublicRgetUserInfoById(Integer id){if(id ==null|| id <=0)returnR.error().message("参数错误");returnR.ok().data("userInfo",this.baseMapper.selectById(id));}
查询用户列表
先创建编写SQL语句的文件,在 resources 下新建 mapper 包,包下新建 UserMapper.xml 文件
代码中的SQL语句不能包含注释,所以我在图片给出了每行的注释,代码中删掉了,对应看着
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- namespace路径根据自己而定 --><mappernamespace="com.xiaoyuan.usermanager.db.mapper.UserMapper"><!-- 查询用户列表(带多条件) --><selectid="findUserList"resultType="com.xiaoyuan.usermanager.db.entity.User">
select *
from user
<where>
1 = 1
<iftest="queryParam != null"><iftest="queryParam.username != null and queryParam.username != ''">
and username = #{queryParam.username}
</if><iftest="queryParam.nickname != null and queryParam.nickname != ''">
and nickname like CONCAT(#{queryParam.nickname}, '%')
</if><iftest="queryParam.phone != null and queryParam.phone != ''">
and phone = #{queryParam.phone}
</if><iftest="queryParam.sex != null and queryParam.sex != ''">
and sex = #{queryParam.sex}
</if><iftest="queryParam.time != null and queryParam.time != ''">
and DATE_FORMAT(create_time,'%Y-%m-%d') = #{queryParam.time}
</if></if></where></select></mapper>
我们大致来分析一下上面的SQL语句,首先 select * from user这里没毛病吧,咋们是管理用户,所有信息都得要上,* 查询所有
后面 where 里有个 1 = 1 作用是恒等式,为了防止没有做筛选条件时,queryParam 条件对象为 null 导致 where 里面没东西,执行SQL语句的时候就会出现 where 后面没加东西,就会抛出异常
CONCAT 函数的作用是拼接,当然你直接用下面这样也行,推荐还是使用CONCAT,以后会遇到的,还有就是 % 只写右边就可以了,避免全表扫描,采用单模糊查询
and nickname like #{queryParam.nickname} '%'
DATE_FORMAT 函数作用是对时间进行格式化,
2022-05-06 11:17:36
转换为
2022-05-06
SQL语句写好了,更新一下我们的 UserMapper 类,映射到 UserMapper.xml 文件的SQL语句,函数名就是 < select id=“xxxx”> 这里id的值
@RepositorypublicinterfaceUserMapperextendsBaseMapper<User>{/**
* 查询用户列表
* @param page 分页对象
* @param queryParam 筛选条件
* @return
*/IPage<User>findUserList(Page<User> page,QueryParam queryParam);}
最后就是在 UserServiceImpl 实现查询用户列表的方法
@OverridepublicRfindUserList(Integer index,Integer size,QueryParam queryParam){if(index ==null|| size ==null|| index <=0|| size <=0){returnR.error().message("参数错误");}elseif(size >10){returnR.error().message("一次最多10条数据");}// 构建分页对象Page<User> page =newPage<>(index, size);// 查询IPage<User> iPage =this.baseMapper.findUserList(page, queryParam);// 回传两个数据, 一个 userList --> 用户数据列表, 一个 total -> 总页数returnR.ok().data("userList", iPage.getRecords()).data("total", iPage.getTotal());}
controller控制层
这里是最后一步了,处理接口,我们采用 RESTful 的风格,相同的路径处理不用的操作
@RestController@RequestMapping("/user")publicclassUserController{@AutowiredprivateUserService userService;// 添加用户@PostMapping("")publicRinsertUser(@RequestBodyUser user){return userService.insertUser(user);}// 删除单个用户@DeleteMapping("{id}")publicRdeleteUser(@PathVariable(value ="id")Integer id){return userService.deleteUser(id);}// 删除多个用户@DeleteMapping("")publicRdeleteUserMore(@RequestBodyList<Integer> ids){return userService.deleteUserMore(ids);}// 编辑用户@PutMapping("")publicRmodifyUser(@RequestBodyUser user){return userService.modifyUser(user);}// 查询用户列表@PostMapping("{index}/{size}")publicRfindUserList(@PathVariable(value ="index")Integer index,@PathVariable(value ="size")Integer size,@RequestBodyQueryParam queryParam){return userService.findUserList(index, size, queryParam);}// 根据用户编号查询用户信息@PostMapping("{id}")publicRgetUserInfo(@PathVariable(value ="id")Integer id){return userService.getUserInfoById(id);}}
接口测试
最后,在启动类加上两个注解,一个 MapperScan 扫描我们的 mapper 类,一个 ComponentScan 扫描我们的组件
@SpringBootApplication@MapperScan(basePackages ={"com.xiaoyuan.usermanager.db.mapper"})@ComponentScan({"com.xiaoyuan"})publicclassUserManagerApplication{publicstaticvoidmain(String[] args){SpringApplication.run(UserManagerApplication.class, args);}}
OK,到这里功能已经全部做好了,我们测试一下接口,运行启动类,我这里只展示部分接口的测试接口,全部接口我已经测试过,都没有问题了
大家可以自己去测试每个接口,我用的时 ApiFox 工具,非常好用,还能一键导出接口文档,可以显示接口耗时,分组分项目分接口分环境,非常的方便
添加用户,成功插入数据
当然,你可以在代码里面用测试,新建一个 SpringBoot 的测试类,测试业务层也可以的,如下
其他的就不一一放出来了,大伙自己去试试吧~
扩展(拦截器,权限拦截)
像管理这种一般都会有权限的,总不可能每个人都能访问自己的接口来增删改查用户吧?如何做到不给其他人访问自己的接口,判断别人是否有权限访问呢?
下面我们简单来设计一下,理解大概过程
首先,usermanager 主目录下创建两个包,一个 interceptor 拦截器,一个 WebMVCConfig MVC的配置类
interceptor 包下新建一个 PermissionInterceptor 类,实现 HandlerInterceptor 类
拦截器有三个阶段,preHandle -> postHandle -> afterCompletion,依次按顺序执行,只有前一个return返回true,才会执行下一个阶段方法,简单介绍三个阶段
- preHandle:controller执行目标方法之前执行,一般用于权限验证等操作
- postHandle:controller执行完目标方法返回(如调用service业务层的方法),在前端数据渲染之前执行,一般用于更改视图数据
- afterCompletion:整个接口访问执行完毕,前端数据渲染完成,执行此方法,一般用于资源释放等操作
很明显,我们这个权限验证拦截就是在 preHandle 阶段去全性
这个
code
呢就是我们自己定义的权限码,我这里只是随便敲了长长的一段,反正自己知道就好,你可以做加密处理,这里我简单模拟一下
@ComponentpublicclassPermissionInterceptorimplementsHandlerInterceptor{privatestaticfinalString code ="dwagfhwhgiawpfwabifpjwaidjwaidwiafihwigfhwaigwhaipgwaihiwahifhwdefef";@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{// 获取请求头里面的 Authentication 属性值String authentication = request.getHeader("Authentication");// 两者相等 -> 通过放行, 两者不相等 -> 不通过不放行if(code.equals(authentication)){// 放行returntrue;}else{// 不放行, 回传没有权限
response.setContentType("text/html;charset=utf-8");
response.getWriter().println(JSON.toJSONString(R.error().message("没有操作权限")));returnfalse;}}}
config 包下新建一个 WebMVCConfig 类,实现 WebMvcConfigurer 类,设置拦截器
@ConfigurationpublicclassWebMVCConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){
registry.addInterceptor(newPermissionInterceptor())// 添加拦截器.addPathPatterns("/user/**");// 选择拦截路径,拦截/user下的所有请求}}
好的,我们接下来看看效果,当然,就不适合在代码用SpringBoot测试类啦,因为校验请求头
我们采用接口测试工具,老样子,我使用的是ApiFox,你可以使用postman等其他工具
首先我们继续运行一下之前写好的请求路径(没加Authentication 权限码),可以看到,我们访问的是查询用户列表接口,显示没有权限
接下来我们在 header 请求头加上我们的 Authentication 的权限码,同样的路径,测试一下
是不是就成功访问到了
我们随便改错一个字母,也成功显示没有权限,除非你的权限码被别人知道了,或者被破解了(设置的复杂一点再加密基本不可能被破),不然你的接口别人访问不了
- 都看到这里啦,点点赞呀😋
- 感谢阅读😘
- 有更好的想法和建议可以评论区发言哦
版权归原作者 Yuan-Programmer 所有, 如有侵权,请联系我们删除。