Add notification service

This commit is contained in:
Urtzi Alfaro
2025-07-21 22:44:11 +02:00
parent d029630c8e
commit d9affc950a
11 changed files with 3680 additions and 57 deletions

View File

@@ -0,0 +1,184 @@
# ================================================================
# services/notification/app/models/notifications.py
# ================================================================
"""
Notification models for the notification service
"""
from sqlalchemy import Column, String, Text, Boolean, DateTime, JSON, Integer, Enum
from sqlalchemy.dialects.postgresql import UUID
from datetime import datetime
import uuid
import enum
from shared.database.base import Base
class NotificationType(enum.Enum):
"""Notification types supported by the service"""
EMAIL = "email"
WHATSAPP = "whatsapp"
PUSH = "push"
SMS = "sms"
class NotificationStatus(enum.Enum):
"""Notification delivery status"""
PENDING = "pending"
SENT = "sent"
DELIVERED = "delivered"
FAILED = "failed"
CANCELLED = "cancelled"
class NotificationPriority(enum.Enum):
"""Notification priority levels"""
LOW = "low"
NORMAL = "normal"
HIGH = "high"
URGENT = "urgent"
class Notification(Base):
"""Main notification record"""
__tablename__ = "notifications"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
sender_id = Column(UUID(as_uuid=True), nullable=False)
recipient_id = Column(UUID(as_uuid=True), nullable=True) # Null for broadcast
# Notification details
type = Column(Enum(NotificationType), nullable=False)
status = Column(Enum(NotificationStatus), default=NotificationStatus.PENDING, index=True)
priority = Column(Enum(NotificationPriority), default=NotificationPriority.NORMAL)
# Content
subject = Column(String(255), nullable=True)
message = Column(Text, nullable=False)
html_content = Column(Text, nullable=True)
template_id = Column(String(100), nullable=True)
template_data = Column(JSON, nullable=True)
# Delivery details
recipient_email = Column(String(255), nullable=True)
recipient_phone = Column(String(20), nullable=True)
delivery_channel = Column(String(50), nullable=True)
# Scheduling
scheduled_at = Column(DateTime, nullable=True)
sent_at = Column(DateTime, nullable=True)
delivered_at = Column(DateTime, nullable=True)
# Metadata
log_metadata = Column(JSON, nullable=True)
error_message = Column(Text, nullable=True)
retry_count = Column(Integer, default=0)
max_retries = Column(Integer, default=3)
# Tracking
broadcast = Column(Boolean, default=False)
read = Column(Boolean, default=False)
read_at = Column(DateTime, nullable=True)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, index=True)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class NotificationTemplate(Base):
"""Email and notification templates"""
__tablename__ = "notification_templates"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=True, index=True) # Null for system templates
# Template identification
template_key = Column(String(100), nullable=False, unique=True)
name = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
category = Column(String(50), nullable=False) # alert, marketing, transactional
# Template content
type = Column(Enum(NotificationType), nullable=False)
subject_template = Column(String(255), nullable=True)
body_template = Column(Text, nullable=False)
html_template = Column(Text, nullable=True)
# Configuration
language = Column(String(2), default="es")
is_active = Column(Boolean, default=True)
is_system = Column(Boolean, default=False) # System templates can't be deleted
# Metadata
default_priority = Column(Enum(NotificationPriority), default=NotificationPriority.NORMAL)
required_variables = Column(JSON, nullable=True) # List of required template variables
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class NotificationPreference(Base):
"""User notification preferences"""
__tablename__ = "notification_preferences"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, unique=True, index=True)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
# Email preferences
email_enabled = Column(Boolean, default=True)
email_alerts = Column(Boolean, default=True)
email_marketing = Column(Boolean, default=False)
email_reports = Column(Boolean, default=True)
# WhatsApp preferences
whatsapp_enabled = Column(Boolean, default=False)
whatsapp_alerts = Column(Boolean, default=False)
whatsapp_reports = Column(Boolean, default=False)
# Push notification preferences
push_enabled = Column(Boolean, default=True)
push_alerts = Column(Boolean, default=True)
push_reports = Column(Boolean, default=False)
# Timing preferences
quiet_hours_start = Column(String(5), default="22:00") # HH:MM format
quiet_hours_end = Column(String(5), default="08:00")
timezone = Column(String(50), default="Europe/Madrid")
# Frequency preferences
digest_frequency = Column(String(20), default="daily") # none, daily, weekly
max_emails_per_day = Column(Integer, default=10)
# Language preference
language = Column(String(2), default="es")
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class NotificationLog(Base):
"""Detailed logging for notification delivery attempts"""
__tablename__ = "notification_logs"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
notification_id = Column(UUID(as_uuid=True), nullable=False, index=True)
# Attempt details
attempt_number = Column(Integer, nullable=False)
status = Column(Enum(NotificationStatus), nullable=False)
# Provider details
provider = Column(String(50), nullable=True) # e.g., "gmail", "twilio"
provider_message_id = Column(String(255), nullable=True)
provider_response = Column(JSON, nullable=True)
# Timing
attempted_at = Column(DateTime, default=datetime.utcnow)
response_time_ms = Column(Integer, nullable=True)
# Error details
error_code = Column(String(50), nullable=True)
error_message = Column(Text, nullable=True)
# Additional metadata
log_metadata = Column(JSON, nullable=True)

View File

@@ -0,0 +1,82 @@
# ================================================================
# services/notification/app/models/templates.py
# ================================================================
"""
Template-specific models for email and WhatsApp templates
"""
from sqlalchemy import Column, String, Text, Boolean, DateTime, JSON, Integer
from sqlalchemy.dialects.postgresql import UUID
from datetime import datetime
import uuid
from shared.database.base import Base
class EmailTemplate(Base):
"""Email-specific templates with HTML support"""
__tablename__ = "email_templates"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=True, index=True)
# Template identification
template_key = Column(String(100), nullable=False, unique=True)
name = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
# Email-specific content
subject = Column(String(255), nullable=False)
html_body = Column(Text, nullable=False)
text_body = Column(Text, nullable=True) # Plain text fallback
# Email settings
from_email = Column(String(255), nullable=True)
from_name = Column(String(255), nullable=True)
reply_to = Column(String(255), nullable=True)
# Template variables
variables = Column(JSON, nullable=True) # Expected variables and their types
sample_data = Column(JSON, nullable=True) # Sample data for preview
# Configuration
language = Column(String(2), default="es")
is_active = Column(Boolean, default=True)
is_system = Column(Boolean, default=False)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class WhatsAppTemplate(Base):
"""WhatsApp-specific templates"""
__tablename__ = "whatsapp_templates"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=True, index=True)
# Template identification
template_key = Column(String(100), nullable=False, unique=True)
name = Column(String(255), nullable=False)
# WhatsApp template details
whatsapp_template_name = Column(String(255), nullable=False) # Template name in WhatsApp Business API
whatsapp_template_id = Column(String(255), nullable=True)
language_code = Column(String(10), default="es")
# Template content
header_text = Column(String(60), nullable=True) # WhatsApp header limit
body_text = Column(Text, nullable=False)
footer_text = Column(String(60), nullable=True) # WhatsApp footer limit
# Template parameters
parameter_count = Column(Integer, default=0)
parameters = Column(JSON, nullable=True) # Parameter definitions
# Status
approval_status = Column(String(20), default="pending") # pending, approved, rejected
is_active = Column(Boolean, default=True)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)