一.缓存菜品
1.为什么要提出缓存?
用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大。
2.解决思路
众所周知,redis是基于内存进行存储和访问的,这导致它能够响应执行的时间消耗很少,这也是redis为什么频繁作为缓存数据库的原因之一
3.实现思路
缓存逻辑分析:
1.每个分类下的菜品保存一份缓存数据
2.数据库中菜品数据有变更时清理缓存数据
故采用redis中的string数据结构来存储数据,key的后缀为分类的id
,value为菜品信息集合,只不过转换成了字符串进行存储
4.代码开发
既然是对查询分类做缓存,那就应该在对应的controller类中做修改:
@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) {
//拼接字符串,用于设置key
String key = "dish_" + categoryId;
//查询redis中是否有缓存数据,有则直接返回,没有则添加菜品集合到redis中作为缓存数据
List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
if (list != null && list.size() > 0) {
return Result.success(list);
}
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品
list = dishService.listWithFlavor(dish);
redisTemplate.opsForValue().set(key,list);
return Result.success(list);
}
}
5.功能测试
前后端联调测试
6.Q&A:为什么在使用redisTemplate存储和获取值的时候,字符串和集合能够自动转换?
redisTemplate.opsForValue().set(key, list)
之所以能够自动将list
集合转换为字符串是因为 RedisTemplate 在存储数据到 Redis 中时,会根据泛型类型来进行序列化和反序列化操作。默认情况下,RedisTemplate 使用 Java 默认的序列化器,将对象序列化为字节数组,然后存储到 Redis 中。
当调用redisTemplate.opsForValue().set(key, list)
时,RedisTemplate 会将 list
对象序列化为字节数组,并将其存储在 Redis 中,通常这个字节数组会以二进制格式存储。Redis本身不知道对象的类型,它只是存储字节数组。当从 Redis 中获取数据时,RedisTemplate 会将存储的字节数组反序列化为对象,根据对象的泛型类型将其转换回相应的 Java 对象。
这行代码List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
之所以能够将从 Redis 中获取的字符串转换为集合的原因是因为 RedisTemplate 在后台执行了反序列化操作。
当你从 Redis 中获取数据时,RedisTemplate 会从 Redis 中检索存储的数据,然后进行反序列化操作,将存储的字节数组转换回相应的 Java 对象类型。在这种情况下,它检测到要获取的对象类型是 List<DishVO>
,然后进行反序列化以将存储的字节数组转换为 List<DishVO>
对象。
二.清理缓存菜品
当管理端修改,删除,新增了菜品数据后,导致两个数据库的数据不一致,因为微信小程序只会显示redis缓存的数据,故有了清除缓存菜品
1.抽取清理缓存的方法
/**
* 清理缓存数据
* @param pattern
*/
private void cleanCache(String pattern) {
Set keys = redisTemplate.keys(pattern);
redisTemplate.delete(keys);
}
2.分别应用到对菜品进行操作的方法上
/**
* 菜品的起售和停售
*
* @param status
* @param id
* @return
*/
@PostMapping("status/{status}")
@ApiOperation("菜品的起售和停售")
public Result<String> startOrStop(@PathVariable Integer status, Long id) {
dishService.startOrStop(status, id);
//清理缓存
cleanCache("dish_*");
return Result.success();
}
/**
* 修改菜品
*
* @param dishDTO
* @return
*/
@PutMapping
@ApiOperation("修改菜品")
public Result<String> update(@RequestBody DishDTO dishDTO) {
log.info("修改菜品:{}", dishDTO);
dishService.updateWithFlavor(dishDTO);
//清理缓存
cleanCache("dish_*");
return Result.success();
}
/**
* 菜品批量删除
*
* @param ids
* @return
*/
@DeleteMapping
@ApiOperation("菜品批量删除")
public Result<String> delete(@RequestParam List<Long> ids) {
log.info("菜品批量删除:{}", ids);
dishService.deleteBatch(ids);
//清理缓存
cleanCache("dish_*");
return Result.success();
}
/**
* 新增菜品
*
* @param dishDTO
* @return
*/
@PostMapping
@ApiOperation("新增菜品")
public Result<String> save(@RequestBody DishDTO dishDTO) {
log.info("新增菜品:{}", dishDTO);
dishService.saveWithFlavor(dishDTO);
//清理缓存
Long categoryId = dishDTO.getCategoryId();
String key = "dish_" + categoryId;
cleanCache(key);
return Result.success();
}
3.功能测试
前后端联调测试
三.Spring Cache
1.什么是Spring Cache?
2.Spring Cache的常用注解
简而言之,springcache提供的注解简化了我们手动编写缓存代码,取而代之的是一个小小的注解
四.缓存套餐
1.在user.SetmealController类下
@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);
}
编写注解@Cacheable(cacheNames = “setmealCache”,key = “#categoryId”),其中cacheNmaes属性为键的前缀,key属性为键的后缀,用于动态存储缓存数据的键名称
2.在admin.SetmealController类下
编写清除缓存的注解@CacheEvict,分别用于增删改
@RestController
@RequestMapping("/admin/setmeal")
@Slf4j
@Api(tags = "套餐相关接口")
public class SetmealController {
@Autowired
SetmealService setmealService;
/**
* 新增套餐
* @param setmealDTO
* @return
*/
@PostMapping
@ApiOperation("新增套餐")
@CacheEvict(cacheNames = "setmealCache",key = "#setmealDTO.categoryId")
public Result<String> save(@RequestBody SetmealDTO setmealDTO){
log.info("新增套餐:{}",setmealDTO);
setmealService.saveWithDish(setmealDTO);
return Result.success();
}
/**
* 分页查询
* @param setmealPageQueryDTO
* @return
*/
@GetMapping("/page")
@ApiOperation("分页查询")
public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO){
log.info("分页查询数据:{}",setmealPageQueryDTO);
PageResult pageResult=setmealService.pageQuery(setmealPageQueryDTO);
return Result.success(pageResult);
}
/**
* 批量删除套餐
* @return
*/
@DeleteMapping
@ApiOperation("批量删除套餐")
@CacheEvict(cacheNames = "setmealCache",allEntries = true)
public Result<String> delete(@RequestParam List<Long> ids){
log.info("批量删除套餐id:{}",ids);
setmealService.deleteBatch(ids);
return Result.success();
}
/**
* 根据id查询套餐,用于修改页面回显数据
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询套餐")
public Result<SetmealVO> getById(@PathVariable Long id){
log.info("查询套餐id:{}",id);
SetmealVO setmealVO=setmealService.getByIdWithDish(id);
return Result.success(setmealVO);
}
/**
* 修改套餐
* @param setmealDTO
* @return
*/
@PutMapping
@ApiOperation("修改套餐")
@CacheEvict(cacheNames = "setmealCache",allEntries = true)
public Result<String> update(@RequestBody SetmealDTO setmealDTO){
log.info("修改套餐:{}",setmealDTO);
setmealService.update(setmealDTO);
return Result.success();
}
/**
* 套餐起售停售
* @param status
* @param id
* @return
*/
@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();
}
}
3.测试
前后端联调测试即可
五.添加购物车
1.需求分析和设计
2.代码开发
根据添加购物车接口创建ShoppingCartController:
package com.sky.controller.user;
import com.sky.dto.ShoppingCartDTO;
import com.sky.result.Result;
import com.sky.service.ShoppingCartService;
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.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user/shoppingCart")
@Slf4j
@Api(tags = "C端-购物车接口")
public class ShoppingCartController {
@Autowired
private ShoppingCartService shoppingCartService;
/**
*添加购物车
* @param shoppingCartDTO
* @return
*/
@PostMapping("/add")
@ApiOperation("添加购物车")
public Result<String> add(@RequestBody ShoppingCartDTO shoppingCartDTO) {
log.info("添加购物车:{}", shoppingCartDTO);
//调用service执行对应的业务代码
shoppingCartService.addShoppingCart(shoppingCartDTO);
return Result.success();
}
}
创建ShoppingCartService接口:
package com.sky.service;
import com.sky.dto.ShoppingCartDTO;
public interface ShoppingCartService {
/**
* 添加购物车
* @param shoppingCartDTO
*/
void addShoppingCart(ShoppingCartDTO shoppingCartDTO);
}
创建ShoppingCartServiceImpl实现类,并实现add方法:
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 addShoppingCart(ShoppingCartDTO shoppingCartDTO) {
ShoppingCart shoppingCart = new ShoppingCart();
BeanUtils.copyProperties(shoppingCartDTO, shoppingCart);
shoppingCart.setUserId(BaseContext.getCurrentId());
List<ShoppingCart> shoppingCartList = shoppingCartMapper.list(shoppingCart);
//判断购物车表是否查询到了数据,查询到了说明只需要将数量加一,没有查询到则需要将菜品或者套餐数据添加到购物车表中
if (shoppingCartList != null && shoppingCartList.size() == 1) {
//有数据,数量加一即可
shoppingCart = shoppingCartList.get(0);
shoppingCart.setNumber(shoppingCart.getNumber() + 1);
shoppingCartMapper.updateNumberById(shoppingCart);
} 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 {
//为新增套餐操作
Setmeal setmeal = setmealMapper.getById(shoppingCartDTO.getSetmealId());
shoppingCart.setName(setmeal.getName());
shoppingCart.setImage(setmeal.getImage());
shoppingCart.setAmount(setmeal.getPrice());
}
shoppingCart.setNumber(1);
shoppingCart.setCreateTime(LocalDateTime.now());
//新增套餐数据
shoppingCartMapper.insert(shoppingCart);
}
}
}
创建ShoppingCartMapper接口:
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 shoppingCart
* @return
*/
List<ShoppingCart> list(ShoppingCart shoppingCart);
/**
* 更新商品数量
*
* @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, setmeal_id, dish_flavor, number, amount, image,create_time) VALUES " +
"(#{name},#{userId},#{dishId},#{setmealId},#{dishFlavor},#{number},#{amount},#{image},#{createTime})")
void insert(ShoppingCart shoppingCart);
}
创建ShoppingCartMapper.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="ShoppingCart" parameterType="ShoppingCart">
select * from 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>
order by create_time desc
</select>
</mapper>
3.功能测试
前后端联调测试即可
五.查看购物车
1.需求分析和设计
2.代码开发
在ShoppingCartController中创建查看购物车的方法:
/**
* 查看购物车
* @return
*/
@GetMapping("/list")
@ApiOperation("查看购物车")
public Result<List<ShoppingCart>> list(){
return Result.success(shoppingCartService.showShoppingCart());
}
在ShoppingCartService接口中声明查看购物车的方法:
/**
* 查看购物车
* @return
*/
List<ShoppingCart> showShoppingCart();
在ShoppingCartServiceImpl中实现查看购物车的方法:
/**
* 查看购物车
*
* @return
*/
public List<ShoppingCart> showShoppingCart() {
return shoppingCartMapper.list(ShoppingCart.builder()
.userId(BaseContext.getCurrentId())
.build());
}
3.功能测试
前后端联调测试即可
六.清空购物车
1.代码开发
在ShoppingCartController中创建清空购物车的方法:
/**
* 清空购物车
* @return
*/
@DeleteMapping("/clean")
@ApiOperation("清空购物车")
public Result<String> clean(){
shoppingCartService.cleanShoppingCart();
return Result.success();
}
在ShoppingCartService接口中声明清空购物车的方法:
/**
* 清空购物车
*/
void cleanShoppingCart();
在ShoppingCartServiceImpl中实现清空购物车的方法:
/**
* 清空购物车
*/
public void cleanShoppingCart() {
shoppingCartMapper.deleteByUserId(BaseContext.getCurrentId());
}
在ShoppingCartMapper接口中创建删除购物车数据的方法:
/**
* 清空购物车
*
* @param userId
*/
@Delete("delete from shopping_cart where user_id = #{userId}")
void deleteByUserId(Long userId);
2.功能测试
前后端联调测试即可
七.删除购物车中一个商品
1.需求分析和设计
接收前端传过来的菜品或者套餐id,根据id来进行删除操作
2.代码开发
ShoppingCartController中:
/**
* 删除购物车中一个商品
* @param shoppingCartDTO
* @return
*/
@PostMapping("/sub")
@ApiOperation("删除购物车中一个商品")
public Result<String> deleteById(@RequestBody ShoppingCartDTO shoppingCartDTO){
log.info("删除购物车中一个商品,商品:{}", shoppingCartDTO);
shoppingCartService.deleteById(shoppingCartDTO);
return Result.success();
}
ShoppingCartService中:
/**
* 删除购物车中一个商品
* @param shoppingCartDTO
*/
void deleteById(ShoppingCartDTO shoppingCartDTO);
ShoppingCartServiceImpl中:
/**
* 删除购物车中一个商品
*
* @param shoppingCartDTO
*/
public void deleteById(ShoppingCartDTO shoppingCartDTO) {
//先查询传过来的id对应的菜品或者套菜对应的数量,如果数量等于1则直接删除菜品或套菜,如果大于1则数量减一
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);
if (shoppingCart.getNumber() == 1) {
shoppingCartMapper.deleteById(shoppingCart.getId());
} else {
shoppingCart.setNumber(shoppingCart.getNumber() - 1);
shoppingCartMapper.updateNumberById(shoppingCart);
}
}
}
ShoppingCartMapper中:
/**
* 根据id删除购物车中的某个菜品或者套餐数据
*/
@Delete("delete from shopping_cart where id = #{id}")
void deleteById(Long id);
3.功能测试
前后端联调测试即可