商城产品浏览记录,看了又看一种实现思路

具体需求:
每一个用户浏览的产品做记录,
例如 A 用户看了产品 a,b,c 三个,B用户看了a,m,c 三个
那么a产品的看了又看列表显示为:
c,b,m 即 看a 产品的更多的会看 c产品,其次 b产品 和 m产品

实现思路(很直白的实现思路,希望抛砖引玉):
(实现的是实实在在的浏览关联显示,暂不考虑广告竞价,虚假排名的情况)
首先浏览记录不需要做成实时,且实时会有很多并发问题。那么就只需要记录每个用户的浏览记录,最终找个时间点统计即可。
但是又存在浏览权重的问题,例如 不同用户浏览a产品都会去看c 那么c的权重高。
那么产品的关联性还要有权重的标识。

那么数据库设计就存在多种方式,我采用的设计方式是:
表一:product_view_history
这里写图片描述

id为自增id
u_id是每个用户的唯一标识
value是每个用户浏览产品记录
create_at是记录添加时间
calcu_at是统计时间(打算使用计划任务,到点统计此时需要统计的数据)

表二:product_view_relate
这里写图片描述

id为自增id
p_id是产品id
value是产品关联产品以及权重(格式: 产品id_权重)

当用户浏览一个商品的时候 在表product_view_history找到此用户,有则更新数据,无则添加数据
u_id是用户的唯一标识,可以是 session_id 也可以为每个用户唯一生成,app可以采用token等标识。
注意这里判断此用户是否已经记录过了,不仅仅是根据 u_id 的存在还有 calcu_at 是同一个统计时间。

记录浏览记录 php 伪代码:

function calcuAt($now = ''){
    if($now == ''){
        $now = date();
    }
    $now = strtotime($now);
    // $this->time_period  统计的时间间隔(单位是小时)
    $this->time_period = $this->time_period == 0 ? 1:$this->time_period;
    $ymd = date('Y-m-d',$now);
    $h = date('H',$now);
    $calcu_h = ((int)($h / $this->time_period)) * $this->time_period + $this->time_period;
    $calcu_h = $calcu_h > 24 ? 24 : $calcu_h;
    $calcu_date_to_time = strtotime($ymd) + $calcu_h * 3600;
    $calcu_date = date('Y-m-d H:i:s',$calcu_date_to_time);
    return $calcu_date;
}

function recordProductViewRelate($product){
   $uid = $user->getUid();

   $now = date('Y-m-d H:i:s');
   $calcu_at = $this->calcuAt($now);

   $pvhistory = getModel('product_view_history');
   $pvhistory_data = $pvhistory->getCollection()
       ->addFieldToFilter('u_id',$uid)
       ->addFieldToFilter('calcu_at',$calcu_at)
       ->getFirstItem()
   ;

   if(@$pvhistory_data->getId()){
       $view_ids = explode(',',$pvhistory_data->getValue());
       if(!in_array($product->getId(),$view_ids)){
           // 最多记录 10个相关联浏览记录
           if(count($view_ids) >= 10){
               array_shift($view_ids);
           }
           $view_ids[] = $product->getId();
           $view_ids_str = implode(',',$view_ids);
           $pvhistory_data->setValue($view_ids_str)->save();
       }
   }else{
       $sd = array(
           'u_id' => $uid,
           'value' => $product->getId(),
           'create_at' => $now,
           'calcu_at' => $calcu_at,
       );
       $pvhistory->setData($sd)->save();
   }

   return 1;

}

然后再写一个计划任务,每次去统计这些数据即可。
例如 统计时间间隔是 4小时,那么 0,4,8,12,16,20 点分别会统计一次

计划任务伪代码:

<?php 
// 排序函数
function cmp($a,$b){
    list($a1,$a2) = explode('_',$a);
    list($b1,$b2) = explode('_',$b);
    if ($a2 == $b2) {
        return 0;
    }
    return ($a2 < $b2) ? 1 : -1;
}

$time_period = $this->time_period;
$now_time = time();
$ymd = date('Y-m-d',$now_time);
$h = date('H',$now_time);
if($h % $time_period != 0){
    // 没到启动时间
    exit;
}

