SpringBoot+vue 个人博客系统

SpringBoot+vue 个人博客系统

SpringBoot+vue练手项目—博客系统

项目使用技术 :

springboot + mybatisplus+redis+mysql

1. 工程搭建

前端的工程地址:

链接:https://pan.baidu.com/s/1cg_11ctsbbq_WM9BnpcOaQ
提取码:nrun

npm install
npm run build
npm run dev

1.1 新建maven工程

pom.xml(blog-parent)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jihu</groupId>
    <artifactId>blog-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>blog-api</module>
    </modules>
    <!--声明pom代表他是一个父工程-->
    <packaging>pom</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
        <relativePath/>
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
        
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.2</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.10</version>
        </dependency>
    </dependencies>
</dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

pom.xml(blog-api)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>blog-parent</artifactId>
        <groupId>com.jihu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>blog-api</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <!-- 排除 默认使用的logback  -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- log4j2 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>


        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.2</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.10</version>
        </dependency>
    </dependencies>


</project>

1.2 application.yml

server:
  port: 8888

spring:
  application:
    name: mszlu_blog

#数据库的配置
  datasource:
    url: jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=UTF-8&serverTimeZone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

#mybatis-plus
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  #打印日志 如sql语句
  global-config:
    db-config:
      table-prefix: ms_  #标识表的前缀为ms_
#指定mapper文件的位置
mybatis-plus:
  config-location: classpath:mapper/*.xml

1.3 配置 分页 和跨域

分页

package com.jihu.blog.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
//扫包,将此包下的接口生成代理实现类,并且注册到spring容器中
@MapperScan("com.jihu.blog.mapper")
public class MybatisPlusConfig {
    //分页插件
    @Bean
    public  MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }

}

跨域

package com.jihu.blog.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMVCConfig implements WebMvcConfigurer {
    //实现跨域请求
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET","HEAD","POST","PUT","DELETE","OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

1.4启动类

package com.jihu.blog;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BlogApp {
    public static void main(String[] args) {
        SpringApplication.run(BlogApp.class,args);
    }

}

2.首页-文章列表

2.1 接口说明

接口url:/articles

请求方式:POST

请求参数:

参数名称 参数类型 说明
page int 当前页数
pageSize int 每页显示的数量

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id": 1,
            "title": "springboot介绍以及入门案例",
            "summary": "通过Spring Boot实现的服务,只需要依靠一个Java类,把它打包成jar,并通过`java -jar`命令就可以运行起来。

这一切相较于传统Spring应用来说,已经变得非常的轻便、简单。",
            "commentCounts": 2,
            "viewCounts": 54,
            "weight": 1,
            "createDate": "2609-06-26 15:58",
            "author": "12",
            "body": null,
            "tags": [
                {
                    "id": 5,
                    "avatar": null,
                    "tagName": "444"
                },
                {
                    "id": 7,
                    "avatar": null,
                    "tagName": "22"
                },
                {
                    "id": 8,
                    "avatar": null,
                    "tagName": "11"
                }
            ],
            "categorys": null
        },
        {
            "id": 9,
            "title": "Vue.js 是什么",
            "summary": "Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。",
            "commentCounts": 0,
            "viewCounts": 3,
            "weight": 0,
            "createDate": "2609-06-27 11:25",
            "author": "12",
            "body": null,
            "tags": [
                {
                    "id": 7,
                    "avatar": null,
                    "tagName": "22"
                }
            ],
            "categorys": null
        },
        {
            "id": 10,
            "title": "Element相关",
            "summary": "本节将介绍如何在项目中使用 Element。",
            "commentCounts": 0,
            "viewCounts": 3,
            "weight": 0,
            "createDate": "2609-06-27 11:25",
            "author": "12",
            "body": null,
            "tags": [
                {
                    "id": 5,
                    "avatar": null,
                    "tagName": "444"
                },
                {
                    "id": 6,
                    "avatar": null,
                    "tagName": "33"
                },
                {
                    "id": 7,
                    "avatar": null,
                    "tagName": "22"
                },
                {
                    "id": 8,
                    "avatar": null,
                    "tagName": "11"
                }
            ],
            "categorys": null
        }
    ]
}

2.2 编码

Spring基于注解的开发
每个注解的作用

2.2.1 表结构

文章表

CREATE TABLE `blog`.`ms_article`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `comment_counts` int(0) NULL DEFAULT NULL COMMENT "评论数量",
  `create_date` bigint(0) NULL DEFAULT NULL COMMENT "创建时间",
  `summary` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT "简介",
  `title` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT "标题",
  `view_counts` int(0) NULL DEFAULT NULL COMMENT "浏览数量",
  `weight` int(0) NOT NULL COMMENT "是否置顶",
  `author_id` bigint(0) NULL DEFAULT NULL COMMENT "作者id",
  `body_id` bigint(0) NULL DEFAULT NULL COMMENT "内容id",
  `category_id` int(0) NULL DEFAULT NULL COMMENT "类别id",
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

标签表

id,文章id,标签id,通过文章id可以间接查到标签id

CREATE TABLE `blog`.`ms_tag`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `article_id` bigint(0) NOT NULL,
  `tag_id` bigint(0) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `article_id`(`article_id`) USING BTREE,
  INDEX `tag_id`(`tag_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

用户表

CREATE TABLE `blog`.`ms_sys_user`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `account` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT "账号",
  `admin` bit(1) NULL DEFAULT NULL COMMENT "是否管理员",
  `avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT "头像",
  `create_date` bigint(0) NULL DEFAULT NULL COMMENT "注册时间",
  `deleted` bit(1) NULL DEFAULT NULL COMMENT "是否删除",
  `email` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT "邮箱",
  `last_login` bigint(0) NULL DEFAULT NULL COMMENT "最后登录时间",
  `mobile_phone_number` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT "手机号",
  `nickname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT "昵称",
  `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT "密码",
  `salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT "加密盐",
  `status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT "状态",
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

2.2.2 entity层

Article 文章实体类
package com.jihu.blog.dao.pojo;

import lombok.Data;

@Data
public class Article {
    public static final int Article_TOP = 1;

    public static final int Article_Common = 0;

    private Long id;

    private String title;

    private String summary;

    private int commentCounts;

    private int viewCounts;

    /**
     * 作者id
     */
    private Long authorId;
    /**
     * 内容id
     */
    private Long bodyId;
    /**
     *类别id
     */
    private Long categoryId;

    /**
     * 置顶
     */
    private int weight = Article_Common;


    /**
     * 创建时间
     */
    private Long createDate;
}

