苍穹外卖学习笔记(五)

由于前面已经有系统学习Redis的文章,这里不再详细书写Redis的入门知识(数据结构、常用命令)

一. Java中操作Redis

Redis的Java客户端

常见的几种:

  1. Jedis
  2. Lettuce
  3. Spring Data Redis(建议)

Spring Data Redis使用方式

操作步骤:

  1. 导入Spring data Redis的Maven坐标
    pom.xml
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置Redis数据源
    application.yml
1
2
3
4
spring:
redis:
host: localhost
port: 6379
  1. 编写配置类,创建RedisTemplate对象
    RedisConfiguration.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
@Slf4j
public class RedisConfiguration {

@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
log.info("开始创建Redis模板对象.....");
//设置Redis工厂对象
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置key的序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
//设置value的序列化器
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}
  1. 通过RedisTemplate操作Redis
    SpringDataRedisTest.java
1
2
3
4
5
6
7
8
9
10
11
12
@SpringBootTest
public class SpringDataRedisTest {

@Autowired
private RedisTemplate redisTemplate;

@Test
public void testRedisTemplate() {
redisTemplate.opsForValue().set("name", "sky");
System.out.println(redisTemplate.opsForValue().get("name"));
}
}

完整测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
@SpringBootTest
public class SpringDataRedisTest {

@Autowired
private RedisTemplate redisTemplate;

@Test
public void testRedisTemplate() {
ValueOperations valueOperations = redisTemplate.opsForValue();
HashOperations hashOperations = redisTemplate.opsForHash();
ListOperations listOperations = redisTemplate.opsForList();
SetOperations setOperations = redisTemplate.opsForSet();
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
}

/**
* 测试字符串
*/
@Test
public void testString() {
//set get setex setnx
redisTemplate.opsForValue().set("city", "beijing");
String city = (String) redisTemplate.opsForValue().get("city");
System.out.println(city);
redisTemplate.opsForValue().set("code", "1234", 3, TimeUnit.MINUTES);
redisTemplate.opsForValue().setIfAbsent("lock", "1");
redisTemplate.opsForValue().setIfAbsent("lock", "2");
}

/**
* 测试hash
*/
@Test
public void testHash() {
//hset hget hdel hkeys hvals
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.put("100", "name", "tom");
hashOperations.put("100", "age", "20");
String name = (String) hashOperations.get("100", "name");
System.out.println(name);
hashOperations.keys("100"); //[name, age]
hashOperations.values("100");//[tom, 20]
hashOperations.delete("100", "age");
}

/**
* 测试list
*/
@Test
public void testList() {
//lpush rpush lpop rpop lrange
ListOperations listOperations = redisTemplate.opsForList();
listOperations.leftPush("list", "a");
listOperations.leftPush("list", "b");
listOperations.leftPush("list", "c");
listOperations.rightPush("list", "d");
listOperations.rightPush("list", "e");
listOperations.rightPush("list", "f");
String value = (String) listOperations.leftPop("list");
System.out.println(value);
listOperations.range("list", 0, -1);
}

/**
* 测试set
*/
@Test
public void testSet() {
//sadd smembers srem
SetOperations setOperations = redisTemplate.opsForSet();
setOperations.add("set", "a", "b", "c", "d", "e");
setOperations.remove("set", "a", "b");
setOperations.members("set");
}

/**
* 测试zset
*/
@Test
public void testZSet() {
//zadd zrange zrem
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
zSetOperations.add("zset", "a", 1);
zSetOperations.add("zset", "b", 2);
zSetOperations.add("zset", "c", 3);
zSetOperations.add("zset", "d", 4);
zSetOperations.add("zset", "e", 5);
zSetOperations.range("zset", 0, -1);
zSetOperations.remove("zset", "a", "b");
}

/**
* 通用
*/
@Test
public void testCommon() {
Set keys = redisTemplate.keys("*");
System.out.println(keys);

Boolean city = redisTemplate.hasKey("city");
Boolean code = redisTemplate.hasKey("code");

for (Object key : keys) {
DataType type = redisTemplate.type(key);
System.out.println(type.name());
}
redisTemplate.delete("lock");
}
}

不用了记得注释,不然影响启动速度

二. 店铺营业状态设置

营业状态数据存储方式:基于Redis的字符串进行存储(1表示营业 0表示打烊)

代码

adminShopController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.sky.controller.admin;

import com.sky.result.Result;
import com.sky.service.ShopService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController("adminShopController")
@RequestMapping("/admin/shop")
@Api(tags = "商铺管理")
@Slf4j
public class ShopController {

@Autowired
private ShopService shopService;

/**
* 设置商铺状态
*
* @param status
* @return
*/
@PutMapping("/{status}")
@ApiOperation(value = "设置商铺状态")
public Result setStatus(@PathVariable Integer status) {
log.info("设置商铺状态:{}", status == 1 ? "营业中" : "打烊了");
shopService.setStatus(status);
return Result.success();
}

/**
* 获取商铺状态
*
* @return
*/
@GetMapping("/status")
@ApiOperation(value = "获取商铺状态")
public Result<Integer> getStatus() {
Integer status = shopService.getStatus();
log.info("获取商铺状态:{}", status == 1 ? "营业中" : "打烊了");
return Result.success(status);
}
}

userShopController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.sky.controller.user;

import com.sky.result.Result;
import com.sky.service.ShopService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController("userShopController")
@RequestMapping("/user/shop")
@Api(tags = "商铺管理")
@Slf4j
public class ShopController {

@Autowired
private ShopService shopService;

/**
* 获取商铺状态
*
* @return
*/
@GetMapping("/status")
@ApiOperation(value = "获取商铺状态")
public Result<Integer> getStatus() {
Integer status = shopService.getStatus();
log.info("获取商铺状态:{}", status == 1 ? "营业中" : "打烊了");
return Result.success(status);
}
}

ShopService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.sky.service;

public interface ShopService {

/**
* 设置商铺状态
*
* @param status
*/
void setStatus(Integer status);


/**
* 获取商铺状态
*
* @return
*/
Integer getStatus();
}

ShopServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.sky.service.impl;

import com.sky.service.ShopService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

/**
* @author Jie.
* @description: TODO
* @date 2024/9/22
* @version: 1.0
*/
@Service
@Slf4j
public class ShopServiceImpl implements ShopService {

public static final String SHOP_STATUS = "SHOP_STATUS";

@Autowired
private RedisTemplate redisTemplate;

/**
* 设置商铺状态
*
* @param status
*/
@Override
public void setStatus(Integer status) {
redisTemplate.opsForValue().set(SHOP_STATUS, status);
}

/**
* 获取商铺状态
*
* @return
*/
@Override
public Integer getStatus() {
Integer shopStatus = (Integer) redisTemplate.opsForValue().get(SHOP_STATUS);
return shopStatus == null ? 0 : shopStatus;
}
}

注意

记得把RedisConfiguration中的value的序列化器注释掉,否则会导致Redis的类型转换问题

Swagger文档优化

WebMvcConfiguration.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package com.sky.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.sky.interceptor.JwtTokenAdminInterceptor;
import com.sky.json.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.List;

/**
* 配置类,注册web层相关组件
*/
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

@Autowired
private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;

/**
* 注册自定义拦截器
*
* @param registry
*/
protected void addInterceptors(InterceptorRegistry registry) {
log.info("开始注册自定义拦截器...");
registry.addInterceptor(jwtTokenAdminInterceptor)
.addPathPatterns("/admin/**")
.excludePathPatterns("/admin/employee/login");
}

/**
* 通过knife4j生成接口文档
*
* @return
*/
@Bean
public Docket docket1() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("admin")
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin"))
.paths(PathSelectors.any())
.build();
return docket;
}

@Bean
public Docket docket2() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("user")
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.user"))
.paths(PathSelectors.any())
.build();
return docket;
}
/**
* 设置静态资源映射
*
* @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}

/**
* 配置消息转换器
*
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("配置消息转换器...");
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new JacksonObjectMapper());
converters.add(0, converter);
}

/**
* 配置 MyBatis-Plus 的全局设置
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
log.info("配置 MyBatis-Plus 的全局设置...");
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 多租户插件
// interceptor.addInnerInterceptor(new TenantLineInnerInterceptor());
// 防止全表更新与删除插件
// interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
}