功能:存点时自动同步在职用户勤务日志

- heartbeat 手动存点:调用 tickDutyLog()
- AutoSaveExp 自动存点:调用 tickDutyLog()
- 逻辑:今日已有开放日志则刷新 duration_seconds,无则新建(login_at 取 in_time 进房时间)
- 修复:TIMESTAMPDIFF 结果用 GREATEST(0, ...) 防 unsigned 溢出
- 修复:database.php MySQL 连接加 timezone=+08:00,与 PHP Asia/Shanghai 时区对齐
This commit is contained in:
2026-03-01 00:04:59 +08:00
parent 5f30220609
commit 76fd17c727
3 changed files with 140 additions and 33 deletions

View File

@@ -340,6 +340,9 @@ class ChatController extends Controller
$user->save(); // 存点入库
// 手动心跳存点:同步更新在职用户的勤务时长
$this->tickDutyLog($user, $id);
// 3. 将新的等级反馈给当前用户的在线名单上
// 确保刚刚升级后别人查看到的也是最准确等级
$activePosition = $user->activePosition;
@@ -820,7 +823,57 @@ class ChatController extends Controller
->whereNull('logout_at')
->update([
'logout_at' => now(),
'duration_seconds' => DB::raw('TIMESTAMPDIFF(SECOND, login_at, NOW())'),
'duration_seconds' => DB::raw('GREATEST(0, TIMESTAMPDIFF(SECOND, login_at, NOW()))'),
]);
}
/**
* 存点时同步更新或创建在职用户的勤务日志。
*
* 逻辑:
* 1. 用户无在职职务 跳过
* 2. 今日已有开放日志(无 logout_at 刷新 duration_seconds实时时长
* 3. 今日无任何日志 新建login_at user->in_time进房时间保证时长不丢失
*
* @param \App\Models\User $user 当前用户(必须已 fresh/refresh
* @param int $roomId 所在房间 ID
*/
private function tickDutyLog(User $user, int $roomId): void
{
// 无在职职务,无需记录
$activeUP = $user->activePosition;
if (! $activeUP) {
return;
}
// 今日是否已有开放日志
$openLog = PositionDutyLog::query()
->where('user_id', $user->id)
->whereNull('logout_at')
->whereDate('login_at', today())
->first();
if ($openLog) {
// 刷新实时在线时长
// 绕过模型 castinteger使用 DB::table 直接执行 SQL 表达式
DB::table('position_duty_logs')
->where('id', $openLog->id)
->update(['duration_seconds' => DB::raw('GREATEST(0, TIMESTAMPDIFF(SECOND, login_at, NOW()))')]);
return;
}
// 今日无日志 → 新建login_at 优先用进房时间
$loginAt = $user->in_time && $user->in_time->isToday()
? $user->in_time
: now();
PositionDutyLog::create([
'user_id' => $user->id,
'user_position_id' => $activeUP->id,
'login_at' => $loginAt,
'ip_address' => request()->ip(),
'room_id' => $roomId,
]);
}
}