Files
ewoooc/services/notification_service.py
OoO 779b27f676
All checks were successful
CD Pipeline / deploy (push) Successful in 9m39s
修復 P0 告警自癒鏈與測試收集
2026-04-29 22:37:20 +08:00

168 lines
5.8 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
通知模板服務
管理通知模板的 CRUD 操作和模板渲染
"""
import os
from sqlalchemy import create_engine
from database.manager import DatabaseManager
from database.manager import ensure_metadata_initialized
from database.notification_models import NotificationTemplate, DEFAULT_TEMPLATES, Base
from services.logger_manager import SystemLogger
sys_log = SystemLogger("NotificationService").get_logger()
# 初始化資料庫連線
try:
from config import DATABASE_PATH as CONFIG_DATABASE_PATH
_engine = create_engine(CONFIG_DATABASE_PATH)
except ImportError:
DATABASE_PATH = os.getenv('DATABASE_PATH', 'data/momo_database.db')
if DATABASE_PATH.startswith('postgresql://') or DATABASE_PATH.startswith('sqlite://'):
_engine = create_engine(DATABASE_PATH)
else:
_engine = create_engine(f'sqlite:///{DATABASE_PATH}')
def _init_notification_tables():
"""初始化通知模板資料表"""
try:
ensure_metadata_initialized(_engine, use_postgres_lock=str(_engine.url).startswith('postgresql'))
sys_log.info("[Notification] 通知模板表已初始化")
except Exception as e:
sys_log.error(f"[Notification] 初始化表失敗: {e}")
# 模組載入時自動初始化表
_init_notification_tables()
class NotificationTemplateService:
"""通知模板服務"""
@staticmethod
def init_default_templates():
"""初始化預設模板"""
db = DatabaseManager()
try:
with db.get_session() as session:
for template_data in DEFAULT_TEMPLATES:
existing = session.query(NotificationTemplate).filter_by(
code=template_data['code']
).first()
if not existing:
template = NotificationTemplate(**template_data)
session.add(template)
sys_log.info(f"[Notification] 新增預設模板: {template_data['code']}")
session.commit()
sys_log.info("[Notification] 預設模板初始化完成")
except Exception as e:
sys_log.error(f"[Notification] 初始化模板失敗: {e}")
@staticmethod
def get_all_templates(category=None):
"""取得所有模板"""
db = DatabaseManager()
try:
with db.get_session() as session:
query = session.query(NotificationTemplate)
if category:
query = query.filter_by(category=category)
templates = query.order_by(NotificationTemplate.category, NotificationTemplate.code).all()
return [t.to_dict() for t in templates]
except Exception as e:
sys_log.error(f"[Notification] 取得模板列表失敗: {e}")
return []
@staticmethod
def get_template_by_code(code):
"""根據代碼取得模板"""
db = DatabaseManager()
try:
with db.get_session() as session:
template = session.query(NotificationTemplate).filter_by(code=code).first()
if template:
return template.to_dict()
return None
except Exception as e:
sys_log.error(f"[Notification] 取得模板失敗: {e}")
return None
@staticmethod
def update_template(code, data):
"""更新模板"""
db = DatabaseManager()
try:
with db.get_session() as session:
template = session.query(NotificationTemplate).filter_by(code=code).first()
if not template:
return {'success': False, 'error': '模板不存在'}
# 更新允許的欄位
allowed_fields = ['name', 'title', 'body', 'emoji_prefix', 'channel', 'is_active']
for field in allowed_fields:
if field in data:
setattr(template, field, data[field])
session.commit()
sys_log.info(f"[Notification] 更新模板: {code}")
return {'success': True, 'template': template.to_dict()}
except Exception as e:
sys_log.error(f"[Notification] 更新模板失敗: {e}")
return {'success': False, 'error': str(e)}
@staticmethod
def render_template(code, variables=None):
"""渲染模板"""
if variables is None:
variables = {}
template = NotificationTemplateService.get_template_by_code(code)
if not template:
return None
try:
# 組合完整訊息
emoji = template.get('emoji_prefix', '')
title = template.get('title', '')
body = template.get('body', '')
# 替換變數
if title:
title = title.format(**variables)
if body:
body = body.format(**variables)
# 組合 Markdown 格式
message = f"{emoji} *{title}*\n\n{body}" if title else f"{emoji}\n{body}"
return {
'success': True,
'message': message,
'channel': template.get('channel', 'telegram')
}
except KeyError as e:
sys_log.warning(f"[Notification] 模板變數缺失: {e}")
return {
'success': False,
'error': f'缺少變數: {e}',
'template': template
}
except Exception as e:
sys_log.error(f"[Notification] 渲染模板失敗: {e}")
return {'success': False, 'error': str(e)}
@staticmethod
def get_categories():
"""取得所有分類"""
return [
{'code': 'system', 'name': '系統監控'},
{'code': 'business', 'name': '業務通知'},
{'code': 'report', 'name': '定期報告'}
]