本文目录导读:

我将为你详细介绍PHP项目中实现评分与排名功能的几种常见方案。
基础评分系统实现
数据库表设计
-- 评分表
CREATE TABLE ratings (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
project_id INT NOT NULL,
score DECIMAL(3,1) NOT NULL, -- 1.0-5.0
comment TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY unique_rating (user_id, project_id),
INDEX idx_project_score (project_id, score)
);
-- 项目表(添加评分统计字段)
CREATE TABLE projects (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
avg_score DECIMAL(3,1) DEFAULT 0.0,
rating_count INT DEFAULT 0,
-- 其他项目字段...
);
PHP评分操作类
<?php
class RatingSystem {
private $db;
public function __construct(PDO $db) {
$this->db = $db;
}
/**
* 提交评分
*/
public function submitRating(int $userId, int $projectId, float $score, string $comment = ''): bool {
try {
$this->db->beginTransaction();
// 检查是否已评分
$existing = $this->getUserRating($userId, $projectId);
if ($existing) {
// 更新评分
$stmt = $this->db->prepare("
UPDATE ratings
SET score = :score, comment = :comment
WHERE user_id = :user_id AND project_id = :project_id
");
} else {
// 新增评分
$stmt = $this->db->prepare("
INSERT INTO ratings (user_id, project_id, score, comment)
VALUES (:user_id, :project_id, :score, :comment)
");
}
$stmt->execute([
'user_id' => $userId,
'project_id' => $projectId,
'score' => $score,
'comment' => $comment
]);
// 更新项目评分统计
$this->updateProjectStats($projectId);
$this->db->commit();
return true;
} catch (Exception $e) {
$this->db->rollBack();
error_log("Rating submission failed: " . $e->getMessage());
return false;
}
}
/**
* 获取用户评分
*/
public function getUserRating(int $userId, int $projectId): ?array {
$stmt = $this->db->prepare("
SELECT * FROM ratings
WHERE user_id = :user_id AND project_id = :project_id
");
$stmt->execute(['user_id' => $userId, 'project_id' => $projectId]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
}
/**
* 更新项目评分统计
*/
private function updateProjectStats(int $projectId): void {
$this->db->prepare("
UPDATE projects p
SET
avg_score = (
SELECT ROUND(AVG(score), 1)
FROM ratings
WHERE project_id = :project_id
),
rating_count = (
SELECT COUNT(*)
FROM ratings
WHERE project_id = :project_id
)
WHERE p.id = :project_id
")->execute(['project_id' => $projectId]);
}
}
Rank排名算法实现
简单排名(基于平均分)
<?php
class RankingSystem {
private $db;
public function __construct(PDO $db) {
$this->db = $db;
}
/**
* 获取项目排名列表(基于平均分)
*/
public function getRankingByAverage(int $limit = 10, int $offset = 0): array {
$stmt = $this->db->prepare("
SELECT
p.*,
p.avg_score,
p.rating_count,
@rank := @rank + 1 as rank
FROM projects p
CROSS JOIN (SELECT @rank := 0) r
WHERE p.rating_count > 0
ORDER BY p.avg_score DESC, p.rating_count DESC
LIMIT :limit OFFSET :offset
");
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
贝叶斯平均排名(推荐算法)
<?php
class BayesianRanking {
// 贝叶斯平均公式: (avg_all * m + sum_score) / (m + n)
// m = 最小评分次数阈值
// n = 该项目评分次数
public function getBayesianRanking(int $m = 10, int $limit = 10): array {
global $db;
// 计算所有项目的平均评分
$stmt = $db->query("SELECT AVG(avg_score) as avg_all FROM projects WHERE rating_count > 0");
$avgAll = $stmt->fetchColumn();
$stmt = $db->prepare("
SELECT
p.*,
(($avgAll * :m) + (p.avg_score * p.rating_count)) / (:m + p.rating_count) as bayesian_score
FROM projects p
WHERE p.rating_count > 0
ORDER BY bayesian_score DESC
LIMIT :limit
");
$stmt->bindValue(':m', $m, PDO::PARAM_INT);
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 添加排名
$rank = 0;
foreach ($results as &$row) {
$row['rank'] = ++$rank;
}
return $results;
}
}
威尔逊区间评分(更高级)
<?php
class WilsonScoreRanking {
/**
* 计算威尔逊下限(用于排序)
* 适合处理评分数据不确定性
*/
public function getWilsonScoreRanking(int $limit = 10): array {
global $db;
// 置信度z值 (95% CI -> 1.96)
$z = 1.96;
$stmt = $db->prepare("
SELECT
p.*,
(
(p.avg_score / 5.0 + $z * $z / (2 * p.rating_count)
- $z * SQRT((p.avg_score / 5.0 * (1 - p.avg_score / 5.0) + $z * $z / (4 * p.rating_count)) / p.rating_count)
) / (1 + $z * $z / p.rating_count)
) as wilson_score
FROM projects p
WHERE p.rating_count > 0
ORDER BY wilson_score DESC
LIMIT :limit
");
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
带权重的多维度评分
<?php
class MultiDimensionRating {
private $weights = [
'performance' => 0.3,
'usability' => 0.4,
'support' => 0.2,
'cost' => 0.1
];
public function calculateOverallScore(array $dimensionScores): float {
$total = 0;
foreach ($this->weights as $dimension => $weight) {
if (isset($dimensionScores[$dimension])) {
$total += $dimensionScores[$dimension] * $weight;
}
}
return round($total, 1);
}
}
性能优化建议
缓存层实现
<?php
class CachedRanking {
private $cache;
private $rankingSystem;
public function __construct($cache, $rankingSystem) {
$this->cache = $cache;
$this->rankingSystem = $rankingSystem;
}
public function getRanking(int $limit = 10): array {
$cacheKey = "ranking:top:{$limit}";
// 尝试从缓存获取
$cached = $this->cache->get($cacheKey);
if ($cached !== null) {
return json_decode($cached, true);
}
// 从数据库获取
$ranking = $this->rankingSystem->getBayesianRanking($limit);
// 缓存5分钟
$this->cache->set($cacheKey, json_encode($ranking), 300);
return $ranking;
}
}
索引优化
-- 关键索引 ALTER TABLE ratings ADD INDEX idx_user_project (user_id, project_id); ALTER TABLE ratings ADD INDEX idx_project_score (project_id, score); ALTER TABLE projects ADD INDEX idx_avg_score (avg_score DESC, rating_count DESC);
前端展示示例
<!-- 星级评分组件 -->
<div class="star-rating" data-project-id="1">
<div class="stars">
<?php for ($i = 1; $i <= 5; $i++): ?>
<span class="star" data-value="<?= $i ?>">★</span>
<?php endfor; ?>
</div>
<div class="rating-info">
<span class="average">4.5</span>
<span class="count">(128 评分)</span>
</div>
</div>
<script>
document.querySelectorAll('.star').forEach(star => {
star.addEventListener('click', function() {
const projectId = this.closest('.star-rating').dataset.projectId;
const score = this.dataset.value;
// AJAX提交评分
fetch('/api/submit-rating', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({project_id: projectId, score: score})
});
});
});
</script>
API接口示例
<?php
// api/ratings.php
header('Content-Type: application/json');
$ratingSystem = new RatingSystem($db);
$action = $_GET['action'] ?? '';
$userId = $_SESSION['user_id'] ?? 0;
switch ($action) {
case 'submit':
$projectId = (int)$_POST['project_id'];
$score = (float)$_POST['score'];
$result = $ratingSystem->submitRating($userId, $projectId, $score);
echo json_encode(['success' => $result]);
break;
case 'ranking':
$limit = (int)($_GET['limit'] ?? 10);
$ranking = (new BayesianRanking($db))->getBayesianRanking(10, $limit);
echo json_encode($ranking);
break;
}
## 最佳实践总结
1. **公平性**:使用贝叶斯平均防止刷分
2. **实时性**:异步更新评分统计
3. **安全性**:防止重复评分和刷分
4. **可扩展性**:支持多维度评分
5. **性能**:合理使用缓存和索引
根据你的具体需求选择合适的算法方案,对于小型项目,简单平均分就足够了;对于大型平台,建议使用贝叶斯或威尔逊区间算法。