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

GET /api/admin/referrals/links — Все реферальные ссылки платформы (admin, getReferralLinks)

Назначение

Административный кросс-tenant обзор всех реферальных ссылок PIN с ФИО и телефоном владельца. Даёт администратору платформы полный постраничный список реферальных ссылок всех пользователей (инвесторы, агенты, менеджеры застройщика, сотрудники ПИН) с владельцем (ФИО/телефон), каналом, числом регистраций и признаком default — для контроля партнёрской программы и разбора обращений. В отличие от публичного ../public/get-my-referral-links.md (record-level, обезличенный), операция доступна только по платформенному permission referral.registrations.view-all и раскрывает PII владельца из реплики. Дом item-модели AdminReferralLinkEntryздесь; её переиспользует get-referral-link-by-id.md. Общий конверт, paging-модели и модель FullNameREADME.md.

Metadata

ПолеЗначение
Сервис/контекстreferral
API areaadmin
Feature groupadmin-referral-links
Статусapproved
Документdocs/06-services/referral/api/admin/get-referral-links.md

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

ПроверкаЗначение
Actor typeOperator(Admin) (администратор платформы)
Auth policy / Permissionspermission referral.registrations.view-all (каталог — permission.md)
Scope (RBAC)платформа (кросс-tenant доступ по платформенному permission)
Tenant isolationкросс-tenant по платформенному permission (tenant-предикат не накладывается); доступ фиксируется в audit
Auditread-sensitive (PII: ФИО/телефон владельца) — access-audit

HTTP-контракт

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

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
AdminUserIdidentity claimsGuidДанепустойконтекст admin-области, audit
OffsetqueryintДаmin 0, max 2147483647
TakequeryintНет (admin-клиент передаёт явно, README.md)min 1, max 1000; при отсутствии — server-side defaultограничение размера выдачи
OwnerUserIdqueryGuid?Нетнепустой если задан — фильтр по владельцу
Urlquerystring?Нетнормализация lowercase; подстрока slug, max 64untrusted — параметризованный запрос

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

Container-модель (дом paging-моделей — README.md):

ПолеТипОбязательностьИсточникMaskingОписание
metaPagingResultMetaДаprojectiontotal/take/offsetREADME.md.
itemsIReadOnlyList<AdminReferralLinkEntry>Да (может быть пуст)projectionСтраница ссылок, сортировка CreatedAt desc.

Проекция дома referral-link.md + данные владельца из реплики ReferralUserReplica (раздел «Реплика»); модель FullNameREADME.md:

ПолеТипОбязательностьИсточникMaskingОписание
idstring (Guid)ДаReferralLinkEntity.IdИдентификатор ссылки.
ownerUserIdstring (Guid)ДаReferralLinkEntity.OwnerUserIdВладелец ссылки.
ownerNameFullNameДаReferralUserReplica.NameФИО владельца; пустые строки при lazy-регистрации.
ownerPhoneNumberstring?НетReferralUserReplica.PhoneNumberв логах +7***1234Телефон владельца (E.164); null, если реплика ещё не догнала владельца.
urlstringДаReferralLinkEntity.UrlSlug.
descriptionstring?НетReferralLinkEntity.DescriptionОписание канала.
isDefaultboolДаReferralLinkEntity.IsDefaultСсылка «по умолчанию» владельца.
channelReferralChannel (enum строкой)ДаReferralLinkEntity.ChannelКанал (referral-link.md).
registrationsCountnumber (int)Даcomputed (производный — referral-link.md)Число регистраций по ссылке.
createdAtstring (ISO 8601)ДаReferralLinkEntity.CreatedAtМомент создания.
emailstring?Нетвсегда nullEmail в реплику не реплицируется (PII-минимизация, attach-rules.md «Реплика») → всегда null.

Алгоритм

  1. Контекст и доступ. Оператор — администратор платформы; требуется permission referral.registrations.view-all (иначе REFERRAL_ADMIN_FORBIDDEN). Доступ кросс-tenant.
  2. Проверка входа. Offset >= 0; при заданном Take1 <= Take <= 1000 (иначе server-side default); OwnerUserId непустой если передан; Url — нормализация к lowercase, <= 64; иначе ValidationError.
  3. Выдача. Возвращается кросс-tenant страница не удалённых ссылок (soft-deleted в список не попадают — детальная карточка по id их показывает, get-referral-link-by-id.md). Опциональные фильтры комбинируются по И: OwnerUserId (точное совпадение владельца) и Url (case-insensitive подстрока slug — contains по нормализованному к lowercase значению). Порядок — CreatedAt desc, при равных значениях — по Id (стабильный tie-break; правило порядка/консистентности страницы — README.md). К каждой ссылке присоединяются ФИО/телефон владельца из реплики (реплика отсутствует → пустой FullName и null телефон, fail-soft) и число регистраций (registrationsCount, отсутствует → 0); email всегда null. Телефон в логах маскируется, факт выдачи PII фиксируется в access-audit. meta содержит полное число записей под фильтром, размер и смещение страницы; конкурентное создание ссылок между страницами сдвигает offset (снимок — README.md).

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
ValidationError400Offset/Take/OwnerUserId/Url вне диапазона/форматаперечень полейисправить
REFERRAL_ADMIN_FORBIDDEN403Актор без permission referral.registrations.view-all«Доступ запрещён»no retry
— (auth)401Нет/невалиден токен«Требуется авторизация»re-login

Постусловие

Состояние не изменяется (чистое чтение): ссылки, счётчики и реплика не трогаются, события не публикуются. Побочный факт — access-audit «оператор просматривал кросс-tenant список ссылок с PII».

Acceptance Criteria

  • Given администратор с permission referral.registrations.view-all, When запрашивает GET …/referrals/links, Then 200 со страницей AdminReferralLinkEntry, сортировка createdAt desc, кросс-tenant; в каждой записи — ownerName/ownerPhoneNumber из реплики, registrationsCount, channel, isDefault, email = null; телефон в логах маскирован.
  • Given заданы OwnerUserId и подстрока Url одновременно, When запрос, Then фильтры комбинируются по И: выдаются только ссылки указанного владельца, чей slug содержит подстроку (case-insensitive, нормализация к lowercase); soft-deleted ссылки в список не попадают (их показывает карточка по id — get-referral-link-by-id.md); порядок createdAt desc со стабильным вторичным ключом Id (README.md).
  • Given под фильтр не попадает ни одна ссылка, When запрос, Then 200 с meta.total = 0, items = [].
  • Given реплика владельца отсутствует, When запрос, Then ownerName пустой, ownerPhoneNumber = null, registrationsCount = 0 при отсутствии регистраций (fail-soft).
  • Given Take не передан, When запрос, Then применяется server-side default (admin-параметр опционален); Take вне 1..1000/Offset < 0/невалидный OwnerUserId/Url > 64 → 400 ValidationError.
  • Given актор без permission, When запрос, Then 403 REFERRAL_ADMIN_FORBIDDEN; без токена — 401.

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

  • Контракт стабилен. Новые optional поля AdminReferralLinkEntry — additive; удаление/переименование полей — breaking (migration + rollout). Unknown ReferralChannel при чтении — fallback Other + warning (referral-link.md).
  • Logs: admin-referral-links-listed (AdminUserId, total, размер страницы; телефоны — маской +7***1234, ФИО в лог не пишется). Metrics: referral_admin_links_requests_total, latency p95. Traces — сквозной trace через traceparent; dashboard/runbook — ../README.md (раздел Runtime).

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

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

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