From 327acdd062d5bbda586633ed28c8690b69b35e60 Mon Sep 17 00:00:00 2001 From: Timofey Date: Thu, 29 May 2025 12:35:24 +0300 Subject: [PATCH] backend routes --- backend/api/account/client/serializers.py | 3 ++ backend/api/account/client/views.py | 26 ++++++++++++++- backend/api/urls.py | 5 ++- ...e_highlight_end_dt_route_is_highlighted.py | 22 +++++++++++++ .../migrations/0010_route_highlight_end_dt.py | 18 +++++++++++ backend/routes/models.py | 15 +++++++-- backend/routes/views.py | 32 +++++++++++++++++-- frontend/app/(urls)/account/routes/page.tsx | 2 ++ frontend/app/api/account/highlight/route.ts | 2 +- frontend/app/api/account/upper/route.ts | 4 +-- 10 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 backend/routes/migrations/0009_remove_route_highlight_end_dt_route_is_highlighted.py create mode 100644 backend/routes/migrations/0010_route_highlight_end_dt.py diff --git a/backend/api/account/client/serializers.py b/backend/api/account/client/serializers.py index eb0bbf9..1da172f 100644 --- a/backend/api/account/client/serializers.py +++ b/backend/api/account/client/serializers.py @@ -42,6 +42,7 @@ class RouteSerializer(serializers.ModelSerializer): formatted_arrival = serializers.SerializerMethodField() formatted_cargo_type = serializers.SerializerMethodField() formatted_transport = serializers.SerializerMethodField() + is_highlighted = serializers.SerializerMethodField() class Meta: model = Route @@ -102,6 +103,8 @@ class RouteSerializer(serializers.ModelSerializer): transport_types = dict(type_transport_choices) 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): country_from = serializers.CharField(write_only=True) diff --git a/backend/api/account/client/views.py b/backend/api/account/client/views.py index aa0a3fa..717f837 100644 --- a/backend/api/account/client/views.py +++ b/backend/api/account/client/views.py @@ -9,6 +9,7 @@ from django.core.validators import validate_email from django.core.exceptions import ValidationError from django.db import models from django.db.models import Q +from datetime import datetime from .serializers import RouteSerializer, CreateRouteSerializer, CitySerializer, CountrySerializer, PlanChangeSerializer, PricingSerializer, LeadSerializer, LeadResponseSerializer from api.auth.serializers import UserResponseSerializer @@ -309,4 +310,27 @@ class LeadViewSet(ViewSet): return Response( LeadResponseSerializer(leads, many=True).data, status=status.HTTP_200_OK - ) \ No newline at end of file + ) + +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) \ No newline at end of file diff --git a/backend/api/urls.py b/backend/api/urls.py index ed1708b..1220ae2 100644 --- a/backend/api/urls.py +++ b/backend/api/urls.py @@ -15,7 +15,8 @@ CityView, CountryView, GetMembershipData, ChangeUserMembership, -LeadViewSet) +LeadViewSet, +PremiumMembershipActionsView) 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/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/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/leads/", LeadViewSet.as_view({'get':'get_leads'}), name='get_leads'), diff --git a/backend/routes/migrations/0009_remove_route_highlight_end_dt_route_is_highlighted.py b/backend/routes/migrations/0009_remove_route_highlight_end_dt_route_is_highlighted.py new file mode 100644 index 0000000..2821c62 --- /dev/null +++ b/backend/routes/migrations/0009_remove_route_highlight_end_dt_route_is_highlighted.py @@ -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='Выделено'), + ), + ] diff --git a/backend/routes/migrations/0010_route_highlight_end_dt.py b/backend/routes/migrations/0010_route_highlight_end_dt.py new file mode 100644 index 0000000..a0c6248 --- /dev/null +++ b/backend/routes/migrations/0010_route_highlight_end_dt.py @@ -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='Выделено до'), + ), + ] diff --git a/backend/routes/models.py b/backend/routes/models.py index 67b3143..953f065 100644 --- a/backend/routes/models.py +++ b/backend/routes/models.py @@ -1,6 +1,7 @@ from django.db import models from django.contrib.auth.models import User from routes.constants.routeChoices import owner_type_choices, type_transport_choices, cargo_type_choices +from django.utils import timezone class Country(models.Model): id = models.BigAutoField(primary_key=True) @@ -101,10 +102,11 @@ class Route(models.Model): verbose_name=('Дата и время последнего поднятия'), blank=True, null=True ) - + is_highlighted = models.BooleanField(default=False, verbose_name=('Выделено')) highlight_end_DT = models.DateTimeField( - verbose_name=('Дата и время окончания выделения'), - blank=True, null=True + verbose_name=('Выделено до'), + null=True, + blank=True ) status = models.CharField( @@ -122,6 +124,13 @@ class Route(models.Model): from_city_name = self.from_city.name if self.from_city else 'Не указан' to_city_name = self.to_city.name if self.to_city else 'Не указан' 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: verbose_name = (u'Маршрут') diff --git a/backend/routes/views.py b/backend/routes/views.py index 91ea44a..75ad72b 100644 --- a/backend/routes/views.py +++ b/backend/routes/views.py @@ -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 + ) diff --git a/frontend/app/(urls)/account/routes/page.tsx b/frontend/app/(urls)/account/routes/page.tsx index 9065c70..b226fb9 100644 --- a/frontend/app/(urls)/account/routes/page.tsx +++ b/frontend/app/(urls)/account/routes/page.tsx @@ -26,6 +26,8 @@ export default function UserRoutes() { } const data = await response.json() + console.log(data) + setRoutes(data || []) } catch (error) { console.error('Error fetching routes:', error) diff --git a/frontend/app/api/account/highlight/route.ts b/frontend/app/api/account/highlight/route.ts index a2d10d3..4e23d10 100644 --- a/frontend/app/api/account/highlight/route.ts +++ b/frontend/app/api/account/highlight/route.ts @@ -14,7 +14,7 @@ export async function PATCH(req: NextRequest) { 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', headers: { 'Content-Type': 'application/json', diff --git a/frontend/app/api/account/upper/route.ts b/frontend/app/api/account/upper/route.ts index 9f312f0..442ac2b 100644 --- a/frontend/app/api/account/upper/route.ts +++ b/frontend/app/api/account/upper/route.ts @@ -14,7 +14,7 @@ export async function PATCH(req: NextRequest) { 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', headers: { 'Content-Type': 'application/json', @@ -31,7 +31,7 @@ export async function PATCH(req: NextRequest) { const result = await response.json() return new Response(JSON.stringify(result), { status: 200 }) } 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' }), { status: 500, })