Redis 实际项目中的整合,记录各种用法

Redis缓存餐厅数据

我们来看主要的流程
在这里插入图片描述
很简单,就是在数据库和接口之间加了一层缓冲,在redis之前其实还可以加其他的缓存 例如 nginx的缓存

接下来,就是结合我的业务,来做缓存

在这里插入图片描述
我这里的业务逻辑是,按了分类的按钮,分别以不同的 分类为一组缓存数据
所以,这里的缓存粒度是分类
我设计的id如下

public class RedisContants {//用户端--------------------------------//分类下的菜品缓存public static final String DISH_CATEGORY = "dish:category:";}

为了体现粒度,我们操作redis key的时候,加上分类id

我们来看我已经写好的样子

在这里插入图片描述

我们需要改造的地方有几处
第一:
通过分类id获得此分类下的菜品的时候,我们要保存redis

第二:
当我们修改菜品,不管是添加 + 删除 + 修改,都要修改这里的代码
我在想其实,这里会有写耦合,redis的操作 + 数据库的操作,如果我们可以分离出来就完美了

代码

redis的工具类在我另外一个博客里边
https://blog.csdn.net/weixin_52232901/article/details/135850329

获得商品的controller修改

@RestController("userDishController")
@RequestMapping("/user/dish")
@Slf4j
@Api(tags = "菜品管理")
public class DishController {@Autowiredprivate DishService dishService;@Autowiredprivate RedisCache redisCache;@ApiOperation("根据分类id查询菜品")@GetMapping("/list")public Result getByCategoryId(Long categoryId) {log.info("根据分类id查询菜品 参数为{}",categoryId);//先去redis中查List<DishVO> dishVOs = redisCache.getCacheObject(RedisContants.DISH_CATEGORY + categoryId);if(dishVOs != null && dishVOs.size() > 0) {return Result.success(dishVOs);}//没查到redis,去mysql查,并且更新redis,设置一天的时间为过期时间dishVOs = dishService.getByCategoryIdUser(categoryId);redisCache.setCacheObject(RedisContants.DISH_CATEGORY + categoryId,dishVOs,RedisContants.DISH_CATEGORY_TTL,RedisContants.DISH_CATEGORY_TIMEUNIT);return Result.success(dishVOs);}}
    public static final String DISH_CATEGORY = "dish:category:";public static final Integer DISH_CATEGORY_TTL = 24;public static final TimeUnit DISH_CATEGORY_TIMEUNIT = TimeUnit.HOURS;

修改相关数据的controller修改

这里的修改,你们可以不用看,这里只是我的业务有相关性,所以记录下来

/*** 员工管理*/
@RestController
@RequestMapping("/admin/dish")
@Slf4j
@Api(tags = "菜品管理")
public class DishController {@Autowiredprivate DishService dishService;@Autowiredprivate RedisCache redisCache;@ApiOperation("分页查询")@GetMapping("/page")public Result<PageResult> pageQuery(DishPageQueryDTO dishPageQueryDTO) {log.info("分页查询 参数为{}",dishPageQueryDTO);PageResult page = dishService.pageQuery(dishPageQueryDTO);return Result.success(page);}@ApiOperation("根据id查询菜品")@GetMapping("/{id}")public Result getByid(@PathVariable Long id) {log.info("根据id查询菜品 参数为{}",id);DishVO dish = dishService.getByid(id);return Result.success(dish);}@ApiOperation("根据分类id查询菜品")@GetMapping("/list")public Result getByCategoryId(Long categoryId) {log.info("根据分类id查询菜品 参数为{}",categoryId);List<Dish> dish = dishService.getByCategoryId(categoryId);return Result.success(dish);}@ApiOperation("修改菜品")@PutMappingpublic Result<PageResult> update(@RequestBody DishDTO dishDTO) {log.info("修改菜品 参数为{}",dishDTO);dishService.updateDish(dishDTO);//删除缓存redisCache.deleteObject(RedisContants.DISH_CATEGORY + dishDTO.getCategoryId());return Result.success();}@ApiOperation("新增菜品")@PostMappingpublic Result<PageResult> add(@RequestBody DishDTO dishDTO) {log.info("新增菜品 参数为{}",dishDTO);dishService.addDish(dishDTO);//删除缓存redisCache.deleteObject(RedisContants.DISH_CATEGORY + dishDTO.getCategoryId());return Result.success();}@ApiOperation("修改状态")@PostMapping("/status/{status}")public Result changeStatus(@PathVariable Integer status,Long id) {log.info("修改状态 参数为{},{}",status,id);dishService.changeStatus(status,id);Dish dish = dishService.getById(id);//删除缓存redisCache.deleteObject(RedisContants.DISH_CATEGORY + dish.getCategoryId());return Result.success();}@ApiOperation("删除菜品")@DeleteMappingpublic Result delete(String ids) {log.info("删除菜品 参数为{}",ids);String[] split = ids.split(",");List<Long> list = Arrays.stream(split).map((str) -> Long.parseLong(str)).collect(Collectors.toList());dishService.deleteById(list);Collection<String> keys = redisCache.keys(RedisContants.DISH_CATEGORY + "*");//删除所有的菜品redisCache.deleteObject(keys);return Result.success();}}

这里按道理来说,其实还是要给redis这样的key要至少设置时间才行,不然就会有问题

我这里的修改,比较要注意的是,我这里的keys,我在批量删除的时候,我的业务逻辑是,直接所有的缓存都删除
也就是以 dish:category:分类id,开头的所有都要删除,所以我们需要得到所有这样的keys的集合,也就是

Collection<String> keys = redisCache.keys(RedisContants.DISH_CATEGORY + "*");

通过这个,然后直接删除,通过集合的方式,直接删除

Redis实现模拟手机验证码登录

注意,我这里实现的redisCache是我封装好的redis操作类,在我另外一个博客里边,你可以直接操作redis,都没关系,我这里只是为了省事

需求分析

在这里插入图片描述
使用使用手机验证码登录

我们需要来捋清楚整个的流程

首先我们有三件大事要做
第一件事,完成发送验证码功能
第二件事,完成登录功能
第三件事,完成登录之后的校验功能

发送验证码功能

这个很好完成,因为我们这里是模拟短信,实际上,再实际中,我们要接入第三方短信业务,我们这里就当后端为用户端,省略实际发送的地方

流程
先校验手机号,手机号校验通过,
生成验证码
保存到redis中
返回

短信登录注册

在这里插入图片描述
首先依然是校验手机号
然后是从redis中获取验证码,校验验证码
然后判断用户是否是新用户如果是的化,直接快捷注册到数据库中
将用户的信息保存到redis中
最后返回用户

校验

在这里插入图片描述
首先是前端发带有token的header过来
然后我们从redis中查询用户
判断用户是否存在,如果存在放行
不存在,拦截!

代码实现

发送验证码

接口分析

url: /user/code

请求参数 (Body) phone

返回值: 无

UserController