SysUser 用户实体类
package com.jihu.blog.dao.pojo;

import lombok.Data;

@Data
public class SysUser {

    private Long id;

    private String account;

    private Integer admin;

    private String avatar;

    private Long createDate;

    private Integer deleted;

    private String email;

    private Long lastLogin;

    private String mobilePhoneNumber;

    private String nickname;

    private String password;

    private String salt;

    private String status;
}
Tag 标签实体类
package com.jihu.blog.dao.pojo;

import lombok.Data;

@Data
public class Tag {

    private Long id;

    private String avatar;

    private String tagName;

}

2.2.3 Controller层

ArticleController
package com.jihu.blog.controller;

import com.jihu.blog.service.ArticleService;
import com.jihu.blog.vo.Result;
import com.jihu.blog.vo.params.PageParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("articles")
public class ArticleController {

    @Autowired
    private ArticleService articleService;

    //首页  文章列表
    @PostMapping
    public Result listArticle(@RequestBody PageParams pageParams){
        //ArticleVo 页面接收的数据
        return articleService.listArticle(pageParams);

    }

}

2.2.4 Service层

ArticleService
package com.jihu.blog.service;

import com.jihu.blog.vo.Result;
import com.jihu.blog.vo.params.PageParams;

public interface ArticleService {

    Result listArticle(PageParams pageParams);
}

ArticleServiceImpl
package com.jihu.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jihu.blog.dao.mapper.ArticleMapper;
import com.jihu.blog.dao.mapper.TagMapper;
import com.jihu.blog.dao.pojo.Article;
import com.jihu.blog.service.ArticleService;
import com.jihu.blog.service.SysUserService;
import com.jihu.blog.service.TagService;
import com.jihu.blog.vo.ArticleVo;
import com.jihu.blog.vo.Result;
import com.jihu.blog.vo.params.PageParams;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private TagService tagService;

    @Autowired
    private SysUserService sysUserService;


    @Override
    public Result listArticle(PageParams pageParams) {

        Page<Article> page = new Page<>(pageParams.getPage(), pageParams.getPageSize());
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();

        //是否置顶进行排序
        queryWrapper.orderByDesc(Article::getWeight,Article::getCreateDate);

        Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);
        List<Article> records = articlePage.getRecords();
        //能直接返回吗  肯定不行  所以需要进行如下转换
       List<ArticleVo> articleVoList = copyList(records,true,true);
        return Result.success(articleVoList);
    }

    private List<ArticleVo> copyList(List<Article> records,boolean isTag,boolean isAuthor) {
        List<ArticleVo> articleVoList = new ArrayList<>();
        for (Article record : records) {
            articleVoList.add(copy(record,isTag,isAuthor));
        }
        return articleVoList;
    }

    private ArticleVo copy(Article article,boolean isTag,boolean isAuthor){
        ArticleVo articleVo = new ArticleVo();
        BeanUtils.copyProperties(article,articleVo);
        articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-MM-dd HH:mm"));

        //并不是所有的接口,都需要标签,作者信息
        if (isTag){
            Long articleId = article.getId();
            articleVo.setTags(tagService.findTagsByrticleId(articleId));
        }
        if (isAuthor){
            Long authorId = article.getAuthorId();
            articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());
        }
        
        return articleVo;
    }
  
}
SysUserService
package com.jihu.blog.service;
import com.jihu.blog.dao.pojo.SysUser;
public interface SysUserService {
    SysUser findUserById(Long id);
}

