优设计网站建设,可以做策略回测的网站,电子邮件怎么注册,专业建站公司报价Redis缓存餐厅数据
我们来看主要的流程 很简单,就是在数据库和接口之间加了一层缓冲,在redis之前其实还可以加其他的缓存 例如 nginx的缓存
接下来,就是结合我的业务,来做缓存 我这里的业务逻辑是,按了分类的按钮,分别以不同的 分类为一组缓存数据 所以,这里的缓存粒度是分类…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中查ListDishVO 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 ResultPageResult 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);ListDish dish dishService.getByCategoryId(categoryId);return Result.success(dish);}ApiOperation(修改菜品)PutMappingpublic ResultPageResult update(RequestBody DishDTO dishDTO) {log.info(修改菜品 参数为{},dishDTO);dishService.updateDish(dishDTO);//删除缓存redisCache.deleteObject(RedisContants.DISH_CATEGORY dishDTO.getCategoryId());return Result.success();}ApiOperation(新增菜品)PostMappingpublic ResultPageResult 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(,);ListLong list Arrays.stream(split).map((str) - Long.parseLong(str)).collect(Collectors.toList());dishService.deleteById(list);CollectionString keys redisCache.keys(RedisContants.DISH_CATEGORY *);//删除所有的菜品redisCache.deleteObject(keys);return Result.success();}}
这里按道理来说,其实还是要给redis这样的key要至少设置时间才行,不然就会有问题
我这里的修改,比较要注意的是,我这里的keys,我在批量删除的时候,我的业务逻辑是,直接所有的缓存都删除 也就是以 dish:category:分类id,开头的所有都要删除,所以我们需要得到所有这样的keys的集合,也就是
CollectionString 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 IServiceUser {/*** 发送验证码* param phone* param session*/Result sendCode(String phone, HttpSession session);
}UserServiceImpl Service
Slf4j
public class UserServiceImpl extends ServiceImplUserMapper, 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
参数名类型举例codeString131222phoneString13168885555
返回值: token UserController /*** 登录功能* param loginForm 登录参数包含手机号、验证码或者手机号、密码*/PostMapping(/login)public Result login(RequestBody LoginFormDTO loginForm){return userService.login(loginForm);}IUserService public interface IUserService extends IServiceUser {/*** 发送验证码* param phone* param session*/Result sendCode(String phone, HttpSession session);/*** 短信登录* param loginForm* return*/Result login(LoginFormDTO loginForm);
} UserServiceImpl Service
Slf4j
public class UserServiceImpl extends ServiceImplUserMapper, 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(手机号不正确!);}//查用户是否是新用户,是的化,创建新用户LambdaQueryWrapperUser 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的化,就应该自己再写入,这里的校验还行