微信登录功能实现

采用HTTPS 调用

1
GET https://api.weixin.qq.com/sns/jscode2session 

请求参数

属性 类型 必填 说明
appid string 小程序 appId
secret string 小程序 appSecret
js_code string 登录时获取的 code,可通过wx.login获取
grant_type string 授权类型,此处只需填写 authorization_code

返回参数

属性 类型 说明
session_key string 会话密钥
unionid string 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明
errmsg string 错误信息,请求失败时返回
openid string 用户唯一标识
errcode int32 错误码,请求失败时返回

功能测试

第一二项参数为小程序自带,第三项参数为每次调用时的唯一code,第四项参数为固定值。

image-20250506204258027

可以获得唯一的用户标识openid,查看我们的开发文档:

image-20250506205308726尝试为用户写登录功能。

代码实现

首先在配置文件写用户的令牌生成配置信息。

image-20250506204714743

写用户的DTO和VO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.sky.dto;

import lombok.Data;

import java.io.Serializable;

/**
* C端用户登录
*/
@Data
public class UserLoginDTO implements Serializable {

private String code;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.sky.vo;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserLoginVO implements Serializable {

private Long id;
private String openid;
private String token;

}

继续三件套,controller->service->mapper,先写controller层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import ...//省略

@RestController
@RequestMapping("/user/user")
@Api(tags = "c端用户相关接口")
@Slf4j
public class UserController {

@Autowired
private UserService userService;
@Autowired
private JwtProperties jwtProperties;

/**
* 微信登录
* @param userLoginDTO
* @return
*/
@PostMapping("/login")
@ApiOperation("微信登录")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO) {
log.info("微信用户登录:{}", userLoginDTO.getCode());
User user = userService.wxLogin(userLoginDTO);

//为微信用户生成jwt令牌
Map<String, Object> claims = new HashMap<>();
claims.put(JwtClaimsConstant.USER_ID, user.getId());//键值对
String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),claims);

UserLoginVO userLoginVO = UserLoginVO.builder()
.id(user.getId())
.openid(user.getOpenid())
.token(token)
.build();
return Result.success(userLoginVO);
}
}

写service实现层

1
2
3
4
5
6
7
8
9
10
11
package com.sky.service;

import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;

public interface UserService {


User wxLogin(UserLoginDTO userLoginDTO);

}

它的实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@Service
@Slf4j
public class UserServiceImpl implements UserService {

public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";

@Autowired
private WeChatProperties properties;
@Autowired
private UserMapper userMapper;
/**
* 微信登录
* @param userLoginDTO
* @return
*/
public User wxLogin(UserLoginDTO userLoginDTO) {
String openid = getOpenid(userLoginDTO.getCode());

//判断openid是否为空,如果为空表示登录失败,抛出业务异常
if (openid == null) {
throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
}
//判断openid是否已存在,存在则为老用户,否则为新用户
User user = userMapper.getByOpenid(openid);
//如果是新用户自动完成注册
if (user == null) {
user = User.builder()
.openid(openid)
.createTime(LocalDateTime.now())
.build();
userMapper.insert(user);
}
//返回用户对象
return user;
}

/**
* 调用微信接口服务获得微信用户的openid
* @param code
* @return
*/
private String getOpenid(String code) {
//调用微信接口服务获得当前微信用户的openid
Map<String, String> map = new HashMap<>();
map.put("appid", properties.getAppid());
map.put("secret", properties.getSecret());
map.put("js_code", code);
map.put("grant_type", "authorization_code");
String json = HttpClientUtil.doGet(WX_LOGIN,map);

JSONObject jsonObject = JSON.parseObject(json);
String openid = jsonObject.getString("openid");
return openid;
}
}

mapper层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.sky.mapper;

import com.sky.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserMapper {

/**
* 根据openid查询用户
* @param openid
* @return
*/
@Select("select * from user where openid = #{openid}")
User getByOpenid(String openid);

/**
* 插入数据
* @param user
*/
void insert(User user);
}
1
2
3
4
5
6
7
8
9
10
11
<?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.UserMapper">

