feat: implementacao inicial do sistema LeadRadar

This commit is contained in:
2026-08-26 17:14:35 -03:00
commit eb64f9a0f6
43 changed files with 2888 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
from app.models.user import User
from app.models.lead import Lead
from app.models.lead_interacao import LeadInteracao
__all__ = ['User', 'Lead', 'LeadInteracao']
+60
View File
@@ -0,0 +1,60 @@
import uuid
from datetime import datetime, timezone
from app.extensions import db
class Lead(db.Model):
__tablename__ = 'leads'
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
nome_empresa = db.Column(db.String(255), index=True, nullable=False)
ramo_atividade = db.Column(db.String(100), index=True, nullable=False)
cep_busca = db.Column(db.String(10), index=True, nullable=False)
logradouro = db.Column(db.String(255), nullable=True)
bairro = db.Column(db.String(100), nullable=True)
cidade = db.Column(db.String(100), index=True, nullable=True)
uf = db.Column(db.String(2), nullable=True)
telefone = db.Column(db.String(50), nullable=True)
telefone_sanitizado = db.Column(db.String(30), index=True, nullable=True)
whatsapp_valido = db.Column(db.Boolean, nullable=True, default=True)
website = db.Column(db.String(255), nullable=True)
google_rating = db.Column(db.Float, default=0.0)
total_avaliacoes = db.Column(db.Integer, default=0)
google_maps_url = db.Column(db.Text, nullable=True)
status_funil = db.Column(db.String(30), default='novo', nullable=False, index=True)
tags = db.Column(db.JSON, default=list)
notas = db.Column(db.Text, nullable=True)
usuario_responsavel_id = db.Column(db.String(36), db.ForeignKey('users.id', ondelete='SET NULL'), nullable=True)
criado_em = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))
atualizado_em = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
# Relationships
interacoes = db.relationship('LeadInteracao', backref='lead', cascade='all, delete-orphan', lazy='dynamic')
def to_dict(self):
return {
'id': self.id,
'nome_empresa': self.nome_empresa,
'ramo_atividade': self.ramo_atividade,
'cep_busca': self.cep_busca,
'logradouro': self.logradouro,
'bairro': self.bairro,
'cidade': self.cidade,
'uf': self.uf,
'telefone': self.telefone,
'telefone_sanitizado': self.telefone_sanitizado,
'whatsapp_valido': self.whatsapp_valido,
'website': self.website,
'google_rating': self.google_rating,
'total_avaliacoes': self.total_avaliacoes,
'google_maps_url': self.google_maps_url,
'status_funil': self.status_funil,
'tags': self.tags or [],
'notas': self.notas or '',
'usuario_responsavel_id': self.usuario_responsavel_id,
'usuario_responsavel_nome': self.usuario_responsavel.nome if self.usuario_responsavel else None,
'criado_em': self.criado_em.isoformat() if self.criado_em else None,
'atualizado_em': self.atualizado_em.isoformat() if self.atualizado_em else None
}
def __repr__(self):
return f'<Lead {self.nome_empresa} ({self.status_funil})>'
+29
View File
@@ -0,0 +1,29 @@
import uuid
from datetime import datetime, timezone
from app.extensions import db
class LeadInteracao(db.Model):
__tablename__ = 'lead_interacoes'
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
lead_id = db.Column(db.String(36), db.ForeignKey('leads.id', ondelete='CASCADE'), nullable=False, index=True)
usuario_id = db.Column(db.String(36), db.ForeignKey('users.id', ondelete='SET NULL'), nullable=True, index=True)
tipo = db.Column(db.String(50), nullable=False, index=True)
descricao = db.Column(db.Text, nullable=False)
metadados = db.Column(db.JSON, default=dict)
timestamp = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc), index=True)
def to_dict(self):
return {
'id': self.id,
'lead_id': self.lead_id,
'usuario_id': self.usuario_id,
'usuario_nome': self.usuario.nome if self.usuario else 'Sistema',
'tipo': self.tipo,
'descricao': self.descricao,
'metadados': self.metadados or {},
'timestamp': self.timestamp.isoformat() if self.timestamp else None
}
def __repr__(self):
return f'<LeadInteracao {self.tipo} lead={self.lead_id}>'
+45
View File
@@ -0,0 +1,45 @@
import uuid
from datetime import datetime, timezone
from flask_sqlalchemy import SQLAlchemy
# We will import db from extensions, but let's define extensions properly
from app.extensions import db, bcrypt
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
nome = db.Column(db.String(120), nullable=False)
email = db.Column(db.String(180), unique=True, index=True, nullable=False)
password_hash = db.Column(db.String(255), nullable=False)
role = db.Column(db.String(20), nullable=False, default='user') # 'admin' or 'user'
ativo = db.Column(db.Boolean, default=True, nullable=False)
criado_em = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))
atualizado_em = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
# Relationships
leads_responsaveis = db.relationship('Lead', backref='usuario_responsavel', lazy='dynamic')
interacoes = db.relationship('LeadInteracao', backref='usuario', lazy='dynamic')
def set_password(self, password: str):
self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8')
def check_password(self, password: str) -> bool:
return bcrypt.check_password_hash(self.password_hash, password)
def is_admin(self) -> bool:
return self.role == 'admin'
def to_dict(self):
return {
'id': self.id,
'nome': self.nome,
'email': self.email,
'role': self.role,
'ativo': self.ativo,
'criado_em': self.criado_em.isoformat() if self.criado_em else None,
'atualizado_em': self.atualizado_em.isoformat() if self.atualizado_em else None
}
def __repr__(self):
return f'<User {self.email} ({self.role})>'