苍穹外卖 项目记录 day07 商品缓存-购物车模块开发

server/2025/1/24 21:52:32/

文章目录

  • 前言
  • 清理缓存数据
  • Spring Cache 和常用注解
  • 缓存套餐
  • 添加购物车
  • 查看购物车
  • 清空购物车


前言

将商品信息放进redis缓存 Spring Cache技术

系统查询性能 用户端访问量过大 数据库访问压力随之增大 系统响应慢

使用Redis 缓存菜品数据 减少数据库查询 基于内存保存数据

前端 发请求 查询 后端服务 查询缓存是否存在 (存在缓存 读取缓存 不存在缓存 查询数据库 查询到数据 载入缓存)

key-value 键值对匹配

缓存逻辑分析 根据分类缓存数据 每个分类下菜品保存一份缓存数据

key dish_id value string List对象序列化成字符串 存储

菜品数据变更时 清理缓存数据 (延迟双删)

每次切换查询数据 都会发请求 查询数据库

java">public class DishController {@Autowiredprivate DishService dishService;@Autowired//  已创建RedisConfiguration  直接注入即可private RedisTemplate redisTemplate;/*** 根据分类id查询菜品** @param categoryId* @return*/@GetMapping("/list")@ApiOperation("根据分类id查询菜品")public Result<List<DishVO>> list(Long categoryId) {//查询Redis 中是否存在菜品数据  操作redis  注入对象//构造redis的 keyString key = "dish_" + categoryId;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);}
}

清理缓存数据

保证数据一致性 缓存和数据库

修改 删除时 状态变化 新增时 清理缓存

改造对应方法 (DishController) 修改 admin 下 DishController 方法

清理缓存数据

