前后端分離博客(blogs)項目(SpringBoot/Redis/Shiro/Swagger/MyBatisPlus/Vue/ElementUI)


blogs項目

  • blogs項目主要由blogs_springboot后端項目與blogs_vue前端項目組成
  • SpringBoot
  • Redis
  • MyBatis-Plus
  • Shiro
  • Swagger
  • Lombok
  • Jwt
  • Vue
  • ElementUI
  • Axios
  • Mavon-Editor
  • MarkDown-It
  • GitHub-MarkDown-CSS

MySQL數據庫

DROP TABLE IF EXISTS `blog`;

CREATE TABLE `blog` (
  `user_id` BIGINT NOT NULL,
  `blog_id` BIGINT NOT NULL,
  `blog_author` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `blog_name` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `blog_content` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
  `blog_num` INT DEFAULT NULL,
  `blog_category` VARCHAR(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `gmt_create` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`blog_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

DROP TABLE IF EXISTS `comment`;

CREATE TABLE `comment` (
  `comment_id` BIGINT NOT NULL,
  `user_id` BIGINT NOT NULL,
  `blog_id` BIGINT NOT NULL,
  `comment_content` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
  `gmt_create` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`comment_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `user_id` BIGINT NOT NULL,
  `user_name` VARCHAR(30) DEFAULT NULL,
  `user_password` VARCHAR(40) DEFAULT NULL,
  `user_email` VARCHAR(50) DEFAULT NULL,
  `user_phone` VARCHAR(11) DEFAULT NULL,
  `user_power` VARCHAR(10) DEFAULT NULL COMMENT '管理員/用戶',
  `user_address` VARCHAR(200) DEFAULT NULL,
  `user_img` VARCHAR(100) DEFAULT NULL,
  `gmt_create` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`user_id`),
  UNIQUE KEY `user_name` (`user_name`),
  UNIQUE KEY `user_email` (`user_email`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

blogs_springboot后端項目

  • SpringBoot
  • Redis
  • MyBatis-Plus
  • Shiro
  • Swagger
  • Lombok
  • Jwt

導入依賴(pom.xml)

<dependencies>
    <!-- springboot -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-servlet-api</artifactId>
        <version>8.0.36</version>
    </dependency>

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

    <!-- mysql驅動  -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <!-- mybatis-plus 代碼自動生成-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.1</version>
    </dependency>

    <!-- swagger2 前后端分離 -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>
    <dependency>
        <groupId>io.swagger</groupId>
        <artifactId>swagger-annotations</artifactId>
        <version>1.5.21</version>
    </dependency>
    <dependency>
        <groupId>io.swagger</groupId>
        <artifactId>swagger-models</artifactId>
        <version>1.5.21</version>
    </dependency>
    <!-- 引入swagger-bootstrap-ui包 -->
    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>swagger-bootstrap-ui</artifactId>
        <version>1.9.6</version>
    </dependency>

    <!-- 上傳下載需要設計到的jar包 -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.6</version>
    </dependency>
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.3</version>
    </dependency>
    <!--servlet-api導入高版本的-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
    </dependency>
    <!-- 圖片處理類 -->
    <dependency>
        <groupId>net.coobird</groupId>
        <artifactId>thumbnailator</artifactId>
        <version>0.4.8</version>
    </dependency>

    <!-- 郵箱登錄、發送 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
        <version>2.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>com.sun.mail</groupId>
        <artifactId>jakarta.mail</artifactId>
        <version>1.6.4</version>
    </dependency>

    <!--整合Shiro安全框架-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.5.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.4.0</version>
    </dependency>
    <dependency>
        <groupId>org.crazycake</groupId>
        <artifactId>shiro-redis-spring-boot-starter</artifactId>
        <version>3.2.1</version>
    </dependency>
    <!-- hutool工具類-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.3.3</version>
    </dependency>
    <!-- jwt -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>

    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-core</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>javax.activation</groupId>
        <artifactId>activation</artifactId>
        <version>1.1.1</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>2.2.6.RELEASE</version>
            <configuration>
                <mainClass>com.blogs_springboot.BlogsApplication</mainClass>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <version>3.1.0</version>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22.2</version>
            <configuration>
                <skipTests>true</skipTests>
            </configuration>
        </plugin>
    </plugins>
</build>

配置文件(properties)

  • application.properties
# Windows環境
spring.profiles.active=dev
# Linux環境
# spring.profiles.active=prod
  • application-dev.properties
# 連接數據庫
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/blogs?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 配置Redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
# 郵件發送設置
spring.mail.host=smtp.qq.com
spring.mail.username=2991596029@qq.com
spring.mail.password=fwlohstwgimddefi
spring.mail.default-encoding=UTF-8
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.ssl.enable=true
spring.mail.properties.mail.smtp.socketFactory.port=465
#上傳文件大小
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
# bean允許重命名
spring.main.allow-bean-definition-overriding=true
# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
#默認編碼配置
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.force=true
server.servlet.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8
# 接口設置
server.port=8085
  • application-prod.properties
# 連接數據庫
spring.datasource.username=root
spring.datasource.password=****
spring.datasource.url=jdbc:mysql://localhost:3306/blogs?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 配置Redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
# 郵件發送設置
spring.mail.host=smtp.qq.com
spring.mail.username=2991596029@qq.com
spring.mail.password=fwlohstwgimddefi
spring.mail.default-encoding=UTF-8
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.ssl.enable=true
spring.mail.properties.mail.smtp.socketFactory.port=465
#上傳文件大小
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
# bean允許重命名
spring.main.allow-bean-definition-overriding=true
# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
#默認編碼配置
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.force=true
server.servlet.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8
# 接口設置
server.port=8085

WebMvcConfig配置類

package com.blogs_springboot.config;

import org.springframework.boot.system.ApplicationHome;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;

import java.io.File;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    // 網站首頁影射
    @Override
    public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {
        // url輸入 / /index /index.html 返回到index網站首頁
        viewControllerRegistry.addViewController("/").setViewName("index");
        viewControllerRegistry.addViewController("/index").setViewName("index");
        viewControllerRegistry.addViewController("/index.html").setViewName("index");
    }

    // 解決跨域問題
    @Override
    public void addCorsMappings(CorsRegistry corsRegistry) {
        corsRegistry.addMapping("/**")
                .allowedOrigins("allowedOriginPatterns")
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                .maxAge(3600)
                .allowCredentials(true)
                .exposedHeaders("Authorization")
                .allowedHeaders("*");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        ApplicationHome h = new ApplicationHome(getClass());
        File jarF = h.getSource();
        String dirPath = jarF.getParentFile().toString() + "/upload/";

        String os = System.getProperty("os.name");

        if (os.toLowerCase().startsWith("win")) {  //如果是Windows系統
            registry.addResourceHandler("/upload/**").addResourceLocations("file:" + dirPath);
        } else {
            registry.addResourceHandler("/upload/**").addResourceLocations("file:" + dirPath);
        }
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

}

BlogsApplication啟動類

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@EnableAsync //開啟異步注解功能
@EnableSwagger2 // 開啟Swagger2的自動配置
@SpringBootApplication // springboot啟動類
@MapperScan("com.blogs_springboot.mapper") // 掃描mapper所在包的路徑
public class BlogsApplication {

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

}

實體類

User類

import com.baomidou.mybatisplus.annotation.IdType;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 *
 * </p>
 *
 * @author DMFlySky
 * @since 2021-03-17
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "User對象", description = "")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "用戶id")
    @TableId(value = "user_id", type = IdType.ID_WORKER)
    @JsonSerialize(using = ToStringSerializer.class)
    private Long userId;

    @ApiModelProperty(value = "用戶昵稱")
    @TableField(value = "user_name")
    private String userName;

    @ApiModelProperty(value = "用戶密碼")
    private String userPassword;

    @ApiModelProperty(value = "用戶郵箱")
    @TableField(value = "user_email")
    private String userEmail;

    @ApiModelProperty(value = "用戶手機號碼")
    private String userPhone;

    @ApiModelProperty(value = "管理員/用戶")
    private String userPower;

    @ApiModelProperty(value = "用戶地址")
    private String userAddress;

    @ApiModelProperty(value = "用戶頭像")
    private String userImg;

    @ApiModelProperty(value = "創建時間")
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;
}

Blog類

import com.baomidou.mybatisplus.annotation.IdType;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 *
 * </p>
 *
 * @author DMFlySky
 * @since 2021-03-17
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "Blog對象", description = "")
public class Blog implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "博客作者id")
    @TableField(value = "user_id")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long userId;

    @ApiModelProperty(value = "博客id")
    @TableId(value = "blog_id", type = IdType.ID_WORKER)
    @JsonSerialize(using = ToStringSerializer.class)
    private Long blogId;

    @ApiModelProperty(value = "博客作者")
    private String blogAuthor;

    @ApiModelProperty(value = "博客名稱")
    @TableField(value = "blog_name")
    private String blogName;

    @ApiModelProperty(value = "博客內容")
    private String blogContent;

    @ApiModelProperty(value = "博客閱讀量")
    private Integer blogNum;

    @ApiModelProperty(value = "博客分類")
    private String blogCategory;

    @ApiModelProperty(value = "創建時間")
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;
}

