Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
d5856d6
Начать реализовывать API для работы с категориями
AndreyMMGH Aug 12, 2025
5bea6e4
Добавить обработчик ошибок
AndreyMMGH Aug 13, 2025
3fdbebc
Добавить API для работы с категориями
AndreyMMGH Aug 14, 2025
e1ffabb
Покрыть тестами API для работы с категориями
AndreyMMGH Aug 15, 2025
587b2a0
Добавить API для работы с пользователями и покрыть его тестами
AndreyMMGH Aug 16, 2025
3bf1317
Внести корректировки в предыдущий код
AndreyMMGH Aug 20, 2025
d2b62f7
Добавить закрытый API для работы с событиями и закрытый API для работ…
AndreyMMGH Aug 20, 2025
2997c82
Внести изменения в предыдущий код
AndreyMMGH Aug 20, 2025
a7de730
Добавить Admin и Public API для работы с подборками событий. Покрыть …
AndreyMMGH Aug 20, 2025
2719072
Сделать изменения в предыдущем коде
AndreyMMGH Aug 22, 2025
f694008
Добавить Public и Admin API для работы с событиями. Покрыть тестами
AndreyMMGH Aug 22, 2025
2202fce
Добавить spring-boot-maven-plugin
AndreyMMGH Aug 22, 2025
835f39c
Скорректировать настройки подключения в application.yml для server и …
AndreyMMGH Aug 22, 2025
17abe17
Добавить обработчик ошибок в server
AndreyMMGH Aug 22, 2025
be9b633
Исправить замечания
AndreyMMGH Aug 24, 2025
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
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ services:
- "8080:8080"
depends_on:
- main-db
- stats-server
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://main-db:5432/maindb
- SPRING_DATASOURCE_USERNAME=dbuser2
Expand Down
43 changes: 43 additions & 0 deletions main/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,48 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ru.practicum.category.controller;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import ru.practicum.category.dto.CategoryDto;
import ru.practicum.category.dto.NewCategoryDto;
import ru.practicum.category.service.CategoryService;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping(path = "/admin/categories")
public class CategoryAdminController {

private final CategoryService categoryService;

@ResponseStatus(HttpStatus.CREATED)
@PostMapping
public CategoryDto createCategory(@Valid @RequestBody NewCategoryDto newCategoryDto) {
log.info("POST /admin/categories");
return categoryService.createCategory(newCategoryDto);
}

@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/{catId}")
public void deleteCategory(@PathVariable("catId") Long id) {
log.info("DELETE /admin/categories/{}", id);
categoryService.deleteCategory(id);
}

@PatchMapping("/{catId}")
public CategoryDto updateCategory(@PathVariable("catId") Long id, @Valid @RequestBody NewCategoryDto newCategoryDto) {
log.info("PATCH /admin/categories/{}", id);
return categoryService.updateCategory(id, newCategoryDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ru.practicum.category.controller;

import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import ru.practicum.category.dto.CategoryDto;
import ru.practicum.category.service.CategoryService;

import java.util.List;

@Validated
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping(path = "/categories")
public class CategoryPublicController {

private final CategoryService categoryService;

@GetMapping
public List<CategoryDto> getCategories(@RequestParam(defaultValue = "0") @PositiveOrZero Integer from,
@RequestParam(defaultValue = "10") @Positive Integer size) {
log.info("GET /categories?from={}&size={}", from, size);
return categoryService.getCategories(from, size);
}

@GetMapping("/{catId}")
public CategoryDto getCategory(@PathVariable(value = "catId") Long id) {
log.info("GET /categories/{}", id);
return categoryService.getCategory(id);
}
}
15 changes: 15 additions & 0 deletions main/src/main/java/ru/practicum/category/dto/CategoryDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ru.practicum.category.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class CategoryDto {
private Long id;
private String name;
}
18 changes: 18 additions & 0 deletions main/src/main/java/ru/practicum/category/dto/NewCategoryDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ru.practicum.category.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class NewCategoryDto {
@NotBlank(message = "Название категории не может быть пустым")
@Size(min = 1, max = 50, message = "Название категории должно быть в границах от {min} до {max} символов")
private String name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package ru.practicum.category.dto.mapper;

import lombok.experimental.UtilityClass;
import ru.practicum.category.dto.CategoryDto;
import ru.practicum.category.dto.NewCategoryDto;
import ru.practicum.category.model.Category;

@UtilityClass
public class CategoryMapper {
public static CategoryDto toCategoryDto(Category category) {
return new CategoryDto(
category.getId(),
category.getName()
);
}

public static Category toCategory(NewCategoryDto newCategoryDto) {
return new Category(
null,
newCategoryDto.getName()
);
}
}
34 changes: 34 additions & 0 deletions main/src/main/java/ru/practicum/category/model/Category.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package ru.practicum.category.model;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.Objects;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Table(name = "categories")
@Entity
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Category category)) return false;
return Objects.equals(id, category.id);
}

@Override
public int hashCode() {
return Objects.hash(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ru.practicum.category.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import ru.practicum.category.model.Category;

import java.util.Optional;

public interface CategoryRepository extends JpaRepository<Category, Long> {
boolean existsByName(String name);

Optional<Category> findByName(String name);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ru.practicum.category.service;

import ru.practicum.category.dto.CategoryDto;
import ru.practicum.category.dto.NewCategoryDto;

import java.util.List;

public interface CategoryService {
CategoryDto createCategory(NewCategoryDto newCategoryDto);

void deleteCategory(Long id);

CategoryDto updateCategory(Long id, NewCategoryDto newCategoryDto);

List<CategoryDto> getCategories(Integer from, Integer size);

CategoryDto getCategory(Long id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package ru.practicum.category.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.practicum.category.dto.CategoryDto;
import ru.practicum.category.dto.NewCategoryDto;
import ru.practicum.category.dto.mapper.CategoryMapper;
import ru.practicum.category.model.Category;
import ru.practicum.category.repository.CategoryRepository;
import ru.practicum.event.repository.EventRepository;
import ru.practicum.exception.ConflictException;
import ru.practicum.exception.NotFoundException;

import java.util.List;
import java.util.stream.Collectors;

@Transactional(readOnly = true)
@Slf4j
@RequiredArgsConstructor
@Service
public class CategoryServiceImpl implements CategoryService {
private final CategoryRepository categoryRepository;
private final EventRepository eventRepository;

@Transactional
@Override
public CategoryDto createCategory(NewCategoryDto newCategoryDto) {
if (categoryRepository.existsByName(newCategoryDto.getName())) {
log.warn("Категория - {} уже существует", newCategoryDto.getName());
throw new ConflictException("Категория - " + newCategoryDto.getName() + " уже существует");
}

return CategoryMapper.toCategoryDto(categoryRepository.save(CategoryMapper.toCategory(newCategoryDto)));
}

@Transactional
@Override
public void deleteCategory(Long id) {
findCategoryById(id);
validateCategoryIsEmpty(id);
categoryRepository.deleteById(id);
}

@Transactional
@Override
public CategoryDto updateCategory(Long id, NewCategoryDto newCategoryDto) {
Category category = findCategoryById(id);

if (newCategoryDto.getName() != null && !category.getName().equals(newCategoryDto.getName())) {
categoryRepository.findByName(newCategoryDto.getName())
.ifPresent(existing -> {
if (!existing.getId().equals(id)) {
log.warn("Категория с именем '{}' уже существует", newCategoryDto.getName());
throw new ConflictException("Категория с именем '" + newCategoryDto.getName() + "' уже существует");
}
});

category.setName(newCategoryDto.getName());
}

return CategoryMapper.toCategoryDto(categoryRepository.save(category));
}

@Override
public List<CategoryDto> getCategories(Integer from, Integer size) {
Pageable pageable = PageRequest.of(from / size, size, Sort.by("id").ascending());

return categoryRepository.findAll(pageable)
.stream()
.map(CategoryMapper::toCategoryDto)
.collect(Collectors.toList());
}

@Override
public CategoryDto getCategory(Long id) {
return CategoryMapper.toCategoryDto(findCategoryById(id));
}

private Category findCategoryById(Long categoryId) {
return categoryRepository.findById(categoryId)
.orElseThrow(() -> new NotFoundException("Категория с данным id: " + categoryId + " не найдена"));
}

private void validateCategoryIsEmpty(Long categoryId) {
boolean isUsed = eventRepository.existsByCategoryId(categoryId);

if (isUsed) {
log.warn("Категория с данным id: {} используется", categoryId);
throw new ConflictException("Категория используется");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package ru.practicum.compilation.controller;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import ru.practicum.compilation.dto.CompilationDto;
import ru.practicum.compilation.dto.NewCompilationDto;
import ru.practicum.compilation.dto.UpdateCompilationRequest;
import ru.practicum.compilation.service.CompilationService;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping(path = "/admin/compilations")
public class CompilationAdminController {
private final CompilationService compilationService;

@ResponseStatus(HttpStatus.CREATED)
@PostMapping
public CompilationDto createCompilation(@RequestBody @Valid NewCompilationDto newCompilationDto) {
log.info("POST /admin/compilations");
return compilationService.createCompilation(newCompilationDto);
}

@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/{compId}")
public void deleteCompilation(@PathVariable("compId") Long id) {
log.info("DELETE /admin/compilations");
compilationService.deleteCompilation(id);
}

@PatchMapping("/{compId}")
public CompilationDto updateCompilation(@PathVariable("compId") Long id,
@RequestBody(required = false) @Valid UpdateCompilationRequest updateCompilationRequest) {
log.info("PATCH /admin/compilations/{}", id);
return compilationService.updateCompilation(id, updateCompilationRequest);
}
}
Loading