330 lines
11 KiB
PHP
330 lines
11 KiB
PHP
<?php
|
||||
|
|
// ============================================
|
|||
|
|
// 文件: lib/stats.php
|
|||
|
|
// 说明: 统计仪表板与年度报告的查询逻辑
|
|||
|
|
// ============================================
|
|||
|
|
|
|||
|
|
// ===== 统计 =====
|
|||
|
|
function getStatsOverview() {
|
|||
|
|
$db = getDB();
|
|||
|
|
$stats = [];
|
|||
|
|
$stmt = $db->query("SELECT COUNT(*) FROM movies");
|
|||
|
|
$stats['total_movies'] = $stmt->fetchColumn();
|
|||
|
|
$stmt = $db->query("SELECT COUNT(*) FROM cinemas");
|
|||
|
|
$stats['total_cinemas'] = $stmt->fetchColumn();
|
|||
|
|
$stmt = $db->query("SELECT COUNT(*) FROM persons");
|
|||
|
|
$stats['total_persons'] = $stmt->fetchColumn();
|
|||
|
|
$stmt = $db->query("SELECT COUNT(*) FROM viewers");
|
|||
|
|
$stats['total_viewership'] = $stmt->fetchColumn();
|
|||
|
|
$stmt = $db->query("SELECT MIN(watch_date) as first, MAX(watch_date) as last FROM movies WHERE watch_date IS NOT NULL");
|
|||
|
|
$row = $stmt->fetch();
|
|||
|
|
$stats['first_date'] = $row['first'];
|
|||
|
|
$stats['last_date'] = $row['last'];
|
|||
|
|
return $stats;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function statsByCinema() {
|
|||
|
|
$db = getDB();
|
|||
|
|
$stmt = $db->query("
|
|||
|
|
SELECT c.id, c.name, c.color, COUNT(m.id) as count
|
|||
|
|
FROM cinemas c
|
|||
|
|
LEFT JOIN movies m ON c.id = m.cinema_id
|
|||
|
|
GROUP BY c.id
|
|||
|
|
ORDER BY count DESC
|
|||
|
|
");
|
|||
|
|
return $stmt->fetchAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function statsByPerson() {
|
|||
|
|
$db = getDB();
|
|||
|
|
$stmt = $db->query("
|
|||
|
|
SELECT p.id, p.name, p.color, p.avatar, COUNT(v.movie_id) as count
|
|||
|
|
FROM persons p
|
|||
|
|
LEFT JOIN viewers v ON p.id = v.person_id
|
|||
|
|
GROUP BY p.id
|
|||
|
|
ORDER BY count DESC
|
|||
|
|
");
|
|||
|
|
return $stmt->fetchAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function statsByMonth($limit = 12) {
|
|||
|
|
$db = getDB();
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT strftime('%Y-%m', watch_date) as month, COUNT(*) as count
|
|||
|
|
FROM movies
|
|||
|
|
WHERE watch_date IS NOT NULL
|
|||
|
|
GROUP BY month
|
|||
|
|
ORDER BY month DESC
|
|||
|
|
LIMIT ?
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$limit]);
|
|||
|
|
return $stmt->fetchAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function statsByYear() {
|
|||
|
|
$db = getDB();
|
|||
|
|
$stmt = $db->query("
|
|||
|
|
SELECT strftime('%Y', watch_date) as year, COUNT(*) as count
|
|||
|
|
FROM movies
|
|||
|
|
WHERE watch_date IS NOT NULL
|
|||
|
|
GROUP BY year
|
|||
|
|
ORDER BY year DESC
|
|||
|
|
");
|
|||
|
|
return $stmt->fetchAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== 年度报告 =====
|
|||
|
|
function getAnnualStats($year) {
|
|||
|
|
$db = getDB();
|
|||
|
|
$stats = [];
|
|||
|
|
|
|||
|
|
// 获取当前管理员
|
|||
|
|
$admin = getCurrentAdmin();
|
|||
|
|
$self_person_id = $admin['person_id'] ?? null;
|
|||
|
|
$stats['self_person_name'] = $admin['person_name'] ?? null;
|
|||
|
|
$stats['self_person_avatar'] = $admin['person_avatar'] ?? null;
|
|||
|
|
|
|||
|
|
if (!$self_person_id) {
|
|||
|
|
// 未关联观影人,返回空数据
|
|||
|
|
$stats['total'] = 0;
|
|||
|
|
$stats['days'] = 0;
|
|||
|
|
$stats['top_cinema'] = '无';
|
|||
|
|
$stats['top_cinema_count'] = 0;
|
|||
|
|
$stats['top_person'] = '无';
|
|||
|
|
$stats['top_person_count'] = 0;
|
|||
|
|
$stats['first_date'] = null;
|
|||
|
|
$stats['last_date'] = null;
|
|||
|
|
$stats['most_viewers_movie'] = '无';
|
|||
|
|
$stats['most_viewers_count'] = 0;
|
|||
|
|
$stats['month_distribution'] = array_fill(1, 12, 0);
|
|||
|
|
$stats['weekday_distribution'] = ['日'=>0,'一'=>0,'二'=>0,'三'=>0,'四'=>0,'五'=>0,'六'=>0];
|
|||
|
|
$stats['avg_per_month'] = 0;
|
|||
|
|
$stats['avg_days_between'] = 0;
|
|||
|
|
$stats['keywords'] = [];
|
|||
|
|
$stats['recent_movies'] = [];
|
|||
|
|
$stats['my_movies'] = [];
|
|||
|
|
$stats['common_viewers'] = [];
|
|||
|
|
return $stats;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 1. 总观影数(当前用户参与的电影)
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT COUNT(DISTINCT m.id)
|
|||
|
|
FROM movies m
|
|||
|
|
JOIN viewers v ON m.id = v.movie_id
|
|||
|
|
WHERE strftime('%Y', m.watch_date) = ?
|
|||
|
|
AND v.person_id = ?
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$year, $self_person_id]);
|
|||
|
|
$stats['total'] = (int)$stmt->fetchColumn();
|
|||
|
|
|
|||
|
|
// 2. 观影天数
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT COUNT(DISTINCT m.watch_date)
|
|||
|
|
FROM movies m
|
|||
|
|
JOIN viewers v ON m.id = v.movie_id
|
|||
|
|
WHERE strftime('%Y', m.watch_date) = ?
|
|||
|
|
AND v.person_id = ?
|
|||
|
|
AND m.watch_date IS NOT NULL
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$year, $self_person_id]);
|
|||
|
|
$stats['days'] = (int)$stmt->fetchColumn();
|
|||
|
|
|
|||
|
|
// 3. 最常去的影院
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT c.name, COUNT(m.id) as count
|
|||
|
|
FROM movies m
|
|||
|
|
JOIN viewers v ON m.id = v.movie_id
|
|||
|
|
LEFT JOIN cinemas c ON m.cinema_id = c.id
|
|||
|
|
WHERE strftime('%Y', m.watch_date) = ?
|
|||
|
|
AND v.person_id = ?
|
|||
|
|
AND m.cinema_id IS NOT NULL
|
|||
|
|
GROUP BY m.cinema_id
|
|||
|
|
ORDER BY count DESC LIMIT 1
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$year, $self_person_id]);
|
|||
|
|
$top_cinema = $stmt->fetch();
|
|||
|
|
$stats['top_cinema'] = $top_cinema ? $top_cinema['name'] : '无';
|
|||
|
|
$stats['top_cinema_count'] = $top_cinema ? (int)$top_cinema['count'] : 0;
|
|||
|
|
|
|||
|
|
// 4. 最常一起观影的人(与当前用户同场最多的人)
|
|||
|
|
// 先获取当前用户看过的所有电影ID
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT DISTINCT m.id
|
|||
|
|
FROM movies m
|
|||
|
|
JOIN viewers v ON m.id = v.movie_id
|
|||
|
|
WHERE strftime('%Y', m.watch_date) = ?
|
|||
|
|
AND v.person_id = ?
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$year, $self_person_id]);
|
|||
|
|
$movie_ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|||
|
|
|
|||
|
|
// 共同观影人统计
|
|||
|
|
$common_viewers = [];
|
|||
|
|
if (!empty($movie_ids)) {
|
|||
|
|
$placeholders = implode(',', array_fill(0, count($movie_ids), '?'));
|
|||
|
|
$params = array_merge($movie_ids, [$self_person_id]);
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT p.id, p.name, p.avatar, COUNT(*) as count
|
|||
|
|
FROM viewers v
|
|||
|
|
JOIN persons p ON v.person_id = p.id
|
|||
|
|
WHERE v.movie_id IN ($placeholders)
|
|||
|
|
AND v.person_id != ?
|
|||
|
|
GROUP BY v.person_id
|
|||
|
|
ORDER BY count DESC
|
|||
|
|
");
|
|||
|
|
$stmt->execute($params);
|
|||
|
|
$common_viewers = $stmt->fetchAll();
|
|||
|
|
}
|
|||
|
|
$stats['common_viewers'] = $common_viewers;
|
|||
|
|
|
|||
|
|
// 最常一起观影的人(取第一个)
|
|||
|
|
$stats['top_person'] = !empty($common_viewers) ? $common_viewers[0]['name'] : '无';
|
|||
|
|
$stats['top_person_count'] = !empty($common_viewers) ? (int)$common_viewers[0]['count'] : 0;
|
|||
|
|
|
|||
|
|
// 5. 最早和最晚日期
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT MIN(m.watch_date) as first, MAX(m.watch_date) as last
|
|||
|
|
FROM movies m
|
|||
|
|
JOIN viewers v ON m.id = v.movie_id
|
|||
|
|
WHERE strftime('%Y', m.watch_date) = ?
|
|||
|
|
AND v.person_id = ?
|
|||
|
|
AND m.watch_date IS NOT NULL
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$year, $self_person_id]);
|
|||
|
|
$row = $stmt->fetch();
|
|||
|
|
$stats['first_date'] = $row['first'];
|
|||
|
|
$stats['last_date'] = $row['last'];
|
|||
|
|
|
|||
|
|
// 6. 单次最多人的电影(当前用户参与的电影中)
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT m.id, m.title, COUNT(v.id) as viewer_count
|
|||
|
|
FROM movies m
|
|||
|
|
JOIN viewers v ON m.id = v.movie_id
|
|||
|
|
WHERE strftime('%Y', m.watch_date) = ?
|
|||
|
|
AND m.id IN (SELECT movie_id FROM viewers WHERE person_id = ?)
|
|||
|
|
GROUP BY m.id
|
|||
|
|
ORDER BY viewer_count DESC LIMIT 1
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$year, $self_person_id]);
|
|||
|
|
$most_viewers = $stmt->fetch();
|
|||
|
|
$stats['most_viewers_movie'] = $most_viewers ? $most_viewers['title'] : '无';
|
|||
|
|
$stats['most_viewers_count'] = $most_viewers ? (int)$most_viewers['viewer_count'] : 0;
|
|||
|
|
|
|||
|
|
// 7. 月份分布
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT strftime('%m', m.watch_date) as month, COUNT(*) as count
|
|||
|
|
FROM movies m
|
|||
|
|
JOIN viewers v ON m.id = v.movie_id
|
|||
|
|
WHERE strftime('%Y', m.watch_date) = ?
|
|||
|
|
AND v.person_id = ?
|
|||
|
|
AND m.watch_date IS NOT NULL
|
|||
|
|
GROUP BY month
|
|||
|
|
ORDER BY month
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$year, $self_person_id]);
|
|||
|
|
$month_data = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
|||
|
|
$stats['month_distribution'] = [];
|
|||
|
|
for ($m=1; $m<=12; $m++) {
|
|||
|
|
$key = str_pad($m, 2, '0', STR_PAD_LEFT);
|
|||
|
|
$stats['month_distribution'][$m] = isset($month_data[$key]) ? (int)$month_data[$key] : 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 8. 星期分布
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT strftime('%w', m.watch_date) as wday, COUNT(*) as count
|
|||
|
|
FROM movies m
|
|||
|
|
JOIN viewers v ON m.id = v.movie_id
|
|||
|
|
WHERE strftime('%Y', m.watch_date) = ?
|
|||
|
|
AND v.person_id = ?
|
|||
|
|
AND m.watch_date IS NOT NULL
|
|||
|
|
GROUP BY wday
|
|||
|
|
ORDER BY wday
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$year, $self_person_id]);
|
|||
|
|
$wday_data = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
|||
|
|
$weekdays = ['日','一','二','三','四','五','六'];
|
|||
|
|
$stats['weekday_distribution'] = [];
|
|||
|
|
foreach ($weekdays as $idx => $name) {
|
|||
|
|
$stats['weekday_distribution'][$name] = isset($wday_data[$idx]) ? (int)$wday_data[$idx] : 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 9. 月均和频率
|
|||
|
|
$stats['avg_per_month'] = $stats['total'] > 0 ? round($stats['total'] / 12, 1) : 0;
|
|||
|
|
$stats['avg_days_between'] = $stats['total'] > 0 ? round(365 / $stats['total'], 1) : 0;
|
|||
|
|
|
|||
|
|
// 10. 关键词(基于当前用户电影的备注)
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT m.remark
|
|||
|
|
FROM movies m
|
|||
|
|
JOIN viewers v ON m.id = v.movie_id
|
|||
|
|
WHERE strftime('%Y', m.watch_date) = ?
|
|||
|
|
AND v.person_id = ?
|
|||
|
|
AND m.remark IS NOT NULL
|
|||
|
|
AND m.remark != ''
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$year, $self_person_id]);
|
|||
|
|
$remarks = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|||
|
|
$word_count = [];
|
|||
|
|
foreach ($remarks as $text) {
|
|||
|
|
$words = preg_split('/[\s,,。.!!??、;;::]+/', $text, -1, PREG_SPLIT_NO_EMPTY);
|
|||
|
|
foreach ($words as $w) {
|
|||
|
|
$w = trim($w);
|
|||
|
|
if (mb_strlen($w) >= 2) {
|
|||
|
|
if (!isset($word_count[$w])) $word_count[$w] = 0;
|
|||
|
|
$word_count[$w]++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
arsort($word_count);
|
|||
|
|
$stats['keywords'] = array_slice(array_keys($word_count), 0, 5);
|
|||
|
|
|
|||
|
|
// 11. 最近5部(当前用户参与的)
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT DISTINCT m.title
|
|||
|
|
FROM movies m
|
|||
|
|
JOIN viewers v ON m.id = v.movie_id
|
|||
|
|
WHERE strftime('%Y', m.watch_date) = ?
|
|||
|
|
AND v.person_id = ?
|
|||
|
|
ORDER BY m.watch_date DESC
|
|||
|
|
LIMIT 5
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$year, $self_person_id]);
|
|||
|
|
$stats['recent_movies'] = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|||
|
|
|
|||
|
|
// 12. 我的观影记录(含海报)
|
|||
|
|
$stmt = $db->prepare("
|
|||
|
|
SELECT m.id, m.title, m.watch_date, m.poster_path
|
|||
|
|
FROM movies m
|
|||
|
|
JOIN viewers v ON m.id = v.movie_id
|
|||
|
|
WHERE strftime('%Y', m.watch_date) = ?
|
|||
|
|
AND v.person_id = ?
|
|||
|
|
GROUP BY m.id
|
|||
|
|
ORDER BY m.watch_date DESC
|
|||
|
|
");
|
|||
|
|
$stmt->execute([$year, $self_person_id]);
|
|||
|
|
$movie_list = $stmt->fetchAll();
|
|||
|
|
|
|||
|
|
// 对每个电影查询一起观影的人(含头像)
|
|||
|
|
foreach ($movie_list as &$movie) {
|
|||
|
|
$stmt2 = $db->prepare("
|
|||
|
|
SELECT p.id, p.name, p.avatar
|
|||
|
|
FROM viewers v
|
|||
|
|
LEFT JOIN persons p ON v.person_id = p.id
|
|||
|
|
WHERE v.movie_id = ?
|
|||
|
|
AND v.person_id != ?
|
|||
|
|
");
|
|||
|
|
$stmt2->execute([$movie['id'], $self_person_id]);
|
|||
|
|
$movie['viewers'] = $stmt2->fetchAll();
|
|||
|
|
}
|
|||
|
|
$stats['my_movies'] = $movie_list;
|
|||
|
|
|
|||
|
|
return $stats;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function getYearsWithData() {
|
|||
|
|
$db = getDB();
|
|||
|
|
$stmt = $db->query("SELECT DISTINCT strftime('%Y', watch_date) as year FROM movies WHERE watch_date IS NOT NULL ORDER BY year DESC");
|
|||
|
|
return $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|||
|
|
}
|