Refactor notification handling and streamline router registration
- Replaced legacy notification functions with a unified NotificationService for better maintainability and clarity. - Updated the main bot router registration to utilize a root router, simplifying the inclusion of user and admin routes. - Removed unused middleware and helper functions to enhance code cleanliness and focus on essential components. - Improved localization by adding new error messages for user interactions.
This commit is contained in:
@@ -47,6 +47,38 @@ async def get_payment_by_provider_payment_id(
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def ensure_payment_with_provider_id(
|
||||
session: AsyncSession,
|
||||
*,
|
||||
user_id: int,
|
||||
amount: float,
|
||||
currency: str,
|
||||
months: int,
|
||||
description: str,
|
||||
provider: str,
|
||||
provider_payment_id: str) -> Payment:
|
||||
"""Idempotently create a payment record for a provider event.
|
||||
|
||||
If a payment with the same provider_payment_id already exists, returns it.
|
||||
Otherwise creates a new succeeded payment with provided data.
|
||||
"""
|
||||
existing = await get_payment_by_provider_payment_id(session, provider_payment_id)
|
||||
if existing:
|
||||
return existing
|
||||
|
||||
payment_payload: Dict[str, Any] = {
|
||||
"user_id": user_id,
|
||||
"amount": float(amount),
|
||||
"currency": currency,
|
||||
"status": "succeeded",
|
||||
"description": description,
|
||||
"subscription_duration_months": months,
|
||||
"provider_payment_id": provider_payment_id,
|
||||
"provider": provider,
|
||||
}
|
||||
return await create_payment_record(session, payment_payload)
|
||||
|
||||
|
||||
async def get_payment_by_db_id(session: AsyncSession,
|
||||
payment_db_id: int) -> Optional[Payment]:
|
||||
|
||||
|
||||
@@ -53,6 +53,29 @@ async def update_subscription(
|
||||
return sub
|
||||
|
||||
|
||||
async def set_user_subscriptions_cancelled_with_grace(
|
||||
session: AsyncSession, user_id: int, grace_days: int = 1) -> int:
|
||||
"""Mark all active user subscriptions as cancelled with a short grace period.
|
||||
|
||||
Sets end_date to now + grace_days, status_from_panel to 'CANCELLED', and
|
||||
skip future notifications to reduce noise after cancellation.
|
||||
Returns number of updated rows.
|
||||
"""
|
||||
from datetime import datetime, timezone, timedelta
|
||||
grace_end = datetime.now(timezone.utc) + timedelta(days=grace_days)
|
||||
stmt = (
|
||||
update(Subscription)
|
||||
.where(Subscription.user_id == user_id, Subscription.is_active == True)
|
||||
.values(
|
||||
end_date=grace_end,
|
||||
status_from_panel="CANCELLED",
|
||||
skip_notifications=True,
|
||||
)
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
return result.rowcount or 0
|
||||
|
||||
|
||||
async def upsert_subscription(session: AsyncSession,
|
||||
sub_payload: Dict[str, Any]) -> Subscription:
|
||||
panel_sub_uuid = sub_payload.get("panel_subscription_uuid")
|
||||
|
||||
+1
-14
@@ -30,20 +30,7 @@ async def get_user_by_panel_uuid(
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def get_user(
|
||||
session: AsyncSession,
|
||||
*,
|
||||
user_id: Optional[int] = None,
|
||||
username: Optional[str] = None,
|
||||
panel_uuid: Optional[str] = None,
|
||||
) -> Optional[User]:
|
||||
if user_id is not None:
|
||||
return await get_user_by_id(session, user_id)
|
||||
if username is not None:
|
||||
return await get_user_by_username(session, username)
|
||||
if panel_uuid is not None:
|
||||
return await get_user_by_panel_uuid(session, panel_uuid)
|
||||
return None
|
||||
## Removed unused generic get_user helper to keep DAL explicit and simple
|
||||
|
||||
|
||||
async def create_user(session: AsyncSession, user_data: Dict[str, Any]) -> User:
|
||||
|
||||
Reference in New Issue
Block a user