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

GET /api/admin/referrals/investors/{investorId}/referrals — Список приглашённых реферером (getInvestorReferrals, admin)

Назначение

Постраничный список всех пользователей, которых привёл конкретный реферер (investorId в route — это реферер, чьи приглашённые запрашиваются), для администратора платформы: разбор реферальных связей «кто кого привёл», сверка оснований будущих выплат, обработка претензий. Выдача содержит PII приглашённого (ФИО/телефон), поэтому доступна только по платформенному permission referral.registrations.view-all. Общий конверт и paging-модели — README.md.

Metadata

ПолеЗначение
Сервис/контекстreferral
API areaadmin
Feature groupreferral-bonds
Статусapproved
Документdocs/06-services/referral/api/admin/get-investor-referrals.md
AssumptionsФильтр статуса связей — открытый вопрос: по умолчанию отдаётся Status = Active, доступ к истории (Replaced/Revoked) — через additive query-параметр в дальнейшем (см. «Совместимость»).

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

ПроверкаЗначение
Actor typeOperator(Admin) (администратор платформы)
Auth policy / Permissionsauthenticated admin; permission referral.registrations.view-all (permission.md)
Scope (RBAC)платформа (кросс-tenant admin-выдача)
Auditread-sensitive: access-audit «кто/когда/какого реферера просматривал»; телефоны в логах маскируются +7***1234

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/admin/referrals/investors/{investorId}/referrals
Success status200
Correlationсквозной trace через заголовок traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
OperatorIdidentity claims (sub)GuidДавалидный токен + permission referral.registrations.view-all401 / 403; audit-стамп «кто просматривал»
investorIdrouteGuidДанепустой; реферер (ReferralBondEntity.ReferrerUserId), не приглашённыйlogical id, не PII
OffsetqueryintДаmin 0, max 2147483647
TakequeryintНет (admin-клиент передаёт явно)min 1, max 1000ограничение размера выдачи

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

Контейнер offset-paging; базовые meta (total/take/offset) — дом README.md.

ПолеТипОбязательностьИсточникMaskingОписание
metaPagingResultMetaДаprojectionМетаданные страницы (README.md).
itemsAdminReferredInvestorEntry[]ДаprojectionПриглашённые реферером на странице.

Модель AdminReferredInvestorEntry (дом item-модели)

Проекция ReferralBondEntity (id/дата/ссылка — referral-bond.md) + данные приглашённого из реплики ReferralUserReplica (ФИО/телефон — attach-rules.md, раздел «Реплика»); FullNameREADME.md.

ПолеТипОбязательностьИсточникMaskingОписание
referredInvestorIdstring (Guid)ДаReferralBondEntity.ReferredUserIdId приглашённого.
referredUserIdstring (Guid)ДаReferralBondEntity.ReferredUserIdПсевдоним поля (тот же id; владелец — любой пользователь).
nameFullNameДаReferralUserReplica.Name (по ReferredUserId)ФИО приглашённого; при lazy-регистрации firstName/lastName — пустые строки.
phoneNumberstring?НетReferralUserReplica.PhoneNumber+7***1234 в логахТелефон приглашённого (E.164); null, если реплика отстаёт от связи.
emailstring?Нетвсегда nullEmail в реплику не реплицируется (PII-минимизация) → всегда null.
boundAtstring (ISO 8601)ДаReferralBondEntity.BoundAtМомент привязки.
referralLinkIdstring (Guid)ДаReferralBondEntity.ReferralLinkIdСсылка, через которую произошла привязка.

Алгоритм

  1. Контекст и доступ. Оператор — администратор платформы; требуется permission referral.registrations.view-all (иначе REFERRAL_FORBIDDEN). Доступ кросс-tenant; факт просмотра пишется в access-audit.
  2. Проверка входа. investorId непустой, Offset >= 0, 1 <= Take <= 1000; иначе ValidationError. Существование реферера отдельно не проверяется (несуществующий/пустой реферер → пустая страница, шаг 3).
  3. Выдача. Возвращается страница связей, где investorId выступает реферером (ReferrerUserId == investorId, по умолчанию Status = Active — открытый вопрос), упорядоченная по дате привязки BoundAt desc, при равных значениях — по Id (стабильный tie-break; правило порядка/консистентности страницы — README.md). К каждой связи присоединяются ФИО/телефон приглашённого из реплики (реплика отсутствует, отстаёт от связи → name пустой FullName, phoneNumber = null, graceful degradation); email всегда null. PII — только в теле ответа, в логах телефон маскируется. Пустой/несуществующий реферер → meta.total = 0, items = []. meta содержит полное число записей под фильтром, размер и смещение страницы. Между запросами соседних страниц статус связи может смениться конкурентной admin-коррекцией (Active → Replaced/Revoked, rebind-bond.md/revoke-bond.md) — при фильтре Status = Active такая связь исчезает из последующих страниц (offset-снимок, README.md); чистое чтение конфликтов не создаёт.

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
ValidationError400investorId пустой или Offset/Take вне диапазонаперечень полейисправить
— (auth)401Нет/невалиден токен«Требуется авторизация»re-login
REFERRAL_FORBIDDEN403Отсутствует permission referral.registrations.view-all«Недостаточно прав»no retry

Несуществующий/пустой реферер — не ошибка: 200 с пустой страницей (total = 0).

Постусловие

Состояние не изменяется (чистое чтение): связи, журнал и реплика не трогаются, события не публикуются. Единственный побочный факт — запись access-audit «оператор просмотрел приглашённых реферера investorId».

Acceptance Criteria

  • Given администратор с permission referral.registrations.view-all и реферер с активными связями, When запрашивает GET …/investors/{investorId}/referrals, Then 200 со страницей AdminReferredInvestorEntry, упорядоченной по boundAt desc; в теле — ФИО/телефон приглашённого, в логах телефон маскирован +7***1234, email = null; факт доступа к PII — в access-audit.
  • Given реферер без связей или несуществующий investorId, When запрос, Then 200 с meta.total = 0 и items = [] (не ошибка; существование реферера отдельно не проверяется).
  • Given приглашённый есть в связи, но его реплика ReferralUserReplica ещё не доставлена (лаг consumer-а pin.identity.users.v1), When запрос, Then запись всё равно в выдаче: name — пустой FullName, phoneNumber = null (graceful degradation, не ошибка).
  • Given актор без permission referral.registrations.view-all, When запрос, Then 403 REFERRAL_FORBIDDEN; без токена — 401.
  • Given Offset < 0 или Take вне 1..1000, When запрос, Then 400 ValidationError с перечнем полей.
  • Given реферер с историей коррекций (Replaced/Revoked), When запрос без доп. параметров, Then выдаются только связи Status = Active (доступ к неактивным — будущий additive-параметр).
  • Given у реферера несколько связей с одинаковым boundAt, When постраничный обход, Then порядок детерминирован вторичным ключом Id — записи не дублируются и не пропадают между страницами (стабильный tie-break, README.md).
  • Given между запросами страниц связь отзывается/заменяется admin-коррекцией, When следующая страница, Then при фильтре Status = Active эта связь в неё уже не попадает (offset-снимок не транзакционен между страницами).

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

  • Контракт стабилен; удаление/переименование полей item-а — breaking (migration + rollout).
  • Открытый вопрос — фильтр статуса связей: по умолчанию Status = Active, доступ к Replaced/Revoked для разбора выплат — через additive query-параметр (напр. IncludeInactive) без breaking change.
  • Logs: investorId, размер страницы, total, факт выдачи PII (access-audit; телефоны — маской +7***1234, ФИО в лог не пишется); metrics: referral_admin_investor_referrals_requests_total, latency p95; traces — сквозной trace через traceparent; dashboard — referral-overview (../README.md, Runtime).

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

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

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