springboot实战项目——个人博客系统

1.项目介绍

1.1项目效果

博客首页

登录功能 

 

注册功能

文章分类 

 

文章归档 

文章页面 

  

发布文章 (集成富文本编译器)

 

 1.2项目使用技术

前端:

  • vue
  • element-ui

 后端:

  • Springboot
  • mybatis-plus
  • redis
  • mysql
  • jwt

2.工程搭建

2.1新建springboot项目

2.2在maven中导入相关依赖

<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>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.qiniu</groupId>
            <artifactId>qiniu-java-sdk</artifactId>
            <version>[7.7.0, 7.7.99]</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</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>

        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

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

        <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>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>

        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.10</version>
        </dependency>

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

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

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

        <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

    </dependencies>

 2.3配置文件

mybatis-plus分页插件

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
@MapperScan("com.leggasai.dao.mapper")
public class MybatisPlusConfig {
    //分页插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

跨域配置文件

import com.leggasai.handler.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
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("/**")
                .allowedOrigins("http://localhost:8080")
                .allowedMethods("GET","POST","PUT","OPTIONS","DELETE")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

springboot配置文件 application.yml

server:
  port: 8888


spring:
  datasource:
    username: "mysql用户名"
    password: "mysql密码"
    url: jdbc:mysql://localhost:3306/blog?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
  redis:
    host: localhost
    port: 6379
  servlet:
    multipart:
      max-file-size: 2MB
      max-request-size: 20MB

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#mysql中表前缀为ms_
  global-config:
    db-config:
      table-prefix: ms_
  type-aliases-package: com.leggasai.dao.pojo
  mapper-locations: classpath*:mapper/*.xml

2.4特殊类

异常类:用于统一处理异常

package com.leggasai.handler;


import com.leggasai.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 {
    @ExceptionHandler(Exception.class)
    @ResponseBody//返回json数据
    public Result doException(Exception ex){
        ex.printStackTrace();
        return Result.fail(-999, "系统异常");
    }
}

返回结果类:用于给前端返回数据 

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
//返回结果类
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);
    }
}

异常码类:枚举类,用于显示不同的出错提示 

package com.leggasai.vo;

public enum  ErrorCode {

    PARAMS_ERROR(10001,"参数有误"),
    ACCOUNT_PWD_NOT_EXIST(10002,"用户名或密码不存在"),
    TOKEN_ERROR(10003,"token不合法"),
    ACCOUNT_EXISTED(10004,"账号已存在"),

    NO_PERMISSION(70001,"无访问权限"),
    SESSION_TIME_OUT(90001,"会话超时"),
    NO_LOGIN(90002,"未登录"),
    UPLOAD_ERROR(20001,"上传失败"),;
    private int code;
    private String msg;

    ErrorCode(int code, String msg){
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

3.数据库搭建 

在sqlyog(或navicat)中新建名为blog的数据库

3.1表一:ms_article文章表

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;

 3.2表二:ms_article_body文章内容表

drop table if exists `ms_article_body`;
create table `ms_article_body`  (
  `id` bigint(0) not null auto_increment,
  `content` longtext character set utf8 collate utf8_general_ci null,
  `content_html` longtext character set utf8 collate utf8_general_ci null,
  `article_id` bigint(0) not null,
  primary key (`id`) using btree,
  index `article_id`(`article_id`) using btree
) engine = innodb auto_increment = 1405916999854342147 character set = utf8 collate = utf8_general_ci row_format = dynamic;

 3.3表三:ms_article_tag关联表

 这个表是用于关联article表和tag表(文章标签表)

drop table if exists `ms_article_tag`;
create table `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 = 1405916999787233282 character set = utf8 collate = utf8_general_ci row_format = dynamic;

3.4表四:ms_sys_user用户表

drop table if exists `ms_sys_user`;
create table `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 = 1404448588146192387 character set = utf8 collate = utf8_general_ci row_format = dynamic;

3.5表五: ms_tag文章标签表

drop table if exists `ms_tag`;
create table `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;

3.6表六:ms_comment评论表 

drop table if exists `ms_comment`;
create table `ms_comment`  (
  `id` bigint(0) not null auto_increment,
  `content` varchar(255) character set utf8mb4 collate utf8mb4_unicode_ci not null,
  `create_date` bigint(0) not null,
  `article_id` int(0) not null,
  `author_id` bigint(0) not null,
  `parent_id` bigint(0) not null,
  `to_uid` bigint(0) not null,
  `level` varchar(1) character set utf8 collate utf8_general_ci not null,
  primary key (`id`) using btree,
  index `article_id`(`article_id`) using btree
) engine = innodb auto_increment = 1405209691876790275 character set = utf8 collate = utf8_general_ci row_format = dynamic;

3.7表七: ms_category类别表

drop table if exists `ms_category`;
create table `ms_category`  (
  `id` bigint(0) not null auto_increment,
  `avatar` varchar(255) character set utf8mb4 collate utf8mb4_unicode_ci null default null,
  `category_name` varchar(255) character set utf8mb4 collate utf8mb4_unicode_ci null default null,
  `description` varchar(255) character set utf8mb4 collate utf8mb4_unicode_ci null default null,
  primary key (`id`) using btree
) engine = innodb auto_increment = 6 character set = utf8 collate = utf8_general_ci row_format = dynamic;

4.首页功能

4.1文章列表实现

效果图

 

4.1.1pojo类 

 1.建立pojo类——article

package com.leggasai.dao.pojo;


import lombok.Data;

@Data
public class Article {
    public static final int Article_TOP=1;//是否指定,默认是
    public static final int Article_Common=0;//默认评论数0
    private Long id;//文章id
    private String title;//文章标题
    private String summary;//文章简介
    private Integer commentCounts;//文章评论数
    private Integer viewCounts;//文章阅读数
    private Long authorId;//作者id
    private Long bodyId;//文章内容id
    private Long categoryId;//文章类别id
    private Integer weight=Article_Common;//文章权重
    private Long createDate;//文章创建时间
}

 2.建立pojo类——sysUser用户类

package com.leggasai.dao.pojo;


import lombok.Data;

@Data
public class SysUser {
    private Long id;//用户id
    private String account;//用户账号
    private String password;//用户密码
    private Integer admin;//是否管理员
    private String avatar;//用户头像地址
    private Long createDate;//用户创建时间
    private Integer deleted;//是否被删除
    private String email;//用户邮箱
    private Long lastLogin;//用户最后一次登录时间
    private String nickname;//用户昵称
    private String mobilePhoneNumber;//用户手机号
    private String salt;//加密盐
    private String status;//用户状态

}

3.建立pojo类——tag标签类 

package com.leggasai.dao.pojo;
import lombok.Data;
@Data
public class Tag {
    private Long id;//标签id
    private String avatar;//标签图片地址
    private String tagName;//标签名
}

4.建立vo类——articleVo:前端显示类往往和数据库中的类有差别 


import java.util.List;

@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;

}

4.1.2controller类

ArticleController类:用于处理和article相关业务

import java.util.List;

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

    @Autowired
    private ArticleService articleService;
	//Result是统一结果返回
    @PostMapping
    public Result articles(@RequestBody PageParams pageParams) {
        //ArticleVo 页面接收的数据
        List<ArticleVo> articles = articleService.listArticlesPage(pageParams);

        return Result.success(articles);
    }


}

 4.1.3service类

ArticleService接口与ArticleServiceImpl实现类

import java.util.List;

public interface ArticleService {

    List<ArticleVo> listArticlesPage(PageParams pageParams);

}
package com.leggasai.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.leggasai.common.aop.LogAnnotation;
import com.leggasai.dao.dos.Archives;
import com.leggasai.dao.mapper.ArticleBodyMapper;
import com.leggasai.dao.mapper.ArticleMapper;
import com.leggasai.dao.mapper.ArticleTagMapper;
import com.leggasai.dao.pojo.Article;
import com.leggasai.dao.pojo.ArticleBody;
import com.leggasai.dao.pojo.ArticleTag;
import com.leggasai.dao.pojo.SysUser;
import com.leggasai.service.*;
import com.leggasai.utils.UserThreadLocal;
import com.leggasai.vo.ArticleBodyVo;
import com.leggasai.vo.ArticleVo;
import com.leggasai.vo.Result;
import com.leggasai.vo.TagVo;
import com.leggasai.vo.params.ArticleParam;
import com.leggasai.vo.params.PageParams;
import net.sf.jsqlparser.expression.DateTimeLiteralExpression;
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.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ArticleServiceImpl implements ArticleService {
    @Autowired
    private ArticleMapper articleMapper;
   @Autowired
   private SysUserService sysUserService;
    @Autowired
    private TagsService tagsService;

    public ArticleVo copy(Article article,boolean isAuthor,boolean isBody,boolean isTags){
        ArticleVo articleVo = new ArticleVo();
        BeanUtils.copyProperties(article, articleVo);
        if (isAuthor) {
            SysUser sysUser = sysUserService.findSysUserById(article.getAuthorId());
            articleVo.setAuthor(sysUser.getNickname());
        }
        articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-MM-dd HH:mm"));
        if (isTags){
           List<TagVo> tags = tagsService.findTagsByArticleId(article.getId());
           articleVo.setTags(tags);
        }
        return articleVo;
    }

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


    @Override
    public List<ArticleVo> listArticlesPage(PageParams pageParams) {
        QueryWrapper<Article> queryWrapper = new QueryWrapper<>();
        Page<Article> page = new Page<>(pageParams.getPage(),pageParams.getPageSize());
        Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);
        List<ArticleVo> articleVoList = copyList(articlePage.getRecords(),true,false,true);
        return articleVoList;
    }


}

sysUserService和sysUserServiceImpl 

public interface UserService {

    SysUser findUserById(Long userId);
}
package com.leggasai.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.leggasai.dao.mapper.SysUserMapper;
import com.leggasai.dao.pojo.SysUser;
import com.leggasai.service.LoginService;
import com.leggasai.service.SysUserService;
import com.leggasai.vo.ErrorCode;
import com.leggasai.vo.LoginUserVo;
import com.leggasai.vo.Result;
import com.leggasai.vo.UserVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    public SysUser findUserById(Long userId) {
        SysUser sysUser = sysUserMapper.selectById(userId);
        if (sysUser == null) {
            sysUser = new SysUser();
            sysUser.setNickname("码神之路");
        }
        return sysUser;
    }
}

tagService和tagServiceImpl 

package com.leggasai.service;

import com.leggasai.vo.Result;
import com.leggasai.vo.TagVo;

import java.util.List;

public interface TagService {
    List<TagVo> findTagsByArticleId(Long articleId);
}
package com.leggasai.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.leggasai.dao.mapper.TagMapper;
import com.leggasai.dao.pojo.Tag;
import com.leggasai.service.TagService;
import com.leggasai.vo.Result;
import com.leggasai.vo.TagVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


@Service
public class TagServiceImpl implements TagService {
    @Autowired
    private TagMapper tagMapper;
    @Override
    public List<TagVo> findTagsByArticleId(Long articleId) {
        //mybatis-plus无法进行多表查询
        List<Tag> tags=tagMapper.findTagsByArticleId(articleId);
        return copyList(tags);
    }


    public TagVo copy(Tag tag){
        TagVo tagVo=new TagVo();
        BeanUtils.copyProperties(tag,tagVo);
        tagVo.setId(String.valueOf(tag.getId()));
        return tagVo;
    }
    public List<TagVo> copyList(List<Tag> tagList){
        List<TagVo> tagVoList = new ArrayList<>();
        for (Tag tag : tagList) {
            tagVoList.add(copy(tag));
        }
        return tagVoList;
    }



}

4.1.4dao层

ArticleMapper接口

package com.leggasai.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.leggasai.dao.dos.Archives;
import com.leggasai.dao.pojo.Article;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;
@Mapper
public interface ArticleMapper extends BaseMapper<Article> {
    List<Archives> listArchives();

    IPage<Article> listArticle(Page<Article> page,
                               Long categoryId,
                               Long tagId,
                               String year,
                               String month);
}

SysUserMapper接口 

package com.leggasai.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.leggasai.dao.pojo.SysUser;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
}

TagMapper接口 

package com.leggasai.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.leggasai.dao.pojo.Tag;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;


import java.util.List;
@Mapper
public interface TagMapper extends BaseMapper<Tag> {
    /*
    根据文章id查询标签列表
     */
    List<Tag> findTagsByArticleId(Long articleId);

   
}

 TagMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!--中文错误改成UTF8即可-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.leggasai.dao.mapper.TagMapper">
<!--    List<Tag> findTagsByArticleId(Long articleId);-->
    <select id="findTagsByArticleId" parameterType="long" resultType="com.leggasai.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>

4.2首页最热标签功能

 4.2.1前端接口

接口url:/tags/hot

请求方式:GET

请求参数:无

返回数据:

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

4.2.2controller类

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

4.2.3service层

    Result hots(int limit);
    @Override
    public Result hots(int limit) {
        /*
        标签所拥有的文章数量做多,就是最热标签
         */
        List<Long> tagIds=tagMapper.findHotsTagIds(limit);
        if(CollectionUtils.isEmpty(tagIds)){
            return Result.success(Collections.emptyList());
        }
        //需要的是tagId和tagName
        List<Tag> tagList=tagMapper.findTagesByTagIds(tagIds);
        //System.out.println("最热标签");
        //System.out.println(tagList);
        return Result.success(tagList);
    }

4.2.4mapper层

    <select id="findHotsTagIds" 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="findTagesByTagIds" parameterType="list" resultType="com.leggasai.dao.pojo.Tag">
        select id,tag_name as tagName from ms_tag
        where id in
        <foreach collection="tagIds" item="tagId" separator="," open="(" close=")">
            #{tagId}
        </foreach>
    </select>

4.3首页最热文章功能

 

