1分钟学会SpringBoot2知识点,让你35岁不再失业(四)
第十五节、springboot2整合redis
1、pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>redis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>redis</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-configuration-processor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、application.yaml
#jedisPool配置开始
# 连接池中的最大空闲连接
redis:
maxIdle: 30
minIdle: 1
maxTotal: 100
maxWait: 10000
host: 172.0.0.1
port: 6379
timeout: 10000
password: 123456
3、RedisPoolConfig.java
package com.example.redis.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @author 刘阳洋
* @date 2020/4/29 14:42
*/
@ConfigurationProperties(prefix = "redis")
@Component
public class RedisPoolConfig {
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private int port;
@Value("${redis.maxTotal}")
private int maxTotal;
@Value("${redis.maxIdle}")
private int maxIdle;
@Value("${redis.minIdle}")
private int minIdle;
@Value("${redis.timeout}")
private int timeout;
@Value("${redis.maxWait}")
private long maxWait;
@Value("${redis.password}")
private String password;
@Bean
public JedisPool init(){
JedisPoolConfig jedisPoolConfig=new JedisPoolConfig();
//连接池阻塞最大等待时间
jedisPoolConfig.setMaxWaitMillis(maxWait);
//连接池最大空闲连接数
jedisPoolConfig.setMaxIdle(maxIdle);
//连接池最小空闲连接数
jedisPoolConfig.setMinIdle(maxIdle);
//连接池最大链接数
jedisPoolConfig.setMaxTotal(maxTotal);
//连接池最小链接数
jedisPoolConfig.setMinIdle(minIdle);
JedisPool jedisPool=new JedisPool(jedisPoolConfig,host,port,timeout,password,0);
System.out.println("password"+password);
//JedisPool jedisPool=new JedisPool(jedisPoolConfig,"127.0.0.1",6379,10000,"foobared",0);
return jedisPool;
}
}
4、RedisService.java
package com.example.redis.service;
/**
* @author 刘阳洋
* @date 2020/4/29 14:59
*/
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Service
public class RedisService {
@Resource
private JedisPool jedisPool;
//***************************对 key 常用操作**************************
/**
* 判断key是否存在
* @param key
* @return boolean true 存在 false 不存在
* @throws
*/
public boolean exists(String key){
Jedis jedis = null;
boolean result;
try {
jedis=jedisPool.getResource();
result=jedis.exists(key);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 删除指定的key,也可以传入一个包含key的数组
* @param keys
* @return java.lang.Long 返回删除成功的个数
* @throws
*/
public Long del(String... keys) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result= jedis.del(keys);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* KEYS pattern 通配符模式匹配
* 查找所有符合给定模式 pattern 的 key 。
* KEYS * 匹配数据库中所有 key 。
* KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
* KEYS h*llo 匹配 hllo 和 heeeeello 等。
* KEYS 的速度非常快,但在一个大的数据库中使用它仍然可能造成性能问题,如果你需要从一个数据集中查找特定的 key ,
* 你最好还是用 Redis 的集合结构(set)来代替。
* @param pattern
* @return java.util.Set<java.lang.String>
* @throws
*/
public Set<String> keys(String pattern) {
Jedis jedis = null;
Set<String> result ;
try {
jedis = jedisPool.getResource();
result = jedis.keys(pattern);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 设置过期时间
* @param key
* @param seconds
* @return Long 1:表示设置成功,0:设置失败
* @throws
*/
public Long expire(String key,int seconds){
Jedis jedis=null;
Long result=0L;
try {
jedis=jedisPool.getResource();
if(seconds>0){
result=jedis.expire(key,seconds);
}
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 移除给定 key 的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(永不过期的 key )
* @param key
* @return java.lang.Long 当生存时间移除成功时,返回 1 .如果 key 不存在或 key 没有设置生存时间,返回 0
* @throws
*/
public Long persist(String key) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result=jedis.persist(key);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 以秒为单位,返回给定 key 的剩余生存时间
* @param key
* @return java.lang.Long 当 key 不存在时,返回 -2 。当 key 存在但没有设置剩余生存时间时,返回 -1 。否则,以秒为单位,返回 key
* * 的剩余生存时间
* @throws
*/
public Long ttl(String key) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result=jedis.ttl(key);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
//**************String数据类型********************
/**
* 获取指定Key的Value。如果与该Key关联的Value不是string类型,Redis将抛出异常,
* 因为GET命令只能用于获取string Value,如果该Key不存在,返回null
* @param key
* @return 成功返回value 失败返回null
* @throws
*/
public String get(String key) {
Jedis jedis = null;
String value ;
try {
jedis = jedisPool.getResource();
value = jedis.get(key);
} finally {
if (jedis!=null){
jedis.close();
}
}
return value;
}
/**
* 设定该Key持有指定的字符串Value,如果该Key已经存在,则覆盖其原有值。总是返回"OK"。
* @param key
* @param value
* @param expire 过期时间秒
* @return void
* @throws
*/
public String set(String key, String value,int expire) {
Jedis jedis = null;
String result;
try {
jedis = jedisPool.getResource();
result=jedis.set(key,value);
if(expire>0){
jedis.expire(key, expire);
}
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 加锁操作:jedis.set(key,value,"NX","EX",timeOut)【保证加锁的原子操作】
* @param key key就是redis的key值作为锁的标识,
* @param value value在这里作为客户端的标识,
* @param nxxx NX:只有这个key不存才的时候才会进行操作,if not exists;
* @param nxxx XX:只有这个key存才的时候才会进行操作,if it already exist;
* @param expx EX:设置key的过期时间为秒,具体时间由第5个参数决定
* @param expx PX:设置key的过期时间为毫秒,具体时间由第5个参数决定
* @param time 通过timeOut设置过期时间保证不会出现死锁【避免死锁】
* @return java.lang.String 成功OK不成功null
* @throws
*/
public String set(String key, String value, String nxxx, String expx, long time){
Jedis jedis=null;
String result;
try {
jedis=jedisPool.getResource();
result = jedis.set(key, value, nxxx, expx, time);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 将指定Key的Value原子性的递增1。如果该Key不存在,其初始值为0,在incr之后其值为1。
* 如果Value的值不能转换为整型值,如Hi,该操作将执行失败并抛出相应的异常。
* 注意:该操作的取值范围是64位有符号整型;返回递增后的Value值。
* @param key
* @return java.lang.Long 加值后的结果
* @throws
*/
public Long incr(String key) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result = jedis.incr(key);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 将指定Key的Value原子性的递增1。如果该Key不存在,其初始值为0,在decr之后其值为-1。
* 如果Value的值不能转换为整型值,如Hi,该操作将执行失败并抛出相应的异常。
* 注意:该操作的取值范围是64位有符号整型;返回递减后的Value值。
* @Author: 小霍
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.Long 加值后的结果
* @throws
*/
public Long decr(String key) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result = jedis.decr(key);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
//******************hash数据类型*********************
/**
* 通过key 和 field 获取指定的 value
* @param key
* @param field
* @return java.lang.String
* @throws
*/
public String hget(String key, String field) {
Jedis jedis = null;
String result ;
try {
jedis = jedisPool.getResource();
result = jedis.hget(key, field);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 为指定的Key设定Field/Value对,如果Key不存在,该命令将创建新Key以用于存储参数中的Field/Value对,
* 如果参数中的Field在该Key中已经存在,则用新值覆盖其原有值。
* 返回1表示新的Field被设置了新值,0表示Field已经存在,用新值覆盖原有值。
* @param key
* @param field
* @param value
* @return java.lang.Long
* @throws
*/
public Long hset(String key, String field, String value) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result = jedis.hset(key, field, value);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 判断指定Key中的指定Field是否存在,返回true表示存在,false表示参数中的Field或Key不存在。
* @param key
* @param field
* @return java.lang.Boolean
* @throws
*/
public Boolean hexists(String key, String field) {
Jedis jedis = null;
Boolean result;
try {
jedis = jedisPool.getResource();
result = jedis.hexists(key, field);
}finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 从指定Key的Hashes Value中删除参数中指定的多个字段,如果不存在的字段将被忽略,
* 返回实际删除的Field数量。如果Key不存在,则将其视为空Hashes,并返回0。1
* @param key
* @param fields
* @return java.lang.Long
* @throws
*/
public Long hdel(String key, String... fields) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result = jedis.hdel(key, fields);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 通过key获取所有的field和value
* @param key
* @return java.util.Map<java.lang.String,java.lang.String>
* @throws
*/
public Map<String, String> hgetall(String key) {
Jedis jedis = null;
Map<String, String> result;
try {
jedis = jedisPool.getResource();
result = jedis.hgetAll(key);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 逐对依次设置参数中给出的Field/Value对。如果其中某个Field已经存在,则用新值覆盖原有值。
* 如果Key不存在,则创建新Key,同时设定参数中的Field/Value。
* @param key
* @param hash
* @return java.lang.String
* @throws
*/
public String hmset(String key, Map<String, String> hash) {
Jedis jedis = null;
String result;
try {
jedis = jedisPool.getResource();
result = jedis.hmset(key, hash);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 对应key的字段自增相应的值
* @param key
* @param field
* @param increment
* @return java.lang.Long
* @throws
*/
public Long hIncrBy(String key,String field,long increment){
Jedis jedis=null;
Long result;
try {
jedis=jedisPool.getResource();
return jedis.hincrBy(key, field, increment);
} finally {
if(jedis!=null){
jedis.close();
}
}
}
//***************List数据类型***************
/**
* 向列表左边添加元素。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的头部插入。
* 如果该键的Value不是链表类型,该命令将将会抛出相关异常。操作成功则返回插入后链表中元素的数量。
* @param key
* @param strs 可以使一个string 也可以使string数组
* @return java.lang.Long 返回操作的value个数
* @throws
*/
public Long lpush(String key, String... strs) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result = jedis.lpush(key, strs);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 向列表右边添加元素。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的尾部插入。
* 如果该键的Value不是链表类型,该命令将将会抛出相关异常。操作成功则返回插入后链表中元素的数量。1
* @param key
* @param strs 可以使一个string 也可以使string数组
* @return java.lang.Long 返回操作的value个数
* @throws
*/
public Long rpush(String key, String... strs) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result = jedis.rpush(key, strs);
}finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 返回并弹出指定Key关联的链表中的第一个元素,即头部元素。如果该Key不存在,
* 返回nil。LPOP命令执行两步操作:第一步是将列表左边的元素从列表中移除,第二步是返回被移除的元素值。
* @Author: 小霍
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.String
* @throws
*/
public String lpop(String key) {
Jedis jedis = null;
String result ;
try {
jedis = jedisPool.getResource();
result = jedis.lpop(key);
}finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 返回并弹出指定Key关联的链表中的最后一个元素,即头部元素。如果该Key不存在,返回nil。
* RPOP命令执行两步操作:第一步是将列表右边的元素从列表中移除,第二步是返回被移除的元素值。0.1
* @param key
* @return java.lang.String
* @throws
*/
public String rpop(String key) {
Jedis jedis = null;
String result ;
try {
jedis = jedisPool.getResource();
result = jedis.rpop(key);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
*该命令的参数start和end都是0-based。即0表示链表头部(leftmost)的第一个元素。
* 其中start的值也可以为负值,-1将表示链表中的最后一个元素,即尾部元素,-2表示倒数第二个并以此类推。
* 该命令在获取元素时,start和end位置上的元素也会被取出。如果start的值大于链表中元素的数量,
* 空链表将会被返回。如果end的值大于元素的数量,该命令则获取从start(包括start)开始,链表中剩余的所有元素。
* 注:Redis的列表起始索引为0。显然,LRANGE numbers 0 -1 可以获取列表中的所有元素。返回指定范围内元素的列表。
* @param key
* @param start
* @param end
* @return java.util.List<java.lang.String>
* @throws
*/
public List<String> lrange(String key, long start, long end) {
Jedis jedis = null;
List<String> result;
try {
jedis = jedisPool.getResource();
result = jedis.lrange(key, start, end);
}finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 该命令将返回链表中指定位置(index)的元素,index是0-based,表示从头部位置开始第index的元素,
* 如果index为-1,表示尾部元素。如果与该Key关联的不是链表,该命令将返回相关的错误信息。 如果超出index返回这返回nil。
* @param key
* @param index
* @return java.lang.String
* @throws
*/
public String lindex(String key, long index) {
Jedis jedis = null;
String result;
try {
jedis = jedisPool.getResource();
result = jedis.lindex(key, index);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
//***************Set数据类型*************
/**
* 如果在插入的过程用,参数中有的成员在Set中已经存在,该成员将被忽略,而其它成员仍将会被正常插入。
* 如果执行该命令之前,该Key并不存在,该命令将会创建一个新的Set,此后再将参数中的成员陆续插入。返回实际插入的成员数量。
* @param key
* @param members 可以是一个String 也可以是一个String数组
* @return java.lang.Long 添加成功的个数
* @throws
*/
public Long sadd(String key, String... members) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result = jedis.sadd(key, members);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 判断参数中指定成员是否已经存在于与Key相关联的Set集合中。返回1表示已经存在,0表示不存在,或该Key本身并不存在。
* @param key
* @param member
* @return java.lang.Boolean
* @throws
*/
public Boolean sismember(String key, String member) {
Jedis jedis = null;
Boolean result;
try {
jedis = jedisPool.getResource();
result = jedis.sismember(key, member);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 通过key获取set中所有的value
* @param key
* @return java.util.Set<java.lang.String>
* @throws
*/
public Set<String> smembers(String key) {
Jedis jedis = null;
Set<String> result ;
try {
jedis = jedisPool.getResource();
result = jedis.smembers(key);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
//**********Sorted Set 数据类型********************
/**
*添加参数中指定的所有成员及其分数到指定key的Sorted Set中,在该命令中我们可以指定多组score/member作为参数。
* 如果在添加时参数中的某一成员已经存在,该命令将更新此成员的分数为新值,同时再将该成员基于新值重新排序。
* 如果键不存在,该命令将为该键创建一个新的Sorted Set Value,并将score/member对插入其中。
* 如果该键已经存在,但是与其关联的Value不是Sorted Set类型,相关的错误信息将被返回。添加成功返回实际插入的成员数量。
* @param key
* @param score
* @param member
* @return java.lang.Long
* @throws
*/
public Long zadd(String key, double score, String member) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result = jedis.zadd(key, score, member);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 返回Sorted Set中的成员数量,如果该Key不存在,返回0。
* @param key
* @return java.lang.Long
* @throws
*/
public Long zcard(String key) {
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result = jedis.zcard(key);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 该命令将为指定Key中的指定成员增加指定的分数。如果成员不存在,该命令将添加该成员并假设其初始分数为0,
* 此后再将其分数加上increment。如果Key不存在,该命令将创建该Key及其关联的Sorted Set,
* 并包含参数指定的成员,其分数为increment参数。如果与该Key关联的不是Sorted Set类型,
* 相关的错误信息将被返回。如果不报错则以串形式表示的新分数。
* @param key
* @param score
* @param member
* @return java.lang.Double
* @throws
*/
public Double zincrby(String key, double score, String member) {
Jedis jedis = null;
Double result;
try {
jedis = jedisPool.getResource();
result = jedis.zincrby(key, score, member);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 如果该成员存在,以字符串的形式返回其分数,否则返回null
* @param key
* @param member
* @return java.lang.Double
* @throws
*/
public Double zscore(String key, String member) {
Jedis jedis = null;
Double result;
try {
jedis = jedisPool.getResource();
result = jedis.zscore(key, member);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 该命令返回顺序在参数start和stop指定范围内的成员,这里start和stop参数都是0-based,即0表示第一个成员,-1表示最后一个成员。如果start大于该Sorted
* Set中的最大索引值,或start > stop,此时一个空集合将被返回。如果stop大于最大索引值,
* 该命令将返回从start到集合的最后一个成员。如果命令中带有可选参数WITHSCORES选项,
* 该命令在返回的结果中将包含每个成员的分数值,如value1,score1,value2,score2...。
* @param key
* @param min
* @param max
* @return java.util.Set<java.lang.String> 指定区间内的有序集成员的列表。
* @throws
*/
public Set<String> zrange(String key, long start, long stop) {
Jedis jedis = null;
Set<String> result;
try {
jedis = jedisPool.getResource();
result= jedis.zrange(key, start, stop);
} finally {
if (jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 该命令的功能和ZRANGE基本相同,唯一的差别在于该命令是通过反向排序获取指定位置的成员,
* 即从高到低的顺序。如果成员具有相同的分数,则按降序字典顺序排序。
* @param key
* @param start
* @param end
* @return java.util.Set<java.lang.String>
* @throws
*/
public Set<String> zrevrange(String key, long start, long end) {
Jedis jedis = null;
Set<String> result;
try {
jedis = jedisPool.getResource();
result = jedis.zrevrange(key, start, end);
}finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
/**
* 该命令除了排序方式是基于从高到低的分数排序之外,其它功能和参数含义均与ZRANGEBYSCORE相同。
* 需要注意的是该命令中的min和max参数的顺序和ZRANGEBYSCORE命令是相反的。
* @param key
* @param max
* @param min
* @return java.util.Set<java.lang.String>
* @throws
*/
public Set<String> zrevrangeByScore(String key, double max, double min) {
Jedis jedis = null;
Set<String> result ;
try {
jedis = jedisPool.getResource();
result = jedis.zrevrangeByScore(key, max, min);
} finally {
if(jedis!=null){
jedis.close();
}
}
return result;
}
}
5、启动类
package com.example.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
@SpringBootApplication
public class RedisApplication {
public static void main(String[] args) {
SpringApplication.run(RedisApplication.class, args);
System.out.println("启动成功");
}
}
6、测试
package com.example.redis;
import com.example.redis.service.RedisService;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
@RunWith(SpringRunner.class)
@SpringBootTest
class RedisApplicationTests {
@Autowired
private RedisService redisService;
@Autowired
private JedisPool jedisPool;
@Test
public void testRedis(){
// String password="foobared";
// String localhost="127.0.0.1";
// int port=6379;
// Jedis jedis=new Jedis(localhost,port);
// jedis.auth(password);
// System.out.println(jedis.get("name"));
System.out.println("结果为:"+jedisPool.getResource());
System.out.println("结果为:"+redisService.get("name"));
}
}
第十六节、redis分布式session共享
1、以往我们的项目都是部署在单台服务器运行,因为客户的所有请求都是由唯一服务器来处理,sessionId 保存在这台服务器上是没有问
题的。但是当项目同时部署在多台服务器上时,就会出现 sessionId 共享问题。
现在有两台服务器同时运行,分别是 Server A 和 Server B,通过 nginx 配置了负载均衡,客户端的请求会被随机分配到两台服务器上
进行处理。假设客户现在有第一次请求(登录请求)被分配到 Server A 进行处理,Server A 接受到请求之后会生成 sessionId 并且保
存到内存当中,然后返回给客户(浏览器),浏览器会把 sessionId 保存到 cookie 中,第一次请求完成。如果之后每一次请求还是由
Server A 来处理当然一切正常,但是一旦出现意外(比如 Server A 宕机)或者nginx 采用了轮询、weight方式负载均衡,请求被分配
到 Server B进行处理,这时候 Server B 拿到客户请求的 sessionId 是由 Server A 生成的,两边对不上啊 ! 于是客户会发现,本来还
用的好好的,怎么会突然跳到登录页需要重新登录了呢!!!???
那我们该怎么去解决呢?
既然问题的根源出在 sessionId 无法共享上面,那么是不是让 sessionId 可以在多台服务器之间共享就可以了?换个思路,即把
sessionId 保存到数据库即可(最终选择redis,因为会比较快!),验证时不再从当前服务器获取 sessionId 改从 redis 中获取即可!
实现思路:
- 登录页面提交用户名密码。
- 登录成功后生成token。Token相当于原来的sessionid,字符串,可以使用uuid。
- 把用户信息保存到redis。Key就是token,value就是userId。
- 设置key的过期时间。模拟Session的过期时间。一般一个小时。
- 拦截器拦截请求校验 sessionId。
2、登录成功 生成 sessionId 存入 redis
/**
* 用户登录
* @param vo
* @return
*/
@Override
public LoginRespVO login(LoginReqVO vo) {
SysUser sysUser = sysUserDao.selectByUsername(vo.getUsername());
if (sysUser == null) {
throw new BusinessException(3001, "不存在该用户,请先注册");
}
if (sysUser.getStatus() == 2) {
throw new BusinessException(3002, "该账号已被禁用请联系系统管理员");
}
if (!PasswordUtils.matches(sysUser.getSalt(), vo.getPassword(), sysUser.getPassword())) {
throw new BusinessException(3003, "用户名密码不匹配");
}
String token = UUID.randomUUID().toString();
LoginRespVO loginRespVO = new LoginRespVO();
loginRespVO.setUserId(sysUser.getId());
loginRespVO.setToken(token);
//分布式session凭证存入redis 60分钟失效
redisService.set(token,sysUser.getId(),60, TimeUnit.MINUTES);
return loginRespVO;
}
3、SessionInterceptor 拦截器校验 sessionId
package com.example.distributed.demo.interceptor;
import com.example.distributed.demo.exception.BusinessException;
import com.example.distributed.demo.service.impl.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 16:52
*/
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private RedisService redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token=request.getHeader("token");
if(StringUtils.isEmpty(token)){
throw new BusinessException(3004,"用户凭证不能为空,请重新登录");
}else {
if(!redisService.hasKey(token)){
throw new BusinessException(3005,"用户凭证无效,请重新登录");
}
String userId= (String) redisService.get(token);
if(redisService.hasKey(userId)&&!token.equals(redisService.get(userId))){
throw new BusinessException(3006,"您的账号已经在异地登录,请重新登录");
}
}
return true;
}
}
4、配置拦截器策略
package com.example.distributed.demo.config;
import com.example.distributed.demo.interceptor.TokenInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 16:51
*/
//配置拦截器策略
@Configuration
public class WebAppConfig implements WebMvcConfigurer {
@Bean
public TokenInterceptor tokenInterceptor(){
return new TokenInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor()).addPathPatterns("/api/**").excludePathPatterns("/api/user/login","/api/user/register","/api/user/code/*");
}
}
第十七节、redis 异地登录提醒下线
1、业务分析
最近接到产品提的一个需求说,一个账号同时只能在一个地方登录,如果在其他地方登录则提示已在别处登录,同时,同一浏览器同
时只能登录一个用户。
思路:
- 登录页面提交用户名密码。
- 登录成功后生成token。Token相当于原来的 sessionid,字符串,可以使用uuid。
- 把用户信息保存到redis。Key就是token,value就是userId。
- 设置key的过期时间。模拟Session的过期时间。一般一个小时。
- 标记 Token把Token存入redis,key为 userId,value 就是 Token过期时间和 key 为 Token 的过期时间一致
- 拦截器拦截请求校验 token。
- 获取 userId 后再去比较 header 携带的token和redis标记的token是否一致,不一致则提示用户已经异地登录。
2、代码实现
/**
* 用户登录
* @param vo
* @return
*/
@Override
public LoginRespVO login(LoginReqVO vo) {
SysUser sysUser = sysUserDao.selectByUsername(vo.getUsername());
if (sysUser == null) {
throw new BusinessException(3001, "不存在该用户,请先注册");
}
if (sysUser.getStatus() == 2) {
throw new BusinessException(3002, "该账号已被禁用请联系系统管理员");
}
if (!PasswordUtils.matches(sysUser.getSalt(), vo.getPassword(), sysUser.getPassword())) {
throw new BusinessException(3003, "用户名密码不匹配");
}
String token = UUID.randomUUID().toString();
LoginRespVO loginRespVO = new LoginRespVO();
loginRespVO.setUserId(sysUser.getId());
loginRespVO.setToken(token);
//异地登录提醒下线功能
redisService.set(sysUser.getId(),token,60,TimeUnit.MINUTES);
return loginRespVO;
}
3、修改token拦截器逻辑
package com.example.distributed.demo.interceptor;
import com.example.distributed.demo.exception.BusinessException;
import com.example.distributed.demo.service.impl.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 16:52
*/
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private RedisService redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token=request.getHeader("token");
if(StringUtils.isEmpty(token)){
throw new BusinessException(3004,"用户凭证不能为空,请重新登录");
}else {
if(!redisService.hasKey(token)){
throw new BusinessException(3005,"用户凭证无效,请重新登录");
}
String userId= (String) redisService.get(token);
if(redisService.hasKey(userId)&&!token.equals(redisService.get(userId))){
throw new BusinessException(3006,"您的账号已经在异地登录,请重新登录");
}
}
return true;
}
}
第十八节、redis 注册短信验证码
1、短信验证码是所有项目必不可少的基础功能模块之一,假如突然有一天你领导给你布置的一个需求。在用户注册的时候要校验手机
号。
要求如下:
- 注册的时候校验手机号
- 每个手机号每天最多发送五条注册短信验证码
- 验证码5分钟内有效。
思路: - 发送前验证手机号是否符合要求。
- 生成短信验证码。
- 发送验证码到手机。
- 把验证码存入redis
- 标记手机号
- 注册的时候校验手机号和验证码是否正确
第十九节、 redis 计数器(订单号/特殊有规律编码/点赞数)
1、在现实开发中,经常遇到数据组或者产品给的需求,比如统计某个功能 pv、uv 数量、文章点赞数、或着有规律的编码等。
需求:
生成订单号为20191020D0000001 一天内可以生成9999999个不重复订单号(这个由我们自己设定)
实现思路:
- 特定常量+当前日期作为key(确保唯一)
- 每新生成一个订单号 incr 自增
- 编码=日期+类型+自增部分
第二十节、 redis 购物车
1、在做购物车的时候要我们要考虑到对于一个客户来说 不同规格,不同商品 ,在实际应该中我们该怎么处理呢?
需求:
- 加入购物车先得登录。
- 记录用户购物车数据。
思路:
我们在做购物车功能的时候,我们可以用 redis hash类型来做。首先前端提交购物车的时候需要先判断是否登录,需要把商品的
skuId、规格属性specificationIds (多个的id以‘,’拼接)、和商品的数量。展示的时候去redis 拿到对应购物车数据查询最新数据
redis hash 格式为
第二十一节、 redis templete 和以上功能的代码实现
1、pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.distributed</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.49</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、建表语句
CREATE TABLE `sys_user` (
`id` varchar(64) NOT NULL COMMENT '用户id' ,
`username` varchar(64) NOT NULL COMMENT '账户名称',
`salt` varchar(20) DEFAULT NULL COMMENT '加密盐值',
`password` varchar(200) NOT NULL COMMENT '用户密码密文',
`phone` varchar(11) DEFAULT NULL COMMENT '手机号码',
`dept_id` varchar(64) DEFAULT NULL COMMENT '部门id',
`real_name` varchar(64) DEFAULT NULL COMMENT '真实姓名',
`nick_name` varchar(64) DEFAULT NULL COMMENT '昵称',
`email` varchar(64) DEFAULT NULL COMMENT '邮箱',
`status` tinyint(4) DEFAULT '1' COMMENT '账户状态1正常2锁定',
`sex` tinyint(4) DEFAULT NULL COMMENT '性别1男2女',
`deleted` tinyint(4) DEFAULT '0' COMMENT '0未删除 1已删除',
`create_id` varchar(64) DEFAULT NULL COMMENT '创建人',
`update_id` varchar(64) DEFAULT NULL COMMENT '更新人',
`create_where` varchar(64) DEFAULT NULL COMMENT '创建来源1web 2android 3ios ',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NOT NULL DEFAULT '1970-01-01 08:00:01',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3、application.yaml
#File-->Settings-->File Encodings
#reids连接池配置lettuce
spring:
redis:
host: 127.0.0.1
port: 6379
password: 123456
timeout: PT10S
lettuce:
pool:
max-active: 100 #连接池最大连接数(使用负值表示没有限制) 默认 8
max-idle: 30 # 连接池中的最大空闲连接 默认 8
min-idle: 1 # 连接池中的最小空闲连接 默认 0
max-wait: PT10S #连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
#Druid数据库连接池配置
#1、监控数据库访问性能 2、详细统计线上的访问性能 3、SQL执行日志 4、扩展JDBC
jackson:
date-format: yyyy-MM-dd HH:mm:ss #如果使用字符串表示,用这行设置格式
timezone: GMT+8
serialization:
write-dates-as-timestamps: false #使用时间戳,使用数值timestamp表示日期 serverTimezone=GMT%2B8
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/springboot2?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
############### 连接池配置 ################
#连接池建立时创建的初始化连接数
initial-size: 5
#最大活跃连接数
max-active: 20
#最小活跃连接数
min-idle: 5
#配置获取连接等待超时时间
max-wait: 60000
#打开PSCache,并且指定每个连接上PSCache的大小
max-pool-prepared-statement-per-connection-size: 20
validation-query: SELECT 1 FROM DUAL
query-timeout: 30000
#是否获得连接放回连接池后检测其可用性
test-on-borrow: false
#是否在连接放回连接池后检测其可用性
test-on-return: false
#是否在连接空闲一段时间后检测其可用性
test-while-idle: true
#配置间隔多久进行一次检测,检测需要关闭空闲连接单位是毫秒
time-between-eviction-runs-millis: 60000
#配置一个连接在池中最小生存时间单位是毫秒
min-evictable-idle-time-millis: 300000
#监控后台账号和密码
stat-view-servlet:
login-username: admin
login-password: 123456
#监控页面 http://localhost:8888/druid
#mybatis配置
mybatis:
mapper-locations: classpath:generator/*.xml
server:
port: 8889
4、mybatis配置
mapper:
package com.example.distributed.demo.mapper;
import com.example.distributed.demo.entity.SysUser;
import org.springframework.stereotype.Repository;
@Repository
public interface SysUserDao {
int deleteByPrimaryKey(String id);
int insert(SysUser record);
int insertSelective(SysUser record);
SysUser selectByPrimaryKey(String id);
int updateByPrimaryKeySelective(SysUser record);
int updateByPrimaryKey(SysUser record);
SysUser selectByUsername(String username);
}
entity:
package com.example.distributed.demo.entity;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import java.io.Serializable;
import java.util.Date;
/**
* sys_user
* @author
*/
public class SysUser implements Serializable {
/**
* id
*/
private String id;
private String username;
private String salt;
private String password;
private String phone;
/**
* id
*/
private String deptId;
private String realName;
private String nickName;
private String email;
/**
* 12
*/
private Byte status;
/**
* 12
*/
private Byte sex;
/**
* 0 1
*/
private Byte deleted;
private String createId;
private String updateId;
/**
* 1web 2android 3ios
*/
private String createWhere;
private Date createTime;
private Date updateTime;
private static final long serialVersionUID = 1L;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getDeptId() {
return deptId;
}
public void setDeptId(String deptId) {
this.deptId = deptId;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Byte getStatus() {
return status;
}
public void setStatus(Byte status) {
this.status = status;
}
public Byte getSex() {
return sex;
}
public void setSex(Byte sex) {
this.sex = sex;
}
public Byte getDeleted() {
return deleted;
}
public void setDeleted(Byte deleted) {
this.deleted = deleted;
}
public String getCreateId() {
return createId;
}
public void setCreateId(String createId) {
this.createId = createId;
}
public String getUpdateId() {
return updateId;
}
public void setUpdateId(String updateId) {
this.updateId = updateId;
}
public String getCreateWhere() {
return createWhere;
}
public void setCreateWhere(String createWhere) {
this.createWhere = createWhere;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.distributed.demo.mapper.SysUserDao">
<resultMap id="BaseResultMap" type="com.example.distributed.demo.entity.SysUser">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="username" jdbcType="VARCHAR" property="username" />
<result column="salt" jdbcType="VARCHAR" property="salt" />
<result column="password" jdbcType="VARCHAR" property="password" />
<result column="phone" jdbcType="VARCHAR" property="phone" />
<result column="dept_id" jdbcType="VARCHAR" property="deptId" />
<result column="real_name" jdbcType="VARCHAR" property="realName" />
<result column="nick_name" jdbcType="VARCHAR" property="nickName" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="status" jdbcType="TINYINT" property="status" />
<result column="sex" jdbcType="TINYINT" property="sex" />
<result column="deleted" jdbcType="TINYINT" property="deleted" />
<result column="create_id" jdbcType="VARCHAR" property="createId" />
<result column="update_id" jdbcType="VARCHAR" property="updateId" />
<result column="create_where" jdbcType="VARCHAR" property="createWhere" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
</resultMap>
<sql id="Base_Column_List">
id, username, salt, `password`, phone, dept_id, real_name, nick_name, email, `status`,
sex, deleted, create_id, update_id, create_where, create_time, update_time
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from sys_user
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from sys_user
where id = #{id,jdbcType=VARCHAR}
</delete>
<insert id="insert" parameterType="com.example.distributed.demo.entity.SysUser" >
insert into sys_user (id,username, salt, `password`,
phone, dept_id, real_name,
nick_name, email, `status`,
sex, deleted, create_id,
update_id, create_where, create_time,
update_time)
values (#{id,jdbcType=VARCHAR},#{username,jdbcType=VARCHAR}, #{salt,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR},
#{phone,jdbcType=VARCHAR}, #{deptId,jdbcType=VARCHAR}, #{realName,jdbcType=VARCHAR},
#{nickName,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{status,jdbcType=TINYINT},
#{sex,jdbcType=TINYINT}, #{deleted,jdbcType=TINYINT}, #{createId,jdbcType=VARCHAR},
#{updateId,jdbcType=VARCHAR}, #{createWhere,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP},
#{updateTime,jdbcType=TIMESTAMP})
</insert>
<insert id="insertSelective" parameterType="com.example.distributed.demo.entity.SysUser" >
insert into sys_user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="username != null">
username,
</if>
<if test="salt != null">
salt,
</if>
<if test="password != null">
`password`,
</if>
<if test="phone != null">
phone,
</if>
<if test="deptId != null">
dept_id,
</if>
<if test="realName != null">
real_name,
</if>
<if test="nickName != null">
nick_name,
</if>
<if test="email != null">
email,
</if>
<if test="status != null">
`status`,
</if>
<if test="sex != null">
sex,
</if>
<if test="deleted != null">
deleted,
</if>
<if test="createId != null">
create_id,
</if>
<if test="updateId != null">
update_id,
</if>
<if test="createWhere != null">
create_where,
</if>
<if test="createTime != null">
create_time,
</if>
<if test="updateTime != null">
update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="username != null">
#{username,jdbcType=VARCHAR},
</if>
<if test="salt != null">
#{salt,jdbcType=VARCHAR},
</if>
<if test="password != null">
#{password,jdbcType=VARCHAR},
</if>
<if test="phone != null">
#{phone,jdbcType=VARCHAR},
</if>
<if test="deptId != null">
#{deptId,jdbcType=VARCHAR},
</if>
<if test="realName != null">
#{realName,jdbcType=VARCHAR},
</if>
<if test="nickName != null">
#{nickName,jdbcType=VARCHAR},
</if>
<if test="email != null">
#{email,jdbcType=VARCHAR},
</if>
<if test="status != null">
#{status,jdbcType=TINYINT},
</if>
<if test="sex != null">
#{sex,jdbcType=TINYINT},
</if>
<if test="deleted != null">
#{deleted,jdbcType=TINYINT},
</if>
<if test="createId != null">
#{createId,jdbcType=VARCHAR},
</if>
<if test="updateId != null">
#{updateId,jdbcType=VARCHAR},
</if>
<if test="createWhere != null">
#{createWhere,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
#{createTime,jdbcType=TIMESTAMP},
</if>
<if test="updateTime != null">
#{updateTime,jdbcType=TIMESTAMP},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.example.distributed.demo.entity.SysUser">
update sys_user
<set>
<if test="username != null">
username = #{username,jdbcType=VARCHAR},
</if>
<if test="salt != null">
salt = #{salt,jdbcType=VARCHAR},
</if>
<if test="password != null">
`password` = #{password,jdbcType=VARCHAR},
</if>
<if test="phone != null">
phone = #{phone,jdbcType=VARCHAR},
</if>
<if test="deptId != null">
dept_id = #{deptId,jdbcType=VARCHAR},
</if>
<if test="realName != null">
real_name = #{realName,jdbcType=VARCHAR},
</if>
<if test="nickName != null">
nick_name = #{nickName,jdbcType=VARCHAR},
</if>
<if test="email != null">
email = #{email,jdbcType=VARCHAR},
</if>
<if test="status != null">
`status` = #{status,jdbcType=TINYINT},
</if>
<if test="sex != null">
sex = #{sex,jdbcType=TINYINT},
</if>
<if test="deleted != null">
deleted = #{deleted,jdbcType=TINYINT},
</if>
<if test="createId != null">
create_id = #{createId,jdbcType=VARCHAR},
</if>
<if test="updateId != null">
update_id = #{updateId,jdbcType=VARCHAR},
</if>
<if test="createWhere != null">
create_where = #{createWhere,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=TIMESTAMP},
</if>
<if test="updateTime != null">
update_time = #{updateTime,jdbcType=TIMESTAMP},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="com.example.distributed.demo.entity.SysUser">
update sys_user
set username = #{username,jdbcType=VARCHAR},
salt = #{salt,jdbcType=VARCHAR},
`password` = #{password,jdbcType=VARCHAR},
phone = #{phone,jdbcType=VARCHAR},
dept_id = #{deptId,jdbcType=VARCHAR},
real_name = #{realName,jdbcType=VARCHAR},
nick_name = #{nickName,jdbcType=VARCHAR},
email = #{email,jdbcType=VARCHAR},
`status` = #{status,jdbcType=TINYINT},
sex = #{sex,jdbcType=TINYINT},
deleted = #{deleted,jdbcType=TINYINT},
create_id = #{createId,jdbcType=VARCHAR},
update_id = #{updateId,jdbcType=VARCHAR},
create_where = #{createWhere,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=TIMESTAMP},
update_time = #{updateTime,jdbcType=TIMESTAMP}
where id = #{id,jdbcType=VARCHAR}
</update>
<select id="selectByUsername" resultMap="BaseResultMap">
select <include refid="Base_Column_List"></include>
from sys_user
where username=#{username} and deleted=0
</select>
</mapper>
5、config包
RedisConfig:
package com.example.distributed.demo.config;
import com.example.distributed.demo.serializer.MyStringRedisSerializer;
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.StringRedisSerializer;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 16:01
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new MyStringRedisSerializer());
redisTemplate.setHashValueSerializer(new MyStringRedisSerializer());
return redisTemplate;
}
}
redis实现类:
package com.example.distributed.demo.service.impl;
import com.example.distributed.demo.exception.BusinessException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 16:05
*/
@Service
public class RedisService {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
/** -------------------key相关操作--------------------- */
/**
* 是否存在key
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.Boolean
* @throws
*/
public Boolean hasKey(String key) {
if (null==key){
return false;
}
return redisTemplate.hasKey(key);
}
/**
* 删除key
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return Boolean 成功返回true 失败返回false
* @throws
*/
public Boolean delete(String key) {
if (null==key){
return false;
}
return redisTemplate.delete(key);
}
/**
* 批量删除key
* @Author: 刘阳洋
* @CreateDate: 2019/8/27 20:27
* @UpdateUser:
* @UpdateDate: 2019/8/27 20:27
* @Version: 0.0.1
* @param keys
* @return Long 返回成功删除key的数量
* @throws
*/
public Long delete(Collection<String> keys) {
return redisTemplate.delete(keys);
}
/**
* 设置过期时间
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param timeout
* @param unit
* @return java.lang.Boolean
* @throws
*/
public Boolean expire(String key, long timeout, TimeUnit unit) {
if (null==key||null==unit){
return false;
}
return redisTemplate.expire(key, timeout, unit);
}
/**
* 查找匹配的key
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param pattern
* @return java.util.Set<java.lang.String>
* @throws
*/
public Set<String> keys(String pattern) {
if (null==pattern){
return null;
}
return redisTemplate.keys(pattern);
}
/**
* 移除 key 的过期时间,key 将持久保持
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.Boolean
* @throws
*/
public Boolean persist(String key) {
if (null==key){
return false;
}
return redisTemplate.persist(key);
}
/**
* 返回 key 的剩余的过期时间
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param unit
* @return java.lang.Long 当 key 不存在时,返回 -2 。当 key 存在但没有设置剩余生存时间时,返回 -1 。否则,以秒为单位,返回 key的剩余生存时间
* @throws
*/
public Long getExpire(String key, TimeUnit unit) {
if(null==key||null==unit){
throw new BusinessException(4001004,"key or TomeUnit 不能为空");
}
return redisTemplate.getExpire(key, unit);
}
//*************String相关数据类型***************************
/**
* 设置指定 key 的值
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param value
* @return void
* @throws
*/
public void set(String key, Object value) {
if(null==key||null==value){
return;
}
redisTemplate.opsForValue().set(key, value);
}
/**
* 设置key 的值 并设置过期时间
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param value
* @param time
* @param unit
* @return void
* @throws
*/
public void set(String key,Object value,long time,TimeUnit unit){
if(null==key||null==value||null==unit){
return;
}
redisTemplate.opsForValue().set(key,value,time,unit);
}
/**
* 设置key 的值 并设置过期时间
* key存在 不做操作返回false
* key不存在设置值返回true
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param value
* @param time
* @param unit
* @return java.lang.Boolean
* @throws
*/
public Boolean setifAbsen(String key,Object value,long time,TimeUnit unit){
if(null==key||null==value||null==unit){
throw new BusinessException(4001004,"kkey、value、unit都不能为空");
}
return redisTemplate.opsForValue().setIfAbsent(key,value,time,unit);
}
/**
* 获取指定Key的Value。如果与该Key关联的Value不是string类型,Redis将抛出异常,
* 因为GET命令只能用于获取string Value,如果该Key不存在,返回null
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.Object
* @throws
*/
public Object get(String key){
if(null==key){
return null;
}
return redisTemplate.opsForValue().get(key);
}
/**
* 很明显先get再set就说先获取key值对应的value然后再set 新的value 值。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param value
* @return java.lang.Object
* @throws
*/
public Object getSet(String key,Object value){
if(null==key){
return null;
}
return redisTemplate.opsForValue().getAndSet(key,value);
}
/**
* 通过批量的key获取批量的value
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param keys
* @return java.util.List<java.lang.Object>
* @throws
*/
public List<Object> mget(Collection<String> keys){
if(null==keys){
return Collections.emptyList();
}
return redisTemplate.opsForValue().multiGet(keys);
}
/**
* 将指定Key的Value原子性的增加increment。如果该Key不存在,其初始值为0,在incrby之后其值为increment。
* 如果Value的值不能转换为整型值,如Hi,该操作将执行失败并抛出相应异常。操作成功则返回增加后的value值。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param increment
* @return long
* @throws
*/
public long incrby(String key,long increment){
if(null==key){
throw new BusinessException(4001004,"key不能为空");
}
return redisTemplate.opsForValue().increment(key,increment);
}
/**
*
* 将指定Key的Value原子性的减少decrement。如果该Key不存在,其初始值为0,
* 在decrby之后其值为-decrement。如果Value的值不能转换为整型值,
* 如Hi,该操作将执行失败并抛出相应的异常。操作成功则返回减少后的value值。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param decrement
* @return java.lang.Long
* @throws
*/
public Long decrby(String key,long decrement){
if(null==key){
throw new BusinessException(4001004,"key不能为空");
}
return redisTemplate.opsForValue().decrement(key,decrement);
}
/**
* 如果该Key已经存在,APPEND命令将参数Value的数据追加到已存在Value的末尾。如果该Key不存在,
* APPEND命令将会创建一个新的Key/Value。返回追加后Value的字符串长度。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param value
* @return java.lang.Integer
* @throws
*/
public Integer append(String key,String value){
if(key==null){
throw new BusinessException(4001004,"key不能为空");
}
return redisTemplate.opsForValue().append(key,value);
}
//******************hash数据类型*********************
/**
* 通过key 和 field 获取指定的 value
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param field
* @return java.lang.Object
* @throws
*/
public Object hget(String key, Object field) {
if(null==key||null==field){
return null;
}
return redisTemplate.opsForHash().get(key,field);
}
/**
* 为指定的Key设定Field/Value对,如果Key不存在,该命令将创建新Key以用于存储参数中的Field/Value对,
* 如果参数中的Field在该Key中已经存在,则用新值覆盖其原有值。
* 返回1表示新的Field被设置了新值,0表示Field已经存在,用新值覆盖原有值。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param field
* @param value
* @return
* @throws
*/
public void hset(String key, Object field, Object value) {
if(null==key||null==field){
return;
}
redisTemplate.opsForHash().put(key,field,value);
}
/**
* 判断指定Key中的指定Field是否存在,返回true表示存在,false表示参数中的Field或Key不存在。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param field
* @return java.lang.Boolean
* @throws
*/
public Boolean hexists(String key, Object field) {
if(null==key||null==field){
return false;
}
return redisTemplate.opsForHash().hasKey(key,field);
}
/**
* 从指定Key的Hashes Value中删除参数中指定的多个字段,如果不存在的字段将被忽略,
* 返回实际删除的Field数量。如果Key不存在,则将其视为空Hashes,并返回0。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param fields
* @return java.lang.Long
* @throws
*/
public Long hdel(String key, Object... fields) {
if(null==key||null==fields||fields.length==0){
return 0L;
}
return redisTemplate.opsForHash().delete(key,fields);
}
/**
* 通过key获取所有的field和value
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.util.Map<java.lang.Object,java.lang.Object>
* @throws
*/
public Map<Object, Object> hgetall(String key) {
if(key==null){
return null;
}
return redisTemplate.opsForHash().entries(key);
}
/**
* 逐对依次设置参数中给出的Field/Value对。如果其中某个Field已经存在,则用新值覆盖原有值。
* 如果Key不存在,则创建新Key,同时设定参数中的Field/Value。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param hash
* @return
* @throws
*/
public void hmset(String key, Map<String, Object> hash) {
if(null==key||null==hash){
return;
}
redisTemplate.opsForHash().putAll(key,hash);
}
/**
* 获取和参数中指定Fields关联的一组Values,其返回顺序等同于Fields的请求顺序。
* 如果请求的Field不存在,其值对应的value为null。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param fields
* @return java.util.List<java.lang.Object>
* @throws
*/
public List<Object> hmget(String key, Collection<Object> fields) {
if(null==key||null==fields){
return null;
}
return redisTemplate.opsForHash().multiGet(key,fields);
}
/**
* 对应key的字段自增相应的值
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param field
* @param increment
* @return java.lang.Long
* @throws
*/
public Long hIncrBy(String key,Object field,long increment){
if (null==key||null==field){
throw new BusinessException(4001004,"key or field 不能为空");
}
return redisTemplate.opsForHash().increment(key,field,increment);
}
//***************List数据类型***************
/**
* 向列表左边添加元素。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的头部插入。
* 如果该键的Value不是链表类型,该命令将将会抛出相关异常。操作成功则返回插入后链表中元素的数量。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param strs 可以使一个string 也可以使string数组
* @return java.lang.Long 返回操作的value个数
* @throws
*/
public Long lpush(String key, Object... strs) {
if(null==key){
return 0L;
}
return redisTemplate.opsForList().leftPushAll(key,strs);
}
/**
* 向列表右边添加元素。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的尾部插入。
* 如果该键的Value不是链表类型,该命令将将会抛出相关异常。操作成功则返回插入后链表中元素的数量。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param strs 可以使一个string 也可以使string数组
* @return java.lang.Long 返回操作的value个数
* @throws
*/
public Long rpush(String key, Object... strs) {
if(null==key){
return 0L;
}
return redisTemplate.opsForList().rightPushAll(key,strs);
}
/**
* 返回并弹出指定Key关联的链表中的第一个元素,即头部元素。如果该Key不存在,
* 返回nil。LPOP命令执行两步操作:第一步是将列表左边的元素从列表中移除,第二步是返回被移除的元素值。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.Object
* @throws
*/
public Object lpop(String key) {
if(null==key){
return null;
}
return redisTemplate.opsForList().leftPop(key);
}
/**
* 返回并弹出指定Key关联的链表中的最后一个元素,即头部元素。如果该Key不存在,返回nil。
* RPOP命令执行两步操作:第一步是将列表右边的元素从列表中移除,第二步是返回被移除的元素值。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.Object
* @throws
*/
public Object rpop(String key) {
if(null==key){
return null;
}
return redisTemplate.opsForList().rightPop(key);
}
/**
*该命令的参数start和end都是0-based。即0表示链表头部(leftmost)的第一个元素。
* 其中start的值也可以为负值,-1将表示链表中的最后一个元素,即尾部元素,-2表示倒数第二个并以此类推。
* 该命令在获取元素时,start和end位置上的元素也会被取出。如果start的值大于链表中元素的数量,
* 空链表将会被返回。如果end的值大于元素的数量,该命令则获取从start(包括start)开始,链表中剩余的所有元素。
* 注:Redis的列表起始索引为0。显然,LRANGE numbers 0 -1 可以获取列表中的所有元素。返回指定范围内元素的列表。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param start
* @param end
* @return java.util.List<java.lang.Object>
* @throws
*/
public List<Object> lrange(String key, long start, long end) {
if(null==key){
return null;
}
return redisTemplate.opsForList().range(key,start,end);
}
/**
* 让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
* 下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
* 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param start
* @param end
* @return
* @throws
*/
public void ltrim(String key, long start, long end) {
if(null==key){
return;
}
redisTemplate.opsForList().trim(key,start,end);
}
/**
* 该命令将返回链表中指定位置(index)的元素,index是0-based,表示从头部位置开始第index的元素,
* 如果index为-1,表示尾部元素。如果与该Key关联的不是链表,该命令将返回相关的错误信息。 如果超出index返回这返回nil。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param index
* @return java.lang.Object
* @throws
*/
public Object lindex(String key, long index) {
if(null==key){
return null;
}
return redisTemplate.opsForList().index(key,index);
}
/**
* 返回指定Key关联的链表中元素的数量,如果该Key不存在,则返回0。如果与该Key关联的Value的类型不是链表,则抛出相关的异常。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.Long
* @throws
*/
public Long llen(String key) {
if(null==key){
return 0L;
}
return redisTemplate.opsForList().size(key);
}
//***************Set数据类型*************
/**
* 如果在插入的过程用,参数中有的成员在Set中已经存在,该成员将被忽略,而其它成员仍将会被正常插入。
* 如果执行该命令之前,该Key并不存在,该命令将会创建一个新的Set,此后再将参数中的成员陆续插入。返回实际插入的成员数量。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param members 可以是一个String 也可以是一个String数组
* @return java.lang.Long 添加成功的个数
* @throws
*/
public Long sadd(String key, Object... members) {
if (null==key){
return 0L;
}
return redisTemplate.opsForSet().add(key, members);
}
/**
* 返回Set中成员的数量,如果该Key并不存在,返回0。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.Long
* @throws
*/
public Long scard(String key) {
if (null==key){
return 0L;
}
return redisTemplate.opsForSet().size(key);
}
/**
* 判断参数中指定成员是否已经存在于与Key相关联的Set集合中。返回true表示已经存在,false表示不存在,或该Key本身并不存在。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param member
* @return java.lang.Boolean
* @throws
*/
public Boolean sismember(String key, Object member) {
if (null==key){
return false;
}
return redisTemplate.opsForSet().isMember(key,member);
}
/**
* 和SPOP一样,随机的返回Set中的一个成员,不同的是该命令并不会删除返回的成员。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.String
* @throws
*/
public Object srandmember(String key) {
if (null==key){
return null;
}
return redisTemplate.opsForSet().randomMember(key);
}
/**
* 和SPOP一样,随机的返回Set中的一个成员,不同的是该命令并不会删除返回的成员。
* 还可以传递count参数来一次随机获得多个元素,根据count的正负不同,具体表现也不同。
* 当count 为正数时,SRANDMEMBER 会随机从集合里获得count个不重复的元素。
* 如果count的值大于集合中的元素个数,则SRANDMEMBER 会返回集合中的全部元素。
* 当count为负数时,SRANDMEMBER 会随机从集合里获得|count|个的元素,如果|count|大与集合中的元素,
* 就会返回全部元素不够的以重复元素补齐,如果key不存在则返回nil。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param count
* @return java.util.List<java.lang.String>
* @throws
*/
public List<Object> srandmember(String key,int count) {
if(null==key){
return null;
}
return redisTemplate.opsForSet().randomMembers(key,count);
}
/**
* 通过key随机删除一个set中的value并返回该值
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.String
* @throws
*/
public Object spop(String key) {
if (null==key){
return null;
}
return redisTemplate.opsForSet().pop(key);
}
/**
* 通过key获取set中所有的value
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.util.Set<java.lang.String>
* @throws
*/
public Set<Object> smembers(String key) {
if (null==key){
return null;
}
return redisTemplate.opsForSet().members(key);
}
/**
* 从与Key关联的Set中删除参数中指定的成员,不存在的参数成员将被忽略,
* 如果该Key并不存在,将视为空Set处理。返回从Set中实际移除的成员数量,如果没有则返回0。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param members
* @return java.lang.Long
* @throws
*/
public Long srem(String key, Object... members) {
if (null==key){
return 0L;
}
return redisTemplate.opsForSet().remove(key,members);
}
/**
* 将元素value从一个集合移到另一个集合
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param srckey
* @param dstkey
* @param member
* @return java.lang.Long
* @throws
*/
public Boolean smove(String srckey, String dstkey, Object member) {
if (null==srckey||null==dstkey){
return false;
}
return redisTemplate.opsForSet().move(srckey,member,dstkey);
}
/**
* 获取两个集合的并集
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param otherKeys
* @return java.util.Set<java.lang.Object> 返回两个集合合并值
* @throws
*/
public Set<Object> sUnion(String key, String otherKeys) {
if (null==key||otherKeys==null){
return null;
}
return redisTemplate.opsForSet().union(key, otherKeys);
}
//**********Sorted Set 数据类型********************
/**
*添加参数中指定的所有成员及其分数到指定key的Sorted Set中,在该命令中我们可以指定多组score/member作为参数。
* 如果在添加时参数中的某一成员已经存在,该命令将更新此成员的分数为新值,同时再将该成员基于新值重新排序。
* 如果键不存在,该命令将为该键创建一个新的Sorted Set Value,并将score/member对插入其中。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param score
* @param member
* @return java.lang.Long
* @throws
*/
public Boolean zadd(String key, double score, Object member) {
if (null==key){
return false;
}
return redisTemplate.opsForZSet().add(key,member,score);
}
/**
* 该命令将移除参数中指定的成员,其中不存在的成员将被忽略。
* 如果与该Key关联的Value不是Sorted Set,相应的错误信息将被返回。 如果操作成功则返回实际被删除的成员数量。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param members 可以使一个string 也可以是一个string数组
* @return java.lang.Long
* @throws
*/
public Long zrem(String key, Object... members) {
if(null==key||null==members){
return 0L;
}
return redisTemplate.opsForZSet().remove(key,members);
}
/**
* 返回Sorted Set中的成员数量,如果该Key不存在,返回0。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @return java.lang.Long
* @throws
*/
public Long zcard(String key) {
if (null==key){
return 0L;
}
return redisTemplate.opsForZSet().size(key);
}
/**
* 该命令将为指定Key中的指定成员增加指定的分数。如果成员不存在,该命令将添加该成员并假设其初始分数为0,
* 此后再将其分数加上increment。如果Key不存在,该命令将创建该Key及其关联的Sorted Set,
* 并包含参数指定的成员,其分数为increment参数。如果与该Key关联的不是Sorted Set类型,
* 相关的错误信息将被返回。如果不报错则以串形式表示的新分数。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param score
* @param member
* @return java.lang.Double
* @throws
*/
public Double zincrby(String key, double score, Object member) {
if (null==key){
throw new BusinessException(4001004,"key 不能为空");
}
return redisTemplate.opsForZSet().incrementScore(key,member,score);
}
/**
* 该命令用于获取分数(score)在min和max之间的成员数量。
* (min=<score<=max)如果加上了“(”着表明是开区间例如zcount key (min max 则 表示(min<score=<max)
* 同理zcount key min (max 则表明(min=<score<max) 返回指定返回数量。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param min
* @param max
* @return java.lang.Long
* @throws
*/
public Long zcount(String key, double min, double max) {
if (null==key){
return 0L;
}
return redisTemplate.opsForZSet().count(key, min, max);
}
/**
* Sorted Set中的成员都是按照分数从低到高的顺序存储,该命令将返回参数中指定成员的位置值,
* 其中0表示第一个成员,它是Sorted Set中分数最低的成员。 如果该成员存在,则返回它的位置索引值。否则返回nil。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param member
* @return java.lang.Long
* @throws
*/
public Long zrank(String key, Object member) {
if (null==key){
return null;
}
return redisTemplate.opsForZSet().rank(key,member);
}
/**
* 如果该成员存在,以字符串的形式返回其分数,否则返回null
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param member
* @return java.lang.Double
* @throws
*/
public Double zscore(String key, Object member) {
if (null==key){
return null;
}
return redisTemplate.opsForZSet().score(key,member);
}
/**
* 该命令返回顺序在参数start和stop指定范围内的成员,这里start和stop参数都是0-based,即0表示第一个成员,-1表示最后一个成员。如果start大于该Sorted
* Set中的最大索引值,或start > stop,此时一个空集合将被返回。如果stop大于最大索引值,
* 该命令将返回从start到集合的最后一个成员。如果命令中带有可选参数WITHSCORES选项,
* 该命令在返回的结果中将包含每个成员的分数值,如value1,score1,value2,score2...。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param min
* @param max
* @return java.util.Set<java.lang.String> 指定区间内的有序集成员的列表。
* @throws
*/
public Set<Object> zrange(String key, long min, long max) {
if (null==key){
return null;
}
return redisTemplate.opsForZSet().range(key, min, max);
}
/**
* 该命令的功能和ZRANGE基本相同,唯一的差别在于该命令是通过反向排序获取指定位置的成员,
* 即从高到低的顺序。如果成员具有相同的分数,则按降序字典顺序排序。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param start
* @param end
* @return java.util.Set<java.lang.String>
* @throws
*/
public Set<Object> zReverseRange(String key, long start, long end) {
if (null==key){
return null;
}
return redisTemplate.opsForZSet().reverseRange(key, start, end);
}
/**
* 该命令将返回分数在min和max之间的所有成员,即满足表达式min <= score <= max的成员,
* 其中返回的成员是按照其分数从低到高的顺序返回,如果成员具有相同的分数,
* 则按成员的字典顺序返回。可选参数LIMIT用于限制返回成员的数量范围。
* 可选参数offset表示从符合条件的第offset个成员开始返回,同时返回count个成员。
* 可选参数WITHSCORES的含义参照ZRANGE中该选项的说明。*最后需要说明的是参数中min和max的规则可参照命令ZCOUNT。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param max
* @param min
* @return java.util.Set<java.lang.String>
* @throws
*/
public Set<Object> zrangebyscore(String key, double min, double max) {
if (null==key){
return null;
}
return redisTemplate.opsForZSet().rangeByScore(key, min, max);
}
/**
* 该命令除了排序方式是基于从高到低的分数排序之外,其它功能和参数含义均与ZRANGEBYSCORE相同。
* 需要注意的是该命令中的min和max参数的顺序和ZRANGEBYSCORE命令是相反的。
* @Author: 刘阳洋
* @UpdateUser:
* @Version: 0.0.1
* @param key
* @param max
* @param min
* @return java.util.Set<java.lang.String>
* @throws
*/
public Set<Object> zrevrangeByScore(String key, double min, double max) {
if (null==key){
return null;
}
return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
}
}
SwaggerConfig:
package com.example.distributed.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
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;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 16:47
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createDocket(){
List<Parameter> parameterList=new ArrayList<>();
ParameterBuilder parameterBuilder=new ParameterBuilder();
parameterBuilder.name("token").description("swagger调试用(模拟传入用户认证凭证)").modelRef(new ModelRef("String"))
.parameterType("header").required(false);
parameterList.add(parameterBuilder.build());
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.distributed.demo"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(parameterList)
;
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder().
title("Spring Boot 2")
.description("刘阳洋")
.version("1.0")
.build();
}
}
WebAppConfig 拦截器策略
package com.example.distributed.demo.config;
import com.example.distributed.demo.interceptor.TokenInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 16:51
*/
//配置拦截器策略
@Configuration
public class WebAppConfig implements WebMvcConfigurer {
@Bean
public TokenInterceptor tokenInterceptor(){
return new TokenInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor()).addPathPatterns("/api/**").excludePathPatterns("/api/user/login","/api/user/register","/api/user/code/*");
}
}
6、常用参数类
package com.example.distributed.demo.content;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 16:53
*/
public class Content{
/**
* 判断是否达上线的key
*/
public final static String REGISTER_CODE_COUNT_KEY="register-code-count-key_";
/**
* 验证码有效期key
*/
public final static String REGISTER_CODE_COUNT_VALIDITY_KEY="register-code-count-validity-key_";
/**
* 订单编码
*/
public final static String ORDER_CODE_KEY="order-code-key_";
/**
* 购物车key
*/
public final static String CART_KEY="cart_";
}
7、exception包
package com.example.distributed.demo.exception;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 16:03
*/
public class BusinessException extends RuntimeException{
private final int messageCode;
private final String messageDefault;
public BusinessException(int messageCode,String message ) {
super(message);
this.messageCode = messageCode;
this.messageDefault = message;
}
public int getMessageCode() {
return messageCode;
}
public String getMessageDefault() {
return messageDefault;
}
}
8、inteceptor包
package com.example.distributed.demo.interceptor;
import com.example.distributed.demo.exception.BusinessException;
import com.example.distributed.demo.service.impl.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 16:52
*/
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private RedisService redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token=request.getHeader("token");
if(StringUtils.isEmpty(token)){
throw new BusinessException(3004,"用户凭证不能为空,请重新登录");
}else {
if(!redisService.hasKey(token)){
throw new BusinessException(3005,"用户凭证无效,请重新登录");
}
String userId= (String) redisService.get(token);
if(redisService.hasKey(userId)&&!token.equals(redisService.get(userId))){
throw new BusinessException(3006,"您的账号已经在异地登录,请重新登录");
}
}
return true;
}
}
9、redis templete序列化 类
package com.example.distributed.demo.serializer;
import com.alibaba.fastjson.JSON;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 16:16
*/
public class MyStringRedisSerializer implements RedisSerializer<Object> {
private final Charset charset;
public MyStringRedisSerializer() {
this(StandardCharsets.UTF_8);
}
public MyStringRedisSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
}
@Override
public String deserialize(byte[] bytes) {
return (bytes == null ? null : new String(bytes, charset));
}
@Override
public byte[] serialize(Object object) {
if (object == null) {
return new byte[0];
}
if (object instanceof String) {
return object.toString().getBytes(charset);
} else {
String string = JSON.toJSONString(object);
return string.getBytes(charset);
}
}
}
10、utils包
package com.example.distributed.demo.utils;
import java.security.MessageDigest;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 18:11
*/
public class PasswordEncoder {
private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
"e", "f" };
private final static String MD5 = "MD5";
private final static String SHA = "SHA";
private Object salt;
private String algorithm;
public PasswordEncoder(Object salt) {
this(salt, MD5);
}
public PasswordEncoder(Object salt, String algorithm) {
this.salt = salt;
this.algorithm = algorithm;
}
/**
* 密码加密
* @param rawPass
* @return
*/
public String encode(String rawPass) {
String result = null;
try {
MessageDigest md = MessageDigest.getInstance(algorithm);
// 加密后的字符串
result = byteArrayToHexString(md.digest(mergePasswordAndSalt(rawPass).getBytes("utf-8")));
} catch (Exception ex) {
}
return result;
}
/**
* 密码匹配验证
* @param encPass 密文
* @param rawPass 明文
* @return
*/
public boolean matches(String encPass, String rawPass) {
String pass1 = "" + encPass;
String pass2 = encode(rawPass);
return pass1.equals(pass2);
}
private String mergePasswordAndSalt(String password) {
if (password == null) {
password = "";
}
if ((salt == null) || "".equals(salt)) {
return password;
} else {
return password + "{" + salt.toString() + "}";
}
}
/**
* 转换字节数组为16进制字串
*
* @param b
* 字节数组
* @return 16进制字串
*/
private String byteArrayToHexString(byte[] b) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
/**
* 将字节转换为16进制
* @param b
* @return
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n = 256 + n;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static void main(String[] args) {
}
}
package com.example.distributed.demo.utils;
import java.util.UUID;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 18:11
*/
public class PasswordUtils {
/**
* 匹配密码
* @param salt 盐
* @param rawPass 明文
* @param encPass 密文
* @return
*/
public static boolean matches(String salt, String rawPass, String encPass) {
return new PasswordEncoder(salt).matches(encPass, rawPass);
}
/**
* 明文密码加密
* @param rawPass 明文
* @param salt
* @return
*/
public static String encode(String rawPass, String salt) {
return new PasswordEncoder(salt).encode(rawPass);
}
/**
* 获取加密盐
* @return
*/
public static String getSalt() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 20);
}
}
11、vo包
request:
package com.example.distributed.demo.vo.request;
import io.swagger.annotations.ApiModelProperty;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 21:52
*/
public class AddCartReqVO {
@ApiModelProperty(value = "商品skuId")
private String skuId;
@ApiModelProperty(value = "属性规格id拼接集合(以逗号隔开)")
private String specificationIds;
@ApiModelProperty(value = "商品数量")
private Integer num;
public String getSkuId() {
return skuId;
}
public void setSkuId(String skuId) {
this.skuId = skuId;
}
public String getSpecificationIds() {
return specificationIds;
}
public void setSpecificationIds(String specificationIds) {
this.specificationIds = specificationIds;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
}
package com.example.distributed.demo.vo.request;
import io.swagger.annotations.ApiModelProperty;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 17:54
*/
public class LoginReqVO {
@ApiModelProperty(value = "用户名")
private String username;
@ApiModelProperty(value = "密码")
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "LoginReqVO{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
package com.example.distributed.demo.vo.request;
import io.swagger.annotations.ApiModelProperty;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 17:28
*/
public class RegisterReqVO {
@ApiModelProperty(value = "账号")
private String username;
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "验证码")
private String code;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
response包:
package com.example.distributed.demo.vo.response;
import io.swagger.annotations.ApiModelProperty;
import java.math.BigDecimal;
import java.util.List;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 21:54
*/
public class CartRespVO {
@ApiModelProperty("商品skuId")
private String skuId;
@ApiModelProperty(value = "商品名称")
private String productName;
@ApiModelProperty(value = "规格属性")
private List<ValueItemRespVO> itemRespVOList;
@ApiModelProperty(value = "单价")
private BigDecimal price;
@ApiModelProperty(value = "数量")
private Integer num;
@ApiModelProperty(value = "图标")
private String icon;
public String getSkuId() {
return skuId;
}
public void setSkuId(String skuId) {
this.skuId = skuId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public List<ValueItemRespVO> getItemRespVOList() {
return itemRespVOList;
}
public void setItemRespVOList(List<ValueItemRespVO> itemRespVOList) {
this.itemRespVOList = itemRespVOList;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
}
package com.example.distributed.demo.vo.response;
import io.swagger.annotations.ApiModelProperty;
import java.math.BigDecimal;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 21:55
*/
public class GoodsItemRespVO {
@ApiModelProperty(value = "商品skuId")
private String skuId;
@ApiModelProperty(value = "商品名称")
private String productName;
@ApiModelProperty(value = "价格")
private BigDecimal price;
@ApiModelProperty(value = "图标")
private String icon;
public String getSkuId() {
return skuId;
}
public void setSkuId(String skuId) {
this.skuId = skuId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
}
package com.example.distributed.demo.vo.response;
import io.swagger.annotations.ApiModelProperty;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 17:55
*/
public class LoginRespVO {
@ApiModelProperty(value = "用户认证凭证")
private String token;
@ApiModelProperty(value = "用户id")
private String userId;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
@Override
public String toString() {
return "LoginRespVO{" +
"token='" + token + '\'' +
", userId='" + userId + '\'' +
'}';
}
}
package com.example.distributed.demo.vo.response;
import io.swagger.annotations.ApiModelProperty;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 21:54
*/
public class ValueItemRespVO {
@ApiModelProperty(value = "属性id")
private String valueId;
@ApiModelProperty(value = "商品规格属性名称")
private String valueName;
@ApiModelProperty(value = "商品规格属性类型名称")
private String typeName;
@ApiModelProperty(value = "商品规格属性类型")
private String type;
public String getValueId() {
return valueId;
}
public void setValueId(String valueId) {
this.valueId = valueId;
}
public String getValueName() {
return valueName;
}
public void setValueName(String valueName) {
this.valueName = valueName;
}
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
12、service层
接口包:
package com.example.distributed.demo.service.intefacer;
import com.example.distributed.demo.vo.request.AddCartReqVO;
import com.example.distributed.demo.vo.response.CartRespVO;
import java.util.List;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 21:53
*/
public interface CartService {
String addCart(AddCartReqVO vo, String userId);
List<CartRespVO> showCart(String userId);
}
package com.example.distributed.demo.service.intefacer;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 21:32
*/
public interface CodeService {
String getOrderCode(String type);
}
package com.example.distributed.demo.service.intefacer;
import com.example.distributed.demo.vo.request.LoginReqVO;
import com.example.distributed.demo.vo.request.RegisterReqVO;
import com.example.distributed.demo.vo.response.LoginRespVO;
import com.example.distributed.demo.entity.SysUser;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 18:02
*/
public interface UserService {
LoginRespVO login(LoginReqVO vo);
SysUser getUserInfo(String id);
String register(RegisterReqVO vo);
String getCode(String phone);
}
实现类:
package com.example.distributed.demo.service.impl;
import com.example.distributed.demo.content.Content;
import com.example.distributed.demo.service.intefacer.CartService;
import com.example.distributed.demo.vo.request.AddCartReqVO;
import com.example.distributed.demo.vo.response.CartRespVO;
import com.example.distributed.demo.vo.response.GoodsItemRespVO;
import com.example.distributed.demo.vo.response.ValueItemRespVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 21:56
*/
/**
* 加入购物车功能
*/
@Service
public class CartServiceImpl implements CartService {
@Autowired
private RedisService redisService;
@Override
public String addCart(AddCartReqVO vo, String userId) {
String filed=vo.getSkuId()+","+vo.getSpecificationIds();
redisService.hIncrBy(Content.CART_KEY+userId,filed,vo.getNum());
return "操作成功";
}
@Override
public List<CartRespVO> showCart(String userId) {
//获取用户购物车所有数据
Map<Object, Object> maps = redisService.hgetall(Content.CART_KEY + userId);
//解析数据
List<CartRespVO> result = new ArrayList<>();
if (null == maps) {
return result;
}
// 1. entrySet遍历,在键和值都需要时使用(最常用)
for (Map.Entry<Object, Object> entry : maps.entrySet()) {
CartRespVO respVO = new CartRespVO();
//商品数量
Integer num = Integer.valueOf(entry.getValue().toString());
String file = (String) entry.getKey();
//获取商品skuId和商品熟悉id(数组第一个为skuId)
String ids[] = file.split(",");
//查询商品相信信息
String skuId = ids[0];
//拿到商品详细
GoodsItemRespVO itemVO = getItem(skuId);
respVO.setIcon(itemVO.getIcon());
respVO.setIcon(itemVO.getIcon());
respVO.setPrice(itemVO.getPrice());
respVO.setSkuId(itemVO.getSkuId());
respVO.setProductName(itemVO.getProductName());
respVO.setNum(num);
List<ValueItemRespVO> list=new ArrayList<>();
for (int i=1;i<ids.length;i++){
//拿属性值
ValueItemRespVO item=getValueItem(ids[i]);
list.add(item);
}
respVO.setItemRespVOList(list);
result.add(respVO);
}
return result;
}
private GoodsItemRespVO getItem(String skuId) {
//这里是 mock 数据
GoodsItemRespVO respVO=new GoodsItemRespVO();
respVO.setIcon("http://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E5%9B%BE%E7%89%87&hs=0&pn=0&spn=0&di=7590&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&ie=utf-8&oe=utf-8&cl=2&lm=-1&cs=1411728850%2C1869975885&os=69787666%2C250391253&simid=3412044283%2C207046138&adpicid=0&lpn=0&ln=30&fr=ala&fm=&sme=&cg=&bdtype=0&oriquery=&objurl=http%3A%2F%2Fpic18.nipic.com%2F20120103%2F8993051_170340691334_2.jpg&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Bgtrtv_z%26e3Bv54AzdH3Ffi5oAzdH3F9AzdH3F8a0AzdH3Fccb9nlmhbbcud8kj_z%26e3Bip4s&gsm=&islist=&querylist=");
respVO.setPrice(new BigDecimal(69.99));
respVO.setSkuId(skuId);
respVO.setProductName("spring boot 实战");
return respVO;
}
private ValueItemRespVO getValueItem(String id){
//这里是 mock 数据
ValueItemRespVO respVO=new ValueItemRespVO();
respVO.setType("1");
respVO.setTypeName("普通属性");
respVO.setValueName("VIP 教学");
respVO.setValueId(id);
return respVO;
}
}
package com.example.distributed.demo.service.impl;
import com.example.distributed.demo.content.Content;
import com.example.distributed.demo.service.intefacer.CodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 21:32
*/
/**
* redis实现计数器(订单号/特殊有规律编码/点赞数)功能
*/
@Service
public class CodeServiceImpl implements CodeService {
@Autowired
private RedisService redisService;
@Override
public String getOrderCode(String type) {
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyyMMdd");
String date=simpleDateFormat.format(System.currentTimeMillis());
Long number=redisService.incrby(Content.ORDER_CODE_KEY+date,1);
String pad=padRight(number.toString(),7,"0");
return date+type+pad;
}
private String padRight(String oldStr,int len,String alexin){
String str="";
int strlen=oldStr.length();
for(int i=0;i<len-strlen;i++){
str=str+alexin;
}
str=str+oldStr;
return str;
}
}
package com.example.distributed.demo.service.impl;
import com.example.distributed.demo.content.Content;
import com.example.distributed.demo.exception.BusinessException;
import com.example.distributed.demo.service.intefacer.UserService;
import com.example.distributed.demo.utils.PasswordUtils;
import com.example.distributed.demo.vo.request.LoginReqVO;
import com.example.distributed.demo.vo.request.RegisterReqVO;
import com.example.distributed.demo.vo.response.LoginRespVO;
import com.example.distributed.demo.entity.SysUser;
import com.example.distributed.demo.mapper.SysUserDao;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 18:03
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private RedisService redisService;
@Autowired
private SysUserDao sysUserDao;
/**
* 用户注册
* @param vo
* @return
*/
@Override
public String register(RegisterReqVO vo) {
//判断验证码是否有效
if(!redisService.hasKey(Content.REGISTER_CODE_COUNT_VALIDITY_KEY+vo.getPhone())){
throw new BusinessException(3010,"验证码已失效请重新获取");
}
//校验验证码是否正确
if(!vo.getCode().equals(redisService.get(Content.REGISTER_CODE_COUNT_VALIDITY_KEY+vo.getPhone()))){
throw new BusinessException(3011,"请输入正确的验证码");
}
SysUser sysUser = sysUserDao.selectByUsername(vo.getUsername());
if(sysUser!=null){
throw new BusinessException(3012,"该账号已被占用");
}
SysUser user=new SysUser();
BeanUtils.copyProperties(vo,user);
user.setId(UUID.randomUUID().toString());
user.setCreateTime(new Date());
String salt=PasswordUtils.getSalt();
String ecdPwd=PasswordUtils.encode(vo.getPassword(),salt);
user.setSalt(salt);
user.setPassword(ecdPwd);
int i = sysUserDao.insertSelective(user);
if(i!=1){
throw new BusinessException(3013,"操作失败");
}
redisService.delete(Content.REGISTER_CODE_COUNT_VALIDITY_KEY+vo.getPhone());
redisService.delete(Content.REGISTER_CODE_COUNT_KEY+vo.getPhone());
return "注册成功";
}
/**
* 用户登录
* @param vo
* @return
*/
@Override
public LoginRespVO login(LoginReqVO vo) {
SysUser sysUser = sysUserDao.selectByUsername(vo.getUsername());
if (sysUser == null) {
throw new BusinessException(3001, "不存在该用户,请先注册");
}
if (sysUser.getStatus() == 2) {
throw new BusinessException(3002, "该账号已被禁用请联系系统管理员");
}
if (!PasswordUtils.matches(sysUser.getSalt(), vo.getPassword(), sysUser.getPassword())) {
throw new BusinessException(3003, "用户名密码不匹配");
}
String token = UUID.randomUUID().toString();
LoginRespVO loginRespVO = new LoginRespVO();
loginRespVO.setUserId(sysUser.getId());
loginRespVO.setToken(token);
//分布式session凭证存入redis 60分钟失效
redisService.set(token,sysUser.getId(),60, TimeUnit.MINUTES);
//异地登录提醒下线
redisService.set(sysUser.getId(),token,60,TimeUnit.MINUTES);
return loginRespVO;
}
@Override
public SysUser getUserInfo(String id) {
return sysUserDao.selectByPrimaryKey(id);
}
/**
* 短信验证码功能
* @param phone
* @return
*/
@Override
public String getCode(String phone) {
//验证手机号是否合法
Pattern pattern = Pattern.compile("^1(3|4|5|7|8)\\d{9}$");
Matcher matcher = pattern.matcher(phone);
if(!matcher.matches()) {
throw new BusinessException(3014,"手机号格式错误");
}
//判断手机号是否超限
long count = redisService.incrby(Content.REGISTER_CODE_COUNT_KEY+phone,1);
if(count>5){
throw new BusinessException(3015,"当日发送已达上限");
}
//生成6位随机数
String code=generateCode();
//发送短信(具体根据你们公司所用的api文档来)
//存入 redis 过期时间为 5 分钟
redisService.set(Content.REGISTER_CODE_COUNT_VALIDITY_KEY+phone,code,5,TimeUnit.MINUTES);
//发送短信这里用输出模拟
System.out.println(code);
return code;
}
/**
* 生成六位验证码
* @return
*/
private String generateCode(){
Random random = new Random();
int x = random.nextInt(899999);
String code = String.valueOf(x + 100000);
return code;
}
}
13、controller层
package com.example.distributed.demo.controller;
import com.example.distributed.demo.service.impl.RedisService;
import com.example.distributed.demo.service.intefacer.CartService;
import com.example.distributed.demo.vo.request.AddCartReqVO;
import com.example.distributed.demo.vo.response.CartRespVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 22:03
*/
@RestController
@RequestMapping("/api")
@Api(tags = "购物车模块",description = "购物车模块相关接口")
public class CartController {
@Autowired
private RedisService redisService;
@Autowired
private CartService cartService;
@PostMapping("/cart")
@ApiOperation(value = "加入购物车")
public String addCart(@RequestBody AddCartReqVO vo, HttpServletRequest request){
String token=request.getHeader("token");
String userId= (String) redisService.get(token);
return cartService.addCart(vo,userId);
}
@GetMapping("/cart")
@ApiOperation(value = "获取用户购物车数据")
public List<CartRespVO> showCart(HttpServletRequest request){
String token=request.getHeader("token");
String userId= (String) redisService.get(token);
System.out.println(userId);
return cartService.showCart(userId);
}
}
package com.example.distributed.demo.controller;
import com.example.distributed.demo.service.intefacer.CodeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 21:41
*/
@RestController
@RequestMapping("/api")
@Api(tags = "订单模块",description = "订单模块相关接口")
public class OrderController {
@Autowired
private CodeService codeService;
@GetMapping("/order/code/{type}")
@ApiOperation(value = "生产订单编码")
public String getOrderCode(@PathVariable("type") String type){
return codeService.getOrderCode(type);
}
}
package com.example.distributed.demo.controller;
import com.example.distributed.demo.entity.SysUser;
import com.example.distributed.demo.service.intefacer.UserService;
import com.example.distributed.demo.vo.request.LoginReqVO;
import com.example.distributed.demo.vo.request.RegisterReqVO;
import com.example.distributed.demo.vo.response.LoginRespVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author 刘阳洋
* @date 2020/5/17 0017 17:03
*/
@RestController
@RequestMapping("/api")
@Api(tags = "用户模块",description = "用户模块相关接口")
public class UserController {
//http://localhost:8889/swagger-ui.html
// http://localhost:8889/api/user/register
@Autowired
private UserService userService;
@PostMapping("/user/register")
@ApiOperation(value = "用户注册接口")
public String register(@RequestBody RegisterReqVO vo){
return userService.register(vo);
}
// http://localhost:8889/api/user/login
@PostMapping("/user/login")
@ApiOperation(value = "用户登录接口")
public LoginRespVO login(@RequestBody LoginReqVO vo){
return userService.login(vo);
}
@GetMapping("/user/{id}")
@ApiOperation(value = "获取用户信息接口")
public SysUser getUserInfo(@PathVariable("id") String id){
return userService.getUserInfo(id);
}
@GetMapping("/user/code/{phone}")
public String getCode(@PathVariable("phone") String phone){
return userService.getCode(phone);
}
}
14、启动类
package com.example.distributed.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.distributed.demo.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
System.out.println("启动成功");
}
}
15测试类
package com.example.distributed.demo;
import com.example.distributed.demo.entity.User;
import com.example.distributed.demo.service.impl.RedisService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
private RedisService redisService;
@Test
public void testRedis() {
User user=new User();
user.setUsername("admin");
user.setPassowrd("刘阳洋");
redisService.set("user",user);
System.out.println("结果为:" + redisService.get("user"));
}
}
16 、测试
登录 swagger进行测试
http://localhost:8889/swagger-ui.html
http://localhost:8889/api/user/register
http://localhost:8889/api/user/login