Profile API — реестр операций областей public / internal / admin
Индекс HTTP-операций контекста profile (Pin.Profile.Api) и общие правила областей: модель акторов и
гейтов, разбиение по feature-группам, единый формат ошибок. Файлы операций разложены по областям-акторам
(public/, internal/, admin/) и описывают только свою дельту; дом полей сущностей —
../domain/.
| Поле | Значение |
|---|
| Сервис/контекст | profile (Pin.Profile) |
| Статус | draft |
| Owning team | PIN Platform Core |
| Области | public (self-service ЛК), internal (m2m-снапшоты для crm/catalog), admin (review KYC — сотрудники ПИН) |
Ошибки — ProblemDetails с кодом. Смена email/phone/ФИО/пароля/2FA — не здесь, а в identity
(см. ../README.md, раздел «Ответственность И Границы»).
Общие правила областей
Область public (self-service ЛК, /api/v1/profile/…)
| Правило | Значение |
|---|
| Actor type | User (Bearer JWT identity); sub = UserId. profile/persons/documents/requisites/verification — только UserType = Investor (INV-PRF-4); getMyProfile/updateMySettings доступны всем типам пользователей |
| Record-level | владение по OwnerUserId/Id = UserId; владелец из тела/маршрута не принимается; чужая запись → 404 |
| Tenant isolation | не применяется (ADR-0052: инвестор вне tenant) |
| Guard-ы профиля | Status = Active (INV-PRF-3 → PROFILE_USER_BLOCKED), ContactsManagedBy = SelfService (INV-PRF-2 → PROFILE_MANAGED_EXTERNALLY) |
| PII | ФИО/дата рождения/значения документов — шифротекст (шифрование персональных данных); наружу — маски; логи без значений PII |
Область internal (m2m, /api/v1/internal/profile/…)
| Правило | Значение |
|---|
| Actor type | ServiceAccount — m2m service token (client-credentials), не user JWT; policy InternalServiceOnly, allowlist потребителей (crm, catalog) |
| Tenant isolation | не применяется; владелец передаётся параметром запроса (проверяется, не выводится из токена) |
| Назначение | синхронная выдача расшифрованного иммутабельного снапшота реквизитов и денормализованного VerificationStatus (PII в Kafka не публикуется — simplicity-first, ADR-0056) |
Область admin (review KYC, /api/v1/admin/profile/…)
| Правило | Значение |
|---|
| Actor type | Operator(Admin) — ревьюер ПИН (Bearer JWT), не инвестор-владелец |
| Permission | profile.verification.review (нет permission → 403; permission.md: pin-manager/care/admin) |
| Record-level | не применяется — ревьюер читает чужую заявку; гейт — permission, а не владение |
| Audit | actor-stamping + отдельная запись на каждую расшифровку снапшота (viewVerificationSnapshot, read-sensitive) |
Сквозные соглашения областей
Контракты, на которые операции опираются и не повторяют в каждом файле (реализация должна быть
единообразной во всех областях).
| Соглашение | Правило |
|---|
| Пагинация списков | Page (>= 1, default 1) + Size (1..100, default 20); ответ — Items + Meta { Total, Take, Offset, HasMore }; нарушение диапазонов → ValidationError |
| Идемпотентность мутаций | По умолчанию — естественная защита (дубль-инварианты *_ALREADY_EXISTS, статус-guard, повторный archive как no-op 200); где явно указан заголовок Idempotency-Key — повтор возвращает прежний результат без второго эффекта/события |
| Оптимистичная блокировка | Конкурентная правка агрегата → PROFILE_CONCURRENCY_CONFLICT (409); побеждает первый зафиксировавший, проигравший перечитывает состояние и повторяет |
| Лаг реплики identity | До создания профиля консюмером pin.identity.users.v1 операции ЛК/internal отдают PROFILE_NOT_FOUND (404); клиент/потребитель повторяет с backoff (1–5 с) |
| Гарантии produced-событий | Публикация через транзакционный outbox, at-least-once; порядок гарантирован в пределах ключа партиционирования (persons — PersonId, verification-requests — OwnerUserId); потребители обязаны быть идемпотентны; PII в Kafka не выносится |
| Freshness денормализаций | Денормализованные поля (Profile.VerificationStatus) меняются в одной транзакции с изменением-источником (INV-PRF-6) — синхронные чтения согласованы с соответствующим событием |
| Расшифровка PII | ФИО/даты/значения документов расшифровываются fail-closed; недоступность ключей → PROFILE_CRYPTO_UNAVAILABLE (503); наружу PII идёт только масками (public) либо в аудируемых операциях снапшота (internal getRequisiteSnapshot, admin viewVerificationSnapshot) |
Реестр операций — область public
Профиль ЛК (feature group profile, permission profile.personal.view-self/manage-self)
Персоны (feature group persons, permission profile.personal.view-self/manage-self)
Документы персон (feature group documents, permission profile.documents.view-self/manage-self)
Шаблоны реквизитов (feature group requisites, permission profile.requisites.manage-self)
Верификация инвестора (feature group verification, permission profile.verification.submit)
Реестр операций — область internal (m2m)
| # | Операция | HTTP | Route | Потребитель | Документ |
|---|
| 26 | getRequisiteSnapshot (+RegisterUsage) | POST | /api/v1/internal/profile/requisite-snapshot | crm | get-requisite-snapshot.md |
| 27 | getVerificationStatus | GET | /api/v1/internal/profile/verification-status/{userId} | crm, catalog | get-verification-status.md |
Реестр операций — область admin (review KYC, permission profile.verification.review)
Сквозные ошибки областей (не повторяются в каждом файле)
| Код | HTTP status | Условие |
|---|
ValidationError | 400 | Нарушение format/min/max/pattern/enum |
UNAUTHORIZED | 401 | Нет/просрочен JWT (public/admin) или m2m-токен (internal) |
FORBIDDEN | 403 | Нет требуемого permission / ContactsManagedBy = TalentSync (public) |
PROFILE_NOT_FOUND | 404 | Профиль ещё не создан консюмером pin.identity.users.v1 (лаг реплики; retry) |
PROFILE_USER_BLOCKED | 400 | Profile.Status != Active (INV-PRF-3) |
PROFILE_MANAGED_EXTERNALLY | 403 | Контакты управляются identity (INV-PRF-2) |
PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE | 403 | UserType != Investor для investor-only операций (INV-PRF-4) |
PROFILE_CONCURRENCY_CONFLICT | 409 | Несовпадение версии агрегата (optimistic) |
PROFILE_CRYPTO_UNAVAILABLE | 503 | Ключ шифрования персональных данных недоступен (Vault, fail-closed) |
Связанные документы