【学习日记2023.5.26】 之 客户端之完善缓存和购物车模块

news2024/11/26 23:31:54

文章目录

  • 7. 用户端之完善缓存和购物车模块
    • 7.1 缓存菜品
      • 7.1.1 问题说明
      • 7.1.2 实现思路
      • 7.1.3 代码开发
      • 7.1.4 功能测试
      • 7.1.5 提交代码
    • 7.2 缓存套餐
      • 7.2.1 Spring Cache
        • 7.2.1.1 介绍
        • 2.1.2 常用注解
        • 7.2.1.3 入门案例
      • 7.2.2 实现思路
      • 7.2.3 代码开发
      • 7.2.4 功能测试
      • 7.2.5 提交代码
    • 7.3 添加购物车
      • 7.3.1 需求分析和设计
      • 7.3.2 代码开发
        • 7.3.2.1 DTO设计
        • 7.3.2.2 Controller层
        • 7.3.2.3 Service层接口
        • 7.3.2.4 Service层实现类
        • 7.3.2.5 Mapper层
      • 7.3.3 功能测试
      • 7.3.4 提交代码
    • 7.4 查看购物车
      • 7.4.1 需求分析和设计
      • 7.4.2 代码开发
        • Controller层
        • Service层接口
        • Service层实现类
      • 7.4.3 功能测试
      • 7.4.4 提交代码
    • 7.5 清空购物车
      • 7.5.1 需求分析和设计
      • 7.5.2 代码开发
        • Controller层
        • Service层接口
        • Service层实现类
        • Mapper层
      • 7.5.3 功能测试
      • 7.5.4 提交代码
    • 7.6 删除购物车商品
      • 7.6.1 需求分析和设计
      • 7.6.2 代码开发
        • Controller层
        • Service层接口
        • Service层实现类
        • Mapper层
      • 7.6.3 功能测试
      • 7.6.4 提交代码

7. 用户端之完善缓存和购物车模块

  • 缓存菜品
  • 缓存套餐
  • 添加购物车
  • 查看购物车
  • 清空购物车
  • 删除购物车商品

7.1 缓存菜品

7.1.1 问题说明

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

**结果:**系统响应慢、用户体验差

7.1.2 实现思路

通过Redis来缓存菜品数据,减少数据库查询操作。
请添加图片描述

缓存逻辑分析:

  • 每个分类下的菜品保存一份缓存数据
  • 数据库中菜品数据有变更时清理缓存数据
    请添加图片描述

7.1.3 代码开发

修改用户端接口 DishController 的 list 方法,加入缓存处理逻辑:

	@Autowired
    private RedisTemplate redisTemplate;
	/**
     * 根据分类id查询菜品
     *
     * @param categoryId
     * @return
     */
    @GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result<List<DishVO>> list(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);//查询起售中的菜品

        //如果不存在,查询数据库,将查询到的数据放入redis中
        list = dishService.listWithFlavor(dish);
        
        redisTemplate.opsForValue().set(key, list);

        return Result.success(list);
    }

为了保证数据库Redis中的数据保持一致,修改管理端接口 DishController 的相关方法,加入清理缓存逻辑。

需要改造的方法:

  • 新增菜品
  • 修改菜品
  • 批量删除菜品
  • 起售、停售菜品

抽取清理缓存的方法:

在管理端DishController中添加

	@Autowired
    private RedisTemplate redisTemplate;
	/**
     * 清理缓存数据
     * @param pattern
     */
    private void cleanCache(String pattern){
        Set keys = redisTemplate.keys(pattern);
        redisTemplate.delete(keys);
    }

调用清理缓存的方法,保证数据一致性:

