新增掉线自动结算命令并修复跨天日志归零问题
- 新建 CloseStaleDutyLogs 命令:每 15 分钟扫描无心跳开放日志自动关闭 - 注册调度 duty:close-stale-logs everyFifteenMinutes - 修复 closeDutyLog:跨天遗留日志保留 duration_seconds,不再硬归零
This commit is contained in:
63
app/Console/Commands/CloseStaleDutyLogs.php
Normal file
63
app/Console/Commands/CloseStaleDutyLogs.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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()'),
|
||||
]);
|
||||
|
||||
if ($affected > 0) {
|
||||
$this->info("共关闭 {$affected} 条掉线职务日志。");
|
||||
} else {
|
||||
$this->info('无需处理,无掉线日志。');
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -1025,14 +1025,14 @@ class ChatController extends Controller
|
||||
'duration_seconds' => DB::raw('GREATEST(0, TIMESTAMPDIFF(SECOND, login_at, NOW()))'),
|
||||
]);
|
||||
|
||||
// 清理历史遭留未关闭的日志(login_at 非今日),直接将它们的时长置 0,避免被算入任何榜单
|
||||
// 关闭历史遗留的跨天未关闭日志(login_at 非今日)
|
||||
// 保留最后一次心跳刷新的 duration_seconds,确保已积累时长不丢失
|
||||
PositionDutyLog::query()
|
||||
->where('user_id', $userId)
|
||||
->whereNull('logout_at')
|
||||
->whereDate('login_at', '<', today())
|
||||
->update([
|
||||
'logout_at' => DB::raw('login_at'), // 时长 = 0
|
||||
'duration_seconds' => 0,
|
||||
'logout_at' => DB::raw('login_at + INTERVAL duration_seconds SECOND'),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ Schedule::command('messages:purge')->dailyAt('03:00');
|
||||
// 每 5 分钟为所有在线用户自动存点(经验/金币/等级)
|
||||
Schedule::command('chatroom:auto-save-exp')->everyFiveMinutes();
|
||||
|
||||
// 每 15 分钟:关闭掉线用户的开放职务日志(久无心跳 = 掉线,自动写入 logout_at)
|
||||
Schedule::command('duty:close-stale-logs')->everyFifteenMinutes();
|
||||
|
||||
// ──────────── 婚姻系统定时任务 ────────────────────────────────────
|
||||
|
||||
// 每 5 分钟:扫描超时求婚(48h后失效 + 戒指消失 + 广播通知)
|
||||
|
||||
Reference in New Issue
Block a user