207 lines
15 KiB
Markdown
207 lines
15 KiB
Markdown
# user-profile Specification
|
|
|
|
## Purpose
|
|
|
|
Управление аккаунтом пользователя: просмотр и изменение профиля, смена пароля с отзывом
|
|
сессий, добавление второго идентификатора входа и удаление аккаунта со всем содержимым —
|
|
единообразно в веб-интерфейсе и Android-клиенте.
|
|
|
|
## Requirements
|
|
|
|
### Requirement: Просмотр собственного профиля
|
|
Пользователь SHALL получать свой профиль с именем, email, телефоном и аватаром.
|
|
Телефон SHALL быть виден только владельцу и SHALL NOT попадать в публичные представления
|
|
пользователя (участники альбома, загрузчики, комментарии).
|
|
|
|
#### Scenario: Получение профиля
|
|
- **WHEN** аутентифицированный пользователь запрашивает свой профиль
|
|
- **THEN** ответ содержит его имя, email (если задан), телефон (если задан) и аватар
|
|
|
|
#### Scenario: Телефон не утекает
|
|
- **WHEN** пользователь просматривает участников альбома
|
|
- **THEN** представления других пользователей не содержат телефон
|
|
|
|
### Requirement: Изменение имени
|
|
Пользователь SHALL изменять своё имя. Пустое имя SHALL отклоняться. Смена имени SHALL
|
|
применяться немедленно и отражаться при следующем чтении профиля.
|
|
|
|
#### Scenario: Успешное изменение
|
|
- **WHEN** пользователь отправляет непустое имя
|
|
- **THEN** система сохраняет его и возвращает обновлённый профиль
|
|
|
|
#### Scenario: Пустое имя
|
|
- **WHEN** пользователь отправляет пустое имя
|
|
- **THEN** система отклоняет запрос и не меняет профиль
|
|
|
|
### Requirement: Смена пароля с отзывом сессий
|
|
Пользователь SHALL менять пароль, подтвердив текущий пароль. Система SHALL отклонять
|
|
неверный текущий пароль и новый пароль короче 8 символов. Успешная смена SHALL отзывать
|
|
все refresh-токены пользователя на всех устройствах.
|
|
|
|
#### Scenario: Успешная смена
|
|
- **WHEN** пользователь вводит верный текущий пароль и новый пароль допустимой длины
|
|
- **THEN** система сохраняет новый хэш и отзывает все refresh-токены пользователя
|
|
|
|
#### Scenario: Неверный текущий пароль
|
|
- **WHEN** пользователь вводит неверный текущий пароль
|
|
- **THEN** система возвращает ошибку и не меняет пароль
|
|
|
|
#### Scenario: Слабый новый пароль
|
|
- **WHEN** новый пароль короче 8 символов
|
|
- **THEN** система отклоняет запрос и сохраняет старый пароль
|
|
|
|
### Requirement: Добавление недостающего идентификатора
|
|
Пользователь с одним заданным идентификатором (email или телефон) SHALL добавить
|
|
недостающий один раз после проверки уникальности. Система SHALL отклонять добавление, когда
|
|
оба идентификатора уже заданы, а также изменение или удаление заданного. После добавления
|
|
оба идентификатора SHALL работать для входа.
|
|
|
|
#### Scenario: Добавление телефона
|
|
- **WHEN** пользователь без телефона добавляет свободный номер в формате E.164
|
|
- **THEN** система сохраняет номер, и вход по нему становится возможен
|
|
|
|
#### Scenario: Занятый идентификатор
|
|
- **WHEN** добавляемый email или телефон уже принадлежит другому пользователю
|
|
- **THEN** система возвращает ошибку конфликта и не меняет профиль
|
|
|
|
#### Scenario: Оба уже заданы
|
|
- **WHEN** у пользователя уже заданы и email, и телефон, и он пытается добавить третий
|
|
- **THEN** система отклоняет запрос
|
|
|
|
### Requirement: Удаление аккаунта со всем содержимым
|
|
Пользователь SHALL удалять свой аккаунт, подтвердив текущий пароль. Неверный пароль SHALL
|
|
отклоняться. Успешное удаление SHALL быть немедленным и необратимым: SHALL каскадно
|
|
удаляться альбомы пользователя (вместе с доступом других участников), его фото,
|
|
комментарии, лайки, метки, уведомления, share-ссылки и приглашения. Блобы SHALL
|
|
освобождаться фоновой сверкой хранилища, а не в транзакции удаления.
|
|
|
|
#### Scenario: Успешное удаление
|
|
- **WHEN** пользователь подтверждает удаление верным текущим паролем
|
|
- **THEN** учётная запись и связанное содержимое удаляются, и вход больше невозможен
|
|
|
|
#### Scenario: Общие альбомы
|
|
- **WHEN** удаляемый пользователь владеет альбомом, в котором есть другие участники
|
|
- **THEN** альбом удаляется вместе с ними
|
|
|
|
#### Scenario: Неверный пароль
|
|
- **WHEN** пользователь подтверждает удаление неверным паролем
|
|
- **THEN** система возвращает ошибку и не удаляет аккаунт
|
|
|
|
### Requirement: Страница профиля в веб-интерфейсе
|
|
Веб-приложение SHALL предоставлять отдельную страницу профиля, доступную из шапки, с
|
|
показом идентификаторов и формами изменения имени, смены пароля и удаления аккаунта.
|
|
|
|
#### Scenario: Открытие страницы
|
|
- **WHEN** аутентифицированный пользователь открывает профиль из шапки
|
|
- **THEN** отображаются его идентификаторы и формы редактирования
|
|
|
|
#### Scenario: Выход после смены пароля
|
|
- **WHEN** смена пароля завершилась успешно
|
|
- **THEN** клиент очищает токены и перенаправляет на страницу входа
|
|
|
|
#### Scenario: Выход после удаления
|
|
- **WHEN** удаление аккаунта завершилось успешно
|
|
- **THEN** клиент очищает токены и перенаправляет на страницу входа
|
|
|
|
### Requirement: Экран профиля в Android-клиенте
|
|
Android-приложение SHALL предоставлять отдельный экран профиля, доступный из списка
|
|
альбомов, с теми же возможностями, что и веб-страница.
|
|
|
|
#### Scenario: Открытие экрана
|
|
- **WHEN** пользователь открывает профиль из списка альбомов
|
|
- **THEN** отображаются идентификаторы и формы редактирования
|
|
|
|
#### Scenario: Возврат после смены пароля
|
|
- **WHEN** смена пароля завершилась успешно
|
|
- **THEN** приложение очищает токены и возвращает на экран входа
|
|
|
|
#### Scenario: Возврат после удаления
|
|
- **WHEN** удаление аккаунта завершилось успешно
|
|
- **THEN** приложение очищает токены и возвращает на экран входа
|
|
|
|
### Requirement: Загрузка фото профиля с выбором области
|
|
Пользователь SHALL загружать собственное фото в профиль, выбирая видимую квадратную область.
|
|
Сохранённый аватар SHALL соответствовать выбранной области. Загрузка SHALL быть доступна только
|
|
владельцу профиля.
|
|
|
|
#### Scenario: Успешная загрузка
|
|
- **WHEN** пользователь выбирает изображение и подтверждает квадратную область
|
|
- **THEN** аватар обновляется и возвращается в профиле
|
|
|
|
#### Scenario: Выбранная область учтена
|
|
- **WHEN** пользователь сдвигает или масштабирует область перед подтверждением
|
|
- **THEN** сохранённый аватар показывает именно выбранную область
|
|
|
|
#### Scenario: Не изображение
|
|
- **WHEN** загруженный файл не является декодируемым изображением
|
|
- **THEN** система отклоняет запрос и не меняет аватар
|
|
|
|
### Requirement: Нормализация аватара и снятие метаданных
|
|
Система SHALL сохранять аватар в виде квадратных изображений фиксированных размеров и SHALL NOT
|
|
сохранять EXIF-метаданные загруженного файла.
|
|
|
|
#### Scenario: Метаданные не сохраняются
|
|
- **WHEN** загружено изображение с EXIF, включая GPS-координаты
|
|
- **THEN** сохранённый аватар не содержит EXIF и GPS
|
|
|
|
#### Scenario: Слишком большой файл
|
|
- **WHEN** размер загруженного файла превышает допустимый предел
|
|
- **THEN** система отклоняет запрос и не меняет аватар
|
|
|
|
### Requirement: Отдача аватара по стабильному URL
|
|
Профиль и публичные представления пользователя SHALL содержать URL аватара, когда он задан.
|
|
URL SHALL быть стабильным и SHALL изменяться при обновлении аватара, чтобы клиентские кэши
|
|
инвалидировались. Изображение SHALL отдаваться по публичной ссылке без аутентификации, чтобы
|
|
его могли загружать теги изображений и клиентские загрузчики.
|
|
|
|
#### Scenario: Ссылка в представлениях пользователя
|
|
- **WHEN** аутентифицированный клиент запрашивает профиль, участников альбома, комментарии или автора фото
|
|
- **THEN** представления содержат URL аватара, если он задан
|
|
|
|
#### Scenario: Кэш инвалидируется при замене
|
|
- **WHEN** пользователь заменяет аватар
|
|
- **THEN** URL аватара изменяется
|
|
|
|
#### Scenario: Загрузка изображения без заголовка авторизации
|
|
- **WHEN** браузер или клиентский загрузчик изображений запрашивает изображение аватара по URL без заголовка авторизации
|
|
- **THEN** система отдаёт изображение
|
|
|
|
### Requirement: Круглое отображение аватара в веб-интерфейсе
|
|
Веб-приложение SHALL отображать аватар пользователя круглым в шапке, на странице профиля, у
|
|
участников альбома, в комментариях и у автора фото.
|
|
|
|
#### Scenario: Аватар в шапке
|
|
- **WHEN** аутентифицированный пользователь открывает любое место приложения
|
|
- **THEN** в шапке отображается его круглый аватар
|
|
|
|
#### Scenario: Аватар рядом с другими пользователями
|
|
- **WHEN** пользователь просматривает участников, комментарии или автора фото
|
|
- **THEN** аватары этих пользователей отображаются круглыми
|
|
|
|
### Requirement: Круглое отображение аватара в Android-клиенте
|
|
Android-приложение SHALL отображать аватар пользователя круглым на экране профиля и в шапке
|
|
списка альбомов.
|
|
|
|
#### Scenario: Аватар на экране профиля
|
|
- **WHEN** пользователь открывает экран профиля
|
|
- **THEN** отображается его круглый аватар
|
|
|
|
#### Scenario: Аватар в шапке списка альбомов
|
|
- **WHEN** пользователь открывает список альбомов
|
|
- **THEN** в шапке отображается его круглый аватар
|
|
|
|
### Requirement: Заглушка при отсутствии аватара
|
|
Когда аватар не задан, система SHALL показывать нейтральную заглушку-силуэт вместо изображения.
|
|
|
|
#### Scenario: Аватар не задан
|
|
- **WHEN** у пользователя нет аватара и его представление отображается в вебе или Android
|
|
- **THEN** вместо изображения показывается нейтральная круглая заглушка-силуэт
|
|
|
|
### Requirement: Удаление аватара
|
|
Пользователь SHALL удалять свой аватар; после удаления SHALL показываться заглушка, а связанные
|
|
объекты хранилища SHALL освобождаться.
|
|
|
|
#### Scenario: Сброс аватара
|
|
- **WHEN** пользователь удаляет аватар
|
|
- **THEN** представления возвращаются к заглушке, а объекты старого аватара освобождаются
|