feat(dm): хранилище и миграция звонков в беседах (Фаза 7)
Звонок в личной и групповой беседе живёт в своей таблице: в voice_states guild_id обязателен, а у беседы сервера нет. Миграция 00026 добавляет dm_calls (статус, media, длительность, причина завершения) и dm_call_participants (состояние участника и флаги микрофона, камеры и экрана); уникальный частичный индекс держит один идущий звонок на беседу. В DMParticipantProfiles сравнение с владельцем обёрнуто в COALESCE: у 1:1 dm_owner_id пуст, и NULL нельзя было прочитать в int — ручка звонка в личной беседе падала на 500. Тесты: жизненный цикл звонка в хранилище (гудки, active на втором участнике, история, повторный звонок) и выборка сторожем.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
-- +goose Up
|
||||
-- Фаза 7: звонки в личных и групповых беседах (AGENT.md 7.8, 7.14).
|
||||
--
|
||||
-- Звонок — отдельная сущность беседы: у него своя комната LiveKit, своё
|
||||
-- состояние участников и своя история (кто звонил, когда, сколько длился,
|
||||
-- кто пропустил). В voice_states звонок не помещается: там guild_id
|
||||
-- обязателен, а у личной беседы сервера нет.
|
||||
--
|
||||
-- Статусы звонка (dm_calls.status): ringing — идут гудки, active — хотя бы
|
||||
-- двое в комнате, ended — завершён. Состояния участника
|
||||
-- (dm_call_participants.state): invited — приглашён и ещё не ответил,
|
||||
-- joined — в звонке, declined — отклонил, left — вышел, missed — не ответил.
|
||||
CREATE TABLE dm_calls (
|
||||
id INTEGER PRIMARY KEY,
|
||||
channel_id INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE,
|
||||
initiator_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
status TEXT NOT NULL,
|
||||
-- media: audio | video — чем открыли звонок, клиент показывает это в истории.
|
||||
media TEXT NOT NULL DEFAULT 'audio',
|
||||
created_at TEXT NOT NULL,
|
||||
-- started_at — момент, когда звонок стал active: от него считается
|
||||
-- длительность разговора, а не от гудков.
|
||||
started_at TEXT,
|
||||
ended_at TEXT,
|
||||
ended_by INTEGER REFERENCES users (id) ON DELETE SET NULL,
|
||||
-- end_reason: completed | canceled | declined | missed | failed.
|
||||
end_reason TEXT
|
||||
);
|
||||
CREATE INDEX dm_calls_channel_idx ON dm_calls (channel_id, id DESC);
|
||||
-- В беседе одновременно идёт не больше одного звонка: гонку двух «позвонить»
|
||||
-- закрывает сама база, а не проверка в приложении.
|
||||
CREATE UNIQUE INDEX dm_calls_live_channel_idx ON dm_calls (channel_id) WHERE status <> 'ended';
|
||||
|
||||
CREATE TABLE dm_call_participants (
|
||||
call_id INTEGER NOT NULL REFERENCES dm_calls (id) ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
state TEXT NOT NULL,
|
||||
invited_at TEXT NOT NULL,
|
||||
joined_at TEXT,
|
||||
left_at TEXT,
|
||||
self_mute INTEGER NOT NULL DEFAULT 0,
|
||||
self_deaf INTEGER NOT NULL DEFAULT 0,
|
||||
camera INTEGER NOT NULL DEFAULT 0,
|
||||
screen INTEGER NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (call_id, user_id)
|
||||
);
|
||||
CREATE INDEX dm_call_participants_user_idx ON dm_call_participants (user_id, call_id DESC);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE dm_call_participants;
|
||||
DROP TABLE dm_calls;
|
||||
Reference in New Issue
Block a user