SysUserServiceImpl
package com.jihu.blog.service.impl;

import com.jihu.blog.dao.mapper.SysUserMapper;
import com.jihu.blog.dao.pojo.SysUser;
import com.jihu.blog.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    public SysUser findUserById(Long id) {
        SysUser sysUser = sysUserMapper.selectById(id);
        //防止空指针出现 加一个判断
        if (sysUser == null){
            sysUser = new SysUser();
            sysUser.setNickname("马神之路");
        }
        return sysUser;
    }
}

TagService
package com.jihu.blog.service;
import com.jihu.blog.vo.TagVo;
import java.util.List;
public interface TagService {
    List<TagVo> findTagsByrticleId(Long articleId);
}

TagServiceImpl
package com.jihu.blog.service.impl;

import com.jihu.blog.dao.mapper.TagMapper;
import com.jihu.blog.dao.pojo.Tag;
import com.jihu.blog.service.TagService;
import com.jihu.blog.vo.TagVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class TagServiceImpl implements TagService {

    @Autowired
    private TagMapper tagMapper;

    @Override
    public List<TagVo> findTagsByrticleId(Long articleId) {
        //mybatisplus  无法进行多表查询
        List<Tag> tags = tagMapper.findTagsByrticleId(articleId);

        return copyList(tags);
    }

    private List<TagVo> copyList(List<Tag> tags) {
        List<TagVo> tagVoList = new ArrayList<>();
        for (Tag tag : tags) {
            tagVoList.add(copy(tag));
        }
        return  tagVoList;
    }

    private TagVo copy(Tag tag) {
        TagVo tagVo = new TagVo();
        BeanUtils.copyProperties(tag,tagVo);
        return  tagVo;
    }
}

2.2.5 Mapper层

ArticleMapper
package com.jihu.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jihu.blog.dao.pojo.Article;
//BaseMapper mybatisplus中提供的可以让我们很方便的查询这张表
public interface ArticleMapper  extends BaseMapper<Article> {

}
SysUserMapper
package com.jihu.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jihu.blog.dao.pojo.SysUser;
public interface SysUserMapper extends BaseMapper<SysUser> {
}
TagMapper
package com.jihu.blog.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jihu.blog.dao.pojo.Tag;

import java.util.List;

public interface TagMapper extends BaseMapper<Tag> {

    /**
     * 根据文章id 查询标签列表
     * @param articleId
     * @return
     */
    List<Tag> findTagsByrticleId(Long articleId);
}
         
TagMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis配置文件-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.jihu.blog.dao.mapper.TagMapper">

    <sql id="all">
        id,avatar,tag_name as tagName
    </sql>

<!--        List<Tag> findTagsByArticleId(Long articleId);
在这个文件中,id代表方法名,parameterType表示输入变量的名字,resultType表示泛型的类型-->

    <select id="findTagsByrticleId" parameterType="long" resultType="com.jihu.blog.dao.pojo.Tag">
        select  id,avatar,tag_name as tagName from ms_tag
        where id in
        (select tag_id from ms_article_tag where article_id=#{articleId})
    </select>
</mapper>

2.2.6 Vo层

Result(统一最后的结果)
package com.jihu.blog.vo;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Result {
    private boolean success;
    private int code ;
    private String msg;
    private  Object data;

    public static Result success(Object data) {
        return new Result(true, 200, "success", data);
    }

    public static Result fail(int code,String msg) {
        return new Result(false, code, msg, null);
    }
}
ArticleVo 建立与前端交互的Vo文件
package com.jihu.blog.vo;

import lombok.Data;

import java.util.List;

//建立与前端交互的Vo文件
@Data
public class ArticleVo {
    private Long id;

    private String title;

    private String summary;

    private int commentCounts;

    private int viewCounts;

    private int weight;
    /**
     * 创建时间
     */
    private String createDate;

    private String author;

//    private ArticleBodyVo body;

    private List<TagVo> tags;

//    private List<CategoryVo> categorys;
}
新建TagVo
package com.jihu.blog.vo;

import lombok.Data;

@Data
public class TagVo {
    private Long id;
    private String tagName;
}
新建PageParams
package com.jihu.blog.vo.params;
import lombok.Data;
@Data
public class PageParams {
    private  int Page =1;  //当前页数
    private  int PageSize =10;  //每页显示的数量
}	

2.2.7 测试:

image-20220404162338575

3.首页- 最热标签

3.1接口说明

接口url:/tags/hot

请求方式:GET

请求参数:无

id: 标签名称 ,我们期望点击标签关于文章的所有列表都显示出来

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id":1,
            "tagName":"4444"
        }
    ]
}

3.2编码

3.2.1Controller层

package com.jihu.blog.controller;

import com.jihu.blog.service.TagService;
import com.jihu.blog.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("tags")
public class TagsController {

    @Autowired
    private TagService tagService;

    @GetMapping("hot")
    public Result hot(){
        int limit = 6;
        return tagService.hots(limit);
    }
}

3.2.2 Service层

建立service接口

TagService
package com.jihu.blog.service;
import com.jihu.blog.vo.Result;
import com.jihu.blog.vo.TagVo;
import java.util.List;
public interface TagService {
    Result hots(int limit);
}

建立serviceimpl实现类

TagServiceImpl
@Service
public class TagServiceImpl implements TagService {

    @Autowired
    private TagMapper tagMapper;
    
	@Override
    public Result hots(int limit) {
        /*
          1.标签所拥有的文章数量最多  即为最热标签
          2. 查询  根据tag_id 分组  计数,从大到小  排列  取前 limit个
         */
        List<Long> tagIds = tagMapper.findHotsIds(limit);
        //判断一下是否为空
        if (tagIds == null){
            return  Result.success(Collections.emptyList());
        }

        //需求的是  tagId 和 tagName  tag对象
        //需要的是这样的一个sql语句  select * from tag where id in (1,2,3...)
        List<Tag> tagList = tagMapper.findTagdByTagIds(tagIds);

        return Result.success(tagList);
    }
}    

3.2.3 Mapper层

TagMapper
package com.jihu.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jihu.blog.dao.pojo.Tag;
import java.util.List;
public interface TagMapper extends BaseMapper<Tag> {

    /**
     * 查询最热的标签 前limit条
     * @param limit
     * @return
     */
    List<Long> findHotsIds(int limit);
    /*
        根据最热标签查询 最热文章名字
     */
    List<Tag> findTagdByTagIds(List<Long> tagIds);
}
TagMapper
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis配置文件-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.jihu.blog.dao.mapper.TagMapper">

    <sql id="all">
        id,avatar,tag_name as tagName
    </sql>

    <select id="findHotsIds" parameterType="int" resultType="java.lang.Long">
        SELECT tag_id from ms_article_tag GROUP BY tag_id ORDER BY count(*) DESC limit #{limit}

    </select>

    <select id="findTagdByTagIds" parameterType="list" resultType="com.jihu.blog.dao.pojo.Tag">
        select id,tag_name as tagName  from ms_tag
        where id in
        <foreach collection="collection" item="tagId" separator="," open="("  close=")">
              #{tagId}
        </foreach>
    </select>
</mapper>

3.2.4 测试

image-20220404170243616

4.统一异常处理

不管是controller层还是service,dao层,都有可能报异常,如果是预料中的异常,可以直接捕获处理,如果是意料之外的异常,需要统一进行处理,进行记录,并给用户提示相对比较友好的信息。

AllExceptionHandler

package com.jihu.blog.handler;

import com.jihu.blog.vo.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

//对加了 @Controller 注解方法进行拦截处理   AOP的实现
@ControllerAdvice
public class AllExceptionHandler {

    //进行异常处理,  处理Exception.class的异常
    @ExceptionHandler(Exception.class)
    @ResponseBody  //返回json数据
    public Result doException(Exception ex){
        ex.printStackTrace();
        return Result.fail(-999,"系统异常");
    }

}

image-20220404171112342

5.首页-最热文章

5.1 接口说明

接口url:/articles/hot

请求方式:POST

请求参数:无

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id": 1,
            "title": "springboot介绍以及入门案例",
        },
        {
            "id": 9,
            "title": "Vue.js 是什么",
        },
        {
            "id": 10,
            "title": "Element相关",
            
        }
    ]
}

