本文目录导读:

我将为你详细介绍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. **性能**:合理使用缓存和索引
根据你的具体需求选择合适的算法方案,对于小型项目,简单平均分就足够了;对于大型平台,建议使用贝叶斯或威尔逊区间算法。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播