Comment類

import com.baomidou.mybatisplus.annotation.IdType;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 *
 * </p>
 *
 * @author DMFlySky
 * @since 2021-03-17
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "Comment對象", description = "")
public class Comment implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "評論id")
    @TableId(value = "comment_id", type = IdType.ID_WORKER)
    @JsonSerialize(using = ToStringSerializer.class)
    private Long commentId;

    @ApiModelProperty(value = "用戶id")
    @TableField(value = "user_id")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long userId;

    @ApiModelProperty(value = "博客id")
    @TableField(value = "blog_id")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long blogId;

    @ApiModelProperty(value = "評論內容")
    private String commentContent;

    @ApiModelProperty(value = "創建時間")
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;
}

整合MyBatis-Plus

CodeGenerator啟動類

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

import java.util.ArrayList;
import java.util.Scanner;

public class CodeGenerator {
    /**
     * <p>
     * 讀取控制台內容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("請輸入" + tip + ":");
        System.out.println(stringBuilder.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("請輸入正確的" + tip + "!");
    }

    public static void main(String[] args) {
        // 需要構建一個 代碼自動生成器 對象
        AutoGenerator mpg = new AutoGenerator();
        // 配置策略
        // 1、全局配置
        GlobalConfig gc = new GlobalConfig();

        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("DMFlySky");
        gc.setOpen(false);
        gc.setFileOverride(false); // 是否覆蓋
        gc.setServiceName("%sService");// 去Service的I前綴
        gc.setIdType(IdType.ID_WORKER);
        gc.setDateType(DateType.ONLY_DATE);
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);

        //2、設置數據源
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/blogs?useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        //3、包的配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.blogs_springboot");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setService("service");
        pc.setController("controller");
        mpg.setPackageInfo(pc);

        //4、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("blog", "comment", "user");// 設置要映射的表名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true); // 自動lombok;
        strategy.setLogicDeleteFieldName("deleted");

        // 自動填充配置
        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        //TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(gmtCreate);
        //    tableFills.add(gmtModified);
        strategy.setTableFillList(tableFills);
        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true);
        // localhost:8080/hello_id_2
        mpg.setStrategy(strategy);

        mpg.execute(); //執行
    }
}

MybatisPlusConfig配置類

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration // 配置類
@EnableTransactionManagement
@MapperScan("com.blogs_springboot.mapper")
public class MybatisPlusConfig {

    // 注冊分頁
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

}

統一結果封裝

Result類

import lombok.Data;

import java.io.Serializable;

/**
 * 數據統一結果返回
 */
@Data
public class Result implements Serializable {
    private Integer code;
    private String message;
    private Object data;

    public static Result success(String message) {
        Result result = new Result();
        result.setCode(0);
        result.setData(null);
        result.setMessage(message);
        return result;
    }

    public static Result success(Object data) {
        Result result = new Result();
        result.setCode(0);
        result.setData(data);
        result.setMessage("操作成功");
        return result;
    }

    public static Result success(String message, Object data) {
        Result result = new Result();
        result.setCode(0);
        result.setMessage(message);
        result.setData(data);
        return result;
    }

    public static Result success(Integer code, String message, Object data) {
        Result result = new Result();
        result.setCode(code);
        result.setMessage(message);
        result.setData(data);
        return result;
    }

    public static Result fail(String message) {
        Result result = new Result();
        result.setCode(-1);
        result.setMessage(message);
        result.setData(null);
        return result;
    }

    public static Result fail(String message, Object data) {
        Result result = new Result();
        result.setCode(-1);
        result.setMessage(message);
        result.setData(data);
        return result;
    }

    public static Result fail(Integer code, String message, Object data) {
        Result result = new Result();
        result.setCode(code);
        result.setMessage(message);
        result.setData(data);
        return result;
    }
}

自定義異常

GlobalExceptionHandler類

import org.apache.shiro.ShiroException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.io.IOException;

/**
 * 全局異常處理
 */
public class GlobalExceptionHandler {

    /**
     * 捕捉shiro的異常
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(ShiroException.class)
    public Result handle401(ShiroException e) {
        return Result.fail(401, e.getMessage(), null);
    }

    /**
     * 處理Assert的異常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IllegalArgumentException.class)
    public Result handler(IllegalArgumentException e) {
        return Result.fail(e.getMessage());
    }

    /**
     * 處理IO流的異常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IOException.class)
    public Result handler(IOException e) {
        return Result.fail(e.getMessage());
    }

    /**
     * 校驗錯誤異常處理
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result handler(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get();
        return Result.fail(objectError.getDefaultMessage());
    }

    /**
     * 處理運行時的異常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = RuntimeException.class)
    public Result handler(RuntimeException e) {
        return Result.fail(e.getMessage());
    }
}

整合Redis

RedisConfig配置類

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.net.UnknownHostException;

@Configuration
public class RedisConfig {
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        // 自定義 String Object
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);

        // Json 序列化配置
        Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        // ObjectMapper 轉譯
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        objectJackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key 采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash 的key也采用 String 的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value 序列化方式采用 jackson
        template.setValueSerializer(objectJackson2JsonRedisSerializer);
        // hash 的 value 采用 jackson
        template.setHashValueSerializer(objectJackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }
}

RedisUtils工具類

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils extends CachingConfigurerSupport {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // -------------------key相關操作--------------------- */

    /**
     * 刪除key
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 批量刪除key
     */
    public void delete(Collection<String> keys) {
        redisTemplate.delete(keys);
    }

    /**
     * 序列化key
     */
    public byte[] dump(String key) {
        return redisTemplate.dump(key);
    }

    /**
     * 是否存在key
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 設置過期時間
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 設置過期時間
     */
    public Boolean expireAt(String key, Date date) {
        return redisTemplate.expireAt(key, date);
    }

    /**
     * 查找匹配的key
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 將當前數據庫的 key 移動到給定的數據庫 db 當中
     */
    public Boolean move(String key, int dbIndex) {
        return redisTemplate.move(key, dbIndex);
    }

    /**
     * 移除 key 的過期時間,key 將持久保持
     */
    public Boolean persist(String key) {
        return redisTemplate.persist(key);
    }

    /**
     * 返回 key 的剩余的過期時間
     */
    public Long getExpire(String key, TimeUnit unit) {
        return redisTemplate.getExpire(key, unit);
    }

    /**
     * 返回 key 的剩余的過期時間
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 從當前數據庫中隨機返回一個 key
     */
    public String randomKey() {
        return redisTemplate.randomKey();
    }

    /**
     * 修改 key 的名稱
     */
    public void rename(String oldKey, String newKey) {
        redisTemplate.rename(oldKey, newKey);
    }

    /**
     * 僅當 newkey 不存在時,將 oldKey 改名為 newkey
     */
    public Boolean renameIfAbsent(String oldKey, String newKey) {
        return redisTemplate.renameIfAbsent(oldKey, newKey);
    }

    /**
     * 返回 key 所儲存的值的類型
     */
    public DataType type(String key) {
        return redisTemplate.type(key);
    }

    // -------------------string相關操作--------------------- */

    /**
     * 設置指定 key 的值
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 獲取指定 key 的值
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 返回 key 中字符串值的子字符
     */
    public String getRange(String key, long start, long end) {
        return redisTemplate.opsForValue().get(key, start, end);
    }

    /**
     * 將給定 key 的值設為 value ,並返回 key 的舊值(old value)
     */
    public Object getAndSet(String key, String value) {
        return redisTemplate.opsForValue().getAndSet(key, value);
    }

    /**
     * 對 key 所儲存的字符串值,獲取指定偏移量上的位(bit)
     */
    public Boolean getBit(String key, long offset) {
        return redisTemplate.opsForValue().getBit(key, offset);
    }

    /**
     * 批量獲取
     */
    public List<Object> multiGet(Collection<String> keys) {
        return redisTemplate.opsForValue().multiGet(keys);
    }

    /**
     * 設置ASCII碼, 字符串'a'的ASCII碼是97, 轉為二進制是'01100001',
     * 此方法是將二進制第offset位值變為value
     *
     * @param value 值,true為1, false為0
     */
    public boolean setBit(String key, long offset, boolean value) {
        return redisTemplate.opsForValue().setBit(key, offset, value);
    }

    /**
     * 將值 value 關聯到 key ,並將 key 的過期時間設為 timeout
     *
     * @param timeout 過期時間
     * @param unit    時間單位, 天:TimeUnit.DAYS 小時:TimeUnit.HOURS 分鍾:TimeUnit.MINUTES
     *                秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS
     */
    public void setEx(String key, String value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 只有在 key 不存在時設置 key 的值
     *
     * @return 之前已經存在返回false, 不存在返回true
     */
    public boolean setIfAbsent(String key, String value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }

    /**
     * 用 value 參數覆寫給定 key 所儲存的字符串值,從偏移量 offset 開始
     *
     * @param offset 從指定位置開始覆寫
     */
    public void setRange(String key, String value, long offset) {
        redisTemplate.opsForValue().set(key, value, offset);
    }

    /**
     * 獲取字符串的長度
     */
    public Long size(String key) {
        return redisTemplate.opsForValue().size(key);
    }

    /**
     * 批量添加
     */
    public void multiSet(Map<String, String> maps) {
        redisTemplate.opsForValue().multiSet(maps);
    }

    /**
     * 同時設置一個或多個 key-value 對,當且僅當所有給定 key 都不存在
     *
     * @return 之前已經存在返回false, 不存在返回true
     */
    public boolean multiSetIfAbsent(Map<String, String> maps) {
        return redisTemplate.opsForValue().multiSetIfAbsent(maps);
    }

    /**
     * 增加(自增長), 負數則為自減
     */
    public Long incrBy(String key, long increment) {
        return redisTemplate.opsForValue().increment(key, increment);
    }

    /**
     *
     */
    public Double incrByFloat(String key, double increment) {
        return redisTemplate.opsForValue().increment(key, increment);
    }

    /**
     * 追加到末尾
     */
    public Integer append(String key, String value) {
        return redisTemplate.opsForValue().append(key, value);
    }

    // -------------------hash相關操作------------------------- */

    /**
     * 獲取存儲在哈希表中指定字段的值
     */
    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 獲取所有給定字段的值
     */
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 獲取所有給定字段的值
     */
    public List<Object> hMultiGet(String key, Collection<Object> fields) {
        return redisTemplate.opsForHash().multiGet(key, fields);
    }

    /**
     * 添加一個鍵值對
     */
    public void hPut(String key, String hashKey, String value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * 添加一個map集合
     */
    public void hPutAll(String key, Map<String, Object> maps) {
        redisTemplate.opsForHash().putAll(key, maps);
    }

    /**
     * 僅當hashKey不存在時才設置
     */
    public Boolean hPutIfAbsent(String key, String hashKey, String value) {
        return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
    }

    /**
     * 刪除一個或多個哈希表字段
     */
    public Long hDelete(String key, Object... fields) {
        return redisTemplate.opsForHash().delete(key, fields);
    }

    /**
     * 查看哈希表 key 中,指定的字段是否存在
     */
    public boolean hExists(String key, String field) {
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    /**
     * 為哈希表 key 中的指定字段的整數值加上增量 increment
     */
    public Long hIncrBy(String key, Object field, long increment) {
        return redisTemplate.opsForHash().increment(key, field, increment);
    }

    /**
     * 為哈希表 key 中的指定字段的整數值加上增量 increment
     */
    public Double hIncrByFloat(String key, Object field, double delta) {
        return redisTemplate.opsForHash().increment(key, field, delta);
    }

    /**
     * 獲取所有哈希表中的字段
     */
    public Set<Object> hKeys(String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    /**
     * 獲取哈希表中字段的數量
     */
    public Long hSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    /**
     * 獲取哈希表中所有值
     */
    public List<Object> hValues(String key) {
        return redisTemplate.opsForHash().values(key);
    }

    /**
     * 迭代哈希表中的鍵值對
     */
    public Cursor<Map.Entry<Object, Object>> hScan(String key, ScanOptions options) {
        return redisTemplate.opsForHash().scan(key, options);
    }

    // ------------------------list相關操作----------------------------

    /**
     * 通過索引獲取列表中的元素
     */
    public Object lIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * 獲取列表指定范圍內的元素
     *
     * @param start 開始位置, 0是開始位置
     * @param end   結束位置, -1返回所有
     */
    public List<Object> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 存儲在list頭部
     */
    public Long lLeftPush(String key, String value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 向list頭部添加多個值
     */
    public Long lLeftPushAll(String key, String... value) {
        return redisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     * 向list尾部添加集合
     */
    public Long lLeftPushAll(String key, Collection<String> value) {
        return redisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     * 當list存在的時候才加入
     */
    public Long lLeftPushIfPresent(String key, String value) {
        return redisTemplate.opsForList().leftPushIfPresent(key, value);
    }

    /**
     * 如果pivot存在,在pivot前面添加
     */
    public Long lLeftPush(String key, String pivot, String value) {
        return redisTemplate.opsForList().leftPush(key, pivot, value);
    }

    /**
     * 向list尾部添加值
     */
    public Long lRightPush(String key, String value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 向list尾部添加多個值
     */
    public Long lRightPushAll(String key, String... value) {
        return redisTemplate.opsForList().rightPushAll(key, value);
    }

    /**
     * 向list尾部添加集合
     */
    public Long lRightPushAll(String key, Collection<String> value) {
        return redisTemplate.opsForList().rightPushAll(key, value);
    }

    /**
     * 為已存在的列表添加值
     */
    public Long lRightPushIfPresent(String key, String value) {
        return redisTemplate.opsForList().rightPushIfPresent(key, value);
    }

    /**
     * 如果pivot存在,在pivot后面添加
     */
    public Long lRightPush(String key, String pivot, String value) {
        return redisTemplate.opsForList().rightPush(key, pivot, value);
    }

    /**
     * 通過索引設置列表元素的值
     *
     * @param index 位置
     */
    public void lSet(String key, long index, String value) {
        redisTemplate.opsForList().set(key, index, value);
    }

    /**
     * 移出並獲取列表的第一個元素
     *
     * @return 刪除的元素
     */
    public Object lLeftPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 移出並獲取列表的第一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素為止
     *
     * @param timeout 等待時間
     * @param unit    時間單位
     */
    public Object lBLeftPop(String key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().leftPop(key, timeout, unit);
    }

    /**
     * 移除並獲取列表最后一個元素
     *
     * @return 刪除的元素
     */
    public Object lRightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 移出並獲取列表的最后一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素為止
     *
     * @param timeout 等待時間
     * @param unit    時間單位
     */
    public Object lBRightPop(String key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().rightPop(key, timeout, unit);
    }

    /**
     * 移除列表的最后一個元素,並將該元素添加到另一個列表並返回
     */
    public Object lRightPopAndLeftPush(String sourceKey, String destinationKey) {
        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey);
    }

    /**
     * 從列表中彈出一個值,將彈出的元素插入到另外一個列表中並返回它;
     * 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素為止
     */
    public Object lBRightPopAndLeftPush(String sourceKey, String destinationKey,
                                        long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
                destinationKey, timeout, unit);
    }

    /**
     * 刪除集合中值等於value得元素
     *
     * @param index index=0, 刪除所有值等於value的元素;
     *              index>0, 從頭部開始刪除第一個值等於value的元素;
     *              index<0, 從尾部開始刪除第一個值等於value的元素;
     */
    public Long lRemove(String key, long index, String value) {
        return redisTemplate.opsForList().remove(key, index, value);
    }

    /**
     * 裁剪list
     */
    public void lTrim(String key, long start, long end) {
        redisTemplate.opsForList().trim(key, start, end);
    }

    /**
     * 獲取列表長度
     */
    public Long lLen(String key) {
        return redisTemplate.opsForList().size(key);
    }

    // --------------------set相關操作--------------------------

    /**
     * set添加元素
     */
    public Long sAdd(String key, String... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    /**
     * set移除元素
     */
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    /**
     * 移除並返回集合的一個隨機元素
     */
    public Object sPop(String key) {
        return redisTemplate.opsForSet().pop(key);
    }

    /**
     * 將元素value從一個集合移到另一個集合
     */
    public Boolean sMove(String key, String value, String destKey) {
        return redisTemplate.opsForSet().move(key, value, destKey);
    }

    /**
     * 獲取集合的大小
     */
    public Long sSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * 判斷集合是否包含value
     */
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 獲取兩個集合的交集
     */
    public Set<Object> sIntersect(String key, String otherKey) {
        return redisTemplate.opsForSet().intersect(key, otherKey);
    }

    /**
     * 獲取key集合與多個集合的交集
     */
    public Set<Object> sIntersect(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().intersect(key, otherKeys);
    }

    /**
     * key集合與otherKey集合的交集存儲到destKey集合中
     */
    public Long sIntersectAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().intersectAndStore(key, otherKey, destKey);
    }

    /**
     * key集合與多個集合的交集存儲到destKey集合中
     */
    public Long sIntersectAndStore(String key, Collection<String> otherKeys, String destKey) {
        return redisTemplate.opsForSet().intersectAndStore(key, otherKeys, destKey);
    }

    /**
     * 獲取兩個集合的並集
     */
    public Set<Object> sUnion(String key, String otherKeys) {
        return redisTemplate.opsForSet().union(key, otherKeys);
    }

    /**
     * 獲取key集合與多個集合的並集
     */
    public Set<Object> sUnion(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().union(key, otherKeys);
    }

    /**
     * key集合與otherKey集合的並集存儲到destKey中
     */
    public Long sUnionAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey);
    }

    /**
     * key集合與多個集合的並集存儲到destKey中
     */
    public Long sUnionAndStore(String key, Collection<String> otherKeys, String destKey) {
        return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey);
    }

    /**
     * 獲取兩個集合的差集
     */
    public Set<Object> sDifference(String key, String otherKey) {
        return redisTemplate.opsForSet().difference(key, otherKey);
    }

    /**
     * 獲取key集合與多個集合的差集
     */
    public Set<Object> sDifference(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().difference(key, otherKeys);
    }

    /**
     * key集合與otherKey集合的差集存儲到destKey中
     */
    public Long sDifference(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().differenceAndStore(key, otherKey, destKey);
    }

    /**
     * key集合與多個集合的差集存儲到destKey中
     */
    public Long sDifference(String key, Collection<String> otherKeys, String destKey) {
        return redisTemplate.opsForSet().differenceAndStore(key, otherKeys, destKey);
    }

    /**
     * 獲取集合所有元素
     */
    public Set<Object> setMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 隨機獲取集合中的一個元素
     */
    public Object sRandomMember(String key) {
        return redisTemplate.opsForSet().randomMember(key);
    }

    /**
     * 隨機獲取集合中count個元素
     */
    public List<Object> sRandomMembers(String key, long count) {
        return redisTemplate.opsForSet().randomMembers(key, count);
    }

    /**
     * 隨機獲取集合中count個元素並且去除重復的
     */
    public Set<Object> sDistinctRandomMembers(String key, long count) {
        return redisTemplate.opsForSet().distinctRandomMembers(key, count);
    }

    /**
     * 迭代set中的元素
     */
    public Cursor<Object> sScan(String key, ScanOptions options) {
        return redisTemplate.opsForSet().scan(key, options);
    }

    //------------------zSet相關操作--------------------------------

    /**
     * 添加元素,有序集合是按照元素的score值由小到大排列
     */
    public Boolean zAdd(String key, String value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 添加元素
     */
    public Long zAdd(String key, Set<TypedTuple<Object>> values) {
        return redisTemplate.opsForZSet().add(key, values);
    }

    /**
     * 刪除元素
     */
    public Long zRemove(String key, Object... values) {
        return redisTemplate.opsForZSet().remove(key, values);
    }

    /**
     * 增加元素的score值,並返回增加后的值
     */
    public Double zIncrementScore(String key, String value, double delta) {
        return redisTemplate.opsForZSet().incrementScore(key, value, delta);
    }

    /**
     * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
     *
     * @return 0表示第一位
     */
    public Long zRank(String key, Object value) {
        return redisTemplate.opsForZSet().rank(key, value);
    }

    /**
     * 返回元素在集合的排名,按元素的score值由大到小排列
     */
    public Long zReverseRank(String key, Object value) {
        return redisTemplate.opsForZSet().reverseRank(key, value);
    }

    /**
     * 獲取集合的元素, 從小到大排序
     *
     * @param start 開始位置
     * @param end   結束位置, -1查詢所有
     */
    public Set<Object> zRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 獲取集合元素, 並且把score值也獲取
     */
    public Set<TypedTuple<Object>> zRangeWithScores(String key, long start, long end) {
        return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
    }

    /**
     * 根據Score值查詢集合元素
     */
    public Set<Object> zRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    /**
     * 根據Score值查詢集合元素, 從小到大排序
     */
    public Set<TypedTuple<Object>> zRangeByScoreWithScores(String key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
    }

    /**
     *  //從開始到結束的范圍內獲取一組元組,其中分數在分類集中的最小值和最大值之間
     */
    public Set<TypedTuple<Object>> zRangeByScoreWithScores(String key, double min,
                                                           double max, long start, long end) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max, start, end);
    }

    /**
     * 獲取集合的元素, 從大到小排序
     */
    public Set<Object> zReverseRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    /**
     * 獲取集合的元素, 從大到小排序, 並返回score值
     */
    public Set<TypedTuple<Object>> zReverseRangeWithScores(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
    }

    /**
     * 根據Score值查詢集合元素, 從大到小排序
     */
    public Set<Object> zReverseRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
    }

    /**
     * 根據Score值查詢集合元素, 從大到小排序
     */
    public Set<TypedTuple<Object>> zReverseRangeByScoreWithScores(String key,
                                                                  double min, double max) {
        return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, min, max);
    }

    /**
     * 獲取從開始到結束的范圍內的元素,其中分數介於排序集“高->低”的最小值和最大值之間。
     */
    public Set<Object> zReverseRangeByScore(String key, double min,
                                            double max, long start, long end) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, start, end);
    }

    /**
     * 根據score值獲取集合元素數量
     */
    public Long zCount(String key, double min, double max) {
        return redisTemplate.opsForZSet().count(key, min, max);
    }

    /**
     * 獲取集合大小
     */
    public Long zSize(String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    /**
     * 獲取集合大小
     */
    public Long zZCard(String key) {
        return redisTemplate.opsForZSet().zCard(key);
    }

    /**
     * 獲取集合中value元素的score值
     */
    public Double zScore(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 移除指定索引位置的成員
     */
    public Long zRemoveRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().removeRange(key, start, end);
    }

    /**
     * 根據指定的score值的范圍來移除成員
     */
    public Long zRemoveRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
    }

    /**
     * 獲取key和otherKey的並集並存儲在destKey中
     */
    public Long zUnionAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey);
    }

    /**
     * 將key和otherKeys的集合合並排序,並將結果存儲到目標destKey中。
     */
    public Long zUnionAndStore(String key, Collection<String> otherKeys, String destKey) {
        return redisTemplate.opsForZSet().unionAndStore(key, otherKeys, destKey);
    }

    /**
     * 交集
     */
    public Long zIntersectAndStore(String key, String otherKey,
                                   String destKey) {
        return redisTemplate.opsForZSet().intersectAndStore(key, otherKey, destKey);
    }

    /**
     * 交集
     */
    public Long zIntersectAndStore(String key, Collection<String> otherKeys,
                                   String destKey) {
        return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys, destKey);
    }

    /**
     * 迭代zset表中的數據
     */
    public Cursor<TypedTuple<Object>> zScan(String key, ScanOptions options) {
        return redisTemplate.opsForZSet().scan(key, options);
    }

    // ===============================HyperLogLog=================================

    public long pfadd(String key, String value) {
        return redisTemplate.opsForHyperLogLog().add(key, value);
    }

    public long pfcount(String key) {
        return redisTemplate.opsForHyperLogLog().size(key);
    }

    public void pfremove(String key) {
        redisTemplate.opsForHyperLogLog().delete(key);
    }

    public void pfmerge(String key1, String key2) {
        redisTemplate.opsForHyperLogLog().union(key1, key2);
    }


}

MapUtils工具類

