苍穹外卖day07

缓存菜品

在这里插入图片描述
由于大量的请求如果访问数据库的不太好,所以存在redis里面减少访问数据库的需求

在这里插入图片描述

dish_分类的id作为key
redis的string不是java的string
redis的string可以存储不同的对象
这边应该返回一个list给redis的string

controller修改

 	@GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result<List<DishVO>> list(Long categoryId) {
        Dish dish = new Dish();
        dish.setCategoryId(categoryId);
        dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品

        List<DishVO> list = dishService.listWithFlavor(dish);

        return Result.success(list);
    }

需要将查询msql的代码,改成存储到redis然后查询的操作

	@GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result<List<DishVO>> list(Long categoryId) {

        //构造redis的key 规则 dish_categoryid

        String key = "dish_" + categoryId;

        //查询redis里面是否已经存在了菜品
        List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
        if(list!=null && list.size() > 0){
            return Result.success(list);
        }

        //没有查到 就去查sql
        Dish dish = new Dish();
        dish.setCategoryId(categoryId);
        dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品

        list = dishService.listWithFlavor(dish);
        //然后再存入redis数据库
        redisTemplate.opsForValue().set(key,list);

        return Result.success(list);
    }

清理redis(防止管理操作的时候redis没有变化)

保存的时候

@PostMapping()
    public Result save(@RequestBody DishDTO dishDTO){
        log.info("新增菜品:{}",dishDTO);


        dishService.saveWithFlavor(dishDTO);

        //清理缓存数据 只清理当前categroy里面的list
        String key = "dish_" + dishDTO.getCategoryId();
        redisTemplate.delete(key);

        return Result.success();
    }

批量删除的时候

	@DeleteMapping
    @ApiOperation("删除菜品")
    public Result delete(@RequestParam  List<Long> ids){

        log.info("菜品批量删除:{}",ids);

        dishService.deleteBatch(ids);

        //清理redis 将所有的菜品缓存的清理掉 以dish_开头的清理掉
        Set keys = redisTemplate.keys("dish_*");
        redisTemplate.delete(keys);

        return Result.success();
    }

修改(可以会修改套餐,直接全清了)

	@PutMapping
    @ApiOperation("修改菜品")
    public Result update(@RequestBody DishDTO dishDTO){

        log.info("修改菜品:{}",dishDTO);
        dishService.updateWithFlavor(dishDTO);

        //修改的时候可以修改分类
        //清理redis 将所有的菜品缓存的清理掉 以dish_开头的清理掉
        Set keys = redisTemplate.keys("dish_*");
        redisTemplate.delete(keys);

        return Result.success();

    }

修改菜品状态(清理一个也可以)

 @PutMapping("/status/{status}")
    public Result startOrStop(@PathVariable Integer status,long id){

        log.info("改变菜品销售状态:{}",status);
        dishService.startOrStop(status,id);

        Set keys = redisTemplate.keys("dish_*");
        redisTemplate.delete(keys);
        return Result.success();

    }

上面全部白写,定义一个private函数cleanCache

cleanCache("dish_*");

上面添加上去

private void cleanCache(String pattern){
        Set keys = redisTemplate.keys(pattern);
        redisTemplate.delete(keys);
    }

bug

傻逼bug,因为redis里面的dish_前面有一堆乱码,所以使用dish_*就能清理内存了

缓存套餐

spring cache (大部分都在controller的方法里面添加)

在这里插入图片描述

在application上面加入 @EnableCaching

在Controller方法里面写@CachePut

 	@PostMapping
    //@CachePut(cacheNames = "userCache",key = "#user.id") //如果使用spring cache缓存数据,key的生成:userCache::user.id
    //@CachePut(cacheNames = "userCache",key = "#result.id")
    @CachePut(cacheNames = "userCache",key = "#p0.id")
    public User save(@RequestBody User user){
        userMapper.insert(user);
        return user;
    }

也可以写result.id返回值的id属性,将返回值存入redis
p0或者a0代表的是第一个输入的参数
root.args[0]也代表的是第一个输入的参数

配置好mysql和redis之后就能正常运行了,可以去Another Redis Desktop Manager的二号数据库查看

Cacheable (先查redis查不到再去进行controller的方法)

	@DeleteMapping
    @Cacheable(cacheNames = "userCache",key = "#id") //生成的键就是 usercache::#id 值已经存在redis数据库
    public void deleteById(Long id){
        userMapper.deleteById(id);
    }

CacheEvict(key指定删除特定的数据,使用allEntries=true)

	@GetMapping
    @CacheEvict(cacheNames = "userCache",key = "#id")
    public User getById(Long id){
        User user = userMapper.getById(id);
        return user;
    }
	@DeleteMapping("/delAll")
    @CacheEvict(cacheNames = "userCache",allEntries = true)
    public void deleteAll(){
        userMapper.deleteAll();
    }

实现思路

在setmealController里面进行存入redis的操作

@GetMapping("/list")
    @ApiOperation("根据分类id查询套餐")
    @Cacheable(cacheNames = "setmealCache",key = "#categoryId") //setmealCache::#categoryId
    public Result<List<Setmeal>> list(Long categoryId) {
        Setmeal setmeal = new Setmeal();
        setmeal.setCategoryId(categoryId);
        setmeal.setStatus(StatusConstant.ENABLE);

        List<Setmeal> list = setmealService.list(setmeal);
        return Result.success(list);
    }
