backend routes

This commit is contained in:
2025-05-29 12:35:24 +03:00
parent 6987560a37
commit 327acdd062
10 changed files with 119 additions and 10 deletions

View File

@@ -42,6 +42,7 @@ class RouteSerializer(serializers.ModelSerializer):
formatted_arrival = serializers.SerializerMethodField() formatted_arrival = serializers.SerializerMethodField()
formatted_cargo_type = serializers.SerializerMethodField() formatted_cargo_type = serializers.SerializerMethodField()
formatted_transport = serializers.SerializerMethodField() formatted_transport = serializers.SerializerMethodField()
is_highlighted = serializers.SerializerMethodField()
class Meta: class Meta:
model = Route model = Route
@@ -102,6 +103,8 @@ class RouteSerializer(serializers.ModelSerializer):
transport_types = dict(type_transport_choices) transport_types = dict(type_transport_choices)
return transport_types.get(obj.type_transport, obj.type_transport) return transport_types.get(obj.type_transport, obj.type_transport)
def get_is_highlighted(self, obj):
return obj.is_currently_highlighted
class CreateRouteSerializer(serializers.ModelSerializer): class CreateRouteSerializer(serializers.ModelSerializer):
country_from = serializers.CharField(write_only=True) country_from = serializers.CharField(write_only=True)

View File

@@ -9,6 +9,7 @@ from django.core.validators import validate_email
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import Q
from datetime import datetime
from .serializers import RouteSerializer, CreateRouteSerializer, CitySerializer, CountrySerializer, PlanChangeSerializer, PricingSerializer, LeadSerializer, LeadResponseSerializer from .serializers import RouteSerializer, CreateRouteSerializer, CitySerializer, CountrySerializer, PlanChangeSerializer, PricingSerializer, LeadSerializer, LeadResponseSerializer
from api.auth.serializers import UserResponseSerializer from api.auth.serializers import UserResponseSerializer
@@ -309,4 +310,27 @@ class LeadViewSet(ViewSet):
return Response( return Response(
LeadResponseSerializer(leads, many=True).data, LeadResponseSerializer(leads, many=True).data,
status=status.HTTP_200_OK status=status.HTTP_200_OK
) )
class PremiumMembershipActionsView(ViewSet):
"""Выделение объявления"""
@action(detail=False, methods=['patch'])
@handle_exceptions
def highlight_route(self, request):
"""Выделяем объявление"""
route_id = request.data.get('route_id')
route = get_object_or_404(Route, id=route_id)
route.is_highlighted = True
route.save()
return Response({"message": "Объявление выделено"}, status=status.HTTP_200_OK)
@action(detail=False, methods=['patch'])
@handle_exceptions
def upper_route(self, request):
"""Поднимаем объявление"""
route_id = request.data.get('route_id')
route = get_object_or_404(Route, id=route_id)
route.rising_DT = datetime.now()
route.save()
return Response({"message": "Объявление поднято"}, status=status.HTTP_200_OK)

View File

@@ -15,7 +15,8 @@ CityView,
CountryView, CountryView,
GetMembershipData, GetMembershipData,
ChangeUserMembership, ChangeUserMembership,
LeadViewSet) LeadViewSet,
PremiumMembershipActionsView)
from api.search.views import SearchRouteListView from api.search.views import SearchRouteListView
@@ -35,6 +36,8 @@ urlpatterns = [
path("v1/account/change_main_data/", AccountActionsView.as_view({'patch':'change_data_main_tab'}), name='change_data_main_tab'), path("v1/account/change_main_data/", AccountActionsView.as_view({'patch':'change_data_main_tab'}), name='change_data_main_tab'),
path("v1/account/routes/", AccountActionsView.as_view({'get':'user_routes'}), name='user_routes'), path("v1/account/routes/", AccountActionsView.as_view({'get':'user_routes'}), name='user_routes'),
path("v1/account/create_route/", AccountActionsView.as_view({'post':'create_route'}), name='create_route'), path("v1/account/create_route/", AccountActionsView.as_view({'post':'create_route'}), name='create_route'),
path("v1/account/highlight/", PremiumMembershipActionsView.as_view({'patch':'highlight_route'}), name='highlight_route'),
path("v1/account/upper/", PremiumMembershipActionsView.as_view({'patch':'upper_route'}), name='upper_route'),
path("v1/account/send_lead/", LeadViewSet.as_view({'post':'send_lead'}), name='send_lead'), path("v1/account/send_lead/", LeadViewSet.as_view({'post':'send_lead'}), name='send_lead'),
path("v1/account/leads/", LeadViewSet.as_view({'get':'get_leads'}), name='get_leads'), path("v1/account/leads/", LeadViewSet.as_view({'get':'get_leads'}), name='get_leads'),

View File

@@ -0,0 +1,22 @@
# Generated by Django 5.2.1 on 2025-05-29 09:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('routes', '0008_city_russian_name_alter_city_name'),
]
operations = [
migrations.RemoveField(
model_name='route',
name='highlight_end_DT',
),
migrations.AddField(
model_name='route',
name='is_highlighted',
field=models.BooleanField(default=False, verbose_name='Выделено'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.1 on 2025-05-29 09:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('routes', '0009_remove_route_highlight_end_dt_route_is_highlighted'),
]
operations = [
migrations.AddField(
model_name='route',
name='highlight_end_DT',
field=models.DateTimeField(blank=True, null=True, verbose_name='Выделено до'),
),
]

View File

@@ -1,6 +1,7 @@
from django.db import models from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
from routes.constants.routeChoices import owner_type_choices, type_transport_choices, cargo_type_choices from routes.constants.routeChoices import owner_type_choices, type_transport_choices, cargo_type_choices
from django.utils import timezone
class Country(models.Model): class Country(models.Model):
id = models.BigAutoField(primary_key=True) id = models.BigAutoField(primary_key=True)
@@ -101,10 +102,11 @@ class Route(models.Model):
verbose_name=('Дата и время последнего поднятия'), verbose_name=('Дата и время последнего поднятия'),
blank=True, null=True blank=True, null=True
) )
is_highlighted = models.BooleanField(default=False, verbose_name=('Выделено'))
highlight_end_DT = models.DateTimeField( highlight_end_DT = models.DateTimeField(
verbose_name=('Дата и время окончания выделения'), verbose_name=('Выделено до'),
blank=True, null=True null=True,
blank=True
) )
status = models.CharField( status = models.CharField(
@@ -122,6 +124,13 @@ class Route(models.Model):
from_city_name = self.from_city.name if self.from_city else 'Не указан' from_city_name = self.from_city.name if self.from_city else 'Не указан'
to_city_name = self.to_city.name if self.to_city else 'Не указан' to_city_name = self.to_city.name if self.to_city else 'Не указан'
return f"Маршрут #{self.id}: {from_city_name}{to_city_name}" return f"Маршрут #{self.id}: {from_city_name}{to_city_name}"
@property
def is_currently_highlighted(self):
"""Проверяем, выделено ли объявление на текущий момент"""
if not self.highlight_end_DT:
return False
return timezone.now() <= self.highlight_end_DT
class Meta: class Meta:
verbose_name = (u'Маршрут') verbose_name = (u'Маршрут')

View File

@@ -1,3 +1,31 @@
from django.shortcuts import render from django.utils import timezone
from datetime import timedelta
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from .models import Route
# Create your views here. @api_view(['PATCH'])
@permission_classes([IsAuthenticated])
def highlight_route(request):
try:
route_id = request.data.get('route_id')
route = Route.objects.get(id=route_id, owner=request.user)
# подсвечиваем объявление на 24 часа
route.highlight_end_DT = timezone.now() + timedelta(days=1)
route.is_highlighted = True
route.save()
return Response({'status': 'success'})
except Route.DoesNotExist:
return Response(
{'error': 'Маршрут не найден или у вас нет прав для его изменения'},
status=status.HTTP_404_NOT_FOUND
)
except Exception as e:
return Response(
{'error': str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)

View File

@@ -26,6 +26,8 @@ export default function UserRoutes() {
} }
const data = await response.json() const data = await response.json()
console.log(data)
setRoutes(data || []) setRoutes(data || [])
} catch (error) { } catch (error) {
console.error('Error fetching routes:', error) console.error('Error fetching routes:', error)

View File

@@ -14,7 +14,7 @@ export async function PATCH(req: NextRequest) {
const { route_id } = await req.json() const { route_id } = await req.json()
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/account/route_highlight/`, { const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/account/highlight/`, {
method: 'PATCH', method: 'PATCH',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',

View File

@@ -14,7 +14,7 @@ export async function PATCH(req: NextRequest) {
const { route_id } = await req.json() const { route_id } = await req.json()
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/account/route_up/`, { const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/account/upper/`, {
method: 'PATCH', method: 'PATCH',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@@ -31,7 +31,7 @@ export async function PATCH(req: NextRequest) {
const result = await response.json() const result = await response.json()
return new Response(JSON.stringify(result), { status: 200 }) return new Response(JSON.stringify(result), { status: 200 })
} catch (error) { } catch (error) {
console.error('PATCH /api/account/route_up error:', error) console.error('PATCH /api/account/upper error:', error)
return new Response(JSON.stringify({ error: 'Internal Server Error' }), { return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
status: 500, status: 500,
}) })