@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.
Files changed (57) hide show
  1. package/MIGRATION_COMPLETE_DOCUMENTATION.md +672 -0
  2. package/package.json +1 -1
  3. package/scripts/README.md +128 -235
  4. package/scripts/constraints.sql +585 -0
  5. package/scripts/indexes.sql +663 -0
  6. package/scripts/migrate_v1.sql +1230 -0
  7. package/scripts/tables/001_accounts.sql +51 -0
  8. package/scripts/tables/002_users.sql +69 -0
  9. package/scripts/tables/003_people.sql +70 -0
  10. package/scripts/tables/004_tags.sql +62 -0
  11. package/scripts/tables/005_credentials.sql +79 -0
  12. package/scripts/tables/006_agents.sql +70 -0
  13. package/scripts/tables/007_tools.sql +68 -0
  14. package/scripts/tables/008_channels.sql +75 -0
  15. package/scripts/tables/009_leads.sql +85 -0
  16. package/scripts/tables/010_missions.sql +62 -0
  17. package/scripts/tables/011_objectives.sql +72 -0
  18. package/scripts/tables/012_conversations.sql +84 -0
  19. package/scripts/tables/013_objectives_tools.sql +66 -0
  20. package/scripts/tables/014_messages.sql +78 -0
  21. package/scripts/tables/015_companies.sql +77 -0
  22. package/scripts/tables/016_conversations_tags.sql +64 -0
  23. package/scripts/tables/017_leads_tags.sql +64 -0
  24. package/scripts/triggers.sql +497 -0
  25. package/src/modules/accounts/index.js +11 -0
  26. package/src/modules/accounts/properties.js +46 -0
  27. package/src/modules/agents/properties.js +1 -30
  28. package/src/modules/channels/properties.js +1 -0
  29. package/src/modules/conversations/index.js +1 -1
  30. package/src/modules/conversations/properties.js +29 -26
  31. package/src/modules/credentials/properties.js +2 -2
  32. package/src/modules/messages/properties.js +40 -2
  33. package/src/modules/missions/properties.js +1 -30
  34. package/src/modules/objectives/properties.js +1 -30
  35. package/src/modules/objectives_tools/index.js +1 -1
  36. package/src/modules/objectives_tools/properties.js +1 -37
  37. package/src/modules/tools/properties.js +1 -0
  38. package/src/modules/users/index.js +11 -0
  39. package/src/modules/users/properties.js +156 -0
  40. package/src/product.js +4 -0
  41. package/scripts/agents/create_table.sql +0 -46
  42. package/scripts/channels/create_table.sql +0 -48
  43. package/scripts/companies/create_table.sql +0 -52
  44. package/scripts/conversations/add_messages_reference.sql +0 -42
  45. package/scripts/conversations/create_table.sql +0 -55
  46. package/scripts/conversations_tags/create_table.sql +0 -39
  47. package/scripts/credentials/create_table.sql +0 -63
  48. package/scripts/leads/create_table.sql +0 -55
  49. package/scripts/leads_tags/create_table.sql +0 -39
  50. package/scripts/messages/create_table.sql +0 -44
  51. package/scripts/missions/create_table.sql +0 -43
  52. package/scripts/objectives/create_table.sql +0 -49
  53. package/scripts/objectives_tools/create_table.sql +0 -52
  54. package/scripts/people/create_table.sql +0 -51
  55. package/scripts/tags/create_table.sql +0 -38
  56. package/scripts/tools/create_table.sql +0 -43
  57. 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 $$;