POST /api/developers/v1/ranking-rules — Создание правила ранжирования
Назначение
Менеджер застройщика создаёт правило приоритета показа своего проекта или объекта недвижимости в
рамках своих объектов (ranking-rule.md). Правило создаётся сразу
Active; значение Priority в той же транзакции денормализуется в DeveloperPriority затронутых
карточек (INV-4 RecomputeInTx) и начинает действовать в сортировке «Рекомендованные» (формула —
ranking-rule.md; на явные сортировки пользователя не влияет).
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | catalog |
| API area | developers |
| Feature group | ranking |
| Статус | draft |
| Документ | docs/06-services/catalog/api/developers/create-ranking-rule.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (менеджер застройщика — JWT identity) |
| Auth policy / Permissions | permission catalog.ranking.manage в scope организации/группы проектов/проекта (Scoped-RBAC) |
| Scope (RBAC) | организация и ниже; при scope «проект» цель обязана входить в scope-проекты актора |
| Tenant isolation | TenantId = CompanyId застройщика (ADR-0052); дополнительно INV-2 OwnObjectsOnly — карточка цели имеет DeveloperId == TenantId |
| Audit | create: actor-stamping CreatedByUserId + Version = 1; история — soft-delete-модель |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | POST |
| Route | /api/developers/v1/ranking-rules |
| Success status | 201 |
| Idempotency header | не применяется (естественная идемпотентность: повтор упрётся в INV-1 CATALOG_RANKING_RULE_DUPLICATE) |
| Correlation | сквозной trace через traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims | Guid | Да | Required (= CompanyId) | tenant isolation |
TargetKind | body | string (enum RankingTargetKind) | Да | ∈ {Project, PropertyObject} | — |
TargetId | body | Guid | Да | != Guid.Empty; существование и владение — шаг 2 (INV-2) | — |
Priority | body | int | Да | 0..1000 (INV-3 PriorityRange) | — |
Модель ответа RankingRuleResult
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | DeveloperRankingRule.Id | — | Созданное правило. |
TargetKind | string (enum RankingTargetKind) | Да | DeveloperRankingRule.TargetKind | — | — |
TargetId | Guid | Да | DeveloperRankingRule.TargetId | — | — |
Priority | int | Да | DeveloperRankingRule.Priority | — | — |
Status | string (enum RankingRuleStatus) | Да | DeveloperRankingRule.Status | — | Active. |
CardsRecomputed | int | Да | счётчик пересчитанных карточек | — | Сколько карточек получили новый DeveloperPriority. |
Version | long | Да | DeveloperRankingRule.Version | — | 1. |
Алгоритм
- Контекст и доступ. Актор — менеджер застройщика (JWT + permission
catalog.ranking.manage);TenantIdиз claims. Идемпотентность не применяется. - Проверка входа.
TargetKind∈ enum иTargetId != Guid.Empty— нарушение →ValidationError;Priorityвне диапазона0..1000(INV-3 PriorityRange) →CATALOG_RANKING_PRIORITY_OUT_OF_RANGE(400). Существование и владение (INV-2 OwnObjectsOnly): карточка цели (проект или объект недвижимости поTargetKind) существует; нет →CATALOG_RANKING_TARGET_NOT_FOUND(404);DeveloperId != TenantId→CATALOG_RANKING_TARGET_NOT_OWNED(403);Status = Delisted→CATALOG_RANKING_TARGET_NOT_FOUND(404 — удалённая цель неотличима от несуществующей для пользователя). Дубль (INV-1 SingleRulePerTarget): живое правило на ту же цель уже есть →CATALOG_RANKING_RULE_DUPLICATE(409). Scope: при scope-назначении уровня «проект/группа проектов» — проект цели (TargetIdдля Project, проект объекта для PropertyObject) ∈ scope-проектов актора, иначеFORBIDDEN. - Что читаем. Карточку цели. Для пересчёта каскада: при
TargetKind=Project— объекты недвижимости проекта (кромеDelisted) и их активные правила объектов недвижимости (правило объекта перекрывает проектное — INV-5); приTargetKind=PropertyObject— карточку объекта недвижимости. - Правила и переходы. Создаётся правило со статусом
Active(ranking-rule.md, stateDiagram:[*] → Active),CreatedByUserId= актор,Version = 1; фиксируетсяRankingRuleChangedEvent(Id,TenantId,TargetKind,TargetId,Priority,Status). Каскад INV-5 (единственная точка расчёта): эффективныйDeveloperPriorityобъекта недвижимости =Active-правило объекта ??Active-правило его проекта ??0. Маппинг:TargetKind=Project→ карточка проекта получаетDeveloperPriority = Priority, и каждый объект недвижимости проекта без собственногоActive-правила →Priority;TargetKind=PropertyObject→ один объект недвижимости →Priority. - Что меняется. Создаётся правило; обновляются затронутые карточки (счётчик →
CardsRecomputed). Порядок: правило → карточки. Единственность страхуется unique partial indexux_ranking_rules_target(гонка двух создающих запросов →CATALOG_RANKING_RULE_DUPLICATE); правка не должна затирать чужую (конфликт с консюмером → retry). - События. Только доменное
RankingRuleChangedEvent(в том же коммите). Интеграционные события не публикуются — ранжирование потребляется только витриной (ranking-rule.md, «События»). - Согласованность. Правило + карточки — в одном согласованном изменении (INV-4 RecomputeInTx).
- Результат.
RankingRuleResult(201, Location:/api/developers/v1/ranking-rules/{id}); отказ → ProblemDetails.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|---|
| Проекции / поиск | обновление DeveloperPriority в catalog.project_cards/catalog.property_object_cards (ленточный индекс ix_*_feed) в той же транзакции |
Интеграционные события не публикуются; внешних вызовов и уведомлений нет.
В ленте без поиска (сортировка Recommended на PostgreSQL-индексе ix_*_feed) новый DeveloperPriority
виден сразу после коммита. В результатах поиска (searchTerm, Elasticsearch — ADR-0059) приоритет
проявляется после синхронизации search-проекции карточек (окно свежести проекции — секунды); пользователь
может краткий промежуток видеть прежний порядок в поиске при уже применённом приоритете в обычной ленте.
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
ValidationError | 400 | Неизвестное значение TargetKind / пустой TargetId | Ошибка валидации запроса | no retry |
CATALOG_RANKING_PRIORITY_OUT_OF_RANGE | 400 | Priority вне 0..1000 (INV-3) | Приоритет вне допустимого диапазона | no retry |
UNAUTHORIZED | 401 | Нет/просрочен JWT | Требуется авторизация | no retry |
FORBIDDEN | 403 | Нет permission / цель вне scope-проектов актора | Доступ запрещён | no retry |
CATALOG_RANKING_TARGET_NOT_OWNED | 403 | card.DeveloperId != TenantId (INV-2) | Объект не принадлежит вашей компании | no retry |
CATALOG_RANKING_TARGET_NOT_FOUND | 404 | Карточка цели не существует или Delisted | Объект не найден в каталоге | no retry |
CATALOG_RANKING_RULE_DUPLICATE | 409 | Живое правило на цель уже есть (INV-1) | Правило для этого объекта уже существует | no retry (использовать update) |
CONCURRENCY_CONFLICT | 409 | Гонка версий карточек с консюмером | Повторите операцию | retry |
Совместимость и наблюдаемость
- Новые optional поля — additive; удаление/переименование/смена типа — breaking (migration + rollout).
- Logs:
ranking-rule-created(ruleId, tenantId, targetKind, targetId, priority, cardsRecomputed); metrics:catalog_developers_operations_total{operation="create-ranking-rule"}, duration,catalog_projection_recompute_total{trigger="ranking"}; traces: span приёма → span обработки → span обращения к хранилищу; correlation — сквозной trace черезtraceparent. Dashboard catalog-overview.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (5): Catalog Developers API — реестр методов и общие правила области, DELETE /api/developers/v1/ranking-rules/{ruleId} — Удаление правила ранжирования, GET /api/developers/v1/ranking-rules — Список правил ранжирования застройщика, POST /api/developers/v1/ranking-rules/{ruleId}/enable — Включение правила ранжирования, PUT /api/developers/v1/ranking-rules/{ruleId}/priority — Изменение приоритета правила
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущность: ranking-rule.md (INV-1..5, формула «Рекомендованные»);
карточки: catalog-project-card.md,
catalog-property-object-card.md (
DeveloperPriority). - Соседние операции: list-ranking-rules.md, update-ranking-rule-priority.md, enable-ranking-rule.md, delete-ranking-rule.md.
- ADR-0052.