购物车功能设计(二)(使用redis实现购物车功能)

    科技2026-03-27  14

    使用redis实现购物车

    redis是什么

    redis是一款开源的、功能强大、支持多种数据类型、高性能的键值对存储数据库。 redis支持的数据类型有:

    字符串哈希列表有序集合散列集合

    为什么选择redis来实现购物车

    将购物车数据存放到Redis中,可以加快购物车的读写性能,从而提高用户体验,缺点就是Redis数据是存放到内存,相对成本较高。但是这个成本,一般企业都可以接受。 一般情况下购物车功能都是使用session/cookie实现的,也就是将整个购物车数据都存储到session中。这样做的好处就是不用操作数据库就可以实现,同时用户可以不同登录就可以将商品加入到购物车中,缺点就是1. 导致session过于臃肿 2. session数据默认是存储到文件中的,所以操作session是相对比较慢的。

    如何使用redis实现购物车功能

    首先我们创建一个JedisClient类来操作redis /* Created by IntelliJ IDEA. User: Kalvin Date: 2020/5/13 Time: 14:21 */ package com.lianwei.lssg.cache; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @Component public class JedisClient { @Autowired private StringRedisTemplate redisTemplate; // Key(键),简单的key-value操作 /** * 实现命令:TTL key,以秒为单位,返回给定 key的剩余生存时间(TTL, time to live)。 * * @param key * @return */ public long ttl(String key) { return redisTemplate.getExpire(key); } /** * 实现命令:expire 设置过期时间,单位秒 * * @param key * @return */ public void expire(String key, long timeout) { redisTemplate.expire(key, timeout, TimeUnit.SECONDS); } /** * 实现命令:INCR key,增加key一次 * * @param key * @return */ public long incr(String key, long delta) { return redisTemplate.opsForValue().increment(key, delta); } /** * 实现命令:KEYS pattern,查找所有符合给定模式 pattern的 key */ public Set<String> keys(String pattern) { return redisTemplate.keys(pattern); } /** * 实现命令:DEL key,删除一个key * * @param key */ public void del(String key) { redisTemplate.delete(key); } // String(字符串) /** * 实现命令:SET key value,设置一个key-value(将字符串值 value关联到 key) * * @param key * @param value */ public void set(String key, String value) { redisTemplate.opsForValue().set(key, value); } /** * 实现命令:SET key value EX seconds,设置key-value和超时时间(秒) * * @param key * @param value * @param timeout * (以秒为单位) */ public void set(String key, String value, long timeout) { redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS); } /** * 实现命令:GET key,返回 key所关联的字符串值。 * * @param key * @return value */ public String get(String key) { return (String)redisTemplate.opsForValue().get(key); } // Hash(哈希表) /** * 实现命令:HEXISTS key field,查找哈希表中是否包含指定键值对 key中给定域 field的值 * @param key * @param field * @return */ public Boolean hexists(String key, String field){ return redisTemplate.opsForHash().hasKey(key, field); } /** * 实现命令:HSET key field value,将哈希表 key中的域 field的值设为 value * * @param key * @param field * @param value */ public void hset(String key, String field, Object value) { redisTemplate.opsForHash().put(key, field, value); } /** * 实现命令:HGET key field,返回哈希表 key中给定域 field的值 * * @param key * @param field * @return */ public String hget(String key, String field) { return (String) redisTemplate.opsForHash().get(key, field); } /** * 实现命令:HDEL key field [field ...],删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。 * * @param key * @param fields */ public void hdel(String key, Object... fields) { redisTemplate.opsForHash().delete(key, fields); } /** * 实现命令:HGETALL key,返回哈希表 key中,所有的域和值。 * * @param key * @return */ public Map<Object, Object> hgetall(String key) { return redisTemplate.opsForHash().entries(key); } // List(列表) /** * 实现命令:LPUSH key value,将一个值 value插入到列表 key的表头 * * @param key * @param value * @return 执行 LPUSH命令后,列表的长度。 */ public long lpush(String key, String value) { return redisTemplate.opsForList().leftPush(key, value); } /** * 实现命令:LPOP key,移除并返回列表 key的头元素。 * * @param key * @return 列表key的头元素。 */ public String lpop(String key) { return (String)redisTemplate.opsForList().leftPop(key); } /** * 实现命令:RPUSH key value,将一个值 value插入到列表 key的表尾(最右边)。 * * @param key * @param value * @return 执行 LPUSH命令后,列表的长度。 */ public long rpush(String key, String value) { return redisTemplate.opsForList().rightPush(key, value); } }

    在redis数据库中我们使用cart作为filed 以用户名作为key 购物车内容为value

    /* Created by IntelliJ IDEA. User: Kalvin Date: 2020/5/12 Time: 17:41 */ package com.lianwei.lssg.controller.before; import com.alibaba.fastjson.JSON; import com.lianwei.lssg.cache.JedisClient; import com.lianwei.lssg.entity.LssgCart; import com.lianwei.lssg.entity.LssgCartItem; import com.lianwei.lssg.entity.LssgProduct; import com.lianwei.lssg.service.before.LssgProductService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; import java.util.Set; @Controller @RequestMapping("cart") public class CartController { @Resource private JedisClient jedisClient; @Resource private LssgProductService lssgProductService; private static final String REDIS_CART = "cart"; /** * 商品加入到购物车 * */ @RequestMapping("/addProductToCart") @ResponseBody public LssgCart addProductToCart(@RequestParam("productId")Integer productId, @RequestParam(value = "nums",required = false)String nums){ //获取已登录的用户信息 Subject subject = SecurityUtils.getSubject(); String userLoginName = (String)subject.getPrincipal(); LssgProduct lssgProduct = lssgProductService.selectOneProductByProductId(productId); /** * 判断lssgProduct是否为空 * */ String productNumState; if(lssgProduct.getProductNum()<=0){ productNumState = "无货"; }else { productNumState = "有货"; } /** * 存在一个问题:当session超时后因该强制用户进行登入,只有登入成功才能做相应的操作 * */ if(("").equals(userLoginName)){ System.out.println("session超时了"); /*map.put("lssg",false);*/ return null; } Boolean hexists = jedisClient.hexists(REDIS_CART, userLoginName); System.out.println("hexists---->"+hexists); LssgCart lssgCart = null; if(nums==null|| nums.equals("")){ nums = String.valueOf(1); } BigDecimal num =new BigDecimal(nums); if(!hexists){ lssgCart = new LssgCart(); LssgCartItem lssgCartItem = new LssgCartItem(lssgProduct,num,lssgProduct.getProductMallPrice(),productNumState); lssgCartItem.setSubtotalPrice((lssgProduct.getProductMallPrice()).multiply(lssgCartItem.getBuyNum())); Map<String,LssgCartItem> lssgCartItemMap = new HashMap<String, LssgCartItem>(); lssgCartItemMap.put(String.valueOf(lssgProduct.getProductId()),lssgCartItem); lssgCart.setCartItems(lssgCartItemMap); lssgCart.setTotalPrice(lssgCartItem.getSubtotalPrice()); }else { lssgCart = JSON.parseObject(jedisClient.hget(REDIS_CART,userLoginName),LssgCart.class); if(lssgCart.getCartItems().containsKey(String.valueOf(lssgProduct.getProductId()))){ LssgCartItem lssgCartItem = lssgCart.getCartItems().get(String.valueOf(lssgProduct.getProductId())); //数量 lssgCartItem.setBuyNum(lssgCartItem.getBuyNum().add(num)); //小计+商品的单价(由于是BigDecimal类型所以用他自己封装好的) lssgCartItem.setSubtotalPrice((lssgProduct.getProductMallPrice()).multiply(lssgCartItem.getBuyNum())); lssgCart.getCartItems().put(String.valueOf(lssgProduct.getProductId()),lssgCartItem); }else{ //没买过,先创建新的购物项 LssgCartItem lssgCartItem = new LssgCartItem(lssgProduct,num,lssgProduct.getProductMallPrice(),productNumState); lssgCartItem.setSubtotalPrice((lssgProduct.getProductMallPrice()).multiply(lssgCartItem.getBuyNum())); //存入购物车的Map集合 lssgCart.getCartItems().put(String.valueOf(lssgProduct.getProductId()),lssgCartItem); } //计算总价格,并加入到购物车 BigDecimal sum = new BigDecimal("0.00"); Set<String> ks = lssgCart.getCartItems().keySet(); for(String key: ks){ LssgCartItem lssgCartItem = lssgCart.getCartItems().get(key); sum = sum.add(((lssgCartItem.getLssgProduct().getProductMallPrice()).multiply(lssgCartItem.getBuyNum()))); } lssgCart.setTotalPrice(sum); } jedisClient.hset(REDIS_CART,userLoginName, JSON.toJSONString(lssgCart)); System.out.println("添加成功!"); jedisClient.hget(REDIS_CART,userLoginName); return JSON.parseObject(jedisClient.hget(REDIS_CART,userLoginName),LssgCart.class); } /** *购物车信息展示到前台购物车页 * */ @RequestMapping("/fromCartShowProduct") @ResponseBody public LssgCart fromCartShowProduct(){ //获取已登录的用户信息 Subject subject = SecurityUtils.getSubject(); String userLoginName = (String) subject.getPrincipal(); /** * 存在一个问题:当session超时后因该强制用户进行登入,只有登入成功才能做相应的操作 * */ if(("").equals(userLoginName)){ System.out.println("session超时了"); /*map.put("lssg",false);*/ return null; } Boolean hexists = jedisClient.hexists(REDIS_CART, userLoginName); if(hexists){ return JSON.parseObject(jedisClient.hget(REDIS_CART,userLoginName),LssgCart.class); } return null; } /** * 实现购物车页面通过+/-/输入 按钮来对商品的数量更新操作 * */ @RequestMapping("/updateCartProductNum") @ResponseBody public LssgCart decCartProductNum(@RequestParam("productId")Integer productId, @RequestParam("nums")String nums){ //获取已登录的用户信息 Subject subject = SecurityUtils.getSubject(); String userLoginName = (String) subject.getPrincipal(); LssgCart lssgCart = JSON.parseObject(jedisClient.hget(REDIS_CART,userLoginName),LssgCart.class); Map<String,LssgCartItem> lssgCartItemMap = lssgCart.getCartItems(); LssgCartItem lssgCartItem = lssgCartItemMap.get(String.valueOf(productId)); //购物项(该商品)原来的小计 BigDecimal oldSubtotalPrice = lssgCartItem.getSubtotalPrice(); System.out.println("oldSubtotalPrice--->"+oldSubtotalPrice); BigDecimal num =new BigDecimal(nums); //更新购买数量 lssgCartItem.setBuyNum(num); //更新小计 lssgCartItem.setSubtotalPrice((lssgCartItem.getBuyNum()).multiply(lssgCartItem.getLssgProduct().getProductMallPrice())); //把购物项放到购物项(map)集合里 lssgCartItemMap.put(String.valueOf(productId),lssgCartItem); lssgCart.setCartItems(lssgCartItemMap); //更新购物车总价 新的购物车总价 = 原来购物车总价 - 原来购物项的小计 + 新的购物项的小计 lssgCart.setTotalPrice(lssgCart.getTotalPrice().subtract(oldSubtotalPrice).add(lssgCartItem.getSubtotalPrice())); //更新购物车到redis jedisClient.hset(REDIS_CART,userLoginName,JSON.toJSONString(lssgCart)); //从redis中取出购物车传到到前台 return JSON.parseObject(jedisClient.hget(REDIS_CART,userLoginName),LssgCart.class); } /** * 删除购物车中某一个购物项 * */ @RequestMapping("/delCartProduct") @ResponseBody public LssgCart delCartProduct(@RequestParam("productId")Integer productId, @RequestParam("nums")String nums){ //获取已登录的用户信息 Subject subject = SecurityUtils.getSubject(); String userLoginName = (String) subject.getPrincipal(); LssgCart lssgCart = JSON.parseObject(jedisClient.hget(REDIS_CART,userLoginName),LssgCart.class); Map<String,LssgCartItem> lssgCartItemMap = lssgCart.getCartItems(); LssgCartItem lssgCartItem = lssgCartItemMap.get(String.valueOf(productId)); BigDecimal num =new BigDecimal(nums); System.out.println("num---->"+num); //通过商品id删除该购物项 lssgCartItemMap.remove(String.valueOf(productId)); //更新总价 新的购物车总价 = 原来购物车总价 - 原来购物项的小计 lssgCart.setTotalPrice(lssgCart.getTotalPrice().subtract(((lssgCartItem.getLssgProduct().getProductMallPrice()).multiply(num)))); //更新购物车到redis jedisClient.hset(REDIS_CART,userLoginName,JSON.toJSONString(lssgCart)); //从redis中取出购物车传到到前台 return JSON.parseObject(jedisClient.hget(REDIS_CART,userLoginName),LssgCart.class); } /** * 清空购物车 * */ @RequestMapping("/emptyCart") public void emptyCart(){ //获取已登录的用户信息 Subject subject = SecurityUtils.getSubject(); String userLoginName = (String) subject.getPrincipal(); jedisClient.hdel(REDIS_CART,userLoginName); } /** *删除已结算的购物项 * */ @RequestMapping("/delProductByProductIds") @ResponseBody public LssgCart delProductByProductIds(@RequestParam(value="productIds[]")String[] productIds){ //获取已登录的用户信息 Subject subject = SecurityUtils.getSubject(); String userLoginName = (String) subject.getPrincipal(); LssgCart lssgCart = JSON.parseObject(jedisClient.hget(REDIS_CART,userLoginName),LssgCart.class); BigDecimal delTotalPrice =new BigDecimal("0.00"); Map<String,LssgCartItem> lssgCartItemMap = lssgCart.getCartItems(); for(String productId : productIds){ LssgCartItem lssgCartItem = lssgCartItemMap.get(productId); // MySQL数据库中商品加上相对应的库存量 delTotalPrice = delTotalPrice.add(lssgCartItem.getSubtotalPrice()); //通过商品id删除该购物项 lssgCartItemMap.remove(productId); } //更新总价 新的购物车总价 = 原来购物车总价 - 要结算购物项的小计 lssgCart.setTotalPrice(lssgCart.getTotalPrice().subtract(delTotalPrice)); //更新购物车到redis jedisClient.hset(REDIS_CART,userLoginName,JSON.toJSONString(lssgCart)); //从redis中取出购物车传到到前台 return JSON.parseObject(jedisClient.hget(REDIS_CART,userLoginName),LssgCart.class); } /** * 统计购物车中的购物项 * */ @RequestMapping("/countCartItme") @ResponseBody public Integer countCartItme(){ //获取已登录的用户信息 Subject subject = SecurityUtils.getSubject(); String userLoginName = (String) subject.getPrincipal(); LssgCart lssgCart = JSON.parseObject(jedisClient.hget(REDIS_CART,userLoginName),LssgCart.class); if(lssgCart==null){ return 0; } return lssgCart.getCartItems().size(); } }

    在lssgCartItemMap.put(String.valueOf(lssgProduct.getProductId()),lssgCartItem);中我们把商品ID作为购物项集合lssgCartItemMap的key

    Processed: 0.011, SQL: 9