@greatapps/greatagents 0.1.5 → 0.1.7

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/docs/ERD_GAGents_Database.mmd +333 -0
  2. package/package.json +1 -1
  3. package/scripts/MIGRATION_COMPLETE_DOCUMENTATION.md +672 -0
  4. package/scripts/README.md +128 -235
  5. package/scripts/constraints.sql +585 -0
  6. package/scripts/indexes.sql +662 -0
  7. package/scripts/migrate_v1.sql +1227 -0
  8. package/scripts/tables/001_accounts.sql +51 -0
  9. package/scripts/tables/002_users.sql +69 -0
  10. package/scripts/tables/003_people.sql +70 -0
  11. package/scripts/tables/004_tags.sql +62 -0
  12. package/scripts/tables/005_credentials.sql +79 -0
  13. package/scripts/tables/006_agents.sql +70 -0
  14. package/scripts/tables/007_tools.sql +68 -0
  15. package/scripts/tables/008_channels.sql +75 -0
  16. package/scripts/tables/009_leads.sql +85 -0
  17. package/scripts/tables/010_missions.sql +62 -0
  18. package/scripts/tables/011_objectives.sql +68 -0
  19. package/scripts/tables/012_conversations.sql +84 -0
  20. package/scripts/tables/013_objectives_tools.sql +66 -0
  21. package/scripts/tables/014_messages.sql +78 -0
  22. package/scripts/tables/015_companies.sql +77 -0
  23. package/scripts/tables/016_conversations_tags.sql +64 -0
  24. package/scripts/tables/017_leads_tags.sql +64 -0
  25. package/scripts/triggers.sql +497 -0
  26. package/src/modules/accounts/index.js +11 -0
  27. package/src/modules/accounts/properties.js +46 -0
  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/leads/properties.js +4 -4
  33. package/src/modules/messages/properties.js +40 -2
  34. package/src/modules/missions/properties.js +1 -0
  35. package/src/modules/objectives/properties.js +1 -18
  36. package/src/modules/objectives_tools/index.js +1 -1
  37. package/src/modules/objectives_tools/properties.js +1 -37
  38. package/src/modules/tools/properties.js +1 -0
  39. package/src/modules/users/index.js +11 -0
  40. package/src/modules/users/properties.js +156 -0
  41. package/src/product.js +4 -0
  42. package/scripts/agents/create_table.sql +0 -43
  43. package/scripts/channels/create_table.sql +0 -48
  44. package/scripts/companies/create_table.sql +0 -52
  45. package/scripts/conversations/add_messages_reference.sql +0 -42
  46. package/scripts/conversations/create_table.sql +0 -55
  47. package/scripts/conversations_tags/create_table.sql +0 -39
  48. package/scripts/credentials/create_table.sql +0 -63
  49. package/scripts/leads/create_table.sql +0 -55
  50. package/scripts/leads_tags/create_table.sql +0 -39
  51. package/scripts/messages/create_table.sql +0 -44
  52. package/scripts/missions/create_table.sql +0 -39
  53. package/scripts/objectives/create_table.sql +0 -47
  54. package/scripts/objectives_tools/create_table.sql +0 -52
  55. package/scripts/people/create_table.sql +0 -51
  56. package/scripts/tags/create_table.sql +0 -38
  57. package/scripts/tools/create_table.sql +0 -43