1). 新增菜品优化

	/**
     * 新增菜品
     *
     * @param dishDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增菜品")
    public Result save(@RequestBody DishDTO dishDTO) {
        log.info("新增菜品:{}", dishDTO);
        dishService.saveWithFlavor(dishDTO);

        //清理缓存数据
        String key = "dish_" + dishDTO.getCategoryId();
        cleanCache(key);
        return Result.success();
    }

2). 菜品批量删除优化

	/**
     * 菜品批量删除
     *
     * @param ids
     * @return
     */
    @DeleteMapping
    @ApiOperation("菜品批量删除")
    public Result delete(@RequestParam List<Long> ids) {
        log.info("菜品批量删除:{}", ids);
        dishService.deleteBatch(ids);

        //将所有的菜品缓存数据清理掉,所有以dish_开头的key
        cleanCache("dish_*");

        return Result.success();
    }

3). 修改菜品优化

	/**
     * 修改菜品
     *
     * @param dishDTO
     * @return
     */
    @PutMapping
    @ApiOperation("修改菜品")
    public Result update(@RequestBody DishDTO dishDTO) {
        log.info("修改菜品:{}", dishDTO);
        dishService.updateWithFlavor(dishDTO);

        //将所有的菜品缓存数据清理掉,所有以dish_开头的key
        cleanCache("dish_*");

        return Result.success();
    }

4). 菜品起售停售优化

	/**
     * 菜品起售停售
     *
     * @param status
     * @param id
     * @return
     */
    @PostMapping("/status/{status}")
    @ApiOperation("菜品起售停售")
    public Result<String> startOrStop(@PathVariable Integer status, Long id) {
        dishService.startOrStop(status, id);

        //将所有的菜品缓存数据清理掉,所有以dish_开头的key
        cleanCache("dish_*");

        return Result.success();
    }

7.1.4 功能测试

可以通过如下方式进行测试:

  • 查看控制台sql
  • 前后端联调
  • 查看Redis中的缓存数据

加入缓存菜品修改两个功能测试为例,通过前后端联调方式,查看控制台sql的打印和Redis中的缓存数据变化。

1). 加入缓存

当第一次查询某个分类的菜品时,会从数据为中进行查询,同时将查询的结果存储到Redis中,在后绪的访问,若查询相同分类的菜品时,直接从Redis缓存中查询,不再查询数据库。

**登录小程序:**选择蜀味牛蛙(id=17)
请添加图片描述

**查看控制台sql:**有查询语句,说明是从数据库中进行查询
请添加图片描述

**查看Redis中的缓存数据:**说明缓存成功
请添加图片描述

**再次访问:**选择蜀味牛蛙(id=17)
请添加图片描述

说明是从Redis中查询的数据。

2). 菜品修改

当在后台修改菜品数据时,为了保证Redis缓存中的数据和数据库中的数据时刻保持一致,当修改后,需要清空对应的缓存数据。用户再次访问时,还是先从数据库中查询,同时再把查询的结果存储到Redis中,这样,就能保证缓存和数据库的数据保持一致。

**进入后台:**修改蜀味牛蛙分类下的任意一个菜品,当前分类的菜品数据已在Redis中缓存。
请添加图片描述

修改:
请添加图片描述

**查看Redis中的缓存数据:**说明修改时,已清空缓存
请添加图片描述

用户再次访问同一个菜品分类时,需要先查询数据库,再把结果同步到Redis中,保证了两者数据一致性。

7.1.5 提交代码

commit—>describe—>push

7.2 缓存套餐

7.2.1 Spring Cache

7.2.1.1 介绍

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>

2.1.2 常用注解

在SpringCache中提供了很多缓存操作的注解,常见的是以下的几个:

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

在spring boot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用@EnableCaching开启缓存支持即可。

例如,使用Redis作为缓存技术,只需要导入Spring data Redis的maven坐标即可。

7.2.1.3 入门案例

1). 环境准备

**导入基础工程:**底层已使用Redis缓存实现
请添加图片描述

数据库准备:

创建名为spring_cache_demo数据库,将springcachedemo.sql脚本直接导入数据库中。

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(45) DEFAULT NULL,
  `age` int DEFAULT NULL,
  PRIMARY KEY (`id`)
);

请添加图片描述

引导类上加@EnableCaching:

