准备工作和环境搭建
前后端用到的技术栈:弄懂并完全理解这个项目就基本能自己开发相似的其他管理类项目了。
开发模式:前后端分离的基于接口文档的开发模式。
后端idea手动创建工程
idea23.1、jdk21 (这个安装请查看我的相关文章)
创建完后需要 手动添加resource目录 和 application.yml文件。
创建结构如下:
引入相关依赖
当前工程继承一个父工程springboot工程的初始化starter
导入相关依赖。(按需求导入:例如web依赖、mybatis依赖、mysql驱动依赖)
建议直接拷贝已经有的项目文件依赖(难得敲代码)
也可用安装插件方便代码书写(例如)
到此后端开发环境,简单创建基本完成,其他业务需求按需要添加。
前端工程创建
后端开发接口的流程
基本步骤:
确定包层级
(略)
定义 统一响应类
Result实体类上(注意 构造方法上 注解的 使用)
//类上注解 @AllArgsConstructor //提供全参数的 构造方法(泛型) .解决个问题 Result<> @NoArgsConstructor //提供无参数的 构造方法 .解决个问题 @Data //提供 get set toString 方法
实体类上的注解
//类上注解 @Data //提供 get set toString 方法 //属性上注解 @NotNull @JsonIgnore // 忽略私密数据 let springmvc ignore password when converting object to json @NotEmpty @Email
全局异常处理器
//类上 @RestControllerAdvice //用于这个类是用来处理异常的 //方法上 @ExceptionHandler({Exception.class}) //{Exception.class}作用所以的异常。异常都按这个方法的返回值返回。(满足接口文档要求!!!)
方法:传参(传入异常)得到一个返回值(接口文档统一返回值)!!
ioc容器管理注解
@Component //普通类放入(注入)ioc容器中 @Configuration //配置类放入ioc容器中 @Autowired //注入资源注解
参数 校验注解!!!(企业中一定要校验)
@Validated @Pattern(regexp = "^\\S{5,16}$") final String username @URL final String avatarUrl @NotNull private Integer id;//主键ID @NotEmpty @Email private String email;//邮箱
手动校验
if(){ }else{ }
分组校验
需求:解决不同接口功能,组中的校验麻烦
1、实体类定义分组接口
public interface Add extends Default { } public interface Update extends Default{ } // 分组可以继承 // 如果不指定分组,则属于默认分组 // 默认分组和自定义的分组是平行状态,除非继承。如果继承,那么默认不写分组,就可以在所有用到的地方校验 }
2、实体类的属性上指定归属的分组
@NotNull(groups = {Update.class}) //用于更新接口分组校验 private Integer id;//主键ID
3、方法参数参数上,指定校验的分组
public Result<String> update(@RequestBody @Validated(Category.Update.class) Category category) {
自定义校验(自定义注解)!!!
过程:程序运行到自定义注解——>自定义的接口——>运行校验的方法。
自定义注解接口
(anno)存放自定义注解接口 的包
可以根据spring的 注解源码修改
@Documented //元注解 允许定义的注解抽取到我当 @Target({ElementType.FIELD}) //元注解 标识注解可以用的位置(类、属性、方法上) @Retention(RetentionPolicy.RUNTIME) //元注解 指明这个注解保留到什么阶段(编译、源码、运行时阶段) @Constraint(validatedBy = {StateValidation.class}) //谁给这个注解提供校验规则(方法) public @interface State { //提供校验后的失败信息 String message() default "state参数的值只能是已发布或草稿"; //指定分组 Class<?>[] groups() default {}; //负载 可以获取到State注解的附加信息 (必须提供,但一般用不着) Class<? extends Payload>[] payload() default {}; }
自定义校验注解的类
(validaion)存放自定义注解 类的包
public class StateValidation implements ConstraintValidator<State, String> { @Override public boolean isValid(final String value, final ConstraintValidatorContext constraintValidatorContext) { if (value == null) { return false; } if (value.equals("已发布") || value.equals("草稿")){ return true; } return false; } }
先创建接口再创建类:体现了———单继承,多实现。(只能继承一个父类,却能实现多个接口)
MVC注解
@RequestBody Map<String, String> params
三层注解
@RestController @Service @Mapper
三层架构注册接口实现
(在三层架构里面实现相关业务开发)
controller层
//类上的注解 @RestController // controller层注解 @RequestMapping("/user") // 请求主干路线(主) @Validated //参数校验框架 springValidated 的参数校验注解(主) // 注入资源注解 @Autowired //方法上的注解(分) @GetMapping("/userInfo") // 查关系 @PostMapping("/register") // 改关系(json) @PatchMapping("/updateAvatar") // 改关系(局部/单一) //参数上的注解 @Pattern //校验注解(分) @Pattern(regexp = "^\\S{5,16}$") final String username。定义在检验参数的属性前。(不满足直接抛出/定义一个全局异常处理器抛出(全局异常处理器 满足接口文档 异常问题信息)) @RequestBody //传入的是json格式的数据(用实体封装) @RequestParam //
service层
service.imlp层
//类上的注解 @Service //注入资源注解 @Autowired //方法上的注解 @Override
mapper层
//类上的注解 @Mapper //方法上的注解 @增删改查注解(简单sql实现) //复杂SQL 用mybatis
测试
工具:postman (不会用 ?可以参考官方文档)
测试返回形式
200 // 恭喜你成功了 401 //未授权 eg:jwt令牌过期 404 // 406 // 统一响应类上缺少@Data注解无法实现get set toString方法 500 //
业务流程!!!
用户
第一步:输入参数校验(不满足条件则定义全局处理器捕捉异常)
注册:
先查询 判断用户是否存在(存在就直接返回存在,不存在就业务方法注册)
登录:登录成功 生成jwt令牌 并自动携带在浏览器的请求头中
先查询 判断用户名是否存在(不存在直接跳出),存在再判断密码是否正确。 登录成功:生成jwt令牌并还回。
登录认证!!
(使用拦截器,校验jwt令牌)!!!
jwt令牌:身份识别的作用(将用户信息放在令牌中,减少每次数据库查询)有效性,合法性,防篡改。
注意!它由三部分组成。中间部分不要放私密数据。
特性:错误或者过期。
jwt令牌相关代码放在工具类中(生成和校验)
拦截器 interceptor
interceptor包内
需求:多个接口有同样的技术要完成。(作用其他接口的jwt令牌校验)
注册 拦截器
config包内配置 web功能
将拦截器注册到webmvc内,方便在web内就拦截所以接口校验
调用放行接口方法(登录接口,注册接口不拦截)指明放行接口
本地线程 ThreadLocal
ThreadLocal 线程隔离机制 !!!(解决多个用户可以访问这个系统,而不混乱的问题。每一个用户信息在自己独立的线程中,在登录时候拦截器将信息注入webMVC中) 实现同一个线程共享数据!!!
提供线程局部变量 ,可以存储数据,线程隔离数据独立,互不影响更,安全。(get set)
查询信息
从线程中获取用户对象(需求)————>service层(业务)————>mapper层(实现)
修改信息(json参数用实体封装)
登录名称(不可修改,例如qq登录,微信登录,电话登录的信息) 用户名(可修改)
实体参数 封装json数据的校验
先在用来封装的实体类参数上添加注解——>实体类参数前添加注解 ——> 定义用于校验的类(包)
//实体类 属性参数上添加注解 @NotNull //值不能为unll private Integer id;//主键ID @NotEmpty //值不能为unll,且字符串内容不能为空 @Pattern(regexp = "^\\S{1,10}$") private String nickname;//昵称 @Email //满足邮箱格式 private String email;//邮箱 //实体参数前的注解 @Validated //必须添加这个注解才能扫描到
####
用户其它业务接口流程
(只能当前用户操作!!!mvc用户校验已经实现)
都需要先 查询回显信息——>再可修改信息
更新用户信息
更新数据记得每一次都需要修改这一次的更新时间!!!(可以在mapper层 自定义注解解决 / 调用mysql的now函数解决 update_time=now() !!!!)
时间更新方法
@Update("update user set nickname=#{nickname}, email=#{email}, update_time=now() where id=#{id}") //个人推荐使用
方法和参数上的注解
@PutMapping("/update") //方法上注解 @RequestBody @Validated final User user //参数上注解
更新头像(图片)
(图片)用阿里云的oss管理图片———>本地只存储图片的路径url
@PatchMapping("/updateAvatar") //方法上注解 @RequestParam @URL final String avatarUrl //参数上注解
更新用户密码
传入参数(原密码,新密码 ,确认密码)——>校验(原密码与数据库原密码——>新密码与确认密码比较)——>业务 更新密码
其他业务接口开发
(只能当前用户操作!!!mvc用户校验已经实现)
注意接口文档请求数据样例!!返回数据样例!!!date (数据类型)
分类管理
(category) eg:文章 分类管理
实体类补充属性值(set方法补充) 。
当前时间(时间now中获得) 。
时间样式 实现
在实体类的时间属性上添加注解
//接口文档有指定返回时间样式的时候用,文档需求就用 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") //指定时间样式的注解
当前用户id(threadlocal中获得)。
分组校验
需求:解决不同接口功能,组中的校验麻烦
1、实体类定义分组接口
public interface Add extends Default { } public interface Update extends Default{ } // 分组可以继承 // 如果不指定分组,则属于默认分组 // 默认分组和自定义的分组是平行状态,除非继承。如果继承,那么默认不写分组,就可以在所有用到的地方校验 }
2、实体类的属性上指定归属的分组
@NotNull(groups = {Update.class}) //用于更新接口分组校验 private Integer id;//主键ID
3、方法参数参数上,指定校验的分组
public Result<String> update(@RequestBody @Validated(Category.Update.class) Category category) {
管理(crud)
分类管理下面的子集,在事务中与分类管理绑定。
eg:文章管理、部门内人员的管理、某个部门财产的管理
条件分页 查询展示
列表分页展示(代码流程都类似,可单独抽出模块)
注意!!映射配置文件的路径一定和mapper接口路径一致
定义一个对象来封装分页查询的结果,为了满足还回的数据类型。(在pojo包内创建PageBean类)
//分页返回结果对象 @Data @NoArgsConstructor @AllArgsConstructor // <T> public class PageBean <T>{ private Long total;//总条数 private List<T> items;//当前页数据集合 }
方法实现(imlp 用Mybatis的分页查询插件解决。PageHeler 方便!!!)
必须传入参数 和 非必须传入参数
//文章的分页查询。必须传入参数 和 非必须传入参数 @RequestParam(required = false) @GetMapping public Result<PageBean<Article>> list(final Integer pageNum,final Integer pageSize, @RequestParam(required = false) final Integer categoryId, @RequestParam(required = false) final String state) { final PageBean<Article> articleList = articleService.list(pageNum, pageSize, categoryId, state); return Result.success(articleList); //别名 }
mapper接口到映射文件
动态SQL
SQL事务
多表查询:一对多,多对多,一对一等关系。
文件上传
使用阿里云oss
Redis优化
这个可以查看我其他博客