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

使用redis实现购物车

redis是什么

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

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

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

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

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

  1. 首先我们创建一个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

猜你喜欢

转载自blog.csdn.net/qq_43900677/article/details/108969435