blogs项目
- blogs项目是myblog项目演进与完善的迭代项目,设计框架由springboot+thymeleaf+mybatis-plus+shiro+swagger改为springboot+vue+mybatis-plus+redis+shiro+swagger的前后端分离项目
- blogs项目主要由blogs_springboot后端项目与blogs_vue前端项目组成
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("删除评论");
}