Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ databaseChangeLog:
type: VARCHAR(255)
constraints:
primaryKey: true
nullable: false
- column:
name: username
type: VARCHAR(255)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -31,4 +33,12 @@ public interface UserControllerApi {
*/
ResponseEntity<GetReviewResponse> getReview(@RequestParam("user_id") String userId);

/**
* Создает нового пользователя в системе.
*
* @param createUserRequest ДТО запроса с данными для создания пользователя
* @return ДТО с анными пользователя
*/
ResponseEntity<UserResponse> createUser(CreateUserRequest createUserRequest);

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -23,7 +25,7 @@
public class UserControllerImpl implements UserControllerApi {

private final UserService userService;

@Override
@PostMapping("/setIsActive")
public ResponseEntity<UserWrapperResponse> setIsActive(@Valid @RequestBody SetIsActiveRequest request) {
Expand All @@ -46,5 +48,16 @@ public ResponseEntity<GetReviewResponse> getReview(@RequestParam("user_id") Stri

return ResponseEntity.ok(response);
}

@Override
@PostMapping
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody CreateUserRequest createUserRequest) {
log.info("POST /users - Создание нового пользователя");

UserResponse response = userService.createUser(createUserRequest);

return ResponseEntity.ok(response);
}

}

Original file line number Diff line number Diff line change
@@ -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
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public interface UserMapper {
* @return ДТО ответа с информацией о пользователе
*/
@Mapping(target = "userId", source = "id")
@Mapping(target = "teamName", expression = "java(user.getTeamName())")
UserResponse toResponse(User user);

}
Original file line number Diff line number Diff line change
@@ -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;

/**
* Сервис для работы с пользователями.
Expand All @@ -27,4 +29,10 @@ public interface UserService {
*/
GetReviewResponse getReview(String userId);

/**
* Создает нового пользователя в системе.
* Эндпоинт создан в целях тестирования.
*/
UserResponse createUser(CreateUserRequest createUserRequest);

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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<PullRequest> 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);
}

}
60 changes: 60 additions & 0 deletions openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down