Implantação da geração de mensagem de forma livre e mudança na engine de mensagem

This commit is contained in:
2026-08-24 17:32:16 -03:00
parent 94bbe7556f
commit 88f19dd8f5
77 changed files with 4650 additions and 25 deletions

View File

@@ -0,0 +1,85 @@
# app/models/mensagem_template.rb
#
# Corpo de um evento em UM canal, montado com blocos no editor.
#
# `blocos` vem de um campo hidden preenchido por JavaScript — ou seja, de fora.
# Por isso #normalizar_blocos DESCARTA o que não está no catálogo: tipo
# desconhecido, campo que aquele tipo não tem, e qualquer coisa que não seja
# Hash. Sem isso, um JSON adulterado no form viraria conteúdo renderizado sem
# passar por validação nenhuma.
class MensagemTemplate < ApplicationRecord
CANAIS = %w[whatsapp email].freeze
# Teto de segurança: um template com milhares de blocos só serviria para
# travar o render do e-mail.
MAX_BLOCOS = 40
MAX_LINHAS_TABELA = 20
belongs_to :evento_notificacao
validates :canal, inclusion: { in: CANAIS }
validates :canal, uniqueness: { scope: :evento_notificacao_id }
validate :assunto_no_email
before_validation :normalizar_blocos
scope :ativos, -> { where(ativo: true) }
scope :do_canal, ->(canal) { where(canal: canal.to_s) }
def email? = canal == 'email'
def whatsapp? = canal == 'whatsapp'
# Um template só entra em uso quando tem conteúdo — template ativo e vazio
# mandaria mensagem em branco, o que é pior do que cair no texto padrão.
def utilizavel?
ativo? && blocos.present?
end
def renderizador(dados = {})
Notificacao::Renderizador.new(blocos, dados)
end
def preview(dados = nil)
dados ||= Notificacao::Variaveis.amostra(evento_notificacao&.gatilho)
r = renderizador(dados)
email? ? r.html : r.texto
end
private
def normalizar_blocos
lista = blocos.is_a?(Array) ? blocos : []
self.blocos = lista.filter_map { |bruto| limpar(bruto) }.first(MAX_BLOCOS)
end
def limpar(bruto)
return nil unless bruto.is_a?(Hash)
tipo = bruto['tipo'] || bruto[:tipo]
return nil unless Notificacao::Blocos.valido?(tipo)
limpo = { 'tipo' => tipo.to_s }
Notificacao::Blocos.campos(tipo).each do |campo, especie|
valor = bruto[campo] || bruto[campo.to_sym]
limpo[campo] = especie == :lista ? limpar_linhas(valor) : valor.to_s
end
limpo
end
def limpar_linhas(valor)
Array(valor).filter_map do |linha|
next unless linha.is_a?(Hash)
rotulo = (linha['rotulo'] || linha[:rotulo]).to_s
conteudo = (linha['valor'] || linha[:valor]).to_s
next if rotulo.blank? && conteudo.blank?
{ 'rotulo' => rotulo, 'valor' => conteudo }
end.first(MAX_LINHAS_TABELA)
end
def assunto_no_email
return unless email? && ativo? && blocos.present? && assunto.to_s.strip.blank?
errors.add(:assunto, 'é obrigatório no e-mail')
end
end