【苍穹外卖】Day 7 缓存、购物车相关接口

news2024/12/26 20:57:22

1 缓存

存在问题:

        用户端小程序展示的菜品数据都是通过査询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大

=>解决:

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

缓存逻辑分析:

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

浏览菜品页面使用的路径:

GET  /user/dish/list?categoryId=1

在 redis 这样保存

package com.sky.controller.user;

import com.sky.constant.StatusConstant;
import com.sky.entity.Dish;
import com.sky.result.Result;
import com.sky.service.DishService;
import com.sky.vo.DishVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController("userDishController")
@RequestMapping("/user/dish")
@Slf4j
@Api(tags = "C端-菜品浏览接口")
public class DishController {
    @Autowired
    private DishService dishService;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 根据分类id查询菜品
     *
     * @param categoryId
     * @return
     */
    @GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result<List<DishVO>> list(Long categoryId) {
        // 1.查询 redis 中是否有菜品数据
        //构造 key
        String key = "dish_" + categoryId.toString();
        List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
        if (list != null && list.size() > 0) {
            // 2.如果有,直接返回
            return Result.success(list);

        }
        // 3.如果没有,先去数据库查询,然后数据缓存在 redis,返回
        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);
    }

}

清理缓存

        当修改完菜品信息时,修改了sql数据库,但再去浏览菜品,访问的是 redis,需要 清理缓存
 

然后在增删改操作return前,加上

cleanCache("dish_*");

2 Spring Cache

        Spring Cache 提供了一层抽象,底层可以切换不同的缓存实观,例如:

  • EHCache
  • Caffeine
  • Redis
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    <version>2.7.3</version>
</dependency>

 

/**
     * 新增菜品,不光加入 菜品表 dish 还有 口味表 dish flavor
     *
     * @param dishDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增菜品")
    @CacheEvict(cacheNames = "dishCache",key = "#dishDTO.categoryId")
    public Result add(@RequestBody DishDTO dishDTO) {
        log.info("新增菜品");
        dishService.add(dishDTO);
        return Result.success();
    }

如果不好获得categoryId,就全删除

@CacheEvict(cacheNames = "dishCache",allEntries = true)
/**
     * 根据菜品 id 删除菜品
     *
     * @param ids
     * @return
     */
    @DeleteMapping
    @ApiOperation("根据菜品 id 删除菜品")
    @CacheEvict(cacheNames = "dishCache",allEntries = true)
    public Result deleteById(@RequestParam List<Long> ids) {
        dishService.delete(ids);
        return Result.success();
    }

为什么不是 key = "#{dishDTO.categoryId}":

  • # 用于在Spring缓存注解中的SpEL表达式,表示引用方法参数
  • #{} 用于解析Spring上下文中的Bean或者系统属性

3 缓存套餐

【增删改】要 【删除缓存】---> @CacheEvict

【查】要【访问缓存】---> @Cacheable

 

对于用户端:

        只能查询,不能增删改,所以只加 @Cacheable

对于管理端:

