用的halo
https://github.com/halo-dev/halo
导入project之后就开始build了
![10651191-35a897c596031107.png](https://i-blog.csdnimg.cn/blog_migrate/964db00cd98d81b91c337573a8d7ea4d.png)
然后尝试run起来
![10651191-b7c9fae27ba3726b.png](https://i-blog.csdnimg.cn/blog_migrate/7264a9efe1e2d7cd60f855e2fa166d96.png)
对于开源项目,一定要跑起来,如果不run起来就直接看源代码的话会头疼的
项目已经启动了
![10651191-290f1a5fe0dbcbdf.png](https://i-blog.csdnimg.cn/blog_migrate/76ce9cbf88c41076ab0862a8a24a044a.png)
之后我们访问
![10651191-bfe5901ec6b44398.png](https://i-blog.csdnimg.cn/blog_migrate/be0d50dd6ad85faad272b66a9f2a1b72.png)
开始调试
@PostMapping("login")
@ApiOperation("Login")
@CacheLock(autoDelete = false)
public AuthToken auth(@RequestBody @Valid LoginParam loginParam) {
return adminService.authenticate(loginParam);
}
PostMapping 应该就是路由的选择
其他的先不管
auth函数,收到了
loginParam 这个结构
我们可以具体看下LoginParam
public class LoginParam {
@NotBlank(message = "用户名或邮箱不能为空")
@Size(max = 255, message = "用户名或邮箱的字符长度不能超过 {max}")
private String username;
@NotBlank(message = "登陆密码不能为空")
@Size(max = 100, message = "用户密码字符长度不能超过 {max}")
private String password;
}
是一个类
有两个私有字段username和password
在对username和password进行判断(字符长度)
然后继续验证
看下adminService这个类
在service文件夹中
![10651191-bd08c12e523b9ccc.png](https://i-blog.csdnimg.cn/blog_migrate/36956bfafd0180d040d9a26323243f68.png)
有一个authenticate方法
@NonNull
AuthToken authenticate(@NonNull LoginParam loginParam);
也就是对loginParam
进行进一步的验证
继续跟踪AuthToken
这个类
@Data
public class AuthToken {
/**
* Access token.
*/
@JsonProperty("access_token")
private String accessToken;
/**
* Expired in. (seconds)
*/
@JsonProperty("expired_in")
private int expiredIn;
/**
* Refresh token.
*/
@JsonProperty("refresh_token")
private String refreshToken;
}
应该是登陆之后会有一个accessToken
,然后还会设置一个有效期
同时还会有更新的token
这个类位于security这个文件夹中
![10651191-9df2d41b8884d1a6.png](https://i-blog.csdnimg.cn/blog_migrate/c3874d8c5d8ac8aa5b09a7909d27fb67.png)
突然发现AdminService
其实只是一个接口,具体看实现AdminServiceImpl
类
直奔authenticate方法
@Override
public AuthToken authenticate(LoginParam loginParam) {
Assert.notNull(loginParam, "Login param must not be null");
String username = loginParam.getUsername();
String mismatchTip = "用户名或者密码不正确";
final User user;
try {
// Get user by username or email
user = Validator.isEmail(username) ?
userService.getByEmailOfNonNull(username) : userService.getByUsernameOfNonNull(username);
} catch (NotFoundException e) {
log.error("Failed to find user by name: " + username, e);
throw new BadRequestException(mismatchTip);
}
userService.mustNotExpire(user);
if (!userService.passwordMatch(user, loginParam.getPassword())) {
// If the password is mismatch
throw new BadRequestException(mismatchTip);
}
if (SecurityContextHolder.getContext().isAuthenticated()) {
// If the user has been logged in
throw new BadRequestException("You have been logged in, do not log in repeatedly please");
}
// Generate new token
return buildAuthToken(user);
}
可以看到调用了Validator
的验证器去验证isEmail
然后还有一系列验证,具体的就不看了
同时这个类是位于service
文件夹中
![10651191-1cefc85f070154c7.png](https://i-blog.csdnimg.cn/blog_migrate/a1140e4b8e8852d46ec6d7c1e07ccb39.png)
所以登陆的逻辑就很清晰了,接收到请求,验证用户名和密码,然后返回一个token
我们接下来分析是如何请求显示文章的
发送的URL如下:
![10651191-f281857d0d150842.png](https://i-blog.csdnimg.cn/blog_migrate/aa448d75ba5e623e7cb6ea3910ecd6f0.png)
有两个参数page = 0&&size = 10
对应的是/admin/posts
然后找到/posts
![10651191-9c7fca74a22ae9c4.png](https://i-blog.csdnimg.cn/blog_migrate/3f4d9d49d08aa01ed646580d25dca7b5.png)
有一个类叫做PostController
然后有一个pageBy
方法
public Page<PostListVO> pageBy(@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable,
PostQuery postQuery) {
Page<Post> postPage = postService.pageBy(postQuery, pageable);
return postService.convertToListVo(postPage);
}
同时接收 pageable
和postQuery
两个参数
![10651191-77925b2ea8b1ae46.png](https://i-blog.csdnimg.cn/blog_migrate/def22124831c00bd0e5e39d9eff26170.png)
看到pageable
就是我们的请求,number=0,size=10
同时默认排序是DESC
Pageable
这个接口不是自己写的,就不看了
来看PostQuery
这个类
@Data
public class PostQuery {
/**
* Keyword.
*/
private String keyword;
/**
* Post status.
*/
private PostStatus status;
/**
* Category id.
*/
private Integer categoryId;
}
也就是拥有了关键词,状态,和类别的ID
Page
是一个泛型的接口吧,然后我们指定的是post
这个类post
这个类是继承自BasePost
, 简要看一下BasePost
这个类,它又是继承自BaseEntity
这个类的
public class BasePost extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
/**
* Post title.
*/
@Column(name = "title", columnDefinition = "varchar(100) not null")
private String title;
/**
* Post status.
*/
@Column(name = "status", columnDefinition = "int default 1")
private PostStatus status;
....
这个类其实有很多个字段,也就是文章的各种属性吧
接下来着重看一下convertToListVo
方法
@Override
public Page<PostListVO> convertToListVo(Page<Post> postPage) {
Assert.notNull(postPage, "Post page must not be null");
List<Post> posts = postPage.getContent();
Set<Integer> postIds = ServiceUtils.fetchProperty(posts, Post::getId);
// Get tag list map
Map<Integer, List<Tag>> tagListMap = postTagService.listTagListMapBy(postIds);
// Get category list map
Map<Integer, List<Category>> categoryListMap = postCategoryService.listCategoryListMap(postIds);
// Get comment count
Map<Integer, Long> commentCountMap = postCommentService.countByPostIds(postIds);
return postPage.map(post -> {
PostListVO postListVO = new PostListVO().convertFrom(post);
if (StringUtils.isBlank(postListVO.getSummary())) {
// Set summary
postListVO.setSummary(convertToSummary(post.getOriginalContent()));
}
Optional.ofNullable(tagListMap.get(post.getId())).orElseGet(LinkedList::new);
// Set tags
postListVO.setTags(Optional.ofNullable(tagListMap.get(post.getId()))
.orElseGet(LinkedList::new)
.stream()
.filter(Objects::nonNull)
.map(tag -> (TagDTO) new TagDTO().convertFrom(tag))
.collect(Collectors.toList()));
// Set categories
postListVO.setCategories(Optional.ofNullable(categoryListMap.get(post.getId()))
.orElseGet(LinkedList::new)
.stream()
.filter(Objects::nonNull)
.map(category -> (CategoryDTO) new CategoryDTO().convertFrom(category))
.collect(Collectors.toList()));
// Set comment count
postListVO.setCommentCount(commentCountMap.getOrDefault(post.getId(), 0L));
return postListVO;
});
}
postPage
相当于一个文章的数据结构了
然后对得到这篇文章的内容,然后将其summary
,tags
,categories
,comment count
设置好
最后就返回了吧,更具体的细节不清楚了
删除文章
没太看懂是怎么删除文章的
![10651191-99a56761a42b2868.png](https://i-blog.csdnimg.cn/blog_migrate/cbfea54349daa89ff5410abfc013c34a.png)
还有这个publish
对应的就是复原文章
![10651191-693b554ec681edeb.png](https://i-blog.csdnimg.cn/blog_migrate/246a0b632049393b133a2a0f7d7f1fc4.png)