Система администраторов в Telegram-магазине (Часть 6)

Система администраторов в Telegram-магазине (Часть 6)

В этом обновлении мы добавим систему администраторов в наш Telegram-магазин. Теперь можно назначать администраторов, которые смогут управлять товарами и заказами, но не смогут назначать других админов. Мы создадим супер-администратора с возможностью добавлять обычных админов.

Удаляем старую базу и создаем новую

Так как нам нужно добавить новую таблицу admins, нам нужно удалить старый файл shop.db, иначе бот не сможет работать с обновленной структурой базы.

Удаляем файл shop.db (его можно просто удалить вручную) или удалить через терминал, как это сделать можно посмотреть в предыдущей статьи.

Изменения в database.py

Теперь в файле database.py нужно добавить таблицу admins, в которой будут храниться user_id администраторов. Так же не забываем добавить и другие таблицы.

import sqlite3

# Подключение к базе данных
conn = sqlite3.connect("shop.db")
cursor = conn.cursor()

# Таблица товаров
cursor.execute("""
CREATE TABLE IF NOT EXISTS products (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    price INTEGER NOT NULL
)
""")

# Таблица корзины
cursor.execute("""
CREATE TABLE IF NOT EXISTS cart (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    product_id INTEGER NOT NULL,
    quantity INTEGER NOT NULL,
    FOREIGN KEY (product_id) REFERENCES products (id)
)
""")

# Таблица заказов
cursor.execute("""
CREATE TABLE IF NOT EXISTS orders (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    total_price INTEGER NOT NULL,
    status TEXT NOT NULL DEFAULT 'pending'
)
""")

# Таблица администраторов
cursor.execute("""
CREATE TABLE IF NOT EXISTS admins (
    user_id INTEGER PRIMARY KEY,
    is_super_admin INTEGER DEFAULT 0
)
""")

conn.commit()
conn.close()

Запускаем database.py и таблицы с базой данной создадустся. Также мы добавим функции для управления администраторами:

def add_admin(user_id):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("INSERT OR IGNORE INTO admins (user_id) VALUES (?)", (user_id,))
    conn.commit()
    conn.close()

def remove_admin(user_id):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("DELETE FROM admins WHERE user_id = ?", (user_id,))
    conn.commit()
    conn.close()

def is_admin(user_id):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("SELECT 1 FROM admins WHERE user_id = ?", (user_id,))
    result = cursor.fetchone()
    conn.close()
    return result is not None

Мы удаляли базу, поэтому нужно добавить тестовые товары. Чтобы добавить несколько тестовых товары, можно запустить Python-скрипт вручную.

✅ Открой терминал и запусти Python:

Далее в интерактивном режиме выполни команды:

from database import add_product

add_product("📦 Товар A", 100)
add_product("📦 Товар B", 200)
add_product("📦 Товар C", 300)

print("Товары добавлены!")

После этого товары появятся в базе. Чтобы убедиться, что товары записаны, в терминале введи:

from database import get_products

print(get_products())

Ты увидишь список товаров:

После всех изменений полный код database.py выглядит так:

import sqlite3

# Подключение к базе данных
conn = sqlite3.connect("shop.db")
cursor = conn.cursor()

# Таблица товаров
cursor.execute("""
CREATE TABLE IF NOT EXISTS products (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    price INTEGER NOT NULL
)
""")

# Таблица корзины
cursor.execute("""
CREATE TABLE IF NOT EXISTS cart (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    product_id INTEGER NOT NULL,
    quantity INTEGER NOT NULL,
    FOREIGN KEY (product_id) REFERENCES products (id)
)
""")

# Таблица заказов
cursor.execute("""
CREATE TABLE IF NOT EXISTS orders (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    total_price INTEGER NOT NULL,
    status TEXT NOT NULL DEFAULT 'pending'
)
""")

# Таблица администраторов
cursor.execute("""
CREATE TABLE IF NOT EXISTS admins (
    user_id INTEGER PRIMARY KEY,
    is_super_admin INTEGER DEFAULT 0
)
""")

conn.commit()
conn.close()


### Функции для работы с товарами
def add_product(name, price):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("INSERT INTO products (name, price) VALUES (?, ?)", (name, price))
    conn.commit()
    conn.close()


def get_products():
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM products")
    products = cursor.fetchall()
    conn.close()
    return products


### Функции для работы с корзиной
def add_to_cart(user_id, product_id, quantity=1):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()

    cursor.execute("SELECT quantity FROM cart WHERE user_id=? AND product_id=?", (user_id, product_id))
    result = cursor.fetchone()

    if result:
        new_quantity = result[0] + quantity
        cursor.execute("UPDATE cart SET quantity=? WHERE user_id=? AND product_id=?", (new_quantity, user_id, product_id))
    else:
        cursor.execute("INSERT INTO cart (user_id, product_id, quantity) VALUES (?, ?, ?)", (user_id, product_id, quantity))

    conn.commit()
    conn.close()


def remove_from_cart(user_id, product_id):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()

    cursor.execute("SELECT quantity FROM cart WHERE user_id=? AND product_id=?", (user_id, product_id))
    result = cursor.fetchone()

    if result:
        if result[0] > 1:
            new_quantity = result[0] - 1
            cursor.execute("UPDATE cart SET quantity=? WHERE user_id=? AND product_id=?", (new_quantity, user_id, product_id))
        else:
            cursor.execute("DELETE FROM cart WHERE user_id=? AND product_id=?", (user_id, product_id))

    conn.commit()
    conn.close()


def get_cart(user_id):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("""
        SELECT products.name, cart.quantity, products.price, products.id 
        FROM cart 
        JOIN products ON cart.product_id = products.id 
        WHERE cart.user_id=?
    """, (user_id,))
    cart_items = cursor.fetchall()
    conn.close()
    return cart_items


def create_order(user_id):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()

    cursor.execute("""
        SELECT products.id, products.price, cart.quantity
        FROM cart
        JOIN products ON cart.product_id = products.id
        WHERE cart.user_id=?
    """, (user_id,))
    cart_items = cursor.fetchall()

    if not cart_items:
        conn.close()
        return None

    total_price = sum(item[1] * item[2] for item in cart_items)

    cursor.execute("INSERT INTO orders (user_id, total_price) VALUES (?, ?)", (user_id, total_price))
    order_id = cursor.lastrowid

    cursor.execute("DELETE FROM cart WHERE user_id=?", (user_id,))

    conn.commit()
    conn.close()
    return order_id


### Функции для работы с администраторами
def set_super_admin(user_id):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()

    cursor.execute("SELECT COUNT(*) FROM admins WHERE is_super_admin=1")
    if cursor.fetchone()[0] == 0:
        cursor.execute("INSERT INTO admins (user_id, is_super_admin) VALUES (?, 1)", (user_id,))
        conn.commit()
        conn.close()
        return True
    conn.close()
    return False


def add_admin(user_id):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("INSERT OR IGNORE INTO admins (user_id) VALUES (?)", (user_id,))
    conn.commit()
    conn.close()


def remove_admin(user_id):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("DELETE FROM admins WHERE user_id=? AND is_super_admin=0", (user_id,))
    conn.commit()
    conn.close()


def is_admin(user_id):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("SELECT COUNT(*) FROM admins WHERE user_id=?", (user_id,))
    result = cursor.fetchone()[0]
    conn.close()
    return result > 0


def is_super_admin(user_id):
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("SELECT is_super_admin FROM admins WHERE user_id=?", (user_id,))
    result = cursor.fetchone()
    conn.close()
    return result and result[0] == 1


def get_admins():
    conn = sqlite3.connect("shop.db")
    cursor = conn.cursor()
    cursor.execute("SELECT user_id, is_super_admin FROM admins")
    admins = cursor.fetchall()
    conn.close()
    return admins

Команды для управления администраторами в handlers.py

Теперь у нас будет три команды:

  • /add_admin <user_id> — добавляет администратора (доступно только супер-админу).
  • /remove_admin <user_id> — удаляет администратора (доступно только супер-админу).
  • /admins — показывает список администраторов.

Добавляем их в handlers.py:

from aiogram import Router
from aiogram.types import Message
from aiogram.filters import Command

from database import add_admin, remove_admin, is_admin, get_admins

router = Router()

SUPER_ADMIN_ID = 123456789  # Замените на ваш user_id

@router.message(Command("add_admin"))
async def add_admin_handler(message: Message):
    if message.from_user.id != SUPER_ADMIN_ID:
        await message.answer("У вас нет прав для выполнения этой команды.")
        return

    parts = message.text.split()
    if len(parts) != 2 or not parts[1].isdigit():
        await message.answer("Использование: /add_admin ")
        return

    user_id = int(parts[1])
    add_admin(user_id)
    await message.answer(f"Пользователь {user_id} добавлен в администраторы.")

@router.message(Command("remove_admin"))
async def remove_admin_handler(message: Message):
    if message.from_user.id != SUPER_ADMIN_ID:
        await message.answer("У вас нет прав для выполнения этой команды.")
        return

    parts = message.text.split()
    if len(parts) != 2 or not parts[1].isdigit():
        await message.answer("Использование: /remove_admin ")
        return

    user_id = int(parts[1])
    remove_admin(user_id)
    await message.answer(f"Пользователь {user_id} удален из администраторов.")

@router.message(Command("admins"))
async def list_admins_handler(message: Message):
    admins = get_admins()
    if not admins:
        await message.answer("Список администраторов пуст.")
    else:
        admin_list = "\n".join([str(admin[0]) for admin in admins])
        await message.answer(f"Администраторы:\n{admin_list}")

Теперь супер-админ сможет добавлять и удалять администраторов. При первом запуске нужно запустить команду /set_super_admin и если до этого не было супер админа, то вы будете им назначены. В противном случаи будет выводится сообщение «⚠️ Супер-администратор уже назначен.»

Полный код handlers.py теперь такой:

from aiogram import Router, F
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton
from aiogram.filters import Command
from keyboards import main_menu
from database import (
    set_super_admin, add_admin, remove_admin, is_admin, is_super_admin, get_admins,
    get_products, add_to_cart, get_cart, create_order, remove_from_cart
)


router = Router()


### 🔹 ОБРАБОТКА КОМАНДЫ /START
@router.message(Command("start"))
async def start_command(message: Message):
    await message.answer("Добро пожаловать в магазин! Выберите действие:", reply_markup=main_menu)


### 🔹 УСТАНОВКА СУПЕР-АДМИНА (ОДИН РАЗ)
@router.message(Command("set_super_admin"))
async def set_super_admin_handler(message: Message):
    if set_super_admin(message.from_user.id):
        await message.answer("✅ Вы назначены супер-администратором!")
    else:
        await message.answer("⚠️ Супер-администратор уже назначен.")


### 🔹 ДОБАВЛЕНИЕ АДМИНИСТРАТОРА (ТОЛЬКО ДЛЯ СУПЕР-АДМИНА)
@router.message(Command("add_admin"))
async def add_admin_handler(message: Message):
    if not is_super_admin(message.from_user.id):
        await message.answer("❌ У вас нет прав для этой команды.")
        return

    try:
        user_id = int(message.text.split()[1])
        add_admin(user_id)
        await message.answer(f"✅ Пользователь {user_id} добавлен в администраторы.")
    except:
        await message.answer("⚠️ Используйте: `/add_admin user_id`")


### 🔹 УДАЛЕНИЕ АДМИНИСТРАТОРА (ТОЛЬКО ДЛЯ СУПЕР-АДМИНА)
@router.message(Command("remove_admin"))
async def remove_admin_handler(message: Message):
    if not is_super_admin(message.from_user.id):
        await message.answer("❌ У вас нет прав для этой команды.")
        return

    try:
        user_id = int(message.text.split()[1])
        if is_super_admin(user_id):
            await message.answer("⚠️ Нельзя удалить супер-администратора!")
        else:
            remove_admin(user_id)
            await message.answer(f"✅ Пользователь {user_id} удален из администраторов.")
    except:
        await message.answer("⚠️ Используйте: `/remove_admin user_id`")


### 🔹 СПИСОК АДМИНИСТРАТОРОВ
@router.message(Command("admins"))
async def admins_handler(message: Message):
    if not is_super_admin(message.from_user.id):
        await message.answer("❌ У вас нет прав для этой команды.")
        return

    admins = get_admins()
    if not admins:
        await message.answer("❌ Администраторов нет.")
        return

    text = "👤 Список администраторов:\n"
    for user_id, is_super in admins:
        text += f"• `{user_id}` {'(Супер-админ)' if is_super else ''}\n"

    await message.answer(text)


### 🔹 ОБРАБОТКА КОМАНДЫ "КАТАЛОГ"
@router.message(F.text == "🛍 Каталог")
async def catalog_command(message: Message):
    await message.answer("Вот наш каталог. Выберите товар и укажите его количество:")

    products = get_products()
    for product in products:
        product_id, name, price = product
        keyboard = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="➕ Добавить", callback_data=f"add_{product_id}")],
            [InlineKeyboardButton(text="➖ Убрать", callback_data=f"remove_{product_id}")]
        ])
        await message.answer(f"{name} - {price}₽", reply_markup=keyboard)


### 🔹 ОБРАБОТКА КНОПОК "ДОБАВИТЬ" И "УДАЛИТЬ"
@router.callback_query(F.data.startswith("add_") | F.data.startswith("remove_"))
async def update_cart_handler(callback: CallbackQuery):
    action, product_id = callback.data.split("_")
    product_id = int(product_id)

    if action == "add":
        add_to_cart(callback.from_user.id, product_id)
        await callback.answer("✅ Товар добавлен!")
    elif action == "remove":
        remove_from_cart(callback.from_user.id, product_id)
        await callback.answer("❌ Товар убран!")


### 🔹 ОБРАБОТКА КОМАНДЫ "КОРЗИНА" + КНОПКА "ОБНОВИТЬ"
@router.message(F.text == "🛒 Корзина")
@router.callback_query(F.data == "refresh_cart")
async def cart_command(event):
    user_id = event.from_user.id if isinstance(event, CallbackQuery) else event.from_user.id

    cart_items = get_cart(user_id)

    if not cart_items:
        await event.message.answer("Ваша корзина пуста.")
    else:
        cart_text = "🛒 Ваша корзина:\n"
        total_price = 0
        keyboard_buttons = []

        for name, quantity, price, product_id in cart_items:
            total_price += quantity * price
            cart_text += f"{name} — {quantity} шт. × {price}₽ = {quantity * price}₽\n"

            keyboard_buttons.append([
                InlineKeyboardButton(text="➕", callback_data=f"add_{product_id}"),
                InlineKeyboardButton(text="➖", callback_data=f"remove_{product_id}")
            ])

        cart_text += f"\n💰 Итоговая сумма: {total_price}₽"

        keyboard_buttons.append([
            InlineKeyboardButton(text="🔄 Обновить корзину", callback_data="refresh_cart")
        ])
        keyboard_buttons.append([
            InlineKeyboardButton(text="✅ Оформить заказ", callback_data="checkout")
        ])

        keyboard = InlineKeyboardMarkup(inline_keyboard=keyboard_buttons)

        if isinstance(event, CallbackQuery):
            await event.message.edit_text(cart_text, reply_markup=keyboard)
            await event.answer()
        else:
            await event.answer(cart_text, reply_markup=keyboard)



### 🔹 ОБРАБОТКА "ОФОРМИТЬ ЗАКАЗ"
@router.callback_query(F.data == "checkout")
async def request_contact(callback: CallbackQuery):
    contact_keyboard = ReplyKeyboardMarkup(
        keyboard=[[KeyboardButton(text="📱 Отправить номер", request_contact=True)]],
        resize_keyboard=True,
        one_time_keyboard=True
    )
    await callback.message.answer("📱 Отправьте свой номер телефона для оформления заказа:", reply_markup=contact_keyboard)
    await callback.answer()


### 🔹 ПОЛУЧЕНИЕ НОМЕРА ТЕЛЕФОНА
@router.message(F.contact | F.text.regexp(r'^\+?\d{10,15}$'))
async def receive_contact(message: Message):
    phone_number = message.contact.phone_number if message.contact else message.text
    order_id = create_order(message.from_user.id)

    if order_id:
        await message.answer(f"✅ Заказ #{order_id} оформлен!\n📞 Мы свяжемся с вами по номеру: {phone_number}.")
    else:
        await message.answer("❌ Ваша корзина пуста, добавьте товары перед заказом.")

Вывод:

Теперь у нас есть полноценная система администраторов в Telegram-магазине:
✅ Супер-админ – единственный, кто может управлять администраторами.
✅ Обычные админы – могут управлять товарами и заказами.
✅ Безопасность – супер-админа нельзя удалить.

Следующий шаг – улучшение панели администратора и управление магазином через меню! 🚀

Комментарии

Пока нет комментариев. Будьте первым!

Оставить комментарий

Чтобы оставить комментарий, пожалуйста, войдите или зарегистрируйтесь.