PHP推荐权重算法以及分页

    1. 如果用户浏览的第一页,则清空浏览历史
    1. 每次加载完数据,都保存到read_log, 方便下一页数据排重
  • 数据权重分析:
    • 1获取文章高点赞,高评论的50%数量的文章
    • 2获取用户有点赞过作者的文章,那就推荐该作者的最新文章,拿30%
    • 其余随机文章20%
    • 如果1和2点还不够limit的文章数量,那就再随机补足文章数量
  • todo 以下代码还待优化,只是先提供思路
php 复制代码
<?php
// 1. 查询用户最近交互过的文章相关作者(点赞/评论记录)
use app\common\service\HelperService;
use think\facade\Db;

// 初始化变量
$interacted_authors = [];

// 用户参数设置
$user_id = 1; // 当前用户ID
$page_type = 0; // 推荐页面类型
$filter_conditions = []; // 查询过滤条件
$page_number = input('page', 1); // 页码
$page_size = input('limit', 10); // 每页记录数
$random_seed = '11'; // 随机数种子,用于随机推荐的一致性

// 当访问第一页时,重置用户阅读记录
if ($page_number == 1) {
    // 清除该用户在此类型页面的阅读记录
    Db::name('user_article_read_log')->where('type', $page_type)->where('user_id', $user_id)->delete();
}

// 获取用户最近点赞过的文章作者
$liked_authors = Db::name('articles_like')
    ->alias('al')
    ->join('articles a', 'a.id = al.article_id')
    ->where('al.user_id', $user_id)
    ->where($filter_conditions)
    ->whereTime('al.create_time', '-30 days') // 最近30天内
    ->column('a.user_id');

// 获取用户最近评论过的文章作者
$commented_authors = DB::name('articles_comment')
    ->alias('ac')
    ->join('articles a', 'a.id = ac.article_id')
    ->where($filter_conditions)
    ->where('ac.user_id', $user_id)
    ->whereTime('ac.create_time', '-30 days') // 最近30天内
    ->column('a.user_id');

// 合并并去重用户交互过的作者ID
$interacted_authors = HelperService::arrayFilterUnique(array_merge($liked_authors, $commented_authors));

// 2. 构建推荐查询 - 使用子查询和UNION方式组合多种推荐策略

// 策略1: 获取热门文章(高点赞、高评论)- 占50%权重
$hot_article_limit = ceil($page_size * 0.5);
$hot_article_query = DB::name('articles')
    ->alias('a')
    ->join('user_article_read_log rl', 'rl.article_id = a.id and rl.user_id='.$user_id, 'left')
    ->where('a.status', 1)
    ->whereNull('rl.id') // 排除已阅读的文章
    ->where($filter_conditions)
    ->where('a.create_time', '>', time() - 7 * 24 * 3600) // 最近7天内
    ->order('a.like_count DESC, a.comment_count DESC, a.create_time DESC')
    ->field('a.*')
    ->limit($hot_article_limit)
    ->buildSql();

// 策略2: 如果用户有交互历史,则获取相关作者的最新文章 - 占30%权重
$related_article_query = '';
$related_article_limit = ceil($page_size * 0.3);
if (!empty($interacted_authors)) {
    $related_article_query = DB::name('articles')
        ->alias('a')
        ->join('user_article_read_log rl', 'rl.article_id = a.id and rl.user_id='.$user_id, 'left')
        ->where($filter_conditions)
        ->whereNull('rl.id') // 排除已阅读的文章
        ->where('a.status', 1)
        ->whereIn('a.user_id', $interacted_authors)
        ->whereTime('a.create_time', '-15 days') // 最近15天
        ->order('a.create_time DESC')
        ->limit($related_article_limit)
        ->field('a.*')
        ->buildSql();
}

// 策略3: 随机推荐文章以增加多样性 - 占20%权重或在无交互历史时占50%
$random_article_limit = empty($interacted_authors) ? ceil($page_size * 0.5) : ceil($page_size * 0.2);
$random_article_base_query = DB::name('articles')
    ->alias('a')
    ->join('user_article_read_log rl', 'rl.article_id = a.id and rl.user_id='.$user_id, 'left')
    ->whereNull('rl.id') // 排除已阅读的文章
    ->where($filter_conditions)
    ->where('a.status', 1)
    ->whereTime('a.create_time', '-30 days') // 最近30天
    ->orderRaw('RAND('.$random_seed.')')
    ->field('a.*');

// 构建随机文章的SQL
$random_article_sql = $random_article_base_query->limit($random_article_limit)->buildSql();

// 组合所有查询
$union_queries = array_filter([$hot_article_query, $related_article_query, $random_article_sql]);
$combined_sql = implode(' UNION ', $union_queries);
$combined_sql = '(' . $combined_sql . ')';

// 执行合并查询
$recommended_articles = Db::table($combined_sql . ' a')
    ->select()->toArray();

// 如果推荐结果不足,补充随机文章
if (count($recommended_articles) < $page_size) {
    $additional_articles = $random_article_base_query
        ->whereNotIn('a.id', array_column($recommended_articles, 'id'))
        ->limit($page_size - count($recommended_articles))
        ->select()
        ->toArray();
    
    $recommended_articles = array_values(array_filter(array_merge(
        $recommended_articles ?: [],
        $additional_articles ?: []
    )));
}

// 设置返回数据
$article_list = $recommended_articles;
$total_count = Db::name('articles')->count();

// 记录用户阅读日志,用于下次推荐时排除已读内容
$read_logs = [];
foreach ($article_list as $article) {
    $read_logs[] = [
        'user_id' => $user_id,
        'article_id' => $article['id'],
        'type' => $page_type
    ];
}

// 批量插入阅读记录
if (!empty($read_logs)) {
    Db::name('user_article_read_log')->insertAll($read_logs);
}
相关推荐
wangjialelele9 分钟前
平衡二叉搜索树:AVL树和红黑树
java·c语言·开发语言·数据结构·c++·算法·深度优先
驱动探索者26 分钟前
linux mailbox 学习
linux·学习·算法
ringking12329 分钟前
autoware-1:安装环境cuda/cudnn/tensorRT库函数的判断
人工智能·算法·机器学习
大闲在人1 小时前
8. 供应链与制造过程术语:产能
算法·制造·供应链管理·智能制造·工业工程
一只小小的芙厨1 小时前
寒假集训笔记·以点为对象的树形DP
c++·算法
历程里程碑1 小时前
普通数组----合并区间
java·数据结构·python·算法·leetcode·职场和发展·tornado
执风挽^1 小时前
Python基础编程题2
开发语言·python·算法·visual studio code
Z9fish2 小时前
sse哈工大C语言编程练习20
c语言·开发语言·算法
晓13132 小时前
第六章 【C语言篇:结构体&位运算】 结构体、位运算全面解析
c语言·算法
iAkuya2 小时前
(leetcode)力扣100 61分割回文串(回溯,动归)
算法·leetcode·职场和发展