package com.blogs_springboot.utils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class MapUtils implements Serializable {

    /**
     * 實體類轉Map集合
     */
    public static Map<String, Object> entityToMap(Object object) {
        Map<String, Object> map = new HashMap<>();
        for (Field field : object.getClass().getDeclaredFields()) {
            try {
                boolean flag = field.isAccessible();
                field.setAccessible(true);
                Object o = field.get(object);
                map.put(field.getName(), o);
                field.setAccessible(flag);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    /**
     * Map集合轉實體類
     */
    public static <T> T mapToEntity(Map<Object, Object> map, Class<T> entity) {
        T t = null;
        try {
            t = entity.newInstance();
            for (Field field : entity.getDeclaredFields()) {
                if (map.containsKey(field.getName())) {
                    boolean flag = field.isAccessible();
                    field.setAccessible(true);
                    Object object = map.get(field.getName());
                    if (object != null && field.getType().isAssignableFrom(object.getClass())) {
                        field.set(t, object);
                    }
                    field.setAccessible(flag);
                }
            }
            return t;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return t;
    }

}

整合Swagger

SwaggerConfig配置類

import com.google.common.collect.Lists;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

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

@Configuration // 配置類
@EnableSwagger2 // 開啟Swagger2的自動配置
public class SwaggerConfig {

    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        registry.addResourceHandler("swagger-ui.html","doc.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    @Bean //配置docket以配置Swagger具體參數
    public Docket docket(Environment environment) {
        // 設置要顯示swagger的環境
        Profiles of = Profiles.of("dev");
        // 判斷當前是否處於該環境
        // 通過 enable() 接收此參數判斷是否要顯示
        boolean b = environment.acceptsProfiles(of);
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(b) //配置是否啟用Swagger,如果是false,在瀏覽器將無法訪問
                .select()// 通過.select()方法,去配置掃描接口,RequestHandlerSelectors配置如何掃描接口
                .apis(RequestHandlerSelectors.basePackage("com.blogs_springboot.controller"))
                .build();
    }

    //配置文檔信息
    private ApiInfo apiInfo() {
        Contact contact = new Contact("青宇", "http://www.dmflysky.com/into/contact", "2991596029@qq.com");
        return new ApiInfo(
                "Blogs Swagger學習", // 標題
                "前后端分離", // 描述
                "v1.0", // 版本
                "http://www.dmflysky.com/", // 組織鏈接
                contact, // 聯系人信息
                "Apach 2.0 許可", // 許可
                "許可鏈接", // 許可連接
                new ArrayList<>()// 擴展
        );
    }

    private List<ApiKey> security() {
        return Lists.newArrayList(
                new ApiKey("token", "token", "header")
        );
    }
}

整合Shiro

ShiroConfig配置類

import com.blogs_springboot.utils.shiro.JwtFilter;
import com.blogs_springboot.utils.shiro.PasswordRealm;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.*;

@Configuration
public class ShiroConfig {

    /**
     * ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

        Map<String, Filter> filterMap = new HashMap<>();
        filterMap.put("jwt", new JwtFilter());

        factoryBean.setFilters(filterMap);
        factoryBean.setSecurityManager(securityManager);

        Map<String, String> filterRuleMap = new HashMap<>();

        // Swagger接口
        filterRuleMap.put("/doc.html", "anon");
        filterRuleMap.put("/api-docs-ext", "anon");
        filterRuleMap.put("/swagger-ui.html", "anon");
        filterRuleMap.put("/webjars/**", "anon");

        filterRuleMap.put("/**", "jwt");

        factoryBean.setFilterChainDefinitionMap(filterRuleMap);

        return factoryBean;
    }

    /**
     * 默認安全管理器
     */
    @Bean("securityManager")
    public DefaultWebSecurityManager securityManager(PasswordRealm passwordRealm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(passwordRealm);
        // 記住我
        defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager());

        // 關閉自帶session
        DefaultSessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator();
        evaluator.setSessionStorageEnabled(false);
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        subjectDAO.setSessionStorageEvaluator(evaluator);

        defaultWebSecurityManager.setSubjectDAO(subjectDAO);

        return defaultWebSecurityManager;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/**", "jwt"); // 主要通過注解方式校驗權限
        chainDefinition.addPathDefinitions(filterMap);
        return chainDefinition;
    }

    /**
     * 管理shiro的生命周期
     */
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 注入 密碼登錄CustomRealm
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public PasswordRealm userPasswordRealm() {
        return new PasswordRealm();
    }

    /**
     * shiro注解
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 加入shiro注解  代理生成器
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 設置cookie 記住我生成cookie
     */
    @Bean
    public CookieRememberMeManager cookieRememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        return cookieRememberMeManager;
    }

    /**
     * 設置cookie有效時間
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        /*這個參數是cookie的名稱,對應前端頁面的checkbox的name=remremberMe*/
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        /*cookie的有效時間為30天,單位秒*/
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }

}

PasswordRealm類

import cn.hutool.core.bean.BeanUtil;
import com.blogs_springboot.entity.User;
import com.blogs_springboot.utils.MapUtils;
import com.blogs_springboot.utils.redis.RedisUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;

/**
 * 密碼登錄驗證
 */
@Component
public class PasswordRealm extends AuthorizingRealm {

    // jwt工具類
    @Autowired
    private JwtUtils jwtUtils;

    // 注入Redis工具類
    @Autowired
    private RedisUtils redisUtils;

    /**
     * 授權
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("————密碼授權————doGetAuthorizationInfo————");

        return null;
    }

    /**
     * 認證
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("————密碼認證————doGetAuthenticationInfo————");

        JwtToken jwtToken = (JwtToken) token;
        String userId = jwtUtils.getClaimByToken((String) jwtToken.getPrincipal()).getSubject();

        // 獲取Redis緩存中用戶數據
        Map<Object, Object> map = redisUtils.hGetAll(userId);
        // map集合轉實體類
        User user = MapUtils.mapToEntity(map, User.class);

        if (user == null) {
            throw new UnknownAccountException("賬戶不存在");
        }
        LoginService loginService = new LoginService();
        BeanUtil.copyProperties(user, loginService);
        return new SimpleAuthenticationInfo(loginService, jwtToken.getCredentials(), getName());
    }

    /**
     * 用來判斷是否使用當前的 realm
     *
     * @param token 傳入的token
     * @return true就使用,false就不使用
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

}

JwtToken類

import lombok.Data;
import org.apache.shiro.authc.AuthenticationToken;

/**
 * 自定義token
 */
@Data
public class JwtToken implements AuthenticationToken {

    private String token;

    public JwtToken() {}

    public JwtToken(String token) {
        this.token = token;
    }

    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}

JwtFilter類

import cn.hutool.json.JSONUtil;
import com.blogs_springboot.common.Result;
import io.jsonwebtoken.Claims;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


/**
 * 自定義shiro過濾器
 */
@Component
public class JwtFilter extends BasicHttpAuthenticationFilter {

    @Override
    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader("Authorization");
        if(StringUtils.isEmpty(jwt)) {
            return null;
        }

        return new JwtToken(jwt);
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader("Authorization");
        if(StringUtils.isEmpty(jwt)) {
            return true;
        } else {
            // 校驗jwt
            JwtUtils jwtUtil = new JwtUtils();

            Claims claim = jwtUtil.getClaimByToken(jwt);
            if(claim == null || jwtUtil.isTokenExpired(claim.getExpiration())) {
                throw new ExpiredCredentialsException("token已失效,請重新登錄");
            }
            // 執行登錄
            return executeLogin(servletRequest, servletResponse);
        }
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {

        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        Throwable throwable = e.getCause() == null ? e : e.getCause();
        Result result = Result.fail(throwable.getMessage());
        String json = JSONUtil.toJsonStr(result);

        try {
            httpServletResponse.getWriter().print(json);
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
        return false;
    }

    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域時會首先發送一個OPTIONS請求,這里我們給OPTIONS請求直接返回正常狀態
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(org.springframework.http.HttpStatus.OK.value());
            return false;
        }

        return super.preHandle(request, response);
    }
}

JwtUtils類

package com.blogs_springboot.utils.shiro;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import org.apache.shiro.ShiroException;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * 生成jwt工具類
 */
@Data
@Service
public class JwtUtils {

    private String secret = "f4e2e52034348f86b67cde581c0f9eb5";
    private long expire = 3 * 24 * 60 * 60 * 1000;
    private String header = "Authorization";

    /**
     * 生成jwt token
     */
    public String generateToken(long userId) {
        Date nowDate = new Date();
        //過期時間
        Date expireDate = new Date(nowDate.getTime() + expire);

        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setSubject(userId+"")
                .setIssuedAt(nowDate)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    public Claims getClaimByToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        }catch (ShiroException e){
            throw new ShiroException(e);
        }
    }

    /**
     * token是否過期
     * @return  true:過期
     */
    public boolean isTokenExpired(Date expiration) {
        return expiration.before(new Date());
    }
}

LoginService類

import lombok.Data;
import org.springframework.stereotype.Component;

import java.io.Serializable;

@Data
@Component
public class LoginService implements Serializable {

    private Long userId;
    private String userName;
    private String userImg;
    private String userEmail;

}

MDPasswordUtils類

import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Component;

/**
 * 密碼MD5加密工具類
 */
@Component
public class MDPasswordUtils {

    public String getMDPasswordUtils(String userName, String userPassword) {
        String hashAlgorithmName = "MD5";  // 加密方式:md5加密
        Object credentials = userPassword;  // 密碼
        Object salt = ByteSource.Util.bytes(userName); // 鹽
        int hashIterations = 512;  // 加密次數
        Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
        return result.toString();
    }
}

設計模式之發送郵件

Email類

import lombok.Data;
import java.io.Serializable;

/**
 * 發送郵件實體類
 */ 
@Data
public class Email implements Serializable {
    // 管理員郵箱
    private final String adminEmail = "2991596029@qq.com";
    // 用戶郵件信息
    private String name;
    private String email;
    private String theme;
    private String message;
    private String code;

    // 發送用戶修改密碼跳轉與提醒郵件
    public Email(String email){
        this.email = email;
    }

    // 發送驗證碼郵件
    public Email(String email, String code){
        this.email = email;
        this.code = code;
    }

    // 發送聯系博主郵件
    public Email(String name, String email, String theme, String message){
        this.name = name;
        this.email = email;
        this.theme = theme;
        this.message = message;
    }

}

EmailService接口

import com.blogs_springboot.utils.pojo.Email;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.stereotype.Service;

/**
 * 定義發送郵件接口
 */
public interface EmailService {

    // 發送郵件方法
    void sendEmail(Email email, JavaMailSenderImpl mailSender);
}

接口實現類

CodeEmailImpl類
import com.blogs_springboot.utils.pojo.Email;
import com.blogs_springboot.utils.service.EmailService;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * 實現發送郵件接口
 * 發送驗證碼郵件
 */
@Component
public class CodeEmailImpl implements EmailService {

    @Async  // 異步
    @Override
    public void sendEmail(Email email, JavaMailSenderImpl mailSender) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setSubject("用戶郵箱登錄驗證碼!");
        message.setText("尊敬的用戶,您好:\n"
                + "\n本次郵件登錄的驗證碼為:" + email.getCode() + ",本驗證碼2分鍾內有效,請及時輸入。(請勿泄露此驗證碼)\n"
                + "\n如非本人操作,請忽略該郵件。\n(這是一封自動發送的郵件,請不要直接回復)");
        message.setTo(email.getEmail());       //收件人
        message.setFrom(email.getAdminEmail());   //發送人  無法更改
        mailSender.send(message);      //發送郵件
    }
}
ChangePasswordEmailImpl類
import com.blogs_springboot.utils.pojo.Email;
import com.blogs_springboot.utils.service.EmailService;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

/**
 * 實現發送郵件接口
 * 發送用戶修改密碼跳轉郵件
 */
@Component
public class ChangePasswordEmailImpl implements EmailService {

    @Async  // 異步
    @Override
    public void sendEmail(Email email, JavaMailSenderImpl mailSender) {
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
            helper.setSubject("用戶修改密碼!");
            helper.setText("點擊鏈接,進入修改密碼入口" + "<br><br>"
                    // Windows環境
                    + "<a href='http://localhost:8090/changePassword?userEmail="
                    // Linux環境
                    // + "<a href='http://www.dmflysky.com/changePassword?userEmail="
                    + email.getEmail()
                    + "' target='_blank'>http://www.dmflysky.com/into/changePassword</a>"
                    + "<br><br>" + "謹慎操作(請不要泄露密碼入口!)", true);
            helper.setTo(email.getEmail());        //收件人
            helper.setFrom(email.getAdminEmail());     //發送人  無法更改
            mailSender.send(mimeMessage);   //發送郵件
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }
}
RemindEmailImpl類
import com.blogs_springboot.utils.pojo.Email;
import com.blogs_springboot.utils.service.EmailService;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

/**
 * 實現發送郵件接口
 * 發送用戶修改密碼時提醒郵件
 */
@Component
public class RemindEmailImpl implements EmailService {

    @Async  // 異步
    @Override
    public void sendEmail(Email email, JavaMailSenderImpl mailSender) {
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
            helper.setSubject("用戶,您好!");
            helper.setText("您正在修改密碼!(謹慎操作!)" + "<br><br>"
                    + "如是本人操作,請忽略該郵件。(這是一封自動發送的郵件,請不要回復)" + "<br><br>"
                    + "如不是本人操作,請" +
                    // Windows環境
                    "<a href='http://localhost:8090/contact' target='_blank'>聯系我們</a>" +
                    // Linux環境
                    // + "<a href='http://www.dmflysky.com/contact' target='_blank'>聯系我們</a>" +
                    ",說明原因和損失情況!", true);
            helper.setTo(email.getEmail());        //收件人
            helper.setFrom(email.getAdminEmail());     //發送人  無法更改
            mailSender.send(mimeMessage);   //發送郵件
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }
}
ContactEmailImpl類
import com.blogs_springboot.utils.pojo.Email;
import com.blogs_springboot.utils.service.EmailService;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * 實現發送郵件接口
 * 發送聯系郵件
 */
@Component
public class ContactEmailImpl implements EmailService {

    @Async  // 異步
    @Override
    public void sendEmail(Email email, JavaMailSenderImpl mailSender) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setSubject("用戶留言!");
        message.setText("發信人: " + email.getName() + "\n"
                + "\n用戶郵箱: " + email.getEmail() + "\n"
                + "\n用戶留言主題: " + email.getTheme() + "\n"
                + "\n用戶留言: " + email.getMessage());
        message.setTo(email.getAdminEmail());      //收件人
        message.setFrom(email.getAdminEmail());    //發送人  無法更改
        mailSender.send(message);       //發送郵件
    }
}

