【苍穹外卖|Day7】缓存菜品、缓存套餐、添加购物车、查看购物车、清空购物车

D7

文章目录

本文记录「苍穹外卖」项目开发中的关键技术实践与踩坑思考,包含个人在实际开发中的具体过程遇到的问题 以及知识点总结
希望可以给一起学习的大家带来帮助

完整项目在github上rannn-1127/sky-take-out: 苍穹外卖 (还没完结)

1.缓存菜品

实现思路

通过Redis来缓存菜品数据,减少数据库查询操作

缓存逻辑分析

  • 每个分类下的菜品保存一份数据
  • 数据库中的菜品数据有变更时清理缓存数据
1.1缓存菜品数据

修改user.DishController中的list方法

java 复制代码
/**
     * 根据分类id查询菜品
     *
     * @param categoryId
     * @return
     */
    @GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result<List<DishVO>> list(Long categoryId) {

        //构造Redis中的key
        String key = "dish_" + categoryId;

        //查询Redis中是否存在菜品数据
        List<DishVO> list = (List<DishVO>)redisTemplate.opsForValue().get(key);
        if(list != null && list.size() != 0){
            //若存在就直接返回,无需查询数据库
            return Result.success(list);
        }

        //若不存在,查询数据库,将查询到的数据放入Redis中
        Dish dish = new Dish();
        dish.setCategoryId(categoryId);
        dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品

        list = dishService.listWithFlavor(dish);
        redisTemplate.opsForValue().set(key,list);
        
        return Result.success(list);
    }
1.2清理缓存

需要改造的方法

  • 新增菜品
  • 修改菜品
  • 批量删除菜品
  • 起售、停售菜品

在admin.DishController类中先加上

java 复制代码
@Autowired
    private RedisTemplate redisTemplate;

新增菜品只清理改菜品对应的缓存,其他需要清理的清理全部缓存以简化代码节省时间

修改后的DishController:

java 复制代码
@RestController
@RequestMapping("/admin/dish")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;
    @Autowired
    private RedisTemplate redisTemplate;

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

        //清理缓存数据
        String key = "dish_" + dishDTO.getCategoryId();
        cleanCache(key);
        return Result.success();
    }

    //菜品分页查询
    @GetMapping("/page")
    public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO){
        log.info("菜品分页查询:{}",dishPageQueryDTO);
        PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
        return Result.success(pageResult);
    }

    //菜品删除
    @DeleteMapping
    public Result delete(@RequestParam List<Long> ids){
        log.info("菜品批量删除:{}",ids);
        dishService.deleteBatch(ids);

        //将所有菜品缓存数据都清理掉
        cleanCache("dish_*");
        return Result.success();
    }

    //根据id查询数据
    @GetMapping("/{id}")
    public Result<DishVO> getById(@PathVariable Long id) {
        log.info("根据id查询商品:{}",id);
        DishVO dishVO = dishService.getByIdWithFlavor(id);
        return Result.success(dishVO);
    }


    //修改菜品
    @PutMapping
    public Result update(@RequestBody DishDTO dishDTO) {
        log.info("修改菜品:{}",dishDTO);
        dishService.updateWithFlavor(dishDTO);

        //将所有菜品缓存数据都清理掉
        cleanCache("dish_*");
        return Result.success();
    }

    //起售停售
    @PostMapping("/status/{status}")
    public Result updateStatus(@PathVariable Integer status, Long id){
        log.info("修改id菜品起售停售:{}",id);
        dishService.updateStatus(status,id);

        //将所有的菜品缓存全部清理
        cleanCache("dish_*");
        return Result.success();
    }

    //根据分类id查询菜品
    @GetMapping("/list")
    public Result<List<Dish>> list(Long categoryId){
        log.info("根据分类id查询菜品:{}",categoryId);
        List<Dish> dishList = dishService.list(categoryId);
        return Result.success(dishList);
    }

    private void cleanCache(String pattern) {
        //将所有菜品缓存数据都清理掉
        Set keys = redisTemplate.keys(pattern);
        redisTemplate.delete(keys);
    }

}

2.缓存套餐

2.1Spring Cache

Spring Cache是一个框架,实现了基于注解的缓存功能

它提供了一层抽象,底层可以切换不同的缓存实现

  • EHCache
  • Caffeine
  • Redis

常用注解

2.2代码开发

实现思路

  • 导入Spring Cache和Redis相关和maven坐标
  • 在启动类上加入@EnableCaching注解,开启缓存注解功能
  • 在用户端接口SetmealController的list方法上加上@Cacheable注解
  • 在管理端接口SetmealController的save、delete、update、startOrStop等方法上加上@CacheEvict注解

1.在sky-server包下的启动类SkyApplication类上加上注解:

java 复制代码
@EnableCaching

2.在controller/user包下的SetmealController类中的list方法上加上:

java 复制代码
@Cacheable(cacheNames="setmealCache",key="#categoryId") //key: setmealCache::100

3.在controller/admin包下的SetmealController类中的save方法上加上:

java 复制代码
@CacheEvict(cacheNames="setmealCache",key="#setmealDTO.categoryId")

