daily- blog项目

快速搭建项目

创建完数据库对应的表 等操作之后,进入idea

使用EasyCode快速创建工程

  1. 首先连接数据库
  2. 然后在对应的表上点击,然后GeneraleCode
  3. 如果想要删除表名中的前缀 ,就可以使用removePre
  4. 创建包、实体类、dao等都可以自动生成

image-20230311094114784

自动生成的代码修改

根据自己的需求,修改相应的代码 比如 : 删除其中的继承东西等

我们这里暂时不做任何修改

image-20230311101039158

接下来就是修改实体类对应的信息

比如: 我们需要添加实体类与数据库中表的对应关系用 @TableName("sg_article") , 对于主键自增的字段使用@TableId

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
/**
* 文章表(Article)表实体类
*
* @author Ray2310
* @since 2023-03-11 09:42:17
*/
@SuppressWarnings("serial")
@TableName("sg_article")
public class Article {

@TableId
private Long id;
//标题
private String title;
//文章内容
private String content;
//文章摘要
private String summary;
//所属分类id
private Long categoryId;
//缩略图
private String thumbnail;
//是否置顶(0否,1是)
private String isTop;
//状态(0已发布,1草稿)
private String status;
//访问量
private Long viewCount;
//是否允许评论 1是,0否
private String isComment;

private Long createBy;

private Date createTime;

private Long updateBy;

private Date updateTime;
//删除标志(0代表未删除,1代表已删除)
private Integer delFlag;
}

修改service、dao等层

对于Mapper层 ,因为使用的是Mybatis-plus,所以使用的Mapper就方便很多

1
2
public interface ArticleMapper extends BaseMapper<Article> {
}

service层

1
2
public interface ArticleService extends IService<Article> {
}

impl实现类上加注解

1
2
3
4
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
}

配置Controller及其注意事项

Controller层

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RequestMapping("/article")
public class ArticleController {
@Resource
private ArticleService articleService;

@GetMapping("/list")
public List<Article> test(){
List<Article> list = articleService.list();
return list;
}
}

注意事项:

对于使用模块化项目,我们再配置完成后需要重新install项目,这样不同模块配置的内容才会加载出来

image-20230311101949457

表的设计分析

image-20230311102627224

通用的响应实体类 和 响应枚举

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
package com.blog.domain;

import com.blog.enums.AppHttpCodeEnum;
import com.fasterxml.jackson.annotation.JsonInclude;

import java.io.Serializable;

/**
* 响应类
* @param <T>
* @author Ray2310
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> implements Serializable {
private Integer code;
private String msg;
private T data;

public ResponseResult() {
this.code = AppHttpCodeEnum.SUCCESS.getCode();
this.msg = AppHttpCodeEnum.SUCCESS.getMsg();
}

public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}

public ResponseResult(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}

public ResponseResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}

public static ResponseResult errorResult(int code, String msg) {
ResponseResult result = new ResponseResult();
return result.error(code, msg);
}
public static ResponseResult okResult() {
ResponseResult result = new ResponseResult();
return result;
}
public static ResponseResult okResult(int code, String msg) {
ResponseResult result = new ResponseResult();
return result.ok(code, null, msg);
}

public static ResponseResult okResult(Object data) {
ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getMsg());
if(data!=null) {
result.setData(data);
}
return result;
}

public static ResponseResult errorResult(AppHttpCodeEnum enums){
return setAppHttpCodeEnum(enums,enums.getMsg());
}

public static ResponseResult errorResult(AppHttpCodeEnum enums, String msg){
return setAppHttpCodeEnum(enums,msg);
}

public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){
return okResult(enums.getCode(),enums.getMsg());
}

private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String msg){
return okResult(enums.getCode(),msg);
}

public ResponseResult<?> error(Integer code, String msg) {
this.code = code;
this.msg = msg;
return this;
}

public ResponseResult<?> ok(Integer code, T data) {
this.code = code;
this.data = data;
return this;
}

public ResponseResult<?> ok(Integer code, T data, String msg) {
this.code = code;
this.data = data;
this.msg = msg;
return this;
}

public ResponseResult<?> ok(T data) {
this.data = data;
return this;
}

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}



}
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
package com.blog.enums;

/**
* 专门存放枚举的类
*/
public enum AppHttpCodeEnum {
// 成功
SUCCESS(200,"操作成功"),
// 登录
NEED_LOGIN(401,"需要登录后操作"),
NO_OPERATOR_AUTH(403,"无权限操作"),
SYSTEM_ERROR(500,"出现错误"),
USERNAME_EXIST(501,"用户名已存在"),
PHONENUMBER_EXIST(502,"手机号已存在"), EMAIL_EXIST(503, "邮箱已存在"),
REQUIRE_USERNAME(504, "必需填写用户名"),
CONTENT_NOT_NULL(506, "评论内容不能为空"),
FILE_TYPE_ERROR(507, "文件类型错误,请上传png文件"),
USERNAME_NOT_NULL(508, "用户名不能为空"),
NICKNAME_NOT_NULL(509, "昵称不能为空"),
PASSWORD_NOT_NULL(510, "密码不能为空"),
EMAIL_NOT_NULL(511, "邮箱不能为空"),
NICKNAME_EXIST(512, "昵称已存在"),
LOGIN_ERROR(505,"用户名或密码错误");
int code;
String msg;

AppHttpCodeEnum(int code, String errorMessage){
this.code = code;
this.msg = errorMessage;
}

public int getCode() {
return code;
}

public String getMsg() {
return msg;
}
}

调用前端接口时出现权限不够

image-20230311130111226

同时会出现无法显示的问题, 那是因为我们前后端不在同一个域中,需要在mvc配置文件中配置跨域连调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration
public class WebConfig implements WebMvcConfigurer {

/**
* 实现跨域配置
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路径
registry.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOriginPatterns("*")
// 是否允许cookie
.allowCredentials(true)
// 设置允许的请求方式
.allowedMethods("GET", "POST", "DELETE", "PUT")
// 设置允许的header属性
.allowedHeaders("*")
// 跨域允许时间
.maxAge(3600);
}

}

博客前台

热门文章列表

image-20230311102117772

需求

查询出浏览量最高的前10篇文章的信息。 要求展示文章标题和浏览量。八能够让用户自己点击跳转到具体的文章详请进行浏览

注意 : 不要把草稿展示出来 ,不要把删除的文章查询出来

接口设计

将返回值使用 通用的返回响应

controller层

1
2
3
4
5
6
//todo 查询热门文章
@GetMapping("/hotArticleList")
public ResponseResult hotArticleList(){
//查询热门文章,然后封装成ResponseResult ,然后返回
return articleService.hotArticleList();
}

service层实现

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
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {


//todo 查询热门文章
/*需求:
查询出浏览量最高的前10篇文章的信息。 要求展览示文章标题和浏量。八能够让用户自己点击跳转到具体的文章详请进行浏览
注意 :`不要把草稿展示出来 ,不要把删除的文章查询出来`
*/
@Override
public ResponseResult hotArticleList() {
//查询热门文章 封装返回
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
//用 LambdaQueryWrapper 写查询条件
queryWrapper.eq(Article::getStatus,0);
queryWrapper.orderByDesc(Article::getViewCount);
Page<Article> page = new Page(1,10);
//判空
if (ObjectUtils.isEmpty(page)){
return ResponseResult.errorResult(AppHttpCodeEnum.valueOf("暂无热门文章"));
}
page(page,queryWrapper);
List<Article> articles = page.getRecords();

return ResponseResult.okResult(articles);
}
}

这里老师的代码有错误,分页没有被加进去 ,我们自己需要修改

使用VO优化

image-20230311141200213

需求 : 从我们的出的接口的返回值我们就可以看出,我们需要的只是博客内容的访问量 以及 博客名 而不是所有的内容都返回。这样不仅会造成信息泄露 ,如果文章字数过多,还会造成内存额外消耗。所以我们需要进行优化

经过处理的类我们叫做 VO类型的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 接口文档中要去响应回去的字段
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HotArticle {
//文章id
private Long id;
//文章标题
private String title;
//访问量
private Long viewCount;
}

然后再通过类型拷贝,就可以将我们需要的数据返回,而不是返回所有【拷贝的原理是两个类的属性相同】

1
2
3
4
5
6
7
8
9
10
List<Article> articles = page.getRecords();
List<HotArticle> hotArticles = new ArrayList<>();
// 类的赋值拷贝 Article中的某些字段 ---> HotArticle
//使用BeanUtils进行拷贝
for (Article article : articles){
HotArticle vo = new HotArticle();
BeanUtils.copyProperties(article,vo);
hotArticles.add(vo);
}
return ResponseResult.okResult(hotArticles);

封装Bean拷贝工具类

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
/**
* 有关拷贝工具类的封装
* @author Ray2310
*/
public class BeanCopyUtils {

private BeanCopyUtils(){
}

/**
* 实现属性拷贝
* @param source
* @param clazz
* @return
*/
public static <V> V copyBean(Object source,Class<V> clazz){
//利用反射创建目标对象
V result = null;
try {
result = clazz.newInstance();
//实现属性的拷贝
BeanUtils.copyProperties(source,result);
} catch (Exception e) {
e.printStackTrace();
}
//返回结果
return result;
}

/**
* 如果是list集合的属性拷贝 ,就直接调用该方法
* @param list 源列表
* @param clazz 目标对象
* @param <V> 需要转换的类型的泛型
* @return 返回转换后的集合
*/
public static <O,V> List<V> copyBeanList(List<O> list , Class<V> clazz ){
List<V> collect = list.stream()
.map(o -> copyBean(o, clazz))
.collect(Collectors.toList());
return collect;
}
}

