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 测试:
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 测试
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,"系统异常");
}
}
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测试
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测试
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>