DELETE /api/developers/v1/ranking-rules/{ruleId} — Удаление правила ранжирования
Назначение
Soft-delete правила ранжирования (ranking-rule.md, stateDiagram:
Active|Disabled → SoftDelete): правило исчезает из списка застройщика, история настроек
сохраняется (DeletedAt/DeletedByUserId), карточки пересчитываются по каскаду INV-5 без этого
правила в той же транзакции (INV-4). Отличие от Disable: удалённое правило нельзя включить обратно —
для той же цели создаётся новое (unique-индекс с фильтром deleted_at is null освобождает цель).
| Поле | Значение |
|---|
| Сервис/контекст | catalog |
| API area | developers |
| Feature group | ranking |
| Статус | draft |
| Документ | docs/06-services/catalog/api/developers/delete-ranking-rule.md |
Актор и доступ
| Проверка | Значение |
|---|
| Actor type | User (менеджер застройщика — JWT identity) |
| Auth policy / Permissions | permission catalog.ranking.manage в scope организации/группы проектов/проекта |
| Scope (RBAC) | организация и ниже |
| Tenant isolation | TenantId = CompanyId (ADR-0052); чужое правило невидимо |
| Audit | soft-delete: DeletedAt/DeletedByUserId + Version++ (история приоритетов — требование аудита ranking-rule.md) |
HTTP-контракт
| Поле | Значение |
|---|
| Method | DELETE |
| Route | /api/developers/v1/ranking-rules/{ruleId} |
| Success status | 200 |
| Idempotency header | не применяется (повтор → 404 — запись уже снята) |
| Correlation | сквозной trace через traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|
TenantId | identity claims | Guid | Да | Required | tenant isolation |
RuleId | route | Guid | Да | != Guid.Empty; существование — шаг 2 | — |
Модель ответа RankingRuleResult
Модель — дом create-ranking-rule.md. Дельта: Status — статус на момент
удаления; CardsRecomputed — карточки, вернувшиеся к каскадному приоритету (для удаления
Disabled-правила — 0, карточки уже без него).
Алгоритм
- Контекст и доступ. Актор — менеджер застройщика (JWT +
catalog.ranking.manage); TenantId из
claims.
- Проверка входа.
RuleId != Guid.Empty → ValidationError. Существование: правило (в рамках
своего tenant, не удалённое) найдено; нет → CATALOG_RANKING_RULE_NOT_FOUND (404). Состояние:
Active и Disabled — оба допускают удаление.
- Что читаем. Только при
Status == Active (иначе пересчёт не нужен): каскадные данные — как в
create-ranking-rule.md шаг 3; для TargetKind=PropertyObject
дополнительно активное проектное правило (для приоритета отката).
- Правила и переходы.
Active|Disabled → SoftDelete: DeletedAt = now, DeletedByUserId =
актор, Version++; фиксируется RankingRuleChangedEvent. Каскад INV-5 без правила (только если был
Active): TargetKind=PropertyObject → объект недвижимости получает DeveloperPriority = активное проектное правило?.Priority ?? 0; TargetKind=Project → карточка проекта DeveloperPriority = 0,
объекты недвижимости проекта без собственного Active-правила → 0.
- Что меняется. Правило переводится в soft-delete (мутация, не физическое удаление); обновляются
затронутые карточки (счётчик →
CardsRecomputed). Snapshot-эффект: soft-delete сразу освобождает
цель (partial unique index deleted_at is null) — на ту же цель можно создать новое правило в
следующем запросе. Правка не должна затирать чужую (конкурентный доступ): два параллельных
DELETE одного правила → один переводит в soft-delete, второй видит уже снятое правило и получает
CATALOG_RANKING_RULE_NOT_FOUND (404); гонка DELETE с enable/update-priority → проигравший по
версии получает CONCURRENCY_CONFLICT (409) и перечитывает состояние.
- События. Только доменное
RankingRuleChangedEvent; интеграционных нет.
- Согласованность. Правило + карточки — в одном согласованном изменении (INV-4).
- Результат.
RankingRuleResult (200); отказ → ProblemDetails.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|
| Проекции / поиск | откат DeveloperPriority карточек (индекс ix_*_feed) |
Интеграционные события не публикуются; внешних вызовов и уведомлений нет.
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|
ValidationError | 400 | Пустой RuleId | Ошибка валидации запроса | no retry |
UNAUTHORIZED | 401 | Нет/просрочен JWT | Требуется авторизация | no retry |
FORBIDDEN | 403 | Нет permission в scope | Доступ запрещён | no retry |
CATALOG_RANKING_RULE_NOT_FOUND | 404 | Правило не существует / чужой tenant / уже удалено | Правило не найдено | no retry |
CONCURRENCY_CONFLICT | 409 | Гонка версий | Повторите операцию | retry |
Совместимость и наблюдаемость
- Новые optional поля — additive; удаление/переименование — breaking.
- Logs:
ranking-rule-deleted (ruleId, tenantId, status-на-момент, cardsRecomputed); metrics:
catalog_developers_operations_total{operation="delete-ranking-rule"}; traces + correlation —
сквозной trace через traceparent. Dashboard catalog-overview.
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные документы