Redis快速入门店铺营业状态设置
Redis简介
Redis是一种基于内存的键值对(K-V)数据库。
这意味着它与MySQL数据库类似,都能够用于存储数据,但两者又有着本质的区别。首先两者存储数据的结构不一样,Redis通过键(key)和值(value)的映射来存储信息,其中键是唯一的标识,而值可以是灵活多样的数据结构,如字符串、列表、集合等。MySQL则采用二维表的结构来组织数据,这种结构更适合复杂的关系型数据存储。
其次两者的存储介质不同,Redis将其数据存储在内存中,这种内存存储机制使得Redis在数据读写操作上远超基于磁盘存储的MySQL。而MySQL则将数据以文件形式存储在硬盘上,虽然这为数据持久化提供了保障,但在性能上自然无法与内存中的Redis相提并论。
简而言之,Redis的内存存储与MySQL的磁盘存储,分别代表了性能与持久性的两种不同选择。因此两者虽都是数据库,但并不意味着能取代对方,两者互相搭配使用才合适。由于Redis存储在内存中,所以能够提供极快的访问速度,读写性能较高,但又因为内存的空间较为宝贵,所以只适合存储一些访问频率较高的数据。
Redis官网https://redis.ioRedis中文网https://www.redis.net.cn/
下载与安装
下载Redis的Windows版,该版本属于绿色软件,直接解压就可使用,解压后有几个比较重要的文件:
- redis.windows.conf:Redis配置文件—修改端口、密码等
- redis-cli.exe:Redis客户端—连接到Redis服务
- redis-server.exe:Redis服务端—服务端启动命令
我们先来介绍如何通过CMD窗口操作Redis(仅供了解)
通过CMD窗口启动须在当前目录的路径框中输入cmd:
然后输入"redis-server.exe redis.windows.conf"(注意中间有一个空格) 显示对应页面即为启动成功,页面显示其默认端口号为6379:
停止该服务在CDM窗口按下ctrl+c即可。我们在服务开启的前提下再使用同样的方法打开一个新的CMD窗口,然后输入redis-cli.exe客户端即可连接到本地的Redis服务器。我们也可以输入key *来验证是否连接成功:
此时就已连接到了本地的服务器,连接外部的服务器也大差不差,我们先输入exit退出当前连接,然后输入"redis-cli -h 主机地址 -p 端口号 -a 密码"即可,我们可以使用本地服务器代替:redis-cli -h localhost -p 6379,同样可以输入key *来验证是否连接成功。
可以注意到此时本地的redis服务器并无密码,我们可以先停止redis服务,然后到redis.windows.conf文件中搜索requirepass,修改"# requirepass foobared"为"requirepass 密码",保存即可(学习时推荐使用123456)。
接下来下载Redis 可视化工具:Another Redis Desktop Manager github
该可视化工具为免费的开源项目,页面非常简洁美观,基本的功能都有,支持多平台、监控统计、页面主题、集群、搜索,分组显示,多选操作。下面是Github和Gitee的下载网址
https://github.com/qishibo/AnotherRedisDesktopManager/releaseshttp://xn--github-np7ii83deeq211d
https://gitee.com/qishibo/AnotherRedisDesktopManager/releaseshttp://xn--gitee-gi1hh06cxd9647c 启动后输入相关信息,在启动Redis数据库的前提下,页面显示如下即为连接成功:
Redis数据类型
上文介绍过Redis通过键(key)和值(value)的映射来存储信息,其中键(key)为string类型,值(value)则有5种常用的数据类型,分别是:
一、字符串string
普通字符串,Redis中最简单的数据类型。它可以包含任何数据,如文本、数字或图片等。
二、哈希hash
也叫散列,类似于java中的HashMap结构。
注意是在value中存储K-V的数据,整体结构为K-(K-V)。例如key为user,value为(name=aaa,password=aaa......)。
三、列表list
按照插入的顺序排序,可以有重复元素,类似于java中的LinkedList(双向链表)。
四、集合set
无序集合,没有重复元素,类似于java中的HashSet,常用于好友列表。
五、有序集合sorted set/zset
集合中每个元素都关联一个分数(score),根据分数升降排序,没有重复元素。
Redis常用命令
我们可以通过这些命令来操作Redis中的数据。但其与MySQL中操作数据不同,操作Redis需要根据数据类型的不同执行不同的命令,同时Redis和MySQL一样是不区分大小写的。接下来我们来看五种类型的常用命令:
一、字符串操作命令
- SET key value:设置指定key的值
- GET key:获取指定key的值
- SETEX key seconds value:设置指定key的值,并将key的过期时间设为seconds秒
- SETNX key value:只有在key不存在时设置key的值
接下来我们来练习如何使用这些命令,打开刚刚已配置好的图形界面,点击左上角的打开Redis控制台,即可在右下方的输入框输入命令set name jack并回车,上方就会显示已执行过的命令与"OK"字样,左侧显示已存在的Key的值,点击该Key即可在右侧新标签页查看Value的信息,包括类型、值等。
> test connected! # 测试连接成功
> set name jack # 设置键"name"的值为"jack"
OK
> get name # 获取键"name"的值
jack
> get aaa # 尝试获取不存在的键"aaa",返回null
null
> SETEX code 30 mycode # 设置键"code"的值为"mycode",并设置过期时间为30秒
OK
> get code # 获取键"code"的值,此时还未过期
mycode
> get code # 再次获取键"code"的值,此时可能已经过期,返回null
null
> SETNX name aaaa # 尝试设置键"name"的值为"aaaa",如果"name"已存在则不设置,返回0表示失败
0
> SETNX name1 aaaa # 尝试设置键"name1"的值为"aaaa",因为"name1"不存在,设置成功,返回1
1
如果添加的key在左侧不显示,可以点击控制台旁边的刷新按钮即可。在value值标签页中,包含了key的值、value的类型、value的值、value的过期时间等信息。
二、哈希操作命令
Redis hash 是一个 string 类型的 field 和 value 的映射表, hash 特别适合用于存储对象, 整体结构为K-(K-V):
常用命令:
- HSET key field aaaa:将哈希表 key 中的字段 field 的值设置为指定的值
- HGET key field:获取存储在哈希表中指定字段的值
- HDEL key field:删除存储在哈希表中的指定字段
- HKEYS key:获取哈希表中所有字段
- HVALS key:获取哈希表中所有值
> hset user name jack # 在哈希表"user"中设置字段"name"的值为"jack",如果字段不存在则创建,返回1表示成功
1
> hset user age 22 # 在哈希表"user"中设置字段"age"的值为"22",如果字段不存在则创建,返回1表示成功
1
> hget user name # 获取哈希表"user"中字段"name"的值
jack
> hdel user age # 删除哈希表"user"中的字段"age",如果字段存在则删除,返回1表示成功
1
> hset user age 22 # 再次在哈希表"user"中设置字段"age"的值为"22",因为字段已被删除,这里重新创建字段
1
> hkeys user # 获取哈希表"user"中所有的字段名
name
age
> hvals user # 获取哈希表"user"中所有的字段值
jack
22
三、列表操作命令
Redis列表是简单的字符串列表,按照插入顺序排序,基本结构为:
列表操作命令大多分类R和L两个版本,R意为right,L意为left。为这些数据生成的索引也是从0开始的。常用命令:
- LPUSH key value1 [value2]:将一个或多个值插入到列表头部([]意为可选),多个元素直接以空格分开。其结构类似于栈,先插入的在尾部
- LRANGE key start stop:获取列表指定范围内的元素,start和stop均为value的索引,后插入的value索引值小。
- RPOP key:移除并获取列表最后一个元素
- LLEN key 获取列表长度
> lpush mylist a b c # 将字符串 'a', 'b', 'c' 依次推入列表 mylist 的头部,返回列表的长度,此时列表为 [c, b, a]
3 # 列表操作后长度为 3
> lpush mylist d # 将字符串 'd' 推入列表 mylist 的头部,返回列表的长度,此时列表为 [d, c, b, a]
4 # 列表操作后长度为 4
> lrange mylist 0 -1 # 获取列表 mylist 从索引 0 到最后一个元素(-1 表示最后一个元素),即获取整个列表
d
c
b
a # 列表元素依次为 'd', 'c', 'b', 'a'
> lrange mylist 1 3 # 获取列表 mylist 从索引 1 到索引 3 的元素,但不包括索引 3 的元素
c
b
a # 列表元素依次为 'c', 'b', 'a'
> rpop mylist # 移除列表 mylist 的最后一个元素,并返回该元素的值,此时列表最后一个元素 'a' 被移除
a # 移除的元素是 'a'
> llen mylist # 获取列表 mylist 的长度
3 # 列表操作后长度为 3,此时列表为 [d, c, b]
四、集合操作命令
Redis set 是string类型的无序集合。 集合成员是唯一的, 集合中不能出现重复的数据。因为其是无序的,所以实际存储顺序可能和插入顺序不一致。常用命令:
- SADD key member1 [member2]:向集合添加一个或多个成员
- SMEMBERS key:返回集合中的所有成员,
- SCARD key:获取集合的成员数
- SINTER key1 [key2]:返回给定所有集合的交集
- SUNION key1 [key2]:返回所有给定集合的并集
- SREM key member1 [member2]:删除集合中的一个或多个成员
# 向集合 setkey 添加元素 'a', 'b', 'c', 'd',并返回成功添加的元素数量
sadd setkey a b c d
4 # 表示有4个元素被成功添加到集合中
# 尝试再次向集合 setkey 添加元素 'a',但由于 'a' 已存在,不会重复添加,返回0
sadd setkey a
0 # 表示没有元素被添加,因为 'a' 已经在集合中
# 获取并显示集合 setkey 中的所有成员
smembers setkey
c
b
d
a # 显示集合中的所有元素,注意集合是无序的
# 获取集合 setkey 的基数,即集合中元素的数量
SCARD setkey
4 # 集合 setkey 中有4个元素
# 向集合 setkey2 添加元素 'a', 'b', 'x', 'y',并返回成功添加的元素数量
sadd setkey2 a b x y
4 # 表示有4个元素被成功添加到集合 setkey2 中
# 获取集合 setkey 和 setkey2 的交集,即两个集合共有的元素
sinter setkey setkey2
b
a # 显示集合 setkey 和 setkey2 的交集元素
# 获取集合 setkey 和 setkey2 的并集,即两个集合中所有的元素,重复的只计算一次
sunion setkey setkey2
c
x
a
d
b
y # 显示集合 setkey 和 setkey2 的并集元素
# 从集合 setkey2 中移除元素 'a',并返回成功移除的元素数量
srem setkey2 a
1 # 表示成功移除了1个元素 'a'
实际存储顺序可能和插入顺序不一致:
五、有序集合操作命令
Redis有序集合是string类型元素的集合,且不允许有重复成员。每个元素都会关联一个double类型的分数。常用命令:
- ZADD key score1 member1 [score2 member2]:向有序集合添加一个或多个成员
- ZRANGE key start stop [WITHSCORES]:通过索引区间返回有序集合中指定区间的成员,添加WITHSCORES表示将分数也一起返回
- ZINCRBY key increment member:对指定成员的分数增加increment
- ZREM key member [member…]:移除有序集合中的一个或多个成员
# 向有序集合 zset 添加三个成员及其分数
# 成员 'a' 的分数是 10.0,成员 'b' 的分数是 10.2,成员 'c' 的分数是 10.5
# 返回值 3 表示成功添加了三个成员
ZADD zset 10.0 a 10.2 b 10.5 c
3
# 获取有序集合 zset 中所有成员及其分数,从索引 0 到最后一个成员
# withscores 参数表示输出结果中包含每个成员的分数
# 输出结果按照分数从小到大排序
ZRANGE zset 0 -1 withscores
a
10
b
10.199999999999999
c
10.5
# 将有序集合 zset 中成员 'a' 的分数增加 0.1
# 返回值是成员 'a' 增加后的新分数
ZINCRBY zset 0.1 a
10.1
# 获取有序集合 zset 中成员 'c' 的倒序排名(从分数最高到最低)
# 返回值 0 表示 'c' 是分数最高的成员
ZREVRANK zset c
0
# 从有序集合 zset 中移除成员 'c'
# 返回值 1 表示成功移除了一个成员
ZREM zset c
1
六、通用命令
Redis的通用命令是不分数据类型的,都可以使用的命令:
- KEYS pattern:查找所有符合给定模式(pattern)的key,pattern可以是"*"、""、""、""。
- EXISTS key:检查给定key是否存在
- TYPE key:返回key所储存的值的类型
- DEL key:该命令用于在key存在是删除key
> keys * # 列出所有以任意字符开头的键
zset
name1
user
setkey2
name
setkey
mylist
> keys set* # 列出所有以"set"开头的键
setkey2
setkey
> EXISTS user # 检查键"user"是否存在,1表示存在
1
> EXISTS user1 # 检查键"user1"是否存在,0表示不存在
0
> TYPE mylist # 查询键"mylist"的数据类型,返回"list"表示它是一个列表
list
> DEL mylist # 删除键"mylist"及其值,返回1表示删除成功
1
使用Redis
通过上述的命令,我们就可以操作Redis中的数据,但最终我们需要为项目服务,也就是说需要在java程序中操作Redis。
Redis的java客户端
Redis的java客户端有很多,常用的有
一、 Jedis
Jedis是一个简单而强大的Java客户端,用于与Redis进行通信。它提供了完整的Redis命令的API,并支持连接池管理。Jedis使用直接连接到Redis服务器的方式,是Redis的原生Java客户端之一。
二、 Lettuce
Lettuce是一个高性能的Redis客户端,基于Netty框架实现。它使用异步和反应式编程模型,可以更有效地利用网络资源。
三、Spring Data Redis
Spring Data Redis是Spring的一部分,对Redis底层开发包(Jedis和Lettuce)进行了高度封装。在Spring项目中,可以使用Spring Data Redis来简化操作。
idea使用Redis准备事项
总计需要四步
一、导入Spring Data Redis 的maven坐标
打开idea中原有的项目,原有代码已经在server模块的pom.xml中预先添加了,我们检查是否正确即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
二、配置Redis数据源
之前在创建Redis数据库时,系统总计创建了从0-15总计16个数据库,每个数据库之间的数据都是相互隔离的,我们之前使用的都是默认的0号数据库。
idea中同理,默认使用0号数据库,如需指定则需配置spring.redis.database:10,代表想要使用10号数据库。
同样,我们不推荐使用硬编码的方式配置数据库信息,因此我们选择先在application-dev.yml中配置数据库信息,然后引用其信息。在server模块的application.yml和application-dev.yml中添加属性:
————application-dev.yml————————————————————————————
sky:
......
redis: # Spring Boot 配置Redis属性
host: localhost # Redis服务器地址
port: 6379 # Redis服务器端口
password: 123456 # Redis服务器密码
database: 10 # 选择哪个数据库,默认为0
————application.yml————————————————————————————————
spring:
......
redis:
host: ${sky.redis.host}
port: ${sky.redis.port}
password: ${sky.redis.password}
database: ${sky.redis.database}
三、编写配置类,创建RedisTemplate对象
在server模块的config包下新建RedisConfiguration类,并添加注解@Configuration将其作为配置类,然后新建一方法返回类型为RedisTemplate,在方法上加入注解@Bean并在该方法的参数中注入RedisConnectionFactory实例。
再在方法体中新建RedisTemplate类的实例,编写对应代码以完成设置Redis连接工厂对象和设置Redis key的序列化器两任务,最后返回RedisTemplate类的实例。
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
log.info("开始创建Redis模板对象");
RedisTemplate redisTemplate = new RedisTemplate();
//设置Redis连接工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置Redis key的序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
};
}
四、通过RedisTemplate对象操作Redis
我们可以先调用测试方法来测试RedisTemplate对象是否能够成功创建,在server模块下的test-java包中新建与启动类一致的目录层级:com.sky.test.SpringDataRedisTest,然后添加相关注解并注入RedisTemplate:
@SpringBootTest
public class SpringDataRedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testRedis() {
System.out.println(redisTemplate);
}
}
如果测试通过即代码 RedisTemplate对象可以被成功创建:
后续操作Redis都会使用到注入的RedisTemplate对象redisTemplate。之前介绍过Redis中的五种数据类型,每种数据类型的操作命令都不同, 在redisTemplate也封装了五个接口用于操作对应的数据:
@Test
public void testRedis() {
// 打印redisTemplate实例,用于确认Redis模板是否已经成功注入
System.out.println(redisTemplate);
// 获取ValueOperations对象,用于执行Redis字符串类型的操作
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
// 获取HashOperations对象,用于执行Redis哈希表类型的操作
HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
// 获取ListOperations对象,用于执行Redis列表类型的操作
ListOperations<String, String> listOperations = redisTemplate.opsForList();
// 获取SetOperations对象,用于执行Redis集合类型的操作
SetOperations<String, String> setOperations = redisTemplate.opsForSet();
// 获取ZSetOperations对象,用于执行Redis有序集合类型的操作
ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
}
接下来学习如何在java程序中通过Spring Data Redis来操作Redis当中的数据。
Idea使用Redis
其同样根据类型的不同,操作方法也不同。先来看如何操作Redis中字符串类的数据。
一、字符串操作命令
其虽在Redis中保存的类型为字符串,但与java中的String仍略有不同,因此在idea中可将其视为Object类来操作,也就是说可以传入任何数据,系统会将其转为Redis的String类型再存入数据库。同理,其返回值也是Object类型,想要使用String类型接收则需强转。
上文介绍给字符串类型的常用操作命令共有四个SET、GET、SETEX、SETNX,其在java程序中也有着对应的方法。两者之间有一定相似,但又大有不同。
我们着重介绍SETEX命令对应的方法,也就是指定key在指定时间后过期的方法。该方法前两个参数同样为key和value,第三个参数表示键值对的过期时间,即这个键值对将在X个时间单位后过期。第四个参数,是一个枚举类型,用于指定过期时间的单位。在这个例子中,TimeUnit.MINUTES表示过期时间的单位是分钟,即key2会在三分钟后过期。
@SpringBootTest
public class SpringDataRedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testString() {
// 打印redisTemplate的引用,主要用于调试查看是否注入成功
System.out.println(redisTemplate);
// 使用ValueOperations设置一个键值对,键为"key1",值为"value1"
redisTemplate.opsForValue().set("key1", "value1");
// 从Redis中获取键为"key1"的值,并将其转换为String类型
String key1 = (String) redisTemplate.opsForValue().get("key1");
System.out.println(key1);
// 设置一个带有过期时间的键值对,键为"key2",值为"value2",过期时间为3分钟
redisTemplate.opsForValue().set("key2", "value2", 3, TimeUnit.MINUTES);
// 使用setIfAbsent方法尝试设置键值对,只有当键不存在时才会设置成功
// 如果"key3"不存在,则设置"key3"的值为"value3"
redisTemplate.opsForValue().setIfAbsent("key3", "value3");
// 再次尝试设置"key3",由于"key3"已经存在,这个操作不会改变"key3"的值
redisTemplate.opsForValue().setIfAbsent("key3", "value3");
}
}
同时在图形工具中我们可以看到,key的值与idea中设置的值一致,但value却是"乱码",这是因为在RedisConfiguration类中我们使用语句
redisTemplate.setKeySerializer(new StringRedisSerializer());
设置了Redis key的序列化器,如果将该语句注释掉,系统会使用默认的序列化器,key的值也会变为乱码。
二、哈希操作命令
哈希常用的操作命令有五个HSET、HGET key field、HDEL key field、HKEYS key、HVALS key。
@Test
public void testHashOperations() {
// 获取哈希操作对象,用于执行哈希相关的Redis命令
HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
// 将键值对存入哈希表,其中"user"是哈希表的键,"username"是字段,"jack"是值
hashOperations.put("user", "username", "jack");
// 将另一个键值对存入同一个哈希表,"age"是字段,"32"是值
hashOperations.put("user", "age", "32");
// 从哈希表中获取字段"username"的值
String name = (String) hashOperations.get("user", "username");
// 打印获取到的用户名
System.out.println(name); // 应该输出 "jack"
// 获取哈希表"user"中所有的字段
Set<Object> keys = hashOperations.keys("user");
// 打印所有字段
System.out.println(keys); // 应该输出包含 "username" 和 "age" 的集合
// 获取哈希表"user"中所有的值
List<Object> values = hashOperations.values("user");
// 打印所有值
System.out.println(values); // 应该输出包含 "jack" 和 "32" 的列表
// 从哈希表中删除字段"username"
hashOperations.delete("user", "username");
// 此时,哈希表"user"中应该只剩下字段"age"和它的值"32"
}
三、列表操作命令
其大致相同,不再赘述,直接看代码:
@Test
public void testList(){
//之前学习的方法:lpush lrange rpop llen
// 获取操作列表类型的对象
ListOperations listOperations = redisTemplate.opsForList();
// 将多个元素从左边插入到列表中
listOperations.leftPushAll("mylist","a", "b", "c");
// 再将一个元素从左边插入到列表中
listOperations.leftPush("mylist", "d");
// 获取列表的所有元素
List myList = listOperations.range("mylist", 0, -1);
System.out.println(myList);
// 从右边弹出列表中的一个元素
listOperations.rightPop("mylist");
// 获取列表的大小
Long size = listOperations.size("mylist");
System.out.println(size);
}
四、集合操作命令
@Test
public void testSet() {
//sadd smembers scard sinter sunion srem
// 创建一个用于集合操作的实例
SetOperations<String, String> setOperations = redisTemplate.opsForSet();
// 向集合set1添加元素 a, b, c, d
setOperations.add("set1", "a", "b", "c", "d");
// 向集合set2添加元素 a, b, x, y
setOperations.add("set2", "a", "b", "x", "y");
// 获取集合set1的所有成员
Set<String> members = setOperations.members("set1");
System.out.println(members);
// 获取集合set1的大小
Long size = setOperations.size("set1");
System.out.println(size);
// 计算集合set1和set2的交集
Set<String> intersect = setOperations.intersect("set1", "set2");
System.out.println(intersect);
// 计算集合set1和set2的并集
Set<String> union = setOperations.union("set1", "set2");
System.out.println(union);
// 从集合set1中移除元素 a 和 b
setOperations.remove("set1", "a", "b");
}
//控制台输出
[d, c, b, a]
4
[b, a]
[x, d, a, c, b, y]
五、有序集合操作命令
@Test
public void testZset() {
//zadd zrange zincrby zrem
// 获取有序集合操作实例
ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
// 向有序集合添加元素及其分数
zSetOperations.add("zset1", "a", 10);
zSetOperations.add("zset1", "b", 12);
zSetOperations.add("zset1", "c", 9);
// 获取有序集合中的所有元素
Set<String> zset1 = zSetOperations.range("zset1", 0, -1);
System.out.println(zset1);
// 增加元素c的分数
zSetOperations.incrementScore("zset1", "c", 10);
// 从有序集合中移除元素a和b
zSetOperations.remove("zset1", "a", "b");
}
六、通用命令
上述命令操作前,都需先获取对应的Operations对象然后再进行操作,但通用命令可直接通过RedisTemplate类的实例redisTemplate来操作。
@Test
public void testCommon() {
//keys exists type del
// 获取所有键
Set keys = redisTemplate.keys("*");
System.out.println(keys);
// 检查特定键是否存在
Boolean nameExists = redisTemplate.hasKey("name");
Boolean set1Exists = redisTemplate.hasKey("set1");
// 遍历所有键并打印其数据类型
for (Object key : keys) {
DataType dataType = redisTemplate.type(key);
System.out.println(dataType.name());
}
// 删除指定键
redisTemplate.delete("mylist");
}
//控制台输出
[set2, mylist, set1, key3, user, zset1, key1]
SET
LIST
SET
STRING
HASH
ZSET
STRING
此时各功能均已简单介绍,因此测试各功能是在测试类中进行的, 在苍穹外卖项目中并不需要该测试类及其内部的方法,为避免测试类拖慢项目启动速度,我们可以将测试类上方的@SpringBootTest注释掉。
店铺营业状态设置
店铺左上角有一营业信息表示是否营业,点击右上角的营业状态设置可修改营业信息。
当前餐厅处于营业状态时,当前餐厅处于打烊状态时,仅接受营业时间内的预定订单,可点击营业中手动恢复营业状态。
接下来分析所需的接口,首先是左上角查询营业信息需要一个查询接口,同时不仅商家需要查询是否营业的接口,用户的小程序也需类似的接口,从技术层面来说两者可共用一个接口,但在该项目中为了规范请求路径(管理端发出的请求统一使用/admin作为前缀、用户端发出的请求统一使用/user作为前缀),我们选择将其设计为两个不同的接口。
分析所需接口,首先上文提到的"管理端查询营业状态"、"用户端查询营业状态两接口",然后是设置营业状态所需的接口,我们依次来看。
设置营业状态
首先是设置营业状态,营业状态的值可通过多种方式传到后端:url地址、请求体、路径参数,在本项目我们选择通过路径参数传递值。
请求路径/admin/shop/{status},请求方法put,传递的值为1则表示营业,0为打烊。
存储营业信息的数据表只需一个字段表示状态,且只需要一列数据用于存储0或1,为了存储这一数据来单独新开一个MySQL表不合理,因此我们选择将其存储到Redis数据库中,选择基于Redis的字符串来进行存储。
@RestController
@RequestMapping("/admin/shop")
@Api(tags = "店铺相关接口")
@Slf4j
public class ShopControlller {
public static final String KEY = "SHOP_STATUS"; // 定义一个常量,用于Redis中存储店铺状态的键名
@Autowired // 自动注入RedisTemplate实例,用于操作Redis
private RedisTemplate redisTemplate;
@PutMapping("/{status}")
@ApiOperation("设置店铺的营业状态")
public Result setStatus(@PathVariable Integer status) { // 接收路径变量status作为方法参数
log.info("设置店铺的营业状态为:{}", status == 1 ? "营业中" : "打烊中");
redisTemplate.opsForValue().set(KEY, status); // 将店铺状态存储到Redis中,使用定义的KEY作为键
return Result.success();
}
}
管理端查询营业状态
请求路径/admin/shop/status,请求方法get。
@GetMapping("/status")
@ApiOperation("获取店铺营业状态") // 使用@ApiOperation注解描述该接口的功能,即获取店铺的营业状态
public Result<Integer> getStatus() { // 定义一个返回类型为Result<Integer>的方法
Integer shopStatus = (Integer) redisTemplate.opsForValue().get(KEY); // 从Redis中获取店铺状态
log.info("获取到的店铺营业状态为:{}", shopStatus == 1 ? "营业中" : "打烊中"); // 记录日志,输出获取到的店铺状态
return Result.success(shopStatus); // 返回包含店铺状态的Result对象
}
用户端查询营业状态
请求路径/user/shop/status,请求方法get。因为该功能为用户端的功能,为和管理端区分,我们需新建一个user包用于保存相关代码
然后将admin包的ShopController类复制到该包下并修改相关信息,再删除修改营业状态的信息。由于两包中的类名称一致,导致其生成的bean名称也一致,同时放到IOC容器中会发生冲突,因此需手动为两类指定bean的名称,分别修改两类的注解为@RestController("userShopController")和@RestController("adminShopController")。
@RestController("userShopController")
@RequestMapping("/user/shop")
@Api(tags = "用户相关接口")
@Slf4j
public class ShopController {
public static final String KEY="SHOP_STATUS";
@Autowired // 自动注入RedisTemplate实例
private RedisTemplate redisTemplate;
@GetMapping("/status")
@ApiOperation("获取店铺营业状态") // 使用@ApiOperation注解描述该接口的功能,即获取店铺的营业状态
public Result<Integer> getStatus() { // 定义一个返回类型为Result<Integer>的方法
Integer shopStatus = (Integer) redisTemplate.opsForValue().get(KEY); // 从Redis中获取店铺状态
log.info("获取到的店铺营业状态为:{}", shopStatus == 1 ? "营业中" : "打烊中"); // 记录日志,输出获取到的店铺状态
return Result.success(shopStatus); // 返回包含店铺状态的Result对象
}
}
代码已完成,开始测试,测试前先删除Redis数据库中的数据,点击对应图标并输入"yes"可快速删除所有数据:
此时无论是管理端接口还是用户端接口都在同一个接口文档下,不便管理,我们可以修改用于生成接口文档的server模块config包WebMvcConfiguration类的docket(),修改接口文档需要扫描的包,使其只扫描管理端接口。
再新建一个Docket类方法用于生成用户端接口的接口文档,并通过.groupName()方法,传入字符串来指定接口:
@Bean
public Docket adminDocket() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档title")
.version("2.0")
.description("苍穹外卖项目接口文档desc")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("管理端接口")
.apiInfo(apiInfo)
.select()
//指定接口文档需要扫描的包
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin"))
.paths(PathSelectors.any())
.build();
return docket;
}
/**
* 用户端接口文档
* @return
*/
@Bean
public Docket userDocket() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档title")
.version("2.0")
.description("苍穹外卖项目接口文档desc")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("用户端接口")
.apiInfo(apiInfo)
.select()
//指定接口文档需要扫描的包
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.user"))
.paths(PathSelectors.any())
.build();
return docket;
}
此时在接口文档页面就可以通过下拉列表区分不同的接口文档: