freeleaps-service-hub/apps/notification/backend/infra/template_message_handler.py
YuehuCao 2f10275493 refactor(architecture): implement 5-layer separation
for both template message and email senders, their workflow will follow:
api->application->business->service->infra
2025-08-07 12:58:19 +08:00

397 lines
14 KiB
Python

from backend.models.models import MessageTemplateDoc
from common.log.module_logger import ModuleLogger
from datetime import datetime
from typing import List, Optional
from common.constants.region import UserRegion
class TemplateMessageHandler:
def __init__(self):
self.module_logger = ModuleLogger(sender_id="TemplateMessageHandler")
async def verify_tenant_access(self, template_id: str, tenant_id: str, region: int) -> Optional[MessageTemplateDoc]:
"""get template by tenant and template ids with region"""
try:
template = await MessageTemplateDoc.find_one({
"template_id": template_id,
"tenant_id": tenant_id,
"region": region
})
await self.module_logger.log_info(
info="Tenant template retrieved from database",
properties={
"template_id": template_id,
"tenant_id": tenant_id,
"region": region
}
)
return template
except Exception as e:
await self.module_logger.log_error(
error="Failed to get tenant template from database",
properties={
"template_id": template_id,
"tenant_id": tenant_id,
"region": region,
"error": str(e)
}
)
raise
# ==================== global templates ====================
async def create_global_template(self, template: MessageTemplateDoc) -> MessageTemplateDoc:
"""create global template"""
try:
await template.create()
await self.module_logger.log_info(
info="Template created in database",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
}
)
return template
except Exception as e:
await self.module_logger.log_error(
error="Failed to create template in database",
properties={
"template_id": template.template_id,
"error": str(e)
}
)
raise
async def update_global_template(self, template_id: str, data: dict, region: int) -> dict:
"""update global template"""
try:
template = await MessageTemplateDoc.find_one({
"template_id": template_id,
"tenant_id": None,
"region": region
})
if not template:
raise ValueError(f"Template not found for template_id: {template_id}, region: {data.get('region')}")
data["updated_at"] = datetime.utcnow()
# remove fields that are not allowed to be updated
if "id" in data:
del data["id"]
if "template_id" in data:
del data["template_id"]
if "tenant_id" in data:
del data["tenant_id"]
if "region" in data:
del data["region"]
await template.set(data)
await self.module_logger.log_info(
info="Template updated in database",
properties={
"template_id": template_id,
"tenant_id": data.get("tenant_id"),
"region": data.get("region"),
"updated_fields": list(data.keys())
}
)
return {"success": True}
except Exception as e:
await self.module_logger.log_error(
error="Failed to update template in database",
properties={
"template_id": template_id,
"region": data.get("region"),
"error": str(e)
}
)
raise
async def delete_global_template(self, template_id: str) -> dict:
"""delete global template"""
try:
# find all global templates named as template_id
templates = await MessageTemplateDoc.find({
"template_id": template_id,
"tenant_id": None
}).to_list()
if not templates:
raise ValueError("Global template not found")
# delete all found templates
deleted_count = 0
for template in templates:
await template.delete()
deleted_count += 1
await self.module_logger.log_info(
info="Global templates deleted from database",
properties={
"template_id": template_id,
"deleted_count": deleted_count,
"total_found": len(templates)
}
)
return {"success": True, "deleted_count": deleted_count}
except Exception as e:
await self.module_logger.log_error(
error="Failed to delete global templates from database",
properties={
"template_id": template_id,
"error": str(e)
}
)
raise
async def list_global_templates(self, region: int) -> List[MessageTemplateDoc]:
"""list global templates"""
try:
templates = await MessageTemplateDoc.find({
"tenant_id": None,
"region": region
}).to_list()
await self.module_logger.log_info(
info="Global templates retrieved from database",
properties={"region": region, "count": len(templates)}
)
return templates
except Exception as e:
await self.module_logger.log_error(
error="Failed to get global templates from database",
properties={
"region": region,
"error": str(e)
}
)
raise
async def find_global_template(self, template_id: str, region: int) -> Optional[MessageTemplateDoc]:
"""find global template"""
try:
template = await MessageTemplateDoc.find_one({
"template_id": template_id,
"tenant_id": None,
"region": region
})
await self.module_logger.log_info(
info="Global template retrieved from database",
properties={
"template_id": template_id,
"region": region
}
)
return template
except Exception as e:
await self.module_logger.log_error(
error="Failed to get global template from database",
properties={
"template_id": template_id,
"region": region,
"error": str(e)
}
)
raise
# ==================== tenant templates ====================
async def list_tenant_templates(self, tenant_id: str, region: int) -> List[MessageTemplateDoc]:
"""list tenant templates"""
try:
templates = await MessageTemplateDoc.find({
"tenant_id": tenant_id,
"region": region
}).to_list()
await self.module_logger.log_info(
info="Tenant templates retrieved from database",
properties={
"tenant_id": tenant_id,
"region": region,
"count": len(templates)
}
)
return templates
except Exception as e:
await self.module_logger.log_error(
error="Failed to get tenant templates from database",
properties={
"tenant_id": tenant_id,
"region": region,
"error": str(e)
}
)
raise
async def find_tenant_template(self, tenant_id: str, template_id: str, region: int) -> Optional[MessageTemplateDoc]:
"""find tenant template"""
try:
template = await MessageTemplateDoc.find_one({
"template_id": template_id,
"tenant_id": tenant_id,
"region": region
})
await self.module_logger.log_info(
info="Tenant template retrieved from database",
properties={
"template_id": template_id,
"tenant_id": tenant_id,
"region": region
}
)
return template
except Exception as e:
await self.module_logger.log_error(
error="Failed to get tenant template from database",
properties={
"template_id": template_id,
"region": region,
"error": str(e)
}
)
raise
async def create_tenant_template(self, tenant_id: str, template: MessageTemplateDoc) -> MessageTemplateDoc:
"""create tenant template"""
try:
await template.create()
await self.module_logger.log_info(
info="Template created in database",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
}
)
return template
except Exception as e:
await self.module_logger.log_error(
error="Failed to create template in database",
properties={
"template_id": template.template_id,
"error": str(e)
}
)
raise
async def update_tenant_template(self, tenant_id: str, template_id: str, data: dict, region: int) -> dict:
"""update template"""
try:
template = await MessageTemplateDoc.find_one({
"template_id": template_id,
"tenant_id": tenant_id,
"region": region
})
if not template:
raise ValueError(f"Template not found for template_id: {template_id}, tenant_id: {data.get('tenant_id')}, region: {data.get('region')}")
data["updated_at"] = datetime.utcnow()
# remove fields that are not allowed to be updated
if "id" in data:
del data["id"]
if "template_id" in data:
del data["template_id"]
if "tenant_id" in data:
del data["tenant_id"]
if "region" in data:
del data["region"]
await template.set(data)
await self.module_logger.log_info(
info="Template updated in database",
properties={
"template_id": template_id,
"tenant_id": data.get("tenant_id"),
"region": data.get("region"),
"updated_fields": list(data.keys())
}
)
return {"success": True}
except Exception as e:
await self.module_logger.log_error(
error="Failed to update template in database",
properties={
"template_id": template_id,
"tenant_id": data.get("tenant_id"),
"region": data.get("region"),
"error": str(e)
}
)
raise
async def delete_tenant_template(self, tenant_id: str, template_id: str, region: int) -> dict:
"""delete tenant template"""
try:
template = await MessageTemplateDoc.find_one({
"template_id": template_id,
"tenant_id": tenant_id,
"region": region
})
if not template:
raise ValueError("Template not found")
await template.delete()
await self.module_logger.log_info(
info="Template deleted from database",
properties={"template_id": template_id}
)
return {"success": True}
except Exception as e:
await self.module_logger.log_error(
error="Failed to delete template from database",
properties={
"template_id": template_id,
"error": str(e)
}
)
raise
async def render_template(self, template: MessageTemplateDoc, properties: dict) -> dict:
"""render template"""
try:
subject = template.subject.format(**properties)
body = template.body.format(**properties)
await self.module_logger.log_info(
info="Template rendered",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region,
"properties_count": len(properties)
}
)
return {"subject": subject, "body": body}
except KeyError as e:
await self.module_logger.log_error(
error="Missing template parameter",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region,
"missing_parameter": str(e)
}
)
raise ValueError(f"Missing template parameter: {e}")
except Exception as e:
await self.module_logger.log_error(
error="Template rendering error",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region,
"error": str(e)
}
)
raise ValueError(f"Template rendering error: {str(e)}")