feat: 续费提醒告警(Telegram/邮件多渠道+续费检查+每日定时+前端控制)
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""续费检查服务
|
||||
|
||||
检查即将到期的资产,生成提醒消息并通过通知渠道发送。
|
||||
"""
|
||||
|
||||
from datetime import date
|
||||
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from app.core.config import settings
|
||||
from app.models.asset import Asset, AssetStatus
|
||||
from app.services import notification_service
|
||||
|
||||
# 已注销的资产不参与续费提醒
|
||||
_SKIP_STATUS = {AssetStatus.CANCELLED}
|
||||
|
||||
|
||||
def get_expiring_assets(session: Session, threshold_days: int) -> list:
|
||||
"""返回 threshold_days 天内到期的资产列表 [(asset, days), ...],按剩余天数升序"""
|
||||
today = date.today()
|
||||
assets = session.exec(select(Asset).where(Asset.expiry_date.is_not(None))).all()
|
||||
expiring = []
|
||||
for asset in assets:
|
||||
if asset.status in _SKIP_STATUS:
|
||||
continue
|
||||
days = (asset.expiry_date - today).days
|
||||
if 0 <= days <= threshold_days:
|
||||
expiring.append((asset, days))
|
||||
expiring.sort(key=lambda x: x[1])
|
||||
return expiring
|
||||
|
||||
|
||||
def build_renewal_message(expiring: list, threshold_days: int) -> str:
|
||||
"""生成续费提醒消息(纯文本,兼容 Telegram/邮件)"""
|
||||
if not expiring:
|
||||
return ""
|
||||
lines = [f"⏰ 资产续费提醒({threshold_days} 天内到期,共 {len(expiring)} 项)", ""]
|
||||
for asset, days in expiring:
|
||||
tag = "(已过期)" if days == 0 else f"({days} 天后)"
|
||||
lines.append(f"• {asset.name} [{asset.provider}] {tag} 到期 {asset.expiry_date}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def check_renewals(session: Session, threshold_days: int = None, send: bool = True) -> dict:
|
||||
"""检查续费并(可选)发送提醒
|
||||
|
||||
threshold_days: 提醒阈值天数,默认取配置 RENEWAL_THRESHOLD_DAYS
|
||||
send: 是否实际发送通知(False 时仅返回预览)
|
||||
"""
|
||||
if threshold_days is None:
|
||||
threshold_days = settings.RENEWAL_THRESHOLD_DAYS
|
||||
|
||||
expiring = get_expiring_assets(session, threshold_days)
|
||||
message = build_renewal_message(expiring, threshold_days)
|
||||
|
||||
result = {
|
||||
"threshold_days": threshold_days,
|
||||
"expiring_count": len(expiring),
|
||||
"expiring": [
|
||||
{
|
||||
"name": asset.name,
|
||||
"provider": asset.provider,
|
||||
"days": days,
|
||||
"expiry_date": str(asset.expiry_date),
|
||||
}
|
||||
for asset, days in expiring
|
||||
],
|
||||
"message": message,
|
||||
}
|
||||
|
||||
if send and message:
|
||||
result["notifications"] = notification_service.notify(message)
|
||||
else:
|
||||
result["notifications"] = []
|
||||
return result
|
||||
Reference in New Issue
Block a user