本文目录导读:

我来详细介绍PHP实现防沉迷系统的几种方案:
基础实名认证 + 时间控制
数据库设计
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
id_card VARCHAR(18) NOT NULL, -- 身份证号
real_name VARCHAR(50) NOT NULL,
birthday DATE NOT NULL, -- 出生日期
status TINYINT DEFAULT 1, -- 1:正常 0:冻结
created_at TIMESTAMP
);
CREATE TABLE play_records (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
play_date DATE NOT NULL,
start_time DATETIME,
end_time DATETIME,
duration INT DEFAULT 0, -- 累计时长(分钟)
FOREIGN KEY (user_id) REFERENCES users(id)
);
核心防沉迷逻辑
年龄验证类
<?php
class AntiAddiction {
private $db;
public function __construct($db) {
$this->db = $db;
}
/**
* 根据身份证号计算年龄
*/
public function calculateAge($idCard) {
// 获取出生日期 (身份证第7-14位)
$birthday = substr($idCard, 6, 8);
$birthYear = substr($birthday, 0, 4);
$birthMonth = substr($birthday, 4, 2);
$birthDay = substr($birthday, 6, 2);
$birthDate = new DateTime("$birthYear-$birthMonth-$birthDay");
$now = new DateTime();
$age = $now->diff($birthDate)->y;
return $age;
}
/**
* 获取防沉迷策略
*/
public function getPolicy($age) {
if ($age < 8) {
return [
'allowed' => false,
'daily_limit' => 0, // 不允许游戏
'night_ban' => null, // 完全禁止
'message' => '未满8周岁用户无法使用游戏服务'
];
} elseif ($age < 16) {
return [
'allowed' => true,
'daily_limit' => 60, // 每天1小时
'night_ban' => ['22:00', '08:00'], // 晚上10点至早8点禁止
'message' => '未成年人需合理安排游戏时间'
];
} elseif ($age < 18) {
return [
'allowed' => true,
'daily_limit' => 120, // 每天2小时
'night_ban' => ['22:00', '08:00'],
'message' => '未成年人需合理安排游戏时间'
];
} elseif ($age >= 18) {
return [
'allowed' => true,
'daily_limit' => null, // 成年人不限制
'night_ban' => null,
'message' => '成年人请适度游戏'
];
}
}
/**
* 检查游戏状态
*/
public function checkPlayStatus($userId) {
// 获取用户信息
$user = $this->getUser($userId);
if (!$user) {
return ['code' => 404, 'msg' => '用户不存在'];
}
// 计算用户年龄
$age = $this->calculateAge($user['id_card']);
$policy = $this->getPolicy($age);
// 未满8岁
if (!$policy['allowed']) {
return [
'code' => 403,
'can_play' => false,
'msg' => $policy['message']
];
}
// 检查夜间限制
$now = date('H:i');
if ($policy['night_ban']) {
list($start, $end) = $policy['night_ban'];
if ($this->isNightTime($now, $start, $end)) {
return [
'code' => 403,
'can_play' => false,
'msg' => "当前时间($now)限制登录"
];
}
}
// 检查今日累计时长
$todayDuration = $this->getTodayDuration($userId);
if ($policy['daily_limit'] && $todayDuration >= $policy['daily_limit']) {
return [
'code' => 403,
'can_play' => false,
'msg' => '今日游戏时间已达上限'
];
}
return [
'code' => 200,
'can_play' => true,
'remaining_time' => $policy['daily_limit'] - $todayDuration,
'policy' => $policy
];
}
/**
* 记录游戏开始
*/
public function startPlay($userId) {
$status = $this->checkPlayStatus($userId);
if (!$status['can_play']) {
return $status;
}
$today = date('Y-m-d');
$record = $this->db->query(
"SELECT * FROM play_records WHERE user_id=$userId AND play_date='$today'"
)->fetch_assoc();
if ($record) {
// 继续昨天的记录或者创建新记录
$this->db->query(
"UPDATE play_records SET start_time=NOW() WHERE id={$record['id']}"
);
} else {
$this->db->query(
"INSERT INTO play_records (user_id, play_date, start_time)
VALUES ($userId, '$today', NOW())"
);
}
return ['code' => 200, 'msg' => '开始游戏'];
}
/**
* 记录游戏结束
*/
public function endPlay($userId) {
$today = date('Y-m-d');
$record = $this->db->query(
"SELECT * FROM play_records
WHERE user_id=$userId AND play_date='$today' AND end_time IS NULL
ORDER BY id DESC LIMIT 1"
)->fetch_assoc();
if ($record) {
$endTime = time();
$startTime = strtotime($record['start_time']);
$duration = ($endTime - $startTime) / 60; // 分钟
$this->db->query(
"UPDATE play_records
SET end_time=NOW(),
duration=duration + $duration
WHERE id={$record['id']}"
);
}
return ['code' => 200, 'msg' => '游戏结束'];
}
/**
* 获取今日累计时长
*/
private function getTodayDuration($userId) {
$today = date('Y-m-d');
$result = $this->db->query(
"SELECT SUM(duration) as total
FROM play_records
WHERE user_id=$userId AND play_date='$today'"
)->fetch_assoc();
return $result['total'] ?? 0;
}
/**
* 判断是否处于夜间禁止时段
*/
private function isNightTime($now, $start, $end) {
$now = strtotime($now);
$start = strtotime($start);
$end = strtotime($end);
// 如果结束时间小于开始时间,说明跨天
if ($end < $start) {
return ($now >= $start || $now <= $end);
}
return ($now >= $start && $now <= $end);
}
}
调用示例
// 使用示例
$db = new mysqli('localhost', 'username', 'password', 'database');
$anti = new AntiAddiction($db);
// 检查游戏状态
$result = $anti->checkPlayStatus(1);
if ($result['can_play']) {
echo "可以开始游戏,剩余时间:" . $result['remaining_time'] . "分钟";
$anti->startPlay(1);
} else {
echo $result['msg'];
exit;
}
// 游戏结束
$anti->endPlay(1);
API接口设计
<?php
// api.php
header('Content-Type: application/json');
$action = $_GET['action'] ?? '';
$userId = (int)($_POST['user_id'] ?? 0);
$anti = new AntiAddiction($db);
switch ($action) {
case 'check':
$result = $anti->checkPlayStatus($userId);
break;
case 'start':
$result = $anti->startPlay($userId);
break;
case 'end':
$result = $anti->endPlay($userId);
break;
default:
$result = ['code' => 400, 'msg' => 'Invalid action'];
}
echo json_encode($result);
高级功能补充
跨平台时长累计
// 使用session或redis存储跨端数据
class CrossPlatformCounter {
private $redis;
public function addTime($userId, $platform, $duration) {
$key = "user:{$userId}:playtime:".date('Y-m-d');
$this->redis->incrBy($key, $duration);
$this->redis->expire($key, 24*3600);
}
public function getTotalTime($userId) {
// 从各个平台汇总
return $this->redis->get("user:{$userId}:playtime:".date('Y-m-d'));
}
}
定时任务清理
// cron.php - 每天凌晨执行 $sql = "DELETE FROM play_records WHERE play_date < DATE_SUB(NOW(), INTERVAL 7 DAY)"; $db->query($sql);
前端配合
// 前端定时检查
setInterval(async () => {
const response = await fetch('/api.php?action=check', {
method: 'POST',
body: new URLSearchParams({ user_id: userId })
});
const data = await response.json();
if (!data.can_play) {
alert(data.msg);
// 强制退出游戏
location.href = '/logout.php';
}
}, 60000); // 每分钟检查一次
注意事项
- 安全验证:身份证号需加密存储,使用AES加密
- 防绕过:服务端强制校验,不仅依赖前端
- 实名制:接入第三方实名认证接口
- 日志记录:记录所有登录和登出行为
- 异常处理:处理时区问题、跨天计算
- 性能优化:使用Redis缓存高频查询
这个方案涵盖了基本的防沉迷需求,实际生产环境还需根据具体业务场景进行调整优化。