@CacheEvict(cacheNames = "setmealCache",key="#setmealDTO.categoryId")
    public Result save(@RequestBody SetmealDTO setmealDTO){
 @CacheEvict(cacheNames = "setmealCache",allEntries = true)
    public Result delete(@RequestParam List<Long> ids){

update 起售停售 删除套餐都是删除全部
只有保存新套餐清理特定的

添加购物车

需求分析在这里插入图片描述

一种有口味需要选择口味,一种没有口味直接添加到购物车中

在这里插入图片描述
void @requsetbody DTO
return success

在这里插入图片描述

Controller

 	@PostMapping("/add")
    @ApiOperation("添加菜品或者套餐到购物车")
    public Result add(@RequestBody ShoppingCartDTO shoppingCartDTO){
        log.info("添加购物车:{}",shoppingCartDTO);
        shoppingCartService.addShoppingCart(shoppingCartDTO);

        return Result.success();
    }

Service(根据是dish还是setmeal添加进去(使用动态sql进行查询),如果存在修改数量,如果不存在进行insert,每次查询的时候都是将DTO转化成正常的entity对象再给Mapper查询)

因为动态sql可以先判断DTO传入过来的是dish还是setmeal再进行查询数据库中有没有这样的值

每次查询的时候都是将DTO转化成正常的entity对象再给Mapper查询->
判断是否已经存在存在就加1->
不存在去查询dish和setmeal的金额等属性赋值给shoppingcart->
然后再insert到表中

public void addShoppingCart(ShoppingCartDTO shoppingCartDTO){
        //DTO属性
//        private Long dishId;
//        private Long setmealId;
//        private String dishFlavor;
        //到底是菜品还是套餐 这个直接用动态sql去实现,因为动态sql里面会判断到底是不是为null如果不是null的话就进行查询
        //需要先转化为entity再进行传入
        ShoppingCart shoppingCart = new ShoppingCart();
        BeanUtils.copyProperties(shoppingCartDTO,shoppingCart);
        //通过上下文获取现在的用户id 还是Thread的知识
        Long currentId = BaseContext.getCurrentId();
        shoppingCart.setUserId(currentId);

        List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);

        //如果已经存在就将number加一 不存在就插入
        if (list!=null && list.size()>0){
            ShoppingCart cart = list.get(0);
            cart.setNumber(cart.getNumber()+1);
            shoppingCartMapper.update(cart);
        }else{
            //到底是菜品还是套餐
            Long setmealId = shoppingCartDTO.getSetmealId();
            Long dishId = shoppingCartDTO.getDishId();
            if(dishId!=null){
                //获取金额图片放入购物车表中
                Dish dish = dishMapper.getById(dishId);
                shoppingCart.setName(dish.getName());
                shoppingCart.setImage(dish.getImage());
                shoppingCart.setAmount(dish.getPrice());
            }else{
                Setmeal setmeal = setmealMapper.getById(setmealId);
                shoppingCart.setName(setmeal.getName());
                shoppingCart.setImage(setmeal.getImage());
                shoppingCart.setAmount(setmeal.getPrice());
            }
            shoppingCart.setCreateTime(LocalDateTime.now());
            shoppingCart.setNumber(1);

            shoppingCartMapper.insert(shoppingCart);
        }

Mapper(查询注意返回的是List)

<select id="list" resultType="com.sky.entity.ShoppingCart">
        select * from shopping_cart
        <where>
            <if test="user_id!= null" >
                and user_id = #{userId}
            </if>
            <if test="setmealId != null">
                and setmeal_id = #{setmealId}
            </if>
            <if test="dishId != null">
                and dish_id = #{dishId}
            </if>
            <if test="dishFlavor != null">
                and dish_flavor = #{dishFlavor}
            </if>
        </where>


    </select>

测试的时候发现拦截器不能通过thread获取user_id

回去找bug,发现登录的时候生成当前用户jwt的时候,我的xml文件里的设置ttl拼写错误(存在是数据库里的是openid微信用户的唯一标识)
我们在处理是不是同一个人操作的时候,没有去访问数据库的openid来确定,而是使用了拦截器的jwt来确定ovo?

首先vx登录的时候需要返回一个jwt令牌的封装在userLoginVO之中,因为这个给拦截器校验用的,为了让后面的功能可以获取当前正在使用用户的UserId

也还行,再对jwt加深一遍理解吧

所以在管理层Employee的新增功能之中,直接使用Thread.currentThread().getId()获取jwt加密之后的id就好?其实也不是

查看购物车

在这里插入图片描述

清空购物车

在这里插入图片描述

关于拦截器部分的理解

除了登录的方法,在使用其他方法之前,拦截器验证jwt是不是对的,如果是对的就可以在Threadlocal访问当前用户的id

流程图示例
登录请求流程:

客户端 → 登录接口 → 验证凭证 → 生成JWT(登录时生成的 JWT(JSON Web Token)会保存在前端) → 返回JWT给客户端
后续请求流程:

客户端 → 带JWT的请求 → 拦截器(preHandle) → 验证JWT → 保存用户ID到 ThreadLocal → 传递请求到控制器 → 控制器处理请求 → 清理 ThreadLocal (afterCompletion)

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值