package com.yishooo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@Slf4j
@SpringBootApplication
@EnableCaching//开启缓存注解功能
public class CacheDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheDemoApplication.class,args);
        log.info("项目启动成功...");
    }
}

2). @CachePut注解

@CachePut 说明:

​ 作用: 将方法返回值,放入缓存

​ value: 缓存的名称, 每个缓存名称下面可以有很多key

​ key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

在save方法上加注解@CachePut

当前UserController的save方法是用来保存用户信息的,在该用户信息保存到数据库的同时,也往缓存中缓存一份数据,可以在save方法上加上注解 @CachePut,用法如下:

	/**
     * cacheNames:缓存的分类名称,代表的是某一类的缓存
     * key:某一类缓存下某歌具体缓存的唯一标识,可以通过(SpEL)表达式获取方法的信息
     * 操作方法的返回值
     * #result
     * #result.属性
     * 操作方法的参数
     * 方式一:通过参数的索引(索引从0开始)
     *      #root.args[索引]
     *      #p索引
     *      #a索引
     * 方式二:通过参数名
     *      #参数名
     */
//    @CachePut(cacheNames = "userCache",key = "#result.id")
//    @CachePut(cacheNames = "userCache",key = "#root.args[0].id")
//    @CachePut(cacheNames = "userCache",key = "#p0.id")
//    @CachePut(cacheNames = "userCache",key = "#a0.id")
 	@CachePut(value = "userCache", key = "#user.id")//key的生成:
	@PostMapping
   userCache::1
    public User save(@RequestBody User user){
        userMapper.insert(user);
        return user;
    }

**说明:**key的写法如下

#user.id : #user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key ;

#result.id : #result代表方法返回值,该表达式 代表以返回对象的id属性作为key ;

#p0.id:#p0指的是方法中的第一个参数,id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key ;

#a0.id:#a0指的是方法中的第一个参数,id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key ;

#root.args[0].id:#root.args[0]指的是方法中的第一个参数,id指的是第一个参数的id属性,也就是使用第一个参数

的id属性作为key ;

启动服务,通过swagger接口文档测试,访问UserController的save()方法

因为id是自增,所以不需要设置id属性
请添加图片描述

查看user表中的数据
请添加图片描述

3). @Cacheable注解

@Cacheable 说明:

​ 作用: 在方法执行前,spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中

​ value: 缓存的名称,每个缓存名称下面可以有多个key

​ key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

在getById上加注解@Cacheable

	/**
	* Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,	  *调用方法并将方法返回值放到缓存中
	* value:缓存的名称,每个缓存名称下面可以有多个key
	* key:缓存的key
	*/
	@GetMapping
    @Cacheable(cacheNames = "userCache",key="#id")
    public User getById(Long id){
        User user = userMapper.getById(id);
        return user;
    }

重启服务,通过swagger接口文档测试,访问UserController的getById()方法

第一次访问,会请求controller的方法,查询数据库。后面再查询相同的id,就直接从Redis中查询数据,不用再查询数据库了,就说明缓存生效了。

提前在redis中手动删除掉id=1的用户数据
请添加图片描述

**查看控制台sql语句:**说明从数据库查询的用户数据
请添加图片描述

**查看Redis中的缓存数据:**说明已成功缓存
请添加图片描述

再次查询相同id的数据时,直接从redis中直接获取,不再查询数据库。

4). @CacheEvict注解

@CacheEvict 说明:

​ 作用: 清理指定缓存

​ value: 缓存的名称,每个缓存名称下面可以有多个key

​ key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

在 delete 方法上加注解@CacheEvict

	@DeleteMapping
    @CacheEvict(cacheNames = "userCache",key = "#id")//删除某个key对应的缓存数据
    public void deleteById(Long id){
        userMapper.deleteById(id);
    }

	@DeleteMapping("/delAll")
    @CacheEvict(cacheNames = "userCache",allEntries = true)//删除userCache下所有的缓存数据
    public void deleteAll(){
        userMapper.deleteAll();
    }

重启服务,通过swagger接口文档测试,访问UserController的deleteAll()方法
请添加图片描述

