SpringBoot+Vue博客前后端分离项目 一工程搭建+首页后端(springboot + mybatisplus+redis+mysql+jwt+缓存)
提供前端工程,只需要实现后端接口即可。
目录
项目使用技术 :
springboot + mybatisplus+redis+mysql
提供前端工程,只需要实现后端接口即可
1. 工程搭建
前端的工程:
npm install
npm run build
npm run dev
1.1 新建maven工程
父工程
<?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.pjp</groupId>
<artifactId>pjpBlog-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- <properties>-->
<!-- <maven.compiler.source>8</maven.compiler.source>-->
<!-- <maven.compiler.target>8</maven.compiler.target>-->
<!-- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>-->
<!-- </properties>-->
<packaging>pom</packaging>
<modules>
<module>blog-api</module>
</modules>
<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>
子工程
<?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>
<parent>
<groupId>com.pjp</groupId>
<artifactId>pjpBlog-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>blog-api</artifactId>
<!-- <properties>-->
<!-- <maven.compiler.source>8</maven.compiler.source>-->
<!-- <maven.compiler.target>8</maven.compiler.target>-->
<!-- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>-->
<!-- </properties>-->
<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>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</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>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.10</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.7.0, 7.7.99]</version>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
</project>
1.2 配置
配置文件 application.propertis
#server
server.port= 8888
spring.application.name=PJPBlog
# datasource
spring.datasource.url=jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=UTF-8&serverTimeZone=UTC
spring.datasource.username=root
spring.datasource.password=pjp
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#mybatis-plus
#打印日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#标识表数据的前缀为ms_
mybatis-plus.global-config.db-config.table-prefix=ms_
配置类
@Configuration
@MapperScan("com.pjp.blog.dao.mapper")
public class MybatisPlusConfig {
//分页插件
@Bean
// 通过拦截器实现分页
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
@Configuration
public class WebMVCConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//跨域配置,不可设置为*,不安全, 前后端分离项目,可能域名不一致
//本地测试 端口不一致 也算跨域
registry.addMapping("/**").allowedOrigins("http://localhost:8080");
}
}
1.3 启动类
@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": "Mybatis-Plus如何使用分页",
"summary": "导入Mybatis-plus的启动器配置分页拦截器利用mybatis-plus的内置Page对象进行分页,然后在vo包下编写一个从页面传参数类TopicCondition(vo包里的类的每一个字段与前台页面相对应)",
"commentCounts": 2,
"viewCounts": 1000,
"weight": 1,
"createDate": "2611-02-03 04:43",
"author": "pjp",
"tags": [
{
"id": 5,
"tagName": "Mybatis-Plus",
"avatar": null
},
{
"id": 7,
"tagName": "表达式",
"avatar": null
},
{
"id": 8,
"tagName": "Java",
"avatar": null
}
]
},
{
"id": 10,
"title": "queryWrapper",
"summary": "queryWrapper是mybatis plus中实现查询的对象封装操作类,可以封装sql对象,包括where条件,order by排序,select哪些字段等等",
"commentCounts": 1,
"viewCounts": 56,
"weight": 0,
"createDate": "2611-02-02 03:45",
"author": "pjp",
"tags": [
{
"id": 7,
"tagName": "表达式",
"avatar": null
},
{
"id": 8,
"tagName": "Java",
"avatar": null
},
{
"id": 5,
"tagName": "Mybatis-Plus",
"avatar": null
},
{
"id": 6,
"tagName": "Lambda",
"avatar": null
}
]
},
{
"id": 9,
"title": "Java中Lambda表达式使用",
"summary": "Lambda表达式(闭包):java8的新特性,lambda运行将函数作为一个方法的参数,也就是函数作为参数传递到方法中。使用lambda表达式可以让代码更加简洁。",
"commentCounts": 0,
"viewCounts": 18,
"weight": 0,
"createDate": "2610-10-02 09:02",
"author": "pjp",
"tags": [
{
"id": 7,
"tagName": "表达式",
"avatar": null
}
]
}
]
}
2.2 编码
2.2.1 表结构
REATE 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;
CREATE TABLE `blog`.`ms_article_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_tag` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`tag_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 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 R
对应的pojo实体类
Article
package com.pjp.blog.dao.pojo;
import lombok.Data;
@Data
//@Data 注解的主要作用是提高代码的简洁,使用这个注解可以
// 省去代码中大量的get()、 set()、 toString()等方法;
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 Integer commentCounts;
private Integer viewCounts;
/**
* 作者id
*/
private Long authorId;
/**
* 内容id
*/
private Long bodyId;
/**
*类别id
*/
private Long categoryId;
/**
* 置顶
*/
private Integer weight;
/**
* 创建时间
*/
private Long createDate;
}
SysUser
package com.pjp.blog.dao.pojo;
import lombok.Data;
@Data
public class SysUser {
// @TableId(type = IdType.ASSIGN_ID) // 默认id类型
// 以后 用户多了之后,要进行分表操作,id就需要用分布式id了
// @TableId(type = IdType.AUTO) 数据库自增
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.pjp.blog.dao.pojo;
import lombok.Data;
@Data
public class Tag {
private Long id;
private String avatar;
private String tagName;
}
2.2.2 Controller(控制层)
package com.pjp.blog.controller;
import com.pjp.blog.service.ArticleService;
import com.pjp.blog.vo.Result;
import com.pjp.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;
//json数据进行交互
@RestController
@RequestMapping("/articles")
public class ArticleController {
@Autowired
private ArticleService articleService;
/**
* 首页 文章列表
* @param pageParams
* @return
*/
@PostMapping
// @RequestBody接收返回的请求信息 page 和 pageSize
public Result listArticle(@RequestBody PageParams pageParams){
return articleService.listArticle(pageParams);
}
}
2.2.3 VO层
View Object,视图层,其作用是将指定页面的展示数据封装起来,通常用于业务层之间的数据传递。
params包下的类主要用于封装前端返回的json参数
PageParams
@Data
public class PageParams {
// 请求参数
private int page = 1; //当前页数
private int pageSize = 10; //每页显示的数量
}
Result返回值
package com.pjp.blog.vo;
import com.sun.org.apache.regexp.internal.RE;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 使用@Data时,默认存在无参构造器(没手写有参构造器)
* 使用@Data时,手写了有参构造器,则无参构造器消失
*
* @AllArgsConstructor : 注在类上,提供类的全参构造
*/
@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
/**
* 和前端数据进行交互
*/
@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;
}
2.2.4 Service层
ArticleService
public interface ArticleService {
/**
* 分页查询 文章列表
* @param pageParams
* @return
*/
Result listArticle(PageParams pageParams);
}
实现类
@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可以使用lambda表达式构建SQL查询语句
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
// 按时间进行排序
// queryWrapper.orderByDesc(“属性”)——根据属性降序排序
// queryWrapper.orderByDesc(Article::getCreateDate);
// 是否置顶排序
// queryWrapper.orderByDesc(Article::getWeight);
queryWrapper.orderByDesc(Article::getWeight,Article::getCreateDate);
Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);
// getRecords();查询到的数据
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 article : records) {
articleVoList.add(cope(article,isTag,isAuthor));
}
return articleVoList;
}
private ArticleVo cope(Article article, boolean isTag, boolean isAuthor) {
ArticleVo articleVo = new ArticleVo();
BeanUtils.copyProperties(article, articleVo);
// vo中createDate为String Article中为long copy不了
articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyy-MM-dd HH:ss"));
// 不是所有的接口 都需要标签 作者信息
if (isTag) {
// 根据ArticleId 查询 TagId
Long articleId = article.getId();
articleVo.setTags(tagService.findTagByArticleId(articleId));
}
if (isAuthor) {
Long authorId = article.getAuthorId();
articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());
}
return articleVo;
}
}
SysUserService
public interface SysUserService {
/**
* 根据id查询用户信息
* @param id
* @return
*/
SysUser findUserById(Long id);
}
实现类
@Service
public class SysUserServiceIml 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("pjp");
}
return sysUser;
}
}
TagService
public interface TagService {
List<TagVo> findTagByArticleId(long articleId);
}
实现类
@Service
public class TagServiceImpl implements TagService {
@Autowired
private TagMapper tagMapper;
@Override
public List<TagVo> findTagByArticleId(long articleId) {
// MyBatis无法进行多表查询
List<Tag> tags = tagMapper.findTagByArticleId(articleId);
return copyList(tags);
}
public TagVo copy(Tag tag){
TagVo tagVo = new TagVo();
BeanUtils.copyProperties(tag,tagVo);
return tagVo;
}
public List<TagVo> copyList(List<Tag> tagList){
List<TagVo> tagVoList = new ArrayList<>();
for (Tag tag : tagList) {
tagVoList.add(copy(tag));
}
return tagVoList;
}
}
SysUserService 和 TagService 在文章列表中主要是实现以下
2.2.5 Dao层(与数据库进行交互)
mapper
ArticleMapper
package com.pjp.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pjp.blog.dao.pojo.Article;
public interface ArticleMapper extends BaseMapper<Article> {
}
TagMapper
public interface TagMapper extends BaseMapper<Tag> {
/**
* 根据文章id查询标签列表
* @param articleId
* @return
*/
List<Tag> findTagByArticleId(long articleId);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pjp.blog.dao.mapper.TagMapper">
<!--List<Tag> findTagByArticleId(long articleId);-->
<select id="findTagByArticleId" parameterType="long" resultType="com.pjp.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>
SysUserMapper
public interface SysUserMapper extends BaseMapper<SysUser> {
}
2.2.6 测试
3. 首页-最热标签
3.1 接口说明
接口url:/tags/hot
请求方式:GET
请求参数:无
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 5,
"tagName": "Mybatis-Plus",
"avatar": null
}
]
}
3.2 编码
3.2.1 Controller
package com.pjp.blog.controller;
import com.pjp.blog.service.TagService;
import com.pjp.blog.vo.Result;
import com.pjp.blog.vo.TagVo;
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;
import java.util.List;
@RestController
@RequestMapping("tags")
public class TagsController {
@Autowired
private TagService tagService;
@GetMapping("hot")//此时接口url相当于拼接 /tags/hot
public Result listHotTags() {
int limit = 6;
List<TagVo> tagVoList = tagService.hot(limit);
return Result.success(tagVoList);
}
}
返回数据的封装类
@Data
public class TagVo {
private long id;
private String tagName;
private String avatar;
}
@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);
}
}
3.2.2 Service
public interface TagService {
/**
*最热标签
* @param limit
* @return
*/
List<TagVo> hot(int limit);
}
@Service
public class TagServiceImpl implements TagService {
@Autowired
private TagMapper tagMapper;
@Override
public List<TagVo> hot(int limit) {
/**
* 1.标签所拥有的文章数量最多 最热标签
* 2.查询 根据tag_id 分组 计数 ,从大到小 取前limit个
*/
List<Long> hotTagIds = tagMapper.findHotTagsId(limit);
//没有查询到返回空List
if (CollectionUtils.isEmpty(hotTagIds)) {
return Collections.emptyList();
}
//需求的是tagId 和 tagName Tag对象
List<Tag> tagList = tagMapper.findHotTagsById(hotTagIds);
return copyList(tagList);
}
public TagVo copy(Tag tag) {
TagVo tagVo = new TagVo();
BeanUtils.copyProperties(tag, tagVo);
return tagVo;
}
public List<TagVo> copyList(List<Tag> tagList) {
List<TagVo> tagVoList = new ArrayList<>();
for (Tag tag : tagList) {
tagVoList.add(copy(tag));
}
return tagVoList;
}
}
3.2.3 Dao
Mapper
public interface TagMapper extends BaseMapper<Tag> {
/**
* 查找最热标签id
* @param limit
* @return
*/
List<Long> findHotTagsId(int limit);
/**
* 查找最热标签
* @param hotTagIds
* @return
*/
List<Tag> findHotTagsById(List<Long> hotTagIds);
}
3.2.4 测试
4. 统一异常处理
不管是controller层还是service,dao层,都有可能报异常,如果是预料中的异常,可以直接捕获处理,如果是意料之外的异常,需要统一进行处理,进行记录,并给用户提示相对比较友好的信息。
package com.pjp.blog.handler;
import com.pjp.blog.vo.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @ControllerAdvice主要用来处理全局数据
* 对加了@Controller注解的方法进行拦截处理 原理就是AOP的实现
*/
@ControllerAdvice
public class AllExceptionHandler {
//用于捕获Controller中抛出的不同类型的异常,从而达到异常全局处理的目的
@ExceptionHandler(Exception.class)
@ResponseBody//返回json数据
public Result doException(Exception ex){
//ex.printStackTrace()方法:在命令行打印异常信息在程序中出错的位置及原因
ex.printStackTrace();
return Result.fail(-999,"系统异常");
}
}
实际开发不建议使用:
e.printStackTrace() 会导致锁死
- 因为e.printStackTrace() 语句要产生的字符串记录的是堆栈信息,太长太多
- 建议使用logger输出到日志文件里面。
后面改用logger输出到日志文件里面
测试
现在随便造一个异常 int i = 1/0;
//json数据进行交互
@RestController
@RequestMapping("/articles")
public class ArticleController {
@Autowired
private ArticleService articleService;
@PostMapping
// @RequestBody接收返回的请求信息 page 和 pageSize
public Result listArticle(@RequestBody PageParams pageParams){
int i = 1/0;
return articleService.listArticle(pageParams);
}
}
前端接收到数据
idea后台
5. 首页-最热文章
5.1 接口说明
接口url:/articles/hot
请求方式:POST
请求参数:
参数名称 |
参数类型 |
说明 |
id |
Long |
文章id |
title |
String |
文章标题 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 1,
"title": "Mybatis-Plus如何使用分页",
},
{
"id": 9,
"title": "Java中Lambda表达式使用",
},
{
"id": 10,
"title": "queryWrapper",
}
]
}
5.2 Controller
//json数据进行交互
@RestController
@RequestMapping("/articles")
public class ArticleController {
@Autowired
private ArticleService articleService;
/**
* 最热文章
* @return
*/
@PostMapping("hot")
public Result listHotArticles(){
int limit = 5;
return articleService.listHotArticles(limit);
}
}
5.3 Service
public interface ArticleService {
/**
* 最热文章
* @param limit
* @return
*/
Result listHotArticles(int limit);
}
@Service
public class ArticleServiceImpl implements ArticleService {
@Autowired
private ArticleMapper articleMapper;
@Override
public Result listHotArticles(int limit) {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
//按浏览量排序
queryWrapper.orderByDesc(Article::getViewCounts);
queryWrapper.select(Article::getId, Article::getTitle);
queryWrapper.last("limit " + limit);
//select id,title from article order by view_counts desc limit 5
List<Article> articles = articleMapper.selectList(queryWrapper);
return Result.success(articles);
}
}
5.4 测试
6. 首页-最新文章
和最热文章类似
6.1 接口说明
接口url:/articles/new
请求方式:POST
请求参数:
参数名称 |
参数类型 |
说明 |
id |
Long |
文章id |
title |
String |
文章标题 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 1,
"title": "Mybatis-Plus如何使用分页",
"summary": null,
"commentCounts": null,
"viewCounts": null,
"authorId": null,
"bodyId": null,
"categoryId": null,
"weight": null,
"createDate": null
},
.....
]
}
6.2 Controller
//json数据进行交互
@RestController
@RequestMapping("/articles")
public class ArticleController {
@Autowired
private ArticleService articleService;
/**
* 最新文章
*
* @return
*/
@PostMapping("new")
public Result newArticles() {
int limit = 5;
return articleService.newArticles(limit);
}
}
6.3 Service
/**
* 最新文章
* @param limit
* @return
*/
Result newArticles(int limit);
@Override
public Result newArticles(int limit) {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
//按发布时间排序
queryWrapper.orderByDesc(Article::getCreateDate);
queryWrapper.select(Article::getId, Article::getTitle);
queryWrapper.last("limit " + limit);
List<Article> articles = articleMapper.selectList(queryWrapper);
return Result.success(articles);
}
6.4 测试
7. 首页-文章归档
7.1接口说明
接口url:/articles/listArchives
请求方式:POST
请求参数:
参数名称 |
参数类型 |
说明 |
year |
Integer |
年 |
month |
Integer |
月 |
count |
Long |
相应年月的文章总数 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"year": 2017,
"month": 8,
"count": 1
},
{
"year": 2021,
"month": 1,
"count": 1
},
{
"year": 2023,
"month": 8,
"count": 1
}
]
}
7.1 Controller
/**
* 文章归档
* @return
*/
@PostMapping("listArchives")
public Result listArchives(){
return articleService. listArchives();
}
在dao目录下建包dos
这个包下的类封装的是 不是从数据库直接查询出来的数据
例如从ms_article表create_date获取的year,month,count(*) 是调用了YEAR()函数生成的
SELECT YEAR(create_date) as year,MONTH(create_date) as month,count(*) as count from ms_article GROUP BY year,month
@Data
public class Archives {
private Integer year;
private Integer month;
private Long count;
}
7.2 Service
/**
* 文章归档
* @return
*/
Result listArchives();
@Override
public Result listArchives() {
List<Archives> archivesList = articleMapper.listArchives();
return Result.success(archivesList);
}
7.3 Dao
public interface ArticleMapper extends BaseMapper<Article> {
/**
* 文章归档
* @return
*/
List<Archives> listArchives();
}
因为数据库存放文章的创建时间为时间戳 要做一个转换
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pjp.blog.dao.mapper.ArticleMapper">
<!-- List<Archives> listArchives();-->
<select id="listArchives" resultType="com.pjp.blog.dao.dos.Archives">
SELECT YEAR(FROM_UNIXTIME(create_date)) as year,MONTH(FROM_UNIXTIME(create_date)) as month,count(*) as count
from ms_article GROUP BY year,month
</select>
</mapper>
from_unixtime(时间戳 ,日期格式)
参数说明
timestamp :时间戳,可为一串数字,也可为字段。
date_format:时间格式,不填默认为%Y-%m-%d %H:%i:%s的格式。
7.4 测试

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)