Перейти к основному содержимому

POST /api/v1/agent/calendar/slots/archive — Архивация слотов (поштучно или по батчу)

Назначение

Агент убирает свою опубликованную доступность из оборота: архивирует один слот (SlotId) или все слоты одной публикации разом (BatchId) — например, при изменении графика или отпуске. Архивируются только свободные и истёкшие слоты; занятый слот (Booked) архивировать нельзя — сначала нужно разрешить судьбу записи (appointment.md). Ответ — отчёт: сколько заархивировано и какие слоты пропущены как занятые.

Metadata

ПолеЗначение
Сервис/контекстcalendar
API areaagent
Feature groupslots
Статусdraft
Документdocs/06-services/calendar/api/agent/archive-slot.md

Актор и доступ

ПроверкаЗначение
Actor typeUser (агент агентства / консультант ПИН — владелец слотов)
Auth policy / Permissionscalendar.slots.manage
Scope (RBAC)организация — компания актора (агентство / ПИН)
Record-levelархивируются только собственные слоты: AgentUserId = actor; чужой слот/батч не раскрывается — CAL_SLOT_NOT_FOUND (fail-closed)
Tenant isolationTenantId из токена (ADR-0052)
Auditupdate (переходы в Archived); BatchId — след массовой операции

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/v1/agent/calendar/slots/archive
Success status200 (отчёт архивации)
Idempotency headerне применяется: повтор структурно безопасен — уже Archived слоты учитываются как AlreadyArchivedCount, состояние не меняется
Correlationсквозной trace через заголовок traceparent

Входные данные / параметры запроса

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
TenantIdidentity claimsGuidДаRequiredTenant isolation
SlotIdbodyGuid?Условноровно одно из SlotId/BatchId должно быть задано; оба или ни одного → VALIDATION_FAILED
BatchIdbodyGuid?Условноровно одно из SlotId/BatchId; архивирует все подходящие слоты публикации (publish-slots.md)

Модель ответа ArchiveSlotsResult

ПолеТипОбязательностьИсточникMaskingОписание
ArchivedCountintДаcomputedСколько слотов переведено Available/Expired → Archived этим запросом.
AlreadyArchivedCountintДаcomputedСколько слотов уже были Archived (идемпотентный повтор — no-op).
SkippedBookedGuid[]ДаcomputedИдентификаторы Booked-слотов батча, пропущенных из-за активной записи (только режим BatchId); пустой массив, если пропусков нет.

Алгоритм

  1. Контекст и доступ. Актор — владелец слотов с permission calendar.slots.manage (иначе CAL_SLOT_ACCESS_DENIED).
  2. Проверка входа. Задано ровно одно из SlotId/BatchId — иначе VALIDATION_FAILED.
  3. Режим 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).
  4. Режим BatchId (по публикации). Найти все слоты актора с этим BatchId; ни одного → CAL_SLOT_NOT_FOUND. Каждый слот батча обрабатывается по правилу шага 3, но Booked-слоты не валят операцию, а пропускаются и попадают в SkippedBooked — их судьбу агент решает через записи. Все переходы в Archived применяются в одном согласованном изменении: либо заархивирован весь отчётный набор, либо ничего.
  5. Конкуренция с бронированием. Архивация Available-слота — условный переход под построчной блокировкой: если параллельный book-slot успел первым, слот уже Booked — поштучно это отказ CAL_SLOT_BOOKED, в батче — пропуск в SkippedBooked. Ровно один из конкурентов побеждает, промежуточных состояний не видно.
  6. Результат. { ArchivedCount, AlreadyArchivedCount, SkippedBooked[] } → HTTP 200. Заархивированные слоты немедленно исчезают с витрины свободных слотов (../public/list-available-slots.md).

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
VALIDATION_FAILED400заданы оба SlotId и BatchId, либо ни одного«Укажите слот или батч (ровно одно)»no retry
UNAUTHORIZED401нет/просрочен JWT«Требуется авторизация»no retry
CAL_SLOT_ACCESS_DENIED403нет permission calendar.slots.manage«Недостаточно прав»no retry
CAL_SLOT_NOT_FOUND404слот/батч не существует или принадлежит другому агенту (fail-closed)«Слот не найден»no retry
CAL_SLOT_BOOKED409поштучная архивация 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.

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы