缓存菜品
由于大量的请求如果访问数据库的不太好,所以存在redis里面减少访问数据库的需求
dish_分类的id作为key
redis的string不是java的string
redis的string可以存储不同的对象
这边应该返回一个list给redis的string
controller修改
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<DishVO>> list(Long categoryId) {
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品
List<DishVO> list = dishService.listWithFlavor(dish);
return Result.success(list);
}
需要将查询msql的代码,改成存储到redis然后查询的操作
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<DishVO>> list(Long categoryId) {
//构造redis的key 规则 dish_categoryid
String key = "dish_" + categoryId;
//查询redis里面是否已经存在了菜品
List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
if(list!=null && list.size() > 0){
return Result.success(list);
}
//没有查到 就去查sql
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);
}
清理redis(防止管理操作的时候redis没有变化)
保存的时候
@PostMapping()
public Result save(@RequestBody DishDTO dishDTO){
log.info("新增菜品:{}",dishDTO);
dishService.saveWithFlavor(dishDTO);
//清理缓存数据 只清理当前categroy里面的list
String key = "dish_" + dishDTO.getCategoryId();
redisTemplate.delete(key);
return Result.success();
}
批量删除的时候
@DeleteMapping
@ApiOperation("删除菜品")
public Result delete(@RequestParam List<Long> ids){
log.info("菜品批量删除:{}",ids);
dishService.deleteBatch(ids);
//清理redis 将所有的菜品缓存的清理掉 以dish_开头的清理掉
Set keys = redisTemplate.keys("dish_*");
redisTemplate.delete(keys);
return Result.success();
}
修改(可以会修改套餐,直接全清了)
@PutMapping
@ApiOperation("修改菜品")
public Result update(@RequestBody DishDTO dishDTO){
log.info("修改菜品:{}",dishDTO);
dishService.updateWithFlavor(dishDTO);
//修改的时候可以修改分类
//清理redis 将所有的菜品缓存的清理掉 以dish_开头的清理掉
Set keys = redisTemplate.keys("dish_*");
redisTemplate.delete(keys);
return Result.success();
}
修改菜品状态(清理一个也可以)
@PutMapping("/status/{status}")
public Result startOrStop(@PathVariable Integer status,long id){
log.info("改变菜品销售状态:{}",status);
dishService.startOrStop(status,id);
Set keys = redisTemplate.keys("dish_*");
redisTemplate.delete(keys);
return Result.success();
}
上面全部白写,定义一个private函数cleanCache
cleanCache("dish_*");
上面添加上去
private void cleanCache(String pattern){
Set keys = redisTemplate.keys(pattern);
redisTemplate.delete(keys);
}
bug
傻逼bug,因为redis里面的dish_前面有一堆乱码,所以使用dish_*就能清理内存了
缓存套餐
spring cache (大部分都在controller的方法里面添加)
在application上面加入 @EnableCaching
在Controller方法里面写@CachePut
@PostMapping
//@CachePut(cacheNames = "userCache",key = "#user.id") //如果使用spring cache缓存数据,key的生成:userCache::user.id
//@CachePut(cacheNames = "userCache",key = "#result.id")
@CachePut(cacheNames = "userCache",key = "#p0.id")
public User save(@RequestBody User user){
userMapper.insert(user);
return user;
}
也可以写result.id返回值的id属性,将返回值存入redis
p0或者a0代表的是第一个输入的参数
root.args[0]也代表的是第一个输入的参数
配置好mysql和redis之后就能正常运行了,可以去Another Redis Desktop Manager的二号数据库查看
Cacheable (先查redis查不到再去进行controller的方法)
@DeleteMapping
@Cacheable(cacheNames = "userCache",key = "#id") //生成的键就是 usercache::#id 值已经存在redis数据库
public void deleteById(Long id){
userMapper.deleteById(id);
}
CacheEvict(key指定删除特定的数据,使用allEntries=true)
@GetMapping
@CacheEvict(cacheNames = "userCache",key = "#id")
public User getById(Long id){
User user = userMapper.getById(id);
return user;
}
@DeleteMapping("/delAll")
@CacheEvict(cacheNames = "userCache",allEntries = true)
public void deleteAll(){
userMapper.deleteAll();
}
实现思路
在setmealController里面进行存入redis的操作
@GetMapping("/list")
@ApiOperation("根据分类id查询套餐")
@Cacheable(cacheNames = "setmealCache",key = "#categoryId") //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);
}
@CacheEvict(cacheNames = "setmealCache",key="#setmealDTO.categoryId")
public Result save(@RequestBody SetmealDTO setmealDTO){
@CacheEvict(cacheNames = "setmealCache",allEntries = true)
public Result delete(@RequestParam List<Long> ids){
update 起售停售 删除套餐都是删除全部
只有保存新套餐清理特定的
添加购物车
需求分析![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/cb2e6b176569499b9f28a250f82ab606.png)
一种有口味需要选择口味,一种没有口味直接添加到购物车中
void @requsetbody DTO
return success
Controller
@PostMapping("/add")
@ApiOperation("添加菜品或者套餐到购物车")
public Result add(@RequestBody ShoppingCartDTO shoppingCartDTO){
log.info("添加购物车:{}",shoppingCartDTO);
shoppingCartService.addShoppingCart(shoppingCartDTO);
return Result.success();
}
Service(根据是dish还是setmeal添加进去(使用动态sql进行查询),如果存在修改数量,如果不存在进行insert,每次查询的时候都是将DTO转化成正常的entity对象再给Mapper查询)
因为动态sql可以先判断DTO传入过来的是dish还是setmeal再进行查询数据库中有没有这样的值
每次查询的时候都是将DTO转化成正常的entity对象再给Mapper查询->
判断是否已经存在存在就加1->
不存在去查询dish和setmeal的金额等属性赋值给shoppingcart->
然后再insert到表中
public void addShoppingCart(ShoppingCartDTO shoppingCartDTO){
//DTO属性
// private Long dishId;
// private Long setmealId;
// private String dishFlavor;
//到底是菜品还是套餐 这个直接用动态sql去实现,因为动态sql里面会判断到底是不是为null如果不是null的话就进行查询
//需要先转化为entity再进行传入
ShoppingCart shoppingCart = new ShoppingCart();
BeanUtils.copyProperties(shoppingCartDTO,shoppingCart);
//通过上下文获取现在的用户id 还是Thread的知识
Long currentId = BaseContext.getCurrentId();
shoppingCart.setUserId(currentId);
List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);
//如果已经存在就将number加一 不存在就插入
if (list!=null && list.size()>0){
ShoppingCart cart = list.get(0);
cart.setNumber(cart.getNumber()+1);
shoppingCartMapper.update(cart);
}else{
//到底是菜品还是套餐
Long setmealId = shoppingCartDTO.getSetmealId();
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(setmealId);
shoppingCart.setName(setmeal.getName());
shoppingCart.setImage(setmeal.getImage());
shoppingCart.setAmount(setmeal.getPrice());
}
shoppingCart.setCreateTime(LocalDateTime.now());
shoppingCart.setNumber(1);
shoppingCartMapper.insert(shoppingCart);
}
Mapper(查询注意返回的是List)
<select id="list" resultType="com.sky.entity.ShoppingCart">
select * from shopping_cart
<where>
<if test="user_id!= 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>
测试的时候发现拦截器不能通过thread获取user_id
回去找bug,发现登录的时候生成当前用户jwt的时候,我的xml文件里的设置ttl拼写错误(存在是数据库里的是openid微信用户的唯一标识)
我们在处理是不是同一个人操作的时候,没有去访问数据库的openid来确定,而是使用了拦截器的jwt来确定ovo?
首先vx登录的时候需要返回一个jwt令牌的封装在userLoginVO之中,因为这个给拦截器校验用的,为了让后面的功能可以获取当前正在使用用户的UserId
也还行,再对jwt加深一遍理解吧
所以在管理层Employee的新增功能之中,直接使用Thread.currentThread().getId()获取jwt加密之后的id就好?其实也不是
查看购物车
清空购物车
关于拦截器部分的理解
除了登录的方法,在使用其他方法之前,拦截器验证jwt是不是对的,如果是对的就可以在Threadlocal访问当前用户的id
流程图示例
登录请求流程:
客户端 → 登录接口 → 验证凭证 → 生成JWT(登录时生成的 JWT(JSON Web Token)会保存在前端) → 返回JWT给客户端
后续请求流程:
客户端 → 带JWT的请求 → 拦截器(preHandle) → 验证JWT → 保存用户ID到 ThreadLocal → 传递请求到控制器 → 控制器处理请求 → 清理 ThreadLocal (afterCompletion)