Files
octopus/myproject/user_roles/services.py

163 lines
6.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from django.db import transaction
from user_roles.models import Role, UserRole
from django.contrib.auth import get_user_model
User = get_user_model()
class RoleService:
"""Сервис для управления ролями"""
@staticmethod
def create_default_roles():
"""
Создает системные роли для тенанта.
Вызывается при создании тенанта (автоматически через signals или admin).
ВАЖНО: Эта функция вызывается в контексте schema_context(tenant.schema_name),
поэтому роли создаются в schema конкретного тенанта автоматически!
"""
default_roles = [
{
'code': Role.PLATFORM_SUPPORT,
'name': 'Техподдержка платформы',
'description': 'Служебный аккаунт техподдержки платформы. Полный доступ для помощи владельцу.',
'is_system': True,
},
{
'code': Role.OWNER,
'name': 'Владелец',
'description': 'Полный доступ ко всем функциям системы. Управление пользователями и настройками.',
'is_system': True,
},
{
'code': Role.MANAGER,
'name': 'Менеджер',
'description': 'Управление заказами, клиентами, товарами и складом. Без доступа к настройкам.',
'is_system': True,
},
{
'code': Role.FLORIST,
'name': 'Флорист',
'description': 'Работа с заказами и складскими операциями для заказов.',
'is_system': True,
},
{
'code': Role.COURIER,
'name': 'Курьер',
'description': 'Доставка заказов (права будут определены позже).',
'is_system': True,
},
]
for role_data in default_roles:
Role.objects.get_or_create(
code=role_data['code'],
defaults=role_data
)
@staticmethod
def get_visible_roles():
"""
Возвращает роли, видимые для пользователей тенанта.
Исключает служебные роли (platform_support).
"""
return Role.objects.exclude(code__in=Role.HIDDEN_ROLES)
@staticmethod
def get_visible_users():
"""
Возвращает пользователей, видимых для владельца тенанта.
Исключает пользователей с ролью platform_support.
"""
from accounts.models import CustomUser
hidden_user_ids = UserRole.objects.filter(
role__code__in=Role.HIDDEN_ROLES
).values_list('user_id', flat=True)
return CustomUser.objects.exclude(id__in=hidden_user_ids)
@staticmethod
def get_role_by_code(code):
"""Получить роль по коду"""
try:
return Role.objects.get(code=code, is_system=True)
except Role.DoesNotExist:
return None
@staticmethod
@transaction.atomic
def assign_role_to_user(user, role_code, created_by=None):
"""
Назначить роль пользователю в текущем тенанте.
Если у пользователя уже есть роль - обновляет её.
ВАЖНО: Вызывается в контексте текущего тенанта (через middleware),
поэтому UserRole создается в schema текущего тенанта автоматически!
"""
role = RoleService.get_role_by_code(role_code)
if not role:
raise ValueError(f"Роль с кодом '{role_code}' не найдена")
user_role, created = UserRole.objects.update_or_create(
user=user,
defaults={
'role': role,
'created_by': created_by,
'is_active': True,
}
)
return user_role
@staticmethod
def get_user_role(user):
"""
Получить роль пользователя в текущем тенанте.
Оптимизация: использует уже загруженные данные если есть select_related.
"""
try:
# Проверяем, есть ли уже загруженная роль через select_related
if hasattr(user, 'tenant_role'):
# Проверяем, что tenant_role загружен (а не RelatedObjectDoesNotExist)
try:
user_role = user.tenant_role
if user_role.is_active and hasattr(user_role, 'role'):
return user_role.role
except UserRole.DoesNotExist:
pass
# Если нет prefetch - делаем запрос
user_role = UserRole.objects.get(user=user, is_active=True)
return user_role.role
except UserRole.DoesNotExist:
return None
@staticmethod
def user_has_role(user, *role_codes):
"""Проверить, имеет ли пользователь одну из указанных ролей"""
user_role = RoleService.get_user_role(user)
if not user_role:
return False
return user_role.code in role_codes
@staticmethod
def can_modify_user_role(modifier_user, target_user_role):
"""
Проверяет, может ли пользователь изменить указанную роль.
Args:
modifier_user: Пользователь, который хочет изменить роль
target_user_role: UserRole объект, который нужно изменить
Returns:
tuple: (bool, str) - (можно ли изменить, причина отказа)
"""
# Защита от самоблокировки
if target_user_role.user == modifier_user:
return False, "Вы не можете изменить свою собственную роль"
# Можно добавить другие проверки в будущем:
# - Запрет удаления последнего owner'а
# - Проверка иерархии ролей и т.д.
return True, ""