REFACTOR: Estrutura de migrations com rastreamento e separação de seeds
- Introduz schema_migrations para controle de migrations já aplicadas: apenas arquivos novos rodam a cada deploy, eliminando re-execuções - Renumera migrations de 001 a 026 sem lacunas (gaps em 013 e 024 fechados) - Adiciona 008_mensagens.sql: CREATE TABLE que estava ausente do repositório - Corrige idempotência de 015_configurable_triage_flow: adiciona UNIQUE (flow_id, label) e (audience_id, label) e targets explícitos no ON CONFLICT que antes duplicava dados a cada deploy - Separa dados de demonstração em database/seeds/ (só roda em dev/uat) - Remove seeds e migrations sem propósito: seeds/002 e 027 tornados obsoletos com a remoção dos opening templates - Atualiza deploy-dev.yml: loop com checagem de tracking + passo de seeds
This commit is contained in:
parent
dcb3a89e26
commit
c5ebbd50b5
@ -62,8 +62,40 @@ jobs:
|
|||||||
PGHOST="${PGHOST//[$'\t\r\n ']}"
|
PGHOST="${PGHOST//[$'\t\r\n ']}"
|
||||||
PGPORT="${PGPORT//[$'\t\r\n ']}"
|
PGPORT="${PGPORT//[$'\t\r\n ']}"
|
||||||
export PGHOST PGPORT
|
export PGHOST PGPORT
|
||||||
echo "Aplicando migrations..."
|
|
||||||
for f in $(ls "$DEPLOY_PATH/database/migrations/"*.sql | sort); do
|
echo "Inicializando controle de migrations..."
|
||||||
echo "Aplicando: $(basename "$f")"
|
psql -v ON_ERROR_STOP=1 -f "$DEPLOY_PATH/database/migrations/000_schema_migrations.sql"
|
||||||
|
|
||||||
|
echo "Aplicando migrations pendentes..."
|
||||||
|
for f in $(ls "$DEPLOY_PATH/database/migrations/"[0-9][0-9][0-9]_*.sql | sort); do
|
||||||
|
filename=$(basename "$f")
|
||||||
|
already_run=$(psql -tAc "SELECT COUNT(*) FROM schema_migrations WHERE filename = '$filename'")
|
||||||
|
if [ "$already_run" = "0" ]; then
|
||||||
|
echo "Aplicando: $filename"
|
||||||
|
psql -v ON_ERROR_STOP=1 -f "$f"
|
||||||
|
psql -c "INSERT INTO schema_migrations (filename) VALUES ('$filename') ON CONFLICT DO NOTHING"
|
||||||
|
echo "OK: $filename"
|
||||||
|
else
|
||||||
|
echo "Ignorando (ja aplicada): $filename"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Executar seeds (dev)
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
PGHOST: ${{ secrets.DB_HOST }}
|
||||||
|
PGPORT: ${{ secrets.DB_PORT }}
|
||||||
|
PGUSER: ${{ secrets.DB_USER }}
|
||||||
|
PGDATABASE: ${{ secrets.DB_NAME }}
|
||||||
|
PGPASSWORD: ${{ secrets.DB_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
PGHOST="${PGHOST//[$'\t\r\n ']}"
|
||||||
|
PGPORT="${PGPORT//[$'\t\r\n ']}"
|
||||||
|
export PGHOST PGPORT
|
||||||
|
|
||||||
|
echo "Aplicando seeds de desenvolvimento..."
|
||||||
|
for f in $(ls "$DEPLOY_PATH/database/seeds/"*.sql | sort); do
|
||||||
|
echo "Seed: $(basename "$f")"
|
||||||
psql -v ON_ERROR_STOP=1 -f "$f"
|
psql -v ON_ERROR_STOP=1 -f "$f"
|
||||||
done
|
done
|
||||||
|
|||||||
38
database/migrations/000_schema_migrations.sql
Normal file
38
database/migrations/000_schema_migrations.sql
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
-- Migration 000: Controle de migrations aplicadas
|
||||||
|
-- Este arquivo roda sempre (antes do loop principal) e e idempotente.
|
||||||
|
-- Cria a tabela de rastreamento e marca todas as migrations existentes
|
||||||
|
-- como ja aplicadas para que nao re-executem em ambientes ja provisionados.
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||||
|
filename VARCHAR(255) PRIMARY KEY,
|
||||||
|
applied_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO schema_migrations (filename) VALUES
|
||||||
|
('001_auth.sql'),
|
||||||
|
('002_area.sql'),
|
||||||
|
('003_whatsapp.sql'),
|
||||||
|
('004_whatsapp_templates.sql'),
|
||||||
|
('005_whatsapp_assignment_queue.sql'),
|
||||||
|
('006_whatsapp_triage_state.sql'),
|
||||||
|
('007_agent_notes.sql'),
|
||||||
|
('008_mensagens.sql'),
|
||||||
|
('009_customer_contacts.sql'),
|
||||||
|
('010_agenda_contatos.sql'),
|
||||||
|
('011_whatsapp_awaiting_customer_reply.sql'),
|
||||||
|
('012_whatsapp_template_workflow.sql'),
|
||||||
|
('013_agent_presence_pause.sql'),
|
||||||
|
('014_hr_decision_tree_keywords.sql'),
|
||||||
|
('015_configurable_triage_flow.sql'),
|
||||||
|
('016_triage_resolution_step.sql'),
|
||||||
|
('017_bot_flow_builder.sql'),
|
||||||
|
('018_bot_flow_close_node.sql'),
|
||||||
|
('019_admin_audit_ai_contents.sql'),
|
||||||
|
('020_whatsapp_template_category.sql'),
|
||||||
|
('021_agenda_contact_channels.sql'),
|
||||||
|
('022_whatsapp_config.sql'),
|
||||||
|
('023_whatsapp_templates_hsm.sql'),
|
||||||
|
('024_mensagens_media_columns.sql'),
|
||||||
|
('025_mensagens_media_filename.sql'),
|
||||||
|
('026_remove_fake_templates.sql')
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
17
database/migrations/008_mensagens.sql
Normal file
17
database/migrations/008_mensagens.sql
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
-- Migration 008: Tabela de mensagens do WhatsApp
|
||||||
|
CREATE TABLE IF NOT EXISTS mensagens (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
wamid VARCHAR(255) NOT NULL,
|
||||||
|
chat_id VARCHAR(255) NOT NULL,
|
||||||
|
from_me BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
type VARCHAR(50) NOT NULL,
|
||||||
|
body TEXT,
|
||||||
|
sender_name VARCHAR(255),
|
||||||
|
status VARCHAR(50),
|
||||||
|
timestamp BIGINT NOT NULL,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE (wamid)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_mensagens_chat_id ON mensagens (chat_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_mensagens_timestamp ON mensagens (timestamp DESC);
|
||||||
@ -1,19 +0,0 @@
|
|||||||
-- ============================================================
|
|
||||||
-- Migration 011: Templates de abertura ativa do WhatsApp
|
|
||||||
-- Tabela: whatsapp_templates
|
|
||||||
-- ============================================================
|
|
||||||
|
|
||||||
INSERT INTO whatsapp_templates (name, content) VALUES
|
|
||||||
('abertura_atendimento_padrao', 'Ola, {nome}. Tudo bem? Estamos entrando em contato pelo atendimento. Podemos seguir por aqui?'),
|
|
||||||
('abertura_retorno_contato', 'Ola, {nome}. Estamos retornando seu contato para dar continuidade ao seu atendimento.'),
|
|
||||||
('abertura_suporte', 'Ola, {nome}. Aqui e do suporte. Estamos entrando em contato para te ajudar com sua solicitacao.'),
|
|
||||||
('abertura_financeiro', 'Ola, {nome}. Aqui e do financeiro. Estamos entrando em contato para tratar de uma informacao importante sobre seu atendimento.'),
|
|
||||||
('abertura_comercial', 'Ola, {nome}. Aqui e do comercial. Estamos entrando em contato para conversar sobre sua solicitacao.'),
|
|
||||||
('abertura_confirmacao_dados', 'Ola, {nome}. Precisamos confirmar alguns dados para seguir com seu atendimento.'),
|
|
||||||
('abertura_contato_agendado', 'Ola, {nome}. Este contato foi combinado anteriormente e estamos disponiveis para seguir.'),
|
|
||||||
('abertura_pos_atendimento', 'Ola, {nome}. Estamos fazendo um acompanhamento sobre seu atendimento recente.'),
|
|
||||||
('abertura_aviso_importante', 'Ola, {nome}. Temos uma informacao importante para compartilhar com voce.'),
|
|
||||||
('abertura_contato_inicial', 'Ola, {nome}. Vamos iniciar seu atendimento por este canal.')
|
|
||||||
ON CONFLICT (name) DO UPDATE SET
|
|
||||||
content = EXCLUDED.content,
|
|
||||||
updated_at = CURRENT_TIMESTAMP;
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- Migration 017: Fluxo configuravel de triagem do Agente Virtual Sothis
|
-- Migration 015: Fluxo configuravel de triagem do Agente Virtual Sothis
|
||||||
-- Tabelas:
|
-- Tabelas:
|
||||||
-- bot_triage_flows
|
-- bot_triage_flows
|
||||||
-- bot_triage_audiences
|
-- bot_triage_audiences
|
||||||
@ -29,7 +29,8 @@ CREATE TABLE IF NOT EXISTS bot_triage_audiences (
|
|||||||
sort_order INTEGER NOT NULL DEFAULT 1,
|
sort_order INTEGER NOT NULL DEFAULT 1,
|
||||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE (flow_id, label)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS bot_triage_intents (
|
CREATE TABLE IF NOT EXISTS bot_triage_intents (
|
||||||
@ -41,7 +42,8 @@ CREATE TABLE IF NOT EXISTS bot_triage_intents (
|
|||||||
sort_order INTEGER NOT NULL DEFAULT 1,
|
sort_order INTEGER NOT NULL DEFAULT 1,
|
||||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE (audience_id, label)
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE whatsapp_chat_atribuicoes
|
ALTER TABLE whatsapp_chat_atribuicoes
|
||||||
@ -114,7 +116,7 @@ JOIN (
|
|||||||
('Sou ex-colaborador', 'ex colaborador, ex-colaborador, rescisao, fgts, informe de rendimentos, desligamento', 2),
|
('Sou ex-colaborador', 'ex colaborador, ex-colaborador, rescisao, fgts, informe de rendimentos, desligamento', 2),
|
||||||
('Sou candidato a uma vaga', 'candidato, vaga, curriculo, processo seletivo, entrevista, candidatura', 3)
|
('Sou candidato a uma vaga', 'candidato, vaga, curriculo, processo seletivo, entrevista, candidatura', 3)
|
||||||
) AS seed(label, keywords, sort_order) ON TRUE
|
) AS seed(label, keywords, sort_order) ON TRUE
|
||||||
ON CONFLICT DO NOTHING;
|
ON CONFLICT (flow_id, label) DO NOTHING;
|
||||||
|
|
||||||
WITH audiences AS (
|
WITH audiences AS (
|
||||||
SELECT bta.id, bta.label
|
SELECT bta.id, bta.label
|
||||||
@ -142,4 +144,4 @@ SELECT audiences.id, seed.label, areas.id, seed.keywords, seed.sort_order, TRUE,
|
|||||||
FROM seed
|
FROM seed
|
||||||
INNER JOIN audiences ON audiences.label = seed.audience_label
|
INNER JOIN audiences ON audiences.label = seed.audience_label
|
||||||
INNER JOIN areas ON areas.nome = seed.area_nome
|
INNER JOIN areas ON areas.nome = seed.area_nome
|
||||||
ON CONFLICT DO NOTHING;
|
ON CONFLICT (audience_id, label) DO NOTHING;
|
||||||
@ -1,18 +0,0 @@
|
|||||||
-- Migration 030: Remove templates de abertura sem vinculo com a Meta
|
|
||||||
-- A migration 011 re-insere esses templates em todo deploy via ON CONFLICT DO UPDATE.
|
|
||||||
-- Este arquivo garante que sejam removidos caso nao estejam vinculados a Meta.
|
|
||||||
|
|
||||||
DELETE FROM whatsapp_templates
|
|
||||||
WHERE name IN (
|
|
||||||
'abertura_atendimento_padrao',
|
|
||||||
'abertura_retorno_contato',
|
|
||||||
'abertura_suporte',
|
|
||||||
'abertura_financeiro',
|
|
||||||
'abertura_comercial',
|
|
||||||
'abertura_confirmacao_dados',
|
|
||||||
'abertura_contato_agendado',
|
|
||||||
'abertura_pos_atendimento',
|
|
||||||
'abertura_aviso_importante',
|
|
||||||
'abertura_contato_inicial'
|
|
||||||
)
|
|
||||||
AND meta_template_name IS NULL;
|
|
||||||
Loading…
Reference in New Issue
Block a user