新增掉线自动结算命令并修复跨天日志归零问题

- 新建 CloseStaleDutyLogs 命令:每 15 分钟扫描无心跳开放日志自动关闭
- 注册调度 duty:close-stale-logs everyFifteenMinutes
- 修复 closeDutyLog:跨天遗留日志保留 duration_seconds,不再硬归零
This commit is contained in:
2026-03-17 20:27:04 +08:00
parent ef9a8ed0b6
commit 7804adc54a
3 changed files with 69 additions and 3 deletions

View 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;
}
}

View File

@@ -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'),
]);
}

View File

@@ -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后失效 + 戒指消失 + 广播通知)