/
tegdif
/
cism
Обзор
Документация
Войти
/
tegdif
/
cism
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/services/task_service.py
101 строка
4 KB
tegdif
first_commit
28 июл 2026, 08:54
28 июл 2026, 08:54
8ecbd07
Код
Авторство
О чём код?
import uuid from datetime import datetime, timezone from typing import Optional from sqlalchemy import select, func from sqlalchemy.ext.asyncio import AsyncSession from src.models.task import Task, TaskStatus, TaskPriority from src.schemas.task import TaskCreate, TaskResponse, TaskStatusResponse, TaskListResponse class TaskService: """Сервисный слой для CRUD-операций над задачами и управления статусами.""" def __init__(self, session: AsyncSession) -> None: self.session = session async def create_task(self, task_data: TaskCreate) -> Task: """Создаёт новую задачу со статусом NEW и возвращает её.""" task = Task( title=task_data.title, description=task_data.description or "", priority=task_data.priority, status=TaskStatus.NEW, ) self.session.add(task) await self.session.commit() await self.session.refresh(task) return task async def get_task(self, task_id: uuid.UUID) -> Optional[Task]: """Возвращает задачу по UUID или None, если не найдена.""" result = await self.session.execute(select(Task).where(Task.id == task_id)) return result.scalar_one_or_none() async def get_tasks( self, page: int = 1, page_size: int = 10, status: Optional[TaskStatus] = None, priority: Optional[TaskPriority] = None, ) -> TaskListResponse: """Возвращает список задач с пагинацией и фильтрацией, отсортированный по дате создания (сначала новые).""" query = select(Task) count_query = select(func.count(Task.id)) if status: query = query.where(Task.status == status) count_query = count_query.where(Task.status == status) if priority: query = query.where(Task.priority == priority) count_query = count_query.where(Task.priority == priority) total_result = await self.session.execute(count_query) total = total_result.scalar() or 0 offset = (page - 1) * page_size query = query.order_by(Task.created_at.desc()).offset(offset).limit(page_size) result = await self.session.execute(query) tasks = list(result.scalars().all()) items = [TaskResponse.model_validate(t) for t in tasks] return TaskListResponse(items=items, total=total, page=page, page_size=page_size) async def cancel_task(self, task_id: uuid.UUID) -> Optional[Task]: """Отменяет задачу. Вызывает ValueError, если задача уже в терминальном состоянии.""" task = await self.get_task(task_id) if not task: return None if task.status in (TaskStatus.COMPLETED, TaskStatus.CANCELLED, TaskStatus.FAILED): raise ValueError(f"Невозможно отменить задачу в статусе {task.status.value}") task.status = TaskStatus.CANCELLED task.completed_at = datetime.now(timezone.utc) await self.session.commit() await self.session.refresh(task) return task async def update_task_status( self, task_id: uuid.UUID, status: TaskStatus, result: Optional[str] = None, error_info: Optional[str] = None, ) -> Optional[Task]: """Обновляет статус задачи и устанавливает временные метки (started_at / completed_at).""" task = await self.get_task(task_id) if not task: return None task.status = status now = datetime.now(timezone.utc) if status == TaskStatus.IN_PROGRESS: task.started_at = now elif status in (TaskStatus.COMPLETED, TaskStatus.FAILED, TaskStatus.CANCELLED): task.completed_at = now if result is not None: task.result = result if error_info is not None: task.error_info = error_info await self.session.commit() await self.session.refresh(task) return task