From eaffaa11b784ecb2012552e7a8407f991e5e23a9 Mon Sep 17 00:00:00 2001 From: Egor Date: Tue, 23 Sep 2025 04:13:09 +0300 Subject: [PATCH] Revert "Add single-use promo group auto assignment" --- app/database/models.py | 10 --- app/database/universal_migration.py | 34 +--------- app/handlers/admin/promo_groups.py | 39 +++-------- app/localization/locales/en.json | 3 +- app/localization/locales/ru.json | 3 +- app/services/promo_group_service.py | 7 -- locales/en.json | 3 +- locales/ru.json | 3 +- ..._add_auto_assigned_promo_group_to_users.py | 65 ------------------- 9 files changed, 16 insertions(+), 151 deletions(-) delete mode 100644 migrations/alembic/versions/3e6c4d6db780_add_auto_assigned_promo_group_to_users.py diff --git a/app/database/models.py b/app/database/models.py index b017a26c..e689b1da 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -224,18 +224,8 @@ class User(Base): vless_uuid = Column(String(255), nullable=True) ss_password = Column(String(255), nullable=True) has_made_first_topup: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) - auto_assigned_promo_group_id = Column( - Integer, - ForeignKey("promo_groups.id", ondelete="SET NULL"), - nullable=True, - ) promo_group_id = Column(Integer, ForeignKey("promo_groups.id", ondelete="RESTRICT"), nullable=False, index=True) promo_group = relationship("PromoGroup", back_populates="users") - auto_assigned_promo_group = relationship( - "PromoGroup", - foreign_keys=[auto_assigned_promo_group_id], - viewonly=True, - ) @property def balance_rubles(self) -> float: diff --git a/app/database/universal_migration.py b/app/database/universal_migration.py index e0a61a46..aa68e45f 100644 --- a/app/database/universal_migration.py +++ b/app/database/universal_migration.py @@ -532,31 +532,6 @@ async def ensure_promo_groups_setup(): except Exception as e: logger.warning(f"Не удалось создать индекс ix_users_promo_group_id: {e}") - auto_assigned_column_exists = await check_column_exists( - "users", "auto_assigned_promo_group_id" - ) - - if not auto_assigned_column_exists: - if db_type == "sqlite": - await conn.execute( - text("ALTER TABLE users ADD COLUMN auto_assigned_promo_group_id INTEGER") - ) - elif db_type == "postgresql": - await conn.execute( - text("ALTER TABLE users ADD COLUMN auto_assigned_promo_group_id INTEGER") - ) - elif db_type == "mysql": - await conn.execute( - text("ALTER TABLE users ADD COLUMN auto_assigned_promo_group_id INT") - ) - else: - logger.error( - f"Неподдерживаемый тип БД для users.auto_assigned_promo_group_id: {db_type}" - ) - return False - - logger.info("Добавлена колонка users.auto_assigned_promo_group_id") - default_group_name = "Базовый юзер" default_group_id = None @@ -1275,8 +1250,7 @@ async def check_migration_status(): "promo_groups_table": False, "promo_groups_auto_assign_column": False, "promo_groups_spent_threshold_column": False, - "users_promo_group_column": False, - "users_auto_assigned_promo_group_column": False, + "users_promo_group_column": False } status["has_made_first_topup_column"] = await check_column_exists('users', 'has_made_first_topup') @@ -1295,9 +1269,6 @@ async def check_migration_status(): status["welcome_texts_is_enabled_column"] = await check_column_exists('welcome_texts', 'is_enabled') status["users_promo_group_column"] = await check_column_exists('users', 'promo_group_id') - status["users_auto_assigned_promo_group_column"] = await check_column_exists( - 'users', 'auto_assigned_promo_group_id' - ) media_fields_exist = ( await check_column_exists('broadcast_history', 'has_media') and @@ -1331,8 +1302,7 @@ async def check_migration_status(): "promo_groups_table": "Таблица промо-групп", "promo_groups_auto_assign_column": "Колонка auto_assign_enabled у промогрупп", "promo_groups_spent_threshold_column": "Колонка spent_threshold_kopeks у промогрупп", - "users_promo_group_column": "Колонка promo_group_id у пользователей", - "users_auto_assigned_promo_group_column": "Колонка auto_assigned_promo_group_id у пользователей", + "users_promo_group_column": "Колонка promo_group_id у пользователей" } for check_key, check_status in status.items(): diff --git a/app/handlers/admin/promo_groups.py b/app/handlers/admin/promo_groups.py index c89f77c1..3ef83a03 100644 --- a/app/handlers/admin/promo_groups.py +++ b/app/handlers/admin/promo_groups.py @@ -349,20 +349,8 @@ async def process_create_group_devices( await message.answer( texts.t( "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT", - "Включить автоматическое назначение по сумме трат?", - ), - reply_markup=types.InlineKeyboardMarkup( - inline_keyboard=[ - [ - types.InlineKeyboardButton( - text=texts.YES, callback_data="promo_group_auto_assign_yes" - ), - types.InlineKeyboardButton( - text=texts.NO, callback_data="promo_group_auto_assign_no" - ), - ] - ] - ), + "Включить автоматическое назначение по сумме трат? (да/нет)", + ) ) @@ -418,7 +406,7 @@ async def _finalize_create_group( @admin_required @error_handler async def process_create_group_auto_assign_enabled( - callback: types.CallbackQuery, + message: types.Message, state: FSMContext, db_user, db: AsyncSession, @@ -426,38 +414,32 @@ async def process_create_group_auto_assign_enabled( data = await state.get_data() texts = get_texts(data.get("language", db_user.language)) - if callback.data not in { - "promo_group_auto_assign_yes", - "promo_group_auto_assign_no", - }: - await callback.answer(texts.t("ADMIN_ACTION_INVALID", "Недопустимое действие"), show_alert=True) + try: + auto_enabled = _parse_bool_response(message.text) + except ValueError: + await message.answer(texts.t("ADMIN_PROMO_GROUP_INVALID_BOOL", "Введите «да» или «нет».")) return - auto_enabled = callback.data == "promo_group_auto_assign_yes" await state.update_data(new_group_auto_assign_enabled=auto_enabled) if auto_enabled: await state.set_state(AdminStates.creating_promo_group_auto_assign_threshold) - await callback.message.edit_text( + await message.answer( texts.t( "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_THRESHOLD_PROMPT", "Введите сумму трат в рублях для автоматического назначения:", ) ) - await callback.answer() return - await callback.message.edit_reply_markup() - await _finalize_create_group( - callback.message, + message, state, db_user, db, auto_assign_enabled=False, spent_threshold_kopeks=0, ) - await callback.answer() @admin_required @@ -872,10 +854,9 @@ def register_handlers(dp: Dispatcher): process_create_group_devices, AdminStates.creating_promo_group_device_discount, ) - dp.callback_query.register( + dp.message.register( process_create_group_auto_assign_enabled, AdminStates.creating_promo_group_auto_assign_enabled, - F.data.in_({"promo_group_auto_assign_yes", "promo_group_auto_assign_no"}), ) dp.message.register( process_create_group_auto_assign_threshold, diff --git a/app/localization/locales/en.json b/app/localization/locales/en.json index 29abcb8b..c9f29fbc 100644 --- a/app/localization/locales/en.json +++ b/app/localization/locales/en.json @@ -114,7 +114,6 @@ "WELCOME": "\n🎉 Welcome to VPN Service!\n\nOur service provides fast and secure internet access without restrictions.\n\n🔐 Advantages:\n• High connection speed\n• Servers in different countries \n• Reliable data protection\n• 24/7 support\n\nTo get started, select interface language:\n", "WELCOME_FALLBACK": "Welcome, {user_name}!", "YES": "✅ Yes", - "ADMIN_ACTION_INVALID": "Invalid action", "ACCESS_DENIED": "❌ Access denied", "ADMIN_MESSAGES": "📨 Broadcasts", "ADMIN_MONITORING": "🔍 Monitoring", @@ -155,7 +154,7 @@ "ADMIN_PROMO_GROUP_CREATE_TRAFFIC_PROMPT": "Enter traffic discount (0-100):", "ADMIN_PROMO_GROUP_CREATE_SERVERS_PROMPT": "Enter server discount (0-100):", "ADMIN_PROMO_GROUP_CREATE_DEVICES_PROMPT": "Enter device discount (0-100):", - "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Enable automatic assignment by total spending?", + "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Enable automatic assignment by total spending? (yes/no)", "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_THRESHOLD_PROMPT": "Enter the spending amount in RUB required for automatic assignment:", "ADMIN_PROMO_GROUP_INVALID_PERCENT": "Enter a number from 0 to 100.", "ADMIN_PROMO_GROUP_INVALID_BOOL": "Please answer \"yes\" or \"no\".", diff --git a/app/localization/locales/ru.json b/app/localization/locales/ru.json index a5d3f7ef..2d3a49ca 100644 --- a/app/localization/locales/ru.json +++ b/app/localization/locales/ru.json @@ -42,7 +42,7 @@ "ADMIN_PROMO_GROUP_CREATE_TRAFFIC_PROMPT": "Введите скидку на трафик (0-100):", "ADMIN_PROMO_GROUP_CREATE_SERVERS_PROMPT": "Введите скидку на серверы (0-100):", "ADMIN_PROMO_GROUP_CREATE_DEVICES_PROMPT": "Введите скидку на устройства (0-100):", - "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Включить автоматическое назначение по сумме трат?", + "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Включить автоматическое назначение по сумме трат? (да/нет)", "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_THRESHOLD_PROMPT": "Введите сумму трат в рублях для автоматического назначения:", "ADMIN_PROMO_GROUP_INVALID_PERCENT": "Введите число от 0 до 100.", "ADMIN_PROMO_GROUP_INVALID_BOOL": "Введите «да» или «нет».", @@ -288,7 +288,6 @@ "WELCOME": "\n🎉 Добро пожаловать в VPN сервис!\n\nНаш сервис предоставляет быстрый и безопасный доступ к интернету без ограничений.\n\n🔐 Преимущества:\n• Высокая скорость подключения\n• Серверы в разных странах\n• Надежная защита данных\n• Круглосуточная поддержка\n\nДля начала работы выберите язык интерфейса:\n", "WELCOME_FALLBACK": "Добро пожаловать, {user_name}!", "YES": "✅ Да", - "ADMIN_ACTION_INVALID": "Недопустимое действие", "SUBSCRIPTION_STATUS_EXPIRED": "Истекла", "SUBSCRIPTION_STATUS_TRIAL": "Тестовая", "SUBSCRIPTION_STATUS_ACTIVE": "Активна", diff --git a/app/services/promo_group_service.py b/app/services/promo_group_service.py index 25e89c9b..04c16579 100644 --- a/app/services/promo_group_service.py +++ b/app/services/promo_group_service.py @@ -35,18 +35,11 @@ async def auto_assign_promo_group_by_spent( if not target_group or user.promo_group_id == target_group.id: return None - if ( - getattr(user, "auto_assigned_promo_group_id", None) is not None - and user.auto_assigned_promo_group_id == target_group.id - ): - return None - previous_group_id = user.promo_group_id user.promo_group_id = target_group.id user.promo_group = target_group user.updated_at = datetime.utcnow() - user.auto_assigned_promo_group_id = target_group.id await db.commit() await db.refresh(user) diff --git a/locales/en.json b/locales/en.json index 3c52bd12..36b5a47e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -114,7 +114,6 @@ "WELCOME": "\n🎉 Welcome to VPN Service!\n\nOur service provides fast and secure internet access without restrictions.\n\n🔐 Advantages:\n• High connection speed\n• Servers in different countries \n• Reliable data protection\n• 24/7 support\n\nTo get started, select interface language:\n", "WELCOME_FALLBACK": "Welcome, {user_name}!", "YES": "✅ Yes", - "ADMIN_ACTION_INVALID": "Invalid action", "ACCESS_DENIED": "❌ Access denied", "ADMIN_MESSAGES": "📨 Broadcasts", "ADMIN_MONITORING": "🔍 Monitoring", @@ -217,7 +216,7 @@ "ADMIN_PROMO_GROUP_CREATE_TRAFFIC_PROMPT": "Enter traffic discount (0-100):", "ADMIN_PROMO_GROUP_CREATE_SERVERS_PROMPT": "Enter server discount (0-100):", "ADMIN_PROMO_GROUP_CREATE_DEVICES_PROMPT": "Enter device discount (0-100):", - "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Enable automatic assignment by total spending?", + "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Enable automatic assignment by total spending? (yes/no)", "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_THRESHOLD_PROMPT": "Enter the spending amount in RUB required for automatic assignment:", "ADMIN_PROMO_GROUP_INVALID_PERCENT": "Enter a number from 0 to 100.", "ADMIN_PROMO_GROUP_INVALID_BOOL": "Please answer \"yes\" or \"no\".", diff --git a/locales/ru.json b/locales/ru.json index c3e8f9bd..75480f09 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -104,7 +104,7 @@ "ADMIN_PROMO_GROUP_CREATE_TRAFFIC_PROMPT": "Введите скидку на трафик (0-100):", "ADMIN_PROMO_GROUP_CREATE_SERVERS_PROMPT": "Введите скидку на серверы (0-100):", "ADMIN_PROMO_GROUP_CREATE_DEVICES_PROMPT": "Введите скидку на устройства (0-100):", - "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Включить автоматическое назначение по сумме трат?", + "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Включить автоматическое назначение по сумме трат? (да/нет)", "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_THRESHOLD_PROMPT": "Введите сумму трат в рублях для автоматического назначения:", "ADMIN_PROMO_GROUP_INVALID_PERCENT": "Введите число от 0 до 100.", "ADMIN_PROMO_GROUP_INVALID_BOOL": "Введите «да» или «нет».", @@ -334,7 +334,6 @@ "WELCOME": "\n🎉 Добро пожаловать в VPN сервис!\n\nНаш сервис предоставляет быстрый и безопасный доступ к интернету без ограничений.\n\n🔐 Преимущества:\n• Высокая скорость подключения\n• Серверы в разных странах\n• Надежная защита данных\n• Круглосуточная поддержка\n\nДля начала работы выберите язык интерфейса:\n", "WELCOME_FALLBACK": "Добро пожаловать, {user_name}!", "YES": "✅ Да", - "ADMIN_ACTION_INVALID": "Недопустимое действие", "SUBSCRIPTION_STATUS_EXPIRED": "Истекла", "SUBSCRIPTION_STATUS_TRIAL": "Тестовая", "SUBSCRIPTION_STATUS_ACTIVE": "Активна", diff --git a/migrations/alembic/versions/3e6c4d6db780_add_auto_assigned_promo_group_to_users.py b/migrations/alembic/versions/3e6c4d6db780_add_auto_assigned_promo_group_to_users.py deleted file mode 100644 index a75f2077..00000000 --- a/migrations/alembic/versions/3e6c4d6db780_add_auto_assigned_promo_group_to_users.py +++ /dev/null @@ -1,65 +0,0 @@ -"""Add auto assigned promo group tracking to users - -Revision ID: 3e6c4d6db780 -Revises: b6b5c77e2a9d -Create Date: 2024-05-06 00:00:00.000000 -""" - -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -revision: str = "3e6c4d6db780" -down_revision: Union[str, None] = "b6b5c77e2a9d" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - -USERS_TABLE = "users" -COLUMN_NAME = "auto_assigned_promo_group_id" -PROMO_GROUPS_TABLE = "promo_groups" - - -def upgrade() -> None: - bind = op.get_bind() - inspector = sa.inspect(bind) - - if USERS_TABLE not in inspector.get_table_names(): - return - - if COLUMN_NAME in [column["name"] for column in inspector.get_columns(USERS_TABLE)]: - return - - op.add_column( - USERS_TABLE, - sa.Column(COLUMN_NAME, sa.Integer(), nullable=True), - ) - op.create_foreign_key( - "fk_users_auto_assigned_promo_group_id", - USERS_TABLE, - PROMO_GROUPS_TABLE, - [COLUMN_NAME], - ["id"], - ondelete="SET NULL", - ) - - -def downgrade() -> None: - bind = op.get_bind() - inspector = sa.inspect(bind) - - if USERS_TABLE not in inspector.get_table_names(): - return - - if COLUMN_NAME not in [column["name"] for column in inspector.get_columns(USERS_TABLE)]: - return - - fk_names = [fk["name"] for fk in inspector.get_foreign_keys(USERS_TABLE)] - if "fk_users_auto_assigned_promo_group_id" in fk_names: - op.drop_constraint( - "fk_users_auto_assigned_promo_group_id", - USERS_TABLE, - type_="foreignkey", - ) - op.drop_column(USERS_TABLE, COLUMN_NAME)