FEAT: documenta regras do produto e adiciona migrations de suporte
- documenta estado atual, regras de negócio e próximos passos do Omnichannel Sothis - adiciona migrations para triagem RH, flow builder, auditoria, conteúdos da IA e categoria de templates - prepara estrutura de banco para fluxo configurável do bot, encerramento automático e templates categorizados
This commit is contained in:
parent
a6a09aa740
commit
e9e214195a
29
README.md
29
README.md
@ -11,7 +11,7 @@ O produto hoje permite:
|
||||
- Login via Active Directory/LDAP e Microsoft OAuth.
|
||||
- Redirecionamento da Home conforme perfil do usuário.
|
||||
- Atendimento WhatsApp em tempo real com socket.
|
||||
- Triagem automática inicial pelo Omnino.
|
||||
- Triagem automática inicial pelo Agente Virtual Sothis.
|
||||
- Fila de atendimento por especialidade.
|
||||
- Assumir, liberar e transferir atendimentos.
|
||||
- Controle da janela de 24 horas do WhatsApp.
|
||||
@ -61,7 +61,7 @@ O admin:
|
||||
|
||||
- Não precisa pertencer a uma especialidade.
|
||||
- Vê todas as filas e todos os atendimentos já roteados/classificados.
|
||||
- Não vê conversas ainda em triagem automática do Omnino (`bot_triage`).
|
||||
- Não vê conversas ainda em triagem automática do Agente Virtual Sothis (`bot_triage`).
|
||||
- Cria e gerencia especialidades.
|
||||
- Define perfis de usuários.
|
||||
- Pode tornar outro usuário admin.
|
||||
@ -72,22 +72,22 @@ O admin:
|
||||
|
||||
## Regras de Atendimento WhatsApp
|
||||
|
||||
### Triagem automática pelo Omnino
|
||||
### Triagem automática pelo Agente Virtual Sothis
|
||||
|
||||
Toda primeira mensagem recebida no WhatsApp passa pela triagem automática do Omnino quando ainda não existe atendimento classificado.
|
||||
Toda primeira mensagem recebida no WhatsApp passa pela triagem automática do Agente Virtual Sothis quando ainda não existe atendimento classificado.
|
||||
|
||||
Regras atuais:
|
||||
|
||||
- Mensagens vazias são registradas, mas não disparam triagem.
|
||||
- Mídia sem legenda é registrada, mas não aciona o Omnino automaticamente.
|
||||
- Mídia sem legenda é registrada, mas não aciona o Agente Virtual Sothis automaticamente.
|
||||
- A triagem é serializada por conversa para evitar respostas duplicadas quando o WhatsApp dispara eventos quase simultâneos.
|
||||
- O backend guarda `last_routed_message_id` para evitar processar a mesma mensagem mais de uma vez.
|
||||
|
||||
Fluxo de decisão:
|
||||
|
||||
1. Se a mensagem já contém uma intenção clara, o Omnino roteia direto para a especialidade.
|
||||
2. Se a primeira mensagem é genérica, o Omnino envia a saudação completa e mantém a conversa em `bot_triage`.
|
||||
3. Se a segunda mensagem ainda não identifica intenção, o Omnino pede explicitamente: suporte, financeiro ou comercial.
|
||||
1. Se a mensagem já contém uma intenção clara, o Agente Virtual Sothis roteia direto para a especialidade.
|
||||
2. Se a primeira mensagem é genérica, o Agente Virtual Sothis envia a saudação completa e mantém a conversa em `bot_triage`.
|
||||
3. Se a segunda mensagem ainda não identifica intenção, o Agente Virtual Sothis pede explicitamente: suporte, financeiro ou comercial.
|
||||
4. Se a terceira mensagem ainda for inválida, a conversa cai automaticamente em Suporte.
|
||||
|
||||
Exemplos de intenção:
|
||||
@ -100,7 +100,7 @@ Exemplos de intenção:
|
||||
|
||||
A tabela `whatsapp_chat_atribuicoes` representa o estado do atendimento:
|
||||
|
||||
- `bot_triage`: conversa ainda está com o Omnino.
|
||||
- `bot_triage`: conversa ainda está com o Agente Virtual Sothis.
|
||||
- `queued`: conversa está na fila da especialidade, sem atendente.
|
||||
- `assigned`: conversa foi assumida ou atribuída diretamente a um atendente.
|
||||
|
||||
@ -180,7 +180,7 @@ O frontend bloqueia anexos acima de 15 MB para evitar falhas de payload e degrad
|
||||
|
||||
## Chat
|
||||
|
||||
A tela `/chat` exibe somente conversas reais vindas do backend/WhatsApp. Não existe mais fallback de conversas mockadas.
|
||||
A tela `/chat` exibe somente conversas reais vindas do backend/WhatsApp. Não existe fallback local de conversas.
|
||||
|
||||
Recursos atuais:
|
||||
|
||||
@ -333,7 +333,7 @@ Tela administrativa para usuários, perfis e especialidades.
|
||||
|
||||
Regras atuais:
|
||||
|
||||
- Usuários vêm do banco/autenticação, não de mock.
|
||||
- Usuários vêm do banco/autenticação.
|
||||
- Admin Demo, Supervisor Demo e Atendente Demo foram removidos por migration.
|
||||
- Admin pode editar um usuário em modal.
|
||||
- Se o usuário for admin, não há seleção de especialidades.
|
||||
@ -357,10 +357,10 @@ Recursos atuais:
|
||||
- Atendentes online simulados.
|
||||
- Painel do time.
|
||||
- Fila de espera.
|
||||
- Atribuição mockada via modal.
|
||||
- Atribuição via modal.
|
||||
- Gráfico do dia por hora.
|
||||
|
||||
Sempre que há dado real disponível, a tendência do produto é substituir mock por dado real. Indicadores sem origem real consolidada ainda usam dados simulados.
|
||||
Sempre que há dado operacional disponível, os painéis usam a origem real consolidada. Indicadores sem origem consolidada ainda usam dados de apoio para compor a visão.
|
||||
|
||||
## Autenticação
|
||||
|
||||
@ -532,7 +532,7 @@ VITE_API_URL=http://localhost:3001
|
||||
|
||||
## Regras Importantes Para Desenvolvimento
|
||||
|
||||
- Não reintroduzir mock de conversas no chat. O `/chat` deve exibir apenas conversas reais vindas do backend/WhatsApp.
|
||||
- Não reintroduzir fallback local de conversas no chat. O `/chat` deve exibir apenas conversas reais vindas do backend/WhatsApp.
|
||||
- Toda alteração de banco deve virar nova migration numerada.
|
||||
- Não permitir resposta sem atendimento assumido.
|
||||
- Não permitir transferência sem atendimento assumido.
|
||||
@ -542,3 +542,4 @@ VITE_API_URL=http://localhost:3001
|
||||
- Depois de contato ativo, bloquear novas mensagens livres até resposta do cliente.
|
||||
- Mídia sem texto não deve enviar cabeçalho solto de atendente.
|
||||
|
||||
|
||||
|
||||
99
database/migrations/016_hr_decision_tree_keywords.sql
Normal file
99
database/migrations/016_hr_decision_tree_keywords.sql
Normal file
@ -0,0 +1,99 @@
|
||||
-- ============================================================
|
||||
-- Migration 016: Arvore de decisao por especialidade para RH
|
||||
-- Tabelas:
|
||||
-- area_routing_keywords
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS area_routing_keywords (
|
||||
id SERIAL PRIMARY KEY,
|
||||
area_id INTEGER NOT NULL REFERENCES areas(id) ON DELETE CASCADE,
|
||||
keyword VARCHAR(160) NOT NULL,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT uq_area_routing_keyword UNIQUE (area_id, keyword)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_area_routing_keywords_area
|
||||
ON area_routing_keywords (area_id, active);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_area_routing_keywords_keyword
|
||||
ON area_routing_keywords (keyword);
|
||||
|
||||
INSERT INTO areas (nome, descricao) VALUES
|
||||
('Benefícios', 'Duvidas de RH sobre beneficios, convenios, vale transporte e vale refeicao'),
|
||||
('Ponto', 'Ajustes de ponto, banco de horas, atrasos e jornada'),
|
||||
('Holerite', 'Holerite, folha de pagamento, descontos e demonstrativos'),
|
||||
('Férias', 'Ferias, abono, programacao e saldo de descanso'),
|
||||
('Recrutamento', 'Vagas internas, candidatura, entrevista e processo seletivo')
|
||||
ON CONFLICT (nome) DO UPDATE SET
|
||||
descricao = EXCLUDED.descricao,
|
||||
ativo = TRUE,
|
||||
updated_at = NOW();
|
||||
|
||||
INSERT INTO area_routing_keywords (area_id, keyword)
|
||||
SELECT a.id, keyword
|
||||
FROM areas a
|
||||
JOIN (
|
||||
VALUES
|
||||
('Benefícios', 'beneficio'),
|
||||
('Benefícios', 'beneficios'),
|
||||
('Benefícios', 'vale refeicao'),
|
||||
('Benefícios', 'vale alimentacao'),
|
||||
('Benefícios', 'vale transporte'),
|
||||
('Benefícios', 'convenio'),
|
||||
('Benefícios', 'plano de saude'),
|
||||
('Benefícios', 'odonto'),
|
||||
('Ponto', 'ponto'),
|
||||
('Ponto', 'espelho de ponto'),
|
||||
('Ponto', 'banco de horas'),
|
||||
('Ponto', 'atraso'),
|
||||
('Ponto', 'jornada'),
|
||||
('Ponto', 'batida'),
|
||||
('Ponto', 'marcacao'),
|
||||
('Holerite', 'holerite'),
|
||||
('Holerite', 'folha'),
|
||||
('Holerite', 'pagamento'),
|
||||
('Holerite', 'salario'),
|
||||
('Holerite', 'desconto'),
|
||||
('Holerite', 'demonstrativo'),
|
||||
('Férias', 'ferias'),
|
||||
('Férias', 'abono'),
|
||||
('Férias', 'descanso'),
|
||||
('Férias', 'saldo de ferias'),
|
||||
('Férias', 'programar ferias'),
|
||||
('Recrutamento', 'vaga'),
|
||||
('Recrutamento', 'vagas'),
|
||||
('Recrutamento', 'processo seletivo'),
|
||||
('Recrutamento', 'entrevista'),
|
||||
('Recrutamento', 'curriculo'),
|
||||
('Recrutamento', 'candidatura')
|
||||
) AS seed(area_nome, keyword) ON seed.area_nome = a.nome
|
||||
ON CONFLICT (area_id, keyword) DO UPDATE SET
|
||||
active = TRUE,
|
||||
updated_at = CURRENT_TIMESTAMP;
|
||||
|
||||
INSERT INTO whatsapp_templates (name, content, area_id, status, requested_by_role, admin_approved_at, meta_submitted_at, meta_approved_at, updated_at)
|
||||
SELECT template.name, template.content, a.id, 'approved', 'admin', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
|
||||
FROM areas a
|
||||
JOIN (
|
||||
VALUES
|
||||
('rh_abertura_beneficios', 'Ola, {nome}. Aqui e o Atendimento Sothis RH. Recebemos sua solicitacao sobre beneficios e vamos te apoiar por aqui.'),
|
||||
('rh_abertura_ponto', 'Ola, {nome}. Aqui e o Atendimento Sothis RH. Vamos te ajudar com sua solicitacao sobre ponto, jornada ou banco de horas.'),
|
||||
('rh_abertura_holerite', 'Ola, {nome}. Aqui e o Atendimento Sothis RH. Vamos te ajudar com holerite, folha ou demonstrativo de pagamento.'),
|
||||
('rh_abertura_ferias', 'Ola, {nome}. Aqui e o Atendimento Sothis RH. Vamos te apoiar com sua solicitacao sobre ferias.'),
|
||||
('rh_abertura_recrutamento', 'Ola, {nome}. Aqui e o Atendimento Sothis RH. Vamos te orientar sobre vagas, candidatura ou processo seletivo.')
|
||||
) AS template(name, content) ON template.name LIKE
|
||||
CASE a.nome
|
||||
WHEN 'Benefícios' THEN '%beneficios'
|
||||
WHEN 'Ponto' THEN '%ponto'
|
||||
WHEN 'Holerite' THEN '%holerite'
|
||||
WHEN 'Férias' THEN '%ferias'
|
||||
WHEN 'Recrutamento' THEN '%recrutamento'
|
||||
END
|
||||
ON CONFLICT (name) DO UPDATE SET
|
||||
content = EXCLUDED.content,
|
||||
area_id = EXCLUDED.area_id,
|
||||
status = 'approved',
|
||||
meta_approved_at = CURRENT_TIMESTAMP,
|
||||
updated_at = CURRENT_TIMESTAMP;
|
||||
145
database/migrations/017_configurable_triage_flow.sql
Normal file
145
database/migrations/017_configurable_triage_flow.sql
Normal file
@ -0,0 +1,145 @@
|
||||
-- ============================================================
|
||||
-- Migration 017: Fluxo configuravel de triagem do Agente Virtual Sothis
|
||||
-- Tabelas:
|
||||
-- bot_triage_flows
|
||||
-- bot_triage_audiences
|
||||
-- bot_triage_intents
|
||||
-- whatsapp_chat_atribuicoes
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bot_triage_flows (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(160) NOT NULL UNIQUE,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
greeting_message TEXT NOT NULL,
|
||||
audience_question TEXT NOT NULL,
|
||||
intent_question_template TEXT NOT NULL,
|
||||
fallback_message TEXT NOT NULL,
|
||||
fallback_area_id INTEGER REFERENCES areas(id) ON DELETE SET NULL,
|
||||
max_attempts INTEGER NOT NULL DEFAULT 2,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bot_triage_audiences (
|
||||
id SERIAL PRIMARY KEY,
|
||||
flow_id INTEGER NOT NULL REFERENCES bot_triage_flows(id) ON DELETE CASCADE,
|
||||
label VARCHAR(160) NOT NULL,
|
||||
keywords TEXT,
|
||||
sort_order INTEGER NOT NULL DEFAULT 1,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bot_triage_intents (
|
||||
id SERIAL PRIMARY KEY,
|
||||
audience_id INTEGER NOT NULL REFERENCES bot_triage_audiences(id) ON DELETE CASCADE,
|
||||
label VARCHAR(160) NOT NULL,
|
||||
area_id INTEGER NOT NULL REFERENCES areas(id) ON DELETE CASCADE,
|
||||
keywords TEXT,
|
||||
sort_order INTEGER NOT NULL DEFAULT 1,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
ALTER TABLE whatsapp_chat_atribuicoes
|
||||
ADD COLUMN IF NOT EXISTS triage_flow_id INTEGER REFERENCES bot_triage_flows(id) ON DELETE SET NULL,
|
||||
ADD COLUMN IF NOT EXISTS triage_audience_id INTEGER REFERENCES bot_triage_audiences(id) ON DELETE SET NULL,
|
||||
ADD COLUMN IF NOT EXISTS triage_step VARCHAR(40);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_bot_triage_flows_active
|
||||
ON bot_triage_flows (active);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_bot_triage_audiences_flow
|
||||
ON bot_triage_audiences (flow_id, active, sort_order);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_bot_triage_intents_audience
|
||||
ON bot_triage_intents (audience_id, active, sort_order);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_whatsapp_atribuicoes_triage_flow
|
||||
ON whatsapp_chat_atribuicoes (triage_flow_id, triage_audience_id, triage_step);
|
||||
|
||||
INSERT INTO areas (nome, descricao) VALUES
|
||||
('Documentos RH', 'Documentos para ex-colaboradores, informe de rendimentos e comprovantes'),
|
||||
('Rescisao', 'Rescisao, FGTS, verbas rescisorias e encerramento de contrato')
|
||||
ON CONFLICT (nome) DO UPDATE SET
|
||||
descricao = EXCLUDED.descricao,
|
||||
ativo = TRUE,
|
||||
updated_at = NOW();
|
||||
|
||||
INSERT INTO bot_triage_flows (
|
||||
name,
|
||||
active,
|
||||
greeting_message,
|
||||
audience_question,
|
||||
intent_question_template,
|
||||
fallback_message,
|
||||
fallback_area_id,
|
||||
max_attempts,
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
'RH CAOA - Atendimento Sothis',
|
||||
TRUE,
|
||||
'Ola! Sou o Agente Virtual Sothis. Vou te direcionar para o atendimento correto de RH.',
|
||||
'Digite o numero da opcao que melhor descreve voce:',
|
||||
'Perfeito. Agora digite o numero do assunto que voce precisa:',
|
||||
'Nao consegui identificar com seguranca. Vou encaminhar seu atendimento para o suporte de RH.',
|
||||
a.id,
|
||||
2,
|
||||
CURRENT_TIMESTAMP
|
||||
FROM areas a
|
||||
WHERE a.nome = 'Suporte'
|
||||
ON CONFLICT (name) DO UPDATE SET
|
||||
active = TRUE,
|
||||
greeting_message = EXCLUDED.greeting_message,
|
||||
audience_question = EXCLUDED.audience_question,
|
||||
intent_question_template = EXCLUDED.intent_question_template,
|
||||
fallback_message = EXCLUDED.fallback_message,
|
||||
fallback_area_id = EXCLUDED.fallback_area_id,
|
||||
max_attempts = EXCLUDED.max_attempts,
|
||||
updated_at = CURRENT_TIMESTAMP;
|
||||
|
||||
WITH flow AS (
|
||||
SELECT id FROM bot_triage_flows WHERE name = 'RH CAOA - Atendimento Sothis'
|
||||
)
|
||||
INSERT INTO bot_triage_audiences (flow_id, label, keywords, sort_order, active, updated_at)
|
||||
SELECT flow.id, seed.label, seed.keywords, seed.sort_order, TRUE, CURRENT_TIMESTAMP
|
||||
FROM flow
|
||||
JOIN (
|
||||
VALUES
|
||||
('Sou colaborador ativo', 'colaborador, funcionario, matricula, holerite, ferias, ponto, beneficios', 1),
|
||||
('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)
|
||||
) AS seed(label, keywords, sort_order) ON TRUE
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
WITH audiences AS (
|
||||
SELECT bta.id, bta.label
|
||||
FROM bot_triage_audiences bta
|
||||
INNER JOIN bot_triage_flows btf ON btf.id = bta.flow_id
|
||||
WHERE btf.name = 'RH CAOA - Atendimento Sothis'
|
||||
),
|
||||
seed AS (
|
||||
SELECT * FROM (
|
||||
VALUES
|
||||
('Sou colaborador ativo', 'Beneficios', 'beneficios, vale refeicao, vale transporte, convenio, plano de saude', 'Benefícios', 1),
|
||||
('Sou colaborador ativo', 'Holerite', 'holerite, folha, salario, pagamento, desconto', 'Holerite', 2),
|
||||
('Sou colaborador ativo', 'Ferias', 'ferias, abono, descanso, saldo de ferias', 'Férias', 3),
|
||||
('Sou colaborador ativo', 'Ponto', 'ponto, espelho de ponto, banco de horas, jornada, batida', 'Ponto', 4),
|
||||
('Sou ex-colaborador', 'Documentos', 'documento, documentos, comprovante, informe de rendimentos', 'Documentos RH', 1),
|
||||
('Sou ex-colaborador', 'FGTS e rescisao', 'fgts, rescisao, verbas rescisorias, desligamento', 'Rescisao', 2),
|
||||
('Sou ex-colaborador', 'Informe de rendimentos', 'informe, rendimentos, imposto de renda, ir', 'Documentos RH', 3),
|
||||
('Sou candidato a uma vaga', 'Status da vaga', 'status, vaga, retorno, resultado', 'Recrutamento', 1),
|
||||
('Sou candidato a uma vaga', 'Reagendar entrevista', 'reagendar, entrevista, agenda, horario', 'Recrutamento', 2),
|
||||
('Sou candidato a uma vaga', 'Nova candidatura', 'nova candidatura, curriculo, candidatar, oportunidade', 'Recrutamento', 3)
|
||||
) AS rows(audience_label, label, keywords, area_nome, sort_order)
|
||||
)
|
||||
INSERT INTO bot_triage_intents (audience_id, label, area_id, keywords, sort_order, active, updated_at)
|
||||
SELECT audiences.id, seed.label, areas.id, seed.keywords, seed.sort_order, TRUE, CURRENT_TIMESTAMP
|
||||
FROM seed
|
||||
INNER JOIN audiences ON audiences.label = seed.audience_label
|
||||
INNER JOIN areas ON areas.nome = seed.area_nome
|
||||
ON CONFLICT DO NOTHING;
|
||||
56
database/migrations/018_triage_resolution_step.sql
Normal file
56
database/migrations/018_triage_resolution_step.sql
Normal file
@ -0,0 +1,56 @@
|
||||
-- ============================================================
|
||||
-- Migration 018: Etapa de resposta antes da escalacao para fila
|
||||
-- Tabelas:
|
||||
-- bot_triage_flows
|
||||
-- bot_triage_intents
|
||||
-- whatsapp_chat_atribuicoes
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE bot_triage_flows
|
||||
ADD COLUMN IF NOT EXISTS resolution_question TEXT NOT NULL DEFAULT 'Essa informacao resolveu sua duvida? Responda 1 para encerrar ou 2 para falar com um especialista.';
|
||||
|
||||
ALTER TABLE bot_triage_intents
|
||||
ADD COLUMN IF NOT EXISTS response_message TEXT,
|
||||
ADD COLUMN IF NOT EXISTS resolution_question TEXT,
|
||||
ADD COLUMN IF NOT EXISTS escalation_message TEXT NOT NULL DEFAULT 'Certo, vou encaminhar seu atendimento para um especialista no assunto.';
|
||||
|
||||
ALTER TABLE whatsapp_chat_atribuicoes
|
||||
ADD COLUMN IF NOT EXISTS triage_intent_id INTEGER REFERENCES bot_triage_intents(id) ON DELETE SET NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_whatsapp_atribuicoes_triage_intent
|
||||
ON whatsapp_chat_atribuicoes (triage_intent_id, triage_step);
|
||||
|
||||
UPDATE bot_triage_intents
|
||||
SET response_message = CASE label
|
||||
WHEN 'Beneficios' THEN 'Para consultar beneficios, verifique no portal de RH a area Beneficios. La voce encontra vale refeicao, vale transporte, convenio medico, odontologico e regras de elegibilidade.'
|
||||
WHEN 'Holerite' THEN 'Seu holerite fica disponivel no portal de RH na area Folha de pagamento. Normalmente ele pode ser consultado por competencia, com detalhamento de salario, descontos e beneficios.'
|
||||
WHEN 'Ferias' THEN 'Para ferias, consulte saldo e periodos disponiveis no portal de RH. A solicitacao precisa respeitar a politica interna e o fluxo de aprovacao do gestor.'
|
||||
WHEN 'Ponto' THEN 'Para ponto, confira o espelho de ponto e solicite correcao de batida quando necessario. Ajustes de jornada e banco de horas seguem aprovacao do gestor.'
|
||||
WHEN 'Documentos' THEN 'Documentos de ex-colaborador podem ser solicitados pelo canal de RH. Informe o documento desejado e mantenha seus dados de contato atualizados.'
|
||||
WHEN 'FGTS e rescisao' THEN 'Para FGTS e rescisao, o time de RH valida o status do desligamento, prazos legais e documentos pendentes antes de retornar.'
|
||||
WHEN 'Informe de rendimentos' THEN 'O informe de rendimentos e disponibilizado conforme calendario fiscal. Caso nao encontre, o RH pode apoiar com a segunda via.'
|
||||
WHEN 'Status da vaga' THEN 'Para status de vaga, acompanhe o processo seletivo pelo canal informado na candidatura. O RH pode consultar a etapa atual quando houver identificacao do candidato.'
|
||||
WHEN 'Reagendar entrevista' THEN 'Para reagendar entrevista, informe nome completo, vaga e melhor disponibilidade. O time de recrutamento avalia a agenda.'
|
||||
WHEN 'Nova candidatura' THEN 'Para nova candidatura, acesse o portal de vagas e mantenha seu curriculo atualizado. O RH pode orientar sobre oportunidades abertas.'
|
||||
ELSE COALESCE(response_message, 'Tenho uma orientacao inicial para esse assunto. Se ainda precisar de ajuda, posso encaminhar para um especialista.')
|
||||
END
|
||||
WHERE response_message IS NULL;
|
||||
|
||||
UPDATE bot_triage_intents
|
||||
SET resolution_question = 'Isso resolveu sua duvida? Responda 1 para encerrar ou 2 para falar com um especialista de RH.'
|
||||
WHERE resolution_question IS NULL;
|
||||
|
||||
UPDATE bot_triage_intents
|
||||
SET escalation_message = CASE label
|
||||
WHEN 'Beneficios' THEN 'Certo, vou te encaminhar para um especialista em beneficios.'
|
||||
WHEN 'Holerite' THEN 'Certo, vou te encaminhar para um especialista em holerite e folha.'
|
||||
WHEN 'Ferias' THEN 'Certo, vou te encaminhar para um especialista em ferias.'
|
||||
WHEN 'Ponto' THEN 'Certo, vou te encaminhar para um especialista em ponto.'
|
||||
WHEN 'Documentos' THEN 'Certo, vou te encaminhar para um especialista em documentos de RH.'
|
||||
WHEN 'FGTS e rescisao' THEN 'Certo, vou te encaminhar para um especialista em rescisao.'
|
||||
WHEN 'Informe de rendimentos' THEN 'Certo, vou te encaminhar para um especialista em documentos de RH.'
|
||||
WHEN 'Status da vaga' THEN 'Certo, vou te encaminhar para o time de recrutamento.'
|
||||
WHEN 'Reagendar entrevista' THEN 'Certo, vou te encaminhar para o time de recrutamento.'
|
||||
WHEN 'Nova candidatura' THEN 'Certo, vou te encaminhar para o time de recrutamento.'
|
||||
ELSE escalation_message
|
||||
END;
|
||||
127
database/migrations/019_bot_flow_builder.sql
Normal file
127
database/migrations/019_bot_flow_builder.sql
Normal file
@ -0,0 +1,127 @@
|
||||
-- ============================================================
|
||||
-- Migration 019: Flow Builder visual do bot
|
||||
-- Tabelas:
|
||||
-- bot_flow_versions
|
||||
-- bot_flow_nodes
|
||||
-- whatsapp_chat_atribuicoes
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bot_flow_versions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(160) NOT NULL DEFAULT 'Fluxo RH Sothis',
|
||||
status VARCHAR(30) NOT NULL DEFAULT 'draft',
|
||||
version_number INTEGER NOT NULL DEFAULT 0,
|
||||
root_node_id INTEGER,
|
||||
published_at TIMESTAMP WITH TIME ZONE,
|
||||
snapshot JSONB,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bot_flow_nodes (
|
||||
id SERIAL PRIMARY KEY,
|
||||
version_id INTEGER NOT NULL REFERENCES bot_flow_versions(id) ON DELETE CASCADE,
|
||||
parent_id INTEGER REFERENCES bot_flow_nodes(id) ON DELETE CASCADE,
|
||||
node_type VARCHAR(30) NOT NULL CHECK (node_type IN ('greeting', 'question', 'agent', 'close')),
|
||||
title VARCHAR(160) NOT NULL,
|
||||
message_text TEXT,
|
||||
keywords TEXT,
|
||||
fallback_message TEXT,
|
||||
fallback_attempts INTEGER NOT NULL DEFAULT 2,
|
||||
fallback_area_id INTEGER REFERENCES areas(id) ON DELETE SET NULL,
|
||||
area_id INTEGER REFERENCES areas(id) ON DELETE SET NULL,
|
||||
sort_order INTEGER NOT NULL DEFAULT 1,
|
||||
position_x INTEGER NOT NULL DEFAULT 0,
|
||||
position_y INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'fk_bot_flow_versions_root'
|
||||
) THEN
|
||||
ALTER TABLE bot_flow_versions
|
||||
ADD CONSTRAINT fk_bot_flow_versions_root
|
||||
FOREIGN KEY (root_node_id) REFERENCES bot_flow_nodes(id) ON DELETE SET NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
ALTER TABLE whatsapp_chat_atribuicoes
|
||||
ADD COLUMN IF NOT EXISTS triage_builder_version_id INTEGER REFERENCES bot_flow_versions(id) ON DELETE SET NULL,
|
||||
ADD COLUMN IF NOT EXISTS triage_builder_node_id INTEGER REFERENCES bot_flow_nodes(id) ON DELETE SET NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_bot_flow_versions_status
|
||||
ON bot_flow_versions (status, published_at DESC, id DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_bot_flow_nodes_version_parent
|
||||
ON bot_flow_nodes (version_id, parent_id, sort_order, id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_whatsapp_atribuicoes_builder_node
|
||||
ON whatsapp_chat_atribuicoes (triage_builder_version_id, triage_builder_node_id);
|
||||
|
||||
WITH inserted_version AS (
|
||||
INSERT INTO bot_flow_versions (name, status, version_number, updated_at)
|
||||
SELECT 'Fluxo RH Sothis', 'draft', 0, CURRENT_TIMESTAMP
|
||||
WHERE NOT EXISTS (SELECT 1 FROM bot_flow_versions WHERE status = 'draft')
|
||||
RETURNING id
|
||||
),
|
||||
draft_version AS (
|
||||
SELECT id FROM inserted_version
|
||||
UNION ALL
|
||||
SELECT id
|
||||
FROM (
|
||||
SELECT id
|
||||
FROM bot_flow_versions
|
||||
WHERE status = 'draft'
|
||||
AND NOT EXISTS (SELECT 1 FROM inserted_version)
|
||||
ORDER BY id ASC
|
||||
LIMIT 1
|
||||
) existing_draft
|
||||
),
|
||||
inserted_root AS (
|
||||
INSERT INTO bot_flow_nodes (
|
||||
version_id,
|
||||
parent_id,
|
||||
node_type,
|
||||
title,
|
||||
message_text,
|
||||
keywords,
|
||||
fallback_message,
|
||||
fallback_attempts,
|
||||
sort_order,
|
||||
position_x,
|
||||
position_y,
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
draft_version.id,
|
||||
NULL,
|
||||
'greeting',
|
||||
'Saudacao inicial',
|
||||
'Ola! Sou o Agente Virtual Sothis. Vou te direcionar para o atendimento correto de RH. Digite o numero da opcao que melhor descreve voce:\n\n1 - Sou colaborador ativo\n2 - Sou ex-colaborador\n3 - Sou candidato a uma vaga',
|
||||
NULL,
|
||||
'Nao consegui identificar seu perfil. Digite 1 para colaborador ativo, 2 para ex-colaborador ou 3 para candidato.',
|
||||
2,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
CURRENT_TIMESTAMP
|
||||
FROM draft_version
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM bot_flow_nodes node
|
||||
WHERE node.version_id = draft_version.id
|
||||
AND node.node_type = 'greeting'
|
||||
)
|
||||
RETURNING id, version_id
|
||||
)
|
||||
UPDATE bot_flow_versions version
|
||||
SET root_node_id = inserted_root.id,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
FROM inserted_root
|
||||
WHERE version.id = inserted_root.version_id
|
||||
AND version.root_node_id IS NULL;
|
||||
@ -0,0 +1,12 @@
|
||||
-- ============================================================
|
||||
-- Migration 020: No terminal de encerramento pelo bot
|
||||
-- Tabelas:
|
||||
-- bot_flow_nodes
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE bot_flow_nodes
|
||||
DROP CONSTRAINT IF EXISTS bot_flow_nodes_node_type_check;
|
||||
|
||||
ALTER TABLE bot_flow_nodes
|
||||
ADD CONSTRAINT bot_flow_nodes_node_type_check
|
||||
CHECK (node_type IN ('greeting', 'question', 'agent', 'close'));
|
||||
41
database/migrations/021_admin_audit_ai_contents.sql
Normal file
41
database/migrations/021_admin_audit_ai_contents.sql
Normal file
@ -0,0 +1,41 @@
|
||||
-- ============================================================
|
||||
-- Migration 021: Auditoria e conteúdos da IA
|
||||
-- Tabelas:
|
||||
-- admin_audit_logs
|
||||
-- ai_knowledge_contents
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS admin_audit_logs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
actor_user_id INTEGER REFERENCES usuarios(id) ON DELETE SET NULL,
|
||||
actor_name VARCHAR(180),
|
||||
action VARCHAR(120) NOT NULL,
|
||||
target_type VARCHAR(80),
|
||||
target_id VARCHAR(120),
|
||||
details TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_created_at
|
||||
ON admin_audit_logs (created_at DESC, id DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ai_knowledge_contents (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(220) NOT NULL,
|
||||
area_id INTEGER REFERENCES areas(id) ON DELETE SET NULL,
|
||||
filename VARCHAR(260),
|
||||
mimetype VARCHAR(160),
|
||||
file_size INTEGER,
|
||||
content_base64 TEXT,
|
||||
status VARCHAR(40) NOT NULL DEFAULT 'available',
|
||||
notes TEXT,
|
||||
created_by_user_id INTEGER REFERENCES usuarios(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ai_knowledge_contents_area
|
||||
ON ai_knowledge_contents (area_id, status);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ai_knowledge_contents_created_at
|
||||
ON ai_knowledge_contents (created_at DESC, id DESC);
|
||||
14
database/migrations/022_whatsapp_template_category.sql
Normal file
14
database/migrations/022_whatsapp_template_category.sql
Normal file
@ -0,0 +1,14 @@
|
||||
-- ============================================================
|
||||
-- Migration 022: Categoria de templates WhatsApp
|
||||
-- Tabela: whatsapp_templates
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE whatsapp_templates
|
||||
ADD COLUMN IF NOT EXISTS category VARCHAR(40) NOT NULL DEFAULT 'UTILITY';
|
||||
|
||||
UPDATE whatsapp_templates
|
||||
SET category = 'UTILITY'
|
||||
WHERE category IS NULL OR category = '';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_whatsapp_templates_category
|
||||
ON whatsapp_templates (category);
|
||||
Loading…
Reference in New Issue
Block a user