用户浏览历史记录

用户在访问每个商品详情页面时,都要记录浏览历史记录

历史记录只需保存多个商品的sku_id即可,而且需要保持添加sku_id的顺序,所以采用redis中的列表来保存,redis的数据存储设计

'history_用户id': [sku_id列表]

在配置文件中增加浏览历史记录的redis配置

CACHES = {
    ...
    "history": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://10.211.55.5:6379/3",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
}

1. 保存

后端接口设计

请求方式:POST /browse_histories/

请求参数:JSON 或 表单

参数 类型 是否必须 说明
sku_id int 商品sku 编号

返回数据:JSON

返回值 类型 是否必须 说明
sku_id int 商品sku 编号

后端实现

在users/serializes.py中实现序列化器

class AddUserBrowsingHistorySerializer(serializers.Serializer):
    """
    添加用户浏览历史序列化器
    """
    sku_id = serializers.IntegerField(label="商品SKU编号", min_value=1)

    def validate_sku_id(self, value):
        """
        检验sku_id是否存在
        """
        try:
            SKU.objects.get(id=value)
        except SKU.DoesNotExist:
            raise serializers.ValidationError('该商品不存在')
        return value

    def create(self, validated_data):
        """
        保存
        """
        user_id = self.context['request'].user.id
        sku_id = validated_data['sku_id']

        redis_conn = get_redis_connection("history")
        pl = redis_conn.pipeline()

        # 移除已经存在的本商品浏览记录
        pl.lrem("history_%s" % user_id, 0, sku_id)
        # 添加新的浏览记录
        pl.lpush("history_%s" % user_id, sku_id)
        # 只保存最多5条记录
        pl.ltrim("history_%s" % user_id, 0, constants.USER_BROWSING_HISTORY_COUNTS_LIMIT-1)

        pl.execute()

        return validated_data

在users/views.py中编写视图

class UserBrowsingHistoryView(CreateAPIView):
    """
    用户浏览历史记录
    """
    serializer_class = AddUserBrowsingHistorySerializer
    permission_classes = [IsAuthenticated]

前端实现

在detail.js中添加

mounted: function(){
        // 添加用户浏览历史记录
        this.get_sku_id();
        if (this.user_id) {
            axios.post(this.host+'/browse_histories/', {
                sku_id: this.sku_id
            }, {
                headers: {
                    'Authorization': 'JWT ' + this.token
                }
            })
        }
        this.get_cart();
        this.get_hot_goods();
        this.get_comments();
    },

2. 查看

后端接口设计

请求方式:GET /browse_histories/

请求参数: 无

返回数据: JSON

[
    {
        "id": 14,
        "name": "华为 HUAWEI P10 Plus 6GB+128GB 玫瑰金 移动联通电信4G手机 双卡双待",
        "price": "3788.00",
        "default_image_url": "http://image.meiduo.site:8888/group1/M00/00/02/CtM3BVrRdMSAaDUtAAVslh9vkK04466364",
        "comments": 1
    },
    {
        "id": 16,
        "name": "华为 HUAWEI P10 Plus 6GB+128GB 曜石黑 移动联通电信4G手机 双卡双待",
        "price": "3788.00",
        "default_image_url": "http://image.meiduo.site:8888/group1/M00/00/02/CtM3BVrRdPeAXNDMAAYJrpessGQ9777651",
        "comments": 0
    }
]
返回值 类型 是否必须 说明
id int 商品sku 编号
name str 商品名称
price decimal 单价
default_image_url str 默认图片
comments int 评论量

后端实现

在users/views.py中UserBrowsingHistoryView视图补充get方法

from goods.serializers import SKUSerializer

class UserBrowsingHistoryView(mixins.CreateModelMixin, GenericAPIView):
    """
    用户浏览历史记录
    """
    ...

    def get(self, request):
        """
        获取
        """
        user_id = request.user.id

        redis_conn = get_redis_connection("history")
        history = redis_conn.lrange("history_%s" % user_id, 0, constants.USER_BROWSING_HISTORY_COUNTS_LIMIT-1)
        skus = []
        # 为了保持查询出的顺序与用户的浏览历史保存顺序一致
        for sku_id in history:
            sku = SKU.objects.get(id=sku_id)
            skus.append(sku)

        s = SKUSerializer(skus, many=True)
        return Response(s.data)

前端实现

修改user_center_info.html

                <h3 class="common_title2">最近浏览</h3>
                <div class="has_view_list">
                    <ul class="goods_type_list clearfix">
                        <li v-for="sku in histories">
                        <a :href="sku.url"><img :src="sku.default_image_url"></a>
                        <h4><a :href="sku.url">{{sku.name}}</a></h4>
                        <div class="operate">
                            <span class="prize">¥{{sku.price}}</span>
                            <span class="unit">{{sku.comments}}评价</span>
                            </div>
                        </li>
                    </ul>
                </div>

修改user_center_info.js

        // 判断用户的登录状态
        if (this.user_id && this.token) {
            axios.get(this.host + '/user/', {
                    // 向后端传递JWT token的方法
                    headers: {
                        'Authorization': 'JWT ' + this.token
                    },
                    responseType: 'json',
                })
                .then(response => {
                    // 加载用户数据
                    this.user_id = response.data.id;
                    this.username = response.data.username;
                    this.mobile = response.data.mobile;
                    this.email = response.data.email;
                    this.email_active = response.data.email_active;
                    // 补充请求浏览历史
                    axios.get(this.host + '/browse_histories/', {
                            headers: {
                                'Authorization': 'JWT ' + this.token
                            },
                            responseType: 'json'
                        })
                        .then(response => {
                            this.histories = response.data;
                            for(var i=0; i<this.histories.length; i++){
                                this.histories[i].url = '/goods/' + this.histories[i].id + '.html';
                            }
                        })
                })
                .catch(error => {
                    if (error.response.status==401 || error.response.status==403) {
                        location.href = '/login.html?next=/user_center_info.html';
                    }
                });
        } else {
            location.href = '/login.html?next=/user_center_info.html';
        }