Day68(5)-F:\硕士阶段\Java\课程资料\1、黑马程序员Java项目《苍穹外卖》企业级开发实战\sky-take-out-Redis

苍穹外卖

Redis

Redis简介

redis是内存存储,通过键值对;mysql是磁盘存储,通过二维表。

image-20251224102613840

Redis的下载与安装

image-20251224102811240

image-20251224102849907

image-20251224103147931

redis默认端口6379,存储于conf文件中

Ctrl+C启动

image-20251224103516245

Redis设置密码

image-20251224103737282

ctrl + F 输入pass (pass+空格)

取消注释符,改为类似于requirepass 123456

设置密码之后服务端启动方式不变,不需要密码。用户端连接需要密码

image-20251224104252945

Redis图形化界面

image-20251224104655005

Redis常用数据类型

image-20251224104852721

image-20251224105531925

Redis的常用命令

字符串操作命令

image-20251224105845876

哈希操作命令

image-20251224110544161

列表操作命令

image-20251224111259256

获取所有元素 lrange mylist 0 -1

这个是双向列表,所以可以rpush mylist也可以lpop mylist

集合操作命令

image-20251224120435860

有序集合操作命令

image-20251224121058368

zset有序集合

根据score排序,默认升序

通用命令

image-20251224123048612

keys set*以set开头的key

Java中操作redis

image-20251224123613352

Spring Data Redis

Spring Data Redis使用方式(环境搭建+工具类配置)

image-20251224123738484

image-20251224123751088

image-20251224123924332

image-20251224124338766

image-20251224125027502

操作工具类

package com.sky.config;

import lombok.extern.slf4j.Slf4j;
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;

@Configuration
@Slf4j
public class RedisConfiguration {

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        log.info("开始创建redis模板对象");
        //引入start依赖后会把RedisConnectionFactory创建好并加入容器中
        RedisTemplate redisTemplate = new RedisTemplate();
        //设置redis连接过程对象
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置redis key序列化器(主要)
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

测试

设置redis key序列化器,防止key乱码(也可以设置其他的序列化器,都有对应方法,设置完就不会乱码)

package com.sky.test;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.*;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@SpringBootTest
//核心任务是寻找标注了@SpringBootApplication的入口类,并初步启动Spring上下文。如果不加它,测试程序就是一个普通的Java类,不会加载任何配置,也不会初始化任何Bean。
public class SpringDataRedisTest {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testRedisTemplate(){
        System.out.println(redisTemplate);
        //操作字符串类型的数据
        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("name","小明");
        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");
    }

    /**
     * 操作哈希类型的数据
     */
    @Test
    public void testHash(){
        //hset hget 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);

        Set keys = hashOperations.keys("100");
        System.out.println(keys);

        List values = hashOperations.values("100");
        System.out.println(values);

        hashOperations.delete("100","age");
    }

    /**
     * 操作列表类型的数据
     */
    @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 setOperations = redisTemplate.opsForSet();

        setOperations.add("set1","a","b","c","d");
        setOperations.add("set2","a","b","x","y");

        Set members = setOperations.members("set1");
        System.out.println(members);

        Long size = setOperations.size("set1");
        System.out.println(size);

        Set intersect = setOperations.intersect("set1", "set2");
        System.out.println(intersect);

        Set union = setOperations.union("set1", "set2");
        System.out.println(union);

        setOperations.remove("set1","a","b");
    }

    /**
     * 操作有序集合类型的数据
     */
    @Test
    public void testZset(){
        //zadd zrange zincrby zrem
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();

        zSetOperations.add("zset1","a",10);
        zSetOperations.add("zset1","b",12);
        zSetOperations.add("zset1","c",9);

        Set zset1 = zSetOperations.range("zset1", 0, -1);
        System.out.println(zset1);

        zSetOperations.incrementScore("zset1","c",10);

        zSetOperations.remove("zset1","a","b");
    }

    /**
     * 通用命令操作
     */
    @Test
    public void testCommon(){
        //keys exists type del
        Set keys = redisTemplate.keys("*");
        System.out.println(keys);

        Boolean name = redisTemplate.hasKey("name");
        Boolean set1 = redisTemplate.hasKey("set1");

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

        redisTemplate.delete("mylist");
    }
}

