云计算百科
云计算领域专业知识百科平台

苍穹外卖-缓存商品、购物车

目录

缓存菜品

问题说明

实现思路

代码开发

功能测试

缓存套餐

Spring Cache

实现思路

代码开发

功能测试

添加购物车

需求分析与设计

产品原型

接口设计

数据库设计

代码开发

功能测试

查看购物车

需求分析和设计

产品原型

接口设计

代码开发

功能测试

清空购物车

需求分析和设计

产品原型

接口设计

代码开发

功能测试


缓存菜品

问题说明

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

实现思路

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

缓存逻辑分析:

  • 每个分类下的菜品保存一份缓存数据

  • 数据库中菜品数据有变更时清理缓存数据

代码开发

controller层改造缓存菜品代码:用户端查询菜品数据时,若Redis中已存在菜品数据则直接返回,若不存在则从数据库查询,并且缓存到Redis中。

/**
* 根据分类id查询菜品
*
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<DishVO>> list(@RequestParam Long categoryId) {
//构造redis中的key,规则:dish_分类id
String key = "dish_" + categoryId;
//查询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);//查询起售中的菜品

//不存在,根据分类id查询数据库,将查询到的菜品数据缓存到redis中
list = dishService.listWithFlavor(dish);
redisTemplate.opsForValue().set(key, list);
return Result.success(list);
}

其次,在管理端对菜品数据进行修改、删除和起售停售时,需要清理缓存数据。因为管理端直接操作数据库,而用户端查询菜品数据时命中缓存就直接返回,这样会导致数据不一致。

Set keys = redisTemplate.keys("dish_*");
redisTemplate.delete(keys);

功能测试

通过前后端、小程序联调测试。

缓存套餐

Spring Cache

Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。 SpringCache提供了一层抽象,底层可以切换不同的缓存实现,例如:

  • EHCache
  • Caffeine
  • Redis

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

常用注解:

注解 说明
@EnableCaching 开启缓存注解功能,通常加在启动类上
@Cacheable 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中(方法执行之前)
@CachePut 将方法的返回值放到缓存中(方法执行之后)
@CacheEvict 将一条或多条数据从缓存中删除(先删除数据库、再删除缓存)

实现思路

具体的实现思路如下:

  • 导入Spring Cache和Redis相关maven坐标
  • 在启动类上加入@EnableCaching注解,开启缓存注解功能
  • 在用户端接口SetmealController的list方法上加入@Cacheable注解
  • 在管理端接口SetmealController的save、 delete、update、 startOrStop等方法上加入CacheEvict注解

代码开发

按照实现思路添加即可。

功能测试

前后端联调测试、与小程序联调测试即可。

添加购物车

需求分析与设计

产品原型

接口设计

  • 请求方式:POST
  • 请求路径:/user/shoppingCart/add
  • 请求参数:套餐id、菜品id、口味
  • 返回结果:code、data、msg

数据库设计

  • 作用:暂时存放所选商品的地方
  • 选的什么商品
  • 每个商品都买了几个
  • 不同用户的购物车需要区分开

代码开发

controller层:

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")
@Api(tags = "C端-购物车接口")
@Slf4j
public class ShoppingCartController {

@Autowired
private ShoppingCartService shoppingCartService;
/**
* 添加购物车
* @return
*/
@PostMapping("/add")
@ApiOperation("添加购物车")
public Result add(@RequestBody ShoppingCartDTO shoppingCartDTO) {
log.info("添加购物车:{}", shoppingCartDTO);
shoppingCartService.add(shoppingCartDTO);
return null;
}

}

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.SetmealDish;
import com.sky.entity.ShoppingCart;
import com.sky.mapper.DishMapper;
import com.sky.mapper.SetmealDishMapper;
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 SetmealDishMapper setmealDishMapper;

/**
* 添加购物车
* @param shoppingCartDTO
*/
public void add(ShoppingCartDTO shoppingCartDTO) {
//判断当前购物车已经存在
ShoppingCart shoppingCart = new ShoppingCart();
BeanUtils.copyProperties(shoppingCartDTO, shoppingCart);
//在小程序传过来的数据中,没有userId,需要手动设置
//每次用户发起请求会携带一个token,拦截器会获取这个token,获取当前登录用户的ID
shoppingCart.setUserId(BaseContext.getCurrentId());
List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);
//若已经存在,只需数量加1
if(list != null && list.size() > 0){
//为什么这样设置数据?
ShoppingCart cart = list.get(0);
cart.setNumber(cart.getNumber() + 1);
shoppingCartMapper.updateNumberById(cart);
}
//若不存在,则添加到购物车,数量默认为1
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 = setmealDishMapper.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层:

<insert id="insert">
insert into shopping_cart (user_id, dish_id, setmeal_id, dish_flavor, number, create_time, amount, image, name)
values (#{userId}, #{dishId}, #{setmealId}, #{dishFlavor}, #{number}, #{createTime}, #{amount}, #{image}, #{name})
</insert>

功能测试

测试添加购物车,同一菜品不同口味以及套餐。

查看购物车

需求分析和设计

产品原型

接口设计

代码开发

controller:

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

service:

/**
* 查看购物车
* @return
*/
public List<ShoppingCart> list() {
//获得当前用户id
Long userId = BaseContext.getCurrentId();
ShoppingCart shoppingCart = new ShoppingCart();
shoppingCart.setUserId(userId);
List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);
return list;
}

Mapper:

<select id="list" resultType="com.sky.entity.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>
</select>

功能测试

前后端联调测试

清空购物车

需求分析和设计

产品原型

接口设计

代码开发

可根据接口设计来开发,可以锻炼一下。

功能测试

前后端联调测试即可。

赞(0)
未经允许不得转载:网硕互联帮助中心 » 苍穹外卖-缓存商品、购物车
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!