**查看user表:**数据清空
请添加图片描述

查询Redis缓存数据
请添加图片描述

7.2.2 实现思路

实现步骤:

1). 导入Spring Cache和Redis相关maven坐标

2). 在启动类上加入@EnableCaching注解,开启缓存注解功能

3). 在用户端接口SetmealController的 list 方法上加入@Cacheable注解

4). 在管理端接口SetmealController的 save、delete、update、startOrStop等方法上加入CacheEvict注解

7.2.3 代码开发

按照上述实现步骤:

1). 导入Spring Cache和Redis相关maven坐标(已实现)

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

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

2). 在启动类上加入@EnableCaching注解,开启缓存注解功能

package com.sky;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@Slf4j
@EnableCaching
public class SkyApplication {
    public static void main(String[] args) {
        SpringApplication.run(SkyApplication.class, args);
        log.info("server started");
    }
}

3). 在用户端接口SetmealController的 list 方法上加入@Cacheable注解

	/**
     * 条件查询
     *
     * @param categoryId
     * @return
     */
    @GetMapping("/list")
    @ApiOperation("根据分类id查询套餐")
    @Cacheable(cacheNames = "setmealCache",key = "#categoryId") //key: setmealCache::100
    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);
    }

4). 在管理端接口SetmealController的 save、delete、update、startOrStop等方法上加入CacheEvict注解

	/**
     * 新增套餐
     *
     * @param setmealDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增套餐")
    @CacheEvict(cacheNames = "setmealCache",key = "#setmealDTO.categoryId")//key: setmealCache::100
    public Result save(@RequestBody SetmealDTO setmealDTO) {
        setmealService.saveWithDish(setmealDTO);
        return Result.success();
    }
	/**
     * 批量删除套餐
     *
     * @param ids
     * @return
     */
    @DeleteMapping
    @ApiOperation("批量删除套餐")
    @CacheEvict(cacheNames = "setmealCache",allEntries = true)
    public Result delete(@RequestParam List<Long> ids) {
        setmealService.deleteBatch(ids);
        return Result.success();
    }
	/**
     * 修改套餐
     *
     * @param setmealDTO
     * @return
     */
    @PutMapping
    @ApiOperation("修改套餐")
    @CacheEvict(cacheNames = "setmealCache",allEntries = true)
    public Result update(@RequestBody SetmealDTO setmealDTO) {
        setmealService.update(setmealDTO);
        return Result.success();
    }

    /**
     * 套餐起售停售
     *
     * @param status
     * @param id
     * @return
     */
    @PostMapping("/status/{status}")
    @ApiOperation("套餐起售停售")
    @CacheEvict(cacheNames = "setmealCache",allEntries = true)
    public Result startOrStop(@PathVariable Integer status, Long id) {
        setmealService.startOrStop(status, id);
        return Result.success();
    }

7.2.4 功能测试

通过前后端联调方式来进行测试,同时观察redis中缓存的套餐数据。和缓存菜品功能测试基本一致,不再赘述。

7.2.5 提交代码

commit—>describe—>push

7.3 添加购物车

7.3.1 需求分析和设计

产品原型

用户可以将菜品或者套餐添加到购物车。对于菜品来说,如果设置了口味信息,则需要选择规格后才能加入购物车;对于套餐来说,可以直接点击➕将当前套餐加入购物车。在购物车中可以修改菜品和套餐的数量,也可以清空购物车。

效果图:
请添加图片描述

接口设计

通过上述原型图,设计出对应的添加购物车接口。
请添加图片描述

**说明:**添加购物车时,有可能添加菜品,也有可能添加套餐。故传入参数要么是菜品id,要么是套餐id。

表设计

用户的购物车数据,也是需要保存在数据库中的,购物车对应的数据表为shopping_cart表,具体表结构如下:

字段名数据类型说明备注
idbigint主键自增
namevarchar(32)商品名称冗余字段
imagevarchar(255)商品图片路径冗余字段
user_idbigint用户id逻辑外键
dish_idbigint菜品id逻辑外键
setmeal_idbigint套餐id逻辑外键
dish_flavorvarchar(50)菜品口味
numberint商品数量
amountdecimal(10,2)商品单价冗余字段
create_timedatetime创建时间

说明:

  • 购物车数据是关联用户的,在表结构中,我们需要记录,每一个用户的购物车数据是哪些
  • 菜品列表展示出来的既有套餐,又有菜品,如果用户选择的是套餐,就保存套餐ID(setmeal_id),如果用户选择的是菜品,就保存菜品ID(dish_id)
  • 对同一个菜品/套餐,如果选择多份不需要添加多条记录,增加数量number即可

7.3.2 代码开发

7.3.2.1 DTO设计

根据添加购物车接口的参数设计DTO:
请添加图片描述

在sky-pojo模块,ShoppingCartDTO.java已定义

package com.sky.dto;

import lombok.Data;
import java.io.Serializable;

@Data
public class ShoppingCartDTO implements Serializable {

    private Long dishId;
    private Long setmealId;
    private String dishFlavor;

}

7.3.2.2 Controller层

根据添加购物车接口创建ShoppingCartController:

package com.sky.controller.user;


import com.sky.dto.ShoppingCartDTO;
import com.sky.result.Result;
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);
        shoppingCartService.addShoppingCart(shoppingCartDTO);//后绪步骤实现
        return Result.success();
    }
}

7.3.2.3 Service层接口

创建ShoppingCartService接口:

package com.sky.service;

import com.sky.dto.ShoppingCartDTO;
import com.sky.entity.ShoppingCart;
import java.util.List;

public interface ShoppingCartService {

    /**
     * 添加购物车
     * @param shoppingCartDTO
     */
    void addShoppingCart(ShoppingCartDTO shoppingCartDTO);
}

7.3.2.4 Service层实现类

创建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.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) {
            //如果已经存在,就更新数量,数量加1
            shoppingCart = shoppingCartList.get(0);
            shoppingCart.setNumber(shoppingCart.getNumber() + 1);
            shoppingCartMapper.updateNumberById(shoppingCart);
        } else {
            //如果不存在,插入数据,数量就是1

            //判断当前添加到购物车的是菜品还是套餐
            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);
        }
    }
}

7.3.2.5 Mapper层

创建ShoppingCartMapper接口:

package com.sky.mapper;

import com.sky.entity.ShoppingCart;
import org.apache.ibatis.annotations.Delete;
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" parameterType="ShoppingCart" resultType="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>

7.3.3 功能测试

进入小程序,添加菜品
请添加图片描述

加入购物车,查询数据库
请添加图片描述

因为现在没有实现查看购物车功能,所以只能在表中进行查看。

在前后联调时,后台可通断点方式启动,查看运行的每一步。

7.3.4 提交代码

commit—>describe—>push

7.4 查看购物车

7.4.1 需求分析和设计

产品原型

当用户添加完菜品和套餐后,可进入到购物车中,查看购物中的菜品和套餐。
请添加图片描述

接口设计
请添加图片描述

7.4.2 代码开发

Controller层

在ShoppingCartController中创建查看购物车的方法:

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

Service层接口

在ShoppingCartService接口中声明查看购物车的方法:

	/**
     * 查看购物车
     * @return
     */
    List<ShoppingCart> showShoppingCart();

Service层实现类

在ShoppingCartServiceImpl中实现查看购物车的方法:

	/**
     * 查看购物车
     * @return
     */
    public List<ShoppingCart> showShoppingCart() {
        return shoppingCartMapper.list(ShoppingCart.builder().userId(BaseContext.getCurrentId()).build());
    }

7.4.3 功能测试

当进入小程序时,就会发起查看购物车的请求
请添加图片描述

点击购物车图标
请添加图片描述

测试成功。

7.4.4 提交代码

commit—>describe—>push

7.5 清空购物车

7.5.1 需求分析和设计

产品原型

当点击清空按钮时,会把购物车中的数据全部清空。
请添加图片描述

接口设计
请添加图片描述

7.5.2 代码开发

Controller层

在ShoppingCartController中创建清空购物车的方法:

	/**
     * 清空购物车商品
     * @return
     */
    @DeleteMapping("/clean")
    @ApiOperation("清空购物车商品")
    public Result<String> clean(){
        shoppingCartService.cleanShoppingCart();
        return Result.success();
    }

Service层接口

在ShoppingCartService接口中声明清空购物车的方法:

	/**
     * 清空购物车商品
     */
    void cleanShoppingCart();

Service层实现类

在ShoppingCartServiceImpl中实现清空购物车的方法:

	/**
     * 清空购物车商品
     */
    public void cleanShoppingCart() {
        shoppingCartMapper.deleteByUserId(BaseContext.getCurrentId());
    }

Mapper层

在ShoppingCartMapper接口中创建删除购物车数据的方法:

	/**
     * 根据用户id删除购物车数据
     *
     * @param userId
     */
    @Delete("delete from shopping_cart where user_id = #{userId}")
    void deleteByUserId(Long userId);

7.5.3 功能测试

进入到购物车页面
请添加图片描述

点击清空
请添加图片描述

查看数据库中的数据
请添加图片描述

说明当前用户的购物车数据已全部删除。

7.5.4 提交代码

commit—>describe—>push

7.6 删除购物车商品

7.6.1 需求分析和设计

产品原型

当点击➖按钮时,会把购物车中的数据数量减1,如果数量为1就会删除购物车的此条信息。
请添加图片描述

接口设计
请添加图片描述

7.6.2 代码开发

Controller层

在ShoppingCartController中创建删除购物车中的一个商品的方法:

	/**
     * 删除购物车中的一个商品
     * @param shoppingCartDTO
     * @return
     */
    @ApiOperation("删除购物车中的一个商品")
    @PostMapping("/sub")
    public Result sub(@RequestBody ShoppingCartDTO shoppingCartDTO){
        log.info("删除购物车中的商品为:{}",shoppingCartDTO);
        shoppingCartService.subShoppingCart(shoppingCartDTO);
        return Result.success();
    }

Service层接口

在ShoppingCartService接口中声明减少购物车数量的方法:

	/**
     * 减少购物车数量
     * @param shoppingCartDTO
     */
    void subShoppingCart(ShoppingCartDTO shoppingCartDTO);

Service层实现类

在ShoppingCartServiceImpl中实现减少购物车数量的方法:

	/**
     * 减少购物车数量
     * @param shoppingCartDTO
     */
    @Override
    public void subShoppingCart(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){//list可不判断,肯定有值
            shoppingCart = list.get(0);
            //判断当前购物车数量值是否为1,为1则删除此条购物数据,否则数量减1
            if (1 == shoppingCart.getNumber()){
                shoppingCartMapper.deleteShoppingCartByid(shoppingCart.getId());
                return;
            }
            //数量减1
            shoppingCart.setNumber(shoppingCart.getNumber()-1);
            shoppingCartMapper.updateNumberById(shoppingCart);
        }
    }

Mapper层

在ShoppingCartMapper接口中创建删除购物车数据的方法:

	/**
     * 根据id删除购物车数据
     * @param id
     */
    @Delete("delete from shopping_cart where id = #{id}")
    void deleteShoppingCartByid(Long id);

7.6.3 功能测试

请添加图片描述

测试成功。

7.6.4 提交代码

commit—>describe—>push

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

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

相关文章

14-C++面向对象(单例模式、const成员、浅拷贝、深拷贝)

单例模式 单例模式&#xff1a;设计模式的一种&#xff0c;保证某个类永远只创建一个对象 构造函数\析构函数 私有化 定义一个私有的static成员变量指向唯一的那个单例对象&#xff08;Rocket* m_rocket&#xff09; 提供一个公共的访问单例对象的接口&#xff0…

[组合数学] 容斥原理polya定理

数学 A 170 物理 B130 化学C120 A ∩ B 45 A ∩ C 20 B ∩ C 22 A ∩ B ∩ C 3 A\cap B 45\quad A\cap C20 \quad B\cap C 22 \quad A\cap B\cap C 3 A∩B45A∩C20B∩C22A∩B∩C3 ∣ A ∪ B ∪ C ∣ A B C − A ∩ B − B ∩ C − A ∩ C A ∩ B ∩ C 170 130 …

vcruntime140.dll丢失怎么办?怎么解决vcruntime140.dll丢失的问题

当您运行一个需要此文件的程序时&#xff0c;如果您的系统中不存在这个文件&#xff0c;会提示出错信息“找不到vcruntime140.dll”或“vcruntime140.dll丢失”。这种情况下&#xff0c;您需要解决这个问题&#xff0c;才能继续运行此应用程序。我们将介绍vcruntime140.dll丢失…

python基于协同过滤推荐算法的电影观后感推荐管理系统的设计

本课题所设计的影单管理系统&#xff0c;使用B/S架构&#xff0c;Python语言进行开发&#xff0c;它的优点代码不能从浏览器查看&#xff0c;保密性非常好&#xff0c;比其他的影单管理更具安全性。Python还容易修改和调试&#xff0c;毕竟影视是在不断发展过程中&#xff0c;难…

【P34】JMeter ForEach控制器(ForEach Controller)

文章目录 一、ForEach控制器&#xff08;ForEach Controller&#xff09;参数说明二、准备工作三、测试计划设计 一、ForEach控制器&#xff08;ForEach Controller&#xff09;参数说明 可以对一个组变量进行循环迭代&#xff1b;该组件通常与后置处理器中的 JSON 提取器、正…

自动化测试工具——Selenium详解

前言 Selenium是一个用于Web应用程序测试的工具。是一个开源的Web的自动化测试工具&#xff0c;最初是为网站自动化测试而开发的&#xff0c;类型像我们玩游戏用的按键精灵&#xff0c;可以按指定的命令自动操作&#xff0c;不同是Selenium可以直接运行在浏览器上&#xff0c;…

AIBlockChain:“知名博主独家讲授”人工智能创新应用竞赛【精选实战作品】之《基于计算机视觉、自然语言处理、区块链和爬虫技术的智能会议系统》软件系统案例的界面简介、功能介绍分享之二、会中智能

AI&BlockChain&#xff1a;“知名博主独家讲授”人工智能创新应用竞赛【精选实战作品】之《基于计算机视觉、自然语言处理、区块链和爬虫技术的智能会议系统》软件系统案例的界面简介、功能介绍分享之二、会中智能系统 目录 人工智能竞赛【精选实战作品】之《基于计算机视…

【网络编程一】初识网络:IP与端口号 网络模型

目录 &#x1f31f;需要知道 一、基础概念 &#x1f308;1、IP地址与端口号 &#x1f308;2、五元组 二、协议分层 &#x1f308;1、OSI七层网络网络模型 &#x1f308;2、TCP/IP五层(四层)模型 &#x1f308;3、封装和分用&#xff08;重点&#xff01;&#xff09; &…

Kotlin 协程中的并发问题:我明明用 mutex 上锁了,为什么没有用?

前言 最近在接手的某项目中&#xff0c;主管给我发来了一个遗留以久的 BUG&#xff0c;让我看看排查一下&#xff0c;把它修复了。 项目的问题大概是在某项业务中&#xff0c;需要向数据库插入数据&#xff0c;而且需要保证同种类型的数据只被插入一次&#xff0c;但是现在却…

每日一练 | 网络工程师软考真题 Day13

阅读以下说明&#xff0c;回答以下问题1至问题6。 【说明】 某公司的两个部门均采用Windows 2003的NAT功能共享宽带连接访问Internet&#xff0c;其网络结构和相关参数如图2-1所示。ISP为该公司分配的公网IP地址段为202.117.12.32/29。 【问题1】在Windows 2003中&#xff0c;…

NodeJS Crypto加密⑤

文章目录 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&#x1f618;前言NODE内置模块Zlib模块 createGzip&#xff08;CreateGzip方法 异步&#xff09;Crypto加密模块 getHashes 方法 createHash 方法 createHmac 方法…

野火RA6M5开发板 DHT11温湿度传感器 OLED显示测试学习

野火RA6M5开发板 DHT11温湿度传感器 OLED显示测试学习 DHT11温湿度传感器 DHT11是一款有已校准数字信号输出的温湿度传感器。 精度湿度5%RH&#xff0c; 温度2℃&#xff0c;量程湿度20-90%RH&#xff0c; 温度0~50℃。 更多DHT11信息请参考&#xff1a;https://baike.sogou…

img[:, :, ::-1] 通俗理解

&#x1f468;‍&#x1f4bb;个人简介&#xff1a; 深度学习图像领域工作者 &#x1f389;工作总结链接&#xff1a;https://blog.csdn.net/qq_28949847/article/details/128552785 链接中主要是个人工作的总结&#xff0c;每个链接都是一些常用demo&#xff0c…

Redis5.0+ Redis集群水平扩容|节点删除

文章目录 1. 背景2. Redis集群水平扩展2.1 启动集群2.2 连接其中一个节点2.3 配置新节点2.4 启动新节点2.5 配置8007节点加入集群&#xff0c;成为主节点2.6 增加子节点2.7 分配hash槽2.8 检查 Redis 集群 3. 节点删除4. redis集群命令帮助5. 参考文献 1. 背景 之前搭建了Redi…

硬件系统工程师宝典(26)-----四种BJT应用电路分析

各位同学大家好&#xff0c;欢迎继续做客电子工程学习圈&#xff0c;今天我们继续来讲这本书&#xff0c;硬件系统工程师宝典。上篇我们说到晶体三极管&#xff08;BJT&#xff09;的几种应用电路&#xff1a;三极管的输入端钳制电压电路、BJT电平转换电路、Boost升压电路、图腾…

WGCNA | 不止一个组的WGCNA怎么分析嘞!?~(二)(共识网络分析-第二步-构建网络与模块-Blockwise)

1写在前面 临床工作越来越忙了&#xff0c;更新的频率也开始降低了&#xff0c;希望各位小伙伴可以理解一下。&#x1f62d; 最近看到塞尔达-王国之泪发售了&#xff0c;真的是想买一个&#xff0c;但想想根本没有时间去玩&#xff0c;买回来只能吃灰。&#x1f972; 想问问小伙…

G0第22章 :获取参数、文件上传、重定向、Gin路由、Gin中间件、运行多个服务

05 获取参数 1、获取querystring参数 querystring 指的是URL中的 ? 后面携带的参数&#xff0c;例如&#xff1a; /user/search?username小王子&address天津 。获取请求querystring参数的方法如下&#xff1a; package mainimport ("github.com/gin-gonic/gin&qu…

Git基础笔记(只含常用命令)

1.Git基本概念 1.git三区 工作区 -->git add 暂存区 --> git commit版本区域 暂存区用来存放临时文件&#xff0c;相当于只编辑没有保存 2.文件状态 命令 git status 查看文件状态 大体来说有被版本管理和没被版本管理两种状态 a.txt 执行了git add 3.git本地仓库命令 g…

面试篇:SpringCloud

一、SpringCloud常见的组件有什么&#xff1f; 1、常见微服务功能架构图 2、阿里巴巴SpringCloud常用组件 注册中心/配置中心&#xff1a;Nacos负载均衡&#xff1a;Ribbon服务调用&#xff1a;Feign服务保护&#xff1a;Sentinel服务网关&#xff1a;Gateway 二、服务注册…

阿里云IoT物联网平台

IoT物联网平台 一、工作原理 1、MQTT&#xff08;Message Queuing Telemetry Transport&#xff09; 是一种轻量级的消息传输协议&#xff0c;专门设计用于物联网&#xff08;IoT&#xff09;应用中的通信。它是一种发布/订阅&#xff08;publish/subscribe&#xff09;模式的…