文章目录
- 博客系统
-
- 前言
- [1. 前端](#1. 前端)
-
- [1.1 登陆页面](#1.1 登陆页面)
- [1.2 博客列表页面](#1.2 博客列表页面)
- [1.3 博客详情页面](#1.3 博客详情页面)
- [1.4 博客编辑页面](#1.4 博客编辑页面)
- [2. 后端](#2. 后端)
- [2.1 项目部署](#2.1 项目部署)
-
-
- [2.1.1 创建maven项目](#2.1.1 创建maven项目)
- [2.1.2 引入依赖](#2.1.2 引入依赖)
- [2.1.3 创建目录结构](#2.1.3 创建目录结构)
- [2.1.4 部署程序](#2.1.4 部署程序)
-
- [2.2 逻辑设计](#2.2 逻辑设计)
-
- [2.2.1 数据库设计](#2.2.1 数据库设计)
- [2.2.2 实体类设计](#2.2.2 实体类设计)
- [2.2.3 Dao层设计](#2.2.3 Dao层设计)
-
- [2.2.3.1 BlogDao](#2.2.3.1 BlogDao)
- [2.2.4 Dao层设计](#2.2.4 Dao层设计)
- [2.2.3 接口设计](#2.2.3 接口设计)
-
- [2.2.3.1 登录接口](#2.2.3.1 登录接口)
- [2.2.3.2 注销接口](#2.2.3.2 注销接口)
- [2.2.3.3 作者查询接口](#2.2.3.3 作者查询接口)
- [2.2.3.4 博客接口](#2.2.3.4 博客接口)
- [2.2.3.4 删除博客接口](#2.2.3.4 删除博客接口)
- [3. 小结](#3. 小结)
博客系统
前言
基于servlet+jdbc进行后端开发,设计了一个可以发布博客,查看博客详情,删除博客,登录注销功能的简易博客系统。
1. 前端
1.1 登陆页面
1.2 博客列表页面
1.3 博客详情页面
1.4 博客编辑页面
2. 后端
2.1 项目部署
2.1.1 创建maven项目
我们在idea里面点击new project创建一个maven项目,然后一路next选择好项目位置以及命名即可。
2.1.2 引入依赖
在创建完maven项目之后我们需要加载依赖,部分同学可能由于网络问题难以加载,可以多试几遍,实在不行可以更换国内的镜像下载此处不作多介绍。以下需要引入的依赖主要有:java.servlet-api、mysql-connector-java,以及后续发送json格式的数据需要的jackson依赖(jackson-databind)
我们通过maven仓库官网https://mvnrepository.com/即可获取。
如图:复制链接黏贴到我们项目的pom.xml文件
代码 : 以下是我部署的依赖可以直接复制黏贴
html
<dependencies>
<!-- servlet依赖-->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jackson依赖-->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
<!-- 数据库依赖-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
2.1.3 创建目录结构
在我们的webapp目录下面创建一个WEB-INF包并创建一个web.xml,此处结构以及名字不能改动。
web.xml内容:
html
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>
2.1.4 部署程序
我们在插件市场搜索 Smart Tomcat 插件,将Tomcat集成到idea就不用我们手动打包,之后点击即可运行。
然后点击新增运行配置,将Smart Tomcat加入,此处选择Tomcat路径,如果还没有下载Tomcat,可以通过官网下载。博主使用的是 3.1版本的servlet+tomcat 8 。
然后就可以点击运行:出现以下结果就是部署成功了。之后我们就可以通过127.0.0.1:8080/MessageWall/xx.html(html页面)访问,其中MessageWall是Context path,一般默认是项目名。
2.2 逻辑设计
2.2.1 数据库设计
我们博客系统主要有用户登录以及编写博客的功能,基于此需求在当前博客系统中主要涉及两个实体,博客和用户,所以我们需要创建两张表用于存储博客和用户信息。
注意:用户与博客之间是一对多的关系;以下设计blog表和user表
- blog表包含blogId,title,content,postTime,userId属性;
- user表包含userId,userName,password;
- 通过userId建立关系,描述用户有哪些博客。
sql代码写到文本文件中用于记录,以便后续在别的机器上部署;
编写封装数据库的连接操作,代码如下:
java
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author zq
*/
public class DBUtil {
private static DataSource dataSource = new MysqlDataSource();
static {
((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSL=false");
((MysqlDataSource) dataSource).setUser("root");
((MysqlDataSource) dataSource).setPassword("123456");
}
public static Connection getConnection() throws SQLException {
return (Connection) dataSource.getConnection();
}
public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){
if (resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
2.2.2 实体类设计
根据我们用户表和博客表设计两个对应的实体类如下:
user类:
java
package model;
/**
* @author zq
*/
public class User {
private String username;
private String password;
private int userId;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}
blog类:
java
package model;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
/**
* @author zq
*/
public class Blog {
private int blogId;
private String title;
private String content;
private Timestamp postTime;
private int userId;
public int getBlogId() {
return blogId;
}
public void setBlogId(int blogId) {
this.blogId = blogId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getPostTime() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return simpleDateFormat.format(postTime);
}
public Timestamp getPostTimestamp(){
return postTime;
}
public void setPostTime(Timestamp postTime) {
this.postTime = postTime;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}
2.2.3 Dao层设计
封装数据库的增删改查,针对博客表创建BlogDao,针对用户表创建UserDao并且提供对应的增删改查方法。
2.2.3.1 BlogDao
针对本项目我们需要的功能有发布博客,也就是在博客表新增一条数据;显示博客详情即根据博客id查询博客内容;删除博客即根据博客id删除博客;博客列表页即查询该用户发布的所有博客;
具体实现如下:
java
package model;
import com.mysql.jdbc.Connection;
import model.Blog;
import model.DBUtil;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* @author zq
*/
//针对博客表提供的基本操作,此处暂时不考虑修改
public class BlogDao {
//1。新增一个博客
public void add(Blog blog) {
//1.先于数据库建立连接、
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = DBUtil.getConnection();
//2.构造sql1语句
String sql = "insert into blog values(null,?,?,now(),?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, blog.getTitle());
preparedStatement.setString(2, blog.getContent());
// preparedStatement.setTimestamp(3,blog.getPostTimestamp());
preparedStatement.setInt(3,blog.getUserId());
//3.执行sql
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtil.close(connection,preparedStatement,null);
}
}
//2.根据博客id查询指定博客,也就是页面的显示全文
public Blog selectById(int blogId){
Connection connection =null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
//1.建立连接
connection = DBUtil.getConnection();
//2.构造sql语句
String sql = "select * from blog where blogId = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1,blogId);
//3.执行sql语句
resultSet = statement.executeQuery();
//由于主键具有唯一性所以如果查到数据那么就只有一条
//所以不需要用while遍历所有结果,判断是否有一条就行
if (resultSet.next()){
Blog blog = new Blog();
blog.setBlogId(resultSet.getInt("blogId"));
blog.setContent(resultSet.getString("content"));
blog.setTitle(resultSet.getString("title"));
blog.setUserId(resultSet.getInt("userId"));
blog.setPostTime(resultSet.getTimestamp("postTime"));
return blog;
}
}catch (SQLException throwables){
throwables.printStackTrace();
}finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
//3.直接查询数据库所有的博客列表(博客列表页)
public List<Blog> selectAll(){
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
List<Blog> blogs = new ArrayList<>();
try{
//建立链接
connection = DBUtil.getConnection();
//构造sql语句
String sql = "select * from blog order by postTime desc";
statement = connection.prepareStatement(sql);
//执行sql
resultSet = statement.executeQuery();
while (resultSet.next()){
Blog blog = new Blog();
blog.setBlogId(resultSet.getInt("blogId"));
//注意这里的正文内容在博客列表页,不需要全部显示出来
String content = resultSet.getString("content");
if (content.length()>=100){
content = content.substring(0,100)+"....";
}
blog.setContent(content);
blog.setTitle(resultSet.getString("title"));
blog.setUserId(resultSet.getInt("userId"));
blog.setPostTime(resultSet.getTimestamp("postTime"));
blogs.add(blog);
}
}catch (SQLException throwables){
throwables.printStackTrace();
}finally {
DBUtil.close(connection,statement,resultSet);
}
return blogs;
}
//4.实现删除指定博客
public void delete(int blogId){
Connection connection = null;
PreparedStatement statement = null;
try{
//建立链接
connection = DBUtil.getConnection();
//构造sql语句
String sql = "delete from blog where blogId =?";
statement = connection.prepareStatement(sql);
statement.setInt(1,blogId);
//执行sql
statement.executeUpdate();
}catch (SQLException throwables){
throwables.printStackTrace();
}finally {
DBUtil.close(connection,statement,null);
}
}
}
2.2.4 Dao层设计
本项目在设计用户表的主要功能,登录需要验证用户名与密码即根据用户名查询用户信息;主页右侧显示的用户信息即根据用户id查询用户信息;
具体实现:
java
package model;
import com.mysql.jdbc.Connection;
import model.DBUtil;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author zq
*/
//针对用户表提供的基本操作,由于没有写注册功能所以就不写add,
// 也没有注销功能,所以也不写delete
public class UserDao {
//1.根据userID查询用户信息
public User selecetById(int userId){
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try{
connection = DBUtil.getConnection();
String sql = "select * from user where userId = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1,userId);
resultSet = statement.executeQuery();
if (resultSet.next()){
User user = new User();
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
}catch (SQLException throwables){
throwables.printStackTrace();
}finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
//2.根据用户名查询用户信息
public User selectByUsername(String username){
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try{
connection = DBUtil.getConnection();
String sql = "select * from user where username = ?";
statement = connection.prepareStatement(sql);
statement.setString(1,username);
resultSet = statement.executeQuery();
if (resultSet.next()){
User user = new User();
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
}catch (SQLException throwables){
throwables.printStackTrace();
}finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
}
2.2.3 接口设计
在前后端进行数据交互时我们约定以json格式进行数据传输,前端通过ajax向后端发送请求。
2.2.3.1 登录接口
创建一个LoginServlet类继承HttpServlet类,重写doPost()方法实现对用户密码的校验,重写doGet()方法进行用户是否登录校验,只有登录了的用户才能进行博客编辑。
具体实现如下:
java
package api;
import com.fasterxml.jackson.databind.ObjectMapper;
import model.User;
import model.UserDao;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @author zq
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.读取用户名和密码,由于是form表单格式所以不用jackson的类进行读取
//注意如果此处的用户名密码如果包含中文会乱码,所以要设置字符集
req.setCharacterEncoding("utf8");
resp.setContentType("text/html; charset=utf8");
String username = req.getParameter("username");
String password = req.getParameter("password");
if (username==null||"".equals(username)||password==null||"".equals(password)){
//登录失败
String html = "<h3> 登陆失败username或者password错误 </h3>" ;
resp.getWriter().write(html);
return;
}
//2.读取数据库看用户名是否存在,密码是否匹配
UserDao userDao = new UserDao();
User user = userDao.selectByUsername(username);
if (user==null){
//用户不存在
String html = "<h3> 用户名或密码错误 </h3>" ;
resp.getWriter().write(html);
return;
}
if (!password.equals(user.getPassword())){
String html = "<h3> 用户名或密码错误 </h3>" ;
resp.getWriter().write(html);
return;
}
//3.验证通过,接下来创建会话,使用会话保存用户信息
HttpSession session = req.getSession(true);
session.setAttribute("user",user);
//4.进行重定向到博客列表页
resp.sendRedirect("blog_list.html");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf8");
//获取用户的登录状态,如果用户未登录当前会话就拿不到
HttpSession session = req.getSession(false);
if (session==null){
//未登录,返回一个user对象
User user = new User();
String respJson = objectMapper.writeValueAsString(user);
resp.getWriter().write(respJson);
return;
}
//登录了取出user对象
User user = (User) session.getAttribute("user");
if (user==null){
//一般不会为空
user = new User();
String respJson = objectMapper.writeValueAsString(user);
resp.getWriter().write(respJson);
return;
}
//成功取出user对象,直接返回
String respjson = objectMapper.writeValueAsString(user);
resp.getWriter().write(respjson);
}
}
2.2.3.2 注销接口
创建一个LogoutServlet类继承HttpServlet类,重写doGet()方法直接将session中的该用户移除,去除该用户缓存如何自动跳转到登录页面。
具体实现如下:
java
package api;
import model.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @author zq
*/
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession httpSession = req.getSession(false);
if (httpSession==null){
//未登录状态,直接提示出错
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前未登录");
}
//然后直接romove
httpSession.removeAttribute("user");
//然后跳转到登录页面
resp.sendRedirect("blog_login.html");
}
}
2.2.3.3 作者查询接口
创建一个AuthorServlet类继承HttpServlet类,重写doGet()方法实现通过前端传输的用户id获取用户信息。
具体实现如下:
java
package api;
import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;
import model.UserDao;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author zq
*/
@WebServlet("/author")
public class AuthorServlet extends HttpServlet {
private ObjectMapper objectMapper;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//博客详情页根据博客id获取用户,由于是queryString所以getParameter就能拿到
String blogId = req.getParameter("blogId");
if (blogId==null){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("参数非法缺少blogID");
return;
}
//根据·blogId查询blog对象
BlogDao blogDao = new BlogDao();
Blog blog = blogDao.selectById(Integer.parseInt(blogId));
if (blog==null){
//说明没找到
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("没有找到指定博客;blogId=" + blogId);
}
UserDao userDao = new UserDao();
User author = userDao.selecetById(blog.getUserId());
String respJson = objectMapper.writeValueAsString(author);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
2.2.3.4 博客接口
创建一个BlogServlet类继承HttpServlet类,重写doGet()方法实现通过判断博客id是否存在确定获取所有博客还是博客详情,重写Post()方法实现博客发布功能。
具体实现如下:
java
package api;
import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* @author zq
*/
//实现前后端交互的代码
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
//加载页面时显示所有博客查询数据库
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BlogDao blogDao = new BlogDao();
//尝试获取一下queryString中的blogId字段
String blogId = req.getParameter("blogId");
if (blogId==null){
//说明本次获取的是列表页
List<Blog> blogs = blogDao.selectAll();
//将blogs转成符合要求的json格式字符串
String respJson = objectMapper.writeValueAsString(blogs);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}else {
//存在,说明此处获取的是博客内容页
//根据id查询博客
Blog blog = blogDao.selectById(Integer.parseInt(blogId));
//可能存在博客Id不存在的情况
if(blog==null){
System.out.println("当前blogId=" +blogId+ "对应的博客不存在");
}
String respJson = objectMapper.writeValueAsString(blog);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respJson);
}
}
@Override //实现发布博客
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//读取请求,构造blog对象,插入数据库1即可
HttpSession httpSession = req.getSession(false);
if (httpSession==null){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前未登录,无法发布博客");
return;
}
User user = (User) httpSession.getAttribute("user");
if (user==null){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前未登录,无法发布博客");
return;
}
//在登录状态时,就拿到作者
//获取博客正文和标题,此时需要指定字符集,然后通过指定的字符集插入数据库
req.setCharacterEncoding("utf8");
String title = req.getParameter("title");
String content = req.getParameter("content");
if (title==null||"".equals(title)||content==null||"".equals(content)){
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前数据为空,无法发布博客");
return;
}
//不为空时构造blog对象然后插入到数据库
Blog blog = new Blog();
blog.setContent(content);
blog.setTitle(title);
blog.setUserId(user.getUserId());
BlogDao blogDao = new BlogDao();
blogDao.add(blog);//blog里面的新增博客方法
//跳转到博客列表页
resp.sendRedirect("blog_list.html");
}
}
2.2.3.4 删除博客接口
创建一个delBlogServlet类继承HttpServlet类,重写doGet()方法实现通过判断用户是否是登录并且用户是否是博客作者,确定是否能够删除博客。
具体实现如下:
java
package api;
import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* @author zq
*/
@WebServlet("/delBlog")
public class delBlogServlet extends HttpServlet {
//private ObjectMapper objectMapper;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.首先判断是否登录
HttpSession session = req.getSession(false);
if (session==null){
resp.setStatus(403);
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("当前未登录请登录");
return;
}
//获取到当前用户
User user = (User) session.getAttribute("user");
//2.判断用户是否为空
if (user==null){
resp.setStatus(403);
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("当前未登录请登录");
return;
}
BlogDao blogDao = new BlogDao();
//3.尝试获取一下queryString中的blogId字段,判断博客是否存在
String blogId = req.getParameter("blogId");
//查询到博客对象
Blog blog = blogDao.selectById(Integer.parseInt(blogId));
if (blogId==null){
resp.setStatus(404);
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("博客数据不存在");
return;
}
//4.判断登录用户是否是作者
if (blog.getUserId()!= user.getUserId()){
resp.setStatus(403);
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("当前不能删除别人的博客");
return;
}
//根据博客Id删除博客
blogDao.delete(Integer.parseInt(blogId));
resp.sendRedirect("blog_list.html");
}
}
3. 小结
本项目可以在我的gitee上查看源码,后续会通过SSM框架重新实现并完善博客系统功能。https://gitee.com/zxxqqa/blog_system