Files
chatroom/app/Console/Commands/CloseStaleDutyLogs.php

66 lines
2.1 KiB
PHP
Raw Normal View History

<?php
/**
* 文件功能:自动关闭掉线职务日志指令
*
* 15 分钟由 Laravel Scheduler 调用,扫描 position_duty_logs 表中:
* - logout_at IS NULL(尚未结算的开放日志)
* - updated_at 超过 15 分钟未刷新(说明心跳已中断,用户已掉线/关闭浏览器)
*
* 对此类日志写入 logout_at = NOW(),保留 duration_seconds 现有值不清零,
* 确保累计时长计算准确,不因掉线而永久悬空。
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Console\Commands;
use App\Models\PositionDutyLog;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class CloseStaleDutyLogs extends Command
{
/**
* Artisan 指令名称
*/
protected $signature = 'duty:close-stale-logs';
/**
* 指令描述(在 artisan list 中显示)
*/
protected $description = '自动关闭 15 分钟内无心跳的开放职务日志(解决掉线不结算问题)';
/**
* 指令入口:将长时间无心跳刷新的开放日志判定为掉线,写入 logout_at 完成结算。
*
* 判定标准updated_at 超过 15 分钟(心跳间隔约 30 5 分钟自动存点最长 5 分钟,
* 留足 10 分钟容差,总计 15 分钟无刷新即认为掉线)
*/
public function handle(): int
{
// 15 分钟无心跳 = 掉线判定阈值
$threshold = now()->subMinutes(15);
// 批量关闭符合条件的开放日志,保留现有 duration_seconds
$affected = PositionDutyLog::query()
->whereNull('logout_at')
->where('updated_at', '<=', $threshold)
->update([
'logout_at' => DB::raw('NOW()'),
// 补算最终在线时长,避免日榜 SUM 使用过时的旧值
'duration_seconds' => DB::raw('GREATEST(0, TIMESTAMPDIFF(SECOND, login_at, NOW()))'),
]);
if ($affected > 0) {
$this->info("共关闭 {$affected} 条掉线职务日志。");
} else {
$this->info('无需处理,无掉线日志。');
}
return Command::SUCCESS;
}
}