分类列表需求

image-20230311145817223

需求

页面上需要展示分类列表, 用户可以通过点击具体的分类查看该分类下的文章列表。

注意: 1. 要求只展示有发布展示文章的分类 。 2. 必须是正常状态的分类

表信息

image-20230311150302648

接口设计

image-20230311151015334

1
2
3
4
5
6
7
8
9
10
11
12
13
@RequestMapping("/category")
@RestController
public class CategoryController {

@Resource
private CategoryService categoryService;

//todo 分类请求
@GetMapping("/getCategoryList")
public ResponseResult getCategoryList(){
return categoryService.getCategoryList();
}
}

思路:

  1. 先在文章表中查询 status(文章发布or未发布)为 0 的,也就是发布了的 。还有就是未删除的
  2. 查出上一步的之后只需要查分类id就可以了(category_id)
  3. 然后再到category表中查出对应的名称即可

实现

首先我们使用EasyCode生成对应的mapper、pojo实体类等

完成service层等的代码实现

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
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {

@Resource
private ArticleService articleService;


//todo 分类请求
@Override
public ResponseResult getCategoryList() {
//1. 先在文章表中查询 status(文章发布or未发布)为 0 的,也就是发布了的 。还有就是未删除的
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_PUT);
List<Article> articles = articleService.list(queryWrapper);
//2. 查出上一步的之后只需要查分类id就可以了(category_id)
//todo 函数式编程 ,用来将查询到的id查询category
Set<Long> categoryIds = articles.stream()
.map(new Function<Article, Long>() {
@Override
public Long apply(Article article) {
return article.getCategoryId();
}
}).collect(Collectors.toSet());
//3. 然后再到category表中查出对应的名称即可 ,还需要判断分类的状态是正常的
List<Category> categories = listByIds(categoryIds);
//4. 判断分类的状态是正常的
List<Category> collect = categories.stream().filter(category -> category.getStatus().equals(SystemConstants.ARTICLE_CATEGORY_STATUS)).collect(Collectors.toList());
//5. 封装状态
List<CategoryVo> categoryVoList = BeanCopyUtils.copyBeanList(collect, CategoryVo.class);

return ResponseResult.okResult(categoryVoList);
}
}

注意点: 了解函数式编程

分页查询文章列表

需求

在首页查询文章页面都有文章列表 ,首页 :查询所有文章

分类页面: 查询对应分类的文章列表

要求 ::1. 只能查询正式发布的文章 2. 置顶文章要显示在最前面

接口设计

image-20230312143807864

实现

1
2
3
4
5
6
7
8
//todo 文章分页
@GetMapping("/articleList")
//如果在请求路径后面直接 /+值的 需要使用 @PathVariable
//如果是从请求体中获取出来的就需要加上 @RequestBody
public ResponseResult articleList(Integer pageNum, Integer pageSize, Long categoryId){

return articleService.articleList(pageNum,pageSize,categoryId);
}

按照要求 ,我们需要将查询到的信息传入前端, 但是 不能将全部信息传入 ,所以就需要将查询到的信息进行封装

List<ArticleListVo> articleListVo = BeanCopyUtils.copyBeanList(page.getRecords(), ArticleListVo.class);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//todo 文章分页
/*
在首页查询文章页面都有文章列表 ,首页 :查询所有文章
分类页面: 查询对应分类的文章列表
要求 ::1. 只能查询正式发布的文章 2. 置顶文章要显示在最前面
*/
@Override
public ResponseResult articleList(Integer pageNum, Integer pageSize, Long categoryId) {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper();
//如果有categoryId ,那么查询和传入的就需要相同
queryWrapper.eq(Objects.nonNull(categoryId) && categoryId > 0,Article::getCategoryId,categoryId);

//状态 : 正式发布
queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_PUT);
//置顶的文章(对isTop进行排序)
queryWrapper.orderByDesc(Article::getIsTop);
//分页查询
Page<Article> pageN = new Page<>(pageNum,pageSize);
Page<Article> page = page(pageN, queryWrapper);
//封装查询结果
List<ArticleListVo> articleListVo = BeanCopyUtils.copyBeanList(page.getRecords(), ArticleListVo.class);
PageVo pageVo = new PageVo(articleListVo, page.getTotal());
return ResponseResult.okResult(pageVo);
}

因为我们封装的是categoryName,但是查询出来的确实categoryId,所以需要在查询后进行给categoryName赋值

1
2
3
4
for (Article article  : records){
Category category = categoryService.getById(article.getCategoryId());
article.setCategoryName(category.getName());
}

