feat(chat): adiciona encerramento automatico por inatividade (12h)
- Sessoes de chat sao encerradas apos 12 horas sem atividade - Criterios de encerramento automatico: 1. Maquina offline (5 min sem heartbeat) 2. Chat inativo (12 horas sem atividade) - NOVO 3. Ticket orfao (sem maquina vinculada) - Log detalhado com contagem por motivo de encerramento - Evento no timeline com reason "inatividade_chat" Isso evita acumular sessoes abertas indefinidamente quando usuario esquece de encerrar o chat. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
29fbbfaa26
commit
86f818c6f3
1 changed files with 47 additions and 7 deletions
|
|
@ -763,15 +763,22 @@ export const getTicketChatHistory = query({
|
|||
// Timeout de maquina offline: 5 minutos sem heartbeat
|
||||
const MACHINE_OFFLINE_TIMEOUT_MS = 5 * 60 * 1000
|
||||
|
||||
// Mutation interna para encerrar sessões de máquinas offline (chamada pelo cron)
|
||||
// Nova lógica: só encerra se a MÁQUINA estiver offline, não por inatividade de chat
|
||||
// Isso permite que usuários mantenham o chat aberto sem precisar enviar mensagens
|
||||
// Timeout de inatividade do chat: 12 horas sem atividade
|
||||
// Isso evita acumular sessoes abertas indefinidamente quando usuario esquece de encerrar
|
||||
const CHAT_INACTIVITY_TIMEOUT_MS = 12 * 60 * 60 * 1000
|
||||
|
||||
// Mutation interna para encerrar sessões inativas (chamada pelo cron)
|
||||
// Critérios de encerramento:
|
||||
// 1. Máquina offline (5 min sem heartbeat)
|
||||
// 2. Chat inativo (12 horas sem atividade) - mesmo se máquina online
|
||||
// 3. Ticket órfão (sem máquina vinculada)
|
||||
export const autoEndInactiveSessions = mutation({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
console.log("cron: autoEndInactiveSessions iniciado (verificando maquinas offline)")
|
||||
console.log("cron: autoEndInactiveSessions iniciado")
|
||||
const now = Date.now()
|
||||
const offlineCutoff = now - MACHINE_OFFLINE_TIMEOUT_MS
|
||||
const inactivityCutoff = now - CHAT_INACTIVITY_TIMEOUT_MS
|
||||
|
||||
// Limitar a 50 sessões por execução para evitar timeout do cron (30s)
|
||||
const maxSessionsPerRun = 50
|
||||
|
|
@ -784,6 +791,7 @@ export const autoEndInactiveSessions = mutation({
|
|||
|
||||
let endedCount = 0
|
||||
let checkedCount = 0
|
||||
const reasons: Record<string, number> = {}
|
||||
|
||||
for (const session of activeSessions) {
|
||||
checkedCount++
|
||||
|
|
@ -812,6 +820,36 @@ export const autoEndInactiveSessions = mutation({
|
|||
createdAt: now,
|
||||
})
|
||||
endedCount++
|
||||
reasons["ticket_sem_maquina"] = (reasons["ticket_sem_maquina"] ?? 0) + 1
|
||||
continue
|
||||
}
|
||||
|
||||
// Verificar inatividade do chat (12 horas sem atividade)
|
||||
// Isso tem prioridade sobre o status da máquina
|
||||
const chatIsInactive = session.lastActivityAt < inactivityCutoff
|
||||
if (chatIsInactive) {
|
||||
await ctx.db.patch(session._id, {
|
||||
status: "ENDED",
|
||||
endedAt: now,
|
||||
})
|
||||
await ctx.db.insert("ticketEvents", {
|
||||
ticketId: session.ticketId,
|
||||
type: "LIVE_CHAT_ENDED",
|
||||
payload: {
|
||||
sessionId: session._id,
|
||||
agentId: session.agentId,
|
||||
agentName: session.agentSnapshot?.name ?? "Sistema",
|
||||
durationMs: now - session.startedAt,
|
||||
startedAt: session.startedAt,
|
||||
endedAt: now,
|
||||
autoEnded: true,
|
||||
reason: "inatividade_chat",
|
||||
inactiveForMs: now - session.lastActivityAt,
|
||||
},
|
||||
createdAt: now,
|
||||
})
|
||||
endedCount++
|
||||
reasons["inatividade_chat"] = (reasons["inatividade_chat"] ?? 0) + 1
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -819,7 +857,7 @@ export const autoEndInactiveSessions = mutation({
|
|||
const lastHeartbeatAt = await getLastHeartbeatAt(ctx, ticket.machineId)
|
||||
const machineIsOnline = lastHeartbeatAt !== null && lastHeartbeatAt > offlineCutoff
|
||||
|
||||
// Se máquina está online, manter sessão ativa
|
||||
// Se máquina está online e chat está ativo, manter sessão
|
||||
if (machineIsOnline) {
|
||||
continue
|
||||
}
|
||||
|
|
@ -849,10 +887,12 @@ export const autoEndInactiveSessions = mutation({
|
|||
})
|
||||
|
||||
endedCount++
|
||||
reasons["maquina_offline"] = (reasons["maquina_offline"] ?? 0) + 1
|
||||
}
|
||||
|
||||
console.log(`cron: verificadas ${checkedCount} sessoes, encerradas ${endedCount} (maquinas offline)`)
|
||||
return { endedCount, checkedCount, hasMore: activeSessions.length === maxSessionsPerRun }
|
||||
const reasonsSummary = Object.entries(reasons).map(([r, c]) => `${r}=${c}`).join(", ")
|
||||
console.log(`cron: verificadas ${checkedCount} sessoes, encerradas ${endedCount} (${reasonsSummary || "nenhuma"})`)
|
||||
return { endedCount, checkedCount, reasons, hasMore: activeSessions.length === maxSessionsPerRun }
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue