SpringBoot项目-个人博客系统的实现
作者:mmseoamin日期:2023-12-14

1.博客系统简要分析

在这里插入图片描述

一共有6个网页,分别是博客列表页面,博客详情页面,发布博客页面,博客登陆页面,博客更新页面,修改个人信息页面(暂未实现),我们要实现的功能有,实现博客列表的展示页面,博客详情页面的展示功能,用户登录功能,显示用户信息功能,编辑博客功能,发布博客功能,删除博客功能,退出登录功能

我们现在就开始写吧

2.创建springBoot项目

1.创建项目,勾选需要的依赖

在这里插入图片描述

在这里插入图片描述

2.删除无用的目录及文件

在这里插入图片描述

4.创建框架

在这里插入图片描述

3.配置文件(选择yml)

application.xml

spring:
  profiles:
    active: dev
logging:
  file:
    path: logs/
  level:
    root: info

application-dev.xml

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8
    username: root
    password: 111111
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration: # 配置打印 MyBatis 执行的 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true  #自动驼峰转换
  mapper-locations: classpath:mapper/***Mapper.xml

application-prod.xml

spring:
  datasource:
      url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8
      username: root
      password: 111111
      driver-class-name: com.mysql.cj.jdbc.Driver
mybatis: #上线就不用打印mybatis执行日志
  configuration:
    map-underscore-to-camel-case: true  #自动驼峰转换

3.数据库准备

这个项目的数据库表比较简单

只有两个表

1.设计表结构

在这里插入图片描述

2.建表sql

-- 建表SQL
create database if not exists `java_blog_spring1` charset utf8mb4;
-- 用户表
drop table if exists `java_blog_spring`.`user`;
CREATE TABLE `java_blog_spring`.`user` (
 `id` INT NOT NULL AUTO_INCREMENT,
 `user_name` VARCHAR(128) NOT NULL,
 `password` VARCHAR(128) NOT NULL,
 `photo`  VARCHAR(128) NOT NULL,
 `github_url` VARCHAR(128) NULL,
 `delete_flag` TINYINT(4) NULL DEFAULT 0,
 `create_time` TIMESTAMP NULL DEFAULT current_timestamp(),
 PRIMARY KEY (`id`),
 UNIQUE INDEX `user_name_UNIQUE` (`user_name` ASC))
ENGINE = InnoDB DEFAULT CHARACTER SET = utf8mb4 COMMENT = '用户表';
-- 博客表
drop table if exists `java_blog_spring`.`blog`;
CREATE TABLE `java_blog_spring`.`blog` (
 `id` INT NOT NULL AUTO_INCREMENT,
 `title` VARCHAR(200) NULL,
 `content` TEXT NULL,
 `user_id` INT(11) NULL,
 `delete_flag` TINYINT(4) NULL DEFAULT 0,
 `create_time` TIMESTAMP NULL DEFAULT current_timestamp(),
 PRIMARY KEY (`id`))
ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '博客表';
-- 新增用户信息
insert into `java_blog_spring`.`user` (`user_name`, `password`,`photo`,`github_url`)values
("zhangsan","123456","pic/doge.jpg","https://gitee.com/bubble-fish666/class-java45");
insert into `java_blog_spring`.`user` (`user_name`, `password`,`photo`,`github_url`)values
("lisi","123456","pic/doge.jpg","https://gitee.com/bubble-fish666/class-java45");
insert into `java_blog_spring`.`blog` (`title`,`content`,`user_id`) values
("第一篇博客","111我是博客正文我是博客正文我是博客正文",1);
insert into `java_blog_spring`.`blog` (`title`,`content`,`user_id`) values
("第一篇博客","222我是博客正文我是博客正⽂我是博客正文",2);
use java_blog_spring;
-- 查询两个表的数据
select * from user;
select * from blog;

4.接口设计

  1. 获取所有的博客列表

5.数据库相关查询操作

  1. 根据用户id查询用户信息
  2. 根据用户名称查询用户
  3. 查询所有未删除的博客(按照时间降序排列)
  4. 根据博客id查询博客详情
  5. 插入一条博客
  6. 根据博客id更新博客
  7. 删除博客
  8. 根据用户id查询博客数量

6.写代码准备工作

1.model包下,创建Java实体类

我们在配置文件中配置了数据库表字段到类属性的自动驼峰转换,所以可以不用进行重命名

自动驼峰映射

configuration:
 map-underscore-to-camel-case: true

1.User类

@Data
public class User {
    // java中属性使用小驼峰命名
    // 我们配置了自动驼峰转换
    private Integer id;
    private String userName;
    private String passWord;
    private String photo;
    private String githubUrl;
    private Byte deleteFlag;
    private Date createTime;
}

2.Blog类

@Data
@Data
public class Blog {
    private Integer id;
    private String title;
    private String content;
    private Integer userId;
    private Integer deleteFlag;
    private Date createTime;
}

2.mapper层

  • 简单的sql语句我们使用注解实现
  • 复杂的sql语句使用xml配置文件实现

    1.userMapper接口

    @Mapper
    public interface UserMapper {
        /**
         * 根据用户id查询用户信息
         * @param id
         * @return
         */
        @Select("select user_name, password, photo, github_url from user where delete_flag = 0 and id = #{id}")
        User selectById(Integer id);
        /**
         * 根据用户名称查询用户
         * @param userName
         * @return
         */
        @Select(("select user_name, password, photo, github_url from user where delete_flag = 0 and user_name = #{userName}"))
        User selectByName(String userName);
    }
    

    2.BlogMapper接口

    @Mapper
    public interface BlogMapper {
        /**
         * 查询所有未删除的博客.按照时间降序排列
         * @return
         */
        @Select("select id, title, content, user_id, create_time from blog where delete_flag = 0 order by create_time;")
        List selectAllBlog();
        /**
         * 根据博客id查询博客详情
         * @param blogId
         * @return
         */
        @Select("select id, title, content, user_id, create_time from blog where delete_flag = 0 and id = #{blogId}")
        Blog selectByBlogId(Integer blogId);
        /**
         * 插入一条博客
         * @param blog
         * @return
         */
        @Insert("insert into blog (title, content, user_id) values (#{title, #{content}, #{userId})")
        Integer insertBlog(Blog blog);
        /**
         * 根据博客id更新博客
         * 删除博客就是把delete_id改为1
         * @return
         */
        Integer updateBlog(Blog blog);
        /**
         * 根据用户id查询博客数量
         * @param userId
         * @return
         */
        @Select("select count(id) from blog where delete_flag = 0 and user_id = #{userId}")
        Integer selectBlogCount(Integer userId);
    }
    

    因为更新博客内容的sql语句比较复杂,我们就不采用注解的方式,使用配置文件的方式来写

    3.BlogMapper.xml

    
    
    
        
            update blog
            
                title=#{title},
                content=#{content},
                delete_flag=#{deleteFlag},
            
            
                id = #{id}
            
        
    
    

    3.Service包下

    service层被称作业务层,它用来处理逻辑上的业务,而不去考虑具体的实现,这样controller层就不会直接去调用mapper层,可以将代码解耦,便于扩展

    1.UserService类

    @Service
    public class UserService {
        @Autowired
        // 将UserMapper对象注入进来
        private UserMapper userMapper;
        /**
         * 根据用户id查询用户信息
         * @param id
         * @return
         */
        public User selectById(Integer id) {
            return userMapper.selectById(id);
        }
        /**
         * 根据用户名称查询用户
         * @param userName
         * @return
         */
        public User selectByName(String userName) {
            return userMapper.selectByName(userName);
        }
    }
    

    2.BlogService类

    @Service
    public class BlogService {
        @Autowired
        private BlogMapper blogMapper;
        /**
         * 查询所有未删除的博客.按照时间降序排列
         * @return
         */
        public List selectAllBlog() {
            return blogMapper.selectAllBlog();
        }
        /**
         * 根据博客id查询博客详情
         * @param blogId
         * @return
         */
        public Blog selectByBlogId(Integer blogId) {
            return blogMapper.selectByBlogId(blogId);
        }
        /**
         * 插入一条博客
         * @param blog
         * @return
         */
        public Integer insertBlog(Blog blog) {
            return blogMapper.insertBlog(blog);
        }
        /**
         * 根据博客id更新博客
         * 删除博客就是把delete_id改为1
         * @return
         */
        public Integer updateBlog(Blog blog) {
            return blogMapper.updateBlog(blog);
        }
        /**
         * 根据用户id查询博客数量
         * @param userId
         * @return
         */
        public Integer selectBlogCount(Integer userId) {
            return blogMapper.selectBlogCount(userId);
        }
    }
    

    4.测试

    使用BlogMapper做演示,UserMapper同理

    在mapper接口点击Fn+Alt+Insert(按钮因电脑而异,不行可以试下Alt+Insert)

    然后在弹出框中点击Test

    在这里插入图片描述

    然后勾选需要测试的方法

    在这里插入图片描述

    此时就可以看到test包下出现了对应的类

    在这里插入图片描述

    然后我们就可以在这里写测试方法

    1.BlogMapperTest测试类

    @SpringBootTest
    class BlogMapperTest {
        @Autowired
        private BlogService blogService;
        @Test
        void selectAllBlog() {
            List blogs = blogService.selectAllBlog();
            System.out.println(blogs.toString());
        }
        @Test
        void selectByBlogId() {
            System.out.println(blogService.selectByBlogId(2).toString());
        }
        @Test
        void insertBlog() {
            Blog blog = new Blog();
            blog.setTitle("测试");
            blog.setContent("测试正文");
            blog.setUserId(1);
            System.out.println(blogService.insertBlog(blog));
        }
        @Test
        void updateBlog() {
            Blog blog = new Blog();
            blog.setTitle("测试更新");
            blog.setId(1);
            System.out.println(blogService.updateBlog(blog));
        }
        @Test
        void deleteBlog() {
            Blog blog = new Blog();
            blog.setDeleteFlag(1);
            blog.setId(1);
            System.out.println(blogService.updateBlog(blog));
        }
        @Test
        void selectBlogCount() {
            System.out.println(blogService.selectBlogCount(2));
        }
    }
    

    2.UserMapperTest测试类

    @SpringBootTest
    class UserMapperTest {
        @Autowired
        private UserService userService;
        @Test
        void selectById() {
            System.out.println(userService.selectById(1).toString());
        }
        @Test
        void selectByName() {
            System.out.println(userService.selectByName("zhangsan").toString());
        }
    }
    

    3.测试结果

    在这里插入图片描述

    在这里插入图片描述

    5.添加前端界面

    把之前写好的博客系统静态页面拷贝到static⽬录下

    在这里插入图片描述

    6.添加公共模块

    ⼯具层(common) => 统⼀返回类, 统⼀异常处理类

    1.添加统一返回类Result

    @Data
    public class Result {
        private Integer code;
        private String msg;
        private Object data;
        /**
         * 业务执行成功返回的数据
         * @return
         */
        public static Result success(String msg, Object data) {
            Result result = new Result();
            result.setCode(200);
            result.setMsg(msg);
            result.setData(data);
            return result;
        }
        /**
         * 业务执行成功返回的数据
         * @return
         */
        public static Result success(Object data) {
            Result result = new Result();
            result.setCode(200);
            result.setMsg("执行成功");
            result.setData(data);
            return result;
        }
        /**
         * 业务执行失败返回的数据
         * @return
         */
        public static Result fail(Integer code, String msg, Object data) {
            Result result = new Result();
            result.setCode(code);
            result.setMsg(msg);
            result.setData(data);
            return result;
        }
        /**
         * 业务执行失败返回的数据
         * @return
         */
        public static Result fail(Integer code, String msg) {
            Result result = new Result();
            result.setCode(code);
            result.setMsg(msg);
            return result;
        }
    }
    

    2.添加统一异常处理类

    使用code = -1表示出现异常

    @ControllerAdvice
    @ResponseBody
    public class ErrorAdvice {
        @ExceptionHandler
        public Result error (Exception e) {
            return Result.fail(-1, e.getMessage());
        }
    }
    

    3.添加统一返回格式

    在数据返回之前调用此方法,将返回数据格式统一

    如果是String类型会报错,所以我们要处理一下,异常使用@SneakyThrows注解

    如果返回的数据格式,已经是Result类型,就不需要处理,直接返回即可

    @ControllerAdvice
    public class ResponseAdvice implements ResponseBodyAdvice {
        /**
         * 内容是否需要重写
         * 返回true表示需要重写
         * @param returnType
         * @param converterType
         * @return
         */
        @Override
        public boolean supports(MethodParameter returnType, Class converterType) {
            return true;
        }
        /**
         * 方法返回之前调用此方法
         */
        //
        @SneakyThrows
        @Override
        public Object beforeBodyWrite(Object body, // 相应的正文内容
                                      MethodParameter returnType,
                                      MediaType selectedContentType,
                                      Class selectedConverterType,
                                      ServerHttpRequest request,
                                      ServerHttpResponse response) {
            //如果返回的数据格式,已经是Result类型,就不需要处理,直接返回即可
            if (body instanceof Result) {
                return body;
            }
            // 如果是String类型会报错,所以我们要处理一下
            if (body instanceof String) {
                ObjectMapper objectMapper = new ObjectMapper();
                objectMapper.writeValueAsString(body);
            }
            return Result.success(body);
        }
    }
    

    7.实现博客列表显示

    1.约定前后端交互的接口

    【请求】

    • blog/getList

      【响应】

      1. [200]
      • [200]返回数据成功,显示博客列表
      • [-1]目前没有博客
        1. [401]没有权限访问
        2. [error]访问出现错误,打印异常信息

        浏览器给服务器发送一个blog/getList这样的Http请求,服务器返回给浏览器一个json格式的数据

        2.实现服务器代码

        @RestController
        @RequestMapping("/blog")
        public class BlogController {
            @Autowired
            private BlogService blogService;
            @RequestMapping("/getList")
            public List getList() {
                // 获取博客列表
                List blogs = blogService.selectAllBlog();
                if (blogs == null) {
                    return null;
                }
                return blogs;
            }
        }
        

        使用postman测试成功,服务器正确返回数据

        在这里插入图片描述

        3.实现客户端代码

        修改 blog_list.html, 删除之前写死的博客内容(即 ), 并新增js 代码处理 ajax 请求.

        1. 使用ajax给服务器发送数据
        2. 服务器返回一个json数据格式的响应
        3. 前端根据这个响应使用DOM API构造页面内容
        4. 响应中的时间为时间戳,需要修改
        5. 列表中拿到的content应该是已经裁剪过的摘要
        6. 跳转到博客详情页的url应该在后面加上参数blogId=1,这样可以让博客详情页知道当前访问的是哪篇博客
            
            
        

        4.处理日期显示问题

        SimpleDateFormat 格式化

        创建一个DateUtil工具类

        public class DateUtil {
            public static String format(Date date) {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
                return simpleDateFormat.format(date);
            }
        }
        

        重新获取博客创建时间

        @Data
        public class Blog {
            private Integer id;
            private String title;
            private String content;
            private Integer userId;
            private Integer deleteFlag;
            private Date createTime;
            public String getCreateTime() {
                return DateUtil.format(createTime);
            }
        }
        

        5.处理裁剪摘要问题

        博客列表页面应该显示的是正文的摘要,并非全部显示出来,在博客的详情页面才需要全部显示出来

        修改Blog Service中的方法

            /**
             * 查询所有未删除的博客.按照时间降序排列
             * @return
             */
            public List selectAllBlog() {
                List blogs = blogMapper.selectAllBlog();
                // 遍历如果博客的正文长度超过100,就裁剪
                for (Blog blog : blogs) {
                    if (blog.getContent().length() > 100) {
                        blog.setContent(blog.getContent().substring(0,100)+"...");
                    }
                }
                return blogs;
            }
        

        4.博客列表界面显示成功

        在这里插入图片描述

        8.实现博客详情

        点击查看全文能进入当前博客详情页面,根据博客id动态的获取博客详情

        1.约定前后端交互接口

        【请求】

        • blog/getBlogDetails

          【响应】

          1. [200]
          • [200]返回数据成功,显示博客详情
          • [-1]该博客不存在
            1. [401]没有权限访问
            2. [error]访问出现错误,打印异常信息

            浏览器给服务器发送一个blog.getDetails的Http请求,服务器返回给浏览器一个json格式的数据

            2.实现服务器代码

                @RequestMapping("/blog/getBlogDetails")
                public Result getDetails(Integer blogId) {
                    // 判合法
                    if (blogId == null || blogId <= 0) {
                        return Result.fail(-1,"博客不存在");
                    }
                    Blog blog = blogService.selectByBlogId(blogId);
                    if (blog == null) {
                        return Result.fail(-1,"博客不存在");
                    }
                    return Result.success(blog);
                }
            

            使用postman测试成功,服务器正确返回数据

            在这里插入图片描述

            3.实现客户端代码

            1. 使用ajax,根据当前页面的URL中的blogId参数(使⽤ location.search 即可得到形如 ?blogId=1 的数据),给服务器发送请求
            2. 服务器返回一个json数据格式的响应
            3. 前端根据这个响应使用通过 editor.md 转换成 html, 并显示

            1. 引⼊ editor.md

                
                    
            

            4.博客列表界面显示成功

            9.实现登陆

            • 登录页面提供一个form表单,通过form的方式把用户名密码提交给服务器
            • 服务器端验证⽤户名密码是否正确
            • 如果密码正确, 则在服务器端创建 Session , 并把 sessionId 通过 Cookie 返回给浏览器
            • 前后端分离的项⽬中, 虽然主要使⽤ ajax 进⾏前后端交互, 但是也不是完全不能⽤ form
              

              1.约定前后端交互接口

              【请求】

              • user/login

                【响应】

                1. [200]
                • [200] 登陆成功
                • [-1] 用户名或密码不能为空
                • [-2] 用户名或密码错误
                  1. [error]访问出现错误,打印异常信息

                  2.实现服务器代码

                  创建 UserController

                  @RequestMapping("/user")
                  @RestController
                  public class UserController {
                      
                      @Autowired
                      private UserService userService;
                  	@RequestMapping("/login")
                      public Result login(String username, String password) {
                          // 判空
                          if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
                              return Result.fail(-1,"用户名或密码不能为空");
                          }
                          // 判断用户名密码是否匹配
                          User user = userService.selectByName(username);
                          if (user == null || !user.getPassWord().equals(password)) {
                              return Result.fail(-2,"用户名或密码错误");
                          }
                          return Result.success("登陆成功");
                      }
                  }
                  

                  使用postman测试登录成功

                  在这里插入图片描述

                  3.实现客户端代码

                      
                      
                  

                  3.实现客户端代码

                      
                      
                  

                  4.登陆功能实现成功

                  在这里插入图片描述

                  剩下的功能在下篇博客实现~