From 524d700627dc3538a97ace1a6ccb80589bc16528 Mon Sep 17 00:00:00 2001 From: Iakov Lysenko Date: Tue, 18 Nov 2025 18:17:04 +0300 Subject: [PATCH] added create user --- .../v.1.0.0/002-create-users-table.yaml | 1 - .../contest/controller/UserControllerApi.java | 10 ++++ .../controller/impl/UserControllerImpl.java | 15 ++++- .../dto/request/CreateUserRequest.java | 22 +++++++ .../contest/mapper/UserMapper.java | 1 - .../contest/service/UserService.java | 8 +++ .../contest/service/impl/UserServiceImpl.java | 46 ++++++++++---- openapi.yml | 60 +++++++++++++++++++ 8 files changed, 150 insertions(+), 13 deletions(-) create mode 100644 avito-test-impl/src/main/java/ru/iakovlysenko/contest/dto/request/CreateUserRequest.java diff --git a/avito-test-db/src/main/resources/db/changelog/v.1.0.0/002-create-users-table.yaml b/avito-test-db/src/main/resources/db/changelog/v.1.0.0/002-create-users-table.yaml index cad4e3f..fbb0dee 100644 --- a/avito-test-db/src/main/resources/db/changelog/v.1.0.0/002-create-users-table.yaml +++ b/avito-test-db/src/main/resources/db/changelog/v.1.0.0/002-create-users-table.yaml @@ -11,7 +11,6 @@ databaseChangeLog: type: VARCHAR(255) constraints: primaryKey: true - nullable: false - column: name: username type: VARCHAR(255) diff --git a/avito-test-impl/src/main/java/ru/iakovlysenko/contest/controller/UserControllerApi.java b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/controller/UserControllerApi.java index 1111f9b..82119e7 100644 --- a/avito-test-impl/src/main/java/ru/iakovlysenko/contest/controller/UserControllerApi.java +++ b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/controller/UserControllerApi.java @@ -4,8 +4,10 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; +import ru.iakovlysenko.contest.dto.request.CreateUserRequest; import ru.iakovlysenko.contest.dto.request.SetIsActiveRequest; import ru.iakovlysenko.contest.dto.response.GetReviewResponse; +import ru.iakovlysenko.contest.dto.response.UserResponse; import ru.iakovlysenko.contest.dto.response.UserWrapperResponse; /** @@ -31,4 +33,12 @@ public interface UserControllerApi { */ ResponseEntity getReview(@RequestParam("user_id") String userId); + /** + * Создает нового пользователя в системе. + * + * @param createUserRequest ДТО запроса с данными для создания пользователя + * @return ДТО с анными пользователя + */ + ResponseEntity createUser(CreateUserRequest createUserRequest); + } diff --git a/avito-test-impl/src/main/java/ru/iakovlysenko/contest/controller/impl/UserControllerImpl.java b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/controller/impl/UserControllerImpl.java index 3491b42..fae0d3a 100644 --- a/avito-test-impl/src/main/java/ru/iakovlysenko/contest/controller/impl/UserControllerImpl.java +++ b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/controller/impl/UserControllerImpl.java @@ -6,8 +6,10 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import ru.iakovlysenko.contest.controller.UserControllerApi; +import ru.iakovlysenko.contest.dto.request.CreateUserRequest; import ru.iakovlysenko.contest.dto.request.SetIsActiveRequest; import ru.iakovlysenko.contest.dto.response.GetReviewResponse; +import ru.iakovlysenko.contest.dto.response.UserResponse; import ru.iakovlysenko.contest.dto.response.UserWrapperResponse; import ru.iakovlysenko.contest.service.UserService; @@ -23,7 +25,7 @@ public class UserControllerImpl implements UserControllerApi { private final UserService userService; - + @Override @PostMapping("/setIsActive") public ResponseEntity setIsActive(@Valid @RequestBody SetIsActiveRequest request) { @@ -46,5 +48,16 @@ public ResponseEntity getReview(@RequestParam("user_id") Stri return ResponseEntity.ok(response); } + + @Override + @PostMapping + public ResponseEntity createUser(@Valid @RequestBody CreateUserRequest createUserRequest) { + log.info("POST /users - Создание нового пользователя"); + + UserResponse response = userService.createUser(createUserRequest); + + return ResponseEntity.ok(response); + } + } diff --git a/avito-test-impl/src/main/java/ru/iakovlysenko/contest/dto/request/CreateUserRequest.java b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/dto/request/CreateUserRequest.java new file mode 100644 index 0000000..08f1de0 --- /dev/null +++ b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/dto/request/CreateUserRequest.java @@ -0,0 +1,22 @@ +package ru.iakovlysenko.contest.dto.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * ДТО запроса на создание пользователя. + * Используется для передачи данных при создании нового пользователя через REST API. + * + * @author Iakov Lysenko + */ +public record CreateUserRequest( + @NotBlank + String id, + @NotBlank + String username, + @NotBlank + String teamName, + @NotNull + Boolean isActive +) { +} diff --git a/avito-test-impl/src/main/java/ru/iakovlysenko/contest/mapper/UserMapper.java b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/mapper/UserMapper.java index 401159d..faf816a 100644 --- a/avito-test-impl/src/main/java/ru/iakovlysenko/contest/mapper/UserMapper.java +++ b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/mapper/UserMapper.java @@ -21,7 +21,6 @@ public interface UserMapper { * @return ДТО ответа с информацией о пользователе */ @Mapping(target = "userId", source = "id") - @Mapping(target = "teamName", expression = "java(user.getTeamName())") UserResponse toResponse(User user); } diff --git a/avito-test-impl/src/main/java/ru/iakovlysenko/contest/service/UserService.java b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/service/UserService.java index 112368a..f32f574 100644 --- a/avito-test-impl/src/main/java/ru/iakovlysenko/contest/service/UserService.java +++ b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/service/UserService.java @@ -1,8 +1,10 @@ package ru.iakovlysenko.contest.service; +import ru.iakovlysenko.contest.dto.request.CreateUserRequest; import ru.iakovlysenko.contest.dto.request.SetIsActiveRequest; import ru.iakovlysenko.contest.dto.response.GetReviewResponse; import ru.iakovlysenko.contest.dto.response.UserResponse; +import ru.iakovlysenko.contest.exception.NotFoundException; /** * Сервис для работы с пользователями. @@ -27,4 +29,10 @@ public interface UserService { */ GetReviewResponse getReview(String userId); + /** + * Создает нового пользователя в системе. + * Эндпоинт создан в целях тестирования. + */ + UserResponse createUser(CreateUserRequest createUserRequest); + } diff --git a/avito-test-impl/src/main/java/ru/iakovlysenko/contest/service/impl/UserServiceImpl.java b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/service/impl/UserServiceImpl.java index f04327f..59138f4 100644 --- a/avito-test-impl/src/main/java/ru/iakovlysenko/contest/service/impl/UserServiceImpl.java +++ b/avito-test-impl/src/main/java/ru/iakovlysenko/contest/service/impl/UserServiceImpl.java @@ -4,9 +4,12 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import ru.iakovlysenko.contest.dto.request.CreateUserRequest; import ru.iakovlysenko.contest.entity.PullRequest; +import ru.iakovlysenko.contest.entity.Team; import ru.iakovlysenko.contest.entity.User; import ru.iakovlysenko.contest.repository.PullRequestRepository; +import ru.iakovlysenko.contest.repository.TeamRepository; import ru.iakovlysenko.contest.repository.UserRepository; import ru.iakovlysenko.contest.dto.request.SetIsActiveRequest; import ru.iakovlysenko.contest.dto.response.GetReviewResponse; @@ -16,6 +19,7 @@ import ru.iakovlysenko.contest.mapper.UserMapper; import ru.iakovlysenko.contest.service.UserService; +import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; @@ -28,47 +32,69 @@ @Service @RequiredArgsConstructor public class UserServiceImpl implements UserService { - + private final UserRepository userRepository; private final PullRequestRepository pullRequestRepository; + private final TeamRepository teamRepository; private final UserMapper userMapper; private final PullRequestMapper pullRequestMapper; - + @Override @Transactional public UserResponse setIsActive(SetIsActiveRequest request) { log.info("Установка флага активности для пользователя {} в {}", request.userId(), request.isActive()); - + User user = userRepository.findById(request.userId()) .orElseThrow(() -> new NotFoundException("Пользователь не найден: " + request.userId())); - + user.setIsActive(request.isActive()); user = userRepository.save(user); - + log.info("Флаг активности пользователя {} установлен в {}", request.userId(), request.isActive()); return userMapper.toResponse(user); } - + @Override @Transactional(readOnly = true) public GetReviewResponse getReview(String userId) { log.info("Получение ревью для пользователя: {}", userId); - + if (!userRepository.existsById(userId)) { throw new NotFoundException("Пользователь не найден: " + userId); } - + List pullRequests = pullRequestRepository.findByReviewerIdWithDetails(userId); - + GetReviewResponse response = new GetReviewResponse( userId, pullRequests.stream() .map(pullRequestMapper::toShortResponse) .collect(Collectors.toList()) ); - + log.info("Найдено {} PR для ревьювера {}", pullRequests.size(), userId); return response; } + @Override + @Transactional + public UserResponse createUser(CreateUserRequest createUserRequest) { + log.info("Создание нового пользователя: {}", createUserRequest.id()); + + Team team = teamRepository.findByTeamName(createUserRequest.teamName()) + .orElseThrow(() -> new NotFoundException("Команда не найдена: " + createUserRequest.teamName())); + + User newUser = User.builder() + .id(createUserRequest.id()) + .username(createUserRequest.username()) + .team(team) + .isActive(createUserRequest.isActive()) + .build(); + + newUser = userRepository.save(newUser); + + log.info("Пользователь успешно создан: {}", createUserRequest.id()); + return userMapper.toResponse(newUser); + } + } diff --git a/openapi.yml b/openapi.yml index 2079640..9354502 100644 --- a/openapi.yml +++ b/openapi.yml @@ -270,6 +270,66 @@ paths: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } + /users: + post: + tags: [Users] + summary: Создать нового пользователя + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [ id, username, teamName, isActive ] + properties: + id: + type: string + description: Уникальный идентификатор пользователя + username: + type: string + description: Имя пользователя + teamName: + type: string + description: Название команды, к которой принадлежит пользователь + isActive: + type: boolean + description: Флаг активности пользователя + example: + id: u1 + username: usertest + teamName: team_test + isActive: true + responses: + '200': + description: Пользователь успешно создан + content: + application/json: + schema: + $ref: '#/components/schemas/User' + example: + user_id: u1 + username: usertest + team_name: team_test + is_active: true + '404': + description: Команда не найдена + content: + application/json: + schema: { $ref: '#/components/schemas/ErrorResponse' } + example: + error: + code: NOT_FOUND + message: Команда не найдена: team_test + '400': + description: Ошибка валидации запроса + content: + application/json: + schema: { $ref: '#/components/schemas/ErrorResponse' } + example: + error: + code: VALIDATION_ERROR + message: teamName: must not be blank + /pullRequest/create: post: tags: [PullRequests]