$h_start = ((int)($h / $time_period)) * $time_period;
$h_end = ((int)($h / $time_period)) * $time_period + $time_period;
$h_end = $h_end > 24 ? 24 : $h_end;

$period_start = strtotime($ymd) + $h_start * 3600;
$period_start = date('Y-m-d H:i:s',$period_start);
$period_end = strtotime($ymd) + $h_end * 3600;
$period_end = date('Y-m-d H:i:s',$period_end);

$pvhistory = getModel('product_view_history');
$history_array = $pvhistory->getCollection()
    ->addFieldToFilter("calcu_at > $period_start")
    ->addFieldToFilter("calcu_at <= $period_end")
;

if(count($history_array) <= 0){
    // 无数据
    exit;
}

//todo 整理此时间段内所有用户的浏览记录到
$data = array();
foreach($history_array as $history){
    $history_line_array = explode(',',$history['value']);
    $history_line_tmp_array = explode(',',$history['value']);
    foreach($history_line_array as $history_item){
        reset($history_line_tmp_array);
        foreach($history_line_tmp_array as $history_item_tmp){
            if($history_item == $history_item_tmp){
                continue;
            }
            if(is_array(@$data[$history_item]) && array_key_exists($history_item_tmp,@$data[$history_item])){
                list($p_id,$p_weight) = explode('_',$data[$history_item][$history_item_tmp]);
                $data[$history_item][$history_item_tmp] = $p_id.'_'.(string)((int)$p_weight + 1);
            }else{
                // 最多记录 10个相关联浏览记录
                if(count(@$data[$history_item]) >= 10){
                    continue;
                }else{
                    $data[$history_item][$history_item_tmp] = $history_item_tmp.'_1';
                }
            }
        }
    }

}

// todo 保存 data 到表 product_view_relate
$pvrelate = getModel('product_view_relate');
foreach($data as $pid => $v_arr){
    $v_arr = array_slice($v_arr,0,10,true);  // 截取数组前10个 保留键值
    $pvrelate_data = $pvrelate->getCollection()->addFieldToFilter('p_id',$pid)->getFirstItem();

    if(@$pvrelate_data->getId()){

        $v_handle_arr = array();
        foreach($v_arr as $v1 => $v2){
            list($v2k,$v2v) = explode('_',$v2);
            $v_handle_arr[$v1] = $v2v;
        }

        if($pvrelate_data->getValue() == ''){
            $original_relate_arr = array();
        }else{
            $original_relate_data_arr = explode(',',$pvrelate_data->getValue());
            $original_relate_arr = array();
            foreach($original_relate_data_arr as $original_relate_data){
                list($op_id,$op_weight) = explode('_',$original_relate_data);
                $original_relate_arr[$op_id] = $op_weight;
                //$original_relate_arr[$op_id] = $original_relate_data;
            }
            foreach($original_relate_arr as $relate_pid => $relate_weight){
                if(array_key_exists($relate_pid,$v_handle_arr)){
                    $v_handle_arr[$relate_pid] = $v_handle_arr[$relate_pid] + $relate_weight;
                    unset($original_relate_arr[$relate_pid]);
                }
            }
        }


        $arr_final_tmp = $v_handle_arr + $original_relate_arr;
        $arr_final = array();
        foreach($arr_final_tmp as $arr_final_tmp_k => $arr_final_tmp_v){
            $arr_final[$arr_final_tmp_k] = $arr_final_tmp_k.'_'.$arr_final_tmp_v;
        }
        uasort($arr_final, 'cmp');
        $arr_final = array_slice($arr_final,0,10,true);  // 截取数组前10个 保留键值

        $value = implode(',',$arr_final);
        $pvrelate_data->setValue($value)->save();

    }else{
        uasort($v_arr, 'cmp');
        $value = implode(',',$v_arr);
        $sd = array(
            'p_id' => $pid,
            'value' => $value
        );
        $pvrelate->setData($sd)->save();
    }
}

最后查看每个产品的时候 异步取出关联产品数据即可。

展开阅读全文
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值