@greatapps/greatagents 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION_COMPLETE_DOCUMENTATION.md +672 -0
- package/package.json +1 -1
- package/scripts/README.md +128 -235
- package/scripts/constraints.sql +585 -0
- package/scripts/indexes.sql +663 -0
- package/scripts/migrate_v1.sql +1230 -0
- package/scripts/tables/001_accounts.sql +51 -0
- package/scripts/tables/002_users.sql +69 -0
- package/scripts/tables/003_people.sql +70 -0
- package/scripts/tables/004_tags.sql +62 -0
- package/scripts/tables/005_credentials.sql +79 -0
- package/scripts/tables/006_agents.sql +70 -0
- package/scripts/tables/007_tools.sql +68 -0
- package/scripts/tables/008_channels.sql +75 -0
- package/scripts/tables/009_leads.sql +85 -0
- package/scripts/tables/010_missions.sql +62 -0
- package/scripts/tables/011_objectives.sql +72 -0
- package/scripts/tables/012_conversations.sql +84 -0
- package/scripts/tables/013_objectives_tools.sql +66 -0
- package/scripts/tables/014_messages.sql +78 -0
- package/scripts/tables/015_companies.sql +77 -0
- package/scripts/tables/016_conversations_tags.sql +64 -0
- package/scripts/tables/017_leads_tags.sql +64 -0
- package/scripts/triggers.sql +497 -0
- package/src/modules/accounts/index.js +11 -0
- package/src/modules/accounts/properties.js +46 -0
- package/src/modules/agents/properties.js +1 -30
- package/src/modules/channels/properties.js +1 -0
- package/src/modules/conversations/index.js +1 -1
- package/src/modules/conversations/properties.js +29 -26
- package/src/modules/credentials/properties.js +2 -2
- package/src/modules/messages/properties.js +40 -2
- package/src/modules/missions/properties.js +1 -30
- package/src/modules/objectives/properties.js +1 -30
- package/src/modules/objectives_tools/index.js +1 -1
- package/src/modules/objectives_tools/properties.js +1 -37
- package/src/modules/tools/properties.js +1 -0
- package/src/modules/users/index.js +11 -0
- package/src/modules/users/properties.js +156 -0
- package/src/product.js +4 -0
- package/scripts/agents/create_table.sql +0 -46
- package/scripts/channels/create_table.sql +0 -48
- package/scripts/companies/create_table.sql +0 -52
- package/scripts/conversations/add_messages_reference.sql +0 -42
- package/scripts/conversations/create_table.sql +0 -55
- package/scripts/conversations_tags/create_table.sql +0 -39
- package/scripts/credentials/create_table.sql +0 -63
- package/scripts/leads/create_table.sql +0 -55
- package/scripts/leads_tags/create_table.sql +0 -39
- package/scripts/messages/create_table.sql +0 -44
- package/scripts/missions/create_table.sql +0 -43
- package/scripts/objectives/create_table.sql +0 -49
- package/scripts/objectives_tools/create_table.sql +0 -52
- package/scripts/people/create_table.sql +0 -51
- package/scripts/tags/create_table.sql +0 -38
- package/scripts/tools/create_table.sql +0 -43
- package/src/shared/llmModels.js +0 -17
|
@@ -0,0 +1,1230 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- Script: migrate_v1.sql
|
|
3
|
+
-- Descrição: Script principal de migração do banco GAgents v1.0
|
|
4
|
+
-- Versão: 1.0
|
|
5
|
+
-- Dependências: PostgreSQL 14+
|
|
6
|
+
-- Autor: Sistema de Migração GAgents
|
|
7
|
+
-- Data: 2025-09-20
|
|
8
|
+
-- ============================================================================
|
|
9
|
+
|
|
10
|
+
-- ============================================================================
|
|
11
|
+
-- SEÇÃO 1: VERIFICAÇÕES INICIAIS E CONFIGURAÇÃO
|
|
12
|
+
-- ============================================================================
|
|
13
|
+
|
|
14
|
+
-- Verificação de pré-requisitos
|
|
15
|
+
DO $$
|
|
16
|
+
DECLARE
|
|
17
|
+
migration_start_time TIMESTAMP WITH TIME ZONE;
|
|
18
|
+
BEGIN
|
|
19
|
+
migration_start_time := CURRENT_TIMESTAMP;
|
|
20
|
+
|
|
21
|
+
-- Verificar versão PostgreSQL
|
|
22
|
+
IF current_setting('server_version_num')::integer < 140000 THEN
|
|
23
|
+
RAISE EXCEPTION 'PostgreSQL versão 14+ é obrigatória. Versão atual: %', version();
|
|
24
|
+
END IF;
|
|
25
|
+
|
|
26
|
+
-- Verificar permissões básicas
|
|
27
|
+
IF NOT has_database_privilege(current_database(), 'CREATE') THEN
|
|
28
|
+
RAISE EXCEPTION 'Usuário não possui permissão CREATE na database';
|
|
29
|
+
END IF;
|
|
30
|
+
|
|
31
|
+
RAISE NOTICE '========================================';
|
|
32
|
+
RAISE NOTICE 'INICIANDO MIGRAÇÃO GAGENTS v1.0';
|
|
33
|
+
RAISE NOTICE 'PostgreSQL versão: %', version();
|
|
34
|
+
RAISE NOTICE 'Database: %', current_database();
|
|
35
|
+
RAISE NOTICE 'Schema: %', current_schema();
|
|
36
|
+
RAISE NOTICE 'Usuário: %', current_user;
|
|
37
|
+
RAISE NOTICE 'Início: %', migration_start_time;
|
|
38
|
+
RAISE NOTICE '========================================';
|
|
39
|
+
END $$;
|
|
40
|
+
|
|
41
|
+
-- ============================================================================
|
|
42
|
+
-- SEÇÃO 2: TABELA DE CONTROLE DE MIGRAÇÕES
|
|
43
|
+
-- ============================================================================
|
|
44
|
+
|
|
45
|
+
-- Tabela para controle de migrações
|
|
46
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
47
|
+
id SERIAL PRIMARY KEY,
|
|
48
|
+
migration_name VARCHAR(100) NOT NULL UNIQUE,
|
|
49
|
+
script_path VARCHAR(255) NOT NULL,
|
|
50
|
+
executed_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
51
|
+
execution_time_ms INTEGER,
|
|
52
|
+
success BOOLEAN DEFAULT TRUE,
|
|
53
|
+
error_message TEXT,
|
|
54
|
+
rollback_executed BOOLEAN DEFAULT FALSE,
|
|
55
|
+
rollback_at TIMESTAMP WITH TIME ZONE NULL,
|
|
56
|
+
checkpoint_data JSONB
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
-- Índices para tabela de controle
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_schema_migrations_name ON schema_migrations(migration_name);
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_schema_migrations_executed_at ON schema_migrations(executed_at);
|
|
62
|
+
CREATE INDEX IF NOT EXISTS idx_schema_migrations_success ON schema_migrations(success);
|
|
63
|
+
|
|
64
|
+
COMMENT ON TABLE schema_migrations IS 'Tabela para controle e rastreamento de migrações';
|
|
65
|
+
|
|
66
|
+
-- ============================================================================
|
|
67
|
+
-- SEÇÃO 3: FUNCTIONS DE CONTROLE DE MIGRAÇÃO
|
|
68
|
+
-- ============================================================================
|
|
69
|
+
|
|
70
|
+
-- Function para executar scripts com controle
|
|
71
|
+
CREATE OR REPLACE FUNCTION execute_migration_script(
|
|
72
|
+
script_name VARCHAR,
|
|
73
|
+
script_path VARCHAR,
|
|
74
|
+
script_content TEXT
|
|
75
|
+
) RETURNS BOOLEAN AS $$
|
|
76
|
+
DECLARE
|
|
77
|
+
start_time TIMESTAMP WITH TIME ZONE;
|
|
78
|
+
end_time TIMESTAMP WITH TIME ZONE;
|
|
79
|
+
execution_time INTEGER;
|
|
80
|
+
script_executed BOOLEAN DEFAULT FALSE;
|
|
81
|
+
BEGIN
|
|
82
|
+
-- Verificar se já foi executado
|
|
83
|
+
IF EXISTS (SELECT 1 FROM schema_migrations WHERE migration_name = script_name AND success = TRUE) THEN
|
|
84
|
+
RAISE NOTICE '[%] Script % já foi executado com sucesso, pulando...', now(), script_name;
|
|
85
|
+
RETURN TRUE;
|
|
86
|
+
END IF;
|
|
87
|
+
|
|
88
|
+
start_time := CURRENT_TIMESTAMP;
|
|
89
|
+
RAISE NOTICE '[%] Iniciando execução do script: %', start_time, script_name;
|
|
90
|
+
|
|
91
|
+
-- Executar o script (será substituído pelo conteúdo real)
|
|
92
|
+
-- EXECUTE script_content; -- Comentado: não necessário neste contexto
|
|
93
|
+
|
|
94
|
+
end_time := CURRENT_TIMESTAMP;
|
|
95
|
+
execution_time := EXTRACT(EPOCH FROM (end_time - start_time)) * 1000;
|
|
96
|
+
|
|
97
|
+
-- Registrar sucesso
|
|
98
|
+
INSERT INTO schema_migrations (migration_name, script_path, execution_time_ms, success)
|
|
99
|
+
VALUES (script_name, script_path, execution_time, TRUE)
|
|
100
|
+
ON CONFLICT (migration_name) DO UPDATE SET
|
|
101
|
+
executed_at = CURRENT_TIMESTAMP,
|
|
102
|
+
execution_time_ms = execution_time,
|
|
103
|
+
success = TRUE,
|
|
104
|
+
error_message = NULL;
|
|
105
|
+
|
|
106
|
+
RAISE NOTICE '[%] Script % executado com sucesso em %ms', end_time, script_name, execution_time;
|
|
107
|
+
RETURN TRUE;
|
|
108
|
+
|
|
109
|
+
EXCEPTION WHEN OTHERS THEN
|
|
110
|
+
-- Registrar erro
|
|
111
|
+
INSERT INTO schema_migrations (migration_name, script_path, execution_time_ms, success, error_message)
|
|
112
|
+
VALUES (script_name, script_path, 0, FALSE, SQLERRM)
|
|
113
|
+
ON CONFLICT (migration_name) DO UPDATE SET
|
|
114
|
+
executed_at = CURRENT_TIMESTAMP,
|
|
115
|
+
execution_time_ms = 0,
|
|
116
|
+
success = FALSE,
|
|
117
|
+
error_message = SQLERRM;
|
|
118
|
+
|
|
119
|
+
RAISE EXCEPTION 'Erro ao executar script %: %', script_name, SQLERRM;
|
|
120
|
+
END;
|
|
121
|
+
$$ LANGUAGE plpgsql;
|
|
122
|
+
|
|
123
|
+
-- Function para criar checkpoint
|
|
124
|
+
CREATE OR REPLACE FUNCTION create_migration_checkpoint(
|
|
125
|
+
checkpoint_name VARCHAR,
|
|
126
|
+
checkpoint_data JSONB DEFAULT NULL
|
|
127
|
+
) RETURNS void AS $$
|
|
128
|
+
BEGIN
|
|
129
|
+
INSERT INTO schema_migrations (migration_name, script_path, success, checkpoint_data)
|
|
130
|
+
VALUES ('CHECKPOINT_' || checkpoint_name, 'checkpoint', TRUE, checkpoint_data)
|
|
131
|
+
ON CONFLICT (migration_name) DO UPDATE SET
|
|
132
|
+
executed_at = CURRENT_TIMESTAMP,
|
|
133
|
+
checkpoint_data = checkpoint_data;
|
|
134
|
+
|
|
135
|
+
RAISE NOTICE '[%] Checkpoint criado: %', now(), checkpoint_name;
|
|
136
|
+
END;
|
|
137
|
+
$$ LANGUAGE plpgsql;
|
|
138
|
+
|
|
139
|
+
-- Function para rollback
|
|
140
|
+
CREATE OR REPLACE FUNCTION rollback_migration(migration_name VARCHAR)
|
|
141
|
+
RETURNS void AS $$
|
|
142
|
+
BEGIN
|
|
143
|
+
UPDATE schema_migrations
|
|
144
|
+
SET rollback_executed = TRUE,
|
|
145
|
+
rollback_at = CURRENT_TIMESTAMP
|
|
146
|
+
WHERE migration_name = rollback_migration.migration_name;
|
|
147
|
+
|
|
148
|
+
RAISE NOTICE '[%] Rollback registrado para: %', now(), migration_name;
|
|
149
|
+
END;
|
|
150
|
+
$$ LANGUAGE plpgsql;
|
|
151
|
+
|
|
152
|
+
-- ============================================================================
|
|
153
|
+
-- SEÇÃO 4: EXECUÇÃO PRINCIPAL DA MIGRAÇÃO
|
|
154
|
+
-- ============================================================================
|
|
155
|
+
|
|
156
|
+
DO $$
|
|
157
|
+
DECLARE
|
|
158
|
+
migration_start_time TIMESTAMP WITH TIME ZONE;
|
|
159
|
+
current_block VARCHAR(100);
|
|
160
|
+
block_start_time TIMESTAMP WITH TIME ZONE;
|
|
161
|
+
block_end_time TIMESTAMP WITH TIME ZONE;
|
|
162
|
+
block_duration INTEGER;
|
|
163
|
+
total_tables INTEGER := 0;
|
|
164
|
+
total_indexes INTEGER := 0;
|
|
165
|
+
total_constraints INTEGER := 0;
|
|
166
|
+
total_triggers INTEGER := 0;
|
|
167
|
+
total_functions INTEGER := 0;
|
|
168
|
+
migration_end_time TIMESTAMP WITH TIME ZONE;
|
|
169
|
+
total_duration INTEGER;
|
|
170
|
+
BEGIN
|
|
171
|
+
migration_start_time := CURRENT_TIMESTAMP;
|
|
172
|
+
|
|
173
|
+
-- ========================================
|
|
174
|
+
-- BLOCO 1: CRIAÇÃO DE TABELAS (001-017)
|
|
175
|
+
-- ========================================
|
|
176
|
+
current_block := 'TABLES_CREATION';
|
|
177
|
+
block_start_time := CURRENT_TIMESTAMP;
|
|
178
|
+
|
|
179
|
+
BEGIN
|
|
180
|
+
RAISE NOTICE '[%] ==========================================', now();
|
|
181
|
+
RAISE NOTICE '[%] BLOCO 1: CRIAÇÃO DE TABELAS', now();
|
|
182
|
+
RAISE NOTICE '[%] ==========================================', now();
|
|
183
|
+
|
|
184
|
+
-- Criar checkpoint
|
|
185
|
+
PERFORM create_migration_checkpoint('start_tables');
|
|
186
|
+
|
|
187
|
+
-- TABELA 001: ACCOUNTS
|
|
188
|
+
RAISE NOTICE '[%] Criando tabela: accounts', now();
|
|
189
|
+
CREATE TABLE IF NOT EXISTS accounts (
|
|
190
|
+
id SERIAL PRIMARY KEY,
|
|
191
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
192
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
193
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
194
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
195
|
+
name VARCHAR(100)
|
|
196
|
+
);
|
|
197
|
+
COMMENT ON TABLE accounts IS 'Tabela base de contas do sistema';
|
|
198
|
+
|
|
199
|
+
-- TABELA 002: USERS
|
|
200
|
+
RAISE NOTICE '[%] Criando tabela: users', now();
|
|
201
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
202
|
+
id SERIAL PRIMARY KEY,
|
|
203
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
204
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
205
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
206
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
207
|
+
id_account INTEGER NOT NULL,
|
|
208
|
+
name VARCHAR(50),
|
|
209
|
+
last_name VARCHAR(255),
|
|
210
|
+
email VARCHAR(255) NOT NULL,
|
|
211
|
+
profile VARCHAR(50) DEFAULT 'collaborator',
|
|
212
|
+
photo VARCHAR(255),
|
|
213
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
214
|
+
CONSTRAINT users_email_valid CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$'),
|
|
215
|
+
CONSTRAINT users_profile_valid CHECK (profile IN ('viewer', 'collaborator', 'admin', 'owner', 'attendant'))
|
|
216
|
+
);
|
|
217
|
+
COMMENT ON TABLE users IS 'Tabela de usuários do sistema';
|
|
218
|
+
|
|
219
|
+
-- TABELA 003: PEOPLE
|
|
220
|
+
RAISE NOTICE '[%] Criando tabela: people', now();
|
|
221
|
+
CREATE TABLE IF NOT EXISTS people (
|
|
222
|
+
id SERIAL PRIMARY KEY,
|
|
223
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
224
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
225
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
226
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
227
|
+
id_account INTEGER NOT NULL,
|
|
228
|
+
full_name VARCHAR(255),
|
|
229
|
+
first_name VARCHAR(255),
|
|
230
|
+
last_name VARCHAR(255),
|
|
231
|
+
photo VARCHAR(255),
|
|
232
|
+
document VARCHAR(50),
|
|
233
|
+
birth_date TIMESTAMP WITH TIME ZONE,
|
|
234
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE
|
|
235
|
+
);
|
|
236
|
+
COMMENT ON TABLE people IS 'Tabela de pessoas do sistema';
|
|
237
|
+
|
|
238
|
+
-- TABELA 004: TAGS
|
|
239
|
+
RAISE NOTICE '[%] Criando tabela: tags', now();
|
|
240
|
+
CREATE TABLE IF NOT EXISTS tags (
|
|
241
|
+
id SERIAL PRIMARY KEY,
|
|
242
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
243
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
244
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
245
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
246
|
+
id_account INTEGER NOT NULL,
|
|
247
|
+
name VARCHAR(255) NOT NULL,
|
|
248
|
+
color VARCHAR(7) DEFAULT '#000000',
|
|
249
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
250
|
+
CONSTRAINT tags_color_valid CHECK (color ~* '^#[0-9A-Fa-f]{6}$')
|
|
251
|
+
);
|
|
252
|
+
COMMENT ON TABLE tags IS 'Tabela de tags do sistema';
|
|
253
|
+
|
|
254
|
+
-- TABELA 005: CREDENTIALS
|
|
255
|
+
RAISE NOTICE '[%] Criando tabela: credentials', now();
|
|
256
|
+
CREATE TABLE IF NOT EXISTS credentials (
|
|
257
|
+
id SERIAL PRIMARY KEY,
|
|
258
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
259
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
260
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
261
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
262
|
+
id_account INTEGER NOT NULL,
|
|
263
|
+
title VARCHAR(255) NOT NULL,
|
|
264
|
+
provider VARCHAR(50) DEFAULT 'web-chat' NOT NULL,
|
|
265
|
+
authType VARCHAR(20) DEFAULT 'none' NOT NULL,
|
|
266
|
+
userName VARCHAR(255),
|
|
267
|
+
password VARCHAR(255),
|
|
268
|
+
apiKey VARCHAR(255),
|
|
269
|
+
token TEXT,
|
|
270
|
+
paramName VARCHAR(255),
|
|
271
|
+
refreshToken TEXT,
|
|
272
|
+
clientSecret VARCHAR(255),
|
|
273
|
+
clientId VARCHAR(255),
|
|
274
|
+
sendIn VARCHAR(20) DEFAULT 'none',
|
|
275
|
+
scheme VARCHAR(20) DEFAULT 'none',
|
|
276
|
+
prefix VARCHAR(100),
|
|
277
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
278
|
+
CONSTRAINT credentials_provider_valid CHECK (provider IN ('web-chat', 'whatsapp-oficial', 'z-api', 'evolution-api', 'telegram', 'facebook', 'instagram', 'custom')),
|
|
279
|
+
CONSTRAINT credentials_authtype_valid CHECK (authType IN ('none', 'basic', 'oauth2', 'apikey')),
|
|
280
|
+
CONSTRAINT credentials_sendin_valid CHECK (sendIn IN ('none', 'bearer', 'header', 'query', 'body')),
|
|
281
|
+
CONSTRAINT credentials_scheme_valid CHECK (scheme IN ('none', 'raw', 'bearer', 'prefix'))
|
|
282
|
+
);
|
|
283
|
+
COMMENT ON TABLE credentials IS 'Tabela de credenciais do sistema';
|
|
284
|
+
|
|
285
|
+
-- TABELA 006: AGENTS
|
|
286
|
+
RAISE NOTICE '[%] Criando tabela: agents', now();
|
|
287
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
288
|
+
id SERIAL PRIMARY KEY,
|
|
289
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
290
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
291
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
292
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
293
|
+
id_account INTEGER NOT NULL,
|
|
294
|
+
title VARCHAR(255) NOT NULL,
|
|
295
|
+
photo VARCHAR(255),
|
|
296
|
+
prompt TEXT,
|
|
297
|
+
delayTyping INTEGER DEFAULT 0,
|
|
298
|
+
waitingTime INTEGER DEFAULT 0,
|
|
299
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
300
|
+
CONSTRAINT agents_delay_typing_valid CHECK (delayTyping >= 0),
|
|
301
|
+
CONSTRAINT agents_waiting_time_valid CHECK (waitingTime >= 0)
|
|
302
|
+
);
|
|
303
|
+
COMMENT ON TABLE agents IS 'Tabela de agentes do sistema';
|
|
304
|
+
|
|
305
|
+
-- TABELA 007: TOOLS
|
|
306
|
+
RAISE NOTICE '[%] Criando tabela: tools', now();
|
|
307
|
+
CREATE TABLE IF NOT EXISTS tools (
|
|
308
|
+
id SERIAL PRIMARY KEY,
|
|
309
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
310
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
311
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
312
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
313
|
+
id_account INTEGER NOT NULL,
|
|
314
|
+
title VARCHAR(255) NOT NULL,
|
|
315
|
+
type VARCHAR(50) DEFAULT 'mcp',
|
|
316
|
+
description TEXT,
|
|
317
|
+
id_credential INTEGER,
|
|
318
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
319
|
+
FOREIGN KEY (id_credential) REFERENCES credentials(id) ON DELETE SET NULL,
|
|
320
|
+
CONSTRAINT tools_type_valid CHECK (type IN ('mcp', 'a2a', 'api', 'internal-agent', 'internal-function', 'native-integration', 'knowledge-base'))
|
|
321
|
+
);
|
|
322
|
+
COMMENT ON TABLE tools IS 'Tabela de ferramentas do sistema';
|
|
323
|
+
|
|
324
|
+
-- TABELA 008: CHANNELS
|
|
325
|
+
RAISE NOTICE '[%] Criando tabela: channels', now();
|
|
326
|
+
CREATE TABLE IF NOT EXISTS channels (
|
|
327
|
+
id SERIAL PRIMARY KEY,
|
|
328
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
329
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
330
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
331
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
332
|
+
id_account INTEGER NOT NULL,
|
|
333
|
+
title VARCHAR(255) NOT NULL,
|
|
334
|
+
provider VARCHAR(50) DEFAULT 'web-chat' NOT NULL,
|
|
335
|
+
url VARCHAR(255),
|
|
336
|
+
active BOOLEAN DEFAULT TRUE,
|
|
337
|
+
id_credential INTEGER,
|
|
338
|
+
handle VARCHAR(255),
|
|
339
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
340
|
+
FOREIGN KEY (id_credential) REFERENCES credentials(id) ON DELETE SET NULL,
|
|
341
|
+
CONSTRAINT channels_provider_valid CHECK (provider IN ('web-chat', 'whatsap-oficial', 'z-api', 'evolution-api', 'telegram', 'facebook', 'instagram')),
|
|
342
|
+
CONSTRAINT channels_url_valid CHECK (url IS NULL OR url ~* '^https?://.*')
|
|
343
|
+
);
|
|
344
|
+
COMMENT ON TABLE channels IS 'Tabela de canais de comunicação do sistema';
|
|
345
|
+
|
|
346
|
+
-- TABELA 009: LEADS
|
|
347
|
+
RAISE NOTICE '[%] Criando tabela: leads', now();
|
|
348
|
+
CREATE TABLE IF NOT EXISTS leads (
|
|
349
|
+
id SERIAL PRIMARY KEY,
|
|
350
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
351
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
352
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
353
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
354
|
+
id_account INTEGER NOT NULL,
|
|
355
|
+
id_person INTEGER NOT NULL,
|
|
356
|
+
email VARCHAR(255),
|
|
357
|
+
phone VARCHAR(20),
|
|
358
|
+
owner_user_id INTEGER,
|
|
359
|
+
current_agent_id INTEGER,
|
|
360
|
+
current_mission_id INTEGER,
|
|
361
|
+
current_objective_id INTEGER,
|
|
362
|
+
notes TEXT,
|
|
363
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
364
|
+
FOREIGN KEY (id_person) REFERENCES people(id) ON DELETE CASCADE,
|
|
365
|
+
FOREIGN KEY (owner_user_id) REFERENCES users(id) ON DELETE SET NULL,
|
|
366
|
+
FOREIGN KEY (current_agent_id) REFERENCES agents(id) ON DELETE SET NULL,
|
|
367
|
+
CONSTRAINT leads_email_valid CHECK (email IS NULL OR email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$'),
|
|
368
|
+
CONSTRAINT leads_phone_valid CHECK (phone IS NULL OR phone ~* '^\+?[1-9]\d{1,14}$')
|
|
369
|
+
);
|
|
370
|
+
COMMENT ON TABLE leads IS 'Tabela de leads do sistema';
|
|
371
|
+
|
|
372
|
+
-- TABELA 010: MISSIONS
|
|
373
|
+
RAISE NOTICE '[%] Criando tabela: missions', now();
|
|
374
|
+
CREATE TABLE IF NOT EXISTS missions (
|
|
375
|
+
id SERIAL PRIMARY KEY,
|
|
376
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
377
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
378
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
379
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
380
|
+
id_account INTEGER NOT NULL,
|
|
381
|
+
id_agent INTEGER,
|
|
382
|
+
title VARCHAR(255) NOT NULL,
|
|
383
|
+
prompt TEXT,
|
|
384
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
385
|
+
FOREIGN KEY (id_agent) REFERENCES agents(id) ON DELETE SET NULL
|
|
386
|
+
);
|
|
387
|
+
COMMENT ON TABLE missions IS 'Tabela de missões do sistema';
|
|
388
|
+
|
|
389
|
+
-- TABELA 011: OBJECTIVES
|
|
390
|
+
RAISE NOTICE '[%] Criando tabela: objectives', now();
|
|
391
|
+
CREATE TABLE IF NOT EXISTS objectives (
|
|
392
|
+
id SERIAL PRIMARY KEY,
|
|
393
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
394
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
395
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
396
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
397
|
+
id_account INTEGER NOT NULL,
|
|
398
|
+
id_agent INTEGER,
|
|
399
|
+
id_mission INTEGER,
|
|
400
|
+
order_number INTEGER DEFAULT 0 NOT NULL,
|
|
401
|
+
title VARCHAR(255) NOT NULL,
|
|
402
|
+
prompt TEXT,
|
|
403
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
404
|
+
FOREIGN KEY (id_agent) REFERENCES agents(id) ON DELETE SET NULL,
|
|
405
|
+
FOREIGN KEY (id_mission) REFERENCES missions(id) ON DELETE SET NULL,
|
|
406
|
+
CONSTRAINT objectives_order_valid CHECK (order_number >= 0)
|
|
407
|
+
);
|
|
408
|
+
COMMENT ON TABLE objectives IS 'Tabela de objetivos do sistema';
|
|
409
|
+
|
|
410
|
+
-- Atualizar referências em LEADS para MISSIONS e OBJECTIVES
|
|
411
|
+
ALTER TABLE leads
|
|
412
|
+
ADD CONSTRAINT fk_leads_current_mission
|
|
413
|
+
FOREIGN KEY (current_mission_id) REFERENCES missions(id) ON DELETE SET NULL;
|
|
414
|
+
|
|
415
|
+
ALTER TABLE leads
|
|
416
|
+
ADD CONSTRAINT fk_leads_current_objective
|
|
417
|
+
FOREIGN KEY (current_objective_id) REFERENCES objectives(id) ON DELETE SET NULL;
|
|
418
|
+
|
|
419
|
+
-- TABELA 012: CONVERSATIONS (sem FK para messages - referência cíclica)
|
|
420
|
+
RAISE NOTICE '[%] Criando tabela: conversations', now();
|
|
421
|
+
CREATE TABLE IF NOT EXISTS conversations (
|
|
422
|
+
id SERIAL PRIMARY KEY,
|
|
423
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
424
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
425
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
426
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
427
|
+
id_account INTEGER NOT NULL,
|
|
428
|
+
id_lead INTEGER,
|
|
429
|
+
id_channel INTEGER,
|
|
430
|
+
id_assignee INTEGER,
|
|
431
|
+
id_last_agent INTEGER,
|
|
432
|
+
last_message_at TIMESTAMP WITH TIME ZONE,
|
|
433
|
+
id_last_message INTEGER,
|
|
434
|
+
stop_ai BOOLEAN DEFAULT FALSE,
|
|
435
|
+
status VARCHAR(20) DEFAULT 'open',
|
|
436
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
437
|
+
FOREIGN KEY (id_lead) REFERENCES leads(id) ON DELETE SET NULL,
|
|
438
|
+
FOREIGN KEY (id_channel) REFERENCES channels(id) ON DELETE SET NULL,
|
|
439
|
+
FOREIGN KEY (id_assignee) REFERENCES users(id) ON DELETE SET NULL,
|
|
440
|
+
FOREIGN KEY (id_last_agent) REFERENCES agents(id) ON DELETE SET NULL,
|
|
441
|
+
CONSTRAINT conversations_status_valid CHECK (status IN ('open', 'pending', 'closed'))
|
|
442
|
+
);
|
|
443
|
+
COMMENT ON TABLE conversations IS 'Tabela de conversas do sistema';
|
|
444
|
+
|
|
445
|
+
-- TABELA 013: OBJECTIVES_TOOLS
|
|
446
|
+
RAISE NOTICE '[%] Criando tabela: objectives_tools', now();
|
|
447
|
+
CREATE TABLE IF NOT EXISTS objectives_tools (
|
|
448
|
+
id SERIAL PRIMARY KEY,
|
|
449
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
450
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
451
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
452
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
453
|
+
id_account INTEGER NOT NULL,
|
|
454
|
+
id_objective INTEGER,
|
|
455
|
+
id_tool INTEGER,
|
|
456
|
+
instructions TEXT,
|
|
457
|
+
wait BOOLEAN DEFAULT FALSE,
|
|
458
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
459
|
+
FOREIGN KEY (id_objective) REFERENCES objectives(id) ON DELETE CASCADE,
|
|
460
|
+
FOREIGN KEY (id_tool) REFERENCES tools(id) ON DELETE CASCADE
|
|
461
|
+
);
|
|
462
|
+
COMMENT ON TABLE objectives_tools IS 'Tabela de relacionamento entre objetivos e ferramentas';
|
|
463
|
+
|
|
464
|
+
-- TABELA 014: MESSAGES (resolve referência cíclica)
|
|
465
|
+
RAISE NOTICE '[%] Criando tabela: messages', now();
|
|
466
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
467
|
+
id SERIAL PRIMARY KEY,
|
|
468
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
469
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
470
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
471
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
472
|
+
id_account INTEGER NOT NULL,
|
|
473
|
+
id_conversation INTEGER,
|
|
474
|
+
id_agent INTEGER,
|
|
475
|
+
id_user INTEGER,
|
|
476
|
+
direction VARCHAR(10) DEFAULT 'in',
|
|
477
|
+
role VARCHAR(20) DEFAULT 'user',
|
|
478
|
+
content TEXT NOT NULL,
|
|
479
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
480
|
+
FOREIGN KEY (id_conversation) REFERENCES conversations(id) ON DELETE CASCADE,
|
|
481
|
+
FOREIGN KEY (id_agent) REFERENCES agents(id) ON DELETE SET NULL,
|
|
482
|
+
FOREIGN KEY (id_user) REFERENCES users(id) ON DELETE SET NULL,
|
|
483
|
+
CONSTRAINT messages_direction_valid CHECK (direction IN ('in', 'out')),
|
|
484
|
+
CONSTRAINT messages_role_valid CHECK (role IN ('user', 'agent', 'system', 'assistant', 'note'))
|
|
485
|
+
);
|
|
486
|
+
COMMENT ON TABLE messages IS 'Tabela de mensagens do sistema';
|
|
487
|
+
|
|
488
|
+
-- TABELA 015: COMPANIES
|
|
489
|
+
RAISE NOTICE '[%] Criando tabela: companies', now();
|
|
490
|
+
CREATE TABLE IF NOT EXISTS companies (
|
|
491
|
+
id SERIAL PRIMARY KEY,
|
|
492
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
493
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
494
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
495
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
496
|
+
id_account INTEGER NOT NULL,
|
|
497
|
+
id_lead INTEGER NOT NULL,
|
|
498
|
+
name VARCHAR(255),
|
|
499
|
+
website VARCHAR(255),
|
|
500
|
+
document VARCHAR(50),
|
|
501
|
+
notes TEXT,
|
|
502
|
+
industry VARCHAR(255),
|
|
503
|
+
size VARCHAR(255),
|
|
504
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
505
|
+
FOREIGN KEY (id_lead) REFERENCES leads(id) ON DELETE CASCADE,
|
|
506
|
+
CONSTRAINT companies_website_valid CHECK (website IS NULL OR website ~* '^https?://.*')
|
|
507
|
+
);
|
|
508
|
+
COMMENT ON TABLE companies IS 'Tabela de empresas do sistema';
|
|
509
|
+
|
|
510
|
+
-- TABELA 016: CONVERSATIONS_TAGS
|
|
511
|
+
RAISE NOTICE '[%] Criando tabela: conversations_tags', now();
|
|
512
|
+
CREATE TABLE IF NOT EXISTS conversations_tags (
|
|
513
|
+
id SERIAL PRIMARY KEY,
|
|
514
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
515
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
516
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
517
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
518
|
+
id_account INTEGER NOT NULL,
|
|
519
|
+
id_conversation INTEGER,
|
|
520
|
+
id_tag INTEGER,
|
|
521
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
522
|
+
FOREIGN KEY (id_conversation) REFERENCES conversations(id) ON DELETE CASCADE,
|
|
523
|
+
FOREIGN KEY (id_tag) REFERENCES tags(id) ON DELETE CASCADE
|
|
524
|
+
);
|
|
525
|
+
COMMENT ON TABLE conversations_tags IS 'Tabela de relacionamento entre conversas e tags';
|
|
526
|
+
|
|
527
|
+
-- TABELA 017: LEADS_TAGS
|
|
528
|
+
RAISE NOTICE '[%] Criando tabela: leads_tags', now();
|
|
529
|
+
CREATE TABLE IF NOT EXISTS leads_tags (
|
|
530
|
+
id SERIAL PRIMARY KEY,
|
|
531
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
532
|
+
datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
533
|
+
datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
534
|
+
datetime_del TIMESTAMP WITH TIME ZONE NULL,
|
|
535
|
+
id_account INTEGER NOT NULL,
|
|
536
|
+
id_lead INTEGER,
|
|
537
|
+
id_tag INTEGER,
|
|
538
|
+
FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
|
|
539
|
+
FOREIGN KEY (id_lead) REFERENCES leads(id) ON DELETE CASCADE,
|
|
540
|
+
FOREIGN KEY (id_tag) REFERENCES tags(id) ON DELETE CASCADE
|
|
541
|
+
);
|
|
542
|
+
COMMENT ON TABLE leads_tags IS 'Tabela de relacionamento entre leads e tags';
|
|
543
|
+
|
|
544
|
+
-- Contar tabelas criadas
|
|
545
|
+
SELECT COUNT(*) INTO total_tables
|
|
546
|
+
FROM information_schema.tables
|
|
547
|
+
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
|
548
|
+
AND table_name IN ('accounts', 'users', 'people', 'tags', 'credentials', 'agents', 'tools', 'channels', 'leads', 'missions', 'objectives', 'conversations', 'objectives_tools', 'messages', 'companies', 'conversations_tags', 'leads_tags');
|
|
549
|
+
|
|
550
|
+
block_end_time := CURRENT_TIMESTAMP;
|
|
551
|
+
block_duration := EXTRACT(EPOCH FROM (block_end_time - block_start_time)) * 1000;
|
|
552
|
+
|
|
553
|
+
PERFORM create_migration_checkpoint('tables_completed', json_build_object('total_tables', total_tables, 'duration_ms', block_duration));
|
|
554
|
+
|
|
555
|
+
RAISE NOTICE '[%] ✓ BLOCO 1 CONCLUÍDO: % tabelas criadas em %ms', now(), total_tables, block_duration;
|
|
556
|
+
|
|
557
|
+
EXCEPTION WHEN OTHERS THEN
|
|
558
|
+
PERFORM rollback_migration('TABLES_CREATION');
|
|
559
|
+
RAISE EXCEPTION 'FALHA no BLOCO 1 (Criação de tabelas): %', SQLERRM;
|
|
560
|
+
END;
|
|
561
|
+
|
|
562
|
+
-- ========================================
|
|
563
|
+
-- BLOCO 2: CRIAÇÃO DE ÍNDICES
|
|
564
|
+
-- ========================================
|
|
565
|
+
current_block := 'INDEXES_CREATION';
|
|
566
|
+
block_start_time := CURRENT_TIMESTAMP;
|
|
567
|
+
|
|
568
|
+
BEGIN
|
|
569
|
+
RAISE NOTICE '[%] ==========================================', now();
|
|
570
|
+
RAISE NOTICE '[%] BLOCO 2: CRIAÇÃO DE ÍNDICES', now();
|
|
571
|
+
RAISE NOTICE '[%] ==========================================', now();
|
|
572
|
+
|
|
573
|
+
PERFORM create_migration_checkpoint('start_indexes');
|
|
574
|
+
|
|
575
|
+
-- ÍNDICES ACCOUNTS
|
|
576
|
+
RAISE NOTICE '[%] Criando índices para: accounts', now();
|
|
577
|
+
CREATE INDEX IF NOT EXISTS idx_accounts_deleted ON accounts(deleted);
|
|
578
|
+
CREATE INDEX IF NOT EXISTS idx_accounts_datetime_add ON accounts(datetime_add);
|
|
579
|
+
CREATE INDEX IF NOT EXISTS idx_accounts_name_search ON accounts USING gin(to_tsvector('portuguese', name)) WHERE name IS NOT NULL;
|
|
580
|
+
CREATE INDEX IF NOT EXISTS idx_accounts_not_deleted ON accounts(id, deleted) WHERE deleted = FALSE;
|
|
581
|
+
|
|
582
|
+
-- ÍNDICES USERS
|
|
583
|
+
RAISE NOTICE '[%] Criando índices para: users', now();
|
|
584
|
+
CREATE INDEX IF NOT EXISTS idx_users_id_account ON users(id_account);
|
|
585
|
+
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
|
586
|
+
CREATE INDEX IF NOT EXISTS idx_users_profile ON users(profile);
|
|
587
|
+
CREATE INDEX IF NOT EXISTS idx_users_deleted ON users(deleted);
|
|
588
|
+
CREATE INDEX IF NOT EXISTS idx_users_name_search ON users USING gin(to_tsvector('portuguese', name)) WHERE name IS NOT NULL;
|
|
589
|
+
CREATE INDEX IF NOT EXISTS idx_users_account_not_deleted ON users(id_account, deleted) WHERE deleted = FALSE;
|
|
590
|
+
|
|
591
|
+
-- ÍNDICES PEOPLE
|
|
592
|
+
RAISE NOTICE '[%] Criando índices para: people', now();
|
|
593
|
+
CREATE INDEX IF NOT EXISTS idx_people_id_account ON people(id_account);
|
|
594
|
+
CREATE INDEX IF NOT EXISTS idx_people_full_name ON people(full_name);
|
|
595
|
+
CREATE INDEX IF NOT EXISTS idx_people_document ON people(document);
|
|
596
|
+
CREATE INDEX IF NOT EXISTS idx_people_birth_date ON people(birth_date);
|
|
597
|
+
CREATE INDEX IF NOT EXISTS idx_people_deleted ON people(deleted);
|
|
598
|
+
CREATE INDEX IF NOT EXISTS idx_people_full_name_search ON people USING gin(to_tsvector('portuguese', full_name)) WHERE full_name IS NOT NULL;
|
|
599
|
+
|
|
600
|
+
-- ÍNDICES TAGS
|
|
601
|
+
RAISE NOTICE '[%] Criando índices para: tags', now();
|
|
602
|
+
CREATE INDEX IF NOT EXISTS idx_tags_id_account ON tags(id_account);
|
|
603
|
+
CREATE INDEX IF NOT EXISTS idx_tags_name ON tags(name);
|
|
604
|
+
CREATE INDEX IF NOT EXISTS idx_tags_color ON tags(color);
|
|
605
|
+
CREATE INDEX IF NOT EXISTS idx_tags_deleted ON tags(deleted);
|
|
606
|
+
CREATE INDEX IF NOT EXISTS idx_tags_name_search ON tags USING gin(to_tsvector('portuguese', name)) WHERE name IS NOT NULL;
|
|
607
|
+
|
|
608
|
+
-- ÍNDICES CREDENTIALS
|
|
609
|
+
RAISE NOTICE '[%] Criando índices para: credentials', now();
|
|
610
|
+
CREATE INDEX IF NOT EXISTS idx_credentials_id_account ON credentials(id_account);
|
|
611
|
+
CREATE INDEX IF NOT EXISTS idx_credentials_provider ON credentials(provider);
|
|
612
|
+
CREATE INDEX IF NOT EXISTS idx_credentials_authtype ON credentials(authType);
|
|
613
|
+
CREATE INDEX IF NOT EXISTS idx_credentials_deleted ON credentials(deleted);
|
|
614
|
+
CREATE INDEX IF NOT EXISTS idx_credentials_title_search ON credentials USING gin(to_tsvector('portuguese', title)) WHERE title IS NOT NULL;
|
|
615
|
+
|
|
616
|
+
-- ÍNDICES AGENTS
|
|
617
|
+
RAISE NOTICE '[%] Criando índices para: agents', now();
|
|
618
|
+
CREATE INDEX IF NOT EXISTS idx_agents_id_account ON agents(id_account);
|
|
619
|
+
CREATE INDEX IF NOT EXISTS idx_agents_title ON agents(title);
|
|
620
|
+
CREATE INDEX IF NOT EXISTS idx_agents_deleted ON agents(deleted);
|
|
621
|
+
CREATE INDEX IF NOT EXISTS idx_agents_title_search ON agents USING gin(to_tsvector('portuguese', title)) WHERE title IS NOT NULL;
|
|
622
|
+
|
|
623
|
+
-- ÍNDICES TOOLS
|
|
624
|
+
RAISE NOTICE '[%] Criando índices para: tools', now();
|
|
625
|
+
CREATE INDEX IF NOT EXISTS idx_tools_id_account ON tools(id_account);
|
|
626
|
+
CREATE INDEX IF NOT EXISTS idx_tools_type ON tools(type);
|
|
627
|
+
CREATE INDEX IF NOT EXISTS idx_tools_id_credential ON tools(id_credential);
|
|
628
|
+
CREATE INDEX IF NOT EXISTS idx_tools_deleted ON tools(deleted);
|
|
629
|
+
CREATE INDEX IF NOT EXISTS idx_tools_title_search ON tools USING gin(to_tsvector('portuguese', title)) WHERE title IS NOT NULL;
|
|
630
|
+
|
|
631
|
+
-- ÍNDICES CHANNELS
|
|
632
|
+
RAISE NOTICE '[%] Criando índices para: channels', now();
|
|
633
|
+
CREATE INDEX IF NOT EXISTS idx_channels_id_account ON channels(id_account);
|
|
634
|
+
CREATE INDEX IF NOT EXISTS idx_channels_provider ON channels(provider);
|
|
635
|
+
CREATE INDEX IF NOT EXISTS idx_channels_active ON channels(active);
|
|
636
|
+
CREATE INDEX IF NOT EXISTS idx_channels_handle ON channels(handle);
|
|
637
|
+
CREATE INDEX IF NOT EXISTS idx_channels_id_credential ON channels(id_credential);
|
|
638
|
+
CREATE INDEX IF NOT EXISTS idx_channels_deleted ON channels(deleted);
|
|
639
|
+
|
|
640
|
+
-- ÍNDICES LEADS
|
|
641
|
+
RAISE NOTICE '[%] Criando índices para: leads', now();
|
|
642
|
+
CREATE INDEX IF NOT EXISTS idx_leads_id_account ON leads(id_account);
|
|
643
|
+
CREATE INDEX IF NOT EXISTS idx_leads_id_person ON leads(id_person);
|
|
644
|
+
CREATE INDEX IF NOT EXISTS idx_leads_email ON leads(email);
|
|
645
|
+
CREATE INDEX IF NOT EXISTS idx_leads_phone ON leads(phone);
|
|
646
|
+
CREATE INDEX IF NOT EXISTS idx_leads_current_agent_id ON leads(current_agent_id);
|
|
647
|
+
CREATE INDEX IF NOT EXISTS idx_leads_deleted ON leads(deleted);
|
|
648
|
+
|
|
649
|
+
-- ÍNDICES MISSIONS
|
|
650
|
+
RAISE NOTICE '[%] Criando índices para: missions', now();
|
|
651
|
+
CREATE INDEX IF NOT EXISTS idx_missions_id_account ON missions(id_account);
|
|
652
|
+
CREATE INDEX IF NOT EXISTS idx_missions_id_agent ON missions(id_agent);
|
|
653
|
+
CREATE INDEX IF NOT EXISTS idx_missions_title ON missions(title);
|
|
654
|
+
CREATE INDEX IF NOT EXISTS idx_missions_deleted ON missions(deleted);
|
|
655
|
+
|
|
656
|
+
-- ÍNDICES OBJECTIVES
|
|
657
|
+
RAISE NOTICE '[%] Criando índices para: objectives', now();
|
|
658
|
+
CREATE INDEX IF NOT EXISTS idx_objectives_id_account ON objectives(id_account);
|
|
659
|
+
CREATE INDEX IF NOT EXISTS idx_objectives_id_agent ON objectives(id_agent);
|
|
660
|
+
CREATE INDEX IF NOT EXISTS idx_objectives_id_mission ON objectives(id_mission);
|
|
661
|
+
CREATE INDEX IF NOT EXISTS idx_objectives_order ON objectives(order_number);
|
|
662
|
+
CREATE INDEX IF NOT EXISTS idx_objectives_deleted ON objectives(deleted);
|
|
663
|
+
|
|
664
|
+
-- ÍNDICES CONVERSATIONS
|
|
665
|
+
RAISE NOTICE '[%] Criando índices para: conversations', now();
|
|
666
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_id_account ON conversations(id_account);
|
|
667
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_id_lead ON conversations(id_lead);
|
|
668
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_id_channel ON conversations(id_channel);
|
|
669
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_status ON conversations(status);
|
|
670
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_last_message_at ON conversations(last_message_at);
|
|
671
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_deleted ON conversations(deleted);
|
|
672
|
+
|
|
673
|
+
-- ÍNDICES OBJECTIVES_TOOLS
|
|
674
|
+
RAISE NOTICE '[%] Criando índices para: objectives_tools', now();
|
|
675
|
+
CREATE INDEX IF NOT EXISTS idx_objectives_tools_id_account ON objectives_tools(id_account);
|
|
676
|
+
CREATE INDEX IF NOT EXISTS idx_objectives_tools_id_objective ON objectives_tools(id_objective);
|
|
677
|
+
CREATE INDEX IF NOT EXISTS idx_objectives_tools_id_tool ON objectives_tools(id_tool);
|
|
678
|
+
CREATE INDEX IF NOT EXISTS idx_objectives_tools_wait ON objectives_tools(wait);
|
|
679
|
+
CREATE INDEX IF NOT EXISTS idx_objectives_tools_deleted ON objectives_tools(deleted);
|
|
680
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_objectives_tools_unique ON objectives_tools(id_objective, id_tool) WHERE deleted = FALSE;
|
|
681
|
+
|
|
682
|
+
-- ÍNDICES MESSAGES
|
|
683
|
+
RAISE NOTICE '[%] Criando índices para: messages', now();
|
|
684
|
+
CREATE INDEX IF NOT EXISTS idx_messages_id_account ON messages(id_account);
|
|
685
|
+
CREATE INDEX IF NOT EXISTS idx_messages_id_conversation ON messages(id_conversation);
|
|
686
|
+
CREATE INDEX IF NOT EXISTS idx_messages_direction ON messages(direction);
|
|
687
|
+
CREATE INDEX IF NOT EXISTS idx_messages_role ON messages(role);
|
|
688
|
+
CREATE INDEX IF NOT EXISTS idx_messages_datetime_add ON messages(datetime_add);
|
|
689
|
+
CREATE INDEX IF NOT EXISTS idx_messages_deleted ON messages(deleted);
|
|
690
|
+
CREATE INDEX IF NOT EXISTS idx_messages_content_search ON messages USING gin(to_tsvector('portuguese', content)) WHERE content IS NOT NULL;
|
|
691
|
+
|
|
692
|
+
-- ÍNDICES COMPANIES
|
|
693
|
+
RAISE NOTICE '[%] Criando índices para: companies', now();
|
|
694
|
+
CREATE INDEX IF NOT EXISTS idx_companies_id_account ON companies(id_account);
|
|
695
|
+
CREATE INDEX IF NOT EXISTS idx_companies_id_lead ON companies(id_lead);
|
|
696
|
+
CREATE INDEX IF NOT EXISTS idx_companies_name ON companies(name);
|
|
697
|
+
CREATE INDEX IF NOT EXISTS idx_companies_document ON companies(document);
|
|
698
|
+
CREATE INDEX IF NOT EXISTS idx_companies_deleted ON companies(deleted);
|
|
699
|
+
|
|
700
|
+
-- ÍNDICES CONVERSATIONS_TAGS
|
|
701
|
+
RAISE NOTICE '[%] Criando índices para: conversations_tags', now();
|
|
702
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_tags_id_account ON conversations_tags(id_account);
|
|
703
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_tags_id_conversation ON conversations_tags(id_conversation);
|
|
704
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_tags_id_tag ON conversations_tags(id_tag);
|
|
705
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_tags_deleted ON conversations_tags(deleted);
|
|
706
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_conversations_tags_unique ON conversations_tags(id_conversation, id_tag) WHERE deleted = FALSE;
|
|
707
|
+
|
|
708
|
+
-- ÍNDICES LEADS_TAGS
|
|
709
|
+
RAISE NOTICE '[%] Criando índices para: leads_tags', now();
|
|
710
|
+
CREATE INDEX IF NOT EXISTS idx_leads_tags_id_account ON leads_tags(id_account);
|
|
711
|
+
CREATE INDEX IF NOT EXISTS idx_leads_tags_id_lead ON leads_tags(id_lead);
|
|
712
|
+
CREATE INDEX IF NOT EXISTS idx_leads_tags_id_tag ON leads_tags(id_tag);
|
|
713
|
+
CREATE INDEX IF NOT EXISTS idx_leads_tags_deleted ON leads_tags(deleted);
|
|
714
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_leads_tags_unique ON leads_tags(id_lead, id_tag) WHERE deleted = FALSE;
|
|
715
|
+
|
|
716
|
+
-- Contar índices criados
|
|
717
|
+
SELECT COUNT(*) INTO total_indexes
|
|
718
|
+
FROM pg_indexes
|
|
719
|
+
WHERE schemaname = 'public' AND indexname LIKE 'idx_%';
|
|
720
|
+
|
|
721
|
+
block_end_time := CURRENT_TIMESTAMP;
|
|
722
|
+
block_duration := EXTRACT(EPOCH FROM (block_end_time - block_start_time)) * 1000;
|
|
723
|
+
|
|
724
|
+
PERFORM create_migration_checkpoint('indexes_completed', json_build_object('total_indexes', total_indexes, 'duration_ms', block_duration));
|
|
725
|
+
|
|
726
|
+
RAISE NOTICE '[%] ✓ BLOCO 2 CONCLUÍDO: % índices criados em %ms', now(), total_indexes, block_duration;
|
|
727
|
+
|
|
728
|
+
EXCEPTION WHEN OTHERS THEN
|
|
729
|
+
PERFORM rollback_migration('INDEXES_CREATION');
|
|
730
|
+
RAISE EXCEPTION 'FALHA no BLOCO 2 (Criação de índices): %', SQLERRM;
|
|
731
|
+
END;
|
|
732
|
+
|
|
733
|
+
-- ========================================
|
|
734
|
+
-- BLOCO 3: CONSTRAINTS ADICIONAIS
|
|
735
|
+
-- ========================================
|
|
736
|
+
current_block := 'CONSTRAINTS_APPLICATION';
|
|
737
|
+
block_start_time := CURRENT_TIMESTAMP;
|
|
738
|
+
|
|
739
|
+
BEGIN
|
|
740
|
+
RAISE NOTICE '[%] ==========================================', now();
|
|
741
|
+
RAISE NOTICE '[%] BLOCO 3: CONSTRAINTS ADICIONAIS', now();
|
|
742
|
+
RAISE NOTICE '[%] ==========================================', now();
|
|
743
|
+
|
|
744
|
+
PERFORM create_migration_checkpoint('start_constraints');
|
|
745
|
+
|
|
746
|
+
-- REFERÊNCIA CÍCLICA CRÍTICA: conversations → messages
|
|
747
|
+
RAISE NOTICE '[%] Resolvendo referência cíclica conversations ↔ messages', now();
|
|
748
|
+
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
|
|
749
|
+
WHERE constraint_name = 'fk_conversations_last_message'
|
|
750
|
+
AND table_name = 'conversations') THEN
|
|
751
|
+
ALTER TABLE conversations
|
|
752
|
+
ADD CONSTRAINT fk_conversations_last_message
|
|
753
|
+
FOREIGN KEY (id_last_message) REFERENCES messages(id)
|
|
754
|
+
ON DELETE SET NULL;
|
|
755
|
+
RAISE NOTICE '[%] ✓ Referência cíclica conversations → messages resolvida', now();
|
|
756
|
+
END IF;
|
|
757
|
+
|
|
758
|
+
-- EXPANDIR CHECK CONSTRAINTS
|
|
759
|
+
RAISE NOTICE '[%] Expandindo constraints de validação', now();
|
|
760
|
+
|
|
761
|
+
-- Users profile expandido
|
|
762
|
+
IF EXISTS (SELECT 1 FROM information_schema.table_constraints
|
|
763
|
+
WHERE constraint_name = 'users_profile_valid'
|
|
764
|
+
AND table_name = 'users') THEN
|
|
765
|
+
ALTER TABLE users DROP CONSTRAINT users_profile_valid;
|
|
766
|
+
END IF;
|
|
767
|
+
ALTER TABLE users
|
|
768
|
+
ADD CONSTRAINT chk_users_profile_expanded
|
|
769
|
+
CHECK (profile IN ('viewer', 'collaborator', 'admin', 'owner', 'attendant', 'user', 'agent'));
|
|
770
|
+
|
|
771
|
+
-- Channels provider expandido
|
|
772
|
+
IF EXISTS (SELECT 1 FROM information_schema.table_constraints
|
|
773
|
+
WHERE constraint_name = 'channels_provider_valid'
|
|
774
|
+
AND table_name = 'channels') THEN
|
|
775
|
+
ALTER TABLE channels DROP CONSTRAINT channels_provider_valid;
|
|
776
|
+
END IF;
|
|
777
|
+
ALTER TABLE channels
|
|
778
|
+
ADD CONSTRAINT chk_channels_provider_expanded
|
|
779
|
+
CHECK (provider IN ('web-chat', 'whatsap-oficial', 'z-api', 'evolution-api', 'telegram', 'facebook', 'instagram', 'whatsapp', 'discord', 'slack', 'email'));
|
|
780
|
+
|
|
781
|
+
-- Messages direction e role expandidos
|
|
782
|
+
IF EXISTS (SELECT 1 FROM information_schema.table_constraints
|
|
783
|
+
WHERE constraint_name = 'messages_direction_valid'
|
|
784
|
+
AND table_name = 'messages') THEN
|
|
785
|
+
ALTER TABLE messages DROP CONSTRAINT messages_direction_valid;
|
|
786
|
+
END IF;
|
|
787
|
+
ALTER TABLE messages
|
|
788
|
+
ADD CONSTRAINT chk_messages_direction_expanded
|
|
789
|
+
CHECK (direction IN ('in', 'out', 'incoming', 'outgoing'));
|
|
790
|
+
|
|
791
|
+
IF EXISTS (SELECT 1 FROM information_schema.table_constraints
|
|
792
|
+
WHERE constraint_name = 'messages_role_valid'
|
|
793
|
+
AND table_name = 'messages') THEN
|
|
794
|
+
ALTER TABLE messages DROP CONSTRAINT messages_role_valid;
|
|
795
|
+
END IF;
|
|
796
|
+
ALTER TABLE messages
|
|
797
|
+
ADD CONSTRAINT chk_messages_role_expanded
|
|
798
|
+
CHECK (role IN ('user', 'agent', 'system', 'assistant', 'note', 'function'));
|
|
799
|
+
|
|
800
|
+
-- CONSTRAINTS DE INTEGRIDADE TEMPORAL
|
|
801
|
+
RAISE NOTICE '[%] Adicionando constraints de integridade temporal', now();
|
|
802
|
+
|
|
803
|
+
-- Conversations datetime consistency
|
|
804
|
+
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
|
|
805
|
+
WHERE constraint_name = 'chk_conversations_datetime_consistency'
|
|
806
|
+
AND table_name = 'conversations') THEN
|
|
807
|
+
ALTER TABLE conversations
|
|
808
|
+
ADD CONSTRAINT chk_conversations_datetime_consistency
|
|
809
|
+
CHECK (datetime_alt >= datetime_add);
|
|
810
|
+
END IF;
|
|
811
|
+
|
|
812
|
+
-- Messages datetime consistency
|
|
813
|
+
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
|
|
814
|
+
WHERE constraint_name = 'chk_messages_datetime_consistency'
|
|
815
|
+
AND table_name = 'messages') THEN
|
|
816
|
+
ALTER TABLE messages
|
|
817
|
+
ADD CONSTRAINT chk_messages_datetime_consistency
|
|
818
|
+
CHECK (datetime_alt >= datetime_add);
|
|
819
|
+
END IF;
|
|
820
|
+
|
|
821
|
+
-- CONSTRAINTS DE SEGURANÇA
|
|
822
|
+
RAISE NOTICE '[%] Adicionando constraints de segurança', now();
|
|
823
|
+
|
|
824
|
+
-- Accounts name obrigatório
|
|
825
|
+
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
|
|
826
|
+
WHERE constraint_name = 'chk_accounts_name_required'
|
|
827
|
+
AND table_name = 'accounts') THEN
|
|
828
|
+
ALTER TABLE accounts
|
|
829
|
+
ADD CONSTRAINT chk_accounts_name_required
|
|
830
|
+
CHECK (name IS NOT NULL AND trim(name) != '');
|
|
831
|
+
END IF;
|
|
832
|
+
|
|
833
|
+
-- Users email obrigatório
|
|
834
|
+
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
|
|
835
|
+
WHERE constraint_name = 'chk_users_email_required'
|
|
836
|
+
AND table_name = 'users') THEN
|
|
837
|
+
ALTER TABLE users
|
|
838
|
+
ADD CONSTRAINT chk_users_email_required
|
|
839
|
+
CHECK (email IS NOT NULL AND trim(email) != '');
|
|
840
|
+
END IF;
|
|
841
|
+
|
|
842
|
+
-- Messages content obrigatório
|
|
843
|
+
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
|
|
844
|
+
WHERE constraint_name = 'chk_messages_content_required'
|
|
845
|
+
AND table_name = 'messages') THEN
|
|
846
|
+
ALTER TABLE messages
|
|
847
|
+
ADD CONSTRAINT chk_messages_content_required
|
|
848
|
+
CHECK (content IS NOT NULL AND trim(content) != '');
|
|
849
|
+
END IF;
|
|
850
|
+
|
|
851
|
+
-- Contar constraints
|
|
852
|
+
SELECT COUNT(*) INTO total_constraints
|
|
853
|
+
FROM information_schema.table_constraints
|
|
854
|
+
WHERE table_schema = current_schema();
|
|
855
|
+
|
|
856
|
+
block_end_time := CURRENT_TIMESTAMP;
|
|
857
|
+
block_duration := EXTRACT(EPOCH FROM (block_end_time - block_start_time)) * 1000;
|
|
858
|
+
|
|
859
|
+
PERFORM create_migration_checkpoint('constraints_completed', json_build_object('total_constraints', total_constraints, 'duration_ms', block_duration));
|
|
860
|
+
|
|
861
|
+
RAISE NOTICE '[%] ✓ BLOCO 3 CONCLUÍDO: % constraints criadas/atualizadas em %ms', now(), total_constraints, block_duration;
|
|
862
|
+
|
|
863
|
+
EXCEPTION WHEN OTHERS THEN
|
|
864
|
+
PERFORM rollback_migration('CONSTRAINTS_APPLICATION');
|
|
865
|
+
RAISE EXCEPTION 'FALHA no BLOCO 3 (Constraints): %', SQLERRM;
|
|
866
|
+
END;
|
|
867
|
+
|
|
868
|
+
-- ========================================
|
|
869
|
+
-- BLOCO 4: TRIGGERS E FUNCTIONS
|
|
870
|
+
-- ========================================
|
|
871
|
+
current_block := 'TRIGGERS_FUNCTIONS';
|
|
872
|
+
block_start_time := CURRENT_TIMESTAMP;
|
|
873
|
+
|
|
874
|
+
BEGIN
|
|
875
|
+
RAISE NOTICE '[%] ==========================================', now();
|
|
876
|
+
RAISE NOTICE '[%] BLOCO 4: TRIGGERS E FUNCTIONS', now();
|
|
877
|
+
RAISE NOTICE '[%] ==========================================', now();
|
|
878
|
+
|
|
879
|
+
PERFORM create_migration_checkpoint('start_triggers');
|
|
880
|
+
|
|
881
|
+
-- TABELA DE AUDITORIA
|
|
882
|
+
RAISE NOTICE '[%] Criando tabela de auditoria', now();
|
|
883
|
+
CREATE TABLE IF NOT EXISTS audit_log (
|
|
884
|
+
id SERIAL PRIMARY KEY,
|
|
885
|
+
table_name VARCHAR(100) NOT NULL,
|
|
886
|
+
operation_type VARCHAR(10) NOT NULL,
|
|
887
|
+
record_id INTEGER NOT NULL,
|
|
888
|
+
old_values JSONB,
|
|
889
|
+
new_values JSONB,
|
|
890
|
+
changed_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
891
|
+
changed_by VARCHAR(100) DEFAULT current_user,
|
|
892
|
+
CONSTRAINT audit_log_operation_valid CHECK (operation_type IN ('INSERT', 'UPDATE', 'DELETE'))
|
|
893
|
+
);
|
|
894
|
+
|
|
895
|
+
CREATE INDEX IF NOT EXISTS idx_audit_log_table_name ON audit_log(table_name);
|
|
896
|
+
CREATE INDEX IF NOT EXISTS idx_audit_log_operation_type ON audit_log(operation_type);
|
|
897
|
+
CREATE INDEX IF NOT EXISTS idx_audit_log_changed_at ON audit_log(changed_at);
|
|
898
|
+
|
|
899
|
+
-- FUNCTIONS UNIVERSAIS
|
|
900
|
+
RAISE NOTICE '[%] Criando functions universais', now();
|
|
901
|
+
|
|
902
|
+
-- Function para atualização automática de datetime_alt
|
|
903
|
+
CREATE OR REPLACE FUNCTION update_datetime_alt()
|
|
904
|
+
RETURNS TRIGGER AS $$
|
|
905
|
+
BEGIN
|
|
906
|
+
NEW.datetime_alt = CURRENT_TIMESTAMP;
|
|
907
|
+
RETURN NEW;
|
|
908
|
+
END;
|
|
909
|
+
$$ LANGUAGE plpgsql;
|
|
910
|
+
|
|
911
|
+
-- Function para auditoria
|
|
912
|
+
CREATE OR REPLACE FUNCTION audit_changes()
|
|
913
|
+
RETURNS TRIGGER AS $$
|
|
914
|
+
DECLARE
|
|
915
|
+
operation_type VARCHAR(10);
|
|
916
|
+
table_name VARCHAR(100);
|
|
917
|
+
BEGIN
|
|
918
|
+
operation_type := TG_OP;
|
|
919
|
+
table_name := TG_TABLE_NAME;
|
|
920
|
+
|
|
921
|
+
INSERT INTO audit_log (
|
|
922
|
+
table_name,
|
|
923
|
+
operation_type,
|
|
924
|
+
record_id,
|
|
925
|
+
old_values,
|
|
926
|
+
new_values,
|
|
927
|
+
changed_at,
|
|
928
|
+
changed_by
|
|
929
|
+
) VALUES (
|
|
930
|
+
table_name,
|
|
931
|
+
operation_type,
|
|
932
|
+
COALESCE(NEW.id, OLD.id),
|
|
933
|
+
CASE WHEN operation_type != 'INSERT' THEN row_to_json(OLD) END,
|
|
934
|
+
CASE WHEN operation_type != 'DELETE' THEN row_to_json(NEW) END,
|
|
935
|
+
CURRENT_TIMESTAMP,
|
|
936
|
+
current_user
|
|
937
|
+
);
|
|
938
|
+
|
|
939
|
+
RETURN COALESCE(NEW, OLD);
|
|
940
|
+
END;
|
|
941
|
+
$$ LANGUAGE plpgsql;
|
|
942
|
+
|
|
943
|
+
-- Function para atualizar última mensagem da conversa
|
|
944
|
+
CREATE OR REPLACE FUNCTION update_conversation_last_message()
|
|
945
|
+
RETURNS TRIGGER AS $$
|
|
946
|
+
BEGIN
|
|
947
|
+
UPDATE conversations
|
|
948
|
+
SET id_last_message = NEW.id,
|
|
949
|
+
last_message_at = NEW.datetime_add
|
|
950
|
+
WHERE id = NEW.id_conversation;
|
|
951
|
+
|
|
952
|
+
RETURN NEW;
|
|
953
|
+
END;
|
|
954
|
+
$$ LANGUAGE plpgsql;
|
|
955
|
+
|
|
956
|
+
-- CRIAÇÃO DE TRIGGERS
|
|
957
|
+
RAISE NOTICE '[%] Criando triggers para todas as tabelas', now();
|
|
958
|
+
|
|
959
|
+
-- Triggers para atualização automática de datetime_alt
|
|
960
|
+
DROP TRIGGER IF EXISTS trigger_accounts_update_datetime ON accounts;
|
|
961
|
+
CREATE TRIGGER trigger_accounts_update_datetime
|
|
962
|
+
BEFORE UPDATE ON accounts
|
|
963
|
+
FOR EACH ROW
|
|
964
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
965
|
+
|
|
966
|
+
DROP TRIGGER IF EXISTS trigger_users_update_datetime ON users;
|
|
967
|
+
CREATE TRIGGER trigger_users_update_datetime
|
|
968
|
+
BEFORE UPDATE ON users
|
|
969
|
+
FOR EACH ROW
|
|
970
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
971
|
+
|
|
972
|
+
DROP TRIGGER IF EXISTS trigger_people_update_datetime ON people;
|
|
973
|
+
CREATE TRIGGER trigger_people_update_datetime
|
|
974
|
+
BEFORE UPDATE ON people
|
|
975
|
+
FOR EACH ROW
|
|
976
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
977
|
+
|
|
978
|
+
DROP TRIGGER IF EXISTS trigger_tags_update_datetime ON tags;
|
|
979
|
+
CREATE TRIGGER trigger_tags_update_datetime
|
|
980
|
+
BEFORE UPDATE ON tags
|
|
981
|
+
FOR EACH ROW
|
|
982
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
983
|
+
|
|
984
|
+
DROP TRIGGER IF EXISTS trigger_credentials_update_datetime ON credentials;
|
|
985
|
+
CREATE TRIGGER trigger_credentials_update_datetime
|
|
986
|
+
BEFORE UPDATE ON credentials
|
|
987
|
+
FOR EACH ROW
|
|
988
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
989
|
+
|
|
990
|
+
DROP TRIGGER IF EXISTS trigger_agents_update_datetime ON agents;
|
|
991
|
+
CREATE TRIGGER trigger_agents_update_datetime
|
|
992
|
+
BEFORE UPDATE ON agents
|
|
993
|
+
FOR EACH ROW
|
|
994
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
995
|
+
|
|
996
|
+
DROP TRIGGER IF EXISTS trigger_tools_update_datetime ON tools;
|
|
997
|
+
CREATE TRIGGER trigger_tools_update_datetime
|
|
998
|
+
BEFORE UPDATE ON tools
|
|
999
|
+
FOR EACH ROW
|
|
1000
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
1001
|
+
|
|
1002
|
+
DROP TRIGGER IF EXISTS trigger_channels_update_datetime ON channels;
|
|
1003
|
+
CREATE TRIGGER trigger_channels_update_datetime
|
|
1004
|
+
BEFORE UPDATE ON channels
|
|
1005
|
+
FOR EACH ROW
|
|
1006
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
1007
|
+
|
|
1008
|
+
DROP TRIGGER IF EXISTS trigger_leads_update_datetime ON leads;
|
|
1009
|
+
CREATE TRIGGER trigger_leads_update_datetime
|
|
1010
|
+
BEFORE UPDATE ON leads
|
|
1011
|
+
FOR EACH ROW
|
|
1012
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
1013
|
+
|
|
1014
|
+
DROP TRIGGER IF EXISTS trigger_missions_update_datetime ON missions;
|
|
1015
|
+
CREATE TRIGGER trigger_missions_update_datetime
|
|
1016
|
+
BEFORE UPDATE ON missions
|
|
1017
|
+
FOR EACH ROW
|
|
1018
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
1019
|
+
|
|
1020
|
+
DROP TRIGGER IF EXISTS trigger_objectives_update_datetime ON objectives;
|
|
1021
|
+
CREATE TRIGGER trigger_objectives_update_datetime
|
|
1022
|
+
BEFORE UPDATE ON objectives
|
|
1023
|
+
FOR EACH ROW
|
|
1024
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
1025
|
+
|
|
1026
|
+
DROP TRIGGER IF EXISTS trigger_conversations_update_datetime ON conversations;
|
|
1027
|
+
CREATE TRIGGER trigger_conversations_update_datetime
|
|
1028
|
+
BEFORE UPDATE ON conversations
|
|
1029
|
+
FOR EACH ROW
|
|
1030
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
1031
|
+
|
|
1032
|
+
DROP TRIGGER IF EXISTS trigger_objectives_tools_update_datetime ON objectives_tools;
|
|
1033
|
+
CREATE TRIGGER trigger_objectives_tools_update_datetime
|
|
1034
|
+
BEFORE UPDATE ON objectives_tools
|
|
1035
|
+
FOR EACH ROW
|
|
1036
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
1037
|
+
|
|
1038
|
+
DROP TRIGGER IF EXISTS trigger_messages_update_datetime ON messages;
|
|
1039
|
+
CREATE TRIGGER trigger_messages_update_datetime
|
|
1040
|
+
BEFORE UPDATE ON messages
|
|
1041
|
+
FOR EACH ROW
|
|
1042
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
1043
|
+
|
|
1044
|
+
DROP TRIGGER IF EXISTS trigger_companies_update_datetime ON companies;
|
|
1045
|
+
CREATE TRIGGER trigger_companies_update_datetime
|
|
1046
|
+
BEFORE UPDATE ON companies
|
|
1047
|
+
FOR EACH ROW
|
|
1048
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
1049
|
+
|
|
1050
|
+
DROP TRIGGER IF EXISTS trigger_conversations_tags_update_datetime ON conversations_tags;
|
|
1051
|
+
CREATE TRIGGER trigger_conversations_tags_update_datetime
|
|
1052
|
+
BEFORE UPDATE ON conversations_tags
|
|
1053
|
+
FOR EACH ROW
|
|
1054
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
1055
|
+
|
|
1056
|
+
DROP TRIGGER IF EXISTS trigger_leads_tags_update_datetime ON leads_tags;
|
|
1057
|
+
CREATE TRIGGER trigger_leads_tags_update_datetime
|
|
1058
|
+
BEFORE UPDATE ON leads_tags
|
|
1059
|
+
FOR EACH ROW
|
|
1060
|
+
EXECUTE FUNCTION update_datetime_alt();
|
|
1061
|
+
|
|
1062
|
+
-- Triggers de auditoria para tabelas críticas
|
|
1063
|
+
RAISE NOTICE '[%] Criando triggers de auditoria', now();
|
|
1064
|
+
|
|
1065
|
+
DROP TRIGGER IF EXISTS trigger_conversations_audit ON conversations;
|
|
1066
|
+
CREATE TRIGGER trigger_conversations_audit
|
|
1067
|
+
AFTER INSERT OR UPDATE OR DELETE ON conversations
|
|
1068
|
+
FOR EACH ROW
|
|
1069
|
+
EXECUTE FUNCTION audit_changes();
|
|
1070
|
+
|
|
1071
|
+
DROP TRIGGER IF EXISTS trigger_messages_audit ON messages;
|
|
1072
|
+
CREATE TRIGGER trigger_messages_audit
|
|
1073
|
+
AFTER INSERT OR UPDATE OR DELETE ON messages
|
|
1074
|
+
FOR EACH ROW
|
|
1075
|
+
EXECUTE FUNCTION audit_changes();
|
|
1076
|
+
|
|
1077
|
+
DROP TRIGGER IF EXISTS trigger_leads_audit ON leads;
|
|
1078
|
+
CREATE TRIGGER trigger_leads_audit
|
|
1079
|
+
AFTER INSERT OR UPDATE OR DELETE ON leads
|
|
1080
|
+
FOR EACH ROW
|
|
1081
|
+
EXECUTE FUNCTION audit_changes();
|
|
1082
|
+
|
|
1083
|
+
DROP TRIGGER IF EXISTS trigger_accounts_audit ON accounts;
|
|
1084
|
+
CREATE TRIGGER trigger_accounts_audit
|
|
1085
|
+
AFTER INSERT OR UPDATE OR DELETE ON accounts
|
|
1086
|
+
FOR EACH ROW
|
|
1087
|
+
EXECUTE FUNCTION audit_changes();
|
|
1088
|
+
|
|
1089
|
+
-- Trigger para controle de conversas
|
|
1090
|
+
DROP TRIGGER IF EXISTS trigger_messages_update_conversation ON messages;
|
|
1091
|
+
CREATE TRIGGER trigger_messages_update_conversation
|
|
1092
|
+
AFTER INSERT ON messages
|
|
1093
|
+
FOR EACH ROW
|
|
1094
|
+
EXECUTE FUNCTION update_conversation_last_message();
|
|
1095
|
+
|
|
1096
|
+
-- Contar functions e triggers
|
|
1097
|
+
SELECT COUNT(*) INTO total_functions
|
|
1098
|
+
FROM information_schema.routines
|
|
1099
|
+
WHERE routine_schema = 'public'
|
|
1100
|
+
AND routine_name IN ('update_datetime_alt', 'audit_changes', 'update_conversation_last_message');
|
|
1101
|
+
|
|
1102
|
+
SELECT COUNT(*) INTO total_triggers
|
|
1103
|
+
FROM information_schema.triggers
|
|
1104
|
+
WHERE trigger_schema = 'public'
|
|
1105
|
+
AND trigger_name LIKE 'trigger_%';
|
|
1106
|
+
|
|
1107
|
+
block_end_time := CURRENT_TIMESTAMP;
|
|
1108
|
+
block_duration := EXTRACT(EPOCH FROM (block_end_time - block_start_time)) * 1000;
|
|
1109
|
+
|
|
1110
|
+
PERFORM create_migration_checkpoint('triggers_completed', json_build_object('total_functions', total_functions, 'total_triggers', total_triggers, 'duration_ms', block_duration));
|
|
1111
|
+
|
|
1112
|
+
RAISE NOTICE '[%] ✓ BLOCO 4 CONCLUÍDO: % functions + % triggers criados em %ms', now(), total_functions, total_triggers, block_duration;
|
|
1113
|
+
|
|
1114
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1115
|
+
PERFORM rollback_migration('TRIGGERS_FUNCTIONS');
|
|
1116
|
+
RAISE EXCEPTION 'FALHA no BLOCO 4 (Triggers/Functions): %', SQLERRM;
|
|
1117
|
+
END;
|
|
1118
|
+
|
|
1119
|
+
-- ========================================
|
|
1120
|
+
-- VERIFICAÇÃO FINAL E ESTATÍSTICAS
|
|
1121
|
+
-- ========================================
|
|
1122
|
+
migration_end_time := CURRENT_TIMESTAMP;
|
|
1123
|
+
total_duration := EXTRACT(EPOCH FROM (migration_end_time - migration_start_time));
|
|
1124
|
+
|
|
1125
|
+
RAISE NOTICE '========================================';
|
|
1126
|
+
RAISE NOTICE 'MIGRAÇÃO GAGENTS v1.0 CONCLUÍDA COM SUCESSO';
|
|
1127
|
+
RAISE NOTICE 'Tempo total de execução: % segundos', total_duration;
|
|
1128
|
+
RAISE NOTICE 'Finalizada em: %', migration_end_time;
|
|
1129
|
+
RAISE NOTICE '========================================';
|
|
1130
|
+
|
|
1131
|
+
-- Estatísticas finais
|
|
1132
|
+
RAISE NOTICE 'COMPONENTES CRIADOS:';
|
|
1133
|
+
RAISE NOTICE '- Tabelas: % (esperado: 17+)',
|
|
1134
|
+
(SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public');
|
|
1135
|
+
RAISE NOTICE '- Índices: % (esperado: 50+)',
|
|
1136
|
+
(SELECT COUNT(*) FROM pg_indexes WHERE schemaname = 'public');
|
|
1137
|
+
RAISE NOTICE '- Constraints: % (esperado: 30+)',
|
|
1138
|
+
(SELECT COUNT(*) FROM information_schema.table_constraints WHERE table_schema = 'public');
|
|
1139
|
+
RAISE NOTICE '- Functions: % (esperado: 3+)',
|
|
1140
|
+
(SELECT COUNT(*) FROM information_schema.routines WHERE routine_schema = 'public');
|
|
1141
|
+
RAISE NOTICE '- Triggers: % (esperado: 17+)',
|
|
1142
|
+
(SELECT COUNT(*) FROM information_schema.triggers WHERE trigger_schema = 'public');
|
|
1143
|
+
|
|
1144
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1145
|
+
RAISE EXCEPTION 'FALHA CRÍTICA NA MIGRAÇÃO no bloco %: %', current_block, SQLERRM;
|
|
1146
|
+
END $$;
|
|
1147
|
+
|
|
1148
|
+
-- ============================================================================
|
|
1149
|
+
-- SEÇÃO 5: FUNCTIONS UTILITÁRIAS PARA MANUTENÇÃO
|
|
1150
|
+
-- ============================================================================
|
|
1151
|
+
|
|
1152
|
+
-- Function para obter status da migração
|
|
1153
|
+
CREATE OR REPLACE FUNCTION get_migration_status()
|
|
1154
|
+
RETURNS TABLE(
|
|
1155
|
+
checkpoint_name VARCHAR,
|
|
1156
|
+
executed_at TIMESTAMP WITH TIME ZONE,
|
|
1157
|
+
success BOOLEAN,
|
|
1158
|
+
execution_time_ms INTEGER,
|
|
1159
|
+
error_message TEXT
|
|
1160
|
+
) AS $$
|
|
1161
|
+
BEGIN
|
|
1162
|
+
RETURN QUERY
|
|
1163
|
+
SELECT sm.migration_name, sm.executed_at, sm.success, sm.execution_time_ms, sm.error_message
|
|
1164
|
+
FROM schema_migrations sm
|
|
1165
|
+
ORDER BY sm.executed_at DESC;
|
|
1166
|
+
END;
|
|
1167
|
+
$$ LANGUAGE plpgsql;
|
|
1168
|
+
|
|
1169
|
+
-- Function para validar integridade do banco
|
|
1170
|
+
CREATE OR REPLACE FUNCTION validate_database_integrity()
|
|
1171
|
+
RETURNS TABLE(
|
|
1172
|
+
validation_type VARCHAR,
|
|
1173
|
+
expected_count INTEGER,
|
|
1174
|
+
actual_count INTEGER,
|
|
1175
|
+
status VARCHAR
|
|
1176
|
+
) AS $$
|
|
1177
|
+
DECLARE
|
|
1178
|
+
table_count INTEGER;
|
|
1179
|
+
index_count INTEGER;
|
|
1180
|
+
constraint_count INTEGER;
|
|
1181
|
+
trigger_count INTEGER;
|
|
1182
|
+
BEGIN
|
|
1183
|
+
-- Validar tabelas
|
|
1184
|
+
SELECT COUNT(*) INTO table_count FROM information_schema.tables
|
|
1185
|
+
WHERE table_schema = 'public' AND table_type = 'BASE TABLE';
|
|
1186
|
+
|
|
1187
|
+
RETURN QUERY SELECT 'TABLES'::VARCHAR, 17, table_count,
|
|
1188
|
+
CASE WHEN table_count >= 17 THEN 'OK' ELSE 'FAIL' END::VARCHAR;
|
|
1189
|
+
|
|
1190
|
+
-- Validar índices
|
|
1191
|
+
SELECT COUNT(*) INTO index_count FROM pg_indexes WHERE schemaname = 'public';
|
|
1192
|
+
|
|
1193
|
+
RETURN QUERY SELECT 'INDEXES'::VARCHAR, 50, index_count,
|
|
1194
|
+
CASE WHEN index_count >= 50 THEN 'OK' ELSE 'FAIL' END::VARCHAR;
|
|
1195
|
+
|
|
1196
|
+
-- Validar constraints
|
|
1197
|
+
SELECT COUNT(*) INTO constraint_count FROM information_schema.table_constraints
|
|
1198
|
+
WHERE table_schema = 'public';
|
|
1199
|
+
|
|
1200
|
+
RETURN QUERY SELECT 'CONSTRAINTS'::VARCHAR, 30, constraint_count,
|
|
1201
|
+
CASE WHEN constraint_count >= 30 THEN 'OK' ELSE 'FAIL' END::VARCHAR;
|
|
1202
|
+
|
|
1203
|
+
-- Validar triggers
|
|
1204
|
+
SELECT COUNT(*) INTO trigger_count FROM information_schema.triggers
|
|
1205
|
+
WHERE trigger_schema = 'public';
|
|
1206
|
+
|
|
1207
|
+
RETURN QUERY SELECT 'TRIGGERS'::VARCHAR, 20, trigger_count,
|
|
1208
|
+
CASE WHEN trigger_count >= 20 THEN 'OK' ELSE 'FAIL' END::VARCHAR;
|
|
1209
|
+
END;
|
|
1210
|
+
$$ LANGUAGE plpgsql;
|
|
1211
|
+
|
|
1212
|
+
-- Comentários finais
|
|
1213
|
+
COMMENT ON FUNCTION get_migration_status() IS 'Function para obter status detalhado da migração';
|
|
1214
|
+
COMMENT ON FUNCTION validate_database_integrity() IS 'Function para validar integridade completa do banco';
|
|
1215
|
+
|
|
1216
|
+
-- Log final de conclusão
|
|
1217
|
+
DO $$
|
|
1218
|
+
BEGIN
|
|
1219
|
+
RAISE NOTICE '';
|
|
1220
|
+
RAISE NOTICE '============================================================================';
|
|
1221
|
+
RAISE NOTICE 'SCRIPT MIGRATE_V1.SQL CARREGADO E EXECUTADO COM SUCESSO!';
|
|
1222
|
+
RAISE NOTICE '';
|
|
1223
|
+
RAISE NOTICE 'PRÓXIMOS PASSOS:';
|
|
1224
|
+
RAISE NOTICE '1. Execute: SELECT * FROM get_migration_status(); -- Para ver status';
|
|
1225
|
+
RAISE NOTICE '2. Execute: SELECT * FROM validate_database_integrity(); -- Para validar';
|
|
1226
|
+
RAISE NOTICE '3. Execute: ANALYZE; -- Para atualizar estatísticas do PostgreSQL';
|
|
1227
|
+
RAISE NOTICE '';
|
|
1228
|
+
RAISE NOTICE 'SISTEMA GAGENTS v1.0 TOTALMENTE OPERACIONAL!';
|
|
1229
|
+
RAISE NOTICE '============================================================================';
|
|
1230
|
+
END $$;
|