diff --git a/apps/notification/backend/business/template_message_manager.py b/apps/notification/backend/business/template_message_manager.py index 017de66..d002b24 100644 --- a/apps/notification/backend/business/template_message_manager.py +++ b/apps/notification/backend/business/template_message_manager.py @@ -44,10 +44,9 @@ class TemplateMessageManager: ) result = await self.template_message_service.create_global_template(template) - action = "skipped" if hasattr(result, "_is_existing") and result._is_existing else "created" await self.module_logger.log_info( - info=f"Global template {action}", - properties={"template_id": template_id, "region": region, "action": action} + info="Global template created", + properties={"template_id": template_id, "region": region} ) return result @@ -156,15 +155,12 @@ class TemplateMessageManager: ) result = await self.template_message_service.create_tenant_template(tenant_id, template) - - action = "skipped" if hasattr(result, "_is_existing") and result._is_existing else "created" await self.module_logger.log_info( - info=f"Tenant template {action}", + info="Tenant template created", properties={ "tenant_id": tenant_id, "template_id": template_id, - "region": region, - "action": action + "region": region } ) diff --git a/apps/notification/backend/infra/template_message_handler.py b/apps/notification/backend/infra/template_message_handler.py index 44fa4d3..a951374 100644 --- a/apps/notification/backend/infra/template_message_handler.py +++ b/apps/notification/backend/infra/template_message_handler.py @@ -9,6 +9,15 @@ from common.constants.region import UserRegion class TemplateMessageHandler: def __init__(self): self.module_logger = ModuleLogger(sender_id="TemplateMessageHandler") + + @staticmethod + def _normalize_placeholders(text: str) -> str: + """Convert Handlebars-like placeholders {{name}} into Python str.format style {name}. + Does not touch CSS double braces like `.class {{ ... }}` because those won't match \w+. + """ + if not isinstance(text, str): + return text + return re.sub(r"\{\{(\w+)\}\}", r"{\1}", text) 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: @@ -52,21 +61,16 @@ class TemplateMessageHandler: }) if existing_template: - # if template already exists, skip creation await self.module_logger.log_info( - info="Global template already exists, skipping creation", + info="Global template already exists", properties={ "template_id": template.template_id, "tenant_id": template.tenant_id, "region": template.region } ) - - # mark as existing template - existing_template._is_existing = True - return existing_template + raise ValueError("Global template already exists") else: - # if template does not exist, create new template await template.create() await self.module_logger.log_info( info="Template created in database", @@ -76,8 +80,6 @@ class TemplateMessageHandler: "region": template.region } ) - # mark as new template - template._is_existing = False return template except Exception as e: @@ -294,21 +296,16 @@ class TemplateMessageHandler: }) if existing_template: - # if template already exists, skip creation await self.module_logger.log_info( - info="Tenant template already exists, skipping creation", + info="Tenant Template already exists", properties={ "template_id": template.template_id, "tenant_id": template.tenant_id, "region": template.region } ) - - # mark as existing template - existing_template._is_existing = True - return existing_template + raise ValueError("Template already exists") else: - # if template does not exist, create new template await template.create() await self.module_logger.log_info( info="Template created in database", @@ -318,8 +315,6 @@ class TemplateMessageHandler: "region": template.region } ) - # mark as new template - template._is_existing = False return template except Exception as e: @@ -412,8 +407,12 @@ class TemplateMessageHandler: async def validate_template_parameters(self, template: MessageTemplateDoc, properties: dict) -> list: """validate template parameters""" try: - subject_placeholders = re.findall(r'\{(\w+)\}', template.subject) - body_placeholders = re.findall(r'\{(\w+)\}', template.body) + # Normalize double-curly placeholders to single-curly for extraction + normalized_subject = self._normalize_placeholders(template.subject) + normalized_body = self._normalize_placeholders(template.body) + + subject_placeholders = re.findall(r'\{(\w+)\}', normalized_subject) + body_placeholders = re.findall(r'\{(\w+)\}', normalized_body) all_placeholders = list(set(subject_placeholders + body_placeholders)) missing_params = set(all_placeholders) - set(properties.keys()) @@ -461,10 +460,23 @@ class TemplateMessageHandler: async def render_template(self, template: MessageTemplateDoc, properties: dict) -> dict: """render template""" try: - required_params = await self.validate_template_parameters(template, properties) + # Build normalized copies for rendering + normalized_subject = self._normalize_placeholders(template.subject) + normalized_body = self._normalize_placeholders(template.body) - subject = template.subject.format(**properties) - body = template.body.format(**properties) + # Validate using normalized content + temp_for_validation = MessageTemplateDoc( + template_id=template.template_id, + tenant_id=template.tenant_id, + region=template.region, + subject=normalized_subject, + body=normalized_body, + is_active=template.is_active, + ) + required_params = await self.validate_template_parameters(temp_for_validation, properties) + + subject = normalized_subject.format(**properties) + body = normalized_body.format(**properties) await self.module_logger.log_info( info="Template rendered", diff --git a/apps/notification/backend/models/models.py b/apps/notification/backend/models/models.py index 49c3cf2..542aa64 100644 --- a/apps/notification/backend/models/models.py +++ b/apps/notification/backend/models/models.py @@ -12,7 +12,6 @@ class MessageTemplateDoc(Document): subject: str body: str is_active: bool = True - is_existing: bool = False created_at: datetime = datetime.utcnow() updated_at: Optional[datetime] = None @@ -52,7 +51,7 @@ class EmailSendStatusDoc(Document): updated_at: Optional[datetime] = None class Settings: - name = "email_sender_status_doc" + name = "email_send_status_doc" indexes = [ "email_id", "tenant_id" diff --git a/apps/notification/backend/services/template_message_service.py b/apps/notification/backend/services/template_message_service.py index b3b5053..ecd1bd2 100644 --- a/apps/notification/backend/services/template_message_service.py +++ b/apps/notification/backend/services/template_message_service.py @@ -183,7 +183,7 @@ class TemplateMessageService: continue # copy template to tenant, use unique template_id - tenant_template_id = f"{global_template.template_id}_tenant_{tenant_id}" + tenant_template_id = f"{tenant_id}_{global_template.template_id}" new_template = MessageTemplateDoc( template_id=tenant_template_id, tenant_id=tenant_id, diff --git a/apps/notification/create_global_templates.py b/apps/notification/create_global_templates.py index 90b2547..c71e73b 100644 --- a/apps/notification/create_global_templates.py +++ b/apps/notification/create_global_templates.py @@ -33,7 +33,6 @@ class GlobalTemplateCreator: # generate admin token admin_payload = { - "id": "test_admin_user", "role": 8, # ADMINISTRATOR = 8 "tenant_id": None, "exp": datetime.now(timezone.utc) + timedelta(hours=1) @@ -42,9 +41,8 @@ class GlobalTemplateCreator: # generate tenant token tenant_payload = { - "id": "test_tenant_user", "role": 2, # BUSINESS = 2 - "tenant_id": "test_tenant_user", + "tenant_id": "magicleaps", "exp": datetime.now(timezone.utc) + timedelta(hours=1) } tenant_token = jwt.encode(tenant_payload, secret_key, algorithm=algorithm) @@ -109,57 +107,57 @@ class GlobalTemplateCreator: template_data_cn = { "template_id": "assessment_result_notification", "region": 1, - "subject": "笔试结果 - {candidate_name}", + "subject": "笔试结果 - {{candidate_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

尊敬的 {candidate_name},

+

尊敬的 {{candidate_name}},

感谢您参加我们的笔试考核。以下是您的评估结果:

📊 评估详情

🔍 执行结果

⚠️ 执行错误信息

-

{execution_errors}

+

{{execution_errors}}

📈 详细结果

-

{detailed_results}

+

{{detailed_results}}

🎯 评估结论

-

{assessment_conclusion}

+

{{assessment_conclusion}}

如有任何疑问,请联系我们的技术团队:

-

邮箱:{tech_support_email}

-

电话:{tech_support_phone}

+

邮箱:{{tech_support_email}}

+

电话:{{tech_support_phone}}

-

祝好!
{company_name} 技术团队

+

祝好!
{{company_name}} 技术团队

""" } @@ -168,57 +166,57 @@ class GlobalTemplateCreator: template_data_en = { "template_id": "assessment_result_notification", "region": 0, - "subject": "Assessment Result - {candidate_name}", + "subject": "Assessment Result - {{candidate_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

Dear {candidate_name},

+

Dear {{candidate_name}},

Thank you for participating in our assessment. Here are your evaluation results:

📊 Assessment Details

🔍 Execution Results

⚠️ Execution Errors

-

{execution_errors}

+

{{execution_errors}}

📈 Detailed Results

-

{detailed_results}

+

{{detailed_results}}

🎯 Assessment Conclusion

-

{assessment_conclusion}

+

{{assessment_conclusion}}

If you have any questions, please contact our technical team:

-

Email: {tech_support_email}

-

Phone: {tech_support_phone}

+

Email: {{tech_support_email}}

+

Phone: {{tech_support_phone}}

-

Best regards,
{company_name} Technical Team

+

Best regards,
{{company_name}} Technical Team

""" } @@ -236,13 +234,13 @@ class GlobalTemplateCreator: template_data_cn = { "template_id": "deadline_reminder", "region": 1, - "subject": "截止期限提醒 - {task_name}", + "subject": "截止期限提醒 - {{task_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

尊敬的 {recipient_name},

+

尊敬的 {{recipient_name}},

⚠️ 截止期限提醒

@@ -252,42 +250,42 @@ class GlobalTemplateCreator:

📋 任务详情

    -
  • 任务名称:{task_name}
  • -
  • 任务描述:{task_description}
  • -
  • 截止日期:{deadline_date}
  • -
  • 剩余时间:{remaining_time}
  • +
  • 任务名称:{{task_name}}
  • +
  • 任务描述:{{task_description}}
  • +
  • 截止日期:{{deadline_date}}
  • +
  • 剩余时间:{{remaining_time}}

⏰ 时间信息

    -
  • 当前时间:{current_time}
  • -
  • 截止时间:{deadline_time}
  • -
  • 剩余天数:{days_remaining} 天
  • +
  • 当前时间:{{current_time}}
  • +
  • 截止时间:{{deadline_time}}
  • +
  • 剩余天数:{{days_remaining}} 天

📝 任务要求

-

{task_requirements}

+

{{task_requirements}}

🔗 相关链接

请务必在截止日期前完成相关任务。如有任何问题,请联系:

-

邮箱:{contact_email}

+

邮箱:{{contact_email}}

-

谢谢!
{company_name} 团队

+

谢谢!
{{company_name}} 团队

""" } @@ -296,13 +294,13 @@ class GlobalTemplateCreator: template_data_en = { "template_id": "deadline_reminder", "region": 0, - "subject": "Deadline Reminder - {task_name}", + "subject": "Deadline Reminder - {{task_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

Dear {recipient_name},

+

Dear {{recipient_name}},

⚠️ Deadline Reminder

@@ -312,42 +310,42 @@ class GlobalTemplateCreator:

📋 Task Details

    -
  • Task Name: {task_name}
  • -
  • Task Description: {task_description}
  • -
  • Deadline Date: {deadline_date}
  • -
  • Time Remaining: {remaining_time}
  • +
  • Task Name: {{task_name}}
  • +
  • Task Description: {{task_description}}
  • +
  • Deadline Date: {{deadline_date}}
  • +
  • Time

⏰ Time Information

    -
  • Current Time: {current_time}
  • -
  • Deadline Time: {deadline_time}
  • -
  • Days Remaining: {days_remaining} days
  • +
  • Current Time: {{current_time}}
  • +
  • Deadline Time: {{deadline_time}}
  • +
  • Days Remaining: {{days_remaining}} days

📝 Task Requirements

-

{task_requirements}

+

{{task_requirements}}

🔗 Related Links

Please ensure to complete the related task before the deadline. If you have any questions, please contact:

-

Email: {contact_email}

+

Email: {{contact_email}}

-

Thank you!
{company_name} Team

+

Thank you!
{{company_name}} Team

""" } @@ -365,59 +363,59 @@ class GlobalTemplateCreator: template_data_cn = { "template_id": "interview_status_update", "region": 1, - "subject": "面试状态更新 - {candidate_name}", + "subject": "面试状态更新 - {{candidate_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

尊敬的 {candidate_name},

+

尊敬的 {{candidate_name}},

您的面试状态已更新:

👤 候选人信息

    -
  • 姓名:{candidate_name}
  • -
  • 应聘职位:{position_name}
  • -
  • 申请编号:{application_id}
  • +
  • 姓名:{{candidate_name}}
  • +
  • 应聘职位:{{position_name}}
  • +
  • 申请编号:{{application_id}}

📊 状态更新

    -
  • 当前状态:{current_status}
  • -
  • 更新日期:{update_date}
  • -
  • 更新时间:{update_time}
  • +
  • 当前状态:{{current_status}}
  • +
  • 更新日期:{{update_date}}
  • +
  • 更新时间:{{update_time}}

📋 状态详情

-

{status_details}

+

{{status_details}}

📅 下一步安排

-

{next_steps}

+

{{next_steps}}

⏰ 重要时间

    -
  • 面试时间:{interview_time}
  • -
  • 面试地点:{interview_location}
  • -
  • 面试官:{interviewer_name}
  • +
  • 面试时间:{{interview_time}}
  • +
  • 面试地点:{{interview_location}}
  • +
  • 面试官:{{interviewer_name}}

📞 联系方式

    -
  • 面试官邮箱:{interviewer_email}
  • -
  • 面试官电话:{interviewer_phone}
  • -
  • 公司地址:{company_address}
  • +
  • 面试官邮箱:{{interviewer_email}}
  • +
  • 面试官电话:{{interviewer_phone}}
  • +
  • 公司地址:{{company_address}}
@@ -426,7 +424,7 @@ class GlobalTemplateCreator:
-

祝面试顺利!
{company_name} 人力资源部

+

祝面试顺利!
{{company_name}} 人力资源部

""" } @@ -435,59 +433,59 @@ class GlobalTemplateCreator: template_data_en = { "template_id": "interview_status_update", "region": 0, - "subject": "Interview Status Update - {candidate_name}", + "subject": "Interview Status Update - {{candidate_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

Dear {candidate_name},

+

Dear {{candidate_name}},

Your interview status has been updated:

👤 Candidate Information

    -
  • Name: {candidate_name}
  • -
  • Position Applied: {position_name}
  • -
  • Application ID: {application_id}
  • +
  • Name: {{candidate_name}}
  • +
  • Position Applied: {{position_name}}
  • +
  • Application ID: {{application_id}}

📊 Status Update

    -
  • Current Status: {current_status}
  • -
  • Update Date: {update_date}
  • -
  • Update Time: {update_time}
  • +
  • Current Status: {{current_status}}
  • +
  • Update Date: {{update_date}}
  • +
  • Update Time: {{update_time}}

📋 Status Details

-

{status_details}

+

{{status_details}}

📅 Next Steps

-

{next_steps}

+

{{next_steps}}

⏰ Important Times

    -
  • Interview Time: {interview_time}
  • -
  • Interview Location: {interview_location}
  • -
  • Interviewer: {interviewer_name}
  • +
  • Interview Time: {{interview_time}}
  • +
  • Interview Location: {{interview_location}}
  • +
  • Interviewer: {{interviewer_name}}

📞 Contact Information

    -
  • Interviewer Email: {interviewer_email}
  • -
  • Interviewer Phone: {interviewer_phone}
  • -
  • Company Address: {company_address}
  • +
  • Interviewer Email: {{interviewer_email}}
  • +
  • Interviewer Phone: {{interviewer_phone}}
  • +
  • Company Address: {{company_address}}
@@ -496,7 +494,7 @@ class GlobalTemplateCreator:
-

Good luck with your interview!
{company_name} Human Resources Department

+

Good luck with your interview!
{{company_name}} Human Resources Department

""" } @@ -514,16 +512,16 @@ class GlobalTemplateCreator: template_data_cn = { "template_id": "welcome_email", "region": 1, - "subject": "欢迎加入 {company_name} - {new_employee_name}", + "subject": "欢迎加入 {{company_name}} - {{new_employee_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

亲爱的 {new_employee_name},

+

亲爱的 {{new_employee_name}},

-

🎉 欢迎加入 {company_name}!

+

🎉 欢迎加入 {{company_name}}!

我们很高兴地通知您,您已成功加入我们的团队。以下是您的入职信息:

@@ -531,58 +529,58 @@ class GlobalTemplateCreator:

👤 员工信息

    -
  • 姓名:{new_employee_name}
  • -
  • 员工编号:{employee_id}
  • -
  • 部门:{department}
  • -
  • 职位:{position}
  • -
  • 入职日期:{start_date}
  • +
  • 姓名:{{new_employee_name}}
  • +
  • 员工编号:{{employee_id}}
  • +
  • 部门:{{department}}
  • +
  • 职位:{{position}}
  • +
  • 入职日期:{{start_date}}

🏢 公司信息

    -
  • 公司名称:{company_name}
  • -
  • 公司地址:{company_address}
  • -
  • 联系电话:{company_phone}
  • +
  • 公司名称:{{company_name}}
  • +
  • 公司地址:{{company_address}}
  • +
  • 联系电话:{{company_phone}}

📋 入职安排

-

{onboarding_schedule}

+

{{onboarding_schedule}}

🔑 系统访问信息

    -
  • 邮箱:{email_address}
  • -
  • 初始密码:{initial_password}
  • -
  • 系统登录地址:{system_login_url}
  • +
  • 邮箱:{{email_address}}
  • +
  • 初始密码:{{initial_password}}
  • +
  • 系统登录地址:{{system_login_url}}

📚 重要资源

👥 联系人

    -
  • 直属经理:{manager_name} ({manager_email})
  • -
  • HR联系人:{hr_contact_name} ({hr_contact_email})
  • -
  • IT支持:{it_support_email}
  • +
  • 直属经理:{{manager_name}} ({{manager_email}})
  • +
  • HR联系人:{{hr_contact_name}} ({{hr_contact_email}})
  • +
  • IT支持:{{it_support_email}}

🎯 第一周安排

-

{first_week_schedule}

+

{{first_week_schedule}}

@@ -590,7 +588,7 @@ class GlobalTemplateCreator:
-

再次欢迎您的加入!
{company_name} 团队

+

再次欢迎您的加入!
{{company_name}} 团队

""" } @@ -599,16 +597,16 @@ class GlobalTemplateCreator: template_data_en = { "template_id": "welcome_email", "region": 0, - "subject": "Welcome to {company_name} - {new_employee_name}", + "subject": "Welcome to {{company_name}} - {{new_employee_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

Dear {new_employee_name},

+

Dear {{new_employee_name}},

-

🎉 Welcome to {company_name}!

+

🎉 Welcome to {{company_name}}!

We are pleased to inform you that you have successfully joined our team. Here is your onboarding information:

@@ -616,58 +614,58 @@ class GlobalTemplateCreator:

👤 Employee Information

    -
  • Name: {new_employee_name}
  • -
  • Employee ID: {employee_id}
  • -
  • Department: {department}
  • -
  • Position: {position}
  • -
  • Start Date: {start_date}
  • +
  • Name: {{new_employee_name}}
  • +
  • Employee ID: {{employee_id}}
  • +
  • Department: {{department}}
  • +
  • Position: {{position}}
  • +
  • Start Date: {{start_date}}

🏢 Company Information

    -
  • Company Name: {company_name}
  • -
  • Company Address: {company_address}
  • -
  • Contact Phone: {company_phone}
  • +
  • Company Name: {{company_name}}
  • +
  • Company Address: {{company_address}}
  • +
  • Contact Phone: {{company_phone}}

📋 Onboarding Schedule

-

{onboarding_schedule}

+

{{onboarding_schedule}}

🔑 System Access Information

    -
  • Email: {email_address}
  • -
  • Initial Password: {initial_password}
  • -
  • System Login URL: {system_login_url}
  • +
  • Email: {{email_address}}
  • +
  • Initial Password: {{initial_password}}
  • +
  • System Login URL: {{system_login_url}}

📚 Important Resources

👥 Contacts

    -
  • Direct Manager: {manager_name} ({manager_email})
  • -
  • HR Contact: {hr_contact_name} ({hr_contact_email})
  • -
  • IT Support: {it_support_email}
  • +
  • Direct Manager: {{manager_name}} ({{manager_email}})
  • +
  • HR Contact: {{hr_contact_name}} ({{hr_contact_email}})
  • +
  • IT Support: {{it_support_email}}

🎯 First Week Schedule

-

{first_week_schedule}

+

{{first_week_schedule}}

@@ -675,7 +673,7 @@ class GlobalTemplateCreator:
-

Welcome aboard!
{company_name} Team

+

Welcome aboard!
{{company_name}} Team

""" } @@ -693,36 +691,36 @@ class GlobalTemplateCreator: template_data_cn = { "template_id": "password_reset_email", "region": 1, - "subject": "密码重置请求 - {user_name}", + "subject": "密码重置请求 - {{user_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

尊敬的 {user_name},

+

尊敬的 {{user_name}},

我们收到了您的密码重置请求。

🔐 重置信息

    -
  • 用户名:{user_name}
  • -
  • 邮箱:{email_address}
  • -
  • 请求时间:{request_time}
  • -
  • 请求IP:{request_ip}
  • +
  • 用户名:{{user_name}}
  • +
  • 邮箱:{{email_address}}
  • +
  • 请求时间:{{request_time}}
  • +
  • 请求IP:{{request_ip}}

🔗 重置链接

-

点击重置密码

-

或复制链接:{reset_link}

+

点击重置密码

+

或复制链接:{{reset_link}}

⚠️ 重要提醒

    -
  • • 此链接将在 {expiry_hours} 小时后失效
  • +
  • • 此链接将在 {{expiry_hours}} 小时后失效
  • • 请勿将此链接分享给他人
  • • 如果您没有请求重置密码,请忽略此邮件
@@ -730,7 +728,7 @@ class GlobalTemplateCreator:

📱 验证码

-

{verification_code}

+

{{verification_code}}

@@ -744,12 +742,12 @@ class GlobalTemplateCreator:

如有任何问题,请联系技术支持:

-

邮箱:{support_email}

-

电话:{support_phone}

+

邮箱:{{support_email}}

+

电话:{{support_phone}}

-

谢谢!
{company_name} 技术支持团队

+

谢谢!
{{company_name}} 技术支持团队

""" } @@ -758,36 +756,36 @@ class GlobalTemplateCreator: template_data_en = { "template_id": "password_reset_email", "region": 0, - "subject": "Password Reset Request - {user_name}", + "subject": "Password Reset Request - {{user_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

Dear {user_name},

+

Dear {{user_name}},

We have received your password reset request.

🔐 Reset Information

    -
  • Username: {user_name}
  • -
  • Email: {email_address}
  • -
  • Request Time: {request_time}
  • -
  • Request IP: {request_ip}
  • +
  • Username: {{user_name}}
  • +
  • Email: {{email_address}}
  • +
  • Request Time: {{request_time}}
  • +
  • Request IP: {{request_ip}}

🔗 Reset Link

-

Click to Reset Password

-

Or copy the link: {reset_link}

+

Click to Reset Password

+

Or copy the link: {{reset_link}}

⚠️ Important Reminder

    -
  • • This link will expire in {expiry_hours} hours
  • +
  • • This link will expire in {{expiry_hours}} hours
  • • Please do not share this link with others
  • • If you did not request a password reset, please ignore this email
@@ -795,7 +793,7 @@ class GlobalTemplateCreator:

📱 Verification Code

-

{verification_code}

+

{{verification_code}}

@@ -809,12 +807,12 @@ class GlobalTemplateCreator:

If you have any questions, please contact technical support:

-

Email: {support_email}

-

Phone: {support_phone}

+

Email: {{support_email}}

+

Phone: {{support_phone}}

-

Thank you!
{company_name} Technical Support Team

+

Thank you!
{{company_name}} Technical Support Team

""" } @@ -832,41 +830,41 @@ class GlobalTemplateCreator: template_data_cn = { "template_id": "account_verification_email", "region": 1, - "subject": "账号验证 - {user_name}", + "subject": "账号验证 - {{user_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

尊敬的 {user_name},

+

尊敬的 {{user_name}},

-

感谢您注册 {company_name} 的账户。请验证您的邮箱地址以完成注册。

+

感谢您注册 {{company_name}} 的账户。请验证您的邮箱地址以完成注册。

👤 账户信息

    -
  • 用户名:{user_name}
  • -
  • 邮箱地址:{email_address}
  • -
  • 注册时间:{registration_time}
  • +
  • 用户名:{{user_name}}
  • +
  • 邮箱地址:{{email_address}}
  • +
  • 注册时间:{{registration_time}}

🔗 验证链接

-

点击验证邮箱

-

或复制链接:{verification_link}

+

点击验证邮箱

+

或复制链接:{{verification_link}}

📱 验证码

-

{verification_code}

+

{{verification_code}}

⏰ 验证期限

    -
  • 验证链接有效期:{expiry_hours} 小时
  • -
  • 请在 {expiry_time} 前完成验证
  • +
  • 验证链接有效期:{{expiry_hours}} 小时
  • +
  • 请在 {{expiry_time}} 前完成验证
@@ -874,7 +872,7 @@ class GlobalTemplateCreator:

✅ 验证步骤

  1. 点击上面的验证链接,或
  2. -
  3. 在登录页面输入验证码:{verification_code}
  4. +
  5. 在登录页面输入验证码:{{verification_code}}
@@ -890,8 +888,8 @@ class GlobalTemplateCreator:

📞 需要帮助?

    -
  • 技术支持:{support_email}
  • -
  • 客服热线:{support_phone}
  • +
  • 技术支持:{{support_email}}
  • +
  • 客服热线:{{support_phone}}
@@ -905,7 +903,7 @@ class GlobalTemplateCreator:
-

谢谢!
{company_name} 团队

+

谢谢!
{{company_name}} 团队

""" } @@ -914,41 +912,41 @@ class GlobalTemplateCreator: template_data_en = { "template_id": "account_verification_email", "region": 0, - "subject": "Account Verification - {user_name}", + "subject": "Account Verification - {{user_name}}", "body": """
- {company_name} Logo + {{company_name}} Logo
-

Dear {user_name},

+

Dear {{user_name}},

-

Thank you for registering an account with {company_name}. Please verify your email address to complete your registration.

+

Thank you for registering an account with {{company_name}}. Please verify your email address to complete your registration.

👤 Account Information

    -
  • Username: {user_name}
  • -
  • Email Address: {email_address}
  • -
  • Registration Time: {registration_time}
  • +
  • Username: {{user_name}}
  • +
  • Email Address: {{email_address}}
  • +
  • Registration Time: {{registration_time}}

🔗 Verification Link

-

Click to Verify Email

-

Or copy the link: {verification_link}

+

Click to Verify Email

+

Or copy the link: {{verification_link}}

📱 Verification Code

-

{verification_code}

+

{{verification_code}}

⏰ Verification Period

    -
  • Verification Link Valid: {expiry_hours} hours
  • -
  • Please complete verification before {expiry_time}
  • +
  • Verification Link Valid: {{expiry_hours}} hours
  • +
  • Please complete verification before {{expiry_time}}
@@ -956,7 +954,7 @@ class GlobalTemplateCreator:

✅ Verification Steps

  1. Click the verification link above, or
  2. -
  3. Enter the verification code on the login page: {verification_code}
  4. +
  5. Enter the verification code on the login page: {{verification_code}}
@@ -972,8 +970,8 @@ class GlobalTemplateCreator:

📞 Need Help?

    -
  • Technical Support: {support_email}
  • -
  • Customer Service: {support_phone}
  • +
  • Technical Support: {{support_email}}
  • +
  • Customer Service: {{support_phone}}
@@ -987,7 +985,7 @@ class GlobalTemplateCreator:
-

Thank you!
{company_name} Team

+

Thank you!
{{company_name}} Team

""" } diff --git a/apps/notification/webapi/routes/template_massege.py b/apps/notification/webapi/routes/template_massege.py index 202de43..cea767b 100644 --- a/apps/notification/webapi/routes/template_massege.py +++ b/apps/notification/webapi/routes/template_massege.py @@ -139,18 +139,12 @@ async def create_global_template(request: TemplateCreateRequest): body=request.body, is_active=request.is_active ) - # check if the template is created or skipped (already exists) - is_skipped = hasattr(result, '_is_existing') and result._is_existing - return JSONResponse( - content={ - "message": f"Global template {'skipped (already exists)' if is_skipped else 'created'} successfully", - "template_id": request.template_id, - "action": "skipped" if is_skipped else "created" - }, - status_code=200 if is_skipped else 201 + content={"message": "Global template created successfully", "template_id": request.template_id}, + status_code=201 ) except ValueError as e: + # duplicate or validation error raise HTTPException(status_code=400, detail=str(e)) except Exception as e: import traceback @@ -286,37 +280,19 @@ async def create_tenant_template(request: TemplateCreateRequest, payload: dict = tenant_id=tenant_id, is_active=request.is_active ) - - # check if the template is created or skipped (already exists) - is_skipped = hasattr(result, '_is_existing') and result._is_existing - + # normalize minimal response if hasattr(result, 'dict'): result_dict = result.dict() - for key, value in result_dict.items(): - if hasattr(value, 'isoformat'): - result_dict[key] = value.isoformat() - elif hasattr(value, 'value'): - result_dict[key] = value.value else: - result_dict = { - "template_id": getattr(result, 'template_id', request.template_id), - "tenant_id": getattr(result, 'tenant_id', tenant_id), - "region": getattr(result, 'region', request.region), - "subject": getattr(result, 'subject', request.subject), - "body": getattr(result, 'body', request.body), - "is_active": getattr(result, 'is_active', request.is_active) - } - + result_dict = {"template_id": request.template_id, "tenant_id": tenant_id, "region": request.region} + return JSONResponse( - content={ - "message": f"Tenant template {'skipped (already exists)' if is_skipped else 'created'} successfully", - "result": result_dict, - "action": "skipped" if is_skipped else "created" - }, - status_code=200 if is_skipped else 201, + content={"message": "Tenant template created successfully", "result": result_dict}, + status_code=201, media_type="application/json" ) except ValueError as e: + # duplicate or validation error raise HTTPException(status_code=400, detail=str(e)) except Exception as e: import traceback