Fix admin API gaps for users, exchange checks, and parser logs
This commit is contained in:
@@ -4,6 +4,7 @@ from apps.core.exceptions import NotFoundError
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
from django.db import transaction
|
||||
from django.db.models import F, Q
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
|
||||
from .models import Profile
|
||||
@@ -69,6 +70,65 @@ class UserService:
|
||||
.order_by("-created_at")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_filtered_users_queryset(
|
||||
cls,
|
||||
*,
|
||||
search: str = "",
|
||||
ordering: str = "",
|
||||
):
|
||||
"""Queryset списка пользователей с поиском и сортировкой для админского UI."""
|
||||
queryset = cls.get_users_queryset()
|
||||
search_term = search.strip()
|
||||
|
||||
if search_term:
|
||||
queryset = queryset.filter(
|
||||
Q(username__icontains=search_term)
|
||||
| Q(email__icontains=search_term)
|
||||
| Q(phone__icontains=search_term)
|
||||
| Q(profile__first_name__icontains=search_term)
|
||||
| Q(profile__middle_name__icontains=search_term)
|
||||
| Q(profile__last_name__icontains=search_term)
|
||||
).distinct()
|
||||
|
||||
ordering_fields = []
|
||||
ordering_map = {
|
||||
"id": ("id", False),
|
||||
"email": ("email", False),
|
||||
"username": ("username", False),
|
||||
"phone": ("phone", False),
|
||||
"is_active": ("is_active", False),
|
||||
"is_verified": ("is_verified", False),
|
||||
"created_at": ("created_at", False),
|
||||
"updated_at": ("updated_at", False),
|
||||
"first_name": ("profile__first_name", True),
|
||||
"middle_name": ("profile__middle_name", True),
|
||||
"last_name": ("profile__last_name", True),
|
||||
"role": ("is_staff", False),
|
||||
}
|
||||
|
||||
for raw_field in (item.strip() for item in ordering.split(",") if item.strip()):
|
||||
is_desc = raw_field.startswith("-")
|
||||
field_name = raw_field[1:] if is_desc else raw_field
|
||||
mapped_config = ordering_map.get(field_name)
|
||||
if not mapped_config:
|
||||
continue
|
||||
mapped_field, nulls_last = mapped_config
|
||||
if nulls_last:
|
||||
ordering_fields.append(
|
||||
F(mapped_field).desc(nulls_last=True)
|
||||
if is_desc
|
||||
else F(mapped_field).asc(nulls_last=True)
|
||||
)
|
||||
continue
|
||||
|
||||
ordering_fields.append(f"-{mapped_field}" if is_desc else mapped_field)
|
||||
|
||||
if ordering_fields:
|
||||
queryset = queryset.order_by(*ordering_fields, "-created_at")
|
||||
|
||||
return queryset
|
||||
|
||||
@classmethod
|
||||
def get_user_by_email(cls, email: str) -> User:
|
||||
"""Получает пользователя по email
|
||||
@@ -147,8 +207,9 @@ class UserService:
|
||||
username: str,
|
||||
password: str,
|
||||
role: str,
|
||||
first_name: str | None = None,
|
||||
last_name: str | None = None,
|
||||
first_name: str,
|
||||
last_name: str,
|
||||
middle_name: str | None = None,
|
||||
**extra_fields,
|
||||
) -> User:
|
||||
"""Создаёт пользователя администратором и назначает роль."""
|
||||
@@ -162,6 +223,7 @@ class UserService:
|
||||
cls._update_or_create_profile(
|
||||
user=user,
|
||||
first_name=first_name,
|
||||
middle_name=middle_name,
|
||||
last_name=last_name,
|
||||
)
|
||||
return cls.get_users_queryset().get(id=user.id)
|
||||
@@ -174,7 +236,9 @@ class UserService:
|
||||
role = fields.pop("role", None)
|
||||
password = fields.pop("password", None)
|
||||
profile_fields = {
|
||||
key: fields.pop(key) for key in ("first_name", "last_name") if key in fields
|
||||
key: fields.pop(key)
|
||||
for key in ("first_name", "middle_name", "last_name")
|
||||
if key in fields
|
||||
}
|
||||
|
||||
for field, value in fields.items():
|
||||
@@ -201,6 +265,14 @@ class UserService:
|
||||
user.save(update_fields=["is_active"])
|
||||
return user
|
||||
|
||||
@classmethod
|
||||
def activate_user(cls, user_id: int) -> User:
|
||||
"""Активирует пользователя."""
|
||||
user = cls.get_user_by_id(user_id)
|
||||
user.is_active = True
|
||||
user.save(update_fields=["is_active"])
|
||||
return user
|
||||
|
||||
@classmethod
|
||||
def delete_user(cls, user_id: int) -> None:
|
||||
"""
|
||||
@@ -327,11 +399,14 @@ class UserService:
|
||||
*,
|
||||
user: User,
|
||||
first_name: str | None = None,
|
||||
middle_name: str | None = None,
|
||||
last_name: str | None = None,
|
||||
) -> Profile:
|
||||
profile, _ = Profile.objects.get_or_create(user=user)
|
||||
if first_name is not None:
|
||||
profile.first_name = first_name
|
||||
if middle_name is not None:
|
||||
profile.middle_name = middle_name
|
||||
if last_name is not None:
|
||||
profile.last_name = last_name
|
||||
profile.save()
|
||||
@@ -410,6 +485,7 @@ class ProfileService:
|
||||
"is_verified": user.is_verified,
|
||||
"phone": user.phone,
|
||||
"first_name": profile.first_name,
|
||||
"middle_name": profile.middle_name,
|
||||
"last_name": profile.last_name,
|
||||
"full_name": profile.full_name,
|
||||
"bio": profile.bio,
|
||||
|
||||
Reference in New Issue
Block a user