@@ -0,0 +1,1227 @@
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
+ id_user_owner INTEGER,
359
+ id_current_agent INTEGER,
360
+ id_current_mission INTEGER,
361
+ id_current_objective 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 (id_user_owner) REFERENCES users(id) ON DELETE SET NULL,
366
+ FOREIGN KEY (id_current_agent) 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 NOT NULL,
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 CASCADE
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_mission INTEGER NOT NULL,
399
+ order_number INTEGER DEFAULT 0 NOT NULL,
400
+ title VARCHAR(255) NOT NULL,
401
+ prompt TEXT,
402
+ FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
403
+ FOREIGN KEY (id_mission) REFERENCES missions(id) ON DELETE CASCADE,
404
+ CONSTRAINT objectives_order_valid CHECK (order_number >= 0)
405
+ );
406
+ COMMENT ON TABLE objectives IS 'Tabela de objetivos do sistema';
407
+
408
+ -- Atualizar referências em LEADS para MISSIONS e OBJECTIVES
409
+ ALTER TABLE leads
410
+ ADD CONSTRAINT fk_leads_current_mission
411
+ FOREIGN KEY (id_current_mission) REFERENCES missions(id) ON DELETE SET NULL;
412
+
413
+ ALTER TABLE leads
414
+ ADD CONSTRAINT fk_leads_current_objective
415
+ FOREIGN KEY (id_current_objective) REFERENCES objectives(id) ON DELETE SET NULL;
416
+
417
+ -- TABELA 012: CONVERSATIONS (sem FK para messages - referência cíclica)
418
+ RAISE NOTICE '[%] Criando tabela: conversations', now();
419
+ CREATE TABLE IF NOT EXISTS conversations (
420
+ id SERIAL PRIMARY KEY,
421
+ deleted BOOLEAN DEFAULT FALSE,
422
+ datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
423
+ datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
424
+ datetime_del TIMESTAMP WITH TIME ZONE NULL,
425
+ id_account INTEGER NOT NULL,
426
+ id_lead INTEGER,
427
+ id_channel INTEGER,
428
+ id_assignee INTEGER,
429
+ id_last_agent INTEGER,
430
+ last_message_at TIMESTAMP WITH TIME ZONE,
431
+ id_last_message INTEGER,
432
+ stop_ai BOOLEAN DEFAULT FALSE,
433
+ status VARCHAR(20) DEFAULT 'open',
434
+ FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
435
+ FOREIGN KEY (id_lead) REFERENCES leads(id) ON DELETE SET NULL,
436
+ FOREIGN KEY (id_channel) REFERENCES channels(id) ON DELETE SET NULL,
437
+ FOREIGN KEY (id_assignee) REFERENCES users(id) ON DELETE SET NULL,
438
+ FOREIGN KEY (id_last_agent) REFERENCES agents(id) ON DELETE SET NULL,
439
+ CONSTRAINT conversations_status_valid CHECK (status IN ('open', 'pending', 'closed'))
440
+ );
441
+ COMMENT ON TABLE conversations IS 'Tabela de conversas do sistema';
442
+
443
+ -- TABELA 013: OBJECTIVES_TOOLS
444
+ RAISE NOTICE '[%] Criando tabela: objectives_tools', now();
445
+ CREATE TABLE IF NOT EXISTS objectives_tools (
446
+ id SERIAL PRIMARY KEY,
447
+ deleted BOOLEAN DEFAULT FALSE,
448
+ datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
449
+ datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
450
+ datetime_del TIMESTAMP WITH TIME ZONE NULL,
451
+ id_account INTEGER NOT NULL,
452
+ id_objective INTEGER,
453
+ id_tool INTEGER,
454
+ instructions TEXT,
455
+ wait BOOLEAN DEFAULT FALSE,
456
+ FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
457
+ FOREIGN KEY (id_objective) REFERENCES objectives(id) ON DELETE CASCADE,
458
+ FOREIGN KEY (id_tool) REFERENCES tools(id) ON DELETE CASCADE
459
+ );
460
+ COMMENT ON TABLE objectives_tools IS 'Tabela de relacionamento entre objetivos e ferramentas';
461
+
462
+ -- TABELA 014: MESSAGES (resolve referência cíclica)
463
+ RAISE NOTICE '[%] Criando tabela: messages', now();
464
+ CREATE TABLE IF NOT EXISTS messages (
465
+ id SERIAL PRIMARY KEY,
466
+ deleted BOOLEAN DEFAULT FALSE,
467
+ datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
468
+ datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
469
+ datetime_del TIMESTAMP WITH TIME ZONE NULL,
470
+ id_account INTEGER NOT NULL,
471
+ id_conversation INTEGER,
472
+ id_agent INTEGER,
473
+ id_user INTEGER,
474
+ direction VARCHAR(10) DEFAULT 'in',
475
+ role VARCHAR(20) DEFAULT 'user',
476
+ content TEXT NOT NULL,
477
+ FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
478
+ FOREIGN KEY (id_conversation) REFERENCES conversations(id) ON DELETE CASCADE,
479
+ FOREIGN KEY (id_agent) REFERENCES agents(id) ON DELETE SET NULL,
480
+ FOREIGN KEY (id_user) REFERENCES users(id) ON DELETE SET NULL,
481
+ CONSTRAINT messages_direction_valid CHECK (direction IN ('in', 'out')),
482
+ CONSTRAINT messages_role_valid CHECK (role IN ('user', 'agent', 'system', 'assistant', 'note'))
483
+ );
484
+ COMMENT ON TABLE messages IS 'Tabela de mensagens do sistema';
485
+
486
+ -- TABELA 015: COMPANIES
487
+ RAISE NOTICE '[%] Criando tabela: companies', now();
488
+ CREATE TABLE IF NOT EXISTS companies (
489
+ id SERIAL PRIMARY KEY,
490
+ deleted BOOLEAN DEFAULT FALSE,
491
+ datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
492
+ datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
493
+ datetime_del TIMESTAMP WITH TIME ZONE NULL,
494
+ id_account INTEGER NOT NULL,
495
+ id_lead INTEGER NOT NULL,
496
+ name VARCHAR(255),
497
+ website VARCHAR(255),
498
+ document VARCHAR(50),
499
+ notes TEXT,
500
+ industry VARCHAR(255),
501
+ size VARCHAR(255),
502
+ FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
503
+ FOREIGN KEY (id_lead) REFERENCES leads(id) ON DELETE CASCADE,
504
+ CONSTRAINT companies_website_valid CHECK (website IS NULL OR website ~* '^https?://.*')
505
+ );
506
+ COMMENT ON TABLE companies IS 'Tabela de empresas do sistema';
507
+
508
+ -- TABELA 016: CONVERSATIONS_TAGS
509
+ RAISE NOTICE '[%] Criando tabela: conversations_tags', now();
510
+ CREATE TABLE IF NOT EXISTS conversations_tags (
511
+ id SERIAL PRIMARY KEY,
512
+ deleted BOOLEAN DEFAULT FALSE,
513
+ datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
514
+ datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
515
+ datetime_del TIMESTAMP WITH TIME ZONE NULL,
516
+ id_account INTEGER NOT NULL,
517
+ id_conversation INTEGER,
518
+ id_tag INTEGER,
519
+ FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
520
+ FOREIGN KEY (id_conversation) REFERENCES conversations(id) ON DELETE CASCADE,
521
+ FOREIGN KEY (id_tag) REFERENCES tags(id) ON DELETE CASCADE
522
+ );
523
+ COMMENT ON TABLE conversations_tags IS 'Tabela de relacionamento entre conversas e tags';
524
+
525
+ -- TABELA 017: LEADS_TAGS
526
+ RAISE NOTICE '[%] Criando tabela: leads_tags', now();
527
+ CREATE TABLE IF NOT EXISTS leads_tags (
528
+ id SERIAL PRIMARY KEY,
529
+ deleted BOOLEAN DEFAULT FALSE,
530
+ datetime_add TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
531
+ datetime_alt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
532
+ datetime_del TIMESTAMP WITH TIME ZONE NULL,
533
+ id_account INTEGER NOT NULL,
534
+ id_lead INTEGER,
535
+ id_tag INTEGER,
536
+ FOREIGN KEY (id_account) REFERENCES accounts(id) ON DELETE CASCADE,
537
+ FOREIGN KEY (id_lead) REFERENCES leads(id) ON DELETE CASCADE,
538
+ FOREIGN KEY (id_tag) REFERENCES tags(id) ON DELETE CASCADE
539
+ );
540
+ COMMENT ON TABLE leads_tags IS 'Tabela de relacionamento entre leads e tags';
541
+
542
+ -- Contar tabelas criadas
543
+ SELECT COUNT(*) INTO total_tables
544
+ FROM information_schema.tables
545
+ WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
546
+ AND table_name IN ('accounts', 'users', 'people', 'tags', 'credentials', 'agents', 'tools', 'channels', 'leads', 'missions', 'objectives', 'conversations', 'objectives_tools', 'messages', 'companies', 'conversations_tags', 'leads_tags');
547
+
548
+ block_end_time := CURRENT_TIMESTAMP;
549
+ block_duration := EXTRACT(EPOCH FROM (block_end_time - block_start_time)) * 1000;
550
+
551
+ PERFORM create_migration_checkpoint('tables_completed', json_build_object('total_tables', total_tables, 'duration_ms', block_duration));
552
+
553
+ RAISE NOTICE '[%] ✓ BLOCO 1 CONCLUÍDO: % tabelas criadas em %ms', now(), total_tables, block_duration;
554
+
555
+ EXCEPTION WHEN OTHERS THEN
556
+ PERFORM rollback_migration('TABLES_CREATION');
557
+ RAISE EXCEPTION 'FALHA no BLOCO 1 (Criação de tabelas): %', SQLERRM;
558
+ END;
559
+
560
+ -- ========================================
561
+ -- BLOCO 2: CRIAÇÃO DE ÍNDICES
562
+ -- ========================================
563
+ current_block := 'INDEXES_CREATION';
564
+ block_start_time := CURRENT_TIMESTAMP;
565
+
566
+ BEGIN
567
+ RAISE NOTICE '[%] ==========================================', now();
568
+ RAISE NOTICE '[%] BLOCO 2: CRIAÇÃO DE ÍNDICES', now();
569
+ RAISE NOTICE '[%] ==========================================', now();
570
+
571
+ PERFORM create_migration_checkpoint('start_indexes');
572
+
573
+ -- ÍNDICES ACCOUNTS
574
+ RAISE NOTICE '[%] Criando índices para: accounts', now();
575
+ CREATE INDEX IF NOT EXISTS idx_accounts_deleted ON accounts(deleted);
576
+ CREATE INDEX IF NOT EXISTS idx_accounts_datetime_add ON accounts(datetime_add);
577
+ CREATE INDEX IF NOT EXISTS idx_accounts_name_search ON accounts USING gin(to_tsvector('portuguese', name)) WHERE name IS NOT NULL;
578
+ CREATE INDEX IF NOT EXISTS idx_accounts_not_deleted ON accounts(id, deleted) WHERE deleted = FALSE;
579
+
580
+ -- ÍNDICES USERS
581
+ RAISE NOTICE '[%] Criando índices para: users', now();
582
+ CREATE INDEX IF NOT EXISTS idx_users_id_account ON users(id_account);
583
+ CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
584
+ CREATE INDEX IF NOT EXISTS idx_users_profile ON users(profile);
585
+ CREATE INDEX IF NOT EXISTS idx_users_deleted ON users(deleted);
586
+ CREATE INDEX IF NOT EXISTS idx_users_name_search ON users USING gin(to_tsvector('portuguese', name)) WHERE name IS NOT NULL;
587
+ CREATE INDEX IF NOT EXISTS idx_users_account_not_deleted ON users(id_account, deleted) WHERE deleted = FALSE;
588
+
589
+ -- ÍNDICES PEOPLE
590
+ RAISE NOTICE '[%] Criando índices para: people', now();
591
+ CREATE INDEX IF NOT EXISTS idx_people_id_account ON people(id_account);
592
+ CREATE INDEX IF NOT EXISTS idx_people_full_name ON people(full_name);
593
+ CREATE INDEX IF NOT EXISTS idx_people_document ON people(document);
594
+ CREATE INDEX IF NOT EXISTS idx_people_birth_date ON people(birth_date);
595
+ CREATE INDEX IF NOT EXISTS idx_people_deleted ON people(deleted);
596
+ 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;
597
+
598
+ -- ÍNDICES TAGS
599
+ RAISE NOTICE '[%] Criando índices para: tags', now();
600
+ CREATE INDEX IF NOT EXISTS idx_tags_id_account ON tags(id_account);
601
+ CREATE INDEX IF NOT EXISTS idx_tags_name ON tags(name);
602
+ CREATE INDEX IF NOT EXISTS idx_tags_color ON tags(color);
603
+ CREATE INDEX IF NOT EXISTS idx_tags_deleted ON tags(deleted);
604
+ CREATE INDEX IF NOT EXISTS idx_tags_name_search ON tags USING gin(to_tsvector('portuguese', name)) WHERE name IS NOT NULL;
605
+
606
+ -- ÍNDICES CREDENTIALS
607
+ RAISE NOTICE '[%] Criando índices para: credentials', now();
608
+ CREATE INDEX IF NOT EXISTS idx_credentials_id_account ON credentials(id_account);
609
+ CREATE INDEX IF NOT EXISTS idx_credentials_provider ON credentials(provider);
610
+ CREATE INDEX IF NOT EXISTS idx_credentials_authtype ON credentials(authType);
611
+ CREATE INDEX IF NOT EXISTS idx_credentials_deleted ON credentials(deleted);
612
+ CREATE INDEX IF NOT EXISTS idx_credentials_title_search ON credentials USING gin(to_tsvector('portuguese', title)) WHERE title IS NOT NULL;
613
+
614
+ -- ÍNDICES AGENTS
615
+ RAISE NOTICE '[%] Criando índices para: agents', now();
616
+ CREATE INDEX IF NOT EXISTS idx_agents_id_account ON agents(id_account);
617
+ CREATE INDEX IF NOT EXISTS idx_agents_title ON agents(title);
618
+ CREATE INDEX IF NOT EXISTS idx_agents_deleted ON agents(deleted);
619
+ CREATE INDEX IF NOT EXISTS idx_agents_title_search ON agents USING gin(to_tsvector('portuguese', title)) WHERE title IS NOT NULL;
620
+
621
+ -- ÍNDICES TOOLS
622
+ RAISE NOTICE '[%] Criando índices para: tools', now();
623
+ CREATE INDEX IF NOT EXISTS idx_tools_id_account ON tools(id_account);
624
+ CREATE INDEX IF NOT EXISTS idx_tools_type ON tools(type);
625
+ CREATE INDEX IF NOT EXISTS idx_tools_id_credential ON tools(id_credential);
626
+ CREATE INDEX IF NOT EXISTS idx_tools_deleted ON tools(deleted);
627
+ CREATE INDEX IF NOT EXISTS idx_tools_title_search ON tools USING gin(to_tsvector('portuguese', title)) WHERE title IS NOT NULL;
628
+
629
+ -- ÍNDICES CHANNELS
630
+ RAISE NOTICE '[%] Criando índices para: channels', now();
631
+ CREATE INDEX IF NOT EXISTS idx_channels_id_account ON channels(id_account);
632
+ CREATE INDEX IF NOT EXISTS idx_channels_provider ON channels(provider);
633
+ CREATE INDEX IF NOT EXISTS idx_channels_active ON channels(active);
634
+ CREATE INDEX IF NOT EXISTS idx_channels_handle ON channels(handle);
635
+ CREATE INDEX IF NOT EXISTS idx_channels_id_credential ON channels(id_credential);
636
+ CREATE INDEX IF NOT EXISTS idx_channels_deleted ON channels(deleted);
637
+
638
+ -- ÍNDICES LEADS
639
+ RAISE NOTICE '[%] Criando índices para: leads', now();
640
+ CREATE INDEX IF NOT EXISTS idx_leads_id_account ON leads(id_account);
641
+ CREATE INDEX IF NOT EXISTS idx_leads_id_person ON leads(id_person);
642
+ CREATE INDEX IF NOT EXISTS idx_leads_email ON leads(email);
643
+ CREATE INDEX IF NOT EXISTS idx_leads_phone ON leads(phone);
644
+ CREATE INDEX IF NOT EXISTS idx_leads_id_current_agent ON leads(id_current_agent);
645
+ CREATE INDEX IF NOT EXISTS idx_leads_deleted ON leads(deleted);
646
+
647
+ -- ÍNDICES MISSIONS
648
+ RAISE NOTICE '[%] Criando índices para: missions', now();
649
+ CREATE INDEX IF NOT EXISTS idx_missions_id_account ON missions(id_account);
650
+ CREATE INDEX IF NOT EXISTS idx_missions_id_agent ON missions(id_agent);
651
+ CREATE INDEX IF NOT EXISTS idx_missions_title ON missions(title);
652
+ CREATE INDEX IF NOT EXISTS idx_missions_deleted ON missions(deleted);
653
+
654
+ -- ÍNDICES OBJECTIVES
655
+ RAISE NOTICE '[%] Criando índices para: objectives', now();
656
+ CREATE INDEX IF NOT EXISTS idx_objectives_id_account ON objectives(id_account);
657
+ CREATE INDEX IF NOT EXISTS idx_objectives_id_mission ON objectives(id_mission);
658
+ CREATE INDEX IF NOT EXISTS idx_objectives_order ON objectives(order_number);
659
+ CREATE INDEX IF NOT EXISTS idx_objectives_deleted ON objectives(deleted);
660
+
661
+ -- ÍNDICES CONVERSATIONS
662
+ RAISE NOTICE '[%] Criando índices para: conversations', now();
663
+ CREATE INDEX IF NOT EXISTS idx_conversations_id_account ON conversations(id_account);
664
+ CREATE INDEX IF NOT EXISTS idx_conversations_id_lead ON conversations(id_lead);
665
+ CREATE INDEX IF NOT EXISTS idx_conversations_id_channel ON conversations(id_channel);
666
+ CREATE INDEX IF NOT EXISTS idx_conversations_status ON conversations(status);
667
+ CREATE INDEX IF NOT EXISTS idx_conversations_last_message_at ON conversations(last_message_at);
668
+ CREATE INDEX IF NOT EXISTS idx_conversations_deleted ON conversations(deleted);
669
+
670
+ -- ÍNDICES OBJECTIVES_TOOLS
671
+ RAISE NOTICE '[%] Criando índices para: objectives_tools', now();
672
+ CREATE INDEX IF NOT EXISTS idx_objectives_tools_id_account ON objectives_tools(id_account);
673
+ CREATE INDEX IF NOT EXISTS idx_objectives_tools_id_objective ON objectives_tools(id_objective);
674
+ CREATE INDEX IF NOT EXISTS idx_objectives_tools_id_tool ON objectives_tools(id_tool);
675
+ CREATE INDEX IF NOT EXISTS idx_objectives_tools_wait ON objectives_tools(wait);
676
+ CREATE INDEX IF NOT EXISTS idx_objectives_tools_deleted ON objectives_tools(deleted);
677
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_objectives_tools_unique ON objectives_tools(id_objective, id_tool) WHERE deleted = FALSE;
678
+
679
+ -- ÍNDICES MESSAGES
680
+ RAISE NOTICE '[%] Criando índices para: messages', now();
681
+ CREATE INDEX IF NOT EXISTS idx_messages_id_account ON messages(id_account);
682
+ CREATE INDEX IF NOT EXISTS idx_messages_id_conversation ON messages(id_conversation);
683
+ CREATE INDEX IF NOT EXISTS idx_messages_direction ON messages(direction);
684
+ CREATE INDEX IF NOT EXISTS idx_messages_role ON messages(role);
685
+ CREATE INDEX IF NOT EXISTS idx_messages_datetime_add ON messages(datetime_add);
686
+ CREATE INDEX IF NOT EXISTS idx_messages_deleted ON messages(deleted);
687
+ CREATE INDEX IF NOT EXISTS idx_messages_content_search ON messages USING gin(to_tsvector('portuguese', content)) WHERE content IS NOT NULL;
688
+
689
+ -- ÍNDICES COMPANIES
690
+ RAISE NOTICE '[%] Criando índices para: companies', now();
691
+ CREATE INDEX IF NOT EXISTS idx_companies_id_account ON companies(id_account);
692
+ CREATE INDEX IF NOT EXISTS idx_companies_id_lead ON companies(id_lead);
693
+ CREATE INDEX IF NOT EXISTS idx_companies_name ON companies(name);
694
+ CREATE INDEX IF NOT EXISTS idx_companies_document ON companies(document);
695
+ CREATE INDEX IF NOT EXISTS idx_companies_deleted ON companies(deleted);
696
+
697
+ -- ÍNDICES CONVERSATIONS_TAGS
698
+ RAISE NOTICE '[%] Criando índices para: conversations_tags', now();
699
+ CREATE INDEX IF NOT EXISTS idx_conversations_tags_id_account ON conversations_tags(id_account);
700
+ CREATE INDEX IF NOT EXISTS idx_conversations_tags_id_conversation ON conversations_tags(id_conversation);
701
+ CREATE INDEX IF NOT EXISTS idx_conversations_tags_id_tag ON conversations_tags(id_tag);
702
+ CREATE INDEX IF NOT EXISTS idx_conversations_tags_deleted ON conversations_tags(deleted);
703
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_conversations_tags_unique ON conversations_tags(id_conversation, id_tag) WHERE deleted = FALSE;
704
+
705
+ -- ÍNDICES LEADS_TAGS
706
+ RAISE NOTICE '[%] Criando índices para: leads_tags', now();
707
+ CREATE INDEX IF NOT EXISTS idx_leads_tags_id_account ON leads_tags(id_account);
708
+ CREATE INDEX IF NOT EXISTS idx_leads_tags_id_lead ON leads_tags(id_lead);
709
+ CREATE INDEX IF NOT EXISTS idx_leads_tags_id_tag ON leads_tags(id_tag);
710
+ CREATE INDEX IF NOT EXISTS idx_leads_tags_deleted ON leads_tags(deleted);
711
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_leads_tags_unique ON leads_tags(id_lead, id_tag) WHERE deleted = FALSE;
712
+
713
+ -- Contar índices criados
714
+ SELECT COUNT(*) INTO total_indexes
715
+ FROM pg_indexes
716
+ WHERE schemaname = 'public' AND indexname LIKE 'idx_%';
717
+
718
+ block_end_time := CURRENT_TIMESTAMP;
719
+ block_duration := EXTRACT(EPOCH FROM (block_end_time - block_start_time)) * 1000;
720
+
721
+ PERFORM create_migration_checkpoint('indexes_completed', json_build_object('total_indexes', total_indexes, 'duration_ms', block_duration));
722
+
723
+ RAISE NOTICE '[%] ✓ BLOCO 2 CONCLUÍDO: % índices criados em %ms', now(), total_indexes, block_duration;
724
+
725
+ EXCEPTION WHEN OTHERS THEN
726
+ PERFORM rollback_migration('INDEXES_CREATION');
727
+ RAISE EXCEPTION 'FALHA no BLOCO 2 (Criação de índices): %', SQLERRM;
728
+ END;
729
+
730
+ -- ========================================
731
+ -- BLOCO 3: CONSTRAINTS ADICIONAIS
732
+ -- ========================================
733
+ current_block := 'CONSTRAINTS_APPLICATION';
734
+ block_start_time := CURRENT_TIMESTAMP;
735
+
736
+ BEGIN
737
+ RAISE NOTICE '[%] ==========================================', now();
738
+ RAISE NOTICE '[%] BLOCO 3: CONSTRAINTS ADICIONAIS', now();
739
+ RAISE NOTICE '[%] ==========================================', now();
740
+
741
+ PERFORM create_migration_checkpoint('start_constraints');
742
+
743
+ -- REFERÊNCIA CÍCLICA CRÍTICA: conversations → messages
744
+ RAISE NOTICE '[%] Resolvendo referência cíclica conversations ↔ messages', now();
745
+ IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
746
+ WHERE constraint_name = 'fk_conversations_last_message'
747
+ AND table_name = 'conversations') THEN
748
+ ALTER TABLE conversations
749
+ ADD CONSTRAINT fk_conversations_last_message
750
+ FOREIGN KEY (id_last_message) REFERENCES messages(id)
751
+ ON DELETE SET NULL;
752
+ RAISE NOTICE '[%] ✓ Referência cíclica conversations → messages resolvida', now();
753
+ END IF;
754
+
755
+ -- EXPANDIR CHECK CONSTRAINTS
756
+ RAISE NOTICE '[%] Expandindo constraints de validação', now();
757
+
758
+ -- Users profile expandido
759
+ IF EXISTS (SELECT 1 FROM information_schema.table_constraints
760
+ WHERE constraint_name = 'users_profile_valid'
761
+ AND table_name = 'users') THEN
762
+ ALTER TABLE users DROP CONSTRAINT users_profile_valid;
763
+ END IF;
764
+ ALTER TABLE users
765
+ ADD CONSTRAINT chk_users_profile_expanded
766
+ CHECK (profile IN ('viewer', 'collaborator', 'admin', 'owner', 'attendant', 'user', 'agent'));
767
+
768
+ -- Channels provider expandido
769
+ IF EXISTS (SELECT 1 FROM information_schema.table_constraints
770
+ WHERE constraint_name = 'channels_provider_valid'
771
+ AND table_name = 'channels') THEN
772
+ ALTER TABLE channels DROP CONSTRAINT channels_provider_valid;
773
+ END IF;
774
+ ALTER TABLE channels
775
+ ADD CONSTRAINT chk_channels_provider_expanded
776
+ CHECK (provider IN ('web-chat', 'whatsap-oficial', 'z-api', 'evolution-api', 'telegram', 'facebook', 'instagram', 'whatsapp', 'discord', 'slack', 'email'));
777
+
778
+ -- Messages direction e role expandidos
779
+ IF EXISTS (SELECT 1 FROM information_schema.table_constraints
780
+ WHERE constraint_name = 'messages_direction_valid'
781
+ AND table_name = 'messages') THEN
782
+ ALTER TABLE messages DROP CONSTRAINT messages_direction_valid;
783
+ END IF;
784
+ ALTER TABLE messages
785
+ ADD CONSTRAINT chk_messages_direction_expanded
786
+ CHECK (direction IN ('in', 'out', 'incoming', 'outgoing'));
787
+
788
+ IF EXISTS (SELECT 1 FROM information_schema.table_constraints
789
+ WHERE constraint_name = 'messages_role_valid'
790
+ AND table_name = 'messages') THEN
791
+ ALTER TABLE messages DROP CONSTRAINT messages_role_valid;
792
+ END IF;
793
+ ALTER TABLE messages
794
+ ADD CONSTRAINT chk_messages_role_expanded
795
+ CHECK (role IN ('user', 'agent', 'system', 'assistant', 'note', 'function'));
796
+
797
+ -- CONSTRAINTS DE INTEGRIDADE TEMPORAL
798
+ RAISE NOTICE '[%] Adicionando constraints de integridade temporal', now();
799
+
800
+ -- Conversations datetime consistency
801
+ IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
802
+ WHERE constraint_name = 'chk_conversations_datetime_consistency'
803
+ AND table_name = 'conversations') THEN
804
+ ALTER TABLE conversations
805
+ ADD CONSTRAINT chk_conversations_datetime_consistency
806
+ CHECK (datetime_alt >= datetime_add);
807
+ END IF;
808
+
809
+ -- Messages datetime consistency
810
+ IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
811
+ WHERE constraint_name = 'chk_messages_datetime_consistency'
812
+ AND table_name = 'messages') THEN
813
+ ALTER TABLE messages
814
+ ADD CONSTRAINT chk_messages_datetime_consistency
815
+ CHECK (datetime_alt >= datetime_add);
816
+ END IF;
817
+
818
+ -- CONSTRAINTS DE SEGURANÇA
819
+ RAISE NOTICE '[%] Adicionando constraints de segurança', now();
820
+
821
+ -- Accounts name obrigatório
822
+ IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
823
+ WHERE constraint_name = 'chk_accounts_name_required'
824
+ AND table_name = 'accounts') THEN
825
+ ALTER TABLE accounts
826
+ ADD CONSTRAINT chk_accounts_name_required
827
+ CHECK (name IS NOT NULL AND trim(name) != '');
828
+ END IF;
829
+
830
+ -- Users email obrigatório
831
+ IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
832
+ WHERE constraint_name = 'chk_users_email_required'
833
+ AND table_name = 'users') THEN
834
+ ALTER TABLE users
835
+ ADD CONSTRAINT chk_users_email_required
836
+ CHECK (email IS NOT NULL AND trim(email) != '');
837
+ END IF;
838
+
839
+ -- Messages content obrigatório
840
+ IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints
841
+ WHERE constraint_name = 'chk_messages_content_required'
842
+ AND table_name = 'messages') THEN
843
+ ALTER TABLE messages
844
+ ADD CONSTRAINT chk_messages_content_required
845
+ CHECK (content IS NOT NULL AND trim(content) != '');
846
+ END IF;
847
+
848
+ -- Contar constraints
849
+ SELECT COUNT(*) INTO total_constraints
850
+ FROM information_schema.table_constraints
851
+ WHERE table_schema = current_schema();
852
+
853
+ block_end_time := CURRENT_TIMESTAMP;
854
+ block_duration := EXTRACT(EPOCH FROM (block_end_time - block_start_time)) * 1000;
855
+
856
+ PERFORM create_migration_checkpoint('constraints_completed', json_build_object('total_constraints', total_constraints, 'duration_ms', block_duration));
857
+
858
+ RAISE NOTICE '[%] ✓ BLOCO 3 CONCLUÍDO: % constraints criadas/atualizadas em %ms', now(), total_constraints, block_duration;
859
+
860
+ EXCEPTION WHEN OTHERS THEN
861
+ PERFORM rollback_migration('CONSTRAINTS_APPLICATION');
862
+ RAISE EXCEPTION 'FALHA no BLOCO 3 (Constraints): %', SQLERRM;
863
+ END;
864
+
865
+ -- ========================================
866
+ -- BLOCO 4: TRIGGERS E FUNCTIONS
867
+ -- ========================================
868
+ current_block := 'TRIGGERS_FUNCTIONS';
869
+ block_start_time := CURRENT_TIMESTAMP;
870
+
871
+ BEGIN
872
+ RAISE NOTICE '[%] ==========================================', now();
873
+ RAISE NOTICE '[%] BLOCO 4: TRIGGERS E FUNCTIONS', now();
874
+ RAISE NOTICE '[%] ==========================================', now();
875
+
876
+ PERFORM create_migration_checkpoint('start_triggers');
877
+
878
+ -- TABELA DE AUDITORIA
879
+ RAISE NOTICE '[%] Criando tabela de auditoria', now();
880
+ CREATE TABLE IF NOT EXISTS audit_log (
881
+ id SERIAL PRIMARY KEY,
882
+ table_name VARCHAR(100) NOT NULL,
883
+ operation_type VARCHAR(10) NOT NULL,
884
+ record_id INTEGER NOT NULL,
885
+ old_values JSONB,
886
+ new_values JSONB,
887
+ changed_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
888
+ changed_by VARCHAR(100) DEFAULT current_user,
889
+ CONSTRAINT audit_log_operation_valid CHECK (operation_type IN ('INSERT', 'UPDATE', 'DELETE'))
890
+ );
891
+
892
+ CREATE INDEX IF NOT EXISTS idx_audit_log_table_name ON audit_log(table_name);
893
+ CREATE INDEX IF NOT EXISTS idx_audit_log_operation_type ON audit_log(operation_type);
894
+ CREATE INDEX IF NOT EXISTS idx_audit_log_changed_at ON audit_log(changed_at);
895
+
896
+ -- FUNCTIONS UNIVERSAIS
897
+ RAISE NOTICE '[%] Criando functions universais', now();
898
+
899
+ -- Function para atualização automática de datetime_alt
900
+ CREATE OR REPLACE FUNCTION update_datetime_alt()
901
+ RETURNS TRIGGER AS $$
902
+ BEGIN
903
+ NEW.datetime_alt = CURRENT_TIMESTAMP;
904
+ RETURN NEW;
905
+ END;
906
+ $$ LANGUAGE plpgsql;
907
+
908
+ -- Function para auditoria
909
+ CREATE OR REPLACE FUNCTION audit_changes()
910
+ RETURNS TRIGGER AS $$
911
+ DECLARE
912
+ operation_type VARCHAR(10);
913
+ table_name VARCHAR(100);
914
+ BEGIN
915
+ operation_type := TG_OP;
916
+ table_name := TG_TABLE_NAME;
917
+
918
+ INSERT INTO audit_log (
919
+ table_name,
920
+ operation_type,
921
+ record_id,
922
+ old_values,
923
+ new_values,
924
+ changed_at,
925
+ changed_by
926
+ ) VALUES (
927
+ table_name,
928
+ operation_type,
929
+ COALESCE(NEW.id, OLD.id),
930
+ CASE WHEN operation_type != 'INSERT' THEN row_to_json(OLD) END,
931
+ CASE WHEN operation_type != 'DELETE' THEN row_to_json(NEW) END,
932
+ CURRENT_TIMESTAMP,
933
+ current_user
934
+ );
935
+
936
+ RETURN COALESCE(NEW, OLD);
937
+ END;
938
+ $$ LANGUAGE plpgsql;
939
+
940
+ -- Function para atualizar última mensagem da conversa
941
+ CREATE OR REPLACE FUNCTION update_conversation_last_message()
942
+ RETURNS TRIGGER AS $$
943
+ BEGIN
944
+ UPDATE conversations
945
+ SET id_last_message = NEW.id,
946
+ last_message_at = NEW.datetime_add
947
+ WHERE id = NEW.id_conversation;
948
+
949
+ RETURN NEW;
950
+ END;
951
+ $$ LANGUAGE plpgsql;
952
+
953
+ -- CRIAÇÃO DE TRIGGERS
954
+ RAISE NOTICE '[%] Criando triggers para todas as tabelas', now();
955
+
956
+ -- Triggers para atualização automática de datetime_alt
957
+ DROP TRIGGER IF EXISTS trigger_accounts_update_datetime ON accounts;
958
+ CREATE TRIGGER trigger_accounts_update_datetime
959
+ BEFORE UPDATE ON accounts
960
+ FOR EACH ROW
961
+ EXECUTE FUNCTION update_datetime_alt();
962
+
963
+ DROP TRIGGER IF EXISTS trigger_users_update_datetime ON users;
964
+ CREATE TRIGGER trigger_users_update_datetime
965
+ BEFORE UPDATE ON users
966
+ FOR EACH ROW
967
+ EXECUTE FUNCTION update_datetime_alt();
968
+
969
+ DROP TRIGGER IF EXISTS trigger_people_update_datetime ON people;
970
+ CREATE TRIGGER trigger_people_update_datetime
971
+ BEFORE UPDATE ON people
972
+ FOR EACH ROW
973
+ EXECUTE FUNCTION update_datetime_alt();
974
+
975
+ DROP TRIGGER IF EXISTS trigger_tags_update_datetime ON tags;
976
+ CREATE TRIGGER trigger_tags_update_datetime
977
+ BEFORE UPDATE ON tags
978
+ FOR EACH ROW
979
+ EXECUTE FUNCTION update_datetime_alt();
980
+
981
+ DROP TRIGGER IF EXISTS trigger_credentials_update_datetime ON credentials;
982
+ CREATE TRIGGER trigger_credentials_update_datetime
983
+ BEFORE UPDATE ON credentials
984
+ FOR EACH ROW
985
+ EXECUTE FUNCTION update_datetime_alt();
986
+
987
+ DROP TRIGGER IF EXISTS trigger_agents_update_datetime ON agents;
988
+ CREATE TRIGGER trigger_agents_update_datetime
989
+ BEFORE UPDATE ON agents
990
+ FOR EACH ROW
991
+ EXECUTE FUNCTION update_datetime_alt();
992
+
993
+ DROP TRIGGER IF EXISTS trigger_tools_update_datetime ON tools;
994
+ CREATE TRIGGER trigger_tools_update_datetime
995
+ BEFORE UPDATE ON tools
996
+ FOR EACH ROW
997
+ EXECUTE FUNCTION update_datetime_alt();
998
+
999
+ DROP TRIGGER IF EXISTS trigger_channels_update_datetime ON channels;
1000
+ CREATE TRIGGER trigger_channels_update_datetime
1001
+ BEFORE UPDATE ON channels
1002
+ FOR EACH ROW
1003
+ EXECUTE FUNCTION update_datetime_alt();
1004
+
1005
+ DROP TRIGGER IF EXISTS trigger_leads_update_datetime ON leads;
1006
+ CREATE TRIGGER trigger_leads_update_datetime
1007
+ BEFORE UPDATE ON leads
1008
+ FOR EACH ROW
1009
+ EXECUTE FUNCTION update_datetime_alt();
1010
+
1011
+ DROP TRIGGER IF EXISTS trigger_missions_update_datetime ON missions;
1012
+ CREATE TRIGGER trigger_missions_update_datetime
1013
+ BEFORE UPDATE ON missions
1014
+ FOR EACH ROW
1015
+ EXECUTE FUNCTION update_datetime_alt();
1016
+
1017
+ DROP TRIGGER IF EXISTS trigger_objectives_update_datetime ON objectives;
1018
+ CREATE TRIGGER trigger_objectives_update_datetime
1019
+ BEFORE UPDATE ON objectives
1020
+ FOR EACH ROW
1021
+ EXECUTE FUNCTION update_datetime_alt();
1022
+
1023
+ DROP TRIGGER IF EXISTS trigger_conversations_update_datetime ON conversations;
1024
+ CREATE TRIGGER trigger_conversations_update_datetime
1025
+ BEFORE UPDATE ON conversations
1026
+ FOR EACH ROW
1027
+ EXECUTE FUNCTION update_datetime_alt();
1028
+
1029
+ DROP TRIGGER IF EXISTS trigger_objectives_tools_update_datetime ON objectives_tools;
1030
+ CREATE TRIGGER trigger_objectives_tools_update_datetime
1031
+ BEFORE UPDATE ON objectives_tools
1032
+ FOR EACH ROW
1033
+ EXECUTE FUNCTION update_datetime_alt();
1034
+
1035
+ DROP TRIGGER IF EXISTS trigger_messages_update_datetime ON messages;
1036
+ CREATE TRIGGER trigger_messages_update_datetime
1037
+ BEFORE UPDATE ON messages
1038
+ FOR EACH ROW
1039
+ EXECUTE FUNCTION update_datetime_alt();
1040
+
1041
+ DROP TRIGGER IF EXISTS trigger_companies_update_datetime ON companies;
1042
+ CREATE TRIGGER trigger_companies_update_datetime
1043
+ BEFORE UPDATE ON companies
1044
+ FOR EACH ROW
1045
+ EXECUTE FUNCTION update_datetime_alt();
1046
+
1047
+ DROP TRIGGER IF EXISTS trigger_conversations_tags_update_datetime ON conversations_tags;
1048
+ CREATE TRIGGER trigger_conversations_tags_update_datetime
1049
+ BEFORE UPDATE ON conversations_tags
1050
+ FOR EACH ROW
1051
+ EXECUTE FUNCTION update_datetime_alt();
1052
+
1053
+ DROP TRIGGER IF EXISTS trigger_leads_tags_update_datetime ON leads_tags;
1054
+ CREATE TRIGGER trigger_leads_tags_update_datetime
1055
+ BEFORE UPDATE ON leads_tags
1056
+ FOR EACH ROW
1057
+ EXECUTE FUNCTION update_datetime_alt();
1058
+
1059
+ -- Triggers de auditoria para tabelas críticas
1060
+ RAISE NOTICE '[%] Criando triggers de auditoria', now();
1061
+
1062
+ DROP TRIGGER IF EXISTS trigger_conversations_audit ON conversations;
1063
+ CREATE TRIGGER trigger_conversations_audit
1064
+ AFTER INSERT OR UPDATE OR DELETE ON conversations
1065
+ FOR EACH ROW
1066
+ EXECUTE FUNCTION audit_changes();
1067
+
1068
+ DROP TRIGGER IF EXISTS trigger_messages_audit ON messages;
1069
+ CREATE TRIGGER trigger_messages_audit
1070
+ AFTER INSERT OR UPDATE OR DELETE ON messages
1071
+ FOR EACH ROW
1072
+ EXECUTE FUNCTION audit_changes();
1073
+
1074
+ DROP TRIGGER IF EXISTS trigger_leads_audit ON leads;
1075
+ CREATE TRIGGER trigger_leads_audit
1076
+ AFTER INSERT OR UPDATE OR DELETE ON leads
1077
+ FOR EACH ROW
1078
+ EXECUTE FUNCTION audit_changes();
1079
+
1080
+ DROP TRIGGER IF EXISTS trigger_accounts_audit ON accounts;
1081
+ CREATE TRIGGER trigger_accounts_audit
1082
+ AFTER INSERT OR UPDATE OR DELETE ON accounts
1083
+ FOR EACH ROW
1084
+ EXECUTE FUNCTION audit_changes();
1085
+
1086
+ -- Trigger para controle de conversas
1087
+ DROP TRIGGER IF EXISTS trigger_messages_update_conversation ON messages;
1088
+ CREATE TRIGGER trigger_messages_update_conversation
1089
+ AFTER INSERT ON messages
1090
+ FOR EACH ROW
1091
+ EXECUTE FUNCTION update_conversation_last_message();
1092
+
1093
+ -- Contar functions e triggers
1094
+ SELECT COUNT(*) INTO total_functions
1095
+ FROM information_schema.routines
1096
+ WHERE routine_schema = 'public'
1097
+ AND routine_name IN ('update_datetime_alt', 'audit_changes', 'update_conversation_last_message');
1098
+
1099
+ SELECT COUNT(*) INTO total_triggers
1100
+ FROM information_schema.triggers
1101
+ WHERE trigger_schema = 'public'
1102
+ AND trigger_name LIKE 'trigger_%';
1103
+
1104
+ block_end_time := CURRENT_TIMESTAMP;
1105
+ block_duration := EXTRACT(EPOCH FROM (block_end_time - block_start_time)) * 1000;
1106
+
1107
+ PERFORM create_migration_checkpoint('triggers_completed', json_build_object('total_functions', total_functions, 'total_triggers', total_triggers, 'duration_ms', block_duration));
1108
+
1109
+ RAISE NOTICE '[%] ✓ BLOCO 4 CONCLUÍDO: % functions + % triggers criados em %ms', now(), total_functions, total_triggers, block_duration;
1110
+
1111
+ EXCEPTION WHEN OTHERS THEN
1112
+ PERFORM rollback_migration('TRIGGERS_FUNCTIONS');
1113
+ RAISE EXCEPTION 'FALHA no BLOCO 4 (Triggers/Functions): %', SQLERRM;
1114
+ END;
1115
+
1116
+ -- ========================================
1117
+ -- VERIFICAÇÃO FINAL E ESTATÍSTICAS
1118
+ -- ========================================
1119
+ migration_end_time := CURRENT_TIMESTAMP;
1120
+ total_duration := EXTRACT(EPOCH FROM (migration_end_time - migration_start_time));
1121
+
1122
+ RAISE NOTICE '========================================';
1123
+ RAISE NOTICE 'MIGRAÇÃO GAGENTS v1.0 CONCLUÍDA COM SUCESSO';
1124
+ RAISE NOTICE 'Tempo total de execução: % segundos', total_duration;
1125
+ RAISE NOTICE 'Finalizada em: %', migration_end_time;
1126
+ RAISE NOTICE '========================================';
1127
+
1128
+ -- Estatísticas finais
1129
+ RAISE NOTICE 'COMPONENTES CRIADOS:';
1130
+ RAISE NOTICE '- Tabelas: % (esperado: 17+)',
1131
+ (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public');
1132
+ RAISE NOTICE '- Índices: % (esperado: 50+)',
1133
+ (SELECT COUNT(*) FROM pg_indexes WHERE schemaname = 'public');
1134
+ RAISE NOTICE '- Constraints: % (esperado: 30+)',
1135
+ (SELECT COUNT(*) FROM information_schema.table_constraints WHERE table_schema = 'public');
1136
+ RAISE NOTICE '- Functions: % (esperado: 3+)',
1137
+ (SELECT COUNT(*) FROM information_schema.routines WHERE routine_schema = 'public');
1138
+ RAISE NOTICE '- Triggers: % (esperado: 17+)',
1139
+ (SELECT COUNT(*) FROM information_schema.triggers WHERE trigger_schema = 'public');
1140
+
1141
+ EXCEPTION WHEN OTHERS THEN
1142
+ RAISE EXCEPTION 'FALHA CRÍTICA NA MIGRAÇÃO no bloco %: %', current_block, SQLERRM;
1143
+ END $$;
1144
+
1145
+ -- ============================================================================
1146
+ -- SEÇÃO 5: FUNCTIONS UTILITÁRIAS PARA MANUTENÇÃO
1147
+ -- ============================================================================
1148
+
1149
+ -- Function para obter status da migração
1150
+ CREATE OR REPLACE FUNCTION get_migration_status()
1151
+ RETURNS TABLE(
1152
+ checkpoint_name VARCHAR,
1153
+ executed_at TIMESTAMP WITH TIME ZONE,
1154
+ success BOOLEAN,
1155
+ execution_time_ms INTEGER,
1156
+ error_message TEXT
1157
+ ) AS $$
1158
+ BEGIN
1159
+ RETURN QUERY
1160
+ SELECT sm.migration_name, sm.executed_at, sm.success, sm.execution_time_ms, sm.error_message
1161
+ FROM schema_migrations sm
1162
+ ORDER BY sm.executed_at DESC;
1163
+ END;
1164
+ $$ LANGUAGE plpgsql;
1165
+
1166
+ -- Function para validar integridade do banco
1167
+ CREATE OR REPLACE FUNCTION validate_database_integrity()
1168
+ RETURNS TABLE(
1169
+ validation_type VARCHAR,
1170
+ expected_count INTEGER,
1171
+ actual_count INTEGER,
1172
+ status VARCHAR
1173
+ ) AS $$
1174
+ DECLARE
1175
+ table_count INTEGER;
1176
+ index_count INTEGER;
1177
+ constraint_count INTEGER;
1178
+ trigger_count INTEGER;
1179
+ BEGIN
1180
+ -- Validar tabelas
1181
+ SELECT COUNT(*) INTO table_count FROM information_schema.tables
1182
+ WHERE table_schema = 'public' AND table_type = 'BASE TABLE';
1183
+
1184
+ RETURN QUERY SELECT 'TABLES'::VARCHAR, 17, table_count,
1185
+ CASE WHEN table_count >= 17 THEN 'OK' ELSE 'FAIL' END::VARCHAR;
1186
+
1187
+ -- Validar índices
1188
+ SELECT COUNT(*) INTO index_count FROM pg_indexes WHERE schemaname = 'public';
1189
+
1190
+ RETURN QUERY SELECT 'INDEXES'::VARCHAR, 50, index_count,
1191
+ CASE WHEN index_count >= 50 THEN 'OK' ELSE 'FAIL' END::VARCHAR;
1192
+
1193
+ -- Validar constraints
1194
+ SELECT COUNT(*) INTO constraint_count FROM information_schema.table_constraints
1195
+ WHERE table_schema = 'public';
1196
+
1197
+ RETURN QUERY SELECT 'CONSTRAINTS'::VARCHAR, 30, constraint_count,
1198
+ CASE WHEN constraint_count >= 30 THEN 'OK' ELSE 'FAIL' END::VARCHAR;
1199
+
1200
+ -- Validar triggers
1201
+ SELECT COUNT(*) INTO trigger_count FROM information_schema.triggers
1202
+ WHERE trigger_schema = 'public';
1203
+
1204
+ RETURN QUERY SELECT 'TRIGGERS'::VARCHAR, 20, trigger_count,
1205
+ CASE WHEN trigger_count >= 20 THEN 'OK' ELSE 'FAIL' END::VARCHAR;
1206
+ END;
1207
+ $$ LANGUAGE plpgsql;
1208
+
1209
+ -- Comentários finais
1210
+ COMMENT ON FUNCTION get_migration_status() IS 'Function para obter status detalhado da migração';
1211
+ COMMENT ON FUNCTION validate_database_integrity() IS 'Function para validar integridade completa do banco';
1212
+
1213
+ -- Log final de conclusão
1214
+ DO $$
1215
+ BEGIN
1216
+ RAISE NOTICE '';
1217
+ RAISE NOTICE '============================================================================';
1218
+ RAISE NOTICE 'SCRIPT MIGRATE_V1.SQL CARREGADO E EXECUTADO COM SUCESSO!';
1219
+ RAISE NOTICE '';
1220
+ RAISE NOTICE 'PRÓXIMOS PASSOS:';
1221
+ RAISE NOTICE '1. Execute: SELECT * FROM get_migration_status(); -- Para ver status';
1222
+ RAISE NOTICE '2. Execute: SELECT * FROM validate_database_integrity(); -- Para validar';
1223
+ RAISE NOTICE '3. Execute: ANALYZE; -- Para atualizar estatísticas do PostgreSQL';
1224
+ RAISE NOTICE '';
1225
+ RAISE NOTICE 'SISTEMA GAGENTS v1.0 TOTALMENTE OPERACIONAL!';
1226
+ RAISE NOTICE '============================================================================';
1227
+ END $$;