POST /api/v1/agent/calendar/slots/archive — Архивация слотов (поштучно или по батчу)
Назначение
Агент убирает свою опубликованную доступность из оборота: архивирует один слот (SlotId) или все слоты
одной публикации разом (BatchId) — например, при изменении графика или отпуске. Архивируются только
свободные и истёкшие слоты; занятый слот (Booked) архивировать нельзя — сначала нужно разрешить судьбу
записи (appointment.md). Ответ — отчёт: сколько заархивировано и какие слоты
пропущены как занятые.
| Поле | Значение |
|---|
| Сервис/контекст | calendar |
| API area | agent |
| Feature group | slots |
| Статус | draft |
| Документ | docs/06-services/calendar/api/agent/archive-slot.md |
Актор и доступ
| Проверка | Значение |
|---|
| Actor type | User (агент агентства / консультант ПИН — владелец слотов) |
| Auth policy / Permissions | calendar.slots.manage |
| Scope (RBAC) | организация — компания актора (агентство / ПИН) |
| Record-level | архивируются только собственные слоты: AgentUserId = actor; чужой слот/батч не раскрывается — CAL_SLOT_NOT_FOUND (fail-closed) |
| Tenant isolation | TenantId из токена (ADR-0052) |
| Audit | update (переходы в Archived); BatchId — след массовой операции |
HTTP-контракт
| Поле | Значение |
|---|
| Method | POST |
| Route | /api/v1/agent/calendar/slots/archive |
| Success status | 200 (отчёт архивации) |
| Idempotency header | не применяется: повтор структурно безопасен — уже Archived слоты учитываются как AlreadyArchivedCount, состояние не меняется |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|
TenantId | identity claims | Guid | Да | Required | Tenant isolation |
SlotId | body | Guid? | Условно | ровно одно из SlotId/BatchId должно быть задано; оба или ни одного → VALIDATION_FAILED | — |
BatchId | body | Guid? | Условно | ровно одно из SlotId/BatchId; архивирует все подходящие слоты публикации (publish-slots.md) | — |
Модель ответа ArchiveSlotsResult
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|
ArchivedCount | int | Да | computed | — | Сколько слотов переведено Available/Expired → Archived этим запросом. |
AlreadyArchivedCount | int | Да | computed | — | Сколько слотов уже были Archived (идемпотентный повтор — no-op). |
SkippedBooked | Guid[] | Да | computed | — | Идентификаторы Booked-слотов батча, пропущенных из-за активной записи (только режим BatchId); пустой массив, если пропусков нет. |
Алгоритм
- Контекст и доступ. Актор — владелец слотов с permission
calendar.slots.manage (иначе
CAL_SLOT_ACCESS_DENIED).
- Проверка входа. Задано ровно одно из
SlotId/BatchId — иначе VALIDATION_FAILED.
- Режим
SlotId (поштучно). Найти слот по идентификатору среди слотов актора в его tenant; не найден
или чужой → CAL_SLOT_NOT_FOUND. Дальше по статусу:
Available / Expired → перевод в Archived (переход стейт-машины слота,
availability-slot.md);
Archived → no-op: 200 с AlreadyArchivedCount = 1 (идемпотентный повтор);
Booked → отказ CAL_SLOT_BOOKED (INV-SLT-4): слот управляется каскадами записи — сначала
отклонить/отменить запись (decline-appointment.md,
cancel-appointment.md).
- Режим
BatchId (по публикации). Найти все слоты актора с этим BatchId; ни одного →
CAL_SLOT_NOT_FOUND. Каждый слот батча обрабатывается по правилу шага 3, но Booked-слоты не
валят операцию, а пропускаются и попадают в SkippedBooked — их судьбу агент решает через записи.
Все переходы в Archived применяются в одном согласованном изменении: либо заархивирован весь
отчётный набор, либо ничего.
- Конкуренция с бронированием. Архивация
Available-слота — условный переход под построчной
блокировкой: если параллельный book-slot успел первым, слот уже Booked — поштучно это отказ
CAL_SLOT_BOOKED, в батче — пропуск в SkippedBooked. Ровно один из конкурентов побеждает,
промежуточных состояний не видно.
- Результат.
{ ArchivedCount, AlreadyArchivedCount, SkippedBooked[] } → HTTP 200.
Заархивированные слоты немедленно исчезают с витрины свободных слотов
(../public/list-available-slots.md).
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|
VALIDATION_FAILED | 400 | заданы оба SlotId и BatchId, либо ни одного | «Укажите слот или батч (ровно одно)» | no retry |
UNAUTHORIZED | 401 | нет/просрочен JWT | «Требуется авторизация» | no retry |
CAL_SLOT_ACCESS_DENIED | 403 | нет permission calendar.slots.manage | «Недостаточно прав» | no retry |
CAL_SLOT_NOT_FOUND | 404 | слот/батч не существует или принадлежит другому агенту (fail-closed) | «Слот не найден» | no retry |
CAL_SLOT_BOOKED | 409 | поштучная архивация Booked-слота (INV-SLT-4) | «Слот занят записью — сначала разрешите судьбу записи» | no retry (разрешить запись, повторить) |
Постусловия
После 200: все затронутые слоты — в терминальном статусе Archived (INV-SLT-5), с витрины и из
предложения агента убраны; активные записи не затронуты (Booked-слоты остались как были). Интеграционные
события не публикуются — архивация слотов внутреннее дело контекста
(availability-slot.md).
Acceptance Criteria
- AC-1 (поштучно). Given
Available-слот актора. When архивация по SlotId. Then 200,
ArchivedCount = 1; слот Archived и с витрины исчез.
- AC-2 (занятый). Given
Booked-слот. When архивация по SlotId. Then 409 CAL_SLOT_BOOKED; слот и
запись не изменились.
- AC-3 (батч со смешанными статусами). Given батч из 10 слотов: 6
Available, 2 Expired, 1 Booked,
1 Archived. When архивация по BatchId. Then 200: ArchivedCount = 8, AlreadyArchivedCount = 1,
SkippedBooked содержит один идентификатор; Booked-слот не изменился.
- AC-4 (идемпотентный повтор). Given батч уже заархивирован. When повторная архивация по
BatchId.
Then 200: ArchivedCount = 0, AlreadyArchivedCount = числу слотов; состояние не изменилось.
- AC-5 (гонка с бронированием). Given параллельные
book-slot и архивация одного Available-слота.
Then побеждает ровно один: либо слот Booked с записью (архивация — 409/пропуск), либо слот Archived
(бронирование — 409 CAL_SLOT_NOT_AVAILABLE).
- AC-6 (чужой слот). Given слот другого агента. When архивация по
SlotId. Then
404 CAL_SLOT_NOT_FOUND.
- AC-7 (оба ключа). Given в body заданы и
SlotId, и BatchId. Then 400 VALIDATION_FAILED.
Совместимость и наблюдаемость
- Новые optional поля запроса/отчёта — additive; изменение семантики режимов (
SlotId/BatchId) —
breaking.
- Logs:
slots archived (TenantId, AgentUserId, SlotId/BatchId, ArchivedCount,
SkippedBooked.Count); metrics: calendar_slots_archived_total{mode}; traces — сквозной traceparent.
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные документы