-- Миграция для автоматизации воронки развития -- Таблица истории переходов CREATE TABLE IF NOT EXISTS development_pipeline_history ( id BIGSERIAL PRIMARY KEY, pipeline_id VARCHAR(50) NOT NULL REFERENCES development_pipeline(id) ON DELETE CASCADE, from_status VARCHAR(20), to_status VARCHAR(20) NOT NULL, reason TEXT, triggered_by VARCHAR(50) NOT NULL DEFAULT 'manual', -- 'auto' | 'manual' | user_id created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_pipeline_history_pipeline ON development_pipeline_history(pipeline_id); CREATE INDEX IF NOT EXISTS idx_pipeline_history_status ON development_pipeline_history(to_status); CREATE INDEX IF NOT EXISTS idx_pipeline_history_date ON development_pipeline_history(created_at DESC); -- Таблица правил автоматизации (для будущего расширения) CREATE TABLE IF NOT EXISTS development_automation_rules ( id BIGSERIAL PRIMARY KEY, rule_name VARCHAR(100) NOT NULL UNIQUE, from_status VARCHAR(20), to_status VARCHAR(20) NOT NULL, conditions JSONB, -- условия для срабатывания enabled BOOLEAN DEFAULT TRUE, priority INTEGER DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_automation_rules_status ON development_automation_rules(from_status, to_status); CREATE INDEX IF NOT EXISTS idx_automation_rules_enabled ON development_automation_rules(enabled); -- Добавляем поле для отслеживания последнего автоматического обновления ALTER TABLE development_pipeline ADD COLUMN IF NOT EXISTS last_auto_check TIMESTAMPTZ; CREATE INDEX IF NOT EXISTS idx_pipeline_last_check ON development_pipeline(last_auto_check); -- Триггер для автоматического обновления статуса при создании ОСС CREATE OR REPLACE FUNCTION auto_transition_to_voting() RETURNS TRIGGER AS $$ BEGIN -- Если создано активное ОСС, переводим связанный pipeline объект в voting IF NEW.status = 'active' THEN UPDATE development_pipeline SET status = 'voting', updated_at = NOW() WHERE (building_id = NEW.building_id OR address = NEW.address) AND status = 'preparation'; -- Логируем переход INSERT INTO development_pipeline_history (pipeline_id, from_status, to_status, reason, triggered_by) SELECT id, 'preparation', 'voting', 'Автоматический переход: создано активное ОСС', 'auto' FROM development_pipeline WHERE (building_id = NEW.building_id OR address = NEW.address) AND status = 'voting' AND id NOT IN (SELECT pipeline_id FROM development_pipeline_history WHERE to_status = 'voting' AND created_at > NOW() - INTERVAL '1 minute'); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trigger_auto_transition_to_voting ON development_oss_sessions; CREATE TRIGGER trigger_auto_transition_to_voting AFTER INSERT OR UPDATE ON development_oss_sessions FOR EACH ROW WHEN (NEW.status = 'active') EXECUTE FUNCTION auto_transition_to_voting(); -- Триггер для обработки завершения ОСС CREATE OR REPLACE FUNCTION auto_handle_oss_completion() RETURNS TRIGGER AS $$ DECLARE quorum_percent NUMERIC; BEGIN -- Если ОСС завершено IF NEW.status = 'completed' AND OLD.status != 'completed' THEN quorum_percent := (NEW.quorum_current / NULLIF(NEW.quorum_total, 0)) * 100; -- Если кворум > 50%, переводим в transfer IF quorum_percent > 50 THEN UPDATE development_pipeline SET status = 'transfer', updated_at = NOW() WHERE (building_id = NEW.building_id OR address = NEW.address) AND status = 'voting'; -- Логируем переход INSERT INTO development_pipeline_history (pipeline_id, from_status, to_status, reason, triggered_by) SELECT id, 'voting', 'transfer', format('Автоматический переход: ОСС завершено успешно (кворум %.1f%%)', quorum_percent), 'auto' FROM development_pipeline WHERE (building_id = NEW.building_id OR address = NEW.address) AND status = 'transfer' AND id NOT IN (SELECT pipeline_id FROM development_pipeline_history WHERE to_status = 'transfer' AND created_at > NOW() - INTERVAL '1 minute'); ELSE -- ОСС провалено - снижаем probability UPDATE development_pipeline SET probability = GREATEST(0, probability - 20), updated_at = NOW() WHERE (building_id = NEW.building_id OR address = NEW.address) AND status = 'voting'; -- Откатываем в preparation UPDATE development_pipeline SET status = 'preparation', updated_at = NOW() WHERE (building_id = NEW.building_id OR address = NEW.address) AND status = 'voting'; -- Логируем откат INSERT INTO development_pipeline_history (pipeline_id, from_status, to_status, reason, triggered_by) SELECT id, 'voting', 'preparation', format('Автоматический откат: ОСС провалено (кворум %.1f%%)', quorum_percent), 'auto' FROM development_pipeline WHERE (building_id = NEW.building_id OR address = NEW.address) AND status = 'preparation' AND id NOT IN (SELECT pipeline_id FROM development_pipeline_history WHERE to_status = 'preparation' AND created_at > NOW() - INTERVAL '1 minute'); END IF; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trigger_auto_handle_oss_completion ON development_oss_sessions; CREATE TRIGGER trigger_auto_handle_oss_completion AFTER UPDATE ON development_oss_sessions FOR EACH ROW WHEN (NEW.status = 'completed' AND OLD.status != 'completed') EXECUTE FUNCTION auto_handle_oss_completion();