商城根据用户购物车商品推荐与购物车商品相关的商品(猜你喜欢)

思路来源

https://help.aliyun.com/document_detail/26367.html?spm=a2c4g.11174283.6.624.3e95dce0D4jdTV

实现步骤

  • 下单时触发监听事件,把订单中一起购买的商品存入redis中
    • 为每个商品创建一个zset有序集合,成员为相关的商品,score为同时被购买的次数
  • 洗数据
    • 把之前的订单中的商品以脚本的形式跑一遍,把出现过一起购买的商品相关性记入redis中
  • 接口推荐
    • 根据购物车中的商品, 在redis中取出相关的商品ids 并排序
    • 排序规则
      • 多次出现的商品进行顶置 ,出现最多次数的商品放在'你可能喜欢的'列表的第一位
      • 只出现一次的商品,按每组商品相关性score进行排序 , 每组的第一位拿出来放在最上面,以此类推

代码实现

  • 相关性逻辑类(以Magento中实现的代码为例),只做参考,提供思路,代码可以不看
<?php

class Fun_Catalog_Helper_ProductRelation extends Mage_Core_Helper_Abstract
{
    const PRODUCT_RALATION_KEY_PREFIX = 'product:relation:';
    /**
     * 保存一个quote里的商品相关性
     * @param Fun_sales_quote $quote
     * @return bool
     */
    public function saveProductRelation($quote)
    {
        try{
            $data = $quote -> getAllItems();

            foreach($data as $v){
                $productId = $v->getProductId();
                //如果是简单商品取它的配置商品 Id
                $product = Mage::getModel('catalog/product')->load($productId);
                if($product->getTypeId() == 'simple'){
                    $parentIds = Mage::getResourceSingleton('catalog/product_type_configurable')->getParentIdsByChildFast($productId);
                }
                $productId = $parentIds[0] ? $parentIds[0] : $productId;
                $productIds[] = $productId;
            }
            //给每个商品创建redis 有序集合
            $redis = Mage::helper('redis')->getRedis(9);
            //每次有两个或者两个以上的商品时, 给每个商品的分数增加 1
            // Mage::log($productIds,null,'productRelation.log');
            if(count($productIds) > 1){
                foreach($productIds as $key=>$productId)
                {
                    foreach($productIds as $k => $memberId){
                        if($productId == $memberId){
                            continue;
                        }
                        //成员为每个商品同时添加购物车的商品, 分数为同时添加到购物车的次数
                        if($redis->ZRANK(SELF::PRODUCT_RALATION_KEY_PREFIX.$productId,$memberId) !== FALSE){
                            //已存在, 相关性分数 +1
                            $redis->ZINCRBY(SELF::PRODUCT_RALATION_KEY_PREFIX.$productId,1,$memberId);
                        }else{
                            //不存在, 创建相关性 member
                            $redis->ZADD(SELF::PRODUCT_RALATION_KEY_PREFIX.$productId,1,$memberId);
                        }
                    }
                }
            }

            return TRUE;
        }catch(Exception $e){
            Mage::Log($e->getMessage(),null,'system.log');
            return FALSE;
        }
    }

    /**
     * 从redis中取出与此商品列表相关的商品列表
     * @param array $productIds
     * @param int $page_index
     * @param int $page_size 
     * @param bool $paging 是否分页
     * @return array
     */
    public function getRelationProductList($productIds,$page_index = 1,$page_size = 20,$paging = false)
    {   
        $redis = Mage::helper('redis')->getRedis(9);

        try{
            if(count($productIds)>0)
            {
                $DList = [];
                $DDList = [];
                foreach($productIds as $productId)
                {
                    //一维数组product_id列表,用来取多次出现的相关商品
                    $one = $redis->ZREVRANGE(SELF::PRODUCT_RALATION_KEY_PREFIX.$productId,0,100);
                    $DList = array_merge($DList,$one);
                    //二维数组product_id列表, 用来取以score为排序的相关商品
                    $oneWithScores = $redis->ZREVRANGE(SELF::PRODUCT_RALATION_KEY_PREFIX.$productId,0,100,WITHSCORES);
                    $DDList[] = $oneWithScores;
                }
                //出现次数排序
                //先排序多次出现的相关商品 这些要顶置
                $productCountList = array_count_values($DList);
                foreach($productCountList as $k=>$v){
                    if($v<2){
                        unset($productCountList[$k]);
                    }
                }
                arsort($productCountList);
                // Mage::log($productCountList,null,'productRelation.log');
                
                //得分排序
                //再按各个商品的的相关性商品的得分排序
                //每个商品的相关商品列表里得分最高的放在前面 ,以此类推
                foreach($DDList as $key=>$value)
                {
                    $i = 0;
                    foreach($value as $k=>$v)
                    {
                        $temp[$i][] = $k;
                        $i++;
                    }
                }
                $scoreList = [];
                foreach($temp as $v){
                    $scoreList = array_merge($scoreList,$v);
                }
                $scoreList = array_flip($scoreList);
                // Mage::log($scoreList,null,'productRelation.log');

                //合并排序, 出现次数列表顶置, 得分列表随后 , 前面已有的 product_id会覆盖后面出现的 product_id 保证 product_id 不重复
                $finalList = $productCountList + $scoreList;
                $finalProductIds = array_keys($finalList);
                // Mage::log($finalProductIds,null,'productRelation.log');

                //根据数组特性分页
                if($paging){
                    $finalProductIds = array_slice($finalProductIds,$page_size*($page_index-1),$page_size);
                }
                
                //过滤当前购物车里存在的商品
                foreach($finalProductIds as $k=>$id){
                    if(in_array($id,$productIds)){
                        unset($finalProductIds[$K]);
                    }
                }

                return $finalProductIds;
            }
        }catch(Exception $e){
            throw new Exception($e->getMessage());
        }
    }
}
  • 前端接口实现
<?php 
/**
     * 支付或COD验证成功页面和购物车页面加入推荐商品流 (按照 redis里的商品相关性有针对性的真·推荐流)
     * @param int $page_index
     * @param int $page_size
     * @return type josn 
     */
    public function recommendByOthersBoughtAction()
    {
        $page_size  = $this->getRequest()->getParam('page_size', 20);
        $page_index = $this->getRequest()->getParam('page_index', 1);
        $product_ids = $this->getRequest()->getParam('product_ids');
        $resource = $this->getRequest()->getParam('resource');

        $productIds = json_decode($product_ids,true);
        if (empty($productIds)){
            return parent::recommendByOthersBoughtAction();
        }

        $cacheTags = array('product_recommend_by_others_bought');
        $cacheKey  = $this->getCacheKey() . '_recommend_by_others_bought' . '_' . $page_index  . '_' . $page_size . '_' . $resource . '_' .$product_ids;
        $productsData = unserialize($this->loadCache($cacheKey));

        if (!$productsData) {
            //从redis中取当前商品相关的商品
            $relationProductIds = Mage::helper('fun_catalog/productRelation')->getRelationProductList($productIds,$page_index,$page_size,true);
            $relationProducts = [];
            foreach($relationProductIds as $relationProductId){
                $product = Mage::helper('elasticsearch/product')->getByProductId($relationProductId);
                $temp = Mage::helper('elasticsearch')->getListProductDetailLite($product['data'][0]);
                $relationProducts[] = $temp;
            }

            $this->saveCache(serialize($relationProductList), $cacheKey, $cacheTags);
        }

        return $this->toSuccessJsonWithData($relationProducts);
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容