fix: bulk actions review — rollback on error, multi-tariff constraint checks

- Add db.rollback() in per-user exception handler to prevent session
  poisoning (one failed commit would abort all subsequent users)
- Add multi-tariff duplicate subscription check in activate_subscription
  (prevents uq_subscriptions_user_tariff_active violation)
- Add multi-tariff duplicate subscription check in change_tariff
  (prevents switching to a tariff the user already holds)
This commit is contained in:
Fringg
2026-04-24 04:24:20 +03:00
parent fb2773fee4
commit 5b45d4354f
+30
View File
@@ -177,6 +177,19 @@ async def _do_activate_subscription(
if not sub:
return BulkUserResult(user_id=user.id, success=False, message='No subscription found', username=user.username)
# Проверка дубликата в мультитарифном режиме
if settings.is_multi_tariff_enabled() and sub.tariff_id:
from app.database.crud.subscription import get_subscription_by_user_and_tariff
existing = await get_subscription_by_user_and_tariff(db, user.id, sub.tariff_id)
if existing and existing.id != sub.id:
return BulkUserResult(
user_id=user.id,
success=False,
message='Cannot activate: user already has an active subscription for this tariff',
username=user.username,
)
if dry_run:
return BulkUserResult(
user_id=user.id,
@@ -212,6 +225,19 @@ async def _do_change_tariff(
if not sub:
return BulkUserResult(user_id=user.id, success=False, message='No subscription found', username=user.username)
# Проверка дубликата в мультитарифном режиме
if settings.is_multi_tariff_enabled() and tariff.id != sub.tariff_id:
from app.database.crud.subscription import get_subscription_by_user_and_tariff
existing = await get_subscription_by_user_and_tariff(db, user.id, tariff.id)
if existing and existing.id != sub.id:
return BulkUserResult(
user_id=user.id,
success=False,
message='User already has an active subscription for the target tariff',
username=user.username,
)
if dry_run:
return BulkUserResult(
user_id=user.id,
@@ -494,6 +520,10 @@ async def bulk_execute(
except Exception as exc:
logger.error('Bulk action failed for user', user_id=uid, action=action, error=str(exc))
try:
await db.rollback()
except Exception:
pass
results.append(BulkUserResult(user_id=uid, success=False, message=str(exc)))
error_count += 1