分页配置

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* myBatisPlus分页配置
*/
@Configuration
public class MyBatisPlusConfig {

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mb = new MybatisPlusInterceptor();
mb.addInnerInterceptor(new PaginationInnerInterceptor());
return mb;
}
}

修改时间格式配置

在WebConfig中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//todo 修改时间格式 【yyyy-MM-dd HH:mm:ss】 
// 其实也可以直接在相关字段上加注解
// @JsonFormat(timezone="GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
// 只是这样做可以使所有的时间格式都转换
@Bean//使用@Bean注入fastJsonHttpMessageConvert
public HttpMessageConverter fastJsonHttpMessageConverters() {
//1.需要定义一个Convert转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");

// SerializeConfig.globalInstance.put(Long.class, ToStringSerializer.instance);

fastJsonConfig.setSerializeConfig(SerializeConfig.globalInstance);
fastConverter.setFastJsonConfig(fastJsonConfig);
HttpMessageConverter<?> converter = fastConverter;
return converter;
}

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(fastJsonHttpMessageConverters());
}

文章详情接口

需求

要在文章列表页面点击阅读全文时能够跳转到文章详情页面 ,可以让用户阅读文章正文

要求: 1. 要在文章详情中展示其分类名

接口信息

image-20230312164517847

响应格式

image-20230312164556027

实现