EmailFactory類

import com.blogs_springboot.utils.service.EmailService;
import com.blogs_springboot.utils.service.impl.ChangePasswordEmailImpl;
import com.blogs_springboot.utils.service.impl.CodeEmailImpl;
import com.blogs_springboot.utils.service.impl.ContactEmailImpl;
import com.blogs_springboot.utils.service.impl.RemindEmailImpl;
import org.springframework.stereotype.Component;

/**
 * 發送郵件工廠類
 */
@Component
public class EmailFactory {
    //使用 getEmail 方法獲取郵件類型的對象
    public EmailService getEmail(String emailType){
        if(emailType == null){
            return null;
        }
        if(emailType.equalsIgnoreCase("CodeEmailImpl")){
            return new CodeEmailImpl();
        } else if(emailType.equalsIgnoreCase("ContactEmailImpl")){
            return new ContactEmailImpl();
        } else if(emailType.equalsIgnoreCase("RemindEmailImpl")){
            return new RemindEmailImpl();
        }else if(emailType.equalsIgnoreCase("ChangePasswordEmailImpl")){
            return new ChangePasswordEmailImpl();
        }
        return null;
    }

}

郵件接口

/*
 * 發送驗證碼郵件  /codeEmail
 * */
@PostMapping("/codeEmail")
public Result codeEmail(@RequestBody String userEmail) {
    Email email = new Email();
    email.setEmail(userEmail);
    // 生成6位驗證碼
    String emailCode = String.valueOf((int) ((Math.random() * 9 + 1) * 100000));
    email.setCode(emailCode);
    // 利用郵件工廠類,生成對應的郵件發送類
    EmailService emailService = emailFactory.getEmail("CodeEmailImpl");
    // 利用多態,重寫郵件發送方法,發送郵件
    emailService.sendEmail(email, mailSender);
    // 設置驗證碼過期時間
    redisUtils.setEx(userEmail, emailCode, 120, TimeUnit.SECONDS);
    return Result.success("郵件已發送");
}

/*
 * 發送聯系我們郵件  /contactEmail
 * */
@PostMapping("/contactEmail")
public Result contactEmail(@Validated @RequestBody Email email) {
    // 利用郵件工廠類,生成對應的郵件發送類
    EmailService emailService = emailFactory.getEmail("ContactEmailImpl");
    // 利用多態,重寫郵件發送方法,發送郵件
    emailService.sendEmail(email, mailSender);
    return Result.success("郵件已發送");
}

/*
 * 發送用戶修改密碼時跳轉郵件  /changePasswordEmail
 * */
@PostMapping("/changePasswordEmail")
public Result changePasswordEmail(@RequestBody String userEmail) {
    Email email = new Email();
    email.setEmail(userEmail);
    // 利用郵件工廠類,生成對應的郵件發送類
    EmailService emailService = emailFactory.getEmail("ChangePasswordEmailImpl");
    // 利用多態,重寫郵件發送方法,發送郵件
    emailService.sendEmail(email, mailSender);
    return Result.success("郵件已發送");
}

/*
 * 發送用戶修改密碼時提醒郵件  /remindEmail
 * */
@PostMapping("/remindEmail")
public Result remindEmail(@RequestBody String userEmail) {
    Email email = new Email();
    email.setEmail(userEmail);
    // 利用郵件工廠類,生成對應的郵件發送類
    EmailService emailService = emailFactory.getEmail("RemindEmailImpl");
    // 利用多態,重寫郵件發送方法,發送郵件
    emailService.sendEmail(email, mailSender);
    return Result.success("郵件已發送");
}

文件上傳

/*
 * 上傳文件  /upload
 * */
@PostMapping("/upload")
public Result fileUpload(@RequestBody @RequestParam("file") MultipartFile file) throws IOException {
    File pathFile;
    // 判斷系統
    String os = System.getProperty("os.name");
    if (os.toLowerCase().startsWith("win")) {  // win系統
        // 絕對路徑=項目路徑+自定義路徑
        File projectPath = new File(ResourceUtils.getURL("classpath:").getPath());
        pathFile = new File(projectPath.getAbsolutePath(), "static/upload/");
        // 判斷文件路徑是否存在,不存在則創建文件夾
        if (!pathFile.exists()) {
            pathFile.mkdirs();
        }
    } else {  // Linux服務器
        // 項目路徑
        pathFile = new File("/home/www/upload/");
        // 判斷文件路徑是否存在,不存在則創建文件夾
        if (!pathFile.exists()) {
            pathFile.mkdirs();
        }
    }
    // 設置上傳文件名 uuid_文件名
    UUID uuid = UUID.randomUUID();
    File serverFile = new File(pathFile, uuid + "_" + file.getOriginalFilename());
    // 上傳文件
    file.transferTo(serverFile);
    // 返回文件名
    String imgPath = ("/upload/" + uuid + "_" + file.getOriginalFilename()).replace("\\", "/");
    return Result.success("文件已上傳", imgPath);
}

登錄/注冊接口

登錄

  • 賬號密碼登錄
/*
 * 用戶密碼登錄  /passwordLogin
 * */