后续可以用cache优化

4.3.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相关",
            
        }
    ]
}

4.3.2controller层

    @PostMapping("/hot")
    public Result hotArticle(){
        int limit =5;
        return articleService.hotArticle(limit);
    }

4.3.3service层

    @Override
    public Result hotArticle(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(copyList(articles,false,false));
    }

4.4首页最新文章功能

 

后续可以用cache优化 

4.4.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相关",
            
        }
    ]
}

4.4.2controller层

    @PostMapping("/new")
    public Result newArticles(){
        int limit =5;
        return articleService.newArticles(limit);
    }

4.4.3service层

    @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);
        //select id,title from article order by create_date desc limit 5
        List<Article> articles = articleMapper.selectList(queryWrapper);
        return Result.success(copyList(articles,false,false));
    }

4.5首页文章归档功能

 

4.5.1前端接口

接口url:/articles/listArchives

请求方式:POST

请求参数:

返回数据:

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

}

4.5.2controller层

    @PostMapping("listArchives")
    public Result listArchives(){
        return articleService.listArchives();
    }

4.5.3service层

    @Override
    public Result listArchives() {
        List<Archives> archivesList=articleMapper.listArchives();
        return Result.success(archivesList);
    }

4.5.4mapper层 

    List<Archives> listArchives();

