72b6879f4b
- Refactored max_bot from nested packages to flat module structure - Q2: Extended BotUser model (patronymic, email, org, address, vcf_raw, contact_hash, phone_verified, email_verified, last_interaction, total_conversations, total_tickets) - Q2: VCF parser (FN, N, TEL, EMAIL, ORG, ADR), upsert on re-contact, NLP history context (_get_user_history_context -> YandexGPT) - Q1: Broadcast preview modal with 10s confirmation timer - Q3: CSS var(--white)->var(--bg-card), var(--text)->var(--text-primary) - Q4: bot_settings showNotification(), editable max_bot_id - Q5: Webhook secret passthrough via X-Max-Bot-Api-Secret - Masking sensitive keys, dialog_cleared handler, migrate via _add_column_if_not_exists() - Rate limit (asyncio.sleep 0.5 per 10), dead code removed, conv.intent context in contact.py - Portal pages: bot_consent, bot_kb (edit), bot_settings, bot_test, bot_tickets, portal_settings - Tests: 21/21 passing, added test_yandex_gpt.py, test_email_sender.py - Deploy: deploy_full.sh, schema.sql, seed_knowledge_base.sql
99 lines
6.1 KiB
SQL
99 lines
6.1 KiB
SQL
-- Migration for v1.5.0: Yandex Disk integration + SLA Tickets + MAX notifications
|
|
|
|
-- File sync queue
|
|
CREATE TABLE IF NOT EXISTS file_sync_queue (
|
|
id SERIAL PRIMARY KEY,
|
|
local_path VARCHAR(500) NOT NULL,
|
|
remote_path VARCHAR(500) DEFAULT '',
|
|
entity_type VARCHAR(50) NOT NULL,
|
|
entity_id INTEGER NOT NULL,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','uploading','synced','error')),
|
|
error TEXT DEFAULT '',
|
|
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_file_sync_status ON file_sync_queue(status);
|
|
CREATE INDEX IF NOT EXISTS idx_file_sync_entity ON file_sync_queue(entity_type, entity_id);
|
|
|
|
-- Bot tickets
|
|
CREATE TABLE IF NOT EXISTS bot_tickets (
|
|
id SERIAL PRIMARY KEY,
|
|
ticket_number VARCHAR(20) UNIQUE NOT NULL,
|
|
user_id INTEGER REFERENCES bot_users(id) ON DELETE SET NULL,
|
|
object_id INTEGER REFERENCES objects(id) ON DELETE SET NULL,
|
|
customer_id INTEGER REFERENCES customers(id) ON DELETE SET NULL,
|
|
title VARCHAR(500) NOT NULL,
|
|
description TEXT DEFAULT '',
|
|
priority VARCHAR(20) DEFAULT 'normal' CHECK (priority IN ('low','normal','high','urgent')),
|
|
status VARCHAR(20) NOT NULL DEFAULT 'open' CHECK (status IN ('open','in_progress','resolved','closed')),
|
|
created_by VARCHAR(20) DEFAULT 'bot',
|
|
assigned_to INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
|
sla_contract_id INTEGER REFERENCES sla_contracts(id) ON DELETE SET NULL,
|
|
has_attachment BOOLEAN DEFAULT FALSE,
|
|
attachment_type VARCHAR(50) DEFAULT '',
|
|
attachment_path VARCHAR(500) DEFAULT '',
|
|
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
|
resolved_at TIMESTAMP NULL,
|
|
closed_at TIMESTAMP NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_bot_ticket_status ON bot_tickets(status);
|
|
CREATE INDEX IF NOT EXISTS idx_bot_ticket_user ON bot_tickets(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_bot_ticket_object ON bot_tickets(object_id);
|
|
CREATE INDEX IF NOT EXISTS idx_bot_ticket_number ON bot_tickets(ticket_number);
|
|
|
|
-- Bot ticket messages
|
|
CREATE TABLE IF NOT EXISTS bot_ticket_messages (
|
|
id SERIAL PRIMARY KEY,
|
|
ticket_id INTEGER REFERENCES bot_tickets(id) ON DELETE CASCADE,
|
|
direction VARCHAR(3) NOT NULL CHECK (direction IN ('in','out')),
|
|
text TEXT DEFAULT '',
|
|
has_attachment BOOLEAN DEFAULT FALSE,
|
|
attachment_path VARCHAR(500) DEFAULT '',
|
|
sender VARCHAR(20) DEFAULT 'client',
|
|
timestamp TIMESTAMP NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_bot_ticket_msg_ticket ON bot_ticket_messages(ticket_id);
|
|
|
|
-- Bot ticket status log
|
|
CREATE TABLE IF NOT EXISTS bot_ticket_statuses (
|
|
id SERIAL PRIMARY KEY,
|
|
ticket_id INTEGER REFERENCES bot_tickets(id) ON DELETE CASCADE,
|
|
old_status VARCHAR(20) DEFAULT '',
|
|
new_status VARCHAR(20) NOT NULL,
|
|
changed_by VARCHAR(20) DEFAULT 'system',
|
|
timestamp TIMESTAMP NOT NULL DEFAULT NOW(),
|
|
comment TEXT DEFAULT ''
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_bot_ticket_status_log ON bot_ticket_statuses(ticket_id);
|
|
|
|
-- Add max_user_id to users table
|
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS max_user_id BIGINT NULL;
|
|
|
|
-- Add Yandex Disk settings
|
|
INSERT INTO bot_settings (key, value, description) VALUES
|
|
('yandex_disk_token', 'y0__wgBEMHM9zsYiuxBIMi6sswXML_twIQIyCDWBrrNPjQcoQZR6J2x57oBLcg', 'OAuth токен Яндекс Диска'),
|
|
('yandex_disk_root', '/AegisOne_Service', 'Корневая папка на Яндекс Диске'),
|
|
('yandex_disk_client_id', '00fd594faf604161b773a53737bb5462', 'Client ID приложения Яндекс'),
|
|
('yandex_disk_client_secret', '32a46b6dfd3c499e8cced25fed64b426', 'Client Secret приложения Яндекс'),
|
|
('storage_backend', 'yandex', 'Режим хранения: local/yandex/both'),
|
|
('max_file_size_mb', '100', 'Максимальный размер файла (MB)'),
|
|
('public_link_expiry_days', '30', 'Срок публичных ссылок по умолчанию (дни)'),
|
|
('auto_archive_enabled', 'true', 'Авто-архивация закрытых обращений'),
|
|
('auto_archive_after_days', '30', 'Через сколько дней архивировать'),
|
|
('max_group_chat_id', '', 'ID группового чата MAX для уведомлений')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
|
|
-- Add SLA Ticket feature
|
|
INSERT INTO bot_features (feature_key, name, description, enabled, sort_order) VALUES
|
|
('sla_ticket', 'Заявка по SLA', 'Подать заявку о неисправности для клиентов с активным SLA', true, 0)
|
|
ON CONFLICT (feature_key) DO NOTHING;
|
|
|
|
-- Add ticket-related response templates
|
|
INSERT INTO bot_response_templates (template_key, template_text, description, variables) VALUES
|
|
('sla_ticket_check', 'Проверяю ваш SLA контракт...', 'Проверка SLA перед созданием заявки', '[]'),
|
|
('sla_ticket_found', 'SLA найден: {object_name}, уровень: {service_level}. Опишите неисправность. Вы можете прикрепить фото или документ.', 'SLA найден, запрос описания', '["object_name","service_level"]'),
|
|
('sla_ticket_not_found', 'SLA контракт не найден или не активен. Свяжитесь с нами:\n\n📞 {phone_1}\n📞 {phone_2}\n✉️ {support_email}', 'SLA не найден', '["phone_1","phone_2","support_email"]'),
|
|
('sla_ticket_created', 'Заявка #{ticket_number} создана.\n\nПриоритет: {priority}\nОжидаемое время реакции: {response_time}\n\nИнженер будет назначен в ближайшее время.', 'Подтверждение создания заявки', '["ticket_number","priority","response_time"]'),
|
|
('ticket_status_update', 'Ваша заявка #{ticket_number} — {status}.\nИнженер: {engineer}', 'Уведомление о смене статуса заявки', '["ticket_number","status","engineer"]'),
|
|
('ticket_resolved', 'Заявка #{ticket_number} выполнена. Оцените качество:\n\n⭐ 1 — Плохо\n⭐⭐ 2\n⭐⭐⭐ 3\n⭐⭐⭐⭐ 4\n⭐⭐⭐⭐⭐ 5 — Отлично', 'Запрос оценки после выполнения', '["ticket_number"]')
|
|
ON CONFLICT (template_key) DO NOTHING;
|