    /*** 发送手机验证码*/@PostMapping("code")public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {return userService.sendCode(phone,session);}

IUserService

public interface IUserService extends IService<User> {/*** 发送验证码* @param phone* @param session*/Result sendCode(String phone, HttpSession session);
}

UserServiceImpl

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Autowiredprivate RedisCache redisCache;/*** 发送验证码* @param phone* @param session*/@Overridepublic Result sendCode(String phone, HttpSession session) {//先校验手机号if(RegexUtils.isPhoneInvalid(phone)) {return Result.fail("校验不通过");}String code = RandomUtil.randomString(6);//存到redis中redisCache.setCacheObject(RedisConstants.LOGIN_CODE_KEY + phone,code,2, TimeUnit.MINUTES);log.info("验证码: {}",code);return Result.ok();}
}

这里的校验工具类,在我另外一篇博客里边
https://blog.csdn.net/weixin_52232901/article/details/135957348?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22135957348%22%2C%22source%22%3A%22weixin_52232901%22%7D

发送验证码还挺简单的,只要把验证码放到redis上就 ok,这个redis的作用就类似于session,而为什么不用session呢,就是怕我们有几个端,这样子就好用一些,类似于这种验证码的东西,最好还是放在一个服务器里边,这样就不会有一致性问题

短信登录 + 注册

接口分析
url: /user/login
请求方法: post
请求参数: Body

参数名类型举例
codeString131222
phoneString13168885555

返回值: token

UserController

/*** 登录功能* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码*/@PostMapping("/login")public Result login(@RequestBody LoginFormDTO loginForm){return userService.login(loginForm);}

IUserService

public interface IUserService extends IService<User> {/*** 发送验证码* @param phone* @param session*/Result sendCode(String phone, HttpSession session);/*** 短信登录* @param loginForm* @return*/Result login(LoginFormDTO loginForm);
}

UserServiceImpl

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Autowiredprivate RedisCache redisCache;@Overridepublic Result login(LoginFormDTO loginForm) {//从redis中查String code = redisCache.getCacheObject(RedisConstants.LOGIN_CODE_KEY + loginForm.getPhone());if(Objects.isNull(code)) {return Result.fail("错误!");}//先校验验证码是否相同if(!code.equals(loginForm.getCode())) {return Result.fail("验证码错误!");}//校验手机号if(RegexUtils.isPhoneInvalid(loginForm.getPhone())) {return Result.fail("手机号不正确!");}//查用户是否是新用户,是的化,创建新用户LambdaQueryWrapper<User> userWrapper = new LambdaQueryWrapper<>();userWrapper.eq(User::getPhone,loginForm.getPhone());User user = getOne(userWrapper);//是否是新用户if(Objects.isNull(user)) {user = User.builder().phone(loginForm.getPhone()).nickName(SystemConstants.USER_NICK_NAME_PREFIX + RandomUtil.randomString(10)).build();save(user);}//保存到redis中String token = UUID.randomUUID().toString();UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);//保存redisCache.setCacheObject(RedisConstants.LOGIN_USER_KEY + token,userDTO,RedisConstants.LOGIN_USER_TTL.intValue(),TimeUnit.MINUTES);return Result.ok(token);}
}

这里是先查redis中的验证码,然后校验一波,通过的化,就查询数据库是否是新用户,这里的查的方法,我用的lambda 的wrapper,这个lambda,很好用,我可以不用知道字段真正的名字是什么,而是使用User.getXXX就能获得响应的字段,建议大家学这个东西,蛮好用的

然后这里的生成一个新的User对象,是lombok的@Builder注解,可以这么构造,我觉得他很优雅,所以我就用了

最后这里的token生成,是直接用UUID生成的,我估计这里可以使用其他的token生成的方法,都可以

最后把用户对象保存到redis中,返回token

校验

校验我们得用到拦截器,这个事实上是SpringMvc的

public class RefreshTokenInterceptor implements HandlerInterceptor {private RedisCache redisCache;public  RefreshTokenInterceptor(RedisCache redisCache) {this.redisCache = redisCache;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("authorization");//为空的化,拦截if(StrUtil.isBlank(token)) {response.setStatus(401);return false;}String userkey = RedisConstants.LOGIN_USER_KEY + token;UserDTO userDTO = redisCache.getCacheObject(userkey);//没查到这个用户,拦截if(Objects.isNull(userDTO)) {response.setStatus(401);return true;}//保存到ThreadLocal里边UserHolder.saveUser(userDTO);//刷新redis缓存redisCache.expire(userkey,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);return true;}
}

配置

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Autowiredprivate RedisCache redisCache;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor(redisCache)).excludePathPatterns("/shop/**","/voucher/**","/shop-type/**","/upload/**","/blog/hot","/user/code","/user/login");}
}

我这里的不用登录的接口都写在这

测试

我们再来粗略的捋一下整个的流程,做到心中有数

第一: 发送验证码到后端(模拟发送到用户手机),存验证码到redis中
第二: 登录,得到redis中的验证码,做一个校验,校验通过,查数据库,是否是新用户,如果是的化,快速注册到数据库中, 然后用户信息保存到redis中,最后返回token
第三: 校验,先检查是否带着token,带着token去查redis,如果有这个用户信息,同行,否则,不同行

发送验证码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

输入验证码登录

登录成功!
我们测试一下是否可以过滤

在这里插入图片描述
没问题,过滤成功,带着autorization
可以访问个人页面说明没问题!!!

总结

这整个流程比较复杂,但是逻辑还算清楚,其中我们可以改其中的发送验证码的接口,我们可以自己接入第三方服务,然后这个验证码存储也不一定要在redis中,只要有一个数据库就行,最好是类似于redis这样的,这样就没有一致性问题,然后还有一个问题就是我这里的token,是很简单的生成,如果要生成复杂的token的化,就应该自己再写入,这里的校验还行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/659121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JS逆向实战-入门篇】某gov网站加密参数分析与Python算法还原

文章目录 1. 写在前面2. 请求分析3. 断点分析4. 算法还原 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff1a;对JS逆向感兴趣的朋…

java中Ajax与Axios的使用

1&#xff0c;Ajax 1.1 概述 AJAX (Asynchronous JavaScript And XML)&#xff1a;异步的 JavaScript 和 XML。 我们先来说概念中的 JavaScript 和 XML&#xff0c;JavaScript 表明该技术和前端相关&#xff1b;XML 是指以此进行数据交换。而这两个我们之前都学习过。 1.1.…

iOS开发Xcode中的ld64和-ld_classic是什么意思

在iOS应用程序开发中&#xff0c;Xcode是一款广泛使用的集成开发环境&#xff08;IDE&#xff09;&#xff0c;而链接器是构建应用程序的关键组成部分之一。在Xcode中&#xff0c;我们常常会遇到两个重要的概念&#xff1a;ld64和-ld_classic。它们分别代表了默认链接器和经典链…

前端大屏展示可视化——地图的绘制(持续更新)

一、ECharts 1、安装 npm install echarts2、引入 import * as echarts from echarts;3、渲染 3.1、前期准备&#xff0c;基础配置 // 地图实例 const myChart ref(null); // 地图配置 const option reactive({tooltip: {trigger: item,formatter: function (params) {re…

电脑风扇控制温度软件 Macs Fan Control Pro 中文

Macs Fan Control Pro是一款专为Mac用户设计的风扇控制软件&#xff0c;旨在提供更精细的风扇转速控制和温度监控。这款软件通过实时监测Mac内部硬件的温度&#xff0c;自动或手动调整风扇的转速&#xff0c;以确保系统温度保持在理想范围内。 Macs Fan Control Pro提供了直观…

【01】Linux 基本操作指令

带⭐的为重要指令 &#x1f308; 01、ls 展示当前目录下所有文件&#x1f308; 02、pwd 显示用户当前所在路径&#x1f308; 03、cd 进入指定目录&#x1f308; 04、touch 新建文件&#x1f308; 05、tree 以树形结构展示所有文件⭐ 06、mkdir 新建目录⭐ 07、rmdir 删除目录⭐…

赋能未来社区:数据中台智慧园区的全方位解决方案_光点科技

在信息技术与互联网快速发展的今天&#xff0c;传统的园区管理方式已无法满足时代对效率与智能化的追求。数据中台作为企业数字化转型的核心&#xff0c;正引领着智慧园区的发展趋势。一个集成了数据中台的智慧园区&#xff0c;不仅能有效地整合资源&#xff0c;优化管理流程&a…

2024美赛数学建模B题思路+代码

文章目录 1 赛题思路2 美赛比赛日期和时间3 赛题类型4 美赛常见数模问题5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 美赛比赛日期和时间 比赛开始时间&#xff1a;北京时间2024年2月2日&#xff08;周五&#xff…

代码随想录算法刷题训练营day21

代码随想录算法刷题训练营day21&#xff1a;LeetCode(501)二叉搜索树中的众数、LeetCode(530)二叉搜索树的最小绝对差 LeetCode(501)二叉搜索树中的众数 题目 代码 import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;/*…

【Linux】多线程(线程概念+线程控制)

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…

旧物回收小程序:环保与便捷的完美结合

随着社会的发展和人们生活水平的提高&#xff0c;消费行为也日益频繁&#xff0c;这导致了大量的废旧物品的产生。如何有效处理这些废旧物品&#xff0c;既保护环境&#xff0c;又节约资源&#xff0c;成为了一个重要的议题。此时&#xff0c;旧物回收小程序的出现&#xff0c;…

跟着pink老师前端入门教程-day14+15

2.6 main 主体模块制作 HTML&#xff1a; <div class"w"><div class"main"><!-- 焦点图模块 --><div class"focus"><ul><li><img src"./images/banner_bg.png" alt""></li>…

python给word插入脚注

1.需求 最近因为工作需要&#xff0c;需要给大量文本的脚注插入内容&#xff0c;我就写了个小程序。 2.实现 下面程序是我已经给所有脚注插入了两次文本“幸福”&#xff0c;给脚注2到4再插入文本“幸福” from win32com import clientdef add_text_to_specific_footnotes(…

会议会务报名签到互动抽奖视频图片直播公众号开发

会议会务报名签到互动抽奖视频图片直播公众号开发 现在越来越多的人进行网络签到&#xff0c;特别是大型的会务/展会&#xff0c;网络签到进场形式应用的越来越广泛也是发展的趋势&#xff1b;会助力智能会议管理系统&#xff1a;可以实现在线报名&#xff08;免费/付费/后台导…

Linux/ScriptKiddie

Enumeration nmap 第一次扫描发现系统对外开放了22和5000端口&#xff0c;端口详细信息如下 22端口运行着openssh&#xff0c;5000端口则是werkzeug的httpd&#xff0c;tittle是kids hacker tools TCP/5000 首先从5000端口开始&#xff0c;先访问站点&#xff0c;站点是一个…

RHCE 部署Ubuntu系统(ubuntu-23.10-live-server-amd64.iso)

目录 一、新建虚拟机 二、安装系统 1、 选择安装语言&#xff0c;默认 【 English 】&#xff0c;直接回车 2、选择键盘&#xff0c;默认回车 3、安装的服务器版本&#xff0c;根据需求自行选择&#xff0c;本次安装选择 【 Ubuntu Server 】 4、网络设置&#xff0c;此…

Unix/Linux上的五种IO模型

a.阻塞 blocking 调用者调用了某个函数&#xff0c;等待这个函数返回&#xff0c;期间什么也不做&#xff0c;不停的去检查这个函数有没有返回&#xff0c;必须等这个函数返回才能进行下一步动作。 注意&#xff1a;阻塞并不是函数的行为&#xff0c;而是跟文件描述符有关。通…

力扣题目训练(5)

2024年1月29日力扣题目训练 2024年1月29日力扣题目训练345. 反转字符串中的元音字母349. 两个数组的交集350. 两个数组的交集 II96. 不同的二叉搜索树97. 交错字符串44. 通配符匹配 2024年1月29日力扣题目训练 2024年1月29日第五天编程训练&#xff0c;今天主要是进行一些题训…

【云上建站】快速在云上构建个人网站3——网站选型和搭建

快速在云上构建个人网站3——网站选型和搭建 一、网站选型二、云市场镜像方式一&#xff1a;方式二&#xff1a;1. 进入ECS实例详情页面&#xff0c;点击停止&#xff0c;确保更换操作系统的之前ECS实例处于已停止状态&#xff0c;点击更换操作系统&#xff0c;进行镜像配置。2…

appsmith安装手记:4.Sql server数据库容器安装

appsmith安装好&#xff0c;那就可以看是练练手。 数据当然是来自数据库&#xff0c;那就连接局域网中现成的一台数据库服务器试试&#xff0c;但是连接数据库的时候一直错误。 找到/home/appsmith/backend 目录下的日志&#xff0c;看到了错误&#xff1a; [rootlocalhost bac…