/**
     * 新增套餐
     *
     * @param setmealDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增套餐")
    @CacheEvict(cacheNames = "setmealCache",key = "#setmealDTO.categoryId")
    public Result add(@RequestBody SetmealDTO setmealDTO) {
        setmealService.add(setmealDTO);
        return Result.success();
    }

4 添加购物车

4.1 设计

 

 设计冗余字段:用空间换时间

4.2 实现

controller

service

package com.sky.service.impl;

import com.sky.context.BaseContext;
import com.sky.dto.ShoppingCartDTO;
import com.sky.entity.Dish;
import com.sky.entity.Setmeal;
import com.sky.entity.ShoppingCart;
import com.sky.mapper.DishMapper;
import com.sky.mapper.SetmealMapper;
import com.sky.mapper.ShoppingCartMapper;
import com.sky.service.ShoppingCartService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class ShoppingCartServiceImpl implements ShoppingCartService {
    @Autowired
    private ShoppingCartMapper shoppingCartMapper;

    @Autowired
    private DishMapper dishMapper;

    @Autowired
    private SetmealMapper setmealMapper;

    /**
     * 添加购物车
     *
     * @param shoppingCartDTO
     */
    public void add(ShoppingCartDTO shoppingCartDTO) {
        //判断当前商品是否购物车中已存在
        ShoppingCart shoppingCart = new ShoppingCart();
        BeanUtils.copyProperties(shoppingCartDTO, shoppingCart);

        Long userId = BaseContext.getCurrentId();
        shoppingCart.setUserId(userId);

        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 {
            //如果不存在,插入数据
            //首先构造一个 ShoppingCart 对象
            //再补充数据:
            //name image amount create_time

            Long dishId = shoppingCart.getDishId();
            Long setmealId = shoppingCart.getSetmealId();
            if (dishId != null) {
                // 查询菜品表获取 name image amount
                Dish dish = dishMapper.getById(dishId);
                
                shoppingCart.setName(dish.getName());
                shoppingCart.setImage(dish.getImage());
                shoppingCart.setAmount(dish.getPrice());
                shoppingCart.setNumber(1); // 注意要设置数量为 1
            } else {
                // 查询套餐表获取 name image amount
                Setmeal setmeal = setmealMapper.getById(setmealId);
                
                shoppingCart.setName(setmeal.getName());
                shoppingCart.setImage(setmeal.getImage());
                shoppingCart.setAmount(setmeal.getPrice());
                shoppingCart.setNumber(1); // 注意要设置数量为 1
            }
            shoppingCart.setCreateTime(LocalDateTime.now());
        }
        shoppingCartMapper.insert(shoppingCart);
    }
}

mapper

package com.sky.mapper;

import com.sky.entity.ShoppingCart;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Update;

import java.util.List;

@Mapper
public interface ShoppingCartMapper {
    /**
     * 条件查询购物车
     *
     * @param shoppingCartDTO
     * @return
     */
    List<ShoppingCart> list(ShoppingCart shoppingCartDTO);

    /**
     * 根据id更新菜品数量number(加一)
     *
     * @param cart
     */
    @Update("update sky_take_out.shopping_cart set number = #{number} where id = #{id}")
    void updateNumberById(ShoppingCart cart);

    /**
     * 加入购物车
     * @param shoppingCart
     */
    @Insert("insert into sky_take_out.shopping_cart" +
            "(name, image, user_id, dish_id, setmeal_id, dish_flavor, amount, create_time)" +
            "values" +
            "(#{name},#{image},#{userId},#{dishId},#{setmealId},#{dishFlavor},#{amount},#{createTime})")
    void insert(ShoppingCart shoppingCart);
}

xml

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

4.3 测试

5 查看购物车 与 清空购物车

查看

Path:/user/shoppingCart/list

Method:GET

清空

Path:/user/shoppingCart/clean

Method:DELETE

controller

/**
     * 查看购物车
     *
     * @return
     */
    @ApiOperation("查看购物车")
    @GetMapping("/list")
    public Result<List<ShoppingCart>> list() {
        List<ShoppingCart> list = shoppingCartService.list();
        return Result.success(list);
    }

    /**
     * 清空购物车
     *
     * @return
     */
    @ApiOperation("清空购物车")
    @DeleteMapping("/clean")
    public Result clean() {
        shoppingCartService.clean();
        return Result.success();
    }

 service

    /**
     * 查看购物车
     */
    @Override
    public List<ShoppingCart> list() {
        List<ShoppingCart> list = shoppingCartMapper.listAll();
        return list;
    }


    /**
     * 清空购物车
     */
    @Override
    public void clean() {
        shoppingCartMapper.clean();
    }

mapper

/**
     * 查看购物车,返回所有数据
     *
     * @return
     */
    @Select("select * from sky_take_out.shopping_cart")
    List<ShoppingCart> listAll();

    /**
     * 清空购物车
     */
    @Delete("delete from sky_take_out.shopping_cart")
    void clean();

测试

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

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

相关文章

PyDracula:用 PySide6 和 PyQt6打造现代感 Python GUI 的利器

