62 lines
2.7 KiB
SQL
62 lines
2.7 KiB
SQL
-- Yealink/PBX call IDs are reused after a phone or PBX lifecycle.
|
|
-- Keep historical calls and require uniqueness only while a call is active.
|
|
|
|
DROP INDEX IF EXISTS ux_telefoni_opkald_callid;
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS ux_telefoni_opkald_active_callid
|
|
ON telefoni_opkald(callid)
|
|
WHERE ended_at IS NULL;
|
|
|
|
-- Recover calls that were previously merged into an older row with the same
|
|
-- reused call ID. System events are written from the successful callback and
|
|
-- retain the callback metadata required for a read-safe reconstruction.
|
|
INSERT INTO telefoni_opkald (
|
|
callid, bruger_id, direction, ekstern_nummer, intern_extension,
|
|
started_at, ended_at, duration_sec, raw_payload,
|
|
lifecycle_status, termination_source, last_event_at
|
|
)
|
|
SELECT
|
|
established.correlation_id,
|
|
established.user_id,
|
|
COALESCE(NULLIF(established.details->'payload'->>'direction', ''), 'inbound'),
|
|
NULLIF(established.details->>'external_number', ''),
|
|
NULLIF(established.details->>'extension', ''),
|
|
established.occurred_at AT TIME ZONE 'UTC',
|
|
terminated.occurred_at AT TIME ZONE 'UTC',
|
|
CASE
|
|
WHEN NULLIF(terminated.details->>'duration_sec', '') ~ '^[0-9]+$'
|
|
THEN (terminated.details->>'duration_sec')::INTEGER
|
|
WHEN terminated.occurred_at IS NOT NULL
|
|
THEN GREATEST(EXTRACT(EPOCH FROM (terminated.occurred_at - established.occurred_at))::INTEGER, 0)
|
|
ELSE NULL
|
|
END,
|
|
COALESCE(established.details->'payload', '{}'::jsonb),
|
|
CASE WHEN terminated.id IS NULL THEN 'active' ELSE 'completed' END,
|
|
CASE WHEN terminated.id IS NULL THEN NULL ELSE 'system_log_recovery' END,
|
|
COALESCE(terminated.occurred_at, established.occurred_at)
|
|
FROM system_event_log established
|
|
LEFT JOIN LATERAL (
|
|
SELECT term.id, term.occurred_at, term.details
|
|
FROM system_event_log term
|
|
WHERE term.source = 'voip'
|
|
AND term.event_type = 'voip.terminated'
|
|
AND term.correlation_id = established.correlation_id
|
|
AND term.occurred_at >= established.occurred_at
|
|
ORDER BY term.occurred_at ASC, term.id ASC
|
|
LIMIT 1
|
|
) terminated ON TRUE
|
|
WHERE established.source = 'voip'
|
|
AND established.event_type = 'voip.established'
|
|
AND NULLIF(TRIM(established.correlation_id), '') IS NOT NULL
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM telefoni_opkald current_call
|
|
WHERE current_call.callid = established.correlation_id
|
|
AND current_call.started_at BETWEEN
|
|
(established.occurred_at AT TIME ZONE 'UTC') - INTERVAL '5 minutes'
|
|
AND (established.occurred_at AT TIME ZONE 'UTC') + INTERVAL '5 minutes'
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_telefoni_opkald_callid_started
|
|
ON telefoni_opkald(callid, started_at DESC);
|