java">package com.sky.controller.admin;/*** @author Admin* @title: DishController* @projectName minjiang-takeaway* @description: DishController* @date 2024/1/20 18:17*/import com.sky.dto.DishDTO;
import com.sky.dto.DishPageQueryDTO;
import com.sky.entity.Dish;
import com.sky.result.PageResult;
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.*;import java.util.List;
import java.util.Set;/*** 菜品管理*/
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品管理相关接口")
@Slf4j
public class DishController {@Autowiredprivate DishService dishService;@Autowiredprivate RedisTemplate redisTemplate;/*** 新增菜品*/@PostMapping@ApiOperation("新增菜品")public Result save(@RequestBody DishDTO dishDTO){log.info("新增菜品:{}",dishDTO.toString());dishService.saveWithFlavor(dishDTO);//清理缓存数据String key = "dish_" + dishDTO.getCategoryId();redisTemplate.delete(key);return Result.success();}/*** 菜品分页查询** @param dishPageQueryDTO* @return*/@GetMapping("/page")@ApiOperation("菜品分页查询")public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {log.info("菜品分页查询:{}", dishPageQueryDTO);//调用分页查询接口PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);return Result.success(pageResult);}/*** 菜品批量删除*/@DeleteMapping@ApiOperation("菜品批量删除")public Result delete(@RequestParam List<Long> ids){log.info("菜品批量删除:{}",ids);dishService.deleteBatch(ids);//将所有菜品缓存数据清理掉  所有以dish_开头的keycleanCache("dish_*");return Result.success();}/*** 根据id查询菜品  先由id进行菜品数据回显 才能进行菜品修改** @param id* @return*/@GetMapping("/{id}")@ApiOperation("根据id查询菜品")public Result<DishVO> getById(@PathVariable Long id) {log.info("根据id查询菜品:{}", id);DishVO dishVO = dishService.getByIdWithFlavor(id);return Result.success(dishVO);}/*** 修改菜品** @param dishDTO* @return*/@PutMapping@ApiOperation("修改菜品")public Result update(@RequestBody DishDTO dishDTO) {log.info("修改菜品:{}", dishDTO);dishService.updateWithFlavor(dishDTO);//将所有菜品缓存数据清理掉  所有以dish_开头的keycleanCache("dish_*");return Result.success();}/*** 实现菜品起售停售*/@PostMapping("/status/{status}")@ApiOperation("菜品起售停售")public Result<String> startOrStop(@PathVariable Integer status,Long id){dishService.startOrStop(status,id);cleanCache("dish_*");return Result.success();}/**** 根据分类id查询菜品列表* @param categoryId* @return*/@GetMapping("/list")@ApiOperation("根据分类id查询菜品列表")public Result<List<Dish>> list(Long categoryId){List<Dish> list = dishService.list(categoryId);return Result.success(list);}/*** 抽取统一清理缓存数据方法*/private void cleanCache(String pattern){//将所有菜品缓存数据清理掉  所有以dish_开头的keySet keys = redisTemplate.keys(pattern);redisTemplate.delete(keys);}
}

Spring Cache 和常用注解

Spring Cache spring缓存框架 简单加一个注解 实现缓存功能

Spring Cache 提供一层抽象 底层可切换不同缓存实现 EHCache Caffeine Redis

导入pom.xml

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Spring Cache 常用注解

@EnableCaching 开启缓存注解 加载启动类上

@Cacheable 方法执行前 查询缓存是否有数据 有数据返回缓存数据 无数据 调用方法 将方法换回值 放到缓存

@CachePut 将方法返回值放到缓存

@CacheEvict 将一条或多条数据从缓存中删除

java">@PostMapping
//@CachePut(cacheNames = "userCache",key = "#user.id")
//@CachePut(cacheNames = "userCache",key = "#p0.id")
//@CachePut(cacheNames = "userCache",key = "#root.args[0].id")
@CachePut(cacheNames = "userCache",key = "#result.id")
//存到Redis的key值 userCache::user.id
public User save(@RequestBody User user){userMapper.insert(user);//插入数据时 保存数据到redisreturn user;
}@GetMapping
@Cacheable(cacheNames = "userCache",key = "#id")
//获取user前 先从redis里找
public User getById(Long id){User user = userMapper.getById(id);return user;
}@DeleteMapping
@CacheEvict(cacheNames = "userCache",key="#id")
public void deleteById(Long id){userMapper.deleteById(id);
}@DeleteMapping("/delAll")
@CacheEvict(cacheNames = "userCache",allEntries = true)
public void deleteAll(){userMapper.deleteAll();
}

缓存套餐

导入 Spring Cache 和 Redis 相关pom.xml坐标

启动类上 加入 @EnableCaching注解 开启缓存功能

用户端接口 SetmealController list方法加上@Cacheable注解

管理端接口 SetmealController save delete update startOrStop 加上CacheEvict注解

java">@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@Slf4j
@EnableCaching
public class SkyApplication {public static void main(String[] args) {SpringApplication.run(SkyApplication.class, args);log.info("server started");}
}@GetMapping("/list")
@ApiOperation("根据分类id查询套餐")
@Cacheable(cacheNames = "setmealCache", key = "#categoryId") // key: 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);
}@RestController
@RequestMapping("/admin/setmeal")
@Api(tags = "套餐管理相关接口")
@Slf4j
public class SetmealController {@Autowiredprivate SetmealService setmealService;@PostMapping@ApiOperation("新增套餐")@CacheEvict(cacheNames = "setmealCache",key="#setmealDto.categoryId")public  Result save(@RequestBody SetmealDTO setmealDto){setmealService.saveWithDish(setmealDto);return Result.success("新增套餐成功");}/*** 分页查询* @param setmealPageQueryDTO* @return*/@GetMapping("/page")@ApiOperation("套餐分页查询")public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO){PageResult pageResult = setmealService.pageQuery(setmealPageQueryDTO);return Result.success(pageResult);}/***  删除套餐* @param ids* @return*/@DeleteMapping@ApiOperation("批量删除套餐")@CacheEvict(cacheNames = "setmealCache",allEntries = true)public Result delete(@RequestParam List<Long> ids){//删除套餐表setmealService.deleteBatch(ids);return Result.success();}/***  根据id查询套餐 实现修改回显* @param id* @return*/@GetMapping("/{id}")@ApiOperation("根据id查询套餐")public Result<SetmealVO> getById(@PathVariable Long id){SetmealVO setmealVO = setmealService.getByIdWithDish(id);return Result.success(setmealVO);}@PutMapping@ApiOperation("修改套餐")@CacheEvict(cacheNames = "setmealCache",allEntries = true)public Result update(@RequestBody  SetmealDTO setmealDTO){setmealService.update(setmealDTO);return Result.success("修改成功");}@PostMapping("/status/{status}")@ApiOperation("套餐起售停售")@CacheEvict(cacheNames = "setmealCache",allEntries = true)public Result<String> startOrStop(@PathVariable Integer status,Long id){setmealService.startOrStop(status,id);return Result.success();}
}

添加购物车

增删改查

POST请求 接口设计

添加购物车 /user/shoppingCart/add

数据库设计 shopping_cart (冗余字段 逻辑外键) 设计 冗余字段 提高查询速度

不同用户 购物车区分开

创建ShoppingCartController ShoppingCartMapper.java SshoppingCartMapper.xml

ShoppingCartService ShoppingCartServiceImpl

java">@RestController
@RequestMapping("/user/shoppingCart")
@Slf4j
@Api(tags = "C端购物车接口")
public class ShoppingCartController {@Autowiredprivate ShoppingCartService shoppingCartService;@PostMapping("/add")@ApiOperation("添加购物车")public Result add(@RequestBody ShoppingCartDTO shoppingCartDTO){log.info("添加购物车:{}",shoppingCartDTO);//业务操作一般封装到service中shoppingCartService.addShoppingCart(shoppingCartDTO);return Result.success("添加购物车成功");}
}
java">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;/*** @author Admin* @title: ShoppingCartMapper* @projectName minjiang-takeaway* @description: ShoppingCartMapper* @date 2024/6/23 3:24*/
@Mapper
public interface ShoppingCartMapper {//动态条件 查询购物车数据List<ShoppingCart> list(ShoppingCart shoppingCart);/*** 根据id修改商品数量* @param shoppingCart*/@Update("update shopping_cart set number = #{number} where id = #{id}")void updateNumberById(ShoppingCart shoppingCart);/***  添加购物车数据* @param shoppingCart*/@Insert("insert into shopping_cart(name,user_id,dish_id,dish_favor,number,amount,image,create_time)" +" values(#{name},#{userId},#{dishId},#{setmealId},#{dishFlavor},#{number}#{amount},#{image},#{createTime})")void insert(ShoppingCart shoppingCart);
}
java"><?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>
java">public interface ShoppingCartService {/***  添加购物车* @param shoppingCartDTO*/void addShoppingCart(ShoppingCartDTO shoppingCartDTO);
}@Service
@Slf4j
public class ShoppingCartServiceImpl implements ShoppingCartService {@Autowiredprivate ShoppingCartMapper shoppingCartMapper;@Autowiredprivate DishMapper dishMapper;@Autowiredprivate SetmealMapper setmealMapper;@Overridepublic void addShoppingCart(ShoppingCartDTO shoppingCartDTO) {/***  添加购物车逻辑*///判断商品是否已存在与购物车//查询是否已存在  套餐id + userId  select * from shopping_cart where setmeal_id = ? and user_id = ?// select * from shopping_cart where dish_id = ? and user_id = ? and dish_flavor = ?//动态sqlShoppingCart shoppingCart = new ShoppingCart();//属性拷贝BeanUtils.copyProperties(shoppingCartDTO,shoppingCart);Long userId = BaseContext.getCurrentId();//添加userId属性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); // update shopping_cart set number = ? where id = ?shoppingCartMapper.updateNumberById(cart);}else{//如果不存在,则添加到购物车//前端上送idLong 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);}}
}

查看购物车

Get /user/shoppingCart/list

java">/*** 查询购物车数据*/
@GetMapping("/list")
@ApiOperation("查询购物车数据")
public Result<List<ShoppingCart>> list(){List<ShoppingCart> list = shoppingCartService.showShoppingCart();return Result.success(list);
}/*** 查看购物车* @return*/
@Override
public List<ShoppingCart> showShoppingCart() {//获取当前微信用户idLong  userId = BaseContext.getCurrentId();ShoppingCart shoppingCart = ShoppingCart.builder().userId(userId).build();List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);return list;
}

清空购物车

user/shoppingCart/clean

删除购物车某一项商品

java">/*** 删除购物车商品*/@PostMapping("/sub")
@ApiOperation("删除购物车商品")
public Result deleteById(@RequestBody  ShoppingCartDTO shoppingCartDTO){shoppingCartService.deleteShoppingCart(shoppingCartDTO);return Result.success("删除成功");
}/*** 删除购物车商品* @param shoppingCartDTO*/
void deleteShoppingCart(ShoppingCartDTO shoppingCartDTO);@Override
public void deleteShoppingCart(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 = list.get(0);Integer number = shoppingCart.getNumber();if(number == 1){//当前商品在购物车中份数为1 直接删除shoppingCartMapper.deleteById(shoppingCart.getId());}else{shoppingCart.setNumber(shoppingCart.getNumber() -1);shoppingCartMapper.updateNumberById(shoppingCart);}}
}

http://www.ppmy.cn/server/161114.html

相关文章

Node.js 能做什么

一、服务器端开发 1. 构建 Web 服务器 使用内置的 http 模块或流行的框架&#xff08;如 Express、Koa 等&#xff09;创建 Web 服务器&#xff0c;处理 HTTP 请求和响应。可以处理各种类型的请求&#xff0c;如 GET、POST、PUT、DELETE 等&#xff0c;并返回相应的 HTML、JS…

栈和队列(C语言)

目录 数据结构之栈 定义 实现方式 基本功能实现 1&#xff09;定义&#xff0c;初始化栈 2&#xff09;入栈 3&#xff09;出栈 4&#xff09;获得栈顶元素 5)获得栈中有效元素个数 6&#xff09;检测栈是否为空 7&#xff09;销毁栈 数据结构之队列 定义 实现方…

JupyterLab 安装以及部分相关配置

安装 JupyterLab pip install jupyter启动 JupyterLab jupyter lab [--port <指定的端口号>] [--no-browser] # --port 指定端口 # --no-browser 启动时不打开浏览器安装中文 首先安装中文包 pip install jupyterlab-language-pack-zh-CN安装完成后重启 JupyterLab 选…

MySQL为什么使用B+树?B+树和B树的区别

MySQL为什么使用B树&#xff1f;B树和B树的区别 在数据库系统中&#xff0c;索引是提高数据检索效率的关键技术。MySQL 默认使用 B树 作为索引的数据结构&#xff0c;而不是 B 树或其他数据结构。这是因为 B树在范围查询、磁盘 I/O 效率以及数据存储方式等方面具有显著优势。 …

08-ArcGIS For JavaScript-通过Mesh绘制几何体(Cylinder,Circle,Box,Pyramid)

目录 概述代码实现1、Mesh.createBox2、createPyramid3、Mesh.createSphere4、Mesh.createCylinder 完整代码 概述 对于三维场景而言&#xff0c;二位的点、线、面&#xff0c;三维的圆、立方体、圆柱等都是比较常见的三维对象&#xff0c;在ArcGIS For JavaScript中我们知道点…

【技术洞察】2024科技绘卷:浪潮、突破、未来

涌动与突破 2024年&#xff0c;科技的浪潮汹涌澎湃&#xff0c;人工智能、量子计算、脑机接口等前沿技术如同璀璨星辰&#xff0c;方便了大家的日常生活&#xff0c;也照亮了人类未来的道路。这一年&#xff0c;科技的突破与创新不断刷新着人们对未来的想象。那么回顾2024年的科…

软键盘显示/交互问题

日常开发会经常遇到软键盘覆盖界面布局的问题,比如:我有一个fragment,中心布局了EditText,正常情况是 ,当点击这个EditText的时候,输入法会弹出来,但是输入控件会覆盖掉EditText,看不到输入的内容,这种应该怎么处理呢 这个问题通常是因为当软键盘弹出时&#xff0c;EditText 被…

【Qt】事件

事件 事件的处理鼠标事件键盘事件定时器窗口事件 用户进行的各种操作&#xff0c;就会产生事件。给事件关联上函数或处理逻辑&#xff0c;当事件触发时&#xff0c;就能执行对应的代码。多数场景下&#xff0c;程序和用户的交互可以通过 “信号槽” 完成&#xff0c;但在某些特…