diff --git a/pom.xml b/pom.xml
index 0cad031..64175e2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,6 +32,15 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ 3.3.1
+
@@ -40,6 +49,22 @@
org.springframework.boot
spring-boot-maven-plugin
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ 3.3.1
+
+ checkstyle.xml
+
+
+
+
+ check
+
+ compile
+
+
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/annotation/NotBeforeCinemaBirthday.java b/src/main/java/ru/yandex/practicum/filmorate/annotation/NotBeforeCinemaBirthday.java
new file mode 100644
index 0000000..6bc785f
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/annotation/NotBeforeCinemaBirthday.java
@@ -0,0 +1,23 @@
+package ru.yandex.practicum.filmorate.annotation;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+import ru.yandex.practicum.filmorate.util.NotBeforeCinemaBirthdayValidator;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Documented
+@Constraint(validatedBy = NotBeforeCinemaBirthdayValidator.class)
+@Target({ElementType.FIELD, ElementType.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NotBeforeCinemaBirthday {
+ String message() default "The date cannot be earlier than the cinema's birthday — December 28, 1895";
+
+ Class>[] groups() default {};
+
+ Class extends Payload>[] payload() default {};
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
index 08cf0a1..3943efd 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
@@ -1,7 +1,51 @@
package ru.yandex.practicum.filmorate.controller;
+import jakarta.validation.Valid;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import ru.yandex.practicum.filmorate.exception.FilmNotFoundException;
+import ru.yandex.practicum.filmorate.model.Film;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
@RestController
+@RequestMapping("/films")
public class FilmController {
+ private final Map films = new HashMap<>();
+ private long lastGeneratedID = 0;
+
+ @PostMapping
+ public Film createFilm(@Valid @RequestBody Film film) {
+ film.setId(++lastGeneratedID);
+ films.put(film.getId(), film);
+
+ log.info("Film created: id={}, name={}", film.getId(), film.getName());
+ return film;
+ }
+
+ @GetMapping
+ public List getAllFilms() {
+ log.debug("Retrieving all films (total={})", films.size());
+ return new ArrayList<>(films.values());
+ }
+
+ @PutMapping
+ public Film updateFilm(@Valid @RequestBody Film film) {
+ if (!films.containsKey(film.getId())) {
+ log.warn("Attempt to update non-existent film id={}", film.getId());
+ throw new FilmNotFoundException();
+ }
+ films.put(film.getId(), film);
+ log.info("Film updated: id={}, name={}", film.getId(), film.getName());
+ return film;
+ }
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
new file mode 100644
index 0000000..4c7d4ff
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
@@ -0,0 +1,51 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import jakarta.validation.Valid;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import ru.yandex.practicum.filmorate.exception.UserNotFoundException;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@RestController
+@RequestMapping("/users")
+public class UserController {
+ private final Map users = new HashMap<>();
+ private long lastGeneratedID = 0;
+
+ @PostMapping
+ public User createUser(@Valid @RequestBody User user) {
+ user.setId(++lastGeneratedID);
+ users.put(user.getId(), user);
+
+ log.info("User created: id={}, email={}, login={}", user.getId(), user.getEmail(), user.getLogin());
+ return user;
+ }
+
+ @GetMapping
+ public List getAllUsers() {
+ log.debug("Retrieving all users (total={})", users.size());
+ return new ArrayList<>(users.values());
+ }
+
+ @PutMapping
+ public User updateUser(@Valid @RequestBody User user) {
+ if (!users.containsKey(user.getId())) {
+ log.warn("Attempt to update non-existent user id={}", user.getId());
+ throw new UserNotFoundException();
+ }
+ users.put(user.getId(), user);
+ log.info("User updated: id={}, email={}, login={}", user.getId(), user.getEmail(), user.getLogin());
+ return user;
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/FilmNotFoundException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/FilmNotFoundException.java
new file mode 100644
index 0000000..b3564d4
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/FilmNotFoundException.java
@@ -0,0 +1,15 @@
+package ru.yandex.practicum.filmorate.exception;
+
+public class FilmNotFoundException extends RuntimeException {
+ public FilmNotFoundException() {
+ super("Film not found");
+ }
+
+ public FilmNotFoundException(String message) {
+ super(message);
+ }
+
+ public FilmNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/GlobalExceptionHandler.java b/src/main/java/ru/yandex/practicum/filmorate/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..b9ec44c
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/GlobalExceptionHandler.java
@@ -0,0 +1,48 @@
+package ru.yandex.practicum.filmorate.exception;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+@ControllerAdvice
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(ValidationException.class)
+ public ResponseEntity