店铺营业状态配置

image-20251224145039625

image-20251224145301595

image-20251224150140694

image-20251224150301629

image-20251224150509626

技术栈

  1. 操作redis,直接在controller层操作就行
  2. 拦截器配置,因为admin和user端的jwt不一样,不能用同一套jwt校验,拦截器配置在WebMvcConfiguration,这里面能修改注册拦截器所适用吃的controller类
  3. 同一个controller(名字),放入spring的bean容器里面会冲突(默认名为类名小写首字母)需要对该bean重命名
  4. swagger接口分组分类
1.操作redis,直接在controller层操作就行
3.同一个controller(名字),放入spring的bean容器里面会冲突(默认名为类名小写首字母)需要对该bean重命名(第11行)
package com.sky.controller.admin;

import com.sky.constant.MessageConstant;
import com.sky.result.Result;
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.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

@RestController("adminShopController")
@RequestMapping("/admin/shop")
@Api(tags = "店铺相关接口")
@Slf4j
public class ShopController {


    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 设置店铺营业状态
     * @param status
     * @return
     */
    @PutMapping("/{status}")
    @ApiOperation("设置店铺的营业状态")
    public Result setStatus(@PathVariable Integer status){
        log.info("设置店铺营业状态:{}",status == 1?"营业中":"打烊中");
        redisTemplate.opsForValue().set(MessageConstant.SHOP_STATUS,status);
        return Result.success();
    }

    /**
     * 获取店铺的营业状态
     * @return
     */
    @GetMapping("/status")
    @ApiOperation("获取店铺的营业状态")
    public Result<Integer> getStatus(){
        Integer status = (Integer)redisTemplate.opsForValue().get(MessageConstant.SHOP_STATUS);
        log.info("获取店铺的营业状态未:{}",status == 1?"营业中":"打烊中");
        return Result.success(status);
    }
}

package com.sky.controller.user;

import com.sky.constant.MessageConstant;
import com.sky.result.Result;
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.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

@RestController("userShopController")
@RequestMapping("/user/shop")
@Api(tags = "店铺相关接口")
@Slf4j
public class ShopController {


    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 获取店铺的营业状态
     * @return
     */
    @GetMapping("/status")
    @ApiOperation("获取店铺的营业状态")
    public Result<Integer> getStatus(){
        Integer status = (Integer)redisTemplate.opsForValue().get(MessageConstant.SHOP_STATUS);
        log.info("获取店铺的营业状态未:{}",status == 1?"营业中":"打烊中");
        return Result.success(status);
    }
}
2.拦截器配置,因为admin和user端的jwt不一样,不能用同一套jwt校验,拦截器配置在WebMvcConfiguration,这里面能修改注册拦截器所适用的controller类(第38-43行)
package com.sky.config;

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() {
        log.info("准备生成接口文档");
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .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;
    }

    @Bean
    public Docket docket2() {
        log.info("准备生成接口文档");
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .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;
    }

    /**
     * 设置静态资源映射
     * @param registry
     */
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("开始设置静态资源映射");
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    /**
     * 扩展Spring MVC的消息转换器
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展消息转换器");
        //创建一个消息转换器
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        //需要为消息转换器设置一个对象转换器,对象转换器可以将java对象序列化为json数据
        converter.setObjectMapper(new JacksonObjectMapper());
        //将自己的消息转换器加入容器中,并通过索引提升自己消息转换器的优先级
        converters.add(0,converter);
    }
}
4.swagger接口分组分类(第14和32行)
/**
 * 通过knife4j生成接口文档
 * @return
 */
@Bean
public Docket docket1() {
    log.info("准备生成接口文档");
    ApiInfo apiInfo = new ApiInfoBuilder()
            .title("苍穹外卖项目接口文档")
            .version("2.0")
            .description("苍穹外卖项目接口文档")
            .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;
}

@Bean
public Docket docket2() {
    log.info("准备生成接口文档");
    ApiInfo apiInfo = new ApiInfoBuilder()
            .title("苍穹外卖项目接口文档")
            .version("2.0")
            .description("苍穹外卖项目接口文档")
            .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;
}
posted @ 2025-12-25 09:55  David大胃  阅读(5)  评论(0)    收藏  举报