feat: migrate to VitePress from monorepo docs, add test-contour section
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: Авторизация по UID и pcode (Backend)
|
||||
---
|
||||
|
||||
# Сценарий 1.2: Авторизация по **UID** и **pcode**
|
||||
|
||||
## Бизнес-цель
|
||||
|
||||
Часть пользователей приходит из медицинской системы (**Инфоклиника / MIS**): у пациента уже есть **числовой идентификатор** в сторонней системе. Сайт должен позволить:
|
||||
|
||||
- связать этот **UID** с учётной записью в API (`App\Entity\User::uid`);
|
||||
- войти **по email+паролю**, если пользователь уже создан с тем же `uid`;
|
||||
- войти **по pcode + дате рождения**, если личный кабинет строится на «медицинском» коде без ввода email на первом шаге.
|
||||
|
||||
## Что такое `uid` и `pcode` в коде
|
||||
|
||||
| Термин в API | Где в коде | Смысл |
|
||||
| --- | --- | --- |
|
||||
| `uid` | поле `User::$uid`, уникальное `int` | Идентификатор пациента/пользователя из внешней системы (в т.ч. MIS). |
|
||||
| `pcode` в `POST /user/auth-by-pcode` | мапится в `UserUidAuthDto::$uid` | Тот же **числовой идентификатор**, что и `uid`; название «pcode» — контракт фронта/legacy. |
|
||||
|
||||
Откуда берутся значения **на практике**: из МИС/личного кабинета после идентификации пациента (конкретный HTTP-запрос к Инфоклинике **в этом приложении** для «получить pcode» в сценарии логина не вызывается — клиент передаёт уже известные `uid`/`pcode` и дату рождения).
|
||||
|
||||
## Точки входа
|
||||
|
||||
| Тип | Метод + URL | Класс |
|
||||
| --- | --- | --- |
|
||||
| HTTP | `POST /user/auth` | `UserController::auth` |
|
||||
| HTTP | `POST /user/auth-by-pcode` | `UserController::authByPcode` |
|
||||
|
||||
## Пошаговый алгоритм — `POST /user/auth`
|
||||
|
||||
1. Тело: `uid`, `regionId`, `email`, `password`, опционально `bdate` → `UserAuthDto`, валидация.
|
||||
2. `AuthenticationService::jwtAuth(dto)`:
|
||||
- `UserRepository::findOneBy(['uid' => $dto->uid])`;
|
||||
- если пользователь есть — проверка пароля **введённого** `password` через `passwordHasher`;
|
||||
- если пользователя **нет** — возвращается `user: null` (флаг пароля не применяется).
|
||||
3. Если пароль не совпал при существующем пользователе — `400`, как при обычном логине.
|
||||
4. Если пользователя не было — `RegistrationService::create(dto)`:
|
||||
- `setEmail(md5($dto->email))` (в БД снова **не** хранится открытый email);
|
||||
- `setUid`, `setRegionId`, роли `ROLE_USER`, `birthDate` из `bdate` (формат `Ymd`), хэш пароля.
|
||||
5. `updateLoggedIn`, `flush`, выдача JWT через `JWTTokenManagerInterface::create`.
|
||||
|
||||
## Пошаговый алгоритм — `POST /user/auth-by-pcode`
|
||||
|
||||
1. Тело: `pcode` (кладётся в `UserUidAuthDto::$uid`), `birthDate` или `bdate`.
|
||||
2. Валидация DTO, разбор даты (`Ymd` или `Y-m-d`).
|
||||
3. `UserRepository::findOneByUidAndBirthDate($uid, $birthDate)` — сравнение даты **по диапазону суток**.
|
||||
4. Если не найден — `RegistrationService::createByUidAndBirthDate`:
|
||||
- `email = md5((string) uid)` — синтетический идентификатор для колонки `email` / JWT `username`;
|
||||
- регион по умолчанию `1` (аргумент сервиса);
|
||||
- пароль по умолчанию: `hash( md5(uid . birthDateRaw) )` где первая часть — конкатенация строк из DTO перед нормализацией даты в сущности.
|
||||
5. `updateLoggedIn`, `flush`, JWT в ответе.
|
||||
|
||||
## Mermaid
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[POST /user/auth или /user/auth-by-pcode] --> V{Валидация DTO}
|
||||
V -->|ошибка| E400[400 errors]
|
||||
V -->|ок| B{Какой маршрут?}
|
||||
B -->|auth| C[jwtAuth по uid]
|
||||
C --> D{User найден?}
|
||||
D -->|да| P[проверка password]
|
||||
P -->|fail| E400b[400 неверный пароль]
|
||||
P -->|ok| T[JWT + loggedIn]
|
||||
D -->|нет| R[RegistrationService.create]
|
||||
R --> T
|
||||
|
||||
B -->|auth-by-pcode| F[findOneByUidAndBirthDate]
|
||||
F -->|есть| T
|
||||
F -->|нет| G[createByUidAndBirthDate]
|
||||
G -->|исключение| E500[500]
|
||||
G -->|ok| T
|
||||
```
|
||||
|
||||
## Внешние зависимости
|
||||
|
||||
| Система | Участие |
|
||||
| --- | --- |
|
||||
| PostgreSQL | `users` |
|
||||
| Инфоклиника | **не вызывается напрямую** в этих методах; идентификатор приходит от клиента |
|
||||
| JWT (Lexik) | выдача токена |
|
||||
|
||||
## Обработка ошибок и edge cases
|
||||
|
||||
- **Неверный формат даты** в `auth-by-pcode` — `400` с подсказкой по форматам.
|
||||
- **Ошибка при создании пользователя** — `500` с текстом исключения (может раскрывать внутренние детали — вопрос hardening).
|
||||
- **Коллизии `uid`**: на уровне сущности `User` есть ограничение уникальности `uid`; при конфликте БД вернёт ошибку при `flush` (в этом контроллере не разобрана отдельно).
|
||||
|
||||
## Ссылки на классы
|
||||
|
||||
- `apps/backend/src/Controller/UserController.php` (`auth`, `authByPcode`)
|
||||
- `apps/backend/src/Service/User/AuthenticationService.php` (`jwtAuth`)
|
||||
- `apps/backend/src/Service/User/RegistrationService.php` (`create`, `createByUidAndBirthDate`)
|
||||
- `apps/backend/src/Dto/UserAuthDto.php`, `UserUidAuthDto.php`
|
||||
- `apps/backend/src/Repository/UserRepository.php` (`findOneByUidAndBirthDate`)
|
||||
|
||||
См. [backend-ddd.md](../backend-ddd.md) (контекст Identity).
|
||||
Reference in New Issue
Block a user