1
2
3
4
5
6
//todo 查询文章详情
@GetMapping("/{id}")
public ResponseResult getArticleDetails(@PathVariable("id") Long id){
return articleService.getArticleDetails(id);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//todo 查询文章详情
/*
要在文章列表页面点击阅读全文时能够跳转到文章详情页面 ,可以让用户阅读文章正文
要求: 1. 要在文章详情中展示其分类名
*/
@Override
public ResponseResult getArticleDetails(Long id) {
//根据文章id查询文章
Article article = getById(id);
//转换成vo格式
ArticleDetailVo articleDetailVo = BeanCopyUtils.copyBean(article, ArticleDetailVo.class);
//根据分类id查询分类名
Long categoryId = articleDetailVo.getCategoryId();
Category category = categoryService.getById(categoryId);
if(category == null){
return ResponseResult.okResult(articleDetailVo);
}
articleDetailVo.setCategoryName(category.getName());
//封装响应,返回
return ResponseResult.okResult(articleDetailVo);
}

友链查询

需求

如果进行评论 ,那么就可以将用户的网站名、地址、描述、logo放上去

接口设计

image-20230312170340390

响应格式

image-20230312170721528

表信息

image-20230312170447017

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
@RequestMapping("/link")
public class LinkController {
@Resource
private LinkService linkService;

//todo 获取所有友链
@GetMapping("/getAllLink")
public ResponseResult getAllLink(){
return linkService.getAllLink();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Service("linkService")
public class LinkServiceImpl extends ServiceImpl<LinkMapper, Link> implements LinkService {

// todo 获取所有友链
/*
如果进行评论 ,那么就可以将用户的网站名、地址、描述、logo放上去
*/
@Override
public ResponseResult getAllLink() {
//查询所有审核通过的
LambdaQueryWrapper<Link> queryWrapper = new LambdaQueryWrapper();
queryWrapper.eq(Link::getStatus, SystemConstants.LINK_STATUS_NORMAL);
List<Link> links = list(queryWrapper);

List<LinkVo> linkVos = BeanCopyUtils.copyBeanList(links, LinkVo.class);
return ResponseResult.okResult(linkVos);
}
}

评论列表

接口

image-20230316110227487

image-20230316110924073

响应

image-20230316112155537

需求

不仅需要实现评论 ,还要实现”父子评论“

表分析

image-20230316110441239

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping("/comment")
public class CommentController {


@Autowired
private CommentService commentService;

//todo 评论列表
@GetMapping("/commentList")
public ResponseResult commentList(Long articleId ,Integer pageNum , Integer pageSize){
return commentService.commentList(articleId,pageNum,pageSize);
}

}

service层

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
@Service
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {

@Resource
private UserService userService;

//todo 评论列表
@Override
public ResponseResult commentList(Long articleId, Integer pageNum, Integer pageSize) {
//根据文章id 所对应的 根评论(root_id = -1)
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
//对articleId进行判断
queryWrapper.eq(Comment::getArticleId,articleId);
queryWrapper.eq(Comment::getRootId, SystemConstants.ARTICLE_ROOT_COMMENT);
//分页查询
Page<Comment> pageN = new Page<>(pageNum,pageSize);
Page<Comment> page = page(pageN, queryWrapper);
//封装返回
List<CommentVo> list = toCommentVoList(page.getRecords());
return ResponseResult.okResult(new PageVo(list,page.getTotal()));
}

//comment 集合 和commentVo 集合的拷贝
private List<CommentVo> toCommentVoList(List<Comment> list){
List<CommentVo> commentVos = BeanCopyUtils.copyBeanList(list, CommentVo.class);

//遍历vo
for (CommentVo commentVo : commentVos){
//通过createBy查询用户的昵称并且赋值
String nickName = userService.getById(commentVo.getCreateBy()).getNickName();
commentVo.setUsername(nickName);
//通过 toCommentUserId查询用户ude昵称并赋值
//如果 toCommentUserId != -1才进行查询
if (commentVo.getToCommentId() != -1){
String toCommentName = userService.getById(commentVo.getToCommentId()).getNickName();
commentVo.setToCommentUserName(toCommentName);
}
}
return commentVos;
}
}

查询根评论对应的子评论

也就是多条评论

image-20230316122832752

1
2
3
4
5
6
//查询所有根评论对应的子评论的集合 ,并且赋值给对应的属性children
for (CommentVo commentVo : list){
//查询对应子评论
List<CommentVo> children = getChildren(commentVo.getId());
commentVo.setChildren(children);
}

service层

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
/todo 评论列表
@Override
public ResponseResult commentList(Long articleId, Integer pageNum, Integer pageSize) {
//根据文章id 所对应的 根评论(root_id = -1)
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
//对articleId进行判断
queryWrapper.eq(Comment::getArticleId,articleId);
queryWrapper.eq(Comment::getRootId, SystemConstants.ARTICLE_ROOT_COMMENT);
//分页查询
Page<Comment> pageN = new Page<>(pageNum,pageSize);
Page<Comment> page = page(pageN, queryWrapper);
//封装返回
List<CommentVo> list = toCommentVoList(page.getRecords());
//查询所有根评论对应的子评论的集合 ,并且赋值给对应的属性children
for (CommentVo commentVo : list){
//查询对应子评论
List<CommentVo> children = getChildren(commentVo.getId());
commentVo.setChildren(children);
}
return ResponseResult.okResult(new PageVo(list,page.getTotal()));
}

//todo 根据根评论的id查询对应的子评论的集合
private List<CommentVo> getChildren(Long commentId){
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Comment::getRootId,commentId);
List<Comment> list = list(queryWrapper);
//封装成为CommentVo,然后返回
return toCommentVoList(list);
}

//comment 集合 和commentVo 集合的拷贝
private List<CommentVo> toCommentVoList(List<Comment> list){
List<CommentVo> commentVos = BeanCopyUtils.copyBeanList(list, CommentVo.class);

//遍历vo
for (CommentVo commentVo : commentVos){
//通过createBy查询用户的昵称并且赋值
String nickName = userService.getById(commentVo.getCreateBy()).getNickName();
commentVo.setUsername(nickName);
//通过 toCommentUserId查询用户ude昵称并赋值
//如果 toCommentUserId != -1才进行查询
if (commentVo.getToCommentId() != -1){
String toCommentName = userService.getById(commentVo.getToCommentId()).getNickName();
commentVo.setToCommentUserName(toCommentName);
}
}
return commentVos;
}


发表评论

请求

image-20230316124356854

image-20230316124520194

如果是友链 type为 1

还需要有请求体

实现

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
package com.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.blog.domain.ResponseResult;
import com.blog.domain.entity.Comment;
import com.blog.domain.entity.LoginUser;
import com.blog.domain.entity.User;
import com.blog.domain.vo.CommentVo;
import com.blog.domain.vo.PageVo;
import com.blog.enums.AppHttpCodeEnum;
import com.blog.exception.SystemException;
import com.blog.mapper.CommentMapper;
import com.blog.service.CommentService;
import com.blog.service.UserService;
import com.blog.utils.BeanCopyUtils;
import com.blog.utils.SecurityUtils;
import com.blog.utils.SystemConstants;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.sql.rowset.BaseRowSet;
import java.util.List;
import java.util.Objects;

@Service
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {

@Resource
private UserService userService;

//todo 评论列表
@Override
public ResponseResult commentList(Long articleId, Integer pageNum, Integer pageSize) {
//根据文章id 所对应的 根评论(root_id = -1)
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
//对articleId进行判断
queryWrapper.eq(Comment::getArticleId,articleId);
queryWrapper.eq(Comment::getRootId, SystemConstants.ARTICLE_ROOT_COMMENT);
//分页查询
Page<Comment> pageN = new Page<>(pageNum,pageSize);
Page<Comment> page = page(pageN, queryWrapper);

//封装返回
List<CommentVo> list = toCommentVoList(page.getRecords());
//查询所有根评论对应的子评论的集合 ,并且赋值给对应的属性children
for (CommentVo commentVo : list){
//查询对应子评论
List<CommentVo> children = getChildren(commentVo.getRootId());
commentVo.setChildren(children);
}
return ResponseResult.okResult(new PageVo(list,page.getTotal()));
}

//todo 添加评论
@Override
public ResponseResult addComment(Comment comment) {
//评论内容不能为空
if(!StringUtils.hasText(comment.getContent())){
throw new SystemException(AppHttpCodeEnum.CONTENT_NOT_NULL);
}
save(comment);
return ResponseResult.okResult();
}

//todo 根据根评论的id查询对应的子评论的集合
private List<CommentVo> getChildren(Long commentId){
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Comment::getRootId,commentId);
queryWrapper.orderByAsc(Comment::getCreateTime);
List<Comment> list = list(queryWrapper);
//封装成为CommentVo,然后返回
return toCommentVoList(list);
}


//todo comment 集合 和commentVo 集合的拷贝
private List<CommentVo> toCommentVoList(List<Comment> list){
List<CommentVo> commentVos = BeanCopyUtils.copyBeanList(list, CommentVo.class);
//遍历vo
for (CommentVo commentVo : commentVos){
//通过createBy查询用户的昵称并且赋值
String nickName = userService.getById(commentVo.getCreateBy()).getNickName();
commentVo.setUsername(nickName);
//通过 toCommentUserId查询用户ude昵称并赋值
//如果 toCommentUserId != -1才进行查询
if (commentVo.getToCommentUserId() != -1){
String toCommentUserName = userService.getById(commentVo.getToCommentUserId()).getNickName();
commentVo.setToCommentUserName(toCommentUserName);
}
}
return commentVos;
}
}


友链评论

接口

image-20230318111815616

实现

1
2
3
4
@GetMapping("/linkCommentList")
public ResponseResult listCommentList(Integer pageNum , Integer pageSize){
return commentService.commentList(SystemConstants.COMMENT_TYPE_FRIEND,null,pageNum,pageSize);
}
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
@Service
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {

@Resource
private UserService userService;

//todo 评论列表
@Override
public ResponseResult commentList(String commentType, Long articleId, Integer pageNum, Integer pageSize) {
//根据文章id 所对应的 根评论(root_id = -1)
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();

//对articleId进行判断 ,必须是文章评论再显示这个条件
queryWrapper.eq(SystemConstants.COMMENT_TYPE_ARTICLE.equals(commentType),Comment::getArticleId,articleId);
queryWrapper.eq(Comment::getRootId, SystemConstants.ARTICLE_ROOT_COMMENT);
//评论类型
queryWrapper.eq(Comment::getType,commentType);
//分页查询
Page<Comment> pageN = new Page<>(pageNum,pageSize);
Page<Comment> page = page(pageN, queryWrapper);

//封装返回
List<CommentVo> list = toCommentVoList(page.getRecords());
//查询所有根评论对应的子评论的集合 ,并且赋值给对应的属性children
for (CommentVo commentVo : list){
//查询对应子评论
List<CommentVo> children = getChildren(commentVo.getRootId());
commentVo.setChildren(children);
}
return ResponseResult.okResult(new PageVo(list,page.getTotal()));
}
}

个人中心

接口

image-20230318125856965

表分析

image-20230318125957961

实现

1
2
3
4
5
6
7
8
9
10
11
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;

@GetMapping("/userInfo")
public ResponseResult userInfo(){
return userService.userInfo();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

//todo 用户中心
@Override
public ResponseResult userInfo() {
//获取当前用户
Long userId = SecurityUtils.getUserId();
//根据当前用户id查询当前用户
User user = getById(userId);
//封装成userInfoVo返回
UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
return ResponseResult.okResult(userInfoVo);
}
}

个人信息curd

头像上传—-使用七牛云

image-20230318132126703

首先上传至web应用服务器 ,然后再从web服务器上传至 oss

接口

上传文件需求 :

更新个人信息需求 :

image-20230318145103396

实现

上传文件

1
2
3
4
5
6
7
8
9
10
@RestController
public class UploadController {
@Autowired
private UploadService uploadService;

@PostMapping("/upload")
public ResponseResult uploadImg(MultipartFile img){
return uploadService.uploadImg(img);
}
}
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
/**
* 上传文件到七牛云
*/
@ConfigurationProperties(prefix = "oss")
@Service
@Data
public class UploadServiceImpl implements UploadService {

//todo 实现文件的上传
@Override
public ResponseResult uploadImg(MultipartFile img) {
//判断文件的大小
//获取原始文件名进行判断
String originalFilename = img.getOriginalFilename();
if(!originalFilename.endsWith(".png") && !originalFilename.endsWith(".jpg")){
return ResponseResult.errorResult(AppHttpCodeEnum.FILE_TYPE_ERROR);
}
//如果通过,上传文件到oss
String url = uploadOSS(img);

return ResponseResult.okResult(url);
}

private String accessKey;
private String secretKey;
private String bucket;


private String uploadOSS(MultipartFile imgFile){
//构造一个带指定 Region 对象的配置类
Configuration cfg = new Configuration(Region.autoRegion());
//...其他参数参考类注释
UploadManager uploadManager = new UploadManager(cfg);
//默认不指定key的情况下,以文件内容的hash值作为文件名

//images目录下的文件
String originalFilename = imgFile.getOriginalFilename();

String key = "images/"+originalFilename;
try {
//将前端传过来的imgFile文件转换成一个inputStream,然后
InputStream inputStream = imgFile.getInputStream();
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket);
try {
Response response = uploadManager.put(inputStream,key,upToken,null, null);
//解析上传成功的结果
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
System.out.println(putRet.key);
System.out.println(putRet.hash);
} catch (QiniuException ex) {
Response r = ex.response;
System.err.println(r.toString());
try {
System.err.println(r.bodyString());
} catch (QiniuException ex2) {
//ignore
}
}
} catch (Exception ex) {
//ignore
}
//文件地址
return "http://rrpanx30j.hd-bkt.clouddn.com/images/"+ originalFilename;
}
}