@PostMapping("/passwordLogin")
public Result passwordLogin(@RequestBody Map<String, String> userMap, HttpServletResponse response) {
    try {
        // 獲取Redis緩存中用戶數據
        Map<Object, Object> map = redisUtils.hGetAll(userMap.get("userName"));
        // map集合轉實體類
        User user = MapUtils.mapToEntity(map, User.class);
        // 判斷用戶是否存在
        Assert.notNull(user, "用戶不存在");
        // 驗證密碼
        if (!user.getUserPassword().equals(mdPasswordUtils.getMDPasswordUtils(userMap.get("userName"), userMap.get("userPassword")))) {
            return Result.fail("密碼錯誤!");
        }
        // 生成jwt
        String jwt = jwtUtils.generateToken(user.getUserId());
        // 返回頭部信息
        response.setHeader("Authorization", jwt);
        response.setHeader("Access-Control-Expose-Headers", "Authorization");
        return Result.success("登錄成功", user);
    } catch (Exception e) {
        return Result.fail("登錄失敗");
    }
}
  • 郵件驗證碼登錄
/*
* 用戶郵箱登錄  /passwordLogin
* */
@PostMapping("/emailLogin")
public Result emailLogin(@RequestBody Map<String, String> userMap, HttpServletResponse response) {
    try {
        // 連接數據庫 獲取用戶數據
        String userEmail = userMap.get("userEmail");
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("user_email", userEmail);
        User user = userService.getOne(wrapper);
        // 判斷用戶是否存在
        Assert.notNull(user, "用戶不存在");
        // 判斷驗證碼是否存在
        if (!redisUtils.hasKey(userEmail)) {
            return Result.fail("驗證碼過期!");
        }
        // 比對驗證碼
        if (!String.valueOf(redisUtils.get(userEmail)).equals(userMap.get("emailCode"))) {
            return Result.fail("驗證碼錯誤!");
        }
        // 生成jwt
        String jwt = jwtUtils.generateToken(user.getUserId());
        // 返回頭部信息
        response.setHeader("Authorization", jwt);
        response.setHeader("Access-Control-Expose-Headers", "Authorization");
        return Result.success("登錄成功", user);
    } catch (Exception e) {
        return Result.fail("登錄失敗");
    }
}

注冊

/*
 * 用戶注冊  /register
 * */
@PostMapping("/register")
public Result register(@Validated @RequestBody User user) {
    // 設置用戶基本信息
    user.setUserPower("用戶");
    user.setGmtCreate(dateUtils.getNowDate());
    // 密碼鹽值加密
    user.setUserPassword(mdPasswordUtils.getMDPasswordUtils(user.getUserName(), user.getUserPassword()));
    // 保存數據庫
    userService.save(user);
    // 獲取用戶數據
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("user_name", user.getUserName());
    User redisUser = userService.getOne(wrapper);
    // 實體類轉map集合
    Map<String, Object> map = MapUtils.entityToMap(redisUser);
    // 保存用戶實體,名userId,userName的哈希表
    redisUtils.hPutAll(String.valueOf(redisUser.getUserId()), map);
    redisUtils.hPutAll(redisUser.getUserName(), map);
    return Result.success("注冊成功", redisUser);
}

/*
 * 用戶注冊時驗證用戶名有效性  /validUserName
 * */
@PostMapping("/validUserName")
public Result validUserName(@RequestBody String userName) {
    if (userName != null && redisUtils.hExists(userName, "userName")) {
        return Result.fail("用戶已存在,請重新注冊!");
    }
    return Result.success("用戶不存在,可以注冊!");
}

/*
 * 用戶注冊時驗證郵箱有效性  /validUserEmail
 * */
@PostMapping("/validUserEmail")
public Result validUserEmail(@RequestBody String userEmail) {
    // 連接數據庫 獲取用戶數據
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("user_email", userEmail);
    User user = userService.getOne(wrapper);
    if (user != null) {
        return Result.fail("郵箱已使用,請重新注冊!");
    }
    return Result.success("郵箱沒有使用過,可以綁定!");
}

修改密碼

/*
 * 用戶修改密碼時獲取用戶數據  /changePasswordUser
 * */
@PostMapping("/changePasswordUser")
public Result changePasswordUser(@RequestBody String userEmail) {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("user_email", userEmail);
    User user = userService.getOne(wrapper);
    return Result.success("用戶數據", user);
}

/*
 * 用戶修改密碼  /changePassword
 * */
@PostMapping("/changePassword")
public Result changePassword(@Validated @RequestBody User user) {
    // 對密碼進行MD5鹽值加密
    user.setUserPassword(mdPasswordUtils.getMDPasswordUtils(user.getUserName(), user.getUserPassword()));
    String userPassword = user.getUserPassword();
    // 先刪除用戶實體map集合中的userPassword字段
    redisUtils.hDelete(String.valueOf(user.getUserId()), "userPassword");
    redisUtils.hDelete(user.getUserName(), "userPassword");
    // 當map集合中的userPassword字段不存在,再添加userPassword字段和修改的值,否則添加失敗
    redisUtils.hPutIfAbsent(String.valueOf(user.getUserId()), "userPassword", userPassword);
    redisUtils.hPutIfAbsent(user.getUserName(), "userPassword", userPassword);
    // 更新數據庫
    userService.updateById(user);
    return Result.success("密碼修改成功", MapUtils.mapToEntity(redisUtils.hGetAll(user.getUserName()), User.class));
}

用戶接口

獲取用戶

/*
 * 用戶  /user/specificUser
 * */
@RequiresAuthentication
@PostMapping("/specificUser/{userId}")
public Result specificUser(@PathVariable(name = "userId") Long userId) {
    // 根據用戶id獲取 用戶實體的map集合
    Map<Object, Object> map = redisUtils.hGetAll(String.valueOf(userId));
    User user = MapUtils.mapToEntity(map, User.class);
    return Result.success("用戶數據", user);
}

博客接口

博客包含多個評論,評論只能屬於一個博客,評論實體類中需要博客id

添加博客

/*
 * 添加博客  /user/addBlog
 * */
@RequiresAuthentication
@PostMapping("/addBlog")
public Result addBlog(@Validated @RequestBody Blog blog) {
    blog.setGmtCreate(dateUtils.getNowDate());
    // 保存到數據庫
    blogService.save(blog);
    // 連接數據庫,獲取博客數據
    QueryWrapper<Blog> wrapper = new QueryWrapper<>();
    wrapper.eq("user_id", blog.getUserId());
    wrapper.eq("blog_name", blog.getBlogName());
    Blog redisBlog = blogService.getOne(wrapper);
    // 前綴名,防止命名重復
    String str = "blog_";
    String blogId = String.valueOf(redisBlog.getBlogId());
    // 保存博客實體,名blogId哈希表
    Map<String, Object> map = MapUtils.entityToMap(redisBlog);
    redisUtils.hPutAll(blogId, map);
    // 保存博客所屬的用戶,名blog_userId的list集合,元素blogId
    String str_id = str + redisBlog.getUserId().toString();
    redisUtils.lLeftPush(str_id, blogId);
    // 保存博客分類,名blog_blogCategory的list集合,元素blogId
    String str_category = str + redisBlog.getBlogCategory();
    redisUtils.lLeftPush(str_category, blogId);
    // 在Redis中獲取所有的博客分類blogCategory的list集合
    List<Object> allBlogCategoryList = redisUtils.lRange("blogCategory", 0, -1);
    int size = 0;
    for (Object list : allBlogCategoryList) {
        if (list.equals(redisBlog.getBlogCategory())) {
            size = size + 1;
        }
    }
    // 保存所有的博客分類,名blogCategory的list集合,元素blogCategory
    if (size == 0) {
        redisUtils.lLeftPush("blogCategory", redisBlog.getBlogCategory());
    }
    // 保存所有博客,名blog的list集合,元素blogId
    redisUtils.lLeftPush("blog", blogId);
    return Result.success("添加博客", redisBlog);
}

獲取博客

  • 所有博客
/*
 * 所有博客  /blogs
 * */
@PostMapping("/blogs")
public Result blogs() {
    // 在Redis中獲取所有博客blog的list集合
    List<Object> allList = redisUtils.lRange("blog", 0, -1);
    // 定義返回數據的List<Blog>集合
    List<Blog> allBlogList = new ArrayList<>();
    // foreach循環遍歷allList集合的每一個博客id
    for (Object list : allList) {
        // 根據博客id獲取,博客實體的map集合  插入返回的allBlogList集合
        allBlogList.add(MapUtils.mapToEntity(redisUtils.hGetAll((String) list), Blog.class));
    }
    return Result.success("所有博客", allBlogList);
}
  • 所有的博客分類
/*
 * 所有的博客分類  /allBlogCategory
 * */
@PostMapping("/allBlogCategory")
public Result allBlogCategory() {
    // 在Redis中獲取所有的博客分類blogCategory的list集合
    List<Object> allBlogCategoryList = redisUtils.lRange("blogCategory", 0, -1);
    return Result.success("所有的博客分類", allBlogCategoryList);
}
  • 博客分類
/*
 * 博客分類  /blogCategory
 * */
