online 表,記錄用戶/游客最後活動時間<?php
// application/tags.php
return [
'action_begin' => ['app\common\behavior\OnlineCheck'],
];
<?php
// app/common/behavior/OnlineCheck.php
namespace app\common\behavior;
use think\facade\Session;
use think\facade\Config;
use think\facade\Db;
class OnlineCheck
{
protected $options = [
'ONLINE_CHECK' => true,
'ONLINE_CHECK_TIME' => 10, // 分鐘
];
public function run(&$params)
{
if (!Config::get('ONLINE_CHECK')) return;
$onlineModel = Db::name('Online');
$ipLong = ip2long(request()->ip());
$now = time();
$timeout = Config::get('ONLINE_CHECK_TIME') * 60;
// 1. 清理過期記錄
$onlineModel->where('lasttime', '<', $now - $timeout)->delete();
// 2. 會員邏輯
if (Session::has('login_account')) {
// 更新心跳(每分鐘一次)
if ($now - Session::get('access_time', 0) > 60) {
Session::set('access_time', $now);
}
$uid = Session::get('login_uid');
// 查找當前用戶在線記錄
$record = $onlineModel->where(['uid' => $uid, 'lastip' => $ipLong])->find();
if (!$record) {
// 異地登錄或記錄丟失 → 強制下線
Session::clear();
return;
}
// 更新最後活動時間
$onlineModel->where('id', $record['id'])
->update(['lasttime' => $now, 'lastip' => $ipLong]);
}
// 3. 游客邏輯
else {
$record = $onlineModel->where('lastip', $ipLong)->find();
if (!$record) {
// 新游客
$onlineModel->insert([
'uid' => 0,
'account' => 'Guest',
'nickname' => '游客',
'lasttime' => $now,
'lastip' => $ipLong,
]);
} else {
$onlineModel->where('id', $record['id'])
->update(['lasttime' => $now, 'lastip' => $ipLong]);
}
}
}
}
?>
CREATE TABLE `online` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uid` INT UNSIGNED NOT NULL DEFAULT 0,
`account` VARCHAR(50) NOT NULL DEFAULT '',
`nickname` VARCHAR(50) NOT NULL DEFAULT '',
`lasttime` INT UNSIGNED NOT NULL DEFAULT 0,
`lastip` INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_uid_ip` (`uid`, `lastip`),
KEY `idx_lasttime` (`lasttime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
<?php
// config/online.php
return [
'ONLINE_CHECK' => true,
'ONLINE_CHECK_TIME' => 10, // 分鐘
];
<?php
// 控制器
public function onlineList()
{
$list = Db::name('Online')
->where('lasttime', '>', time() - config('online.ONLINE_CHECK_TIME') * 60)
->order('lasttime DESC')
->paginate(20);
foreach ($list as &$item) {
$item['lastip'] = long2ip($item['lastip']);
$item['lasttime'] = date('Y-m-d H:i:s', $item['lasttime']);
}
return view('', ['list' => $list]);
}
?>
# crontab -e */5 * * * * /usr/bin/php /path/to/think online:clean
<?php
// application/command/Online.php
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Db;
use think\facade\Config;
class Online extends Command
{
protected function configure()
{
$this->setName('online:clean')
->setDescription('清理過期在線記錄');
}
public function execute(Input $input, Output $output)
{
$timeout = Config::get('online.ONLINE_CHECK_TIME') * 60;
$deleted = Db::name('Online')
->where('lasttime', '<', time() - $timeout)
->delete();
$output->writeln("清理過期在線記錄: {$deleted} 條");
return 0;
}
}
?>
| 場景 | 處理邏輯 |
|---|---|
| 會員心跳 | 每 60 秒更新一次 Session access_time,減少寫入 |
| 異地登錄 | online 表無記錄 → 清空 Session 強制下線 |
| 游客識別 | IP 長整型存儲,支持 IPv4 |
| 性能優化 | lasttime 索引 + 定期清理,表體積可控 |
think-worker 實現 WebSocket 實時在線狀態推送EXPIRE 自動過期,性能更強行為擴展模式讓「在線檢測」成為橫切關注點,業務代碼零耦合,是 ThinkPHP 5 實現 AOP 的經典案例。