Skip to content

Random avatar feature#3522

Open
7glebalyot wants to merge 5 commits intohydra-master-stream12from
hydra-stream12-BJS2-95673-Random-Avatar
Open

Random avatar feature#3522
7glebalyot wants to merge 5 commits intohydra-master-stream12from
hydra-stream12-BJS2-95673-Random-Avatar

Conversation

@7glebalyot
Copy link
Copy Markdown

@7glebalyot 7glebalyot commented Nov 21, 2025

Реализовал генерацию рандомных аватарок для юзеров.

  1. Основная логика где задействуется главный метод моей фичи, генерации случайной аватарки:
  • При create user генерируется случайная автарка.
  • Когда происходит update user, то только если юзер меняет свой username, то заново генерируется новая аватарка. В остальных случаях она остается такой же. (Сделал именно так, потому что в нашей БД username является UNIQUE и я его использую для уникальной ссылки на аватарку в S3)
  • При delete user проверяется, если его аватарка была загружена на S3, то сначала она удаляется из бакета. А уже затем удаляется юзер. В ином случае просто удаляется юзер.
  1. Правки:
  • Раньше когда юзер удалял аватар, который сам и загрузил в свой профиль (метод тут), то подставлялась дефолтная аватарка, которая физически храниться у нас в проекте. Сейчас же, для лучшего опыта юзера, при удалении своей аватарки, генерируется новая случайная
  • Из-за пункта выше, я поправил AvatarController

Commit_1

Commit_2
@github-actions
Copy link
Copy Markdown

⚠️ ОШЫБКА: Сборка завалилась: либо ошибки компиляции, либо не прошли тесты, либо возникли ошибки в стиле кода. Пожалуйста, проверь логи и внеси соответствующие изменения в ПР. После этого красная ошибка ниже должна пропасть, но этот комментарий останется - так и должно быть.

@github-actions
Copy link
Copy Markdown

⚠️ ОШЫБКА: Сборка завалилась: либо ошибки компиляции, либо не прошли тесты, либо возникли ошибки в стиле кода. Пожалуйста, проверь логи и внеси соответствующие изменения в ПР. После этого красная ошибка ниже должна пропасть, но этот комментарий останется - так и должно быть.

@github-actions
Copy link
Copy Markdown

⚠️ ОШЫБКА: Сборка завалилась: либо ошибки компиляции, либо не прошли тесты, либо возникли ошибки в стиле кода. Пожалуйста, проверь логи и внеси соответствующие изменения в ПР. После этого красная ошибка ниже должна пропасть, но этот комментарий останется - так и должно быть.

@CorporationX CorporationX deleted a comment from sldkfvnaevndsk Nov 23, 2025
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Почему решил через RestTemplate делать, а не через feign или web client?

@ApiResponse(responseCode = "200", description = "URL нового аватара по умолчанию",
content = @Content(mediaType = MediaType.TEXT_PLAIN_VALUE,
schema = @Schema(implementation = String.class)))
@ApiResponse(responseCode = "200", description = "Список новых аватаров пользователя",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Список новых аватаров" не совсем ясно. Аватар же по сути один только? Просто разные его форматы

* @return {@code String}, содержащий URL нового аватара по умолчанию.
*/
String deleteAvatar(long userId);
UserProfilePic deleteAvatar(long userId);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

javadoc обновить нужно

@Override
@Transactional
public String deleteAvatar(long userId) {
public UserProfilePic deleteAvatar(long userId) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вообще интересная логика: пользователь удаляет аватар, а ему генерируется новый. Т.е. получается пользователь вообще не может быть без аватара? По идее генерировать должны только при регистрации пользователя, а дальше нужно дать возможность пользователю удалить аватар и остаться совсем без картинки. На что обращаю внимание: на работе нельзя придумывать логику самостоятельно. У нас уже пусть так остается, раз сделано, но всегда лучше уточнить.

* Сервис для работы с аватарками пользователей.
* <p>
* Позволяет генерировать случайные аватарки через DiceBear API и получать ссылки на разные размеры,
* а также удалять аватарки из S3.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Среди методов интерфейса нет ничего про удаление

* используется fallback-аватарка из статических ресурсов проекта.
*
* @param username имя пользователя, используется для формирования имени файла в S3 и логирования.
* @return карта с ключами "small" и "medium" и значениями — URL соответствующих аватарок.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сейчас не карта возвращается, а UserProfilePic

Comment on lines +39 to +41
private static final int SMALL_SIZE = 64;
private static final int BIG_SIZE = 128;
private static final int MAX_AVATAR_SIZE_BYTES = 2_000_000;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Эти значения нужно брать из application.yaml

Comment on lines +54 to +55
String bigKey = "random-user-avatars/" + username + "/big-" + randomSeed + ".png";
String smallKey = "random-user-avatars/" + username + "/small-" + randomSeed + ".png";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше через String.format и вынести это в константы

Comment on lines +78 to +79
maxAttempts = 4,
backoff = @Backoff(delay = 1000, multiplier = 2))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Очень хорошо, что Retryable использовал. Но значения (4, 1000, 2) должны браться из application.yaml. И ещё аннотация в таком варианте работать не будет, т.к. внутри класса метод вызывается (особенность спринга, читай про proxy. Можем на встрече поговорить, если напомнишь).

@Slf4j
@Service
@RequiredArgsConstructor
public class RandomAvatarServiceImpl implements RandomAvatarService {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

На мой взгляд, этот сервис не удовлетворяет принципу единственной ответственности. Он и к стороннему сервису обращается и ошибки от него обрабатывает, и изображение сжимает, и в s3 ходит.

  1. Для обращения к стороннему api должен быть отдельный класс-клиент. RandomAvatarClient или даже DicebearClient, который через restTemplate обращается к dicebear и обрабатывает ошибки от него
  2. Я бы предложила вынести в отдельный утилитный класс методы по работе с изображением: imageToPngBytes, resizeImage
  3. У метода generateRandomAvatarForUser логика должна быть один-в-один как у метода uploadAvatar с разницей лишь в том, откуда аватар пришел (от пользователя или из dicebear). Поэтому выглядит так, что всё это могло спокойно остаться внутри AvatarServiceImpl

Итого: RandomAvatarServiceImpl выглядит лишним классом. Можем также обсудить на встрече, если нужно

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants