axe/internal/db/migrations/0005_project_invitations.sql

61 lines
3.8 KiB
MySQL
Raw Normal View History

2026-10-18 19:07:25 +00:00
-- 0005_project_invitations — приглашения мастеров в проект.
--
-- Раньше POST /projects/:id/workers сразу давало членство. Теперь это
-- ровно два шага и без обходных путей:
-- 1) клиент/админ отправляет приглашение — строка получает 'invited'
-- и срок жизни expires_at (7 дней);
-- 2) мастер сам решает: accept → 'active' (и только тогда получает
-- заявки проекта) или decline → 'declined' (повторное приглашение
-- перезапускает ту же строку).
-- Принудительного членства (force) нет: активным мастер становится
-- только своим решением.
--
-- Статусы строки project_workers:
-- invited — ждём решения мастера (expires_at обязателен, см. CHECK);
-- active — мастер принял: назначение заявок разрешено;
-- declined — мастер отказал; строка остаётся как история отказа
-- и допускает повторное приглашение.
--
-- Строки, созданные до этой миграции, получают DEFAULT 'active' —
-- членство существующих мастеров не ломается.
--
-- Принцип прежний: переходы проверяются в самом SQL:
-- * назначение заявки требует членства status='active' — приглашённый
-- не получает заявки, пока не принял (см. Assign в repository/ticket.go);
-- * accept/decline двигают только строку в статусе 'invited';
-- * уход мастера (DELETE) снимает строку любого статуса — так же
-- отзывается и не принятое приглашение.
--
-- Применённый файл не редактировать (checksum в schema_migrations).
ALTER TABLE project_workers
ADD COLUMN status text NOT NULL DEFAULT 'active',
ADD COLUMN invited_by uuid REFERENCES profiles(id) ON DELETE SET NULL,
ADD COLUMN invited_at timestamptz,
ADD COLUMN responded_at timestamptz,
ADD COLUMN expires_at timestamptz;
ALTER TABLE project_workers
ADD CONSTRAINT project_workers_status_check
CHECK (status IN ('invited', 'active', 'declined'));
-- У приглашения всегда есть пригласивший и срок; у обычного членства
-- (включая строки до миграции) этих полей может не быть.
ALTER TABLE project_workers
ADD CONSTRAINT project_workers_invited_shape
CHECK (status <> 'invited'
OR (invited_at IS NOT NULL AND invited_by IS NOT NULL
AND expires_at IS NOT NULL AND responded_at IS NULL));
-- Принятое/отклонённое приглашение фиксирует момент ответа.
ALTER TABLE project_workers
ADD CONSTRAINT project_workers_responded_shape
CHECK (status = 'invited' OR responded_at IS NOT NULL
OR invited_at IS NULL); -- строки до миграции без ответа
-- Списки: мастера проекта (по статусу) и приглашения мастера (worker_id + срок).
CREATE INDEX IF NOT EXISTS idx_project_workers_project_status
ON project_workers (project_id, status);
CREATE INDEX IF NOT EXISTS idx_project_workers_worker_status
ON project_workers (worker_id, status, expires_at);