PyDracula 是一个基于 PySide6 或 PyQt6 的 Python GUI 库&#xff0c;它以其现代、美观、易于定制的特点而闻名。PyDracula 融合了流行的 Dracula 主题&#xff0c;并提供了丰富的功能&#xff0c;让开发者可以轻松构建出具有吸引力的图形界面。本文将详细介绍 PyDracula 的核…

【Godot4.3】CanvasShape资源化改造

概述 通过把之前自定义的CanvasShape类变成资源类型&#xff0c;将可以同时用于CanvasItem绘图和创建扩展的Node2D和Polygon2D节点等。 本篇就完成CanvasShape类的资源化改造&#xff0c;并记录改造过程和思路。 CanvasShape资源类型体系 CanvasShape仍然为图形基类&#xf…

Android Auto未来可能支持无线电广播

通过Android Auto&#xff0c;可以在车载收音机上使用 Google 地图、音乐、收听播客&#xff0c;还能获取天气等基本信息。最近&#xff0c;国外科技媒体9to5Google通过分析 Android Auto v12.3 和 v12.4的应用程序的代码发现了一些提示信息&#xff0c;特别提到了 AM、FM、HD …

【Protobuf】xml、json与protobuf有什么区别,protobuf详解(保姆篇)

文章目录 简介Protobuf 的原理安装 Protobuf 编译器在 Python 中使用 Protobuf安装语言特定的 Protobuf 库定义消息结构生成代码使用 Protobuf 进行序列化和反序列化 在 Java 中使用 Protobuf安装和配置编译 .proto 文件使用生成的 Java 类创建和序列化对象 代码注释 高级特性嵌…

LabVIEW灵活集成与调试的方法

在LabVIEW开发中&#xff0c;为了构建一个既便于调试又能灵活集成到主VI中的控制VI&#xff0c;开发者需要采用适当的编程方式和架构。常见的选择包括模块化设计、状态机架构以及事件驱动编程。这些方法有助于简化调试过程、提高系统的稳定性&#xff0c;并确保代码的重用性和可…

day43-测试平台搭建之前端vue学习-基础2

目录 一、数据代理 二、事件处理 三、计算属性 四、监控属性 五、绑定样式 六、今日学习思维导图 一、数据代理 1.1.数据代理&#xff1a;通过一个对象代理对另外一个对象中属性的操作 (读/写) 1.2.Vue中数据代理的好处&#xff1a;更加方便的操作data中的数据 1.3.基本原…

vue2结合element-ui使用tsx格式实现formily自定义组件

简洁 在公司实习&#xff0c;需要参与开发一个基于formily的低代码平台&#xff0c;实现自定义formily组件&#xff0c;在此记录一下。 示例源码 demo源码 实现思路 开始实现自定义组件之前最好先看一下formily官网的core、vue、element部分&#xff0c;如果有能力也可以阅…

2024数学建模国赛A题word版成品论文30页【附带完整解题代码+可视化图表】

0906 0:30 v1.0 问题一、问题二的完整可运行代码&#xff0c;模型建立与求解这一部分的论文。 0906 5:20 v1.1 增加了第三问的完整可运行代码和第二、三问的“模型建立与求解”的论文。&#xff08;即1-3问的代码、模型建立与求解、算法设计、结果分析&#xff09; 1-4问完整可…

TensorFlow创建回归神经网络及Optimizer优化器

一.TensorFlow创建神经层 如图所示&#xff0c;通过该神经网络识别动物猫或狗&#xff0c;共包括输入层&#xff08;Input Layer&#xff09;、隐藏层3层&#xff08;Hidden Layer&#xff09;和输出层&#xff08;Output Layer&#xff09;。其中每个隐藏层神经元都有一个激励…

Unity(2022.3.41LTS) - UI详细介绍- 原始图像

目录 零.简介 一、基本功能 二、属性和设置 三、与其他 UI 元素的配合 四、代码控制 六. 和 image的区别 零.简介 在 Unity 中&#xff0c;RawImage 是一种用于显示原始图像的 UI 组件。 一、基本功能 显示图像&#xff1a;RawImage 主要用于在 UI 中直接显示一张图像。…

Python数组遍历-从基础到高级的全面指南

你有没有想过,为什么有些程序员能够轻松地操纵大量数据,而其他人却在简单的数组操作上挣扎?答案往往藏在一个看似简单却至关重要的技能中:数组遍历。无论你是刚入门的新手,还是寻求提升的老手,掌握Python中的数组遍历技巧都将极大地提升你的编程效率和代码质量。 在这篇文章中…

使用 systemd-analyze 分析 Linux 系统启动慢的原因

使用 systemd-analyze 命令可以查看 Linux 系统在启动过程中每个服务的耗时情况, 方便我们排查是哪个环节导致系统启动缓慢, 以下是整理的常用命令参数和效果. 例子中一下子就可以定位到是 gssproxy.service 服务启动耗时过长. systemd-analyze blame Print list of running u…

LabVIEW如何自学成为专业开发者

自学成为LabVIEW专业开发者需要一个系统化的学习和实践过程&#xff0c;以下是一些关键步骤&#xff1a; 1. 扎实的基础学习 了解LabVIEW的基础概念&#xff1a;首先要熟悉LabVIEW的基本操作、数据流编程理念和图形化编程环境。可以通过LabVIEW的官方教程、Bilibili上的视频课程…

【舞动生命,不缺营养!】亨廷顿舞蹈症患者的维生素补给站

Hey小伙伴们~ &#x1f44b; 今天我们要聊的是一个温暖而重要的话题——关于亨廷顿舞蹈症&#xff08;HD&#xff09;患者如何通过合理补充维生素&#xff0c;来更好地支持他们的健康与生活品质&#xff01;&#x1f31f; &#x1f338; ‌首先&#xff0c;了解亨廷顿舞蹈症‌…

TCP的传输速度

如何确定TCP最大传输速度&#xff1f; TCP 的传输速度&#xff0c;受限于发送窗⼝&#xff0c;接收窗⼝以及⽹络设备传输能⼒。 其中&#xff0c;窗⼝⼤⼩由内核缓冲区⼤⼩决定。如果缓冲区与⽹络传输能⼒匹配&#xff0c;那么缓冲区的利⽤率就达到了最⼤化。 如何计算网络传…

JAVA:Spring Boot 整合 Swagger 的技术指南

请关注微信公众号&#xff1a;拾荒的小海螺 博客地址&#xff1a;http://lsk-ww.cn/ 1、简述 在现代Web开发中&#xff0c;API文档的生成和维护是非常重要的。Swagger是一款流行的API文档生成工具&#xff0c;它可以帮助开发者自动生成API文档&#xff0c;并提供可视化的接口…

Redis从简单使用到底层原理与分布式缓存

文章目录 [Redis参考手册](https://redis.io/docs/latest/commands/)1 基础认识1.1 安装配置1.2 通用命令1.3 数据类型1.3.1 数据结构与内部编码stringkey的结构hashlistsetsorted_set 1.4 单线程模型 2 redis客户端2.1 RESP协议&#xff08;Redis serialization protocol&…

SpringBoot2:请求处理原理分析-接口参数的常用注解

1、PathVariable 作用说明&#xff1a;获取路径参数 案例&#xff1a; 接口收参形式&#xff1a; GetMapping("/car/{id}/owner/{username}")public Map<String,Object> getCar(PathVariable("id") Integer id,PathVariable("username")…

echarts圆饼图定时器动画

(function () {const WdxjEcharts echarts.init(document.getElementById(wdxjEchart))let num 0;var imgURL "../imagesNew/wd-center.png";var trafficWay [{name: 火车,value: 20}, {name: 飞机,value: 10}, {name: 客车,value: 30}, {name: 轮渡,value: 40}]…

深入解读Docker核心网络管理:架构、模式与通信机制

在容器化技术中&#xff0c;网络管理是影响容器通信和应用部署的重要组成部分。Docker不仅简化了应用的部署过程&#xff0c;还提供了强大的网络管理功能&#xff0c;确保容器之间以及容器与外部系统的网络通信能够高效、稳定地进行。 本文将深入解读Docker的核心网络管理原理…