GET /api/investors/me/referral-links/{id}/registrations — Обезличенный журнал одной ссылки (getLinkRegistrations)
Назначение
Постраничный обезличенный журнал регистраций по одной конкретной ссылке владельца: кто и когда
пришёл по ней (Initial/Rebind), без ФИО/телефона. Владелец открывает журнал из карточки своей
ссылки на /profile/partner (детализация одного канала). PII приглашённого раскрывается только в
admin-выдаче (../admin/get-link-registrations.md, permission
referral.registrations.view-all). Общий конверт и paging-модели — README.md.
| Поле | Значение |
|---|
| Сервис/контекст | referral |
| API area | public |
| Feature group | referral-registrations |
| Статус | approved |
| Документ | docs/06-services/referral/api/public/get-link-registrations.md |
Актор и доступ
| Проверка | Значение |
|---|
| Actor type | User |
| Auth policy / Permissions | authenticated; permission referral.registrations.view-self |
| Scope (RBAC) | self |
| Record-level | ownership guard: ссылка {id} должна принадлежать актору (OwnerUserId == UserId, INV-REF-L4); чужая/несуществующая неотличима → REFERRAL_LINK_NOT_FOUND (нет user enumeration) |
HTTP-контракт
| Поле | Значение |
|---|
| Method | GET |
| Route | /api/investors/me/referral-links/{id}/registrations |
| Success status | 200 |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|
UserId | identity claims (sub) | Guid | Да | валидный токен | 401 при отсутствии |
id | route | Guid | Да | непустой; существующая ссылка владельца | record-level guard OwnerUserId == UserId |
Offset | query | int | Да | min 0, max 2147483647 | — |
Take | query | int | Да | min 1, max 1000 | ограничение размера выдачи |
Модель ответа ReferralRegistrationItemPagingResult
Контейнер offset-paging (дом paging-моделей — README.md).
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|
meta | PagingResultMeta | Да | projection | — | total/take/offset — README.md. |
items | ReferralRegistrationItem[] | Да | projection | обезличено | Страница записей журнала (дом item-модели ниже). |
Модель ReferralRegistrationItem (дом item-модели)
Первая операция-владелец дома item-модели; переиспользуется
get-all-registrations.md (журнал по всем ссылкам). Проекция полей —
из referral-registration.md, только скалярные id (без PII).
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|
registeredAt | string (ISO 8601) | Да | ReferralRegistrationEntity.RegisteredAt | — | Момент регистрации/привязки (ключ сортировки). |
referralLinkId | string (Guid) | Да | ReferralRegistrationEntity.ReferralLinkId | — | Ссылка, по которой пришёл пользователь (= {id} запроса). |
linkUrl | string | Да | ReferralRegistrationEntity.LinkUrl (денормализация) | — | Историч. slug на момент записи: показывается даже после переименования/удаления ссылки. |
referredInvestorId | string (Guid) | Да | ReferralRegistrationEntity.ReferredUserId | — | Приглашённый пользователь — обезличенный идентификатор. ФИО/телефон реплики не присоединяются. |
registrationType | string (enum ReferralRegistrationType) | Да | ReferralRegistrationEntity.RegistrationType | — | Initial / Rebind (referral-registration.md). |
Исходы правил привязки (AppliedRule, LeadRequested) в public-выдачу не добавляются (владельцу
не нужна механика привязки); PII (ФИО/телефон/email) — только admin.
Алгоритм
- Контекст и доступ. Пользователь авторизован; требуется permission
referral.registrations.view-self (иначе FORBIDDEN).
- Проверка входа.
id непустой, Offset >= 0, 1 <= Take <= 1000 — иначе ValidationError.
- Проверка владения. Ссылка
{id} должна принадлежать актору (OwnerUserId == UserId,
INV-REF-L4). Если ссылки нет или она принадлежит другому владельцу → REFERRAL_LINK_NOT_FOUND
(чужая, несуществующая и удалённая ссылка неотличимы — нет раскрытия чужого id). Журнал читается
и по soft-deleted ссылке (запись журнала переживает удаление ссылки, INV-REF-R2).
- Выдача. Возвращается страница записей журнала по ссылке (
ReferralLinkId == id),
упорядоченных по дате регистрации RegisteredAt desc, при равных значениях — по Id (стабильный
tie-break; правило порядка/консистентности — README.md); журнал append-only —
исторические страницы стабильны. ФИО/телефон не отдаются (обезличенная проекция,
ReferralUserReplica не присоединяется). meta содержит полное число записей под фильтром, размер
и смещение страницы.
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|
| — (auth) | 401 | Нет/невалиден токен | «Требуется авторизация» | re-login |
ValidationError | 400 | id пустой или Offset/Take вне диапазона | перечень полей | исправить |
REFERRAL_LINK_NOT_FOUND | 404 | Ссылка не найдена или принадлежит другому владельцу (INV-REF-L4, без раскрытия чужой ссылки) | «Ссылка не найдена» | no retry |
Постусловие
Состояние не изменяется (обезличенное чтение): журнал, ссылка и реплика не трогаются, PII не отдаётся,
события не публикуются.
Acceptance Criteria
- Given авторизованный владелец и его ссылка с регистрациями, When
GET …/referral-links/{id}/registrations, Then 200 со страницей ReferralRegistrationItem
(registeredAt desc), обезличенной (только referredInvestorId); linkUrl — исторический slug на
момент записи.
- Given ссылка soft-deleted, но с историческими записями, When запрос, Then
200 — журнал
переживает удаление ссылки (INV-REF-R2).
- Given ссылка чужая, несуществующая или удалённая, When запрос, Then
404 REFERRAL_LINK_NOT_FOUND (все три случая неразличимы — нет раскрытия чужого id, INV-REF-L4).
- Given ссылка принадлежит актору, но регистраций нет, When запрос, Then
200 с
meta.total = 0, items = [].
- Given актор без
referral.registrations.view-self, When запрос, Then 403 FORBIDDEN; без
токена — 401; id пустой / Offset/Take вне диапазона — 400 ValidationError.
- Given записи с равным
registeredAt, When постраничный обход, Then порядок стабилен по
вторичному ключу Id (append-only журнал — исторические страницы не смещаются, README.md).
Совместимость и наблюдаемость
- Контракт стабилен;
ReferralRegistrationItem — обезличенная проекция. Новые optional поля —
additive; удаление/переименование/смена типа полей item-а — breaking (migration + rollout).
Unknown значение registrationType при чтении — по additive-политике enum (../README.md).
- Logs:
UserId, id ссылки, размер страницы (PII в лог не пишется); metrics:
referral_link_registrations_requests_total; traces — сквозной trace через traceparent; dashboard
referral-overview (../README.md).
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные документы