基于SpringBoot+Thymeleaf+Mysql 的博客管理系统
1、前期分析
(1) 界面
- 前端界面
前端使用网站下载的博客系统,主要任务实现后台 blog系统前端
界面:




-
后端界面
后端界面同样是用网站模板 后端界面

(2) 功能分析
-
主页分析
-
首页该有的功能:
1 统计信息 获取当前所有博客数,以及某一天起到当前天的天数
2 推荐博客 点赞数最多的博客并且设置为可推荐状态
3 最新发布 根据发布时间进行排序展示,并分类
4 最新评论文章 根据评论时间进行排序展示
5 搜索功能 模糊查询,根据文章内容和文章标题同时搜索
6 博客详情 点击进入博客详情界面
7 登陆
-
列表页
1 种类分类展示
2 显示标签分类
3 最新评论文章
4 搜索
-
详细页
1 展示文章
2 最新评论文章
3 推荐文章(选择和该文章同一分类和标签下的文章进行推荐
4 搜索
5 评论
-
-
用户分析
根据界面分析分为普通用户和管理员用户两种使用模式
未登录情况:
可以浏览主页和博客详情,可以看到评论,可以点赞,但要发表评论和发表博客,需要登陆
普通用户:
可以浏览主页、可以评论、可以发表博客
管理员:
可以浏览主页、可以评论、可以发表博客、可以管理博客、可以管理用户
(3) 实体分析
用户(id 用户名 昵称 密码 头像 是否管理员 电话 创建时间 更新时间 博客集)
User(id username nickname password image admin phone createTime updateTime blogList)
博客(id 标题 内容 首图 点赞数 能否评论 是否推荐 评论数量 种类 标签集 评论集 创建时间 更新时间 评论更新时间 作者)
Blog(id title content firstImg praiseNum commentable Recommend countComment type Tags comments createTime updateTime commentUpdateTime author)
分类(id 名称 博客集)
Type(id name blogList)
标签(id 名称 博客集)
Tag (id name blogList)
评论(id 用户昵称 电话 内容 头像 创建时间 博客)
Comment(id nickname phone content image createTime blog)
(4)实体ER图、功能图
- E-R图

- 功能图

2、功能实现模块
(1) 登陆注册
界面展示:


功能介绍:
用户使用系统评论功能、管理员使用后台管理功能必须登录才能使用,注册功能部分默认注册的新用户为普通用户。
流程:新建User实体类 -> 新建UserDAO层接口继承JPA -> 新建Service业务层接口和实现方法 -> 新建控制层 -> 和前端数据交互。
(2) 个人信息管理
界面展示:


功能介绍:
包含基础信息修改和密码修改,基础信息修改部分将用户名和是否管理员设置为不可修改状态,修改密码时会在前端js中进行判断两次输入的密码是否一致。
流程: UserDAO层接口继承JPA -> 声明查找当前用户方法 -> 新建Service业务层接口和实现方法 -> 新建控制层监听路由 -> 和前端数据交互
(3) 后端管理
用户在登陆时会先根据在数据库中的信息判断当前是否是管理员,如果是即显示后台管理图标,否即没有权限进入后台


管理员后台系统
主页:

目前用户的CRUD和博客的CRUD功能都已实现,还在主页显示处当前数据库的博客数量、用户数量和评论数量,点赞数量是一个假数据,点赞的功能目前还有一些问题。
流程:在dao层声明用户、博客、评论继承jpa方法,再在业务层声明查询方法,最后在controller层中调用几种方法并将获取到的信息储存到model中,跳转到该html界面进行显示
(4) 管理员端用户信息管理
点击主页的用户管理即可跳转到当前界面(由于给前端添加图标的时候只在主页试验了一下,忘记在每个页面加上了)
用户信息管理里的功能主要有:新增用户、编辑用户、删除用户,查询用户功能在前端模板中已经实现了,新增用户的方法和之前注册一样,编辑用户需要先根据该用户id进行查询,并将查询到的信息显示到界面中,点击提交按钮,在控制器中判断当前用户是否id为空,为空则为新增用户调用注册方法,不是为空则更新一些数据(更新时间等)对数据库进行更新操作。

新增用户:

编辑用户:

(5) 新增博客
在新增博客界面将标签、分类、博客都放到一个界面中进行添加操作,点击相应板块的添加按钮实现相应的功能。
新增博客、新增分类信息、新增标签信息等都是和注册功能类似,都是html界面 -> controller -> Service -> Dao。
此处的博客文本使用了一个工具类MarkdownUtils。


(6) 博客管理
主要功能:博客编辑、博客删除、跳转到博客添加;分类编辑、删除、跳转到添加;标签编辑、删除、跳转到添加;
和用户管理原理一样、先查询到该id下的信息,进行编辑、判断id、保存。


(7) 博客修改页
主要流程:获取当前博客id -> controller使用根据id查询功能 -> Service使用查询功能 -> dao使用查询返回结果 ->controller保存结果到一个blog对象中 -> 前端显示该blog对象参数 -> 修改 -> 保存

(8) 博客图廊
图廊功能是根据每条博客中的图片显示,点击图片进入相应的新闻详情界面
主要实现方法:将数据库中blog的image属性放到一个list中,再在前端遍历显示该list
跳转到的详情界面由于部分数据没有删除,所有存在很多假数据。


(9) 博客详情页
该部分是前端的博客详情界面,根据点击的博客按钮进行查询显示,在标题下显示更新时间、作者、分类、点赞数量、评论数等,右边根据最新评论时间进行排序显示,还可以查询当前新闻的评论,按更新时间进行显示



(10) 首页三模块功能
-
最新发布
根据博客的更新时间进行排序,每篇博客在编辑时都会刷新更新时间
-
推荐文章
更具博客的推荐属性进行排序,但由于界面设计,所以这里只能显示一篇,即推荐状态下最近更新的一篇博客
-
最新评论文章
在博客实体中加入了评论更新时间属性,即用来存放最后一次评论的时间,根据该时间进行排序显示


(11) 列表页
列表页其实和主页差不多,只不过这里显示了当前数据库中的所有标签信息(404是假数据)

(12)错误界面
当界面发生错误的时候(例如评论时是未登录状态),会显示错误界面,错误界面的好处就是对用户更加友好

(13) 评论功能
评论板块会显示在每个blog的下方,不论是否登录都会看到该博客的评论信息,但是要发表评论必须是登陆状态,登录状态下会自动在昵称和电话输入框下显示当前用户信息。


(14) 登录拦截 / 异常处理
登录拦截主要是防止用户直接进入后端管理界面,在登陆拦截时拦截了所有的未登录状态下包含/admin的所有路径,进入管理员界面需要登陆、判断是否是管理员是的话给进入后台接口,进入后台管理。
异常处理即前面的404界面部分,用一个友好的界面反馈到系统中。
3、重要代码
(1)前端部分
前端部分涉及到的代码非常多,上传到了github,文末贴出github地址,主要目录如下

(2)实体类
-
Blog类
@Entity @Table(name = "b_blog") public class Blog { @Id //主键标识 @GeneratedValue(strategy = GenerationType.IDENTITY) //自增 private Long id; private String title; private String content; private String firstImg; private Integer praiseNum; private boolean commentabled; private boolean recommend; //推荐 private Integer countComment;//评论数 @ManyToOne //分类集 private Type type; @ManyToMany(cascade = CascadeType.PERSIST) //标签集 指定级联 给当前设置的实体操作另一个实体的权限 private List<Tag> tags = new ArrayList<>(); @ManyToOne //作者 private User user; private String author; @Transient //表示不会在数据库中生成这个字段 只存在于本实体中 private String tagIds; public Blog() { } @Temporal(TemporalType.TIMESTAMP) //指定时间戳 private Date createTime; @Temporal(TemporalType.TIMESTAMP) //指定时间戳 private Date updateTime; @Temporal(TemporalType.TIMESTAMP) //指定时间戳 private Date commentUpdateTime; //对一对多的标签进行处理 如一个博客有id为12345的tag 初始化为 1,2,3,4,5 public void init(){ this.tagIds = tagsToIds(this.getTags()); } private String tagsToIds(List<Tag> tags){ if(!tags.isEmpty()){ StringBuffer ids = new StringBuffer(); boolean flag = false; for(Tag tag:tags){ if(flag){ ids.append(","); }else{ flag = true; } ids.append(tag.getId()); } return ids.toString(); }else{ return tagIds; } } } //手动添加setter and getter 和 toString -
User类
@Entity @Table(name = "b_user") public class User { @Id //主键标识 @GeneratedValue(strategy = GenerationType.IDENTITY) //自增 private Long id; private String username; private String nickname; private String password; private String image; private Boolean admin; private String phone; @Temporal(TemporalType.TIMESTAMP) //指定时间戳 private Date createTime; @Temporal(TemporalType.TIMESTAMP) //指定时间戳 private Date updateTime; //一对多关系的新闻 @OneToMany(mappedBy = "user") private List<Blog> blogList = new ArrayList<>(); //手动添加setter and getter 和 toString } -
Type类
@Entity
@Table(name = "b_type")
public class Type {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //自增
private Long id;
@NotBlank(message = "分类名称不能为空")
private String name;
@OneToMany(mappedBy = "type")
private List<Blog> blogList = new ArrayList<>();
//手动添加setter and getter 和 toString
}
- Tag类
@Entity
@Table(name = "b_tag")
public class Tag {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //自增
private Long id;
@NotBlank(message = "标签名称不能为空")
private String name;
@ManyToMany(mappedBy = "tags")
private List<Blog> blogList = new ArrayList<>();
//手动添加setter and getter 和 toString
}
- Comment类
@Entity
@Table(name = "b_comment")
public class Comment {
@Id //主键标识
@GeneratedValue(strategy = GenerationType.IDENTITY) //自增
private Long id;
private String nickname;
private String phone;
private String content;
private String image;//头像
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
@ManyToOne
private Blog blog;
//管理员评论
private boolean adminComment;
//手动添加setter and getter 和 toString
}
(3)Dao层
因为很多方法是封装好在JPA中的了,所以不需要太多的方法声明
BlogRepository:
public interface BlogRepository extends JpaRepository<Blog,Long> {
//找到推荐状态下最新更新的
@Query("select b from Blog b where b.recommend = true")
List<Blog> findTop(Pageable pageable);
//找到评论时间排序的博客
@Query("select b from Blog b")
List<Blog> findTopByCommentUpdateTime(Pageable pageable);
}
CommentRepository:
public interface CommentRepository extends JpaRepository<Comment,Long> {
List<Comment> findByBlogId(Long id, Sort sort);
//查当前博客下有多少评论
@Query("select count(c) from Comment c where blog_id = ?1")
Integer countCommentsByBlogId(Long id);
}
TagRepository:
public interface TagRepository extends JpaRepository<Tag,Long> {
//根据标签下的博客数排
@Query("select t from Tag t")
List<Tag> findTop(Pageable pageable);
}
TypeRepository:
public interface TypeRepository extends JpaRepository<Type,Long> {
}
UserRepository:
public interface UserRepository extends JpaRepository<User, Long> {
//登陆操作 根据同户名和密码返回数据库的数据
User findByUsernameAndPassword(String username,String password);
//注册操作 查找当前用户名是否存在
User findByUsername(String username);
//输入用户名或昵称内容查询
@Query("select u from User u where u.username like ?1 or u.nickname like ?1")
Page<User> findByQuery(String query, Pageable pageable);
}
(4)Service层
BlogService:
public interface BlogService {
//保存博客(更新更新时间)
Blog saveBlog(Blog blog);
//保存博客(更新评论时间)
Blog saveBlogByCommentTime(Blog blog);
//列出所有博客
List<Blog> listBlog();
//获取博客跳转
Blog getAndConvert(Long id);
//根据id删除博客
void deleteById(Long id);
//根据id查询博客
Blog findById(Long id);
//更新用户个人信息
Blog updateBlog(Long id,Blog blog);
//分页列出所有博客
Page<Blog> listBlog (Pageable pageable);
//推荐博客
List<Blog> recommendBlogTop();
//评论时间排序博客
List<Blog> commentBlogTop();
}
BlogServiceImpl:
@Service
public class BlogServiceImpl implements BlogService {
@Autowired
private BlogRepository blogRepository;
@Autowired
private CommentRepository commentRepository;
@Override
public Blog saveBlog(Blog blog) {
blog.setUpdateTime(new Date());
return blogRepository.save(blog);
}
@Override
public Blog saveBlogByCommentTime(Blog blog) {
blog.setCommentUpdateTime(new Date());
return blogRepository.save(blog);
}
@Override
public List<Blog> listBlog() {
return blogRepository.findAll();
}
@Override
public Blog getAndConvert(Long id) {
Blog blog = blogRepository.findById(id).orElse(null);
if(blog==null){
System.out.println("该博客不存在");
}
Blog blog1 = new Blog();
blog1.setCountComment(commentRepository.countCommentsByBlogId(id));
BeanUtils.copyProperties(blog,blog1);
String content = blog1.getContent();
blog1.setContent(MarkdownUtils.markdownToHtmlExtensions(content));
return blog1;

1894

被折叠的 条评论
为什么被折叠?