4.在controller/admin包下的SetmealController类中的update、delete、startOrStop方法上加上:

java 复制代码
@CacheEvict(cacheNames = "setmealCache",allEntries = true)

3.添加购物车

数据库设计

  • 作用:暂时存放所选商品的地方
  • 选的什么商品
  • 每个商品都买了几个
  • 不同用户的购物车需要区分开

ShoppingCartController类加入

java 复制代码
//添加购物车
    @PostMapping("/add")
    public Result add(@RequestBody ShoppingCartDTO shoppingCartDTO){
        log.info("添加购物车,添加商品信息:{}",shoppingCartDTO);
        shoppingCartService.addShoppingCart(shoppingCartDTO);
        return Result.success();
    }

在ShoppingCartServiceImpl加入

java 复制代码
//添加购物车
    @Override
    public void addShoppingCart(ShoppingCartDTO shoppingCartDTO) {
        //判断当前加入到购物车中的商品是否已经存在
        ShoppingCart shoppingCart = new ShoppingCart();
        BeanUtils.copyProperties(shoppingCartDTO,shoppingCart);
        shoppingCart.setUserId(BaseContext.getCurrentId());
        List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);

        //如果已存在,只需要数量加一
        if(list != null && list.size() > 0){
            ShoppingCart cart = list.get(0);
            cart.setNumber(cart.getNumber() + 1);
            shoppingCartMapper.updateNumberById(cart);
        }else {
            //如果不存在,需要插入一条购物车数据
            //判断是套餐还是菜品
            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 {
                //是套餐
                Long setmealId = shoppingCartDTO.getSetmealId();
                Setmeal setmeal = setmealMapper.getById(setmealId);
                shoppingCart.setName(setmeal.getName());
                shoppingCart.setImage(setmeal.getImage());
                shoppingCart.setAmount(setmeal.getPrice());
            }
            shoppingCart.setNumber(1);
            shoppingCart.setCreateTime(LocalDateTime.now());
            shoppingCartMapper.insert(shoppingCart);
        }



    }

Mapper层

java 复制代码
//动态条件查询数据
    List<ShoppingCart> list(ShoppingCart shoppingCart);

//根据id修改商品数量
    @Update("update shopping_cart set number = #{number} where id = #{id}")
    void updateNumberById(ShoppingCart cart);

    //插入商品到购物车
    @Insert("insert into shopping_cart(name,user_id,dish_id,setmeal_id,dish_flavor,number,amount,image,create_time)" +
            "value (#{name},#{userId},#{dishId},#{setmealId},#{dishFlavor},#{number},#{amount},#{image},#{createTime})")
    void insert(ShoppingCart shoppingCart);

    //根据用户id删除数据
    @Delete("delete from shopping_cart where user_id = #{userId}")
    void deleteByUserId(Long userId);

xml文件编写list的动态查询语句

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.ShoppingCartMapper">


    <select id="list" resultType="com.sky.entity.ShoppingCart">
        select * from shopping_cart
        <where>
            <if test="userId != 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>
</mapper>

4.查看购物车

Controller层

java 复制代码
//查看购物车
    @GetMapping("/list")
    public Result<List<ShoppingCart>> list(){
        log.info("查看购物车");
        List<ShoppingCart> list = shoppingCartService.showShoppingCart();
        return Result.success(list);
    }

Service实现类

java 复制代码
//查看购物车
    @Override
    public List<ShoppingCart> showShoppingCart() {
        //获取当前用户id
        Long userId = BaseContext.getCurrentId();
        ShoppingCart shoppingCart = ShoppingCart.builder()
                .userId(userId)
                .build();
        List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);
        return list;
    }

5.清空购物车

Controller层

java 复制代码
//清空购物车
    @DeleteMapping("/clean")
    public Result clean(){
        log.info("清空购物车");
        shoppingCartService.cleanShoppingCart();
        return Result.success();
    }

Service层

java 复制代码
//清空购物车
    @Override
    public void cleanShoppingCart() {
        Long userId = BaseContext.getCurrentId();
        shoppingCartMapper.deleteByUserId(userId);
    }
相关推荐
Mr -老鬼1 小时前
基于 Go 的脚本平台 APP 云控系统
开发语言·后端·golang
晔子yy1 小时前
带你了解Java中的Mono接口
java·数据库·oracle
BingoGo1 小时前
“Fatal error: require(): Failed opening required...” 以及如何彻底避免它再次出现
后端·php
zhougl9961 小时前
Springboot - druid 连接池
java·spring boot·后端
全栈前端老曹1 小时前
【Redis】发布订阅模型 —— Pub/Sub 原理、消息队列、聊天系统实战
前端·数据库·redis·设计模式·node.js·全栈·发布订阅模型
EveryPossible2 小时前
工作流练习
服务器·python·缓存
JaguarJack2 小时前
“Fatal error: require(): Failed opening required...” 以及如何彻底避免它再次出现
后端·php·服务端
MX_93592 小时前
Spring基本配置和注入配置注解使用
java·后端·spring
brucelee1862 小时前
AWS IoT Core + Lambda + DynamoDB + Redis 完整教程(Java JDK21)
redis·物联网·aws