From c4cde5efa2ff1bb81f931415d7a6153abff10a6c Mon Sep 17 00:00:00 2001 From: Egor Date: Sat, 17 Jan 2026 05:00:47 +0300 Subject: [PATCH] Add files via upload --- app/database/models.py | 20 ++++++ app/database/universal_migration.py | 101 ++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/app/database/models.py b/app/database/models.py index 3898464b..53a20e80 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -2067,6 +2067,10 @@ class AdvertisingCampaign(Base): subscription_device_limit = Column(Integer, nullable=True) subscription_squads = Column(JSON, default=list) + # Поля для типа "tariff" - выдача тарифа + tariff_id = Column(Integer, ForeignKey("tariffs.id", ondelete="SET NULL"), nullable=True) + tariff_duration_days = Column(Integer, nullable=True) + is_active = Column(Boolean, default=True) created_by = Column(Integer, ForeignKey("users.id"), nullable=True) @@ -2074,6 +2078,7 @@ class AdvertisingCampaign(Base): updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) registrations = relationship("AdvertisingCampaignRegistration", back_populates="campaign") + tariff = relationship("Tariff", foreign_keys=[tariff_id]) @property def is_balance_bonus(self) -> bool: @@ -2083,6 +2088,16 @@ class AdvertisingCampaign(Base): def is_subscription_bonus(self) -> bool: return self.bonus_type == "subscription" + @property + def is_none_bonus(self) -> bool: + """Ссылка без награды - только для отслеживания.""" + return self.bonus_type == "none" + + @property + def is_tariff_bonus(self) -> bool: + """Выдача тарифа на определённое время.""" + return self.bonus_type == "tariff" + class AdvertisingCampaignRegistration(Base): __tablename__ = "advertising_campaign_registrations" @@ -2098,10 +2113,15 @@ class AdvertisingCampaignRegistration(Base): balance_bonus_kopeks = Column(Integer, default=0) subscription_duration_days = Column(Integer, nullable=True) + # Поля для типа "tariff" + tariff_id = Column(Integer, ForeignKey("tariffs.id", ondelete="SET NULL"), nullable=True) + tariff_duration_days = Column(Integer, nullable=True) + created_at = Column(DateTime, default=func.now()) campaign = relationship("AdvertisingCampaign", back_populates="registrations") user = relationship("User") + tariff = relationship("Tariff") @property def balance_bonus_rubles(self) -> float: diff --git a/app/database/universal_migration.py b/app/database/universal_migration.py index c6fef6d9..c49be5f4 100644 --- a/app/database/universal_migration.py +++ b/app/database/universal_migration.py @@ -5395,6 +5395,90 @@ async def add_subscription_tariff_id_column() -> bool: return False +async def add_campaign_tariff_columns() -> bool: + """Добавляет колонки tariff_id и tariff_duration_days в таблицы рекламных кампаний.""" + try: + campaigns_tariff_id_exists = await check_column_exists('advertising_campaigns', 'tariff_id') + campaigns_duration_exists = await check_column_exists('advertising_campaigns', 'tariff_duration_days') + registrations_tariff_id_exists = await check_column_exists('advertising_campaign_registrations', 'tariff_id') + registrations_duration_exists = await check_column_exists('advertising_campaign_registrations', 'tariff_duration_days') + + if campaigns_tariff_id_exists and campaigns_duration_exists and registrations_tariff_id_exists and registrations_duration_exists: + logger.info("ℹ️ Колонки tariff в рекламных кампаниях уже существуют") + return True + + async with engine.begin() as conn: + db_type = await get_database_type() + + # Добавляем колонки в advertising_campaigns + if not campaigns_tariff_id_exists: + if db_type == 'sqlite': + await conn.execute(text( + "ALTER TABLE advertising_campaigns ADD COLUMN tariff_id INTEGER REFERENCES tariffs(id)" + )) + elif db_type == 'postgresql': + await conn.execute(text( + "ALTER TABLE advertising_campaigns ADD COLUMN tariff_id INTEGER REFERENCES tariffs(id) ON DELETE SET NULL" + )) + else: # MySQL + await conn.execute(text( + "ALTER TABLE advertising_campaigns ADD COLUMN tariff_id INT NULL" + )) + logger.info("✅ Колонка tariff_id добавлена в advertising_campaigns") + + if not campaigns_duration_exists: + if db_type == 'sqlite': + await conn.execute(text( + "ALTER TABLE advertising_campaigns ADD COLUMN tariff_duration_days INTEGER NULL" + )) + elif db_type == 'postgresql': + await conn.execute(text( + "ALTER TABLE advertising_campaigns ADD COLUMN tariff_duration_days INTEGER NULL" + )) + else: # MySQL + await conn.execute(text( + "ALTER TABLE advertising_campaigns ADD COLUMN tariff_duration_days INT NULL" + )) + logger.info("✅ Колонка tariff_duration_days добавлена в advertising_campaigns") + + # Добавляем колонки в advertising_campaign_registrations + if not registrations_tariff_id_exists: + if db_type == 'sqlite': + await conn.execute(text( + "ALTER TABLE advertising_campaign_registrations ADD COLUMN tariff_id INTEGER REFERENCES tariffs(id)" + )) + elif db_type == 'postgresql': + await conn.execute(text( + "ALTER TABLE advertising_campaign_registrations ADD COLUMN tariff_id INTEGER REFERENCES tariffs(id) ON DELETE SET NULL" + )) + else: # MySQL + await conn.execute(text( + "ALTER TABLE advertising_campaign_registrations ADD COLUMN tariff_id INT NULL" + )) + logger.info("✅ Колонка tariff_id добавлена в advertising_campaign_registrations") + + if not registrations_duration_exists: + if db_type == 'sqlite': + await conn.execute(text( + "ALTER TABLE advertising_campaign_registrations ADD COLUMN tariff_duration_days INTEGER NULL" + )) + elif db_type == 'postgresql': + await conn.execute(text( + "ALTER TABLE advertising_campaign_registrations ADD COLUMN tariff_duration_days INTEGER NULL" + )) + else: # MySQL + await conn.execute(text( + "ALTER TABLE advertising_campaign_registrations ADD COLUMN tariff_duration_days INT NULL" + )) + logger.info("✅ Колонка tariff_duration_days добавлена в advertising_campaign_registrations") + + return True + + except Exception as error: + logger.error(f"❌ Ошибка добавления колонок tariff в рекламные кампании: {error}") + return False + + async def add_tariff_device_price_column() -> bool: """Добавляет колонку device_price_kopeks в таблицу tariffs.""" try: @@ -6602,6 +6686,13 @@ async def run_universal_migration(): else: logger.warning("⚠️ Проблемы с колонкой tariff_id в subscriptions") + logger.info("=== ДОБАВЛЕНИЕ КОЛОНОК ТАРИФОВ В РЕКЛАМНЫЕ КАМПАНИИ ===") + campaign_tariff_columns_ready = await add_campaign_tariff_columns() + if campaign_tariff_columns_ready: + logger.info("✅ Колонки tariff в рекламных кампаниях готовы") + else: + logger.warning("⚠️ Проблемы с колонками tariff в рекламных кампаниях") + device_price_column_ready = await add_tariff_device_price_column() if device_price_column_ready: logger.info("✅ Колонка device_price_kopeks в tariffs готова") @@ -6814,6 +6905,10 @@ async def check_migration_status(): "promo_offer_templates_active_discount_column": False, "promo_offer_logs_table": False, "subscription_temporary_access_table": False, + "campaign_tariff_id_column": False, + "campaign_tariff_duration_days_column": False, + "campaign_registration_tariff_id_column": False, + "campaign_registration_tariff_duration_days_column": False, } status["has_made_first_topup_column"] = await check_column_exists('users', 'has_made_first_topup') @@ -6847,6 +6942,12 @@ async def check_migration_status(): status["promo_offer_logs_table"] = await check_table_exists('promo_offer_logs') status["subscription_temporary_access_table"] = await check_table_exists('subscription_temporary_access') + # Проверяем колонки tariff в рекламных кампаниях + status["campaign_tariff_id_column"] = await check_column_exists('advertising_campaigns', 'tariff_id') + status["campaign_tariff_duration_days_column"] = await check_column_exists('advertising_campaigns', 'tariff_duration_days') + status["campaign_registration_tariff_id_column"] = await check_column_exists('advertising_campaign_registrations', 'tariff_id') + status["campaign_registration_tariff_duration_days_column"] = await check_column_exists('advertising_campaign_registrations', 'tariff_duration_days') + 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["promo_groups_period_discounts_column"] = await check_column_exists('promo_groups', 'period_discounts')