@PostMapping("/blogCategory/{blogCategory}")
public Result blogCategory(@PathVariable(name = "blogCategory") String blogCategory) {
    // 在Redis中獲取str_category的list集合
    String str_category = "blog_" + blogCategory;
    List<Object> allList = redisUtils.lRange(str_category, 0, -1);
    // 定義返回數據的List<Blog>集合
    List<Blog> blogCategoryList = new ArrayList<>();
    // foreach循環遍歷allList集合的每一個博客id
    for (Object list : allList) {
        // 根據博客id獲取,博客實體的map集合 插入返回的blogCategoryList集合
        blogCategoryList.add(MapUtils.mapToEntity(redisUtils.hGetAll((String) list), Blog.class));
    }
    return Result.success("博客分類", blogCategoryList);
}
  • 用戶博客
/*
 * 用戶博客  /user/userBlog
 * */
@RequiresAuthentication
@PostMapping("/userBlog")
public Result userBlog(@RequestBody String userId) {
    // 在Redis中獲取用戶博客blog_userId的list集合
    String str_id = "blog_" + userId;
    List<Object> allList = redisUtils.lRange(str_id, 0, -1);
    // 定義返回數據的List<Blog>集合
    List<Blog> userBlogList = new ArrayList<>();
    // foreach循環遍歷allList集合的每一個博客id
    for (Object list : allList) {
        // 根據博客id獲取 博客實體的map集合 插入返回的userBlogList集合
        userBlogList.add(MapUtils.mapToEntity(redisUtils.hGetAll((String) list), Blog.class));
    }
    return Result.success("用戶博客", userBlogList);
}
  • 具體博客
/*
 * 具體博客  /specificBlog
 * */
@PostMapping("/specificBlog/{blogId}")
public Result specificBlog(@PathVariable(name = "blogId") Long blogId) {
    // 博客閱讀量自增
    redisUtils.hIncrBy(String.valueOf(blogId), "blogNum", 1);
    // 根據博客id獲取,博客實體的map集合
    Map<Object, Object> map = redisUtils.hGetAll(String.valueOf(blogId));
    Blog blog = MapUtils.mapToEntity(map, Blog.class);
    // 博客閱讀量是100的倍數,更新數據庫
    if ((Integer) redisUtils.hGet(String.valueOf(blogId), "blogNum") % 100 == 0) {
        blogService.updateById(blog);
    }
    return Result.success("具體博客", blog);
}

修改博客

/*
 * 修改博客  /user/modifyBlog
 * */
@RequiresAuthentication
@PostMapping("/modifyBlog")
public Result modifyBlog(@Validated @RequestBody Blog blog) {
    String blogId = String.valueOf(blog.getBlogId());
    // 先刪除博客實體map集合中的blogName blogCategory blogContent字段
    redisUtils.hDelete(blogId, "blogName");
    redisUtils.hDelete(blogId, "blogCategory");
    redisUtils.hDelete(blogId, "blogContent");
    // 當map集合中的blogName字段不存在,再添加blogName blogCategory blogContent字段和修改的值,否則添加失敗
    redisUtils.hPutIfAbsent(blogId, "blogName", blog.getBlogName());
    redisUtils.hPutIfAbsent(blogId, "blogCategory", blog.getBlogCategory());
    redisUtils.hPutIfAbsent(blogId, "blogContent", blog.getBlogContent());
    // 更新數據庫
    blogService.updateById(blog);
    return Result.success("修改博客");
}

評論接口

添加評論

/*
 * 添加評論  /user/addComment
 * */
@RequiresAuthentication
@PostMapping("/addComment")
public Result addComment(@Validated @RequestBody Comment comment) {
    comment.setGmtCreate(dateUtils.getNowDate());
    // 保存到數據庫
    commentService.save(comment);
    // 連接數據庫,獲取評論數據
    QueryWrapper<Comment> wrapper = new QueryWrapper<>();
    wrapper.eq("user_id", comment.getUserId());
    wrapper.eq("blog_id", comment.getBlogId());
    Comment redisComment = commentService.getOne(wrapper);
    // 前綴名,防止命名重復
    String str = "comment_";
    String commentId = String.valueOf(redisComment.getCommentId());
    // 保存評論實體,名commentId哈希表
    Map<String, Object> map = MapUtils.entityToMap(redisComment);
    redisUtils.hPutAll(commentId, map);
    // 保存評論所屬的用戶名,comment_userId的list集合,元素commentId
    String str_id = str + redisComment.getUserId().toString();
    redisUtils.lLeftPush(str_id, commentId);
    // 保存評論所屬的博客,名comment_blogId的list集合,元素commentId
    String str_blogId = str + redisComment.getBlogId().toString();
    redisUtils.lLeftPush(str_blogId, commentId);
    // 保存評論中用戶id數據 名user_blogId的list集合 元素userId
    String str_user_blogId = "user_" + redisComment.getBlogId().toString();
    redisUtils.lLeftPush(str_user_blogId, String.valueOf(redisComment.getUserId()));
    return Result.success("添加評論", redisComment);
}

獲取評論

  • 博客下的評論
/*
 * 博客下的評論  /partComment
 * */
@PostMapping("/partComment")
public Result partComment(@RequestBody String blogId) {
    // 在Redis中獲取名comment_blogId的評論list集合
    String str_blogId = "comment_" + blogId;
    List<Object> partList = redisUtils.lRange(str_blogId, 0, -1);
    // 定義返回數據的List<Comment>集合
    List<Comment> partCommentList = new ArrayList<>();
    // foreach循環遍歷allList集合的每一個評論id
    for (Object list : partList) {
        // 根據評論id獲取,評論實體的map集合 插入返回的partCommentList集合
        partCommentList.add(MapUtils.mapToEntity(redisUtils.hGetAll((String) list), Comment.class));
    }
    return Result.success("博客下的評論", partCommentList);
}
  • 博客下的評論中的用戶數據
/*
 * 博客下的評論中的用戶數據  /partUser
 * */
@PostMapping("/partUser")
public Result partUser(@RequestBody String blogId) {
    // 在Redis中獲取user_blogId下的所有評論用戶id的list集合
    String str_user_blogId = "user_" + blogId;
    List<Object> partList = redisUtils.lRange(str_user_blogId, 0, -1);
    // 定義返回數據的List<User>集合
    List<User> userList = new ArrayList<>();
    // foreach循環遍歷allList集合的每一個用戶id
    for (Object list : partList) {
        // 根據用戶id獲取,用戶實體的map集合  插入返回的partCommentList集合
        userList.add(MapUtils.mapToEntity(redisUtils.hGetAll((String) list), User.class));
    }
    return Result.success("博客下的評論中的用戶", userList);
}
  • 用戶評論
/*
 * 用戶評論  /user/userComment
 * */
@RequiresAuthentication
@PostMapping("/userComment")
public Result userComment(@RequestBody String userId) {
    // 在Redis中獲取用戶評論comment_userId的list集合
    String str_id = "comment_" + userId;
    List<Object> allList = redisUtils.lRange(str_id, 0, -1);
    // 定義返回數據的List<Comment>集合
    List<Comment> userCommentList = new ArrayList<>();
    // foreach循環遍歷allList集合的每一個評論id
    for (Object list : allList) {
        // 根據評論id獲取評論實體的map集合  插入返回的userCommentList集合
        userCommentList.add(MapUtils.mapToEntity(redisUtils.hGetAll((String) list), Comment.class));
    }
    return Result.success("用戶評論", userCommentList);
}

刪除評論

/*
 * 刪除評論  /user/deleteComment
 * */
@RequiresAuthentication
@PostMapping("/deleteComment")
public Result deleteComment(@RequestBody String commentId) {
    Map<Object, Object> map = redisUtils.hGetAll(commentId);
    Comment comment = MapUtils.mapToEntity(map, Comment.class);
    // 在Redis中獲取評論實體commentId哈希表的所有字段,返回set集合
    Set<Object> commentSet = redisUtils.hKeys(commentId);
    // foreach循環遍歷set集合的所有字段
    for (Object set : commentSet) {
        // 逐個刪除評論實體commentId哈希表的所有字段
        redisUtils.hDelete(commentId, set);
    }
    String str = "comment_";
    // 刪除在評論中用戶id數據的list集合的記錄
    String str_id = str + comment.getUserId().toString();
    redisUtils.lRemove(str_id, 1, commentId);
    // 刪除評論所屬博客的記錄
    String str_blogId = str + comment.getBlogId().toString();
    redisUtils.lRemove(str_blogId, 1, commentId);
    // 刪除在評論中用戶id數據的list集合的記錄
    String str_user_blogId = "user_" + comment.getBlogId().toString();
    redisUtils.lRemove(str_user_blogId, 1, String.valueOf(comment.getUserId()));
    // 刪除數據庫中評論的記錄
    commentService.removeById(comment.getCommentId());
    return Result.success("刪除評論");
}

blogs_vue前端項目

  • vue
  • element-ui
  • axios
  • mavon-editor
  • markdown-it
  • github-markdown-css


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM