freeleaps-service-hub/apps/notification/backend/services/email/email_bounce_service.py

194 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from typing import Dict, Optional
from datetime import datetime
from common.log.module_logger import ModuleLogger
from backend.models.models import EmailBounceDoc, EmailSendStatusDoc
from common.constants.email import BounceType
class EmailBounceService:
def __init__(self):
self.module_logger = ModuleLogger(sender_id="EmailBounceService")
async def process_bounce_event(self, email: str, tenant_id: str, bounce_type: BounceType,
reason: str, message_id: str = None) -> Dict:
"""处理退信事件建立email_id关联"""
try:
# 1. 查找对应的邮件记录
email_status_doc = await EmailSendStatusDoc.find_one(
EmailSendStatusDoc.recipient_email == email,
EmailSendStatusDoc.tenant_id == tenant_id
).sort(-EmailSendStatusDoc.created_at) # 获取最新的邮件记录
email_id = None
template_id = None
if email_status_doc:
email_id = email_status_doc.email_id
template_id = email_status_doc.template_id
await self.module_logger.log_info(
"Found email record for bounce",
properties={
"email": email,
"email_id": email_id,
"tenant_id": tenant_id
}
)
else:
await self.module_logger.log_warning(
"No email record found for bounce",
properties={
"email": email,
"tenant_id": tenant_id
}
)
# 2. 创建退信记录
bounce_doc = EmailBounceDoc(
email=email,
tenant_id=tenant_id,
email_id=email_id, # 建立关联
template_id=template_id,
bounce_type=bounce_type,
reason=reason,
bounced_at=datetime.utcnow(),
original_message_id=message_id,
processed=False
)
await bounce_doc.save()
await self.module_logger.log_info(
"Bounce event processed successfully",
properties={
"email": email,
"email_id": email_id,
"tenant_id": tenant_id,
"bounce_type": bounce_type.value,
"reason": reason
}
)
return {
"email": email,
"email_id": email_id,
"tenant_id": tenant_id,
"bounce_type": bounce_type.value,
"reason": reason,
"processed": True
}
except Exception as e:
await self.module_logger.log_error(
"Failed to process bounce event",
properties={
"email": email,
"tenant_id": tenant_id,
"error": str(e)
}
)
raise
async def get_bounce_info(self, email: str, tenant_id: str) -> Optional[Dict]:
"""获取退信信息"""
try:
bounce_doc = await EmailBounceDoc.find_one(
EmailBounceDoc.email == email,
EmailBounceDoc.tenant_id == tenant_id
).sort(-EmailBounceDoc.created_at)
if not bounce_doc:
return None
return {
"email": bounce_doc.email,
"email_id": bounce_doc.email_id,
"tenant_id": bounce_doc.tenant_id,
"template_id": bounce_doc.template_id,
"bounce_type": bounce_doc.bounce_type.value,
"reason": bounce_doc.reason,
"bounced_at": bounce_doc.bounced_at.isoformat(),
"original_message_id": bounce_doc.original_message_id,
"processed": bounce_doc.processed,
"processed_at": bounce_doc.processed_at.isoformat() if bounce_doc.processed_at else None,
"created_at": bounce_doc.created_at.isoformat()
}
except Exception as e:
await self.module_logger.log_error(
"Failed to get bounce info",
properties={
"email": email,
"tenant_id": tenant_id,
"error": str(e)
}
)
raise
async def mark_bounce_processed(self, email: str, tenant_id: str) -> bool:
"""标记退信为已处理"""
try:
bounce_doc = await EmailBounceDoc.find_one(
EmailBounceDoc.email == email,
EmailBounceDoc.tenant_id == tenant_id
).sort(-EmailBounceDoc.created_at)
if bounce_doc:
bounce_doc.processed = True
bounce_doc.processed_at = datetime.utcnow()
await bounce_doc.save()
await self.module_logger.log_info(
"Bounce marked as processed",
properties={
"email": email,
"tenant_id": tenant_id
}
)
return True
return False
except Exception as e:
await self.module_logger.log_error(
"Failed to mark bounce as processed",
properties={
"email": email,
"tenant_id": tenant_id,
"error": str(e)
}
)
raise
async def is_blacklisted(self, email: str, tenant_id: str) -> bool:
"""检查邮箱是否在黑名单中"""
try:
# 查找该邮箱的退信记录
bounce_doc = await EmailBounceDoc.find_one(
EmailBounceDoc.email == email,
EmailBounceDoc.tenant_id == tenant_id,
EmailBounceDoc.bounce_type == BounceType.HARD_BOUNCE # 只检查硬退信
)
is_blacklisted = bounce_doc is not None
await self.module_logger.log_info(
"Email blacklist check completed",
properties={
"email": email,
"tenant_id": tenant_id,
"is_blacklisted": is_blacklisted
}
)
return is_blacklisted
except Exception as e:
await self.module_logger.log_error(
"Failed to check email blacklist",
properties={
"email": email,
"tenant_id": tenant_id,
"error": str(e)
}
)
return False