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

GET /api/investors/me/referral-links/lookup/{id} — Резолв реф-ссылки по id

Назначение

Точечно резолвит одну собственную реферальную ссылку по её id в облегчённое lookup-представление ReferralLinkLookupEntry. Нужен, чтобы отобразить выбранное значение в выпадающем списке на фронте: когда у контрола есть только сохранённый id ссылки, но нужно показать её url/описание без загрузки всего списка (lookup-referral-links.md).

Metadata

ПолеЗначение
Сервис/контекстreferral
API areapublic
Feature groupreferral-links
Статусdraft

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

ПроверкаЗначение
Actor typeUser (авторизованный пользователь)
Auth policy / Permissionsreferral.links.view-self
Scopeself (роль не требуется)
Record-levelфильтр OwnerUserId = UserId актора; чужая ссылка неотличима от несуществующей (нет enumeration по id, INV-REF-L4)
Tenant isolationне применяется (record-level по владельцу, ADR-0052)
Auditnone (несенситивное чтение собственных данных)

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/investors/me/referral-links/lookup/{id}
Success status200
Idempotencyне применяется (safe GET)
CorrelationOpenTelemetry trace (traceparent)

Маршрутизация: статический сегмент lookup имеет приоритет над route-параметром {id} уровня /referral-links/{id} — коллизий нет.

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
UserIdidentity claims (sub)GuidДавалидный JWT (подпись, exp, notBefore)401 при отсутствии/просрочке
idrouteGuidДаid != Guid.Empty; принадлежность не доверяемrecord-level guard OwnerUserId = UserId; чужой idREFERRAL_LINK_NOT_FOUND

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

Результат — одиночная item-модель (не список). Дом модели — lookup-referral-links.md; ниже — проекция полей из referral-link.md.

ПолеТипОбязательностьИсточникMaskingОписание
idstring (Guid)ДаReferralLinkEntity.IdИдентификатор ссылки.
urlstringДаReferralLinkEntity.UrlSlug (lowercase); полный URL собирает фронт.
descriptionstring?НетReferralLinkEntity.DescriptionОписание канала/назначения ссылки.
isDefaultboolДаReferralLinkEntity.IsDefaultПризнак ссылки «по умолчанию» владельца (INV-REF-L2).
channelReferralChannel (string enum)?НетReferralLinkEntity.ChannelКанал распространения (unknown при чтении → fallback Other, referral-link.md).

Предусловия и постусловия

  • Предусловия. Пользователь авторизован. Резолвится только действующая (не удалённая) собственная ссылка.
  • Постусловия. Состояние не меняется (чтение); VisitsCount не инкрементируется (перехода по ссылке нет — счётчик растит только validate-referral-link.md); события не публикуются.

Алгоритм

  1. Контекст и доступ. Операцию выполняет авторизованный пользователь; UserId — из claim sub (иначе 401); id — из route; требуется право referral.links.view-self.
  2. Проверка входа. id != Guid.Empty — иначе ValidationError.
  3. Что читаем. Собственную ссылку по Id = id и OwnerUserId = UserId, не удалённую (DeletedAt == null). Отсутствие / чужая / soft-deleted ссылка неразличимы и дают REFERRAL_LINK_NOT_FOUND (INV-REF-L4: без раскрытия существования). Удалённая ссылка не резолвится — согласовано с referral-link.md.
  4. Маппинг. Поле-в-поле найденной ссылки → entry: id, url, description, isDefault, channel.
  5. Результат. ReferralLinkLookupEntry → HTTP 200; при отсутствии — REFERRAL_LINK_NOT_FOUND (HTTP 404, ProblemDetails). Консистентность — read-your-writes (основная PostgreSQL).

Диаграмма

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
REFERRAL_LINK_NOT_FOUND404id не существует / чужая ссылка / soft-deleted (INV-REF-L4, без раскрытия существования)«Ссылка не найдена»no retry
ValidationError400id == Guid.Emptyсписок ошибок полейисправить вход
— (auth)401Нет/просрочен/невалиден JWTбез тела схемыre-login

Критерии приёмки

  • Given авторизованный владелец и id его действующей ссылки, When резолв по id, Then 200 с ReferralLinkLookupEntry (id, url, description, isDefault, channel).
  • Given id чужой ссылки, When резолв, Then REFERRAL_LINK_NOT_FOUND (404) — чужая ссылка неотличима от несуществующей (INV-REF-L4, без enumeration).
  • Given id soft-deleted собственной ссылки, When резолв, Then REFERRAL_LINK_NOT_FOUND (404).
  • Given id == Guid.Empty, When резолв, Then ValidationError (400).
  • Given отсутствует/просрочен токен, When резолв, Then 401.

Совместимость и наблюдаемость

  • Контракт стабилен; additive: у ReferralLinkLookupEntry — optional channel (ReferralChannel), без breaking change; unknown enum при чтении → fallback Other + warning (referral-link.md). Удаление/переименование полей — breaking (migration + rollout).
  • Logs: UserId, id, outcome=found|not_found — без PII; metrics: referral_link_lookup_by_id_total{outcome}, latency p95; traces — OpenTelemetry. Dashboard — referral-overview; алерт на p95 > 300 мс (SLO public API, README.md). Всплеск outcome=not_found от одного актора — индикатор перебора чужих id (record-level guard INV-REF-L4 скрывает существование, но метрика полезна для rate-based-детекции).

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

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

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