<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into user (openid, name, phone, sex, id_number, avatar, create_time)
values (#{openid},#{name},#{phone},#{sex},#{idNumber},#{avatar},#{createTime})
</insert>

</mapper>

继续完善令牌校验,登录之后保存的token值,此代码没有手搓,是复制原有的管理员令牌改写的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenUserInterceptor implements HandlerInterceptor {

@Autowired
private JwtProperties jwtProperties;

/**
* 校验jwt
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断当前拦截到的是Controller的方法还是其他资源
if (!(handler instanceof HandlerMethod)) {
//当前拦截到的不是动态方法,直接放行
return true;
}

//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getUserTokenName());

//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
log.info("当前用户id:", userId);

BaseContext.setCurrentId(userId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}
}

同时需要去配置类里也注入此bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport{

@Autowired
private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;
@Autowired
private JwtTokenUserInterceptor jwtTokenUserInterceptor;

/**
* 注册自定义拦截器
*
* @param registry
*/
protected void addInterceptors(InterceptorRegistry registry) {
log.info("开始注册自定义拦截器...");
registry.addInterceptor(jwtTokenAdminInterceptor)
.addPathPatterns("/admin/**")
.excludePathPatterns("/admin/employee/login");

registry.addInterceptor(jwtTokenUserInterceptor)
.addPathPatterns("/user/**")
.excludePathPatterns("/user/user/login")
.excludePathPatterns("/user/shop/status");
}

/**
* 通过knife4j生成接口文档
* @return
*/
@Bean
public Docket docket1() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("管理端接口")
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin"))
.paths(PathSelectors.any())
.build();
return docket;
}

@Bean
public Docket docket2() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("用户端接口")
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.user"))
.paths(PathSelectors.any())
.build();
return docket;
}

private static String FILELOCATION = "C:\\Users\\zhangbin\\Desktop\\sky-take-out\\sky-server\\src\\main\\resources\\uploads/";
/**
* 设置静态资源映射
* @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");

// 将本地文件系统的 uploads 目录映射为 /uploads/** 的 URL
registry.addResourceHandler("/uploads/**")
.addResourceLocations("file:" + FILELOCATION);
}

/**
* 扩展spring MVC消息转换器
* @param converters
*/
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器...");
//创建一个消息转换器对象
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//需要为消息转换器设置一个对象转换器,对象转换器可以将java对象序列化为json数据
converter.setObjectMapper(new JacksonObjectMapper());
//将自己的消息转换器加入容器中
converters.add(0,converter);//索引为0,优先使用我们自己的消息转换器
}
}

菜品缓存

问题:用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大,从而导致系统响应慢、用户体验差。

解决思路:通过redis缓存查到的数据,如果数据没有发生改变,直接显示上次缓存的数据而不是查数据库

springcache分析

代码实现:

  • 修改用户端接口 DishController 的 list 方法,加入缓存处理逻辑:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@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.isEmpty()) {
//如果存在,直接返回,无需查询数据库
return Result.success(list);
}

//如果不存在,查询数据库
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);
}
  • 修改管理端接口 DishController 的相关方法,加入清理缓存的逻辑,需要改造以下方法:新增菜品、修改菜品、批量删除菜品和起售、停售菜品。

    • 抽取清理缓存的方法:
    1
    2
    3
    4
    private void cleanCache(String pattern) {
    Set<String> keys = redisTemplate.keys(pattern);
    redisTemplate.delete(keys);
    }
    • 调用清理缓存的方法,保证数据一致性:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @PostMapping
    @ApiOperation("新增菜品")
    public Result save(@RequestBody DishDTO dishDTO) {
    log.info("新增菜品:{}", dishDTO);
    dishService.saveWithFlavor(dishDTO);

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

    //其他方法内添加以下代码
    //将所有的菜品缓存数据清理掉,即所有以dish_开头的key
    cleanCache("dish_*");

功能测试

  • 可以通过如下方式进行测试:查看控制台sql、前后端联调、查看Redis中的缓存数据。

套餐缓存

Spring Cache

  • Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。
  • Spring Cache 提供了一层抽象,底层可以切换不同的缓存实现,例如:EHCache、Caffeine和Redis。
  • maven坐标为
1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.7.3</version>
</dependency>

常用注解

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

实现思路

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

代码实现

  • 在用户端接口SetmealController的 list 方法上加入@Cacheable注解:
1
2
3
4
5
6
7
8
9
10
11
@GetMapping("/list")
@ApiOperation("根据分类id查询套餐")
@Cacheable(cacheNames = "setmealCache", key = "#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);
}
  • 在管理端接口SetmealController的 save、delete、update、startOrStop等方法上加入CacheEvict注解:
1
2
3
@CacheEvict(cacheNames = "setmealCache", key = "#setmealDTO.categoryId") //save上

@CacheEvict(cacheNames = "setmealCache", allEntries = true) //其他方法上

功能测试

  • 通过前后端联调方式来进行测试,同时观察redis中缓存的套餐数据。

添加购物车

需求分析和设计

产品原型

img

接口设计

img

数据库设计

shopping_cart购物车表
字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 商品名称 冗余字段
image varchar(255) 商品图片路径 冗余字段
user_id bigint 用户id 逻辑外键
dish_id bigint 菜品id 逻辑外键
setmeal_id bigint 套餐id 逻辑外键
dish_flavor varchar(50) 菜品口味
number int 商品数量
amount decimal(10,2) 商品单价 冗余字段
create_time datetime 创建时间

代码开发

  • 根据添加购物车接口的参数设计DTO:
1
2
3
4
5
6
7
8
@Data
public class ShoppingCartDTO implements Serializable {

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

}
  • 根据添加购物车接口创建ShoppingCartController:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@RestController("userShoppingCart")
@RequestMapping("/user/shoppingCart")
@Api(tags = "C端购物车相关接口")
@Slf4j
public class ShoppingCartController {

@Autowired
private ShoppingCartService shoppingCartService;

/**
* 添加购物车
* @param shoppingCartDTO
* @return
*/
@PostMapping("/add")
@ApiOperation("添加购物车")
public Result add(@RequestBody ShoppingCartDTO shoppingCartDTO) {
log.info("添加购物车,商品信息为 {}", shoppingCartDTO);
shoppingCartService.addShoppingCart(shoppingCartDTO);
return Result.success();
}
}
  • 创建ShoppingCartService接口:
1
2
3
4
5
6
7
8
9
public interface ShoppingCartService {

/**
* 添加购物车
*
* @param shoppingCartDTO
*/
void addShoppingCart(ShoppingCartDTO shoppingCartDTO);
}
  • 创建ShoppingCartServiceImpl实现类,并实现add方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Service
@Slf4j
public class ShoppingCartServiceImpl implements ShoppingCartService {

@Autowired
private ShoppingCartMapper shoppingCartMapper;
@Autowired
private DishMapper dishMapper;
@Autowired
private SetmealMapper setmealMapper;

/**
* 添加购物车
*
* @param shoppingCartDTO
*/
@Override
public void addShoppingCart(ShoppingCartDTO shoppingCartDTO) {
//判断当前加入购物车中的商品是否已经存在了
ShoppingCart shoppingCart = new ShoppingCart();
BeanUtils.copyProperties(shoppingCartDTO, shoppingCart);
Long userId = BaseContext.getCurrentId();
shoppingCart.setUserId(userId);

List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);

//如果已经存在了,只需要将数量加一
if (list != null && !list.isEmpty()) {
ShoppingCart cart = list.get(0);
cart.setNumber(cart.getNumber() + 1);
shoppingCartMapper.updateNumberById(cart);
} 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 = 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);
}
}
}
  • 创建ShoppingCartMapper接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Mapper
public interface ShoppingCartMapper {

/**
* 动态条件查询
*
* @param shoppingCart
* @return
*/
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, image, user_id, dish_id, setmeal_id, dish_flavor, number, amount, create_time) " +
"VALUES (#{name}, #{image}, #{userId}, #{dishId}, #{setmealId}, #{dishFlavor}, #{number}, #{amount}, #{createTime})")
void insert(ShoppingCart shoppingCart);
}
  • 创建ShoppingCartMapper.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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>

功能测试

  • 可以通过如下方式进行测试:查看控制台sql、Swagger接口文档测试、前后端联调。

查看购物车

需求分析和设计

产品原型

img

接口设计

img

代码开发

  • 在ShoppingCartController中创建查看购物车的方法:
1
2
3
4
5
6
@GetMapping("/list")
@ApiOperation("查看购物车")
public Result<List<ShoppingCart>> list() {
List<ShoppingCart> list = shoppingCartService.showShoppingCart();
return Result.success(list);
}
  • 在ShoppingCartService接口中声明查看购物车的方法:
1
List<ShoppingCart> showShoppingCart();
  • 在ShoppingCartServiceImpl中实现查看购物车的方法:
1
2
3
4
5
6
7
8
@Override
public List<ShoppingCart> showShoppingCart() {
ShoppingCart shoppingCart = new ShoppingCart().builder()
.userId(BaseContext.getCurrentId())
.build();
List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);
return list;
}

功能测试

可以通过接口文档进行测试,最后完成前后端联调测试即可。

清空购物车

需求分析和设计

产品原型

img

接口设计

img

代码开发

  • 在ShoppingCartController中创建清空购物车的方法:
1
2
3
4
5
6
@DeleteMapping("/clean")
@ApiOperation("清空购物车")
public Result clean() {
shoppingCartService.cleanShoppingCart();
return Result.success();
}
  • 在ShoppingCartService接口中声明清空购物车的方法:
1
void cleanShoppingCart();
  • 在ShoppingCartServiceImpl中实现清空购物车的方法:
1
2
3
4
5
@Override
public void cleanShoppingCart() {
Long userId = BaseContext.getCurrentId();
shoppingCartMapper.deleteByUserId(userId);
}
  • 在ShoppingCartMapper接口中创建根据用户id清空购物车的方法:
1
2
@Delete("delete from shopping_cart where user_id = #{userId}")
void deleteByUserId(Long userId);

功能测试

通过Swagger接口文档进行测试,通过后再前后端联调测试即可。

删除购物车中一个商品

需求分析和设计

产品原型

img

接口设计

img

接口设计

代码开发

  • 在ShoppingCartController中创建删除购物车中一个商品的方法:
1
2
3
4
5
6
7
@PostMapping("/sub")
@ApiOperation("删除购物车中一个商品")
public Result sub(@RequestBody ShoppingCartDTO shoppingCartDTO) {
log.info("删除购物车中一个商品:{}", shoppingCartDTO);
shoppingCartService.subShoppingCart(shoppingCartDTO);
return Result.success();
}
  • 在ShoppingCartService接口中声明删除购物车中一个商品的方法:
1
void subShoppingCart(ShoppingCartDTO shoppingCartDTO);
  • 在ShoppingCartServiceImpl中实现删除购物车中一个商品的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public void subShoppingCart(ShoppingCartDTO shoppingCartDTO) {
ShoppingCart shoppingCart = new ShoppingCart();
BeanUtils.copyProperties(shoppingCartDTO, shoppingCart);
//设置查询条件,查询当前登录用户的这一条购物车数据
Long userId = BaseContext.getCurrentId();
shoppingCart.setUserId(userId);

List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);

if (list != null && !list.isEmpty()) {
shoppingCart = list.get(0);

Integer number = shoppingCart.getNumber();
if (number == 1) {
//当前商品在购物车中的分数为1,直接删除当前记录
shoppingCartMapper.deleteById(shoppingCart.getId());
} else {
//当前商品在购物车中的分数不为1,修改份数即可
shoppingCart.setNumber(number - 1);
shoppingCartMapper.updateNumberById(shoppingCart);
}
}
}
  • 在ShoppingCartMapper接口中创建根据id删除购物车中一个商品的方法:
1
2
@Delete("delete from shopping_cart where id = #{id}")
void deleteById(Long id);

功能测试

通过Swagger接口文档进行测试,通过后再前后端联调测试即可。

image-20250509140318181