You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中是否可在会话销毁时执行代码?CRM工时追踪需求

Handling Unexpected Browser/Shutdown Events for CRM Time Tracking

Hey there! This is such a critical edge case for time-tracking features—glad you’re thinking ahead to cover it. Let’s break down the solutions you can use to capture that end_time even when users close their browser or shut down their machine:

1. Browser Unload Events (for Graceful Closures)

Browsers do provide events that fire when a page is about to unload, which you can leverage to send your end_time data. The key here is using the right event and a reliable way to send the data without blocking the user’s exit.

beforeunload + navigator.sendBeacon

The beforeunload event triggers just before the page starts unloading, and navigator.sendBeacon is specifically designed for this scenario—it sends data to the server in the background without blocking the page from closing. This is far more reliable than traditional async requests, which browsers often cancel during unload.

Here’s a quick implementation example:

// Track active task state (store in memory + localStorage for persistence)
let activeTrackingTask = null;

// Start tracking a task
function startTask(taskId) {
  activeTrackingTask = {
    taskId: taskId,
    startTime: new Date().toISOString()
  };
  localStorage.setItem('activeTrackingTask', JSON.stringify(activeTrackingTask));
}

// Listen for page unload
window.addEventListener('beforeunload', (e) => {
  if (activeTrackingTask) {
    const endTime = new Date().toISOString();
    // Prepare data for the server
    const formData = new FormData();
    formData.append('taskId', activeTrackingTask.taskId);
    formData.append('startTime', activeTrackingTask.startTime);
    formData.append('endTime', endTime);

    // Send data via beacon (non-blocking)
    navigator.sendBeacon('/api/crm/track-time', formData);

    // Clean up local state
    localStorage.removeItem('activeTrackingTask');
    activeTrackingTask = null;
  }
});

// Restore active task on page load (in case of accidental refresh)
window.addEventListener('load', () => {
  const savedTask = localStorage.getItem('activeTrackingTask');
  if (savedTask) {
    activeTrackingTask = JSON.parse(savedTask);
    // Optional: Prompt user to resume tracking or mark as completed
    alert(`You have an active task (ID: ${activeTrackingTask.taskId})—would you like to resume tracking?`);
  }
});

Caveats for Unload Events

  • These events won’t fire if the browser crashes, the user kills the browser process via task manager, or their device loses power abruptly. For these cases, you need a fallback.

2. Heartbeat Mechanism (for Abrupt Closures)

To handle scenarios where unload events don’t fire, implement a heartbeat system that periodically sends updates to your server about the active task. Your server can then use the last heartbeat time to infer end_time if the task goes silent.

How it works:

  • When a user starts tracking a task, start an interval that sends a request to your server every 60-120 seconds, updating the task’s last_active_time.
  • On the server side, run a scheduled job (e.g., cron job, database trigger) that checks for tasks marked as "in progress" where the last_active_time is older than a threshold (e.g., 5 minutes). For these tasks, automatically set end_time to the last_active_time and mark the task as completed.

Example frontend code for heartbeats:

let heartbeatInterval;

function startHeartbeat(taskId) {
  // Send heartbeat every 60 seconds
  heartbeatInterval = setInterval(() => {
    fetch('/api/crm/task-heartbeat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        taskId: taskId,
        lastActiveTime: new Date().toISOString()
      })
    });
  }, 60000);
}

// Stop heartbeat when user manually ends the task
function endTask(taskId) {
  clearInterval(heartbeatInterval);
  // Send final end_time request
  fetch('/api/crm/track-time', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      taskId: taskId,
      startTime: activeTrackingTask.startTime,
      endTime: new Date().toISOString()
    })
  });
  // Clean up state
  localStorage.removeItem('activeTrackingTask');
  activeTrackingTask = null;
}

// Start heartbeat when tracking begins
function startTask(taskId) {
  activeTrackingTask = { taskId: taskId, startTime: new Date().toISOString() };
  localStorage.setItem('activeTrackingTask', JSON.stringify(activeTrackingTask));
  startHeartbeat(taskId);
}

3. Combine Both Approaches for Full Coverage

The best solution is to use unload events for graceful closures and heartbeats for abrupt ones, plus localStorage to restore tasks if the user returns to the page later. This covers almost every scenario:

  • User closes browser/tab → beforeunload + beacon captures end_time
  • Browser crashes/device shuts down → heartbeat threshold triggers server-side end_time
  • User refreshes the page → localStorage restores the active task

内容的提问来源于stack exchange,提问作者Dusko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:10:44