【Java 实战】通过Redis实现购物车功能
项目场景:
通过Redis实现购物车,包括添加购物车、更新商品数量、删除购物车、结算验证库存等功能。
设计思路
1.数据存储
对于购物车,我们在Redis中需要存储的是用户和商品信息,数据结构类似于Java中Map<String,Map<String,String>>形式,将外层Map的key记为K,内层Map的key和value分别记为HK、HV
使用K来存储用户标识(例如:用户的ID)
使用HK存储商品标识(例如:商品ID)
使用HV存储购物车商品信息(例如:购物车中商品数量、价格、店铺信息等(根据自己业务需求))
Redis 中可以使用boundHashOps
方法来实现:BoundHashOperations<String, Object, Object> operations = stringRedisTemplate.boundHashOps(userId);
也可以使用Map<String, object> itemMap = redisTemplate.opsForValue().get(userId);
来实现
2.功能结构图
3.功能描述
在添加购物车时需要用户先登录(web界面后台可以通过session获取用户id),这里直接默认传入用户id
添加购物车
添加购物车,需要判断购物车里是否存在该商品,存在则该商品数量增加,不存在需要查询该商品然后将商品加入到购物车,方法:operations.put(key,value);
@Override
public ApiResponse addCart(OperateVo vo) {
// 用户购物车存储的key
String key = CART_KEY + vo.getUserId();
// 获取用户购物车数据
BoundHashOperations<String, Object, Object> cartMap = stringRedisTemplate.boundHashOps(key);
// 判断商品是否存在
String cart = (String) cartMap.get(vo.getSkuId());
if (StringUtils.isBlank(cart)) {
// 商品不存在,则添加商品信息
Item item = itemService.getBySkuId(vo.getSkuId());
// 添加购物车商品数量
item.setNum(vo.getNum());
cartMap.put(vo.getSkuId(), JSON.toJSONString(item));
} else {
// 商品存在,则数量增加
Item item = JSON.parseObject(cart, Item.class);
item.setNum(item.getNum() + vo.getNum());
cartMap.put(vo.getSkuId(), JSON.toJSONString(item));
}
return ApiResponse.ok();
}
获取购物车信息
通过用户id查询该用户的购物车信息,方法:operations.get(key);
@Override
public ApiResponse showCart(OperateVo vo) {
CartEntry cartEntry = new CartEntry();
// 用户购物车存储的key
String key = CART_KEY + vo.getUserId();
BoundHashOperations<String, Object, Object> cartMap = stringRedisTemplate.boundHashOps(key);
// 获取所有的购物车数据
List<Object> cartList = cartMap.values();
if (CollectionUtils.isNotEmpty(cartList)) {
List<Item> items = new ArrayList<>();
cartList.forEach(item -> {
String objStr = (String) item;
Item carItem = JSON.parseObject(objStr, Item.class);
items.add(carItem);
});
cartEntry.setItems(items);
}
return ApiResponse.ok(cartEntry);
}
修改商品数量
对购物车中商品的数量进行修改,方法:operations.put(key);
@Override
public ApiResponse updateCartNum(OperateVo vo) {
// 用户购物车存储的key
String key = CART_KEY + vo.getUserId();
BoundHashOperations<String, Object, Object> cartMap = stringRedisTemplate.boundHashOps(key);
String cart = (String) cartMap.get(vo.getSkuId());
Item item = JSON.parseObject(cart, Item.class);
item.setNum(vo.getNum());
cartMap.put(vo.getSkuId(), JSON.toJSONString(item));
return ApiResponse.ok();
}
删除购物车
删除购物车中的商品,或者清空用户的购物车,方法分别为:operations.delete(key)、stringRedisTemplate.delete(userId);
@Override
public ApiResponse delCart(OperateVo vo) {
// 用户购物车存储的key
String key = CART_KEY + vo.getUserId();
BoundHashOperations<String, Object, Object> cartMap = stringRedisTemplate.boundHashOps(key);
cartMap.delete(vo.getSkuId());
return ApiResponse.ok();
}
结算页面验证购物车商品库存
添加晚购物车后,点击去结算,验证商品库存是否充足
@Override
public ApiResponse checkNumCart(OperateVo vo) {
CartEntry cartEntry = new CartEntry();
// 用户购物车存储的key
String key = CART_KEY + vo.getUserId();
BoundHashOperations<String, Object, Object> cartMap = stringRedisTemplate.boundHashOps(key);
if (vo != null) {
List<Item> items = new ArrayList<>();
vo.getSkuIds().forEach(id -> {
String cart = (String) cartMap.get(id);
Item redisItem = JSON.parseObject(cart, Item.class);
// 获取商品库存数量
Item item = itemService.getBySkuId(vo.getSkuId());
if (item.getNum() < redisItem.getNum()) {
// 库存不足
redisItem.setEnough(false);
}
items.add(redisItem);
});
cartEntry.setItems(items);
}
return ApiResponse.ok(cartEntry);
}
代码实现:
好啦,购物车操作的功能到这里已经差不多啦,个人觉得后面购物车下单、拆单及退款处理才是难点😏
接下来,又到了愉快的贴代码环节了,废话不多说,直接上代码:
CartEntry.java
/**
* 购物车
*
* @author:MaodeShu
* @date:2022-11-12 11:24
*/
@Data
public class CartEntry {
List<Item> items;// 商品列表
BigDecimal totalPrice; // 总结
int totalNum; // 总数
}
Item.java
/**
* 商品信息
*
* @author:MaodeShu
* @date:2022-11-12 11:24
*/
@Data
public class Item extends ItemEntity{
// 商品是否充足
private Boolean enough = true;
}
ItemEntity.java
/**
* 商品实体信息
*
* @author:MaodeShu
* @date:2022-11-12 11:24
*/
@Data
@TableName("item_sku_info")
public class ItemEntity {
private String productId;// 产品id
private String productName; // 产品名称
private String skuId;// 商品skuId
private String skuName; // 商品skuName
private String shopId;// 店铺id
private String shopName;// 店铺名
private String shopLog; // 店铺logo
private String coverUrl; // 商品图
private BigDecimal sellPrice; // 售价
private Integer num; // 库存
}
OperateVo.java
/**
* 操作购物车信息
*
* @author:MaodeShu
* @date:2022-11-12 11:24
*/
@Data
public class OperateVo {
private String userId;// 用户id
private String skuId; // 商品skuId
private Integer num;// 数量
private List<String> skuIds; // 商品skuId列表(选择购物车时使用)
}
CartServiceImpl.java
@Service
public class CartServiceImpl implements CartService {
private final String CART_KEY = "cart:";
@Resource
private RedisTemplate<String, String> stringRedisTemplate;
@Resource
private ItemInfoService itemService;
@Override
public ApiResponse addCart(OperateVo vo) {
// 用户购物车存储的key
String key = CART_KEY + vo.getUserId();
// 获取用户购物车数据
BoundHashOperations<String, Object, Object> cartMap = stringRedisTemplate.boundHashOps(key);
// 判断商品是否存在
String cart = (String) cartMap.get(vo.getSkuId());
if (StringUtils.isBlank(cart)) {
// 商品不存在,则添加商品信息
Item item = itemService.getBySkuId(vo.getSkuId());
// 添加购物车商品数量
item.setNum(vo.getNum());
cartMap.put(vo.getSkuId(), JSON.toJSONString(item));
} else {
// 商品存在,则数量增加
Item item = JSON.parseObject(cart, Item.class);
item.setNum(item.getNum() + vo.getNum());
cartMap.put(vo.getSkuId(), JSON.toJSONString(item));
}
return ApiResponse.ok();
}
@Override
public ApiResponse showCart(OperateVo vo) {
CartEntry cartEntry = new CartEntry();
// 用户购物车存储的key
String key = CART_KEY + vo.getUserId();
BoundHashOperations<String, Object, Object> cartMap = stringRedisTemplate.boundHashOps(key);
// 获取所有的购物车数据
List<Object> cartList = cartMap.values();
if (CollectionUtils.isNotEmpty(cartList)) {
List<Item> items = new ArrayList<>();
cartList.forEach(item -> {
String objStr = (String) item;
Item carItem = JSON.parseObject(objStr, Item.class);
items.add(carItem);
});
cartEntry.setItems(items);
}
return ApiResponse.ok(cartEntry);
}
@Override
public ApiResponse updateCartNum(OperateVo vo) {
// 用户购物车存储的key
String key = CART_KEY + vo.getUserId();
BoundHashOperations<String, Object, Object> cartMap = stringRedisTemplate.boundHashOps(key);
String cart = (String) cartMap.get(vo.getSkuId());
Item item = JSON.parseObject(cart, Item.class);
item.setNum(vo.getNum());
cartMap.put(vo.getSkuId(), JSON.toJSONString(item));
return ApiResponse.ok();
}
@Override
public ApiResponse delCart(OperateVo vo) {
// 用户购物车存储的key
String key = CART_KEY + vo.getUserId();
BoundHashOperations<String, Object, Object> cartMap = stringRedisTemplate.boundHashOps(key);
cartMap.delete(vo.getSkuId());
return ApiResponse.ok();
}
@Override
public ApiResponse checkNumCart(OperateVo vo) {
CartEntry cartEntry = new CartEntry();
// 用户购物车存储的key
String key = CART_KEY + vo.getUserId();
BoundHashOperations<String, Object, Object> cartMap = stringRedisTemplate.boundHashOps(key);
if (vo != null) {
List<Item> items = new ArrayList<>();
vo.getSkuIds().forEach(id -> {
String cart = (String) cartMap.get(id);
Item redisItem = JSON.parseObject(cart, Item.class);
// 获取商品库存数量
Item item = itemService.getBySkuId(vo.getSkuId());
if (item.getNum() < redisItem.getNum()) {
// 库存不足
redisItem.setEnough(false);
}
items.add(redisItem);
});
cartEntry.setItems(items);
}
return ApiResponse.ok(cartEntry);
}
}
总结:
以上就是今天要讲的内容,本文仅仅简单介绍了如何通过Redis实现购物车功能,以上方法亲测有效,希望能给大家一个参考。
创作不易,关注💖、点赞👍、收藏🎉就是对作者最大的鼓励👏,欢迎在下方评论留言🧐
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY