Skip to content

Архитектура движка бронирования

Задача

Расписание N последовательных услуг по M доступным ресурсам с обнаружением конфликтов.

Пример: клиент бронирует стрижку (60 мин, любой из 3 стилистов) + окрашивание (90 мин, только колорист-специалист). Движок должен найти все допустимые комбинации на заданную дату с учётом свободных окон каждого ресурса, буферов и существующих записей.


Компоненты

Компонент Роль
ChainFinder Рекурсивный поиск с возвратом — находит все допустимые цепочки слотов
BookingScorer Ранжирование после поиска — оценивает и пересортировывает решения, не затрагивая сам поиск
SlotCalculator Низкоуровневая арифметика — разбивка окон, объединение занятых интервалов, выравнивание по сетке

Скоринг полностью отделён от поиска. ChainFinder возвращает «сырые» решения; BookingScorer оценивает их независимо. Изменение весов скоринга никак не влияет на найденные слоты.


Поток данных

```mermaid flowchart TD A["BookingEngineRequest\n+ list[MasterAvailability]"] B["ChainFinder.find()"] C["Цикл с возвратом\n_SlotCandidate (только слоты)"] D["EngineResult\n(сырые решения, score=0)"] E["BookingScorer.score()"] F["EngineResult\n(с оценками, отсортировано по убыванию)"]

A --> B --> C --> D --> E --> F

```

При поиске на несколько дней (find_nearest) ChainFinder вызывает переданный пользователем get_availability_for_date для каждой кандидатной даты, пока не найдёт решения или не исчерпает search_days.


BookingMode

Режим Когда использовать
SINGLE_DAY Все услуги в один календарный день. По умолчанию.
MULTI_DAY Услуги распределены по разным дням. (Запланировано — ещё не реализовано.)
MASTER_LOCKED Клиент находится на личной странице конкретного мастера; учитывается только этот ресурс.

Провайдер-интерфейсы

Движок принимает готовые объекты MasterAvailability. Постройте их из своего ORM через провайдер-интерфейсы:

class MyAvailabilityProvider:
    def build_masters_availability(
        self, master_ids: list[str], target_date: date, ...
    ) -> dict[str, MasterAvailability]:
        # запрос к БД, объединение расписаний, вычитание занятых слотов
        ...

AvailabilityProvider (один день) и build_availability_batch (диапазон дат для find_nearest) — две точки интеграции. Движок никогда не импортирует ваши модели.


Производительность

Во время поиска с возвратом движок использует лёгкие объекты _SlotCandidate с атрибутом __slots__. Pydantic-валидация внутри рекурсии не выполняется. SingleServiceSolution и BookingChainSolution (полные Pydantic DTO) создаются только для итогового набора решений, минимизируя аллокации на горячем пути.


Быстрый старт

from codex_services.booking.slot_master import find_slots
from datetime import date

result = find_slots(
    request_data={
        "service_requests": [
            {"service_id": "haircut", "duration_minutes": 60, "possible_master_ids": ["m1", "m2"]},
            {"service_id": "color",   "duration_minutes": 90, "possible_master_ids": ["m2"]},
        ],
        "booking_date": str(date.today()),
    },
    resources_availability=[
        {"master_id": "m1", "free_windows": [["2024-05-15T09:00:00", "2024-05-15T18:00:00"]]},
        {"master_id": "m2", "free_windows": [["2024-05-15T09:00:00", "2024-05-15T18:00:00"]]},
    ],
)
if result["has_solutions"]:
    print(result["solutions"][0]["starts_at"])

Смотри также

  • API Reference — Slot Master
  • Будущее: tasks/using_booking_engine.md — пошаговое руководство по интеграции