注意数据库中create_date是时间戳,需要使用FROM_UNIXTIME转化为xxxx年xx月的格式!

    <select id="listArchives" resultType="com.leggasai.dao.dos.Archives">
        select FROM_UNIXTIME(create_date/1000,'%Y') as year,FROM_UNIXTIME(create_date/1000,'%m') as month,COUNT(*) as COUNT
        from ms_article group by year,month
    </select>

 Archives类

package com.leggasai.dao.dos;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Archives {

    private Integer year;
    private Integer month;
    private Long count;

}

5.登录功能

5.1jwt技术

5.2退出功能

5.3注册功能

5.4登录拦截器

5.5ThreadLocal使用

未完待续~

  • 13
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
核心功能 文章/图片/视频发布、喜欢、统计阅读次数。 文章标签tag功能、支持按tag分类 文章支持ueditor/markdown编辑器切换(后台配置) 评论功能,支持回复,支持表情。 第三方(微博、QQ)登录。 lucene实现的站内搜索。 响应式布局 支持用户订阅 先看效果图 SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) http://localhost:8080/admin/group/list SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能)SpringBoot开发非常美观的java博客系统(包含后台管理功能) 技术选型: JDK8 数据库MySQL 主框架 (Spring-bootSpring-data-jpa) 安全权限 Shiro 搜索工具 Lucene 缓存 Ehcache 视图模板 Freemarker 其它 Jsoup、fastjson jQuery、Seajs Bootstrap 前端框架 UEditor/Markdown编辑器 font-Awesome 字体/图标 准备工作(sql文件在项目里面) 安装 Jdk8 安装 Maven 准备 IDE (如果你不看源码,可以忽略下面的步骤,直接通过Maven编译war包:mvn clean package -DskipTests) IDE 需要配置的东西 编码方式设为UTF-8 配置Maven 设置Jdk8 关于这些配置,网上有一大把的资料,所以此处不再重复。 获取代码导入到IDE 下载代码 导入到IDE的时候请选择以Maven的方式导入 项目配置参考 系统配置手册 配置完毕 启动项目,在控制台看到Mblog加载完毕的信息后,表示启动成功 打开浏览器输入:http//localhost/mblog/ (此处仅是示例,具体具体端口因人而异),访问成功即部署完毕 后台管理的地址是 /admin, 如果你是管理员账号点导航栏的头像会看到"后台管理" 启动成功后,你应该去后台的系统配置里配置你的网站信息等。 常见问题总结 进入系统后, 菜单加载不出来, 那应该是你没有导 db_init.sql 点标签显示乱码, 请设置Tomcat的 URIEncoding 为 UTF-8 项目截图 SpringBoot开发非常美观的java博客系统(包含后台管理功能) 转自:https://gitee.com/mtons/mblog SpringBoot开发非常美观的java博客系统(包含后台管理功能) 注意: 一、java main方式运行mblog-web下的BootApplication.java时抛出异常的解决方案 Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean. SpringBoot开发非常美观的java博客系统(包含后台管理功能) 注释掉后下面图片的这段后,记得maven要重新reimport SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) 否则maven依赖不生效还是会抛出以上的异常 二、第三方登录点击后无响应,那是因为第三方开放平台回调的url失效导致,需要你去对应的第三方开放平台注册app后获取对应的oauth帐号 SpringBoot开发非常美观的java博客系统(包含后台管理功能) 三、idea以maven项目导入该项目后,发现没有maven的依赖包时,需要对每个maven module进行clear和install,并且注意maven的依赖顺序 SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) 四、访问地址是http://localhost:8080 登录时,帐号,密码只要自己找个密码,然后md5下在更新到db中即可登录成功。 比如:zuidaima 111111,md5后密码是 3931MUEQD1939MQMLM4AISPVNE,md5的javaSpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能)
第1章 Spring Boot 简介 讲解Spring Boot的项目背景,已经与其他技术框架(比如,SpringSpringMVC、SpringCloud等)的关系。简单介绍下Spring Boot 整个生态系统 第2章 开启 Spring Boot 的第一个 Web 项目 通过 Spring Initializr 来快速初始化一个 Spring Boot 原型,方便学员来极速体验Spring Boot。本课程也将会采用Gradle作为项目管理工具,让学员掌握最前瞻的构建工具。通过探索项目让学员了解项目的结构,已经相关的配置原理。 第3章 一个Hello World项目 本章是正式开始动手敲代码了。依照惯例,会先编写一个最简单的Hello World程序。从项目配置,应用的编写,再到测试用例,最后运行项目。方面学员了解整个编码的流程。 第4章 开发环境的搭建 为了让实战过程更顺利,避免不要的问题,这里会先将课程所要求的环境进行一个讲解,并要求学员最好跟随课程的环境配置。本节也会讲解如何将项目导入IDE 来运行。 第5章 集成Thymeleaf模版引擎 Thymeleaf 方面的内容,知识点会讲解的相对全面点。Thymeleaf作为界面的模版引擎,对于界面的布局和实现起着非常关键的作用。本章节也会讲解Thymeleaf 如何与 Spring Boot 来进行集成。最后通过一个实战,来让学员更加深刻的理解Thymeleaf。… 第6章 数据持久化Spring Data JPA 本章节涉及数据的持久化。从JPA规范讲起,到Spring对于JPA的用法以及与Hibernate集成实现。本课程的数据库采用MySQL,但也可以方便切换到其他数据库。最后通过一个实战内容,来帮助学员理解掌握。 第7章 全文搜索ElasticSearch 企业级应用中,难免会涉及到全文搜素。对于Java应用来说,ElasticSearch在全文搜索方面是一把“利器”。本章节会将带领学员了解全文搜索的概念,并熟悉如何用ElasticSearch来实现全文搜索。 第8章 架构设计与分层 本章节讲解了系统的整体架构设计思路,包括如何来组织项目结构。让学员理解系统的数据流程。 第9章 集成 Bootstrap Bootsrap最大的好处是,可以让整个系统界面实现响应式布局。本节先从Bootstrap 的基本原理讲起,并将常用的前端框架比如 JQuery等进行集成。最后通过一个实战内容,来帮助学员理解掌握。 第10章 博客系统的需求分析与原型设计 本章节是对博客系统的需求分析与设计。对于企业级应用的完整流程来说,需求的分析与设计是必不可少的环节。本章节设计部分包含了原型设计、数据库设计及接口设计。 第11章 权限管理Spring Security Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,在企业级应用中被广泛使用。本章节不会对该框架做深入探讨,仅从基于角色的权限管理角度,来实现对系统的权限管理。 第12章 博客系统的整体框架实现 先对系统的整个界面、结构、布局、API进行实现,这样方便每个模块进行划分及实现。 第13章 博客系统的用户管理实现 对用户管理模块进行前后台的实现。 第14章 博客系统的角色管理实现 对用户角色理模块进行前后台的实现。 第15章 博客系统的权限管理实现 对用权限理模块进行前后台的实现。 第16章 博客系统的博客管理实现 对博客管理模块进行前后台的实现。 第17章 博客系统的评论管理实现 对评论管理模块进行前后台的实现。 第18章 博客系统的点赞管理实现 对用户点赞理模块进行前后台的实现。 第19章 博客系统的分类管理实现 对分类管理模块进行前后台的实现。 第20章 博客系统的标签管理实现 对标签管理模块进行前后台的实现。 第21章 博客系统的搜索实现 对搜索模块进行前后台的实现。
本篇文章是 Spring Boot 实践十三 9 Spring Boot综合项目实战——个人博客系统管理模块 的续篇,本次将介绍如何实现个人博客系统的拓展模块。 在实际开发中,我们常常需要对系统进行扩展,添加一些新的功能模块。为了不影响原有代码的结构和功能,我们可以将这些新功能模块独立成为一个子模块,然后通过配置文件等方式将其与原有系统进行整合。 本文将以一个个人博客系统为例,介绍如何实现博客的拓展模块,具体包括以下几个方面: 1. 拓展模块的设计和实现 2. 拓展模块的集成和配置 3. 拓展模块的使用示例 ## 1. 拓展模块的设计和实现 在本例中,我们将实现一个博客系统的拓展模块,该模块主要提供以下两个功能: 1. 统计博客文章的阅读量并显示 2. 在博客页面添加底部的版权声明 ### 1.1 统计博客文章的阅读量并显示 首先,我们需要在数据库中添加一个字段来存储博客文章的阅读量。在本例中,我们在 `blog` 表中添加 `read_count` 字段来存储阅读量。 ```sql ALTER TABLE `blog` ADD COLUMN `read_count` INT NOT NULL DEFAULT 0 COMMENT '阅读量' AFTER `update_time`; ``` 接下来,在博客文章页面中添加一个阅读量的显示。我们可以在博客文章详情页面的右侧添加一个阅读量的区域,显示该文章的阅读量。具体的实现方式为: 1. 在博客文章详情页面中添加一个阅读量的区域。 2. 在加载博客文章详情页面时,通过 AJAX 请求统计该文章的阅读量,并更新阅读量区域的显示。 具体的代码实现如下: 在博客文章详情页面中添加一个阅读量的区域: ```html <div class="blog-sidebar-item"> <div class="blog-sidebar-title">阅读量</div> <div class="blog-sidebar-content"> <span id="read-count">0</span> </div> </div> ``` 在加载博客文章详情页面时,通过 AJAX 请求统计该文章的阅读量,并更新阅读量区域的显示。具体的实现方式为: ```javascript $(function () { // 统计阅读量 var blogId = $("#blogId").val(); $.ajax({ url: "/blog/read/" + blogId, type: "POST", success: function (result) { if (result && result.success) { $("#read-count").text(result.data); } else { $("#read-count").text(0); } } }); }); ``` 在服务器端,我们需要实现一个接口来统计博客文章的阅读量,并将其保存到数据库中。具体的实现方式为: ```java @RestController @RequestMapping("/blog") public class BlogController { ... /** * 统计博客文章的阅读量 * * @param blogId 博客文章ID * @return 统计结果 */ @PostMapping("/read/{blogId}") public Result<Integer> readBlog(@PathVariable("blogId") Long blogId) { int readCount = blogService.readBlog(blogId); return Result.success(readCount); } ... } ``` 在 `BlogService` 中实现 `readBlog` 方法: ```java @Service public class BlogServiceImpl implements BlogService { ... /** * 统计博客文章的阅读量 * * @param blogId 博客文章ID * @return 统计结果 */ @Override public int readBlog(Long blogId) { Blog blog = blogMapper.selectByPrimaryKey(blogId); if (blog != null) { int readCount = blog.getReadCount() + 1; blog.setReadCount(readCount); blogMapper.updateByPrimaryKeySelective(blog); return readCount; } return 0; } ... } ``` ### 1.2 在博客页面添加底部的版权声明 接下来,我们将在博客页面底部添加一个版权声明。具体的实现方式为: 1. 在博客页面底部添加一个版权声明的区域。 2. 在加载博客页面时,通过 AJAX 请求获取版权声明的内容,并更新版权声明区域的显示。 具体的代码实现如下: 在博客页面底部添加一个版权声明的区域: ```html <div class="blog-footer"> <div><span id="copyright"> 版权声明:本博客所有文章均为作者原创或转载,未经授权禁止转载。 </span></div> </div> ``` 在加载博客页面时,通过 AJAX 请求获取版权声明的内容,并更新版权声明区域的显示。具体的实现方式为: ```javascript $(function () { // 加载版权声明 $.ajax({ url: "/blog/copyright", type: "GET", success: function (result) { if (result && result.success) { $("#copyright") .html("版权声明:" + result.data); } } }); }); ``` 在服务器端,我们需要实现一个接口来获取版权声明的内容。具体的实现方式为: ```java @RestController @RequestMapping("/blog") public class BlogController { ... /** * 获取版权声明的内容 * * @return 版权声明的内容 */ @GetMapping("/copyright") public Result<String> getCopyright() { String content = "本博客所有文章均为作者原创或转载,未经授权禁止转载。"; return Result.success(content); } ... } ``` ## 2. 拓展模块的集成和配置 在上一篇文章中,我们已经将博客系统的所有模块都整合到了一个工程中,因此我们可以通过添加一个 Maven 模块来实现拓展模块的开发,并将其整合到原有工程中。 具体的步骤如下: 1. 在项目根目录下创建一个新的 Maven 模块,命名为 `blog-ext`,并将其添加到工程中。 2. 在 `blog-ext` 模块中添加 `pom.xml` 文件,并添加依赖关系。 3. 在 `blog-ext` 模块中添加 Spring Boot 的配置文件 `application.yml`,并添加相关配置。 4. 在 `blog-ext` 模块中添加拓展模块的代码和资源文件。 ### 2.1 添加 Maven 模块 在项目根目录下创建一个新的 Maven 模块,命名为 `blog-ext`,并将其添加到工程中。具体的步骤如下: 1. 在项目根目录下创建一个新的 Maven 模块,命名为 `blog-ext`。 ```bash $ cd ~/workspace/springboot-blog $ mvn archetype:generate -DgroupId=com.waylau.spring.boot.blog \ -DartifactId=blog-ext -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false ``` 2. 将 `blog-ext` 模块添加到工程中。 ```xml <modules> <module>blog-api</module> <module>blog-service</module> <module>blog-web</module> <module>blog-ext</module> </modules> ``` ### 2.2 添加依赖关系 在 `blog-ext` 模块中添加 `pom.xml` 文件,并添加依赖关系。具体的依赖关系如下: ```xml <dependencies> <dependency> <groupId>com.waylau.spring.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- 添加 Spring Web MVC 的依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 添加 MyBatis 的依赖 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.boot.version}</version> </dependency> </dependencies> ``` ### 2.3 添加配置文件 在 `blog-ext` 模块中添加 Spring Boot 的配置文件 `application.yml`,并添加相关配置。具体的配置如下: ```yaml spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/blog?useSSL=false&useUnicode=true&characterEncoding=utf8 username: root password: root mvc: view: prefix: /templates/ suffix: .html resources: static-locations: classpath:/static/ ``` ### 2.4 添加拓展模块的代码和资源文件 在 `blog-ext` 模块中添加拓展模块的代码和资源文件。具体的步骤如下: 1. 在 `blog-ext` 模块中添加 `com.waylau.spring.boot.blog.ext` 包,并在该包下添加 `BlogExtApplication` 类。 ```java @SpringBootApplication(scanBasePackages = "com.waylau.spring.boot.blog.ext") public class BlogExtApplication { public static void main(String[] args) { SpringApplication.run(BlogExtApplication.class, args); } } ``` 2. 在 `blog-ext` 模块中添加 `resources` 目录,并在该目录下添加 `templates` 和 `static` 目录。 3. 在 `templates` 目录中添加 `read-count.html` 和 `copyright.html`。 ```html <!-- read-count.html --> <div class="blog-sidebar-item"> <div class="blog-sidebar-title">阅读量</div> <div class="blog-sidebar-content"> <span id="read-count">0</span> </div> </div> ``` ```html <!-- copyright.html --> <div class="blog-footer"> <div><span id="copyright"> 版权声明:本博客所有文章均为作者原创或转载,未经授权禁止转载。 </span></div> </div> ``` 4. 在 `static` 目录中添加 `js` 目录,并在该目录下添加 `read-count.js` 和 `copyright.js`。 ```javascript // read-count.js $(function () { // 统计阅读量 var blogId = $("#blogId").val(); $.ajax({ url: "/blog/read/" + blogId, type: "POST", success: function (result) { if (result && result.success) { $("#read-count").text(result.data); } else { $("#read-count").text(0); } } }); }); ``` ```javascript // copyright.js $(function () { // 加载版权声明 $.ajax({ url: "/blog/copyright", type: "GET", success: function (result) { if (result && result.success) { $("#copyright") .html("版权声明:" + result.data); } } }); }); ``` ## 3. 拓展模块的使用示例 在完成了拓展模块的开发和配置之后,我们需要将其与原有系统进行整合。具体的步骤如下: 1. 在原有系统中添加对拓展模块的依赖关系。 在 `blog-web` 模块的 `pom.xml` 文件中添加对 `blog-ext` 模块的依赖关系: ```xml <dependencies> ... <!-- 添加 blog-ext 的依赖 --> <dependency> <groupId>com.waylau.spring.boot.blog</groupId> <artifactId>blog-ext</artifactId> <version>${project.version}</version> </dependency> </dependencies> ``` 2. 在原有系统中添加拓展模块的使用示例。 在博客文章详情页面中添加一个阅读量的区域: ```html <!-- 添加阅读量的区域 --> <div th:replace="blog-ext :: read-count"></div> ``` 在博客页面底部添加一个版权声明的区域: ```html <!-- 添加版权声明的区域 --> <div th:replace="blog-ext :: copyright"></div> ``` 经过以上的步骤,我们就已经成功地将博客系统的拓展模块整合到了原有系统中。 ## 总结 本文介绍了如何实现 Spring Boot 的拓展模块,并将其与原有系统进行整合。在实际开发中,我们可以根据具体的需求来实现不同的拓展模块,并通过配置文件等方式将其整合到原有系统中。这种方式既提高了代码的可维护性,又方便了模块的扩展和升级。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leg丶Gasai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值