shoppe项目08—-购物车

目录

购物车数据结构

由于购物车数据量小,且数据变化比较频繁,所以采用Redis内存数据库来存储,采用的数据类型如下:

  1. 存储商品数据,采用hash结构,如cart_1:{3:5}。其中的数字部分分别代表用户id,加购的商品id,购买的该商品的数量。
  2. 存储商品的选中状态,采用set结构,如cart_selected_1: {3, 5,…}
    集合中的数字为勾选的商品id。

Redis hash&set数据类型操作回顾:

$ redis-cli -h localhost -p 6379
>ping

>hset cart_1 3 5
>hmset cart_1 5 2 6 3
>hkeys cart_1
>hvals cart_1
>hgetall cart_1
>hdel cart_1 3

>sadd cart_selected_1 3
>sadd cart_selected_1 5 7 9
>srem cart_selected_1 5
>smembers cart_selected_1

python的操作方法与以上类似

import redis
r = redis.Redis(host='localhost', port=6379, db=0)

r.hset('cart_1', key=2, value=3)
r.hget("cart_1", 2)
r.hdel("cart_1", 2)

r.sadd("cart_selected_1", 2)
r.sadd("cart_selected_1", 2,3)
r.smembers("cart_selected_1")
r.srem("cart_selected_1", 2)

r.close()

添加购物车

