2021-05-27

前后端分离博客(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

配置文件(properties)

  • application.properties
# Windows环境spring.profiles.active=dev# Linux环境# spring.profiles.active=prod
  • application-dev.properties
# 连接数据库spring.datasource.username=rootspring.datasource.password=123456spring.datasource.url=jdbc:mysql://localhost:3306/blogs?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=truespring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# 配置Redisspring.redis.host=127.0.0.1spring.redis.port=6379# 邮件发送设置spring.mail.host=smtp.qq.comspring.mail.username=2991596029@qq.comspring.mail.password=fwlohstwgimddefispring.mail.default-encoding=UTF-8spring.mail.properties.mail.smtp.auth=truespring.mail.properties.mail.smtp.starttls.enable=truespring.mail.properties.mail.smtp.starttls.required=truespring.mail.properties.mail.smtp.ssl.enable=truespring.mail.properties.mail.smtp.socketFactory.port=465#上传文件大小spring.servlet.multipart.max-file-size=10MBspring.servlet.multipart.max-request-size=10MB# bean允许重命名spring.main.allow-bean-definition-overriding=true# 配置日志mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImplmybatis-plus.global-config.db-config.logic-delete-value=1mybatis-plus.global-config.db-config.logic-not-delete-value=0#默认编码配置server.servlet.encoding.charset=UTF-8server.servlet.encoding.force=trueserver.servlet.encoding.enabled=trueserver.tomcat.uri-encoding=UTF-8# 接口设置server.port=8085
  • application-prod.properties
# 连接数据库spring.datasource.username=rootspring.datasource.password=****spring.datasource.url=jdbc:mysql://localhost:3306/blogs?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=truespring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# 配置Redisspring.redis.host=127.0.0.1spring.redis.port=6379# 邮件发送设置spring.mail.host=smtp.qq.comspring.mail.username=2991596029@qq.comspring.mail.password=fwlohstwgimddefispring.mail.default-encoding=UTF-8spring.mail.properties.mail.smtp.auth=truespring.mail.properties.mail.smtp.starttls.enable=truespring.mail.properties.mail.smtp.starttls.required=truespring.mail.properties.mail.smtp.ssl.enable=truespring.mail.properties.mail.smtp.socketFactory.port=465#上传文件大小spring.servlet.multipart.max-file-size=10MBspring.servlet.multipart.max-request-size=10MB# bean允许重命名spring.main.allow-bean-definition-overriding=true# 配置日志mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImplmybatis-plus.global-config.db-config.logic-delete-value=1mybatis-plus.global-config.db-config.logic-not-delete-value=0#默认编码配置server.servlet.encoding.charset=UTF-8server.servlet.encoding.force=trueserver.servlet.encoding.enabled=trueserver.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;@Configurationpublic 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.faster

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

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

整合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;/** * 数据统一结果返回 */@Datapublic 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.faster

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;@Componentpublic 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.*;@Configurationpublic 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;/** * 密码登录验证 */@Componentpublic 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 */@Datapublic 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过滤器 */@Componentpublic 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@Servicepublic 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@Componentpublic 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加密工具类 */@Componentpublic 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;/** * 发送邮件实体类 */ @Datapublic 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;/** * 实现发送邮件接口 * 发送验证码邮件 */@Componentpublic 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;/** * 实现发送邮件接口 * 发送用户修改密码跳转邮件 */@Componentpublic 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'>     + "<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;/** * 实现发送邮件接口 * 发送用户修改密码时提醒邮件 */@Componentpublic 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;/** * 实现发送邮件接口 * 发送联系邮件 */@Componentpublic 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;/** * 发送邮件工厂类 */@Componentpublic 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








原文转载:http://www.shaoqun.com/a/761808.html

跨境电商:https://www.ikjzd.com/

ask me:https://www.ikjzd.com/w/2459

黄劲:https://www.ikjzd.com/w/2426


blogs项目blogs项目主要由blogs_springboot后端项目与blogs_vue前端项目组成SpringBootRedisMyBatis-PlusShiroSwaggerLombokJwtVueElementUIAxiosMavon-EditorMarkDown-ItGitHub-MarkDown-CSSMySQL数据库DROPTABLEIFEXISTS`blog`;CREATETA
笨鸟海淘:https://www.ikjzd.com/w/1550
patents:https://www.ikjzd.com/w/857
凹凸曼:https://www.ikjzd.com/w/1392
亚马逊线上工具大起底(六):选品工具上篇:https://www.ikjzd.com/articles/2098
亚马逊在2018的"所作所为",给卖家带来哪些启示?:https://www.ikjzd.com/articles/13295
RevSeller:https://www.ikjzd.com/w/659

No comments:

Post a Comment