From e606a6ce2bcb06ef7464d97a5152c474abd695fd Mon Sep 17 00:00:00 2001 From: lizerk Date: Wed, 18 Mar 2026 18:19:58 +0300 Subject: [PATCH] =?UTF-8?q?refactor(actions):=20=D1=83=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=B8=D1=82=D1=8C=20EmployeeActionViewSet=20-=20?= =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D0=B8=D1=82=D1=8C=20LDAP=20=D0=BB?= =?UTF-8?q?=D0=BE=D0=B3=D0=B8=D0=BA=D1=83,=20=D0=B8=D1=81=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D1=81=D0=B8?= =?UTF-8?q?=D0=B3=D0=BD=D0=B0=D0=BB=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Изменения: - EmployeeActionViewSet: 240 → 115 строк (-125 строк, -52%) - _apply_effects(): удалена вся LDAP логика (~140 строк) Удалено: - Прямые вызовы DirectoryService - Обработка LdapSyncState - Логика перемещения в OU=Dismissed и обратно - Удаление из департаментов в LDAP - Все try/except блоки для LDAP ошибок - Импорты: DirectoryService, DirectoryLdapError, DirectoryServiceError, DirectoryDbError, LdapSyncState, settings, _is_ldap_enabled Осталось только работа с БД: - При ACTION_DISMISSED: is_active=False + деактивация EmployeeDepartment - При других действиях: is_active=True LDAP синхронизация через существующие сигналы: - sync_employee_to_ldap_on_save: обрабатывает is_active (активация/деактивация) - sync_member (signals_department.py): обрабатывает EmployeeDepartment (удаление из отделов) Функционал сохранен, код стал в 2 раза короче --- backend/api/v1/employees/views/actions.py | 153 ++-------------------- 1 file changed, 14 insertions(+), 139 deletions(-) diff --git a/backend/api/v1/employees/views/actions.py b/backend/api/v1/employees/views/actions.py index dc9e1c41..2b145036 100644 --- a/backend/api/v1/employees/views/actions.py +++ b/backend/api/v1/employees/views/actions.py @@ -4,21 +4,17 @@ from __future__ import annotations import logging -from django.conf import settings from django.db import transaction from django.utils import timezone from employees.constants import ACTION_DISMISSED -from employees.ldap.directory_service import DirectoryService -from employees.ldap.errors import (DirectoryDbError, DirectoryLdapError, - DirectoryServiceError) -from employees.models import EmployeeAction, EmployeeDepartment, LdapSyncState +from employees.models import EmployeeAction, EmployeeDepartment from rest_framework import filters, viewsets from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from ...permissions import AdminOrActionOrModelPerms from ..serializers import EmployeeActionSerializer -from ._helpers import HistoryActionMixin, _is_ldap_enabled +from ._helpers import HistoryActionMixin logger = logging.getLogger(__name__) @@ -82,153 +78,32 @@ class EmployeeActionViewSet(HistoryActionMixin, viewsets.ModelViewSet): # --- бизнес-эффекты после записи события --- def _apply_effects(self, action_obj: EmployeeAction): + """Применяет эффекты кадрового события. + + Работает только с БД. LDAP синхронизация через сигналы: + - sync_employee_to_ldap_on_save: активация/деактивация в LDAP + - sync_member (signals_department.py): удаление из отделов в LDAP + """ emp = action_obj.employee - ldap_enabled = _is_ldap_enabled() if action_obj.action == ACTION_DISMISSED: - # Сначала деактивируем сотрудника + # Деактивируем сотрудника (сигнал синхронизирует в LDAP) if emp.is_active: emp.is_active = False + emp._ldap_changes = {"is_active": False} emp.save(update_fields=["is_active"]) - # Деактивируем связи с отделами в БД + # Деактивируем связи с отделами (сигналы синхронизируют в LDAP) EmployeeDepartment.objects.filter(employee=emp, is_active=True).update( is_active=False, date_to=timezone.now().date() ) - - # Синхронизируем с LDAP - if ldap_enabled: - try: - svc = DirectoryService() - has_ldap_dn = LdapSyncState.objects.filter( - model="employee", object_pk=str(emp.pk), ldap_dn__isnull=False - ).exists() - - if has_ldap_dn: - svc.update_user(emp, changes={"is_active": False}) - - active_departments = EmployeeDepartment.objects.filter( - employee=emp - ).select_related("department") - - departments_processed = False - for emp_dept in active_departments: - try: - svc.remove_member(emp_dept.department, emp) - departments_processed = True - except ( - DirectoryLdapError, - DirectoryDbError, - DirectoryServiceError, - ) as e: - logger.error( - f"Failed to remove dismissed employee from department: " - f"employee_id={emp.id}, department_id={emp_dept.department.id}, error={e}" - ) - - if not departments_processed: - try: - from employees.ldap.utils.ldap_utils import \ - get_base_dn_for_employee - - sync_state = LdapSyncState.objects.filter( - model="employee", object_pk=str(emp.pk) - ).first() - - if sync_state and sync_state.ldap_dn: - target_base = get_base_dn_for_employee(emp) - current_dn = sync_state.ldap_dn - - if not current_dn.lower().endswith( - target_base.lower() - ): - try: - new_dn = svc.move_user_to_base( - emp, target_base - ) - logger.info( - f"Dismissed employee without department moved to OU=Dismissed: " - f"employee_id={emp.id}, new_dn={new_dn}" - ) - except Exception as move_err: - logger.error( - f"Failed to move dismissed employee: {move_err}" - ) - except Exception as e: - logger.error( - f"Failed to move dismissed employee without department to OU=Dismissed: " - f"employee_id={emp.id}, error={e}" - ) - except ( - DirectoryLdapError, - DirectoryDbError, - DirectoryServiceError, - ) as e: - logger.error( - f"Failed to disable user in LDAP during dismissal: " - f"employee_id={emp.id}, error={e}" - ) else: - # Любое иное событие делает сотрудника активным - was_inactive = not emp.is_active - if was_inactive: + # Любое иное событие делает сотрудника активным (сигнал синхронизирует в LDAP) + if not emp.is_active: emp.is_active = True + emp._ldap_changes = {"is_active": True} emp.save(update_fields=["is_active"]) - if ldap_enabled: - try: - svc = DirectoryService() - has_ldap_dn = LdapSyncState.objects.filter( - model="employee", object_pk=str(emp.pk), ldap_dn__isnull=False - ).exists() - - if has_ldap_dn: - svc.update_user(emp, changes={"is_active": True}) - - if was_inactive: - try: - sync_state = LdapSyncState.objects.get( - model="employee", object_pk=str(emp.pk) - ) - current_dn = sync_state.ldap_dn - dismissed_base = getattr( - settings, "LDAP_DISMISSED_BASE", "" - ) - - if dismissed_base and current_dn.lower().endswith( - dismissed_base.lower() - ): - users_base = getattr( - settings, "LDAP_USERS_BASE", None - ) or getattr(settings, "LDAP_USER_BASE", None) - if users_base: - try: - new_dn = svc.move_user_to_base( - emp, users_base - ) - logger.info( - f"Restored employee moved from Dismissed to Users: " - f"employee_id={emp.id}, new_dn={new_dn}" - ) - except Exception as move_err: - logger.error( - f"Failed to move restored employee: {move_err}" - ) - except Exception as e: - logger.error( - f"Failed to move restored employee from Dismissed to Users: " - f"employee_id={emp.id}, error={e}" - ) - except ( - DirectoryLdapError, - DirectoryDbError, - DirectoryServiceError, - ) as e: - logger.error( - f"Failed to enable user in LDAP during action: " - f"employee_id={emp.id}, action={action_obj.action}, error={e}" - ) - @transaction.atomic def perform_create(self, serializer): obj = serializer.save()