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