fully functional. UX/UI stage

This commit is contained in:
2024-11-01 19:27:14 +09:00
parent ae95a0c958
commit daa5c49bb9
72 changed files with 480 additions and 996 deletions

View File

@@ -1,162 +1,40 @@
# import telebot
# from decouple import config
# from django.shortcuts import get_object_or_404
# from web.models import Client, ServiceRequest, Order
# from comunication.models import TelegramSettings
# import re
# import base64
# import logging
# class TelegramBot:
# def __init__(self):
# # Get bot settings from the database
# bot_settings = TelegramSettings.objects.first()
# if bot_settings:
# TELEGRAM_BOT_TOKEN = bot_settings.bot_token
# self.bot = telebot.TeleBot(TELEGRAM_BOT_TOKEN)
# else:
# raise Exception("Telegram bot settings not found")
# def start_bot_polling(self):
# @self.bot.message_handler(commands=['start'])
# def send_welcome(message):
# # Проверяем, содержатся ли параметры в команде /start
# match = re.match(r'/start request_(\d+)_token_(.*)', message.text)
# if match:
# self.handle_confirm_command(message, match)
# elif message.text.strip() == '/start':
# self.bot.reply_to(message, "Ошибка: Некорректная команда. Пожалуйста, используйте ссылку, предоставленную на сайте для регистрации.")
# else:
# self.bot.reply_to(message, "Здравствуйте! Чем я могу помочь? Вы можете задать вопросы о статусе заявки или заказе.")
# @self.bot.message_handler(func=lambda message: 'статус заявки' in message.text.lower())
# def handle_service_request_status(message):
# chat_id = message.chat.id
# client = Client.objects.filter(chat_id=chat_id).first()
# if client:
# service_requests = ServiceRequest.objects.filter(client_email=client.email)
# if service_requests.exists():
# response = "Ваши заявки:\n"
# for req in service_requests:
# response += f"Номер заявки: {req.id}, Услуга: {req.service.name}, Дата создания: {req.created_at.strftime('%d-%m-%Y')}\n"
# else:
# response = "У вас нет активных заявок."
# else:
# response = "Клиент не найден. Пожалуйста, зарегистрируйтесь."
# self.bot.reply_to(message, response)
# @self.bot.message_handler(func=lambda message: 'статус заказа' in message.text.lower())
# def handle_order_status(message):
# chat_id = message.chat.id
# client = Client.objects.filter(chat_id=chat_id).first()
# if client:
# orders = Order.objects.filter(client=client)
# if orders.exists():
# response = "Ваши заказы:\n"
# for order in orders:
# response += f"Номер заказа: {order.id}, Услуга: {order.service.name}, Статус: {order.get_status_display()}\n"
# else:
# response = "У вас нет активных заказов."
# else:
# response = "Клиент не найден. Пожалуйста, зарегистрируйтесь."
# self.bot.reply_to(message, response)
# self.bot.polling(non_stop=True)
# def handle_confirm_command(self, message, match=None):
# chat_id = message.chat.id
# if not match:
# match = re.match(r'/start request_(\d+)_token_(.*)', message.text)
# if match:
# request_id = match.group(1)
# encoded_token = match.group(2)
# # Декодируем токен из base64
# try:
# token = base64.urlsafe_b64decode(encoded_token + '==').decode('utf-8')
# logging.info(f"Декодированный токен: {token}")
# except Exception as e:
# logging.error(f"Ошибка при декодировании токена: {e}")
# self.bot.send_message(chat_id, "Ошибка: Некорректный токен. Пожалуйста, повторите попытку позже.")
# return
# # Получаем заявку по ID и токену
# service_request = ServiceRequest.objects.filter(id=request_id, token=token).first()
# if service_request:
# # Обновляем chat_id клиента
# service_request.chat_id = chat_id
# service_request.client_name = message.from_user.first_name
# service_request.save()
# response_message = (
# f"Здравствуйте, {message.from_user.first_name}!\n"
# f"Ваша заявка на услугу успешно зарегистрирована. "
# f"Пожалуйста, вернитесь на сайт для продолжения оформления."
# )
# else:
# response_message = "Ошибка: Неверная заявка или токен. Пожалуйста, проверьте ссылку."
# self.bot.send_message(chat_id, response_message)
# else:
# response_message = "Ошибка: Некорректная команда. Пожалуйста, используйте ссылку, предоставленную на сайте для регистрации."
# self.bot.send_message(chat_id, response_message)
# def send_telegram_message(self, client_id, service_request_id, custom_message, order_id=None):
# # Get the client and service request from the database
# client = get_object_or_404(Client, pk=client_id)
# service_request = get_object_or_404(ServiceRequest, pk=service_request_id)
# chat_id = client.chat_id
# # Build the message content
# message = f"Здравствуйте, {client.first_name} {client.last_name}!\n"
# message += custom_message
# if order_id:
# order = get_object_or_404(Order, pk=order_id)
# message += f"\n\nДетали заказа:\nУслуга: {order.service.name}\nСтатус: {order.get_status_display()}\n"
# # Add service request details
# message += f"\nНомер заявки: {service_request.id}\nДата создания заявки: {service_request.created_at.strftime('%d-%m-%Y')}\n"
# # Send the message using the bot
# try:
# self.bot.send_message(chat_id, message)
# except Exception as e:
# logging.error(f"Ошибка при отправке сообщения в Telegram: {e}")
import telebot
from telebot import types
from decouple import config
from django.shortcuts import get_object_or_404
from web.models import Client, ServiceRequest, Order
from comunication.models import TelegramSettings
import re
import base64
import logging import logging
import json
import requests
import os
import base64
import re
from comunication.models import TelegramSettings
from web.models import ServiceRequest, Order, Project, Client, User
import telebot
from django.shortcuts import get_object_or_404
from django.utils.crypto import get_random_string
class TelegramBot: class TelegramBot:
def __init__(self): def __init__(self):
# Get bot settings from the database # Получение настроек бота из базы данных
bot_settings = TelegramSettings.objects.first() bot_settings = TelegramSettings.objects.first()
if bot_settings: if bot_settings:
TELEGRAM_BOT_TOKEN = bot_settings.bot_token TELEGRAM_BOT_TOKEN = bot_settings.bot_token
self.bot = telebot.TeleBot(TELEGRAM_BOT_TOKEN) self.bot = telebot.TeleBot(TELEGRAM_BOT_TOKEN)
logging.info("[TelegramBot] Бот инициализирован с токеном.")
else: else:
raise Exception("Telegram bot settings not found") raise Exception("Telegram bot settings not found")
def start_bot_polling(self): def start_bot_polling(self):
logging.info("[TelegramBot] Бот начал работу в режиме polling.")
@self.bot.message_handler(commands=['start']) @self.bot.message_handler(commands=['start'])
def send_welcome(message): def send_welcome(message):
# Проверяем, содержатся ли параметры в команде /start # Проверяем, содержатся ли параметры в команде /start
match = re.match(r'/start request_(\d+)_token_(.*)', message.text) match = re.match(r'/start request_(\d+)_token_(.*)', message.text)
logging.info(f"[TelegramBot] Получена команда /start: {message.text}")
if match: if match:
self.handle_confirm_command(message, match) self.handle_confirm_command(message, match)
elif message.text.strip() == '/start': elif message.text.strip() == '/start':
kbd = types.InlineKeyboardMarkup() # Ответ на просто команду /start без параметров
url_btn = types.InlineKeyboardButton('Перейти на сайт', url=config("URL")) self.bot.reply_to(message, "Здравствуйте! Пожалуйста, используйте команду /start с корректными параметрами для подтверждения регистрации.")
kbd.add(url_btn)
self.bot.reply_to(message, "Здравствуйте! Данный бот предназначен для информирования клиентов SmartSolTech и регистрации на сайте. Пройдите на сайт для получения информации.", reply_markup = kbd)
else: else:
self.bot.reply_to(message, "Здравствуйте! Пожалуйста, используйте команду /start с корректными параметрами для подтверждения регистрации.") self.bot.reply_to(message, "Здравствуйте! Пожалуйста, используйте команду /start с корректными параметрами для подтверждения регистрации.")
@@ -165,14 +43,17 @@ class TelegramBot:
chat_id = message.chat.id chat_id = message.chat.id
client = Client.objects.filter(chat_id=chat_id).first() client = Client.objects.filter(chat_id=chat_id).first()
if client: if client:
service_requests = ServiceRequest.objects.filter(chat_id=client.chat_id) service_requests = ServiceRequest.objects.filter(client=client)
if service_requests.exists(): if service_requests.exists():
response = "Ваши заявки:\n" response = "Ваши заявки:\n"
for req in service_requests: for req in service_requests:
response += f"Номер заявки: {req.id}\n" \ response += (
f"Услуга: {req.service.name}\n" \ f"Номер заявки: {req.id}\n"
f"Дата создания: {req.created_at.strftime('%Y-%m-%d')}\n" \ f"Услуга: {req.service.name}\n"
f"Дата создания: {req.created_at.strftime('%Y-%m-%d')}\n"
f"UID заявки: {req.token}\n" f"UID заявки: {req.token}\n"
f"Подтверждена: {'Да' if req.is_verified else 'Нет'}\n\n"
)
else: else:
response = "У вас нет активных заявок." response = "У вас нет активных заявок."
else: else:
@@ -188,19 +69,52 @@ class TelegramBot:
if orders.exists(): if orders.exists():
response = "Ваши заказы:\n" response = "Ваши заказы:\n"
for order in orders: for order in orders:
response += f"Номер заказа: {order.id}, Услуга: {order.service.name}, Статус: {order.get_status_display()}\n" response += (
f"Номер заказа: {order.id}\n"
f"Услуга: {order.service.name}\n"
f"Статус: {order.get_status_display()}\n\n"
)
else: else:
response = "У вас нет активных заказов." response = "У вас нет активных заказов."
else: else:
response = "Клиент не найден. Пожалуйста, зарегистрируйтесь." response = "Клиент не найден. Пожалуйста, зарегистрируйтесь."
self.bot.reply_to(message, response) self.bot.reply_to(message, response)
@self.bot.message_handler(func=lambda message: 'статус проекта' in message.text.lower())
def handle_project_status(message):
chat_id = message.chat.id
client = Client.objects.filter(chat_id=chat_id).first()
if client:
projects = Project.objects.filter(order__client=client)
if projects.exists():
response = "Ваши проекты:\n"
for project in projects:
response += (
f"Номер проекта: {project.id}\n"
f"Название проекта: {project.name}\n"
f"Статус: {project.get_status_display()}\n"
f"Дата завершения: {project.completion_date.strftime('%Y-%m-%d') if project.completion_date else 'В процессе'}\n\n"
)
else:
response = "У вас нет активных проектов."
else:
response = "Клиент не найден. Пожалуйста, зарегистрируйтесь."
self.bot.reply_to(message, response)
# Запуск бота
try:
self.bot.polling(non_stop=True) self.bot.polling(non_stop=True)
except Exception as e:
logging.error(f"[TelegramBot] Ошибка при запуске polling: {e}")
def handle_confirm_command(self, message, match=None): def handle_confirm_command(self, message, match=None):
chat_id = message.chat.id chat_id = message.chat.id
logging.info(f"[TelegramBot] Получено сообщение для подтверждения: {message.text}")
if not match: if not match:
match = re.match(r'/start request_(\d+)_token_(.*)', message.text) match = re.match(r'/start request_(\d+)_token_(.*)', message.text)
if match: if match:
request_id = match.group(1) request_id = match.group(1)
encoded_token = match.group(2) encoded_token = match.group(2)
@@ -208,61 +122,88 @@ class TelegramBot:
# Декодируем токен # Декодируем токен
try: try:
token = base64.urlsafe_b64decode(encoded_token + '==').decode('utf-8') token = base64.urlsafe_b64decode(encoded_token + '==').decode('utf-8')
logging.info(f"[TelegramBot] Декодированный токен: {token}")
except Exception as e: except Exception as e:
logging.error(f"[TelegramBot] Ошибка при декодировании токена: {e}")
self.bot.send_message(chat_id, "Ошибка: Некорректный токен. Пожалуйста, повторите попытку позже.") self.bot.send_message(chat_id, "Ошибка: Некорректный токен. Пожалуйста, повторите попытку позже.")
return return
# Получаем заявку # Получаем заявку
service_request = ServiceRequest.objects.filter(id=request_id, token=token).first() try:
if service_request: service_request = ServiceRequest.objects.get(id=request_id, token=token)
# Обновляем chat_id клиента logging.info(f"[TelegramBot] Заявка найдена: {service_request}")
service_request.chat_id = chat_id except ServiceRequest.DoesNotExist:
service_request.client_name = message.from_user.first_name logging.error(f"[TelegramBot] Заявка с id {request_id} и токеном {token} не найдена.")
service_request.save() self.bot.send_message(chat_id, "Ошибка: Неверная заявка или токен. Пожалуйста, проверьте ссылку.")
return
# Отправляем данные обратно на сервер для создания заявки # Если заявка найдена, обновляем и подтверждаем клиента
data = { if service_request:
"service_request_id": request_id, service_request.chat_id = chat_id
"client_name": message.from_user.first_name, service_request.is_verified = True # Обновляем статус на подтвержденный
"client_chat_id": chat_id service_request.save()
logging.info(f"[TelegramBot] Заявка {service_request.id} подтверждена и обновлена.")
# Обновляем или создаем клиента, связанного с заявкой
client = service_request.client
# Проверяем, существует ли связанный пользователь, если нет — создаем его
if not client.user:
user, created = User.objects.get_or_create(
email=client.email,
defaults={
'username': f"{client.email.split('@')[0]}_{get_random_string(5)}",
'first_name': message.from_user.first_name,
'last_name': message.from_user.last_name if message.from_user.last_name else ''
} }
response = requests.post('http://localhost:8000/service/send_telegram_notification/', json=data) )
if response.status_code == 200:
self.bot.send_message(chat_id, "Ваш аккаунт успешно подтвержден! Вернитесь на сайт для продолжения.") if not created:
# Если пользователь уже существовал, обновляем его данные
user.first_name = message.from_user.first_name
if message.from_user.last_name:
user.last_name = message.from_user.last_name
user.save()
logging.info(f"[TelegramBot] Обновлен пользователь {user.username} с данными из Телеграм.")
# Связываем клиента с пользователем
client.user = user
client.save()
else: else:
self.bot.send_message(chat_id, "Ошибка при подтверждении. Пожалуйста, повторите попытку позже.") # Обновляем данные существующего пользователя
user = client.user
user.first_name = message.from_user.first_name
if message.from_user.last_name:
user.last_name = message.from_user.last_name
user.save()
logging.info(f"[TelegramBot] Пользователь {user.username} обновлен с данными из Телеграм.")
# Обновляем chat_id клиента
client.chat_id = chat_id
client.save()
logging.info(f"[TelegramBot] Клиент {client.id} обновлен с chat_id {chat_id}")
# Отправляем сообщение пользователю в Telegram с подтверждением и информацией о заявке
confirmation_message = (
f"Здравствуйте, {client.first_name}!\n\n"
f"Ваш аккаунт успешно подтвержден! 🎉\n\n"
f"Детали вашей заявки:\n"
f"Номер заявки: {service_request.id}\n"
f"Услуга: {service_request.service.name}\n"
f"Статус заявки: {'Подтверждена' if service_request.is_verified else 'Не подтверждена'}\n"
f"Дата создания: {service_request.created_at.strftime('%Y-%m-%d %H:%M:%S')}\n"
f"Спасибо, что выбрали наши услуги! Если у вас возникнут вопросы, вы всегда можете обратиться к нам."
)
self.bot.send_message(chat_id, confirmation_message)
# Вместо дополнительного POST-запроса — сообщаем о подтверждении через сообщение
self.bot.send_message(chat_id, "Ваш аккаунт успешно подтвержден на сервере! Продолжайте на сайте.")
else: else:
self.bot.send_message(chat_id, "Ошибка: Неверная заявка или токен. Пожалуйста, проверьте ссылку.") self.bot.send_message(chat_id, "Ошибка: Неверная заявка или токен. Пожалуйста, проверьте ссылку.")
self.bot.send_message(chat_id, response_message)
else: else:
response_message = "Ошибка: Некорректная команда. Пожалуйста, используйте ссылку, предоставленную на сайте для регистрации." response_message = "Ошибка: Некорректная команда. Пожалуйста, используйте ссылку, предоставленную на сайте для регистрации."
self.bot.send_message(chat_id, response_message) self.bot.send_message(chat_id, response_message)
def send_telegram_message(self, client_id, service_request_id, custom_message, order_id=None):
# Get the client and service request from the database
client = get_object_or_404(Client, pk=client_id)
service_request = get_object_or_404(ServiceRequest, pk=service_request_id)
chat_id = client.chat_id
# Build the message content
message = f"Здравствуйте, {client.first_name} {client.last_name}!\n"
message += custom_message
if order_id:
order = get_object_or_404(Order, pk=order_id)
message += f"\n\nДетали заказа:\nУслуга: {order.service.name}\nСтатус: {order.get_status_display()}\n"
# Add service request details
message += f"\nНомер заявки: {service_request.id}\nДата создания заявки: {service_request.created_at.strftime('%d-%m-%Y')}\n"
# Send the message using the bot
try:
self.bot.send_message(chat_id, message)
except Exception as e:
logging.error(f"Ошибка при отправке сообщения в Telegram: {e}")
# Example usage:
# bot = TelegramBot()
# bot.start_bot_polling()
# bot.send_telegram_message(client_id=1, service_request_id=1, custom_message="Ваши данные для входа на сайт.")

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,18 @@
// Функция для получения CSRF токена из куков
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
// Инициализация переменной csrftoken, которая будет доступна глобально
const csrftoken = getCookie('csrftoken');

View File

@@ -1,110 +1,49 @@
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
// Работа с модальным окном заявки
var modalElement = document.getElementById('orderModal'); var modalElement = document.getElementById('orderModal');
if (modalElement) { if (modalElement) {
var modal = new bootstrap.Modal(modalElement); var modal = new bootstrap.Modal(modalElement);
// Инициализация модального окна // Инициализация модального окна
modalElement.addEventListener('show.bs.modal', function (event) { modalElement.addEventListener('show.bs.modal', function () {
console.log("Модальное окно открыто"); console.log("Модальное окно открыто");
}); });
modalElement.addEventListener('hide.bs.modal', function (event) { modalElement.addEventListener('hide.bs.modal', function () {
console.log("Модальное окно закрыто"); console.log("Модальное окно закрыто");
}); });
} }
});
document.addEventListener('DOMContentLoaded', function () { // Открытие модального окна для заявки на услугу
const generateQrButton = document.getElementById('generateQrButton'); const openModalBtn = document.getElementById('openModalBtn');
const serviceModal = document.getElementById('serviceModal');
if (generateQrButton) { if (openModalBtn && serviceModal) {
generateQrButton.addEventListener('click', function () { openModalBtn.addEventListener('click', function (event) {
const clientEmail = document.getElementById('clientEmail').value; event.preventDefault();
const clientPhone = document.getElementById('clientPhone').value; const serviceId = openModalBtn.getAttribute('data-service-id');
const clientName = document.getElementById('clientName').value; console.log("Service ID при открытии модального окна:", serviceId);
const description = document.getElementById('description').value;
const serviceId = generateQrButton.getAttribute('data-service-id');
// Проверка заполненности полей if (!serviceId) {
if (!clientEmail || !clientPhone || !clientName || !description || !serviceId) { alert("Идентификатор услуги не найден. Обновите страницу и попробуйте снова.");
alert('Все поля должны быть заполнены.');
return; return;
} }
// Получение CSRF токена из cookies generateQrButton.dataset.serviceId = serviceId;
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken'); serviceModal.classList.add('show');
serviceModal.style.display = 'block';
// Отправка POST запроса на создание заявки
fetch('/service/create_request/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
},
body: JSON.stringify({
client_email: clientEmail,
client_phone: clientPhone,
client_name: clientName,
service_id: serviceId,
description: description
})
})
.then(response => {
if (!response.ok) {
throw new Error('Ошибка при создании заявки');
}
return response.json();
})
.then(data => {
if (data.status === 'success') {
alert(data.message);
} else if (data.status === 'existing_request') {
alert(data.message);
} else {
alert('Неизвестная ошибка. Пожалуйста, попробуйте снова.');
}
})
.catch(error => {
console.error('Ошибка при создании заявки:', error);
});
}); });
} }
});
function checkVerificationStatus(serviceRequestId, interval) { document.querySelectorAll('.close').forEach(closeBtn => {
fetch(`/service/request_status/${serviceRequestId}/`) closeBtn.addEventListener('click', function () {
.then(response => { if (serviceModal) {
if (!response.ok) { serviceModal.classList.remove('show');
throw new Error('Ошибка при проверке статуса заявки'); setTimeout(() => {
serviceModal.style.display = 'none';
}, 500);
} }
return response.json();
})
.then(data => {
if (data.is_verified) {
// Закрываем форму и показываем окно подтверждения
document.getElementById('serviceModal').style.display = 'none';
document.getElementById('confirmationModal').style.display = 'block';
// Останавливаем интервал проверки статуса
clearInterval(interval);
}
})
.catch(error => {
console.error('Ошибка при проверке статуса заявки:', error);
}); });
} });
});

View File

@@ -0,0 +1,116 @@
// service-request.js
document.addEventListener("DOMContentLoaded", function () {
// Открытие модального окна
const openModalBtn = document.getElementById('openModalBtn');
const serviceModal = document.getElementById('serviceModal');
const generateQrButton = document.getElementById('generateQrButton');
if (openModalBtn && serviceModal) {
openModalBtn.addEventListener('click', function (event) {
event.preventDefault();
// Логирование значения serviceId при открытии модального окна
const serviceId = openModalBtn.getAttribute('data-service-id');
console.log("Service ID при открытии модального окна:", serviceId);
// Проверяем, если serviceId отсутствует
if (!serviceId) {
alert("Идентификатор услуги не найден. Обновите страницу и попробуйте снова.");
return;
}
// Сохраняем serviceId для дальнейшего использования
generateQrButton.dataset.serviceId = serviceId;
// Показываем модальное окно
serviceModal.classList.add('show');
serviceModal.style.display = 'block';
});
} else {
console.error('Не удалось найти элемент с id openModalBtn или serviceModal.');
}
// Закрытие модального окна
document.querySelectorAll('.close').forEach(closeBtn => {
closeBtn.addEventListener('click', function () {
if (serviceModal) {
serviceModal.classList.remove('show');
setTimeout(() => {
serviceModal.style.display = 'none';
}, 500);
}
});
});
// Обработчик кнопки "Создать заявку"
if (generateQrButton) {
generateQrButton.addEventListener('click', function () {
// Получение значений полей
const clientEmail = document.getElementById('clientEmail').value.trim();
const clientPhone = document.getElementById('clientPhone').value.trim();
const clientName = document.getElementById('clientName').value.trim();
const description = document.getElementById('description').value.trim();
// Получаем serviceId из кнопки открытия модального окна
const serviceId = generateQrButton.dataset.serviceId;
// Логируем для проверки значения serviceId перед отправкой
console.log("Service ID перед отправкой запроса:", serviceId);
// Проверка заполненности полей
if (!clientEmail || !clientPhone || !clientName || !description || !serviceId) {
let errorMessage = 'Пожалуйста, заполните все поля формы перед продолжением.\n';
if (!serviceId) {
errorMessage += 'Идентификатор услуги не найден. Обновите страницу и попробуйте снова.\n';
}
alert(errorMessage);
return;
}
// Отправка POST запроса на создание заявки
fetch(`/service/generate_qr_code/${serviceId}/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken // Используем глобально инициализированный CSRF токен
},
body: JSON.stringify({
client_email: clientEmail,
client_phone: clientPhone,
client_name: clientName,
description: description
})
})
.then(response => {
if (!response.ok) {
throw new Error('Ошибка при создании заявки');
}
return response.json();
})
.then(data => {
if (data.qr_code_url) {
// Обновляем src изображения QR-кода и показываем его
const qrCodeImg = document.getElementById('qrCodeImg');
if (qrCodeImg) {
qrCodeImg.src = data.qr_code_url;
qrCodeImg.style.display = 'block';
}
// Начинаем проверку статуса заявки
const interval = setInterval(() => {
checkVerificationStatus(data.service_request_id, interval);
}, 5000);
} else if (data.status === 'existing_request') {
alert(data.message);
} else {
alert('Неизвестная ошибка. Пожалуйста, попробуйте снова.');
}
})
.catch(error => {
console.error('Ошибка при создании заявки:', error);
});
});
} else {
console.error('Не удалось найти элемент с id generateQrButton.');
}
});

View File

@@ -0,0 +1,37 @@
// verification-status.js
function checkVerificationStatus(serviceRequestId, interval) {
console.log(`Проверка статуса для заявки с ID: ${serviceRequestId}`); // Лог для проверки
fetch(`/service/request_status/${serviceRequestId}/`)
.then(response => {
if (!response.ok) {
throw new Error('Ошибка при проверке статуса заявки');
}
return response.json();
})
.then(data => {
if (data.is_verified) {
// Закрываем форму и показываем окно подтверждения
const serviceModal = document.getElementById('serviceModal');
const confirmationModal = document.getElementById('confirmationModal');
if (serviceModal) {
serviceModal.style.display = 'none';
}
if (confirmationModal) {
confirmationModal.style.display = 'block';
}
// Останавливаем интервал проверки статуса
clearInterval(interval);
}
})
.catch(error => {
console.error('Ошибка при проверке статуса заявки:', error);
});
}
// Делаем функцию доступной глобально
window.checkVerificationStatus = checkVerificationStatus;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,22 @@
# Generated by Django 5.1.1 on 2024-11-01 04:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0008_alter_servicerequest_client'),
]
operations = [
migrations.AlterModelOptions(
name='servicerequest',
options={'ordering': ['-is_verified', '-created_at'], 'verbose_name': 'Заявка на услугу', 'verbose_name_plural': 'Заявки на услуги'},
),
migrations.AddField(
model_name='servicerequest',
name='is_verified',
field=models.BooleanField(default=False),
),
]

View File

@@ -76,11 +76,12 @@ class ServiceRequest(models.Model):
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
token = models.UUIDField(default=uuid.uuid4, unique=True) # Генерация уникального токена token = models.UUIDField(default=uuid.uuid4, unique=True) # Генерация уникального токена
chat_id = models.CharField(max_length=100, blank=True, null=True) # Telegram chat ID chat_id = models.CharField(max_length=100, blank=True, null=True) # Telegram chat ID
is_verified = models.BooleanField(default=False)
class Meta: class Meta:
verbose_name = 'Заявка на услугу' verbose_name = 'Заявка на услугу'
verbose_name_plural = 'Заявки на услуги' verbose_name_plural = 'Заявки на услуги'
ordering = ['-created_at'] ordering = ['-is_verified', '-created_at']
def __str__(self): def __str__(self):
return f"Request for {self.service.name} by {self.client.first_name}" return f"Request for {self.service.name} by {self.client.first_name}"
@@ -150,5 +151,5 @@ class Review(models.Model):
ordering = ['-review_date'] ordering = ['-review_date']
def __str__(self): def __str__(self):
return f"Review by {self.client.first_name} {self.client.last_name} for {self.service.name}" return f"Отзыв от {self.client.first_name} {self.client.last_name} for {self.service.name}"

View File

@@ -1,64 +0,0 @@
<!-- web/templates/web/modal_order_form.html -->
<div id="orderModal" class="modal fade" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Оформление заявки на услугу</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="orderForm" method="post" action="{% url 'create_service_request' service_id=service.pk %}">
{% csrf_token %}
<div class="form-group mb-3">
<label class="form-label">Имя</label>
<input id="client_name" class="form-control" type="text" name="client_name" required minlength="2" maxlength="50" />
</div>
<div class="form-group mb-3">
<label class="form-label">Телефон</label>
<input id="client_phone" class="form-control" type="tel" name="client_phone" required pattern="^\+?[0-9\s\-]{7,15}$" />
</div>
<div class="form-group mb-3">
<label class="form-label">Адрес электронной почты</label>
<input id="client_email" class="form-control" type="email" name="client_email" required />
</div>
<div class="form-group mb-3 text-center">
<label class="form-label">Сканируйте QR код для регистрации в Telegram боте</label>
<div id="qrCodeContainer">
<img src="{{ qr_code }}" alt="QR код для Telegram бота" class="img-fluid" />
</div>
<div class="form-group mt-3">
<a href="{{ registration_link }}" target="_blank">Перейдите по этой ссылке для регистрации в Telegram боте</a>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary" id="submitButton" disabled>Отправить заявку</button>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const checkVerificationStatus = () => {
fetch(`/service/request_status/{{ service_request_id }}/`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
if (data.is_verified) {
document.getElementById('submitButton').disabled = false;
}
})
.catch(error => console.error('Ошибка при проверке статуса:', error));
};
setInterval(checkVerificationStatus, 5000);
});
</script>

View File

@@ -1,214 +0,0 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<title>Модальное окно для заявки на услугу</title>
<style>
/* Стили для модального окна */
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 600px;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
#qrCodeImg {
display: block;
margin: 20px auto;
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<!-- Модальное окно -->
<div id="serviceModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h4>Заполните заявку на услугу</h4>
<p>QR-код для завершения регистрации:</p>
<img id="qrCodeImg" src="" alt="QR Code">
<form id="serviceRequestForm">
<div class="form-group">
<label for="clientName">Ваше имя:</label>
<input type="text" class="form-control" id="clientName" name="client_name" placeholder="Введите ваше имя" required minlength="2" maxlength="50" readonly>
</div>
<div class="form-group">
<label for="clientChatId">Ваш chat ID:</label>
<input type="text" class="form-control" id="clientChatId" name="client_chat_id" placeholder="Ваш chat ID" readonly>
</div>
<div class="form-group">
<label for="clientEmail">Ваш email:</label>
<input type="email" class="form-control" id="clientEmail" name="client_email" placeholder="Введите ваш email" required>
</div>
<div class="form-group">
<label for="clientPhone">Ваш телефон:</label>
<input type="text" class="form-control" id="clientPhone" name="client_phone" placeholder="Введите ваш телефон" required pattern="^\+?[0-9\s\-]{7,15}$">
</div>
<div class="form-group">
<label for="description">Описание заявки:</label>
<textarea class="form-control" id="description" name="description" placeholder="Опишите вашу заявку" required></textarea>
</div>
<button type="submit" class="btn btn-success" id="submitButton" disabled>Отправить заявку</button>
</form>
</div>
</div>
<script>
// Обработчик открытия модального окна
document.getElementById('openModalBtn').addEventListener('click', function () {
const serviceId = this.getAttribute('data-service-id');
// Открываем модальное окно
document.getElementById('serviceModal').style.display = 'block';
// Выполняем запрос на генерацию QR-кода
fetch(`/service/generate_qr_code/${serviceId}/`)
.then(response => response.json())
.then(data => {
// Обновляем src изображения QR-кода
document.getElementById('qrCodeImg').src = data.qr_code_url;
// Запуск проверки статуса каждые 5 секунд
const interval = setInterval(() => {
checkVerificationStatus(data.service_request_id, interval);
}, 5000);
})
.catch(error => console.error('Ошибка при генерации QR-кода:', error));
});
// Обработчик закрытия модального окна
document.querySelector('.close').addEventListener('click', function () {
document.getElementById('serviceModal').style.display = 'none';
});
// Обработчик отправки формы
document.getElementById('serviceRequestForm').addEventListener('submit', function (event) {
event.preventDefault();
const formData = new FormData(this);
const serviceId = document.getElementById('openModalBtn').getAttribute('data-service-id');
// Отправка данных формы на сервер
fetch('/service/request/' + serviceId + '/', {
method: 'POST',
body: formData
}).then(response => {
if (response.ok) {
alert('Заявка успешно отправлена!');
document.getElementById('serviceModal').style.display = 'none';
// Отправка сообщения в Telegram
sendTelegramNotification(formData);
} else {
alert('Ошибка при отправке заявки. Пожалуйста, попробуйте снова.');
}
}).catch(error => console.error('Ошибка при отправке данных формы:', error));
});
// Проверка статуса заявки на наличие подтверждения Telegram
const checkVerificationStatus = (serviceRequestId, interval) => {
fetch(`/service/request_status/${serviceRequestId}/`)
.then(response => response.json())
.then(data => {
if (data.is_verified) {
// Заполнение полей формы данными пользователя
document.getElementById('clientName').value = data.client_name;
document.getElementById('clientChatId').value = data.client_chat_id;
// Активируем кнопку отправки, если все поля заполнены
updateButtonState();
// Останавливаем интервал проверки статуса
clearInterval(interval);
}
})
.catch(error => console.error('Ошибка при проверке статуса заявки:', error));
};
// Код для активации кнопки "Отправить" при заполнении всех полей
const clientEmail = document.getElementById('clientEmail');
const clientPhone = document.getElementById('clientPhone');
const clientName = document.getElementById('clientName');
const description = document.getElementById('description');
const clientChatId = document.getElementById('clientChatId');
const submitButton = document.getElementById('submitButton');
const updateButtonState = () => {
if (clientEmail.value && clientPhone.value && clientName.value && description.value && clientChatId.value) {
submitButton.disabled = false;
} else {
submitButton.disabled = true;
}
};
// Привязка событий к полям для обновления состояния кнопки отправки
clientEmail.addEventListener('input', updateButtonState);
clientPhone.addEventListener('input', updateButtonState);
description.addEventListener('input', updateButtonState);
// Удаление placeholder при установке фокуса на поле
document.querySelectorAll('input, textarea').forEach(field => {
field.addEventListener('focus', function () {
this.dataset.placeholder = this.placeholder;
this.placeholder = '';
});
field.addEventListener('blur', function () {
this.placeholder = this.dataset.placeholder;
});
});
// Функция для отправки уведомления в Telegram
const sendTelegramNotification = (formData) => {
const clientName = formData.get('client_name');
const serviceDescription = formData.get('description');
const chatId = formData.get('client_chat_id');
const message = `Здравствуйте, ${clientName}! Ваша заявка успешно зарегистрирована. Детали: ${serviceDescription}`;
fetch('/service/send_telegram_notification/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ chat_id: chatId, message: message })
}).then(response => {
if (response.ok) {
console.log('Уведомление успешно отправлено в Telegram');
} else {
console.error('Ошибка при отправке уведомления в Telegram');
}
}).catch(error => console.error('Ошибка при отправке уведомления в Telegram:', error));
};
</script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

View File

@@ -20,6 +20,7 @@
</div> </div>
</div> </div>
<!-- Модальное окно для создания заявки на услугу -->
{% include "web/modal_order_form.html" %} {% include "web/modal_order_form.html" %}
<div class="container py-4"> <div class="container py-4">
@@ -78,174 +79,10 @@
</div> </div>
</div> </div>
<style> <!-- Подключение JavaScript файлов, вынесенных в директорию static/assets/js -->
/* Добавляем стили для анимации появления модального окна */ <script src="{% static 'assets/js/get-csrf-token.js' %}"></script>
#serviceModal { <script src="{% static 'assets/js/modal-init.js' %}"></script>
display: none; <script src="{% static 'assets/js/service_request.js' %}"></script>
position: fixed; <script src="{% static 'assets/js/verification_status.js' %}"></script>
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
position: relative;
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 600px;
transform: scale(0);
transition: transform 0.5s ease;
}
.modal.show .modal-content {
transform: scale(1);
}
</style>
<script <script src="{% static 'assets/js/modal-init.js' %}"> </script>
<script>
function checkVerificationStatus(serviceRequestId, interval) {
fetch(`/service/request_status/${serviceRequestId}/`)
.then(response => {
if (!response.ok) {
throw new Error('Ошибка при проверке статуса заявки');
}
return response.json();
})
.then(data => {
if (data.is_verified) {
// Закрываем форму и показываем окно подтверждения
document.getElementById('serviceModal').style.display = 'none';
document.getElementById('confirmationModal').style.display = 'block';
// Останавливаем интервал проверки статуса
clearInterval(interval);
}
})
.catch(error => {
console.error('Ошибка при проверке статуса заявки:', error);
});
}
document.addEventListener('DOMContentLoaded', function () {
// Получение CSRF токена
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
// Открытие модального окна
const openModalBtn = document.getElementById('openModalBtn');
const serviceModal = document.getElementById('serviceModal');
if (openModalBtn) {
openModalBtn.addEventListener('click', function (event) {
event.preventDefault(); // Предотвращаем отправку формы при нажатии на кнопку
serviceModal.classList.add('show');
serviceModal.style.display = 'block';
});
} else {
console.error('Не удалось найти элемент с id openModalBtn.');
}
// Закрытие модальных окон
document.querySelectorAll('.close').forEach(closeBtn => {
closeBtn.addEventListener('click', function () {
serviceModal.classList.remove('show');
setTimeout(() => {
serviceModal.style.display = 'none';
}, 500); // Ждем, пока завершится анимация закрытия
});
});
// Обработчик кнопки "Продолжить" для генерации QR-кода
const generateQrButton = document.getElementById('generateQrButton');
if (generateQrButton) {
generateQrButton.addEventListener('click', function () {
const clientEmail = document.getElementById('clientEmail').value;
const clientPhone = document.getElementById('clientPhone').value;
const clientName = document.getElementById('clientName').value;
const description = document.getElementById('description').value;
// Проверяем, чтобы все поля были заполнены
if (!clientEmail || !clientPhone || !clientName || !description) {
alert('Все поля должны быть заполнены.');
return;
}
// Выполняем валидацию телефона перед продолжением
let phonePattern = /^\+?[0-9\s\-]{7,15}$/;
if (!phonePattern.test(clientPhone)) {
alert('Введите правильный номер телефона.');
return; // Останавливаем выполнение, если телефон не валиден
}
// Получаем serviceId из кнопки открытия модального окна
const serviceId = openModalBtn.getAttribute('data-service-id');
if (!serviceId) {
console.error('Не удалось найти идентификатор услуги');
return;
}
// Запрос на генерацию QR-кода с использованием метода POST
fetch(`/service/generate_qr_code/${serviceId}/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken // Добавляем CSRF токен в заголовок
},
body: JSON.stringify({
client_email: clientEmail,
client_phone: clientPhone,
client_name: clientName,
description: description
})
})
.then(response => {
if (!response.ok) {
throw new Error('Ошибка при генерации QR-кода');
}
return response.json();
})
.then(data => {
if (!data.qr_code_url) {
throw new Error('Ответ не содержит URL QR-кода');
}
// Обновляем src изображения QR-кода и показываем его
const qrCodeImg = document.getElementById('qrCodeImg');
qrCodeImg.src = data.qr_code_url;
qrCodeImg.style.display = 'block';
// Запуск проверки статуса каждые 5 секунд
const interval = setInterval(() => {
checkVerificationStatus(data.service_request_id, interval, clientEmail, clientPhone, description);
}, 5000);
})
.catch(error => {
console.error('Ошибка при генерации QR-кода:', error);
});
});
}
});
</script>
{% endblock %} {% endblock %}

View File

@@ -17,12 +17,12 @@ urlpatterns = [
path('service/request_status/<int:service_id>/', views.request_status, name='request_status'), path('service/request_status/<int:service_id>/', views.request_status, name='request_status'),
path('service/request/<int:service_id>/', views.create_service_request, name='create_service_request'), path('service/request/<int:service_id>/', views.create_service_request, name='create_service_request'),
path('complete_registration/<int:request_id>/', views.complete_registration, name='complete_registration'), path('complete_registration/<int:request_id>/', views.complete_registration, name='complete_registration'),
path('complete_registration/', views.complete_registration_basic, name='complete_registration_basic'), # path('complete_registration/', views.complete_registration_basic, name='complete_registration_basic'),
path('service/check_service_request_data/', views.check_service_request_data, name='check_service_request_data'), # path('service/check_service_request_data/', views.check_service_request_data, name='check_service_request_data'),
path('client/orders/', views.client_orders, name='client_orders'), # path('client/orders/', views.client_orders, name='client_orders'),
path('order/<int:pk>/', views.order_detail, name='order_detail'), # path('order/<int:pk>/', views.order_detail, name='order_detail'),
path('service/send_telegram_notification/', views.send_telegram_notification, name='send_telegram_notification'), path('service/send_telegram_notification/', views.send_telegram_notification, name='send_telegram_notification'),
path('service/create_request/', views.create_service_request_basic, name='create_service_request_basic'), # path('service/create_request/', views.create_service_request_basic, name='create_service_request_basic'),
] ]
if settings.DEBUG: if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -9,7 +9,7 @@ import uuid
from django.utils.http import urlsafe_base64_encode from django.utils.http import urlsafe_base64_encode
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
from django.http import JsonResponse from django.http import JsonResponse
from django.utils.crypto import get_random_string # Импорт get_random_string from django.utils.crypto import get_random_string
from django.contrib.auth.models import User from django.contrib.auth.models import User
from decouple import config from decouple import config
from django.urls import reverse from django.urls import reverse
@@ -21,13 +21,16 @@ import hashlib
import json import json
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
import logging import logging
from django.db import transaction, IntegrityError
from django.db.models import Q
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# sens
# Initialize Telegram Bot
try: try:
bot = TelegramBot() bot = TelegramBot()
except Exception as e: except Exception as e:
print (e) logger.error(f"Failed to initialize Telegram bot: {str(e)}")
def home(request): def home(request):
services = Service.objects.all() services = Service.objects.all()
@@ -69,21 +72,18 @@ def about_view(request):
def create_service_request(request, service_id): def create_service_request(request, service_id):
if request.method == 'POST': if request.method == 'POST':
try: try:
# Извлечение данных из запроса
data = json.loads(request.body) data = json.loads(request.body)
client_email = data.get('client_email') client_email = data.get('client_email')
client_phone = data.get('client_phone') client_phone = data.get('client_phone')
client_name = data.get('client_name') client_name = data.get('client_name')
# Проверка на наличие всех необходимых данных
if not all([client_email, client_phone, client_name]): if not all([client_email, client_phone, client_name]):
return JsonResponse({'status': 'error', 'message': 'Все поля должны быть заполнены'}, status=400) return JsonResponse({'status': 'error', 'message': 'Все поля должны быть заполнены'}, status=400)
# Получение услуги
service = get_object_or_404(Service, pk=service_id) service = get_object_or_404(Service, pk=service_id)
# Создаем или получаем клиента # Проверяем наличие клиента по email (так как email должен быть уникальным)
client, created = Client.objects.get_or_create( client, client_created = Client.objects.get_or_create(
email=client_email, email=client_email,
defaults={ defaults={
'first_name': client_name.split()[0] if client_name else "", 'first_name': client_name.split()[0] if client_name else "",
@@ -92,27 +92,28 @@ def create_service_request(request, service_id):
} }
) )
# Обновляем данные клиента, если он уже существовал # Обновляем данные клиента, если он уже существовал (например, телефон или имя изменились)
if not created: if not client_created:
client.first_name = client_name.split()[0] client.first_name = client_name.split()[0]
client.last_name = client_name.split()[-1] if len(client_name.split()) > 1 else "" client.last_name = client_name.split()[-1] if len(client_name.split()) > 1 else ""
client.phone_number = client_phone client.phone_number = client_phone
client.save() client.save()
# Проверяем, есть ли у клиента уже активная заявка # Проверяем наличие заявки на эту же услугу, не завершенной и не подтвержденной
existing_requests = ServiceRequest.objects.filter(client=client, service=service, chat_id__isnull=True) existing_requests = ServiceRequest.objects.filter(client=client, service=service, is_verified=False)
if existing_requests.exists(): if existing_requests.exists():
return JsonResponse({ return JsonResponse({
'status': 'existing_request', 'status': 'existing_request',
'message': 'У вас уже есть активная заявка на данную услугу. Пожалуйста, проверьте ваш Telegram для завершения процесса.' 'message': 'У вас уже есть активная заявка на данную услугу. Пожалуйста, проверьте ваш Telegram для завершения процесса.'
}) })
# Создание новой заявки на услугу # Создаем новую заявку для клиента
token = uuid.uuid4().hex token = uuid.uuid4().hex
service_request = ServiceRequest.objects.create( service_request = ServiceRequest.objects.create(
service=service, service=service,
client=client, client=client,
token=token token=token,
is_verified=False
) )
return JsonResponse({ return JsonResponse({
@@ -122,94 +123,8 @@ def create_service_request(request, service_id):
}) })
except json.JSONDecodeError: except json.JSONDecodeError:
logger.error("Invalid JSON format")
return JsonResponse({'status': 'error', 'message': 'Неверный формат данных'}, status=400) return JsonResponse({'status': 'error', 'message': 'Неверный формат данных'}, status=400)
return JsonResponse({'status': 'error', 'message': 'Метод запроса должен быть POST'}, status=405)
def create_service_request_basic(request):
if request.method == 'POST':
try:
# Извлечение данных из тела запроса
data = json.loads(request.body)
client_email = data.get('client_email')
client_phone = data.get('client_phone')
client_name = data.get('client_name')
service_id = data.get('service_id')
description = data.get('description')
# Проверка на наличие всех необходимых данных
if not all([client_email, client_phone, client_name, service_id, description]):
return JsonResponse({'status': 'error', 'message': 'Все поля должны быть заполнены'}, status=400)
# Получаем услугу по ID
service = get_object_or_404(Service, pk=service_id)
# Проверка на существование активной заявки для клиента на данную услугу
existing_requests = ServiceRequest.objects.filter(client__email=client_email, service=service, chat_id__isnull=True)
if existing_requests.exists():
return JsonResponse({
'status': 'existing_request',
'message': 'У вас уже есть активная заявка на данную услугу. Пожалуйста, проверьте ваш Telegram для завершения процесса.'
})
# Создаем или получаем клиента
user, created = User.objects.get_or_create(
username=f"{client_email.split('@')[0]}_{get_random_string(5)}",
defaults={"email": client_email}
)
# Обновляем данные пользователя, если он уже существовал
user.first_name = client_name.split()[0] if client_name else ""
user.last_name = client_name.split()[-1] if len(client_name.split()) > 1 else ""
user.save()
# Создаем или получаем объект клиента
client, _ = Client.objects.get_or_create(
email=client_email,
defaults={
'user': user,
'first_name': user.first_name,
'last_name': user.last_name,
'phone_number': client_phone,
}
)
# Создаем заявку на услугу
token = uuid.uuid4().hex
service_request = ServiceRequest.objects.create(
service=service,
client=client,
token=token
)
# Создаем заказ на основе заявки
order = Order.objects.create(
service_request=service_request,
client=client,
service=service,
message=description,
status="pending"
)
# Отправляем уведомление в Telegram, если chat_id у клиента заполнен
if client.chat_id:
# Предполагается, что bot.send_telegram_message() уже настроен
bot.send_telegram_message(
client.chat_id,
f"Ваш заказ на услугу '{service.name}' был успешно создан. Пожалуйста, завершите процесс регистрации в Telegram."
)
return JsonResponse({
'status': 'success',
'message': 'Заявка успешно создана. Пожалуйста, проверьте ваш Telegram для подтверждения.',
'service_request_id': service_request.id,
})
except json.JSONDecodeError:
return JsonResponse({'status': 'error', 'message': 'Неверный формат данных'}, status=400)
except Exception as e:
return JsonResponse({'status': 'error', 'message': str(e)}, status=500)
return JsonResponse({'status': 'error', 'message': 'Метод запроса должен быть POST'}, status=405) return JsonResponse({'status': 'error', 'message': 'Метод запроса должен быть POST'}, status=405)
def generate_qr_code(request, service_id): def generate_qr_code(request, service_id):
@@ -220,26 +135,27 @@ def generate_qr_code(request, service_id):
client_phone = data.get('client_phone') client_phone = data.get('client_phone')
client_name = data.get('client_name') client_name = data.get('client_name')
# Проверка на наличие всех необходимых данных
if not all([client_email, client_phone, client_name]): if not all([client_email, client_phone, client_name]):
logger.error("Не все поля заполнены.") logger.error("Все поля должны быть заполнены")
return JsonResponse({'error': 'Все поля должны быть заполнены'}, status=400) return JsonResponse({'error': 'Все поля должны быть заполнены'}, status=400)
except json.JSONDecodeError as e: # Используем транзакцию для предотвращения конкурентного создания дубликатов
logger.error(f"Ошибка JSONDecodeError: {str(e)}") with transaction.atomic():
return JsonResponse({'error': 'Неверный формат данных'}, status=400) user, user_created = User.objects.select_for_update().get_or_create(
email=client_email,
# Создание или получение клиента defaults={
try: "username": f"{client_email.split('@')[0]}_{get_random_string(5)}",
user, created = User.objects.get_or_create( "first_name": client_name.split()[0] if client_name else "",
username=f"{client_email.split('@')[0]}_{get_random_string(5)}", "last_name": client_name.split()[-1] if len(client_name.split()) > 1 else ""
defaults={"email": client_email} }
) )
if not user_created:
# Обновляем информацию о пользователе, если он уже существует
user.first_name = client_name.split()[0] if client_name else "" user.first_name = client_name.split()[0] if client_name else ""
user.last_name = client_name.split()[-1] if len(client_name.split()) > 1 else "" user.last_name = client_name.split()[-1] if len(client_name.split()) > 1 else ""
user.save() user.save()
client, _ = Client.objects.get_or_create( client, client_created = Client.objects.select_for_update().get_or_create(
email=client_email, email=client_email,
defaults={ defaults={
'user': user, 'user': user,
@@ -248,34 +164,37 @@ def generate_qr_code(request, service_id):
'phone_number': client_phone 'phone_number': client_phone
} }
) )
except Exception as e:
logger.error(f"Ошибка при создании или получении клиента: {str(e)}")
return JsonResponse({'error': f'Ошибка при создании клиента: {str(e)}'}, status=500)
# Создание новой заявки на услугу if not client_created:
try: # Обновляем информацию о клиенте, если он уже существует
client.first_name = user.first_name
client.last_name = user.last_name
client.phone_number = client_phone
client.save()
# Проверка на существование активной заявки
service = get_object_or_404(Service, pk=service_id) service = get_object_or_404(Service, pk=service_id)
token = uuid.uuid4().hex existing_requests = ServiceRequest.objects.filter(client=client, service=service, is_verified=False)
if existing_requests.exists():
return JsonResponse({
'status': 'existing_request',
'message': 'У вас уже есть активная заявка на данную услугу. Пожалуйста, проверьте ваш Telegram для завершения процесса.'
})
# Создаем новую заявку на услугу
token = uuid.uuid4().hex
service_request = ServiceRequest.objects.create( service_request = ServiceRequest.objects.create(
service=service, service=service,
client=client, client=client,
token=token token=token,
is_verified=False
) )
except Exception as e: logger.info(f"Создана новая заявка: {service_request.id} для клиента: {client.email}")
logger.error(f"Ошибка при создании заявки: {str(e)}")
return JsonResponse({'error': f'Ошибка при создании заявки: {str(e)}'}, status=500)
# Генерация ссылки для регистрации в Telegram # Генерация ссылки и QR-кода для Telegram
try:
telegram_settings = get_object_or_404(TelegramSettings, pk=1) telegram_settings = get_object_or_404(TelegramSettings, pk=1)
registration_link = f'https://t.me/{telegram_settings.bot_name}?start=request_{service_request.id}_token_{urlsafe_base64_encode(force_bytes(token))}' registration_link = f'https://t.me/{telegram_settings.bot_name}?start=request_{service_request.id}_token_{urlsafe_base64_encode(force_bytes(token))}'
except TelegramSettings.DoesNotExist:
logger.error("Не удалось получить настройки Telegram.")
return JsonResponse({'error': 'Не удалось получить настройки Telegram'}, status=500)
# Генерация QR-кода
try:
qr = qrcode.make(registration_link) qr = qrcode.make(registration_link)
qr_code_dir = os.path.join(settings.STATICFILES_DIRS[0], 'qr_codes') qr_code_dir = os.path.join(settings.STATICFILES_DIRS[0], 'qr_codes')
qr_code_path = os.path.join(qr_code_dir, f"request_{service_request.id}.png") qr_code_path = os.path.join(qr_code_dir, f"request_{service_request.id}.png")
@@ -285,11 +204,14 @@ def generate_qr_code(request, service_id):
os.makedirs(qr_code_dir) os.makedirs(qr_code_dir)
qr.save(qr_code_path) qr.save(qr_code_path)
except Exception as e:
logger.error(f"Ошибка при генерации QR-кода: {str(e)}")
return JsonResponse({'error': f'Ошибка при генерации QR-кода: {str(e)}'}, status=500)
# Возвращаем ответ, включающий все необходимые данные except IntegrityError as e:
logger.error(f"Ошибка целостности данных при создании пользователя или клиента: {str(e)}")
return JsonResponse({'error': 'Ошибка при обработке данных. Пожалуйста, попробуйте позже.'}, status=500)
except Exception as e:
logger.error(f"Ошибка при обработке запроса: {str(e)}")
return JsonResponse({'error': f'Ошибка: {str(e)}'}, status=500)
return JsonResponse({ return JsonResponse({
'registration_link': registration_link, 'registration_link': registration_link,
'qr_code_url': f"/{external_qr_link}", 'qr_code_url': f"/{external_qr_link}",
@@ -298,104 +220,57 @@ def generate_qr_code(request, service_id):
'client_phone': client_phone, 'client_phone': client_phone,
'client_name': client_name 'client_name': client_name
}) })
else:
logger.error("Неправильный метод запроса")
return JsonResponse({'error': 'Метод запроса должен быть POST'}, status=405) return JsonResponse({'error': 'Метод запроса должен быть POST'}, status=405)
def complete_registration(request, request_id):
# Завершение регистрации по идентификатору заявки
service_request = get_object_or_404(ServiceRequest, pk=request_id)
if request.method == 'POST':
client_email = request.POST.get('client_email', service_request.client.email)
client_phone = request.POST.get('client_phone', service_request.client.phone_number)
chat_id = request.POST.get('chat_id', service_request.chat_id)
# Проверка корректности данных
if not all([client_email, client_phone, chat_id]):
return JsonResponse({'status': 'error', 'message': 'Все поля должны быть заполнены.'}, status=400)
# Обновляем данные клиента
client = service_request.client
client.email = client_email
client.phone_number = client_phone
client.save()
# Обновляем заявку
service_request.chat_id = chat_id
service_request.save()
return JsonResponse({'status': 'success', 'message': 'Регистрация успешно завершена.'})
return render(request, 'web/complete_registration.html', {'service_request': service_request})
return render(request, 'web/complete_registration.html', {'service_request': service_request})
def request_status(request, service_id): def request_status(request, service_id):
try: try:
# Получаем заявку на услугу по ID
service_request = get_object_or_404(ServiceRequest, pk=service_id) service_request = get_object_or_404(ServiceRequest, pk=service_id)
# Получаем объект клиента через связанное поле client
client = service_request.client client = service_request.client
is_verified = service_request.is_verified
# Проверка, что клиент существует и его Telegram chat_id заполнен
is_verified = bool(client and client.chat_id)
# Возвращаем данные клиента и статус верификации
return JsonResponse({ return JsonResponse({
'is_verified': is_verified, 'is_verified': is_verified,
'client_name': client.first_name if client else "Неизвестно", 'client_name': client.first_name if client else "Неизвестно",
'client_chat_id': client.chat_id if client else None, 'client_chat_id': client.chat_id if client else None,
}) })
except Exception as e: except Exception as e:
logger.error(f"Ошибка при получении статуса заявки: {str(e)}")
return JsonResponse({'error': str(e)}, status=500) return JsonResponse({'error': str(e)}, status=500)
def complete_registration_basic(request): @csrf_exempt
# Базовая регистрация без идентификатора заявки def send_telegram_notification(request):
if request.method == 'POST': if request.method == 'POST':
client_name = request.POST.get('client_name')
client_email = request.POST.get('client_email')
client_phone = request.POST.get('client_phone')
return redirect('home')
return render(request, 'web/complete_registration_basic.html')
def check_service_request_data(request, token=None, request_id=None):
# Проверка наличия данных в таблице ServiceRequest по токену или номеру заявки
service_request = None
if token:
try: try:
service_request = ServiceRequest.objects.get(token=token) data = json.loads(request.body)
except ServiceRequest.DoesNotExist:
service_request = None
elif request_id:
try:
service_request = ServiceRequest.objects.get(id=request_id)
except ServiceRequest.DoesNotExist:
service_request = None
if service_request: # Логируем полученные данные для отладки
return JsonResponse({ logging.info(f"Полученные данные для подтверждения заявки: {data}")
'exists': True,
'client_name': service_request.client_name,
'client_email': service_request.client_email,
'client_phone': service_request.client_phone,
'chat_id': service_request.chat_id
})
else:
return JsonResponse({'exists': False})
def order_detail(request, pk): service_request_id = data.get('service_request_id')
order = get_object_or_404(Order, pk=pk) client_chat_id = data.get('client_chat_id')
return render(request, 'web/order_detail.html', {'order': order}) client_name = data.get('client_name')
@login_required if not service_request_id or not client_chat_id:
def client_orders(request): return JsonResponse({'error': 'Недостаточно данных для подтверждения'}, status=400)
client = request.user.client_profile
orders = client.related_orders.all() # Проверяем существование заявки
return render(request, 'web/client_orders.html', {'orders': orders}) service_request = ServiceRequest.objects.filter(id=service_request_id).first()
if not service_request:
return JsonResponse({'error': 'Заявка не найдена'}, status=404)
# Обновляем заявку с chat_id, если все верно
service_request.chat_id = client_chat_id
service_request.is_verified = True
service_request.save()
return JsonResponse({'status': 'Уведомление успешно отправлено в Telegram'})
except json.JSONDecodeError as e:
logging.error(f"Ошибка при декодировании JSON: {e}")
return JsonResponse({'error': 'Неверный формат данных'}, status=400)
logging.error(f"Неподдерживаемый метод запроса: {request.method}")
return JsonResponse({'error': 'Метод запроса должен быть POST'}, status=405)
def generate_secure_token(service_request_id, secret_key): def generate_secure_token(service_request_id, secret_key):
@@ -403,48 +278,24 @@ def generate_secure_token(service_request_id, secret_key):
data = f'{service_request_id}:{secret_key}' data = f'{service_request_id}:{secret_key}'
return hmac.new(secret_key.encode(), data.encode(), hashlib.sha256).hexdigest() return hmac.new(secret_key.encode(), data.encode(), hashlib.sha256).hexdigest()
@csrf_exempt def complete_registration(request, request_id):
def send_telegram_notification(request): service_request = get_object_or_404(ServiceRequest, pk=request_id)
if request.method == 'POST': if request.method == 'POST':
try: client_email = request.POST.get('client_email', service_request.client.email)
data = json.loads(request.body) client_phone = request.POST.get('client_phone', service_request.client.phone_number)
service_request_id = data.get('service_request_id') chat_id = request.POST.get('chat_id', service_request.chat_id)
provided_token = data.get('token')
# Проверка корректности переданных данных if not all([client_email, client_phone, chat_id]):
if not service_request_id or not provided_token: return JsonResponse({'status': 'error', 'message': 'Все поля должны быть заполнены.'}, status=400)
return JsonResponse({'error': 'Недостаточно данных для подтверждения'}, status=400)
# Получение заявки client = service_request.client
service_request = ServiceRequest.objects.filter(id=service_request_id).first() client.email = client_email
if not service_request: client.phone_number = client_phone
return JsonResponse({'error': 'Заявка не найдена'}, status=404) client.save()
# Генерация токена и сравнение service_request.chat_id = chat_id
secret_key = settings.SECRET_KEY # Используем секретный ключ из настроек service_request.save()
expected_token = generate_secure_token(service_request_id, secret_key)
if not hmac.compare_digest(provided_token, expected_token): return JsonResponse({'status': 'success', 'message': 'Регистрация успешно завершена.'})
return JsonResponse({'error': 'Неверный токен. Доступ запрещен.'}, status=403)
# Отправка сообщения в Telegram return render(request, 'web/complete_registration.html', {'service_request': service_request})
chat_id = service_request.chat_id
if not chat_id:
return JsonResponse({'error': 'Нет chat_id для отправки сообщения'}, status=400)
# Составление и отправка сообщения
message = (
f"Здравствуйте, {service_request.client.first_name}!\n"
f"Ваша заявка на услугу '{service_request.service.name}' успешно зарегистрирована."
)
bot.send_telegram_message(chat_id=chat_id, message=message)
return JsonResponse({'status': 'Уведомление успешно отправлено в Telegram'})
except json.JSONDecodeError:
return JsonResponse({'error': 'Неверный формат данных'}, status=400)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
return JsonResponse({'error': 'Метод запроса должен быть POST'}, status=405)