99 lines
3.8 KiB
Python
99 lines
3.8 KiB
Python
from datetime import datetime
|
||
from typing import Any, Dict, Iterable, Optional
|
||
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from src.core.errors import AccessDeniedException
|
||
|
||
# from src.domain.models import AuditLog, UserRole, Users
|
||
from src.db.models.audit_log import AuditLog
|
||
from src.db.models.role import UserRoleEnum
|
||
from src.db.models.app_user import AppUser
|
||
|
||
from src.repository.auditlog_repository import AuditLogRepository
|
||
|
||
from src.domain.schemas import AuditLog as AuditLogSchema
|
||
|
||
|
||
class AuditLogService:
|
||
"""Сервис для работы с журналом аудита."""
|
||
|
||
def __init__(self, db: AsyncSession):
|
||
self.db = db
|
||
self.audit_repo = AuditLogRepository(db)
|
||
|
||
async def get(self, audit_log_id: int, user: AppUser) -> Optional[AuditLog]:
|
||
"""Получение записи аудита по ID."""
|
||
if not self._can_view_audit_logs(user):
|
||
raise AccessDeniedException(
|
||
"Недостаточно прав для просмотра журнала аудита"
|
||
)
|
||
|
||
return await self.audit_repo.get(audit_log_id)
|
||
|
||
async def get_all(
|
||
self,
|
||
user: AppUser,
|
||
limit: int | None = None,
|
||
offset: int | None = None,
|
||
user_id: int | None = None,
|
||
org_unit_id: int | None = None,
|
||
task_id: int | None = None,
|
||
form_id: int | None = None,
|
||
event_type: str | None = None,
|
||
event: str | None = None,
|
||
date_from: datetime | None = None,
|
||
date_to: datetime | None = None,
|
||
) -> tuple[Iterable[AuditLog], int]:
|
||
"""Получение всех записей аудита с опциональной фильтрацией."""
|
||
if not self._can_view_audit_logs(user):
|
||
raise AccessDeniedException(
|
||
"Недостаточно прав для просмотра журнала аудита"
|
||
)
|
||
|
||
return await self.audit_repo.get_all(
|
||
limit=limit,
|
||
offset=offset,
|
||
user_id=user_id,
|
||
org_unit_id=org_unit_id,
|
||
task_id=task_id,
|
||
form_id=form_id,
|
||
event_type=event_type,
|
||
date_from=date_from,
|
||
date_to=date_to,
|
||
event=event,
|
||
)
|
||
|
||
def _can_view_audit_logs(self, user: AppUser) -> bool:
|
||
"""Проверяет, может ли пользователь просматривать журнал аудита."""
|
||
return user.role_id == UserRoleEnum.ADMIN.value
|
||
|
||
def orm_log_to_response(self, log: AuditLog) -> AuditLogSchema:
|
||
"""Маппинг записи ORM audit_log в формат ответа API (entity, entity_id, action, at, payload_json).
|
||
model_validate(orm) не подходит: в БД поля event_dt/event/event_type/event_data, в API — at/action/entity/entity_id; entity и entity_id из event_data JSON.
|
||
"""
|
||
event_data: Optional[Dict[str, Any]] = getattr(log, "event_data", None) or {}
|
||
entity = (
|
||
event_data.get("entity_type") if isinstance(event_data, dict) else None
|
||
) or getattr(log, "event_type", "unknown")
|
||
entity_id = (
|
||
event_data.get("entity_id") if isinstance(event_data, dict) else None
|
||
)
|
||
if entity_id is not None and not isinstance(entity_id, int):
|
||
try:
|
||
entity_id = int(entity_id)
|
||
except (TypeError, ValueError):
|
||
entity_id = None
|
||
action = getattr(log, "event", "")
|
||
at = getattr(log, "event_dt", None)
|
||
return AuditLogSchema(
|
||
entity=entity,
|
||
entity_id=entity_id,
|
||
action=action,
|
||
payload_json=event_data if isinstance(event_data, dict) else None,
|
||
id=log.id,
|
||
user_id=getattr(log, "user_id", None),
|
||
at=at,
|
||
user=getattr(log, "user", None),
|
||
)
|