61 lines
3.8 KiB
MySQL
61 lines
3.8 KiB
MySQL
|
|
-- 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);
|