GET /api/v1/profile/persons/{personId} — Получение персоны (getPerson)
Назначение
Вернуть одну персону текущего инвестора с расшифрованными персональными данными — для карточки/формы
редактирования персоны в ЛК. Документы персоны отдаёт отдельный метод
list-person-documents.md (агрегат в ответе не разворачивается — UI загружает
документы лениво по табу). Вызывает инвестор-владелец персоны.
| Поле | Значение |
|---|
| Сервис/контекст | profile |
| API area | public |
| Feature group | persons |
| Статус | draft |
| Документ | docs/06-services/profile/api/public/get-person.md |
Актор и доступ
| Проверка | Значение |
|---|
| Actor type | User (Bearer JWT), UserType = Investor |
| Auth policy / Permissions | authenticated; permission profile.personal.view-self |
| Scope (RBAC) | self |
| Record-level | PersonEntity.OwnerUserId = UserId; чужой personId → 404 (не раскрываем существование) |
| Tenant isolation | не применяется (ADR-0052) |
| Audit | read-sensitive: self-расшифровка не аудируется; значения в логи не пишутся |
HTTP-контракт
| Поле | Значение |
|---|
| Method | GET |
| Route | /api/v1/profile/persons/{personId} |
| Success status | 200 |
| Idempotency header | не применяется |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|
UserId | identity claims (sub) | Guid | Да | валидный JWT | 401 без токена |
personId | route | Guid | Да | NotEmpty; существующая персона владельца | чужая → 404 |
Модель ответа PersonModel
Дом модели — list-persons.md (без переописания): id, relation, status,
расшифрованные firstName/lastName/middleName/birthDate, citizenshipCountryId, gender,
documentsCount, createdAt, updatedAt, version.
Алгоритм
- Контекст и доступ. Операцию выполняет инвестор (
UserId из токена); tenant не применяется;
требуется право profile.personal.view-self.
- Проверка входа.
personId задан. Нарушение → ValidationError.
- Проверка предусловий. Профиль владельца существует; иначе
PROFILE_NOT_FOUND. Тип пользователя —
Investor (иначе PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE, INV-PRF-4). Персона personId существует
и принадлежит владельцу; иначе PROFILE_PERSON_NOT_FOUND — единый 404, чтобы не раскрывать существование
чужих id. Архивная персона возвращается (read допустим; UI показывает бейдж «в архиве»).
- Маппинг с расшифровкой. Расшифровать
firstName/lastName/middleName/birthDate ключом версии
записи; недоступность ключей — fail-closed PROFILE_CRYPTO_UNAVAILABLE. Спроецировать поле-в-поле по
таблице PersonModel (list-persons.md).
- Результат. Модель
PersonModel; HTTP 200.
Побочные эффекты
| Тип | Детали |
|---|
| Внешние вызовы | провайдер ключей шифрования (in-process кэш) для расшифровки ФИО/даты рождения; недоступность → PROFILE_CRYPTO_UNAVAILABLE |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|
| — (auth) | 401 | Нет/просрочен JWT | без тела | re-login |
ValidationError | 400 | Пустой personId | перечень невалидных полей | no retry |
PROFILE_NOT_FOUND | 404 | Профиль не создан консюмером | «Профиль ещё создаётся» | retry с backoff |
PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE | 403 | UserType != Investor | «Раздел доступен только инвесторам» | no retry |
PROFILE_PERSON_NOT_FOUND | 404 | Персона не существует или принадлежит другому пользователю | «Персона не найдена» | no retry |
PROFILE_CRYPTO_UNAVAILABLE | 503 | Недоступны ключи шифрования | «Сервис временно недоступен» | retry |
Совместимость и наблюдаемость
- Additive-расширение
PersonModel — см. list-persons.md.
- Logs:
UserId, PersonId (без значений); metrics:
profile_api_operations_total{operation="getPerson",result}; traces — OTel.
Acceptance Criteria
- Given активная персона владельца, When
GET /persons/{personId}, Then 200;
firstName/lastName/middleName/birthDate расшифрованы ключом версии записи; документы в ответе
не разворачиваются (грузятся отдельным методом).
- Given архивная персона владельца, When
GET, Then 200 — чтение допустимо (UI показывает
бейдж «в архиве»).
- Given
personId чужой или несуществующий, When GET, Then PROFILE_PERSON_NOT_FOUND (404) —
единый 404, существование чужих id не раскрывается.
- Given ключи шифрования недоступны, When
GET, Then PROFILE_CRYPTO_UNAVAILABLE (503),
fail-closed.
- Given
UserType != Investor, When GET, Then PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE
(403).
- Given профиль ещё не создан консюмером identity, When
GET, Then PROFILE_NOT_FOUND (404),
retry.
- Given любой успешный ответ, Then расшифрованные значения возвращаются только владельцу
(record-level по
OwnerUserId) и никогда не пишутся в логи.
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные документы