【苍穹外卖|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);
    }
相关推荐
程序猿DD4 小时前
OctaFuse Gateway 2.15.0:MCP 聚合、向量 Embeddings 与官方目录同步
后端·agent
蛋蛋的就会顺顺的6 小时前
hot100 p0——栈,贪心算法,动态规划
java·前端·javascript·算法·leetcode·力扣
余槐i6 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
需要8267 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
小 黄 鸡7 小时前
HashMap 面试必会知识点详解(JDK 8)
java·面试·职场和发展
骇客野人7 小时前
在麒麟操作系统上 分别安装 jdk1.8, 达梦数据库,redis ,memcache 的操作实施方案和步骤
数据库·redis·memcached
凤山老林8 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
伯恩bourne9 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
张彦峰ZYF9 小时前
停顿短不等于延迟低:G1、ZGC 与低延迟服务的目标分野、代价账本与选型标定
后端·回收暂停、应用停止总时长·到达安全点、分配停滞·屏障吃吞吐、并发线程吃核心·并发窗口吃堆余量·分区式靠挑回收集兑现目标·染色指针式不支持压缩引用
silvamux_9 小时前
curl 测试 OpenAI 兼容 API 的完整流程
java·人工智能·缓存·ai·oneapi