feat(phase5): integracion auth hibrida, email dinamico y moodle 4.1 backend con tolerancia a fallos
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
from app import db
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
class MoodleSyncTask(db.Model):
|
||||
"""
|
||||
Cola persistente de eventos y tareas de sincronización asíncrona hacia Moodle 4.1.
|
||||
Garantiza tolerancia a fallos ante caídas o saturación del servidor Moodle,
|
||||
incorporando reintentos exponenciales y Dead Letter Queue (DLQ).
|
||||
"""
|
||||
__tablename__ = 'moodle_sync_tasks'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
action = db.Column(db.String(50), nullable=False, index=True) # CREATE_USER, UPDATE_USER, ENROL_USER, UNENROL_USER, ASSIGN_ROLE
|
||||
entity_type = db.Column(db.String(50), nullable=False) # user, commission, enrollment
|
||||
entity_id = db.Column(db.String(100), nullable=True)
|
||||
_payload = db.Column('payload', db.Text, nullable=False) # JSON serializado en Text
|
||||
status = db.Column(db.String(20), default='PENDING', index=True) # PENDING, PROCESSING, RETRYING, COMPLETED, FAILED
|
||||
attempts = db.Column(db.Integer, default=0)
|
||||
max_attempts = db.Column(db.Integer, default=5)
|
||||
error_message = db.Column(db.Text, nullable=True)
|
||||
next_retry_at = db.Column(db.DateTime, default=datetime.utcnow, index=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if 'payload' in kwargs:
|
||||
raw_payload = kwargs.pop('payload')
|
||||
if isinstance(raw_payload, (dict, list)):
|
||||
kwargs['_payload'] = json.dumps(raw_payload)
|
||||
else:
|
||||
kwargs['_payload'] = str(raw_payload or '{}')
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@property
|
||||
def payload(self):
|
||||
try:
|
||||
return json.loads(self._payload) if self._payload else {}
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
@payload.setter
|
||||
def payload(self, val):
|
||||
if isinstance(val, (dict, list)):
|
||||
self._payload = json.dumps(val)
|
||||
else:
|
||||
self._payload = str(val or '{}')
|
||||
|
||||
def get_payload_dict(self):
|
||||
return self.payload
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'action': self.action,
|
||||
'entity_type': self.entity_type,
|
||||
'entity_id': self.entity_id,
|
||||
'payload': self.payload,
|
||||
'status': self.status,
|
||||
'attempts': self.attempts,
|
||||
'max_attempts': self.max_attempts,
|
||||
'error_message': self.error_message,
|
||||
'next_retry_at': self.next_retry_at.isoformat() if self.next_retry_at else None,
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||||
'updated_at': self.updated_at.isoformat() if self.updated_at else None
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return f'<MoodleSyncTask #{self.id} {self.action} [{self.status}]>'
|
||||
Reference in New Issue
Block a user