更新个人信息实现 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;

@GetMapping("/userInfo")
public ResponseResult userInfo(){
return userService.userInfo();
}

@PutMapping("/userInfo")
public ResponseResult updateUserInfo(@RequestBody User user){
return userService.updateUserInfo(user);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

//todo 用户中心
@Override
public ResponseResult userInfo() {
//获取当前用户
Long userId = SecurityUtils.getUserId();
//根据当前用户id查询当前用户
User user = getById(userId);
//封装成userInfoVo返回
UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
return ResponseResult.okResult(userInfoVo);
}


//todo 更新个人信息
@Override
public ResponseResult updateUserInfo(User user) {
updateById(user);
return ResponseResult.okResult();
}
}

登录系统

接口

image-20230313163836015

请求and响应信息

image-20230313163731178

表分析

image-20230313180949226

思路分析

image-20230313181433354

登录

①自定义登录接口

调用ProviderManager的方法进行认证 如果认证成功生成jwt

把信息存入redis中

②自定义UserDetailsServic e

在这个实现类中进行查询数据库操作

注意配置密码加密BCryptPasswordCoder

校验

①自定义jwt认证过滤器

获取token

解析token获取其中的userId

从redis中获取用户信息

存入securityContextHolder

配置信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--SpringSecurity启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--fastjson依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<!--jwt依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>

实现

登录校验过滤器代码实现

校验

①自定义jwt认证过滤器

获取token

解析token获取其中的userId

从redis中获取用户信息

存入securityContextHolder

JwtAuthenticationTokenFilter

实现

  1. 重新jwt过滤器
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
/**
* jwt过滤器
*
*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

@Autowired
private RedisCache redisCache;

@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
// 获取token
String token = httpServletRequest.getHeader("token");
if(!StringUtils.hasText(token)){
//说明该接口不需要登录,直接放行
filterChain.doFilter(httpServletRequest,httpServletResponse);
return;
}

// 解析token获取其中的userId
Claims claims = null;
try {
claims = JwtUtil.parseJWT(token);
} catch (Exception e) {
//如果异常, 那么就是token超时或者非法
e.printStackTrace();
//返回异常信息
ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
WebUtils.renderString(httpServletResponse, JSON.toJSONString(result));
return;
}
String userId = claims.getSubject();
//从redis中获取用户信息
LoginUser loginUser = redisCache.getCacheObject(SystemConstants.LOGIN_KEY_PREFIX + userId);
//如果获取不到
if(Objects.isNull(loginUser)){
//提示重新登录
ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
WebUtils.renderString(httpServletResponse, JSON.toJSONString(result));
return;
}

//存入securityContextHolder
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser,null,null);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
}
  1. 将过滤器加入到SecurityConfig的配置文件中
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
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

/**
* 密码加密
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}



//todo 认证
@Override
protected void configure(HttpSecurity http) throws Exception {
HttpSecurity disable = http.csrf().disable();
System.out.println("----"+disable.toString());
http
.authorizeRequests()
//不通过session获取SecurityContext
//对于登录接口 ,匿名访问
.mvcMatchers("/login").anonymous()
//用于测试的接口---友链
.antMatchers("/link/getAllLink").authenticated()
//剩下的都不需要认证即可访问
.anyRequest().permitAll()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
;
http.logout().disable();
//允许跨域
http.cors();
//将自定义的filter添加到过滤器链中
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}

}

认证授权异常处理

未处理的异常

image-20230315092254529

上面这种异常处理的方式不符合项目接口的规范,所以我们需要自定义异常处理

实现的认证失败的接口处理

AuthenticationEntryPoint认证失败处理

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
/**
* 认证失败处理
*/
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {


@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
//打印异常信息
e.printStackTrace();
//判断异常的类型信息
ResponseResult result = null;
if(e instanceof BadCredentialsException){
result = ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_ERROR.getCode(),e.getMessage());
}else if (e instanceof InsufficientAuthenticationException){
result = ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_ERROR);
}//其他情况
else{
result = ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR.getCode(),"认证失败!!!");
}
//响应给前端
WebUtils.renderString(httpServletResponse, JSON.toJSONString(result));
}
}

AccessDeniedHandler授权失败处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 授权失败处理
*/
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {


@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
//打印异常信息
e.printStackTrace();
ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH);
//响应给前端
WebUtils.renderString(httpServletResponse, JSON.toJSONString(result));

}
}

放入SecurityConfig中

1
2
3
4
5
6
7
8
9
//todo 认证
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置认证和授权的异常处理器
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);

}

对于controller层出现的异常

比如向如下的异常, 用户登录时没有输入用户名,那么如果我们不在controller层进行拦截 ,他就会进入service层

1
2
3
4
5
6
7
8
@PostMapping("/login")
public ResponseResult login(@RequestBody User user){
if (!StringUtils.hasText(user.getUserName())){
//提示 要传用户名
throw new SystemException(AppHttpCodeEnum.REQUIRE_USERNAME);
}
return blogLoginService.login(user);
}

所以我们直接在controller层进行拦截

操作

  1. 自定义异常类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SystemException extends RuntimeException{

private int code;

private String msg;

public int getCode() {
return code;
}

public String getMsg() {
return msg;
}

public SystemException(AppHttpCodeEnum httpCodeEnum) {
super(httpCodeEnum.getMsg());
this.code = httpCodeEnum.getCode();
this.msg = httpCodeEnum.getMsg();
}

}
  1. 配置自定义异常拦截器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 定义的controller层异常拦截
* 对于controller层的异常 ,直接进行拦截返回
*/
//@ControllerAdvice
//@ResponseBody
@RestControllerAdvice
@Slf4j //使用之后可以直接使用log
public class GlobalExceptionHandler {

@ExceptionHandler(SystemException.class)
public ResponseResult systemExceptionHandler(SystemException e){
//1. 打印异常信息
log.error("出现了异常! {}",e);
//2. 从异常对象中获取提示信息
//3. 封装返回
return ResponseResult.errorResult(e.getCode(),e.getMsg());
}
}

退出登录

接口

需要token

image-20230315101032018

登录时 ,我们将用户的相关信息存入到了redis中 ,同时也包括token

如果想要退出 ,我们只需要删除redis中的用户登录数据即可

实现

1
2
3
4
@PostMapping("/logout")
public ResponseResult logout(){
return blogLoginService.logout();
}

service

1
2
3
4
5
6
7
8
9
10
11
//todo 退出登录
@Override
public ResponseResult logout() {
//获取 token 解析获取 userId
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
Long userId = loginUser.getUser().getId();

redisCache.deleteObject(SystemConstants.LOGIN_KEY_PREFIX + userId);
return ResponseResult.okResult();
}

注册系统

接口

image-20230318151249833

实现

1
2
3
4
@PostMapping("register")
public ResponseResult register(@RequestBody User user){
return userService.register(user);
}

业务层实现

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
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

@Autowired
private PasswordEncoder passwordEncoder;
//todo 注册用户
@Override
public ResponseResult register(User user) {
//对数据进行非空判断 要求用户名 密码 等都不为空
if(!StringUtils.hasText(user.getUserName())){
ResponseResult.errorResult(AppHttpCodeEnum.USERNAME_NOT_NULL);
}
if( StringUtils.hasText(user.getPassword())){
ResponseResult.errorResult(AppHttpCodeEnum.PASSWORD_NOT_NULL);
}
if( StringUtils.hasText(user.getEmail())){
ResponseResult.errorResult(AppHttpCodeEnum.EMAIL_NOT_NULL);
}
if( StringUtils.hasText(user.getNickName())){
ResponseResult.errorResult(AppHttpCodeEnum.NICKNAME_EXIST);
}
//判断数据库中是否存在用户
if(usernameExist(user.getUserName())){
//用户已经存在
ResponseResult.errorResult(USERNAME_EXIST);
}
if(nickNameExist(user.getNickName())){
//昵称存在
ResponseResult.errorResult(NICKNAME_EXIST);
}
///密码加密处理
String encodePass = passwordEncoder.encode(user.getPassword());
user.setPassword(encodePass); //设置加密之后的密码
save(user);
//返回结果
return ResponseResult.okResult();
}


//todo 判断用户名是否存在
private boolean usernameExist(String username){
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUserName,username);
int count = count(queryWrapper);
if(count >= 1){
return true;
}
return false;
}
//todo 判断昵称是否存在
private boolean nickNameExist(String nickName){
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getNickName,nickName);
int count = count(queryWrapper);
if(count >= 1){
return true;
}
return false;
}
}