5.2 Controller层

ArticleController

@RestController
@RequestMapping("articles")
public class ArticleController {

    @Autowired
    private ArticleService articleService; 
//首页  最热文章
    @PostMapping("hot")
    public Result hotArticle(){
        int limit = 5; //取前5条

        return articleService.hotArticle(limit);

    }
}    

5.3 Service层

ArticleService

package com.jihu.blog.service;

import com.jihu.blog.vo.Result;
import com.jihu.blog.vo.params.PageParams;

public interface ArticleService {

    Result listArticle(PageParams pageParams);

    Result hotArticle(int limit);
}

ArticleServiceImpl

@Service
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    private ArticleMapper articleMapper;
/**
     * 最热文章查询
     * @param limit
     * @return
     */

    @Override
    public Result hotArticle(int limit) {
        LambdaQueryWrapper<Article> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.orderByDesc(Article::getViewCounts);
        lambdaQueryWrapper.select(Article::getId,Article::getTitle);
        lambdaQueryWrapper.last("limit "+ limit);
        //SELECT id, title from ms_article ORDER BY view_counts DESC limit 5
        List<Article> articles = articleMapper.selectList(lambdaQueryWrapper);

        return Result.success(copyList(articles,false,false));
    }
}    

5.4测试

image-20220404180253013

6.首页-最新文章

和最热文章非常类似,一个是根据浏览量来选择,一个是根据最新创建时间来选择

6.1 接口说明

接口url:/articles/new

请求方式:POST

请求参数:无

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id": 1,
            "title": "springboot介绍以及入门案例",
        },
        {
            "id": 9,
            "title": "Vue.js 是什么",
        },
        {
            "id": 10,
            "title": "Element相关",
            
        }
    ]
}

6.2 Controller层

在com.jihu.blog.controller.ArticleController中添加

 //首页  最新文章
    @PostMapping("new")
    public Result newArticle(){
        int limit = 5; //取前5条

        return articleService.newArticle(limit);

    }

6.3ArticleService

Result newArticle(int limit);

6.4ArticleServiceImpl

/**
 * 最新文章查询
 * @param limit
 * @return
 */
@Override
public Result newArticle(int limit) {
    LambdaQueryWrapper<Article> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.orderByDesc(Article::getCreateDate);
    lambdaQueryWrapper.select(Article::getId,Article::getTitle);
    lambdaQueryWrapper.last("limit "+limit);
    //SELECT id, title from ms_article ORDER BY create_data DESC limit 5
    List<Article> articles = articleMapper.selectList(lambdaQueryWrapper);

    return Result.success(copyList(articles,false,false));
}

6.5测试

image-20220404181514531

7.首页-文章归档

每一篇文章根据创建时间某年某月发表多少篇文章

7.1接口说明

接口url:/articles/listArchives

请求方式:POST

请求参数:无

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "year": "2021",
            "month": "6",
            "count": 2
        }
            
    ]
}

7.2 Controller层

com.jihu.blog.controller.ArticleController

 //首页  文章归档
    @PostMapping("listArchives")
    public Result listArchives(){
        return articleService.listArchives();
    }

7.3 ArticleService

Result listArchives();

7.4 ArticleServiceImpl

 //文章归档
    @Override
    public Result listArchives() {

       List<Archives>  archivesList  = articleMapper.listArchives();
        return  Result.success(archivesList);

    }

7.5 ArticleMapper

package com.jihu.blog.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jihu.blog.dao.dos.Archives;
import com.jihu.blog.dao.pojo.Article;
import com.jihu.blog.vo.Result;
import java.util.List;

//BaseMapper mybatisplus中提供的可以让我们很方便的查询这张表
public interface ArticleMapper  extends BaseMapper<Article> {

    List<Archives> listArchives();
}

7.6 ArticleMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis配置文件-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.jihu.blog.dao.mapper.ArticleMapper">
<!--
    select YEAR(create_date) as year,YEAR(create_date) as month ,count(*) as count from ms_article GROUP BY year,MONTH  这样查询不行
-->
    
<!--create_date 为bigint 13位,直接year()不行,需要先转date型后year()。-->
<select id="listArchives"  resultType="com.jihu.blog.dao.dos.Archives" >
select year(FROM_UNIXTIME(create_date/1000)) year,month(FROM_UNIXTIME(create_date/1000)) month, count(*)
count from ms_article group by year,month;
</select>

</mapper>

7.7测试

image-20220404184134240

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » SpringBoot+vue 个人博客系统