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

@@ -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)
token = models.UUIDField(default=uuid.uuid4, unique=True) # Генерация уникального токена
chat_id = models.CharField(max_length=100, blank=True, null=True) # Telegram chat ID
is_verified = models.BooleanField(default=False)
class Meta:
verbose_name = 'Заявка на услугу'
verbose_name_plural = 'Заявки на услуги'
ordering = ['-created_at']
ordering = ['-is_verified', '-created_at']
def __str__(self):
return f"Request for {self.service.name} by {self.client.first_name}"
@@ -150,5 +151,5 @@ class Review(models.Model):
ordering = ['-review_date']
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>
<!-- Модальное окно для создания заявки на услугу -->
{% include "web/modal_order_form.html" %}
<div class="container py-4">
@@ -78,174 +79,10 @@
</div>
</div>
<style>
/* Добавляем стили для анимации появления модального окна */
#serviceModal {
display: none;
position: fixed;
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>
<!-- Подключение JavaScript файлов, вынесенных в директорию static/assets/js -->
<script src="{% static 'assets/js/get-csrf-token.js' %}"></script>
<script src="{% static 'assets/js/modal-init.js' %}"></script>
<script src="{% static 'assets/js/service_request.js' %}"></script>
<script src="{% static 'assets/js/verification_status.js' %}"></script>
{% endblock %}

View File

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