前端需求分析(参考已上线的项目

  1. 未登录时点击添加购物车
  2. 已登录时,点击添加购物车
    2.1 该商品第一次加入购物车,在购物车中添加一个商品,并提示’添加购物车成功’
    2.2 该商品已经在购物车,再次加购,提示’该商品已在购物车,数量+1′
    2.3 达到加购上限,提示’加购达到限购数量!’ , 同时’加入购物车’按钮不可点击
    2.4 完成加购后,右上角显示当前购物车中的商品总数。

shoppe项目08----购物车
加购前端接口分析
在Details组件中的模板找到如下代码:
<div class="button">
<el-button class="shop-cart" :disabled="dis" @click="addShoppingCart">加入购物车</el-button>
<el-button class="like" @click="addCollect">喜欢</el-button>
</div>

点击’加入购物车’会触发click事件,对应的函数如下:

addShoppingCart() {

      if (!this.$store.getters.getUser) {
        this.$store.dispatch("setShowLogin", true);
        return;
      }

      console.log("@@getUser,已登录用户:",this.$store.getters.getUser)

      this.$axios
        .post("/carts/user/addCarts/", {
          user: this.$store.getters.getUser.userName,
          product_id: this.productID
        })
        .then(res => {
          console.log("@@添加购物车res:", res)

          switch (res.data.code) {
            case 200:

              this.unshiftShoppingCart(res.data.shoppingCartData);
              this.notifySucceed(res.data.msg);
              break;
            case 201:

              this.addShoppingCartNum(this.productID);
              this.notifySucceed(res.data.msg);
              break;
            case 202:

              this.dis = true;
              this.notifyError(res.data.msg);
              break;
            default:
              this.notifyError(res.data.msg);
          }
        })
        .catch(err => {
          return Promise.reject(err);
        });
    },

总结:
请求地址, /carts/user/addCarts/
请求方法,POST
提交的数据, {
user: this.$store.getters.getUser.userName,
productID: this.productID
}
需要的响应,
{
“code”:200,
“msg”:”新加入购物车成功”,
“shoppingCartData”:{
id: “”, // 购物车id, 如cart_1
productID: “”, // 商品id
productName: “”, // 商品名称
productImg: “”, // 商品图片
price: “”, // 商品价格
num: “”, // 购物车中该商品数量
maxNum: “”, // 商品限购数量,即库存
check: false // 是否勾选
}
}
{“code”:201, “msg”:”该商品已经在购物车,数量+1″}
{“code”:202, “msg”:”加购达到限购数量!”}
{‘code’:203, ‘msg’:’库存不足,无法购买!’}
{“code”:204, “msg”:’其他异常’}

加购后端思路
1. 获取前端数据{“user”:’laufing’, “productID”:3}
2. 根据用户名查询用户对象并构造key,根据商品id查询商品对象,以便获取库存。
3. hash====> cart_uid : {good_id:count, …}
set====> cart_selected_uid: {good_id, …}
存入数据:(加购数量count=1)
a. 如果库存量stock>0 ,则允许加购:
redis如果购物车历史中有该商品,则先获取历史总数num, 然后 num += count,最后比较num总数与库存量。若num

后端加购视图代码


path("user/addCarts/", AddCart.as_view()),

class AddCart(APIView):
"""
        数据存入redis购物车
        1. hash类型,表示购物车商品数据  cart_uid:{good_id:count}
        2. set类型, 表示商品的选中状态(选中) cart_selected_uid: {good_id}
        3. 存入redis之前判断该商品的库存量、它是否在历史购物车中、最终的加购总数num是否
    def post(self, request):
        try:

            username = request.data.get("user")
            good_id = request.data.get("productID")
            count = 1

            try:
                user = User.objects.get(username=username)

                cart_key = "cart_%s"%user.id
                cart_selected_key = "cart_selected_%s"%user.id
            except:
                return Response({"code":204, "msg":"用户不存在!"})

            try:
                good = Goods.objects.get(id=good_id)
            except:
                return Response({"code":204, "msg":"商品不存在!"})

            redis_conn = redis.Redis(host='localhost', port=6379, db=0)

            if good.stock > 0:

                num = redis_conn.hget(cart_key, good_id)

                if num:
                    num = int(num.decode())
                    num += count

                    if num  good.stock:

                        redis_conn.hset(cart_key, good_id, num)
                        redis_conn.sadd(cart_selected_key, good_id)

                        redis_conn.close()
                        return Response({"code":201, "msg":"该商品已经在购物车,数量+1"})
                    else:

                        redis_conn.close()
                        return Response({"code":202, "msg":"加购达到限购数量!"})

                else:
                    redis_conn.hset(cart_key, good_id, count)
                    redis_conn.sadd(cart_selected_key, good_id)
                    shopping_cart_data = {
                        "id": cart_key,
                        "productID": good_id,
                        "productName": good.sku_name,
                        "productImg": good.img,
                        "price": good.selling_price,
                        "num": count,
                        "maxNum": good.stock,
                        "check": True,
                    }

                    redis_conn.close()
                    return Response({"code": 200, "msg": "新加入购物车成功!", "shoppingCartData": shopping_cart_data})

            else:

                redis_conn.close()
                return Response({"code":203, "msg":"库存不足,无法购买!"})
        except:
            return Response({"code":204, "msg":"服务器内部错误"})

以上在浏览器控制台可以看到对应的响应。

查看购物车

  1. 购物车商品总数前端分析
    用户登录后,在首页右上角的购物车处,显示加购商品总数。查看源代码可以发现,它是一个计算属性,如下:
    shoppe项目08----购物车
    getNum,从集中式状态管理中映射过来的 ,即store>getters>getNum。
    该函数统计state.shoppingCart数组中的商品数量。
getNum (state) {

      let totalNum = 0;
      for (let i = 0; i < state.shoppingCart.length; i++) {
        const temp = state.shoppingCart[i];
        totalNum += temp.num;
      }
      return totalNum;
    },
  1. 加载购物车商品数据前端分析
    那么state中的shoppingCart数据什么时候加载的?
    监视属性getUser,用户登录后state.user属性值即由” “变为”laufing”, 触发对应的函数

getUser: function (val) {
  if (val === "") {

    this.setShoppingCart([]);
  } else {

    this.$axios
      .post("/carts/user/getShoppingCart/", {
        user: val,
      })
      .then((res) => {
        console.log("@@当前用户的购物车数据:",res)

        if (res.data.code == 200) {

          this.setShoppingCart(res.data.shoppingCartData);
        } else {

          this.notifyError(res.data.msg);
        }
      })
      .catch((err) => {
        return Promise.reject(err);
      });
  }
},

总结:
用户每次登陆时,state.user的值会发生变化,触发监视属性getUser对应的函数,从而向后端发送请求,加载购物车商品数据。
请求地址,/carts/user/getShoppingCart/
请求方法,POST
提交数据,{
user: val, //当前 用户对象 {userName:’laufing’}
}
需要的响应,{
code:200,
msg:’加载购物车商品数据成功!’
shoppingCartData:[{ },…]
}
其中的每个商品对象格式,
{
id: “”, // 购物车id
productID: “”, // 商品id
productName: “”, // 商品名称
productImg: “”, // 商品图片
price: “”, // 商品价格
num: “”, // 加购的商品数量
maxNum: “”, // 商品限购数量
check: false // 是否勾选
}

  1. 加载购物车商品数据后端视图

path('user/getShoppingCart/', GetCart.as_view()),

class GetCart(APIView):
"""
        1. 根据用户名查询用户对象
        2. 根据用户id构造key,查询redis中的购物车数据(cart_uid、cart_selected_uid)
        3. 根据购物车里的商品id,查询商品对象,组织每一个商品字典
            {
                "id": "cart_uid",
                "productID":xxx,
                "productName":xxx,
                "productImg":xxx,
                "price":商品的selling_price,
                "num": xxx, 加购数量
                "maxNum": xxx, 限购数量
                "check": True or False, 是否勾选
            }
        4.多个商品字典 组织为一个列表[{},{},...], 然后返回响应
"""
    def post(self, request):
        try:

            username = request.data.get("user").get("userName")

            try:
                user = User.objects.get(username=username)
                cart_key = "cart_%s"%user.id
                cart_selected_key = "cart_selected_%s"%user.id
            except:
                return Response({"code":204, "msg":"当前用户不存在!"})

            redis_conn = redis.Redis(host="localhost",  port=6379, db=0)
            shopping_cart_data = []

            cart_selected = [int(i.decode()) for i in redis_conn.smembers(cart_selected_key)]

            for good_id, count in redis_conn.hgetall(cart_key).items():
                good_id = int(good_id.decode())
                count = int(count.decode())

                if good_id in cart_selected:
                    one_good = get_one_good(cart_key, good_id, count, check=True)
                else:
                    one_good = get_one_good(cart_key, good_id, count, check=False)

                shopping_cart_data.append(one_good)

            return Response({"code":200, 'msg':"获取用户的购物车数据成功!",
                             "shoppingCartData":shopping_cart_data})
        except:
            return Response({"code":204, "msg":"服务端获取购物车数据异常"})

def get_one_good(cart_key, good_id, count, check=False):
"""
    :param cart_key: 购物车id
    :param good_id: redis购物车中一个商品的id,
    :param count: 该商品加购的数量
    :param check: 该商品是否被勾选
    :return: 一个商品的字典数据
"""

    try:
        good = Goods.objects.get(id=good_id)
    except:
        return {}
    return {
            "id":cart_key,
            "productID":good_id,
            "productName":good.sku_name,
            "productImg":good.img,
            "price":good.selling_price,
            "num":count,
            "maxNum":good.stock,
            "check": check,
            }
  1. 前端展示购物车商品
    未登录时,点击购物车(router-link),弹出登录框。
    已登录时,点击购物车,则跳转到购物车页面。
    前端代码分析,在APP组件中找到’购物车’
<router-link to="/shoppingCart">
    <i class="el-icon-shopping-cart-full"></i> 购物车
    <span class="num">({{ getNum }})</span>
</router-link>

路由/shoppingCart
组件ShoppingCart
在路由与组件的对应关系中,有requireAuth:true,所以每次未登录时点击购物车都会弹出登录框。
用户完成登录,vuex中的user从空值改为具体的用户对象,触发监视属性getUser对应的函数,去加载用户的购物车数据。
然后跳转到购物车页面,展示加载的购物车商品。

具体参考ShoppingCart组件
页面效果:

shoppe项目08----购物车

Original: https://blog.csdn.net/weixin_45228198/article/details/122730102
Author: laufing
Title: shoppe项目08—-购物车

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/736555/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球