Перейти к содержанию

05 admin user

AdminUserEndpoint — управление пользователями для операторов.

Все методы требуют JWT + запись в admin_user_roles. _requireAdmin проверяет сессию и наличие любой admin-роли. _requireRoles(allowed) дополнительно проверяет конкретные роли через JOIN admin_roles.

Методы

Метод Роль Действие
getUsers({page, pageSize, statusFilter?}) любой admin Список пользователей с пагинацией, необязательный фильтр по status
getDetail(userId) любой admin Карточка: user + profile (маскированный) + kyc статус
revealPii(userId, reason) operator, superadmin Расшифровывает encryptedPii, пишет audit_log(pii_revealed)
blockUser(userId, reason) superadmin, operator Блокирует AuthUser + user.status=blocked + authenticationRevoked
unblockUser(userId, reason) superadmin, operator Разблокирует AuthUser, восстанавливает previousStatus из audit_log
lockProfile(userId, reason) operator, superadmin profile.isLocked=true; если user.status=pending_operator_review — вызывает KycService.finalizeVerification
unlockProfile(userId, reason) operator, superadmin profile.isLocked=false, очищает lockedAt
updateProfile(userId, input) operator, superadmin Корректирует PII через PiiProfileWriter; только если isLocked=false
setEncryptPiiMode(userId, encryptPii, reason) operator, superadmin Переключает режим хранения PII (открытый/зашифрованный)
setNfcEnabled(userId, enabled, reason) operator, superadmin Включает/выключает NFC для пользователя (gate C8)
createWalletForUser(userId, currency) любой admin Ручное провизионирование кошелька через WalletCreationService.ensureWallet (идемпотентно)
createMerchant(email, firstName, lastName, businessName, taxId, businessCategoryCode, languageCode, ...) любой admin Создаёт merchant + auth-аккаунт + PII + инвайт-письмо с tempPassword
createAgent(email, firstName, lastName, businessName, taxId, languageCode, ...) любой admin Создаёт agent + auth-аккаунт + PII + инвайт-письмо с tempPassword
createConsumer(email, firstName, lastName, languageCode, ...) любой admin Создаёт consumer + кошелёк THB (retry FutureCall если fail) + инвайт-письмо

revealPii

  • Обязательный reason ≥ 10 символов (UserApiException(invalid_reason) иначе).
  • Расшифровывает encryptedPii через PiiDecryptionService и возвращает DecryptedProfile.
  • Поля в audit_log: revealedFields (12 полей), reason, traceId.
  • Запись в audit_log пишется всегда — даже если расшифровка не потребовалась.

blockUser / unblockUser

blockUser — три последовательных действия: 1. AuthUsers().update(blocked=true) — Serverpod отклонит refresh-токен. 2. session.messages.authenticationRevoked(authUserId) — активные streaming-соединения закрываются (best-effort, ошибка логируется). 3. User.db.updateById(status='blocked'). 4. Audit: user_block с полем previousStatus (сохраняется для восстановления).

unblockUser — восстанавливает previousStatus: 1. Читает последний user_block audit_log.details → previousStatus. 2. Fallback если не найден: 'active'. 3. AuthUsers().update(blocked=false). 4. User.db.updateById(status=restoredStatus).

Создание пользователей (createMerchant / createAgent / createConsumer)

Все три метода работают в транзакции: 1. Проверка уникальности email. 2. AuthServices.instance.authUsers.create() — создаёт serverpod auth-запись. 3. User.db.insertRow(accountType=...). 4. EmailIdp.admin.createEmailAuthentication(tempPassword) — мерчант/агент/consumer может войти сразу. 5. PiiProfileWriter.upsert(...) — PII записывается / шифруется по encryptPii. 6. Дополнительные бизнес-поля (для merchant/agent: businessName, taxId).

После транзакции: - Audit merchant_created / agent_created / consumer_created. - RegistrationService.sendInviteEmail(tempPassword) — ошибка логируется, не ломает ответ. - Только для consumer: WalletCreationService.ensureWallet(currency='THB') + FutureCall при ошибке.

Важно

  • Merchant и agent создаются только через admin (не самостоятельная регистрация).
  • reason ≥ 10 символов обязателен для revealPii, blockUser, unblockUser, lockProfile, unlockProfile, updateProfile, setEncryptPiiMode, setNfcEnabled.
  • updateProfile работает только при profile.isLocked=false. При isLocked=trueUserApiException(profile_locked).
  • setNfcEnabled(false) — заморозка: метки сохраняются, NFC-операции отклоняются кодом NFC_DISABLED.