From 1fb86d928bc284760dd510b78790c0315f60b8c8 Mon Sep 17 00:00:00 2001 From: Kirill <117719052+KirillFirsof@users.noreply.github.com> Date: Thu, 30 May 2024 15:06:39 +0400 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D1=8C=D1=82?= =?UTF-8?q?=D0=B5=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Laba1/.vscode/launch.json | 14 + Laba1/demo/.gitignore | 37 +++ Laba1/demo/build.gradle | 27 ++ Laba1/demo/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + Laba1/demo/gradlew | 249 ++++++++++++++++++ Laba1/demo/gradlew.bat | 92 +++++++ Laba1/demo/index.html | 61 +++++ Laba1/demo/settings.gradle | 1 + .../java/com/example/demo/ApiController.java | 56 ++++ .../com/example/demo/DemoApplication.java | 13 + .../src/main/java/com/example/demo/User.java | 42 +++ .../main/java/com/example/demo/WebConfig.java | 15 ++ .../src/main/resources/application.properties | 1 + .../example/demo/DemoApplicationTests.java | 13 + Laba2/2.zip | Bin 0 -> 265392 bytes Laba2/Лекция2-src/.gitignore | 37 +++ Laba2/Лекция2-src/build.gradle | 28 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + Laba2/Лекция2-src/gradlew | 249 ++++++++++++++++++ Laba2/Лекция2-src/gradlew.bat | 92 +++++++ Laba2/Лекция2-src/package-lock.json | 6 + Laba2/Лекция2-src/settings.gradle | 1 + .../com/example/demo/DemoApplication.java | 78 ++++++ .../demo/core/configuration/Constants.java | 8 + .../configuration/MapperConfiguration.java | 13 + .../core/configuration/WebConfiguration.java | 15 ++ .../demo/core/error/NotFoundException.java | 7 + .../example/demo/core/model/BaseEntity.java | 20 ++ .../core/repository/CommonRepository.java | 17 ++ .../demo/core/repository/MapRepository.java | 57 ++++ .../demo/films/api/FilmController.java | 70 +++++ .../com/example/demo/films/api/FilmDto.java | 59 +++++ .../example/demo/films/model/FilmEntity.java | 76 ++++++ .../demo/films/repository/FilmRepository.java | 10 + .../demo/films/service/FilmService.java | 56 ++++ .../demo/genres/api/GenreController.java | 65 +++++ .../com/example/demo/genres/api/GenreDto.java | 28 ++ .../demo/genres/model/GenreEntity.java | 42 +++ .../genres/repository/GenreRepository.java | 10 + .../demo/genres/service/GenreService.java | 42 +++ .../configuration/SpeakerConfiguration.java | 5 + .../subscribes/api/SubscribeController.java | 64 +++++ .../demo/subscribes/api/SubscribeDto.java | 41 +++ .../subscribes/model/SubscribeEntity.java | 54 ++++ .../repository/SubscribeRepository.java | 10 + .../subscribes/service/SubscribeService.java | 44 ++++ .../demo/users/api/UserController.java | 85 ++++++ .../com/example/demo/users/api/UserDto.java | 84 ++++++ .../example/demo/users/model/UserEntity.java | 102 +++++++ .../demo/users/repository/UserRepository.java | 10 + .../demo/users/service/UserService.java | 71 +++++ .../src/main/resources/application.properties | 1 + .../com/example/demo/FilmsServiceTests.java | 79 ++++++ .../com/example/demo/GenreServiceTests.java | 61 +++++ .../example/demo/SubscribeServiceTests.java | 65 +++++ .../com/example/demo/UsersServiceTests.java | 110 ++++++++ Laba3/Лекция3-src/.gitignore | 36 +++ .../Лекция3-src/.vscode/extensions.json | 12 + Laba3/Лекция3-src/.vscode/launch.json | 24 ++ Laba3/Лекция3-src/.vscode/settings.json | 21 ++ Laba3/Лекция3-src/build.gradle | 43 +++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + Laba3/Лекция3-src/gradlew | 249 ++++++++++++++++++ Laba3/Лекция3-src/gradlew.bat | 92 +++++++ Laba3/Лекция3-src/readme.md | 18 ++ Laba3/Лекция3-src/settings.gradle | 1 + .../com/example/demo/DemoApplication.java | 74 ++++++ .../demo/core/configuration/Constants.java | 10 + .../configuration/MapperConfiguration.java | 13 + .../core/configuration/WebConfiguration.java | 15 ++ .../demo/core/error/NotFoundException.java | 7 + .../example/demo/core/model/BaseEntity.java | 28 ++ .../demo/films/api/FilmController.java | 73 +++++ .../com/example/demo/films/api/FilmDto.java | 59 +++++ .../example/demo/films/model/FilmEntity.java | 106 ++++++++ .../demo/films/model/UserFilmEntity.java | 15 ++ .../demo/films/repository/FilmRepository.java | 11 + .../demo/films/service/FilmService.java | 64 +++++ .../demo/genres/api/GenreController.java | 68 +++++ .../com/example/demo/genres/api/GenreDto.java | 30 +++ .../demo/genres/model/GenreEntity.java | 47 ++++ .../genres/repository/GenreRepository.java | 11 + .../demo/genres/service/GenreService.java | 62 +++++ .../subscribes/api/SubscribeController.java | 68 +++++ .../demo/subscribes/api/SubscribeDto.java | 42 +++ .../subscribes/model/SubscribeEntity.java | 60 +++++ .../repository/SubscribeRepository.java | 11 + .../subscribes/service/SubscribeService.java | 62 +++++ .../demo/userFilm/model/UserFilmEntity.java | 95 +++++++ .../demo/userFilm/model/UserFilmId.java | 55 ++++ .../demo/users/api/UserController.java | 80 ++++++ .../com/example/demo/users/api/UserDto.java | 89 +++++++ .../example/demo/users/model/UserEntity.java | 116 ++++++++ .../demo/users/repository/UserRepository.java | 11 + .../demo/users/service/UserService.java | 218 +++++++++++++++ .../src/main/resources/application.properties | 20 ++ .../com/example/demo/FilmServiceTests.java | 102 +++++++ .../com/example/demo/GenreServiceTests.java | 79 ++++++ .../src/test/resources/application.properties | 14 + laba3.1/Лекция4-src/.gitignore | 36 +++ .../Лекция4-src/.vscode/extensions.json | 12 + laba3.1/Лекция4-src/.vscode/launch.json | 24 ++ .../Лекция4-src/.vscode/settings.json | 21 ++ laba3.1/Лекция4-src/build.gradle | 68 +++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + laba3.1/Лекция4-src/gradlew | 249 ++++++++++++++++++ laba3.1/Лекция4-src/gradlew.bat | 92 +++++++ laba3.1/Лекция4-src/readme.md | 28 ++ laba3.1/Лекция4-src/settings.gradle | 1 + .../com/example/demo/DemoApplication.java | 74 ++++++ .../com/example/demo/core/api/PageDto.java | 97 +++++++ .../example/demo/core/api/PageDtoMapper.java | 25 ++ .../demo/core/configuration/Constants.java | 12 + .../configuration/MapperConfiguration.java | 13 + .../core/configuration/WebConfiguration.java | 15 ++ .../demo/core/error/AdviceController.java | 54 ++++ .../demo/core/error/ErrorCauseDto.java | 31 +++ .../com/example/demo/core/error/ErrorDto.java | 20 ++ .../demo/core/error/NotFoundException.java | 7 + .../example/demo/core/model/BaseEntity.java | 28 ++ .../demo/orders/api/OrderController.java | 98 +++++++ .../com/example/demo/orders/api/OrderDto.java | 57 ++++ .../demo/orders/api/OrderGroupedDto.java | 31 +++ .../demo/orders/model/OrderEntity.java | 91 +++++++ .../demo/orders/model/OrderGrouped.java | 11 + .../orders/repository/OrderRepository.java | 41 +++ .../demo/orders/service/OrderService.java | 88 +++++++ .../api/SubscriptionController.java | 68 +++++ .../subscriptions/api/SubscriptionDto.java | 30 +++ .../model/SubscriptionEntity.java | 67 +++++ .../repository/SubscriptionRepository.java | 11 + .../service/SubscriptionService.java | 63 +++++ .../demo/types/api/TypeController.java | 68 +++++ .../com/example/demo/types/api/TypeDto.java | 30 +++ .../example/demo/types/model/TypeEntity.java | 48 ++++ .../demo/types/repository/TypeRepository.java | 11 + .../demo/types/service/TypeService.java | 62 +++++ .../demo/users/api/UserController.java | 109 ++++++++ .../com/example/demo/users/api/UserDto.java | 31 +++ .../example/demo/users/model/UserEntity.java | 91 +++++++ .../demo/users/repository/UserRepository.java | 13 + .../demo/users/service/UserService.java | 138 ++++++++++ .../model/UserSubscriptionEntity.java | 95 +++++++ .../model/UserSubscriptionId.java | 55 ++++ .../main/resources/application-dev.properties | 8 + .../resources/application-prod.properties | 9 + .../src/main/resources/application.properties | 21 ++ .../src/main/resources/db/0-sequence.xml | 10 + .../src/main/resources/db/1-types-data.xml | 21 ++ .../src/main/resources/db/1-types-schema.xml | 18 ++ .../src/main/resources/db/2-items-data.xml | 51 ++++ .../src/main/resources/db/2-items-schema.xml | 27 ++ .../main/resources/db/3-users-constraint.xml | 10 + .../src/main/resources/db/3-users-data.xml | 24 ++ .../src/main/resources/db/3-users-schema.xml | 27 ++ .../main/resources/db/4-items-to-orders.xml | 10 + .../resources/db/5-subscriptions-data.xml | 21 ++ .../resources/db/5-subscriptions-schema.xml | 18 ++ .../db/6-users-subscriptions-data.xml | 39 +++ .../db/6-users-subscriptions-schema.xml | 25 ++ .../src/main/resources/db/changelog.xml | 30 +++ .../com/example/demo/TypeServiceTests.java | 79 ++++++ .../example/demo/UserOrderServiceTest.java | 140 ++++++++++ .../src/test/resources/application.properties | 20 ++ laba3.2/.vscode/launch.json | 21 ++ laba3.2/.vscode/settings.json | 3 + laba3.2/data.mv.db | Bin 0 -> 49152 bytes laba3.2/Лекция3-src/.gitignore | 36 +++ .../Лекция3-src/.vscode/extensions.json | 12 + laba3.2/Лекция3-src/.vscode/launch.json | 24 ++ .../Лекция3-src/.vscode/settings.json | 21 ++ laba3.2/Лекция3-src/build.gradle | 43 +++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + laba3.2/Лекция3-src/gradlew | 249 ++++++++++++++++++ laba3.2/Лекция3-src/gradlew.bat | 92 +++++++ laba3.2/Лекция3-src/package-lock.json | 6 + laba3.2/Лекция3-src/readme.md | 18 ++ laba3.2/Лекция3-src/settings.gradle | 1 + .../com/example/demo/DemoApplication.java | 79 ++++++ .../demo/core/configuration/Constants.java | 10 + .../configuration/MapperConfiguration.java | 13 + .../core/configuration/WebConfiguration.java | 15 ++ .../demo/core/error/NotFoundException.java | 7 + .../example/demo/core/model/BaseEntity.java | 28 ++ .../demo/films/api/FilmController.java | 73 +++++ .../com/example/demo/films/api/FilmDto.java | 70 +++++ .../example/demo/films/model/FilmEntity.java | 87 ++++++ .../demo/films/repository/FilmRepository.java | 14 + .../demo/films/service/FilmService.java | 72 +++++ .../demo/genres/api/GenreController.java | 68 +++++ .../com/example/demo/genres/api/GenreDto.java | 30 +++ .../demo/genres/model/GenreEntity.java | 48 ++++ .../genres/repository/GenreRepository.java | 11 + .../demo/genres/service/GenreService.java | 62 +++++ .../subscribes/api/SubscribeController.java | 68 +++++ .../demo/subscribes/api/SubscribeDto.java | 43 +++ .../subscribes/model/SubscribeEntity.java | 60 +++++ .../repository/SubscribeRepository.java | 11 + .../subscribes/service/SubscribeService.java | 63 +++++ .../userfilms/api/UserFilmController.java | 105 ++++++++ .../demo/userfilms/api/UserFilmDto.java | 50 ++++ .../userfilms/api/UserFilmGroupedDto.java | 22 ++ .../demo/userfilms/model/UserFilmEntity.java | 87 ++++++ .../demo/userfilms/model/UserFilmGrouped.java | 9 + .../repository/UserFilmRepository.java | 30 +++ .../userfilms/service/UserFilmService.java | 125 +++++++++ .../demo/users/api/UserController.java | 73 +++++ .../com/example/demo/users/api/UserDto.java | 66 +++++ .../example/demo/users/model/UserEntity.java | 87 ++++++ .../demo/users/repository/UserRepository.java | 14 + .../demo/users/service/UserService.java | 73 +++++ .../src/main/resources/application.properties | 20 ++ .../com/example/demo/FilmServiceTests.java | 103 ++++++++ .../java/com/example/demo/GServiceTests.java | 82 ++++++ .../example/demo/SubscribeServiceTests.java | 86 ++++++ .../example/demo/UserFilmServiceTests.java | 141 ++++++++++ .../com/example/demo/UserServiceTests.java | 142 ++++++++++ .../src/test/resources/application.properties | 14 + laba4/.gitignore | 36 +++ laba4/.vscode/extensions.json | 12 + laba4/.vscode/launch.json | 14 + laba4/.vscode/settings.json | 24 ++ laba4/build.gradle | 48 ++++ laba4/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + laba4/gradlew | 249 ++++++++++++++++++ laba4/gradlew.bat | 92 +++++++ laba4/readme.md | 15 ++ laba4/settings.gradle | 1 + .../com/example/demo/DemoApplication.java | 78 ++++++ .../demo/core/api/GlobalController.java | 43 +++ .../demo/core/api/PageAttributesMapper.java | 18 ++ .../demo/core/configuration/Constants.java | 16 ++ .../configuration/MapperConfiguration.java | 23 ++ .../demo/core/error/AdviceController.java | 53 ++++ .../demo/core/error/NotFoundException.java | 7 + .../example/demo/core/model/BaseEntity.java | 28 ++ .../demo/core/session/SessionCart.java | 14 + .../demo/core/session/SessionHelper.java | 16 ++ .../example/demo/films/model/FilmEntity.java | 63 +++++ .../com/example/demo/orders/api/OrderDto.java | 53 ++++ .../demo/orders/api/OrderGroupedDto.java | 31 +++ .../demo/orders/model/OrderEntity.java | 91 +++++++ .../demo/orders/model/OrderGrouped.java | 11 + .../orders/repository/OrderRepository.java | 42 +++ .../demo/orders/service/OrderService.java | 101 +++++++ .../api/SubscriptionController.java | 104 ++++++++ .../subscriptions/api/SubscriptionDto.java | 27 ++ .../model/SubscriptionEntity.java | 67 +++++ .../repository/SubscriptionRepository.java | 13 + .../service/SubscriptionService.java | 66 +++++ .../demo/types/api/TypeController.java | 104 ++++++++ .../com/example/demo/types/api/TypeDto.java | 27 ++ .../example/demo/types/model/TypeEntity.java | 48 ++++ .../demo/types/repository/TypeRepository.java | 13 + .../demo/types/service/TypeService.java | 75 ++++++ .../demo/users/api/UserCartController.java | 151 +++++++++++ .../example/demo/users/api/UserCartDto.java | 65 +++++ .../demo/users/api/UserController.java | 127 +++++++++ .../com/example/demo/users/api/UserDto.java | 27 ++ .../demo/users/api/UserLoginController.java | 59 +++++ .../example/demo/users/api/UserLoginDto.java | 29 ++ .../demo/users/api/UserProfileController.java | 133 ++++++++++ .../demo/users/api/UserProfileDto.java | 23 ++ .../demo/users/api/UserSubscriptionDto.java | 31 +++ .../example/demo/users/model/UserEntity.java | 91 +++++++ .../model/UserSubscriptionWithStatus.java | 60 +++++ .../demo/users/repository/UserRepository.java | 13 + .../demo/users/service/UserService.java | 141 ++++++++++ .../model/UserSubscriptionEntity.java | 95 +++++++ .../model/UserSubscriptionId.java | 55 ++++ .../src/main/resources/application.properties | 20 ++ laba4/src/main/resources/public/css/style.css | 67 +++++ laba4/src/main/resources/public/favicon.svg | 3 + laba4/src/main/resources/templates/cart.html | 83 ++++++ .../src/main/resources/templates/default.html | 65 +++++ laba4/src/main/resources/templates/error.html | 37 +++ laba4/src/main/resources/templates/login.html | 30 +++ .../src/main/resources/templates/orders.html | 61 +++++ .../main/resources/templates/pagination.html | 51 ++++ .../src/main/resources/templates/profile.html | 60 +++++ .../templates/subscription-edit.html | 29 ++ .../resources/templates/subscription.html | 51 ++++ .../main/resources/templates/type-edit.html | 28 ++ laba4/src/main/resources/templates/type.html | 50 ++++ .../main/resources/templates/user-edit.html | 29 ++ laba4/src/main/resources/templates/user.html | 56 ++++ .../com/example/demo/TypeServiceTests.java | 79 ++++++ .../example/demo/UserOrderServiceTest.java | 132 ++++++++++ .../src/test/resources/application.properties | 14 + 295 files changed, 14423 insertions(+) create mode 100644 Laba1/.vscode/launch.json create mode 100644 Laba1/demo/.gitignore create mode 100644 Laba1/demo/build.gradle create mode 100644 Laba1/demo/gradle/wrapper/gradle-wrapper.jar create mode 100644 Laba1/demo/gradle/wrapper/gradle-wrapper.properties create mode 100644 Laba1/demo/gradlew create mode 100644 Laba1/demo/gradlew.bat create mode 100644 Laba1/demo/index.html create mode 100644 Laba1/demo/settings.gradle create mode 100644 Laba1/demo/src/main/java/com/example/demo/ApiController.java create mode 100644 Laba1/demo/src/main/java/com/example/demo/DemoApplication.java create mode 100644 Laba1/demo/src/main/java/com/example/demo/User.java create mode 100644 Laba1/demo/src/main/java/com/example/demo/WebConfig.java create mode 100644 Laba1/demo/src/main/resources/application.properties create mode 100644 Laba1/demo/src/test/java/com/example/demo/DemoApplicationTests.java create mode 100644 Laba2/2.zip create mode 100644 Laba2/Лекция2-src/.gitignore create mode 100644 Laba2/Лекция2-src/build.gradle create mode 100644 Laba2/Лекция2-src/gradle/wrapper/gradle-wrapper.jar create mode 100644 Laba2/Лекция2-src/gradle/wrapper/gradle-wrapper.properties create mode 100644 Laba2/Лекция2-src/gradlew create mode 100644 Laba2/Лекция2-src/gradlew.bat create mode 100644 Laba2/Лекция2-src/package-lock.json create mode 100644 Laba2/Лекция2-src/settings.gradle create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/DemoApplication.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/Constants.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/core/error/NotFoundException.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/core/model/BaseEntity.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/core/repository/CommonRepository.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/core/repository/MapRepository.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/films/api/FilmController.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/films/api/FilmDto.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/films/model/FilmEntity.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/films/repository/FilmRepository.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/films/service/FilmService.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/genres/api/GenreController.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/genres/api/GenreDto.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/genres/model/GenreEntity.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/genres/service/GenreService.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/speaker/configuration/SpeakerConfiguration.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/users/api/UserController.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/users/api/UserDto.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/users/model/UserEntity.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/users/repository/UserRepository.java create mode 100644 Laba2/Лекция2-src/src/main/java/com/example/demo/users/service/UserService.java create mode 100644 Laba2/Лекция2-src/src/main/resources/application.properties create mode 100644 Laba2/Лекция2-src/src/test/java/com/example/demo/FilmsServiceTests.java create mode 100644 Laba2/Лекция2-src/src/test/java/com/example/demo/GenreServiceTests.java create mode 100644 Laba2/Лекция2-src/src/test/java/com/example/demo/SubscribeServiceTests.java create mode 100644 Laba2/Лекция2-src/src/test/java/com/example/demo/UsersServiceTests.java create mode 100644 Laba3/Лекция3-src/.gitignore create mode 100644 Laba3/Лекция3-src/.vscode/extensions.json create mode 100644 Laba3/Лекция3-src/.vscode/launch.json create mode 100644 Laba3/Лекция3-src/.vscode/settings.json create mode 100644 Laba3/Лекция3-src/build.gradle create mode 100644 Laba3/Лекция3-src/gradle/wrapper/gradle-wrapper.jar create mode 100644 Laba3/Лекция3-src/gradle/wrapper/gradle-wrapper.properties create mode 100644 Laba3/Лекция3-src/gradlew create mode 100644 Laba3/Лекция3-src/gradlew.bat create mode 100644 Laba3/Лекция3-src/readme.md create mode 100644 Laba3/Лекция3-src/settings.gradle create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/DemoApplication.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/Constants.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/core/error/NotFoundException.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/core/model/BaseEntity.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/films/api/FilmController.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/films/api/FilmDto.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/films/model/FilmEntity.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/films/model/UserFilmEntity.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/films/repository/FilmRepository.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/films/service/FilmService.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreController.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreDto.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/genres/model/GenreEntity.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/genres/service/GenreService.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/userFilm/model/UserFilmEntity.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/userFilm/model/UserFilmId.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/users/api/UserController.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/users/api/UserDto.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/users/model/UserEntity.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/users/repository/UserRepository.java create mode 100644 Laba3/Лекция3-src/src/main/java/com/example/demo/users/service/UserService.java create mode 100644 Laba3/Лекция3-src/src/main/resources/application.properties create mode 100644 Laba3/Лекция3-src/src/test/java/com/example/demo/FilmServiceTests.java create mode 100644 Laba3/Лекция3-src/src/test/java/com/example/demo/GenreServiceTests.java create mode 100644 Laba3/Лекция3-src/src/test/resources/application.properties create mode 100644 laba3.1/Лекция4-src/.gitignore create mode 100644 laba3.1/Лекция4-src/.vscode/extensions.json create mode 100644 laba3.1/Лекция4-src/.vscode/launch.json create mode 100644 laba3.1/Лекция4-src/.vscode/settings.json create mode 100644 laba3.1/Лекция4-src/build.gradle create mode 100644 laba3.1/Лекция4-src/gradle/wrapper/gradle-wrapper.jar create mode 100644 laba3.1/Лекция4-src/gradle/wrapper/gradle-wrapper.properties create mode 100644 laba3.1/Лекция4-src/gradlew create mode 100644 laba3.1/Лекция4-src/gradlew.bat create mode 100644 laba3.1/Лекция4-src/readme.md create mode 100644 laba3.1/Лекция4-src/settings.gradle create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/DemoApplication.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/core/api/PageDto.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/core/api/PageDtoMapper.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/Constants.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/AdviceController.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/ErrorCauseDto.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/ErrorDto.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/NotFoundException.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/core/model/BaseEntity.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderController.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderDto.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderGroupedDto.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/model/OrderEntity.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/model/OrderGrouped.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/repository/OrderRepository.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/service/OrderService.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/api/SubscriptionController.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/api/SubscriptionDto.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/model/SubscriptionEntity.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/repository/SubscriptionRepository.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/service/SubscriptionService.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/types/api/TypeController.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/types/api/TypeDto.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/types/model/TypeEntity.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/types/repository/TypeRepository.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/types/service/TypeService.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/users/api/UserController.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/users/api/UserDto.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/users/model/UserEntity.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/users/repository/UserRepository.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/users/service/UserService.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionEntity.java create mode 100644 laba3.1/Лекция4-src/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionId.java create mode 100644 laba3.1/Лекция4-src/src/main/resources/application-dev.properties create mode 100644 laba3.1/Лекция4-src/src/main/resources/application-prod.properties create mode 100644 laba3.1/Лекция4-src/src/main/resources/application.properties create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/0-sequence.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/1-types-data.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/1-types-schema.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/2-items-data.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/2-items-schema.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/3-users-constraint.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/3-users-data.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/3-users-schema.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/4-items-to-orders.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/5-subscriptions-data.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/5-subscriptions-schema.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/6-users-subscriptions-data.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/6-users-subscriptions-schema.xml create mode 100644 laba3.1/Лекция4-src/src/main/resources/db/changelog.xml create mode 100644 laba3.1/Лекция4-src/src/test/java/com/example/demo/TypeServiceTests.java create mode 100644 laba3.1/Лекция4-src/src/test/java/com/example/demo/UserOrderServiceTest.java create mode 100644 laba3.1/Лекция4-src/src/test/resources/application.properties create mode 100644 laba3.2/.vscode/launch.json create mode 100644 laba3.2/.vscode/settings.json create mode 100644 laba3.2/data.mv.db create mode 100644 laba3.2/Лекция3-src/.gitignore create mode 100644 laba3.2/Лекция3-src/.vscode/extensions.json create mode 100644 laba3.2/Лекция3-src/.vscode/launch.json create mode 100644 laba3.2/Лекция3-src/.vscode/settings.json create mode 100644 laba3.2/Лекция3-src/build.gradle create mode 100644 laba3.2/Лекция3-src/gradle/wrapper/gradle-wrapper.jar create mode 100644 laba3.2/Лекция3-src/gradle/wrapper/gradle-wrapper.properties create mode 100644 laba3.2/Лекция3-src/gradlew create mode 100644 laba3.2/Лекция3-src/gradlew.bat create mode 100644 laba3.2/Лекция3-src/package-lock.json create mode 100644 laba3.2/Лекция3-src/readme.md create mode 100644 laba3.2/Лекция3-src/settings.gradle create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/DemoApplication.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/Constants.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/core/error/NotFoundException.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/core/model/BaseEntity.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/films/api/FilmController.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/films/api/FilmDto.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/films/model/FilmEntity.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/films/repository/FilmRepository.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/films/service/FilmService.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreController.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreDto.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/model/GenreEntity.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/service/GenreService.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmController.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmDto.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmGroupedDto.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/model/UserFilmEntity.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/model/UserFilmGrouped.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/repository/UserFilmRepository.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/service/UserFilmService.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/users/api/UserController.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/users/api/UserDto.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/users/model/UserEntity.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/users/repository/UserRepository.java create mode 100644 laba3.2/Лекция3-src/src/main/java/com/example/demo/users/service/UserService.java create mode 100644 laba3.2/Лекция3-src/src/main/resources/application.properties create mode 100644 laba3.2/Лекция3-src/src/test/java/com/example/demo/FilmServiceTests.java create mode 100644 laba3.2/Лекция3-src/src/test/java/com/example/demo/GServiceTests.java create mode 100644 laba3.2/Лекция3-src/src/test/java/com/example/demo/SubscribeServiceTests.java create mode 100644 laba3.2/Лекция3-src/src/test/java/com/example/demo/UserFilmServiceTests.java create mode 100644 laba3.2/Лекция3-src/src/test/java/com/example/demo/UserServiceTests.java create mode 100644 laba3.2/Лекция3-src/src/test/resources/application.properties create mode 100644 laba4/.gitignore create mode 100644 laba4/.vscode/extensions.json create mode 100644 laba4/.vscode/launch.json create mode 100644 laba4/.vscode/settings.json create mode 100644 laba4/build.gradle create mode 100644 laba4/gradle/wrapper/gradle-wrapper.jar create mode 100644 laba4/gradle/wrapper/gradle-wrapper.properties create mode 100644 laba4/gradlew create mode 100644 laba4/gradlew.bat create mode 100644 laba4/readme.md create mode 100644 laba4/settings.gradle create mode 100644 laba4/src/main/java/com/example/demo/DemoApplication.java create mode 100644 laba4/src/main/java/com/example/demo/core/api/GlobalController.java create mode 100644 laba4/src/main/java/com/example/demo/core/api/PageAttributesMapper.java create mode 100644 laba4/src/main/java/com/example/demo/core/configuration/Constants.java create mode 100644 laba4/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java create mode 100644 laba4/src/main/java/com/example/demo/core/error/AdviceController.java create mode 100644 laba4/src/main/java/com/example/demo/core/error/NotFoundException.java create mode 100644 laba4/src/main/java/com/example/demo/core/model/BaseEntity.java create mode 100644 laba4/src/main/java/com/example/demo/core/session/SessionCart.java create mode 100644 laba4/src/main/java/com/example/demo/core/session/SessionHelper.java create mode 100644 laba4/src/main/java/com/example/demo/films/model/FilmEntity.java create mode 100644 laba4/src/main/java/com/example/demo/orders/api/OrderDto.java create mode 100644 laba4/src/main/java/com/example/demo/orders/api/OrderGroupedDto.java create mode 100644 laba4/src/main/java/com/example/demo/orders/model/OrderEntity.java create mode 100644 laba4/src/main/java/com/example/demo/orders/model/OrderGrouped.java create mode 100644 laba4/src/main/java/com/example/demo/orders/repository/OrderRepository.java create mode 100644 laba4/src/main/java/com/example/demo/orders/service/OrderService.java create mode 100644 laba4/src/main/java/com/example/demo/subscriptions/api/SubscriptionController.java create mode 100644 laba4/src/main/java/com/example/demo/subscriptions/api/SubscriptionDto.java create mode 100644 laba4/src/main/java/com/example/demo/subscriptions/model/SubscriptionEntity.java create mode 100644 laba4/src/main/java/com/example/demo/subscriptions/repository/SubscriptionRepository.java create mode 100644 laba4/src/main/java/com/example/demo/subscriptions/service/SubscriptionService.java create mode 100644 laba4/src/main/java/com/example/demo/types/api/TypeController.java create mode 100644 laba4/src/main/java/com/example/demo/types/api/TypeDto.java create mode 100644 laba4/src/main/java/com/example/demo/types/model/TypeEntity.java create mode 100644 laba4/src/main/java/com/example/demo/types/repository/TypeRepository.java create mode 100644 laba4/src/main/java/com/example/demo/types/service/TypeService.java create mode 100644 laba4/src/main/java/com/example/demo/users/api/UserCartController.java create mode 100644 laba4/src/main/java/com/example/demo/users/api/UserCartDto.java create mode 100644 laba4/src/main/java/com/example/demo/users/api/UserController.java create mode 100644 laba4/src/main/java/com/example/demo/users/api/UserDto.java create mode 100644 laba4/src/main/java/com/example/demo/users/api/UserLoginController.java create mode 100644 laba4/src/main/java/com/example/demo/users/api/UserLoginDto.java create mode 100644 laba4/src/main/java/com/example/demo/users/api/UserProfileController.java create mode 100644 laba4/src/main/java/com/example/demo/users/api/UserProfileDto.java create mode 100644 laba4/src/main/java/com/example/demo/users/api/UserSubscriptionDto.java create mode 100644 laba4/src/main/java/com/example/demo/users/model/UserEntity.java create mode 100644 laba4/src/main/java/com/example/demo/users/model/UserSubscriptionWithStatus.java create mode 100644 laba4/src/main/java/com/example/demo/users/repository/UserRepository.java create mode 100644 laba4/src/main/java/com/example/demo/users/service/UserService.java create mode 100644 laba4/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionEntity.java create mode 100644 laba4/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionId.java create mode 100644 laba4/src/main/resources/application.properties create mode 100644 laba4/src/main/resources/public/css/style.css create mode 100644 laba4/src/main/resources/public/favicon.svg create mode 100644 laba4/src/main/resources/templates/cart.html create mode 100644 laba4/src/main/resources/templates/default.html create mode 100644 laba4/src/main/resources/templates/error.html create mode 100644 laba4/src/main/resources/templates/login.html create mode 100644 laba4/src/main/resources/templates/orders.html create mode 100644 laba4/src/main/resources/templates/pagination.html create mode 100644 laba4/src/main/resources/templates/profile.html create mode 100644 laba4/src/main/resources/templates/subscription-edit.html create mode 100644 laba4/src/main/resources/templates/subscription.html create mode 100644 laba4/src/main/resources/templates/type-edit.html create mode 100644 laba4/src/main/resources/templates/type.html create mode 100644 laba4/src/main/resources/templates/user-edit.html create mode 100644 laba4/src/main/resources/templates/user.html create mode 100644 laba4/src/test/java/com/example/demo/TypeServiceTests.java create mode 100644 laba4/src/test/java/com/example/demo/UserOrderServiceTest.java create mode 100644 laba4/src/test/resources/application.properties diff --git a/Laba1/.vscode/launch.json b/Laba1/.vscode/launch.json new file mode 100644 index 0000000..edb9da4 --- /dev/null +++ b/Laba1/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "configurations": [ + { + "type": "java", + "name": "Spring Boot-DemoApplication", + "request": "launch", + "cwd": "${workspaceFolder}", + "mainClass": "com.example.demo.DemoApplication", + "projectName": "demo", + "args": "", + "envFile": "${workspaceFolder}/.env" + } + ] +} \ No newline at end of file diff --git a/Laba1/demo/.gitignore b/Laba1/demo/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/Laba1/demo/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/Laba1/demo/build.gradle b/Laba1/demo/build.gradle new file mode 100644 index 0000000..e30a834 --- /dev/null +++ b/Laba1/demo/build.gradle @@ -0,0 +1,27 @@ +plugins { +id 'java' +id 'org.springframework.boot' version '3.2.2' +id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +java { +sourceCompatibility = '17' +} + +repositories { +mavenCentral() +} + +dependencies { +implementation 'org.springframework.boot:spring-boot-starter-web' +implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + +testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { +useJUnitPlatform() +} diff --git a/Laba1/demo/gradle/wrapper/gradle-wrapper.jar b/Laba1/demo/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/Laba1/demo/gradle/wrapper/gradle-wrapper.properties b/Laba1/demo/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/Laba1/demo/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Laba1/demo/gradlew b/Laba1/demo/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/Laba1/demo/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Laba1/demo/gradlew.bat b/Laba1/demo/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/Laba1/demo/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Laba1/demo/index.html b/Laba1/demo/index.html new file mode 100644 index 0000000..55419a6 --- /dev/null +++ b/Laba1/demo/index.html @@ -0,0 +1,61 @@ + + + + + + Document + + +

Push the button

+ +
+ +
+ +
+ +
+ + + + \ No newline at end of file diff --git a/Laba1/demo/settings.gradle b/Laba1/demo/settings.gradle new file mode 100644 index 0000000..0a383dd --- /dev/null +++ b/Laba1/demo/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'demo' diff --git a/Laba1/demo/src/main/java/com/example/demo/ApiController.java b/Laba1/demo/src/main/java/com/example/demo/ApiController.java new file mode 100644 index 0000000..7d6e175 --- /dev/null +++ b/Laba1/demo/src/main/java/com/example/demo/ApiController.java @@ -0,0 +1,56 @@ +package com.example.demo; + +import java.util.Date; +import java.util.List; +import java.util.ArrayList; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; + + + +@RestController +@RequestMapping("/user") +public class ApiController { + + private List Users = new ArrayList(); + + @PostMapping + public User CreateUser(@RequestBody User user) { + Users.add(user); + return user; + } + + @GetMapping + public List readAllUsers() { + return Users; + } + + @GetMapping("/{id}") + public User readUser(@PathVariable int id) { + return Users.get(id); + } + + @PutMapping("/{id}") + public User updateUser(@RequestBody User user, @PathVariable int id) { + Users.remove(id); + Users.add(id, user); + return user; + } + + @DeleteMapping("/{id}") + public User deleteUser(@PathVariable int id) { + User tmp = Users.get(id); + Users.remove(id); + return tmp; + } +} diff --git a/Laba1/demo/src/main/java/com/example/demo/DemoApplication.java b/Laba1/demo/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..64b538a --- /dev/null +++ b/Laba1/demo/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + +} diff --git a/Laba1/demo/src/main/java/com/example/demo/User.java b/Laba1/demo/src/main/java/com/example/demo/User.java new file mode 100644 index 0000000..3f5affc --- /dev/null +++ b/Laba1/demo/src/main/java/com/example/demo/User.java @@ -0,0 +1,42 @@ +package com.example.demo; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class User { + private String name; + private String phoneNumber; + private String mail; + private String year; + + public User() { + } + + @JsonCreator + public User( + @JsonProperty(value = "name") String name, + @JsonProperty(value = "phoneNumber") String phoneNumber, + @JsonProperty(value = "mail") String mail, + @JsonProperty(value = "year") String year) { + this.name = name; + this.phoneNumber = phoneNumber; + this.mail = mail; + this.year = year; + } + + public String getName() { + return name; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public String getMail() { + return mail; + } + + public String getYear() { + return year; + } +} diff --git a/Laba1/demo/src/main/java/com/example/demo/WebConfig.java b/Laba1/demo/src/main/java/com/example/demo/WebConfig.java new file mode 100644 index 0000000..d5585a1 --- /dev/null +++ b/Laba1/demo/src/main/java/com/example/demo/WebConfig.java @@ -0,0 +1,15 @@ +package com.example.demo; + +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.NonNull; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addCorsMappings(@NonNull CorsRegistry registry) { + registry.addMapping("/**") + .allowedMethods("GET", "POST", "PUT", "DELETE"); + } +} diff --git a/Laba1/demo/src/main/resources/application.properties b/Laba1/demo/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Laba1/demo/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/Laba1/demo/src/test/java/com/example/demo/DemoApplicationTests.java b/Laba1/demo/src/test/java/com/example/demo/DemoApplicationTests.java new file mode 100644 index 0000000..2778a6a --- /dev/null +++ b/Laba1/demo/src/test/java/com/example/demo/DemoApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DemoApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/Laba2/2.zip b/Laba2/2.zip new file mode 100644 index 0000000000000000000000000000000000000000..4ab5a3674c4a23a2044aea05ba2ac30d48c9088f GIT binary patch literal 265392 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
v)4Je26 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

)lL=kTd`KRvBC}P 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

p^6#*?SyG~b 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

ujD}+iw-;Vt~W#E)HXD9j_m(8i_ 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 literal 0 HcmV?d00001 diff --git a/Laba2/Лекция2-src/gradle/wrapper/gradle-wrapper.properties b/Laba2/Лекция2-src/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/Laba2/Лекция2-src/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Laba2/Лекция2-src/gradlew b/Laba2/Лекция2-src/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/Laba2/Лекция2-src/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Laba2/Лекция2-src/gradlew.bat b/Laba2/Лекция2-src/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/Laba2/Лекция2-src/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Laba2/Лекция2-src/package-lock.json b/Laba2/Лекция2-src/package-lock.json new file mode 100644 index 0000000..210e76c --- /dev/null +++ b/Laba2/Лекция2-src/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "Лекция2-src", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/Laba2/Лекция2-src/settings.gradle b/Laba2/Лекция2-src/settings.gradle new file mode 100644 index 0000000..0a383dd --- /dev/null +++ b/Laba2/Лекция2-src/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'demo' diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/DemoApplication.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..741175f --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,78 @@ +package com.example.demo; + +import java.util.Objects; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.service.SubscribeService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@SpringBootApplication +public class DemoApplication implements CommandLineRunner { + private final Logger log = LoggerFactory.getLogger(DemoApplication.class); + + private final FilmService filmService; + private final GenreService genreService; + private final SubscribeService subscribeService; + private final UserService userService; + + public DemoApplication(GenreService genreService, FilmService filmService, SubscribeService subscribeService, + UserService userService) { + this.genreService = genreService; + this.filmService = filmService; + this.subscribeService = subscribeService; + this.userService = userService; + } + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @Override + public void run(String... args) throws Exception { + if (args.length > 0 && Objects.equals("--populate", args[0])) { + + final List emptyList = new ArrayList<>(); + + log.info("Create default genres values"); + final var genre1 = genreService.create(new GenreEntity(null, "Drama")); + final var genre2 = genreService.create(new GenreEntity(null, "Comedy")); + final var genre3 = genreService.create(new GenreEntity(null, "Sport")); + final var genre4 = genreService.create(new GenreEntity(null, "Triller")); + + log.info("Create default subscribes values"); + final var subscribe1 = subscribeService.create(new SubscribeEntity(null, "Standart", 200d)); + final var subscribe2 = subscribeService.create(new SubscribeEntity(null, "Premium", 300d)); + + log.info("Create default films values"); + final var film1 = filmService.create(new FilmEntity(null, "film1", genre1, 200d, 20)); + final var film2 = filmService.create(new FilmEntity(null, "film2", genre2, 2000d, 40)); + final var film3 = filmService.create(new FilmEntity(null, "film3", genre3, 4800d, 30)); + final var film4 = filmService.create(new FilmEntity(null, "film4", genre4, 8800d, 21)); + final var film5 = filmService.create(new FilmEntity(null, "film5", genre1, 2700d, 10)); + + log.info("Create default users values"); + final var user1 = userService.create(new UserEntity(null, subscribe1, "user1", "mail", "phone", emptyList)); + final var user2 = userService.create(new UserEntity(null, subscribe2, "user1", "mail", "phone", emptyList)); + user1.addFilm(film1); + user1.addFilm(film2); + user1.addFilm(film3); + user1.addFilm(film4); + user1.addFilm(film5); + user2.addFilm(film2); + user2.addFilm(film5); + } + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/Constants.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/Constants.java new file mode 100644 index 0000000..d9c6b7c --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/Constants.java @@ -0,0 +1,8 @@ +package com.example.demo.core.configuration; + +public class Constants { + public static final String API_URL = "/api/1.0"; + + private Constants() { + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java new file mode 100644 index 0000000..a5ad6f3 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java @@ -0,0 +1,13 @@ +package com.example.demo.core.configuration; + +import org.modelmapper.ModelMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MapperConfiguration { + @Bean + ModelMapper modelMapper() { + return new ModelMapper(); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java new file mode 100644 index 0000000..762e85a --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java @@ -0,0 +1,15 @@ +package com.example.demo.core.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.NonNull; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfiguration implements WebMvcConfigurer { + @Override + public void addCorsMappings(@NonNull CorsRegistry registry) { + registry.addMapping("/**") + .allowedMethods("GET", "POST", "PUT", "DELETE"); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/core/error/NotFoundException.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/error/NotFoundException.java new file mode 100644 index 0000000..586af3c --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/error/NotFoundException.java @@ -0,0 +1,7 @@ +package com.example.demo.core.error; + +public class NotFoundException extends RuntimeException { + public NotFoundException(Long id) { + super(String.format("Entity with id [%s] is not found or not exists", id)); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/core/model/BaseEntity.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/model/BaseEntity.java new file mode 100644 index 0000000..674ddfb --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/model/BaseEntity.java @@ -0,0 +1,20 @@ +package com.example.demo.core.model; + +public abstract class BaseEntity { + protected Long id; + + protected BaseEntity() { + } + + protected BaseEntity(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/core/repository/CommonRepository.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/repository/CommonRepository.java new file mode 100644 index 0000000..85e1e6d --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/repository/CommonRepository.java @@ -0,0 +1,17 @@ +package com.example.demo.core.repository; + +import java.util.List; + +public interface CommonRepository { + List getAll(); + + E get(T id); + + E create(E entity); + + E update(E entity); + + E delete(E entity); + + void deleteAll(); +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/core/repository/MapRepository.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/repository/MapRepository.java new file mode 100644 index 0000000..6809ac2 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/core/repository/MapRepository.java @@ -0,0 +1,57 @@ +package com.example.demo.core.repository; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import com.example.demo.core.model.BaseEntity; + +public abstract class MapRepository implements CommonRepository { + private final Map entities = new TreeMap<>(); + private Long lastId = 0L; + + protected MapRepository() { + } + + @Override + public List getAll() { + return entities.values().stream().toList(); + } + + @Override + public E get(Long id) { + return entities.get(id); + } + + @Override + public E create(E entity) { + lastId++; + entity.setId(lastId); + entities.put(lastId, entity); + return entity; + } + + @Override + public E update(E entity) { + if (get(entity.getId()) == null) { + return null; + } + entities.put(entity.getId(), entity); + return entity; + } + + @Override + public E delete(E entity) { + if (get(entity.getId()) == null) { + return null; + } + entities.remove(entity.getId()); + return entity; + } + + @Override + public void deleteAll() { + lastId = 0L; + entities.clear(); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/films/api/FilmController.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/films/api/FilmController.java new file mode 100644 index 0000000..2a100b9 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/films/api/FilmController.java @@ -0,0 +1,70 @@ +package com.example.demo.films.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.genres.service.GenreService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/film") +public class FilmController { + private final FilmService filmService; + private final GenreService genreService; + private final ModelMapper modelMapper; + + public FilmController(FilmService filmService, GenreService genreService, ModelMapper modelMapper) { + this.filmService = filmService; + this.genreService = genreService; + this.modelMapper = modelMapper; + } + + private FilmDto toDto(FilmEntity entity) { + return modelMapper.map(entity, FilmDto.class); + } + + private FilmEntity toEntity(FilmDto dto) { + final FilmEntity entity = modelMapper.map(dto, FilmEntity.class); + entity.setGenre(genreService.get(dto.getGenreId())); + return entity; + } + + @GetMapping + public List getAll(@RequestParam(name = "genreId", defaultValue = "0") Long typeId) { + return filmService.getAll(typeId).stream().map(this::toDto).toList(); + } + + @GetMapping("/{id}") + public FilmDto get(@PathVariable(name = "id") Long id) { + return toDto(filmService.get(id)); + } + + @PostMapping + public FilmDto create(@RequestBody @Valid FilmDto dto) { + return toDto(filmService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public FilmDto update(@PathVariable(name = "id") Long id, @RequestBody FilmDto dto) { + return toDto(filmService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public FilmDto delete(@PathVariable(name = "id") Long id) { + return toDto(filmService.delete(id)); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/films/api/FilmDto.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/films/api/FilmDto.java new file mode 100644 index 0000000..fafcd95 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/films/api/FilmDto.java @@ -0,0 +1,59 @@ +package com.example.demo.films.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +public class FilmDto { + private Long id; + @NotNull + private String name; + @NotNull + @Min(1) + private Long genreId; + @NotNull + @Min(1) + private Double price; + @NotNull + @Min(1) + private Integer discount; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getGenreId() { + return genreId; + } + + public void setGenreId(Long genreId) { + this.genreId = genreId; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getDiscount() { + return discount; + } + + public void setDiscount(Integer discount) { + this.discount = discount; + } + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Double getSum() { + return price - ((price * discount) * 0.01); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/films/model/FilmEntity.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/films/model/FilmEntity.java new file mode 100644 index 0000000..e40b14a --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/films/model/FilmEntity.java @@ -0,0 +1,76 @@ +package com.example.demo.films.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.genres.model.GenreEntity; + +public class FilmEntity extends BaseEntity { + private String name; + private GenreEntity genre; + private Double price; + private Integer discount; + + public FilmEntity() { + super(); + } + + public FilmEntity(Long id, String name, GenreEntity genre, Double price, Integer discount) { + super(id); + this.name = name; + this.genre = genre; + this.price = price; + this.discount = discount; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public GenreEntity getGenre() { + return genre; + } + + public void setGenre(GenreEntity genre) { + this.genre = genre; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getDiscount() { + return discount; + } + + public void setDiscount(Integer discount) { + this.discount = discount; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, genre, price, discount); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final FilmEntity other = (FilmEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getName(), name) + && Objects.equals(other.getGenre(), genre) + && Objects.equals(other.getPrice(), price) + && Objects.equals(other.getDiscount(), discount); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/films/repository/FilmRepository.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/films/repository/FilmRepository.java new file mode 100644 index 0000000..ff69db5 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/films/repository/FilmRepository.java @@ -0,0 +1,10 @@ +package com.example.demo.films.repository; + +import org.springframework.stereotype.Repository; + +import com.example.demo.core.repository.MapRepository; +import com.example.demo.films.model.FilmEntity; + +@Repository +public class FilmRepository extends MapRepository { +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/films/service/FilmService.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/films/service/FilmService.java new file mode 100644 index 0000000..f36ea31 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/films/service/FilmService.java @@ -0,0 +1,56 @@ +package com.example.demo.films.service; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.repository.FilmRepository; + +@Service +public class FilmService { + private final FilmRepository repository; + + public FilmService(FilmRepository repository) { + this.repository = repository; + } + + public List getAll() { + return repository.getAll(); + } + + public List getAll(Long typeId) { + if (Objects.equals(typeId, 0L)) { + return repository.getAll(); + } + return repository.getAll().stream() + .filter(film -> film.getGenre().getId().equals(typeId)) + .toList(); + } + + public FilmEntity get(Long id) { + return Optional.ofNullable(repository.get(id)) + .orElseThrow(() -> new NotFoundException(id)); + } + + public FilmEntity create(FilmEntity entity) { + return repository.create(entity); + } + + public FilmEntity update(Long id, FilmEntity entity) { + final FilmEntity existsEntity = get(id); + existsEntity.setName(entity.getName()); + existsEntity.setGenre(entity.getGenre()); + existsEntity.setPrice(entity.getPrice()); + existsEntity.setDiscount(entity.getDiscount()); + return repository.update(existsEntity); + } + + public FilmEntity delete(Long id) { + final FilmEntity existsEntity = get(id); + return repository.delete(existsEntity); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/api/GenreController.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/api/GenreController.java new file mode 100644 index 0000000..2b3d9d8 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/api/GenreController.java @@ -0,0 +1,65 @@ +package com.example.demo.genres.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/genre") +public class GenreController { + private final GenreService genreService; + private final ModelMapper modelMapper; + + public GenreController(GenreService genreService, ModelMapper modelMapper) { + this.genreService = genreService; + this.modelMapper = modelMapper; + } + + private GenreDto toDto(GenreEntity entity) { + return modelMapper.map(entity, GenreDto.class); + } + + private GenreEntity toEntity(GenreDto dto) { + final GenreEntity entity = modelMapper.map(dto, GenreEntity.class); + return entity; + } + + @GetMapping + public List getAll() { + return genreService.getAll().stream().map(this::toDto).toList(); + } + + @GetMapping("/{id}") + public GenreDto get(@PathVariable(name = "id") Long id) { + return toDto(genreService.get(id)); + } + + @PostMapping + public GenreDto create(@RequestBody @Valid GenreDto dto) { + return toDto(genreService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public GenreDto update(@PathVariable(name = "id") Long id, @RequestBody GenreDto dto) { + return toDto(genreService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public GenreDto delete(@PathVariable(name = "id") Long id) { + return toDto(genreService.delete(id)); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/api/GenreDto.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/api/GenreDto.java new file mode 100644 index 0000000..b456060 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/api/GenreDto.java @@ -0,0 +1,28 @@ +package com.example.demo.genres.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.NotNull; + +public class GenreDto { + private Long id; + @NotNull + private String name; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/model/GenreEntity.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/model/GenreEntity.java new file mode 100644 index 0000000..28b3737 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/model/GenreEntity.java @@ -0,0 +1,42 @@ +package com.example.demo.genres.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; + +public class GenreEntity extends BaseEntity { + private String name; + + public GenreEntity() { + super(); + } + + public GenreEntity(Long id, String name) { + super(id); + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final GenreEntity other = (GenreEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getName(), name); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java new file mode 100644 index 0000000..ad92c2a --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java @@ -0,0 +1,10 @@ +package com.example.demo.genres.repository; + +import org.springframework.stereotype.Repository; + +import com.example.demo.core.repository.MapRepository; +import com.example.demo.genres.model.GenreEntity; + +@Repository +public class GenreRepository extends MapRepository { +} \ No newline at end of file diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/service/GenreService.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/service/GenreService.java new file mode 100644 index 0000000..0510d45 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/genres/service/GenreService.java @@ -0,0 +1,42 @@ +package com.example.demo.genres.service; + +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.repository.GenreRepository; + +@Service +public class GenreService { + private final GenreRepository repository; + + public GenreService(GenreRepository repository) { + this.repository = repository; + } + + public List getAll() { + return repository.getAll(); + } + + public GenreEntity get(Long id) { + return Optional.ofNullable(repository.get(id)).orElseThrow(() -> new NotFoundException(id)); + } + + public GenreEntity create(GenreEntity entity) { + return repository.create(entity); + } + + public GenreEntity update(Long id, GenreEntity entity) { + final GenreEntity existsEntity = get(id); + existsEntity.setName(entity.getName()); + return repository.update(existsEntity); + } + + public GenreEntity delete(Long id) { + final GenreEntity existsEntity = get(id); + return repository.delete(existsEntity); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/speaker/configuration/SpeakerConfiguration.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/speaker/configuration/SpeakerConfiguration.java new file mode 100644 index 0000000..a34db6b --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/speaker/configuration/SpeakerConfiguration.java @@ -0,0 +1,5 @@ +package com.example.demo.speaker.configuration; + +public class SpeakerConfiguration { + +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java new file mode 100644 index 0000000..d5ba202 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java @@ -0,0 +1,64 @@ +package com.example.demo.subscribes.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.service.SubscribeService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/subscribe") +public class SubscribeController { + private final SubscribeService subscribeService; + private final ModelMapper modelMapper; + + public SubscribeController(SubscribeService subscribeService, ModelMapper modelMapper) { + this.subscribeService = subscribeService; + this.modelMapper = modelMapper; + } + + private SubscribeDto toDto(SubscribeEntity entity) { + return modelMapper.map(entity, SubscribeDto.class); + } + + private SubscribeEntity toEntity(SubscribeDto dto) { + return modelMapper.map(dto, SubscribeEntity.class); + } + + @GetMapping + public List getAll() { + return subscribeService.getAll().stream().map(this::toDto).toList(); + } + + @GetMapping("/{id}") + public SubscribeDto get(@PathVariable(name = "id") Long id) { + return toDto(subscribeService.get(id)); + } + + @PostMapping + public SubscribeDto create(@RequestBody @Valid SubscribeDto dto) { + return toDto(subscribeService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public SubscribeDto update(@PathVariable(name = "id") Long id, @RequestBody SubscribeDto dto) { + return toDto(subscribeService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public SubscribeDto delete(@PathVariable(name = "id") Long id) { + return toDto(subscribeService.delete(id)); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java new file mode 100644 index 0000000..be42ce5 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java @@ -0,0 +1,41 @@ +package com.example.demo.subscribes.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Min; + +public class SubscribeDto { + private Long id; + @NotBlank + private String name; + @NotNull + @Min(1) + private Double cost; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Double getCost() { + return cost; + } + + public void setCost(Double cost) { + this.cost = cost; + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java new file mode 100644 index 0000000..6dfe493 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java @@ -0,0 +1,54 @@ +package com.example.demo.subscribes.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; + +public class SubscribeEntity extends BaseEntity { + private String name; + private Double cost; + + public SubscribeEntity() { + super(); + } + + public SubscribeEntity(Long id, String name, Double cost) { + super(id); + this.name = name; + this.cost = cost; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Double getCost() { + return cost; + } + + public void setCost(Double cost) { + this.cost = cost; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, cost); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final SubscribeEntity other = (SubscribeEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getName(), name) + && Objects.equals(other.getCost(), cost); + } + +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java new file mode 100644 index 0000000..a68d7d3 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java @@ -0,0 +1,10 @@ +package com.example.demo.subscribes.repository; + +import org.springframework.stereotype.Repository; + +import com.example.demo.core.repository.MapRepository; +import com.example.demo.subscribes.model.SubscribeEntity; + +@Repository +public class SubscribeRepository extends MapRepository { +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java new file mode 100644 index 0000000..0941af0 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java @@ -0,0 +1,44 @@ +package com.example.demo.subscribes.service; + +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.repository.SubscribeRepository; + +@Service +public class SubscribeService { + private final SubscribeRepository repository; + + public SubscribeService(SubscribeRepository repository) { + this.repository = repository; + } + + public List getAll() { + return repository.getAll(); + } + + public SubscribeEntity get(Long id) { + return Optional.ofNullable(repository.get(id)) + .orElseThrow(() -> new NotFoundException(id)); + } + + public SubscribeEntity create(SubscribeEntity entity) { + return repository.create(entity); + } + + public SubscribeEntity update(Long id, SubscribeEntity entity) { + final SubscribeEntity existsEntity = get(id); + existsEntity.setName(entity.getName()); + existsEntity.setCost(entity.getCost()); + return repository.update(existsEntity); + } + + public SubscribeEntity delete(Long id) { + final SubscribeEntity existsEntity = get(id); + return repository.delete(existsEntity); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/users/api/UserController.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/users/api/UserController.java new file mode 100644 index 0000000..d59cd74 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/users/api/UserController.java @@ -0,0 +1,85 @@ +package com.example.demo.users.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; +import com.example.demo.subscribes.service.SubscribeService; + +import com.example.demo.films.service.FilmService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/user") +public class UserController { + private final UserService userService; + private final SubscribeService subscribeService; + private final ModelMapper modelMapper; + private final FilmService filmService; + + public UserController(UserService userService, SubscribeService subscribeService, ModelMapper modelMapper, + FilmService filmService) { + this.userService = userService; + this.subscribeService = subscribeService; + this.modelMapper = modelMapper; + this.filmService = filmService; + } + + private UserDto toDto(UserEntity entity) { + return modelMapper.map(entity, UserDto.class); + } + + private UserEntity toEntity(UserDto dto) { + final UserEntity entity = modelMapper.map(dto, UserEntity.class); + entity.setSubscribe(subscribeService.get(dto.getSubscribeId())); + return entity; + } + + @GetMapping + public List getAll(@RequestParam(name = "subscribeId", defaultValue = "0") Long subscribeId) { + return userService.getAll(subscribeId).stream().map(this::toDto).toList(); + } + + @GetMapping("/{id}") + public UserDto get(@PathVariable(name = "id") Long id) { + return toDto(userService.get(id)); + } + + @PostMapping + public UserDto create(@RequestBody @Valid UserDto dto) { + return toDto(userService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public UserDto update(@PathVariable(name = "id") Long id, @RequestBody UserDto dto) { + return toDto(userService.update(id, toEntity(dto))); + } + + @PutMapping("/addFilm/{id}") + public UserDto addFilm(@PathVariable(name = "id") Long id, @RequestBody Long filmId) { + return toDto(userService.addFilm(id, filmService.get(filmId))); + } + + @PutMapping("/deleteFilm/{id}") + public UserDto deleteFilm(@PathVariable(name = "id") Long id, @RequestBody Long filmId) { + return toDto(userService.deleteFilm(id, filmService.get(filmId))); + } + + @DeleteMapping("/{id}") + public UserDto delete(@PathVariable(name = "id") Long id) { + return toDto(userService.delete(id)); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/users/api/UserDto.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/users/api/UserDto.java new file mode 100644 index 0000000..cb74cf6 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/users/api/UserDto.java @@ -0,0 +1,84 @@ +package com.example.demo.users.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.NotBlank; +import com.example.demo.films.model.FilmEntity; + +public class UserDto { + private Long id; + @NotNull + @Min(1) + private Long subscribeId; + @NotBlank + private String name; + @NotBlank + private String mail; + @NotBlank + private String phone; + + private final List listFilms = new ArrayList(); + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getSubscribeId() { + return subscribeId; + } + + public void setSubscribeId(Long subscribeId) { + this.subscribeId = subscribeId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public List getListFilms() { + return listFilms; + } + + public void setListFilms(List listFilms) { + this.listFilms.clear(); + this.listFilms.addAll(listFilms); + } + + public void addFilm(FilmEntity film) { + this.listFilms.add(film); + } + + public void deletefilm(FilmEntity film) { + this.listFilms.remove(film); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/users/model/UserEntity.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/users/model/UserEntity.java new file mode 100644 index 0000000..0384400 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/users/model/UserEntity.java @@ -0,0 +1,102 @@ +package com.example.demo.users.model; + +import java.util.Objects; +import java.util.ArrayList; +import java.util.List; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.films.model.FilmEntity; + +public class UserEntity extends BaseEntity { + private SubscribeEntity subscribe; + private String name; + private String mail; + private String phone; + private final List listFilms = new ArrayList(); + + public UserEntity() { + super(); + } + + public UserEntity(Long id, SubscribeEntity subscribe, String name, String mail, String phone, + List listFims) { + super(id); + this.subscribe = subscribe; + this.name = name; + this.mail = mail; + this.phone = phone; + if (!listFims.isEmpty()) { + this.listFilms.clear(); + this.listFilms.addAll(listFims); + } + } + + public SubscribeEntity getSubscribe() { + return subscribe; + } + + public void setSubscribe(SubscribeEntity subscribe) { + this.subscribe = subscribe; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public List getListFilms() { + return listFilms; + } + + public void setListFilms(List listFilms) { + this.listFilms.addAll(listFilms); + } + + public void addFilm(FilmEntity film) { + this.listFilms.add(film); + } + + public void deleteFilm(FilmEntity film) { + this.listFilms.remove(film); + } + + @Override + public int hashCode() { + return Objects.hash(id, subscribe, name, mail, phone, listFilms); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final UserEntity other = (UserEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getSubscribe(), subscribe) + && Objects.equals(other.getName(), name) + && Objects.equals(other.getMail(), mail) + && Objects.equals(other.getPhone(), phone) + && Objects.equals(other.getListFilms(), listFilms); + } +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/users/repository/UserRepository.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/users/repository/UserRepository.java new file mode 100644 index 0000000..fa4b654 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/users/repository/UserRepository.java @@ -0,0 +1,10 @@ +package com.example.demo.users.repository; + +import org.springframework.stereotype.Repository; + +import com.example.demo.core.repository.MapRepository; +import com.example.demo.users.model.UserEntity; + +@Repository +public class UserRepository extends MapRepository { +} diff --git a/Laba2/Лекция2-src/src/main/java/com/example/demo/users/service/UserService.java b/Laba2/Лекция2-src/src/main/java/com/example/demo/users/service/UserService.java new file mode 100644 index 0000000..17ecd10 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/java/com/example/demo/users/service/UserService.java @@ -0,0 +1,71 @@ +package com.example.demo.users.service; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.users.model.UserEntity; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.users.repository.UserRepository; + +@Service +public class UserService { + private final UserRepository repository; + + public UserService(UserRepository repository) { + this.repository = repository; + } + + public List getAll(Long subscribeId) { + if (Objects.equals(subscribeId, 0L)) { + return repository.getAll(); + } + return repository.getAll().stream() + .filter(user -> user.getSubscribe().getId().equals(subscribeId)) + .toList(); + } + + public List getAll() { + return repository.getAll(); + } + + public UserEntity get(Long id) { + return Optional.ofNullable(repository.get(id)) + .orElseThrow(() -> new NotFoundException(id)); + } + + public UserEntity create(UserEntity entity) { + return repository.create(entity); + } + + public UserEntity update(Long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + existsEntity.setSubscribe(entity.getSubscribe()); + existsEntity.setName(entity.getName()); + existsEntity.setMail(entity.getMail()); + existsEntity.setPhone(entity.getPhone()); + return repository.update(existsEntity); + } + + public UserEntity delete(Long id) { + final UserEntity existsEntity = get(id); + return repository.delete(existsEntity); + } + + public UserEntity addFilm(Long id, FilmEntity film) { + final UserEntity existsEntity = get(id); + if (!existsEntity.getListFilms().contains(film)) + existsEntity.addFilm(film); + return repository.update(existsEntity); + } + + public UserEntity deleteFilm(Long id, FilmEntity film) { + final UserEntity existsEntity = get(id); + if (existsEntity.getListFilms().contains(film)) + existsEntity.deleteFilm(film); + return repository.update(existsEntity); + } +} diff --git a/Laba2/Лекция2-src/src/main/resources/application.properties b/Laba2/Лекция2-src/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Laba2/Лекция2-src/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/Laba2/Лекция2-src/src/test/java/com/example/demo/FilmsServiceTests.java b/Laba2/Лекция2-src/src/test/java/com/example/demo/FilmsServiceTests.java new file mode 100644 index 0000000..35a96a8 --- /dev/null +++ b/Laba2/Лекция2-src/src/test/java/com/example/demo/FilmsServiceTests.java @@ -0,0 +1,79 @@ +package com.example.demo; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.films.service.FilmService; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.genres.model.GenreEntity; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +public class FilmsServiceTests { + @Autowired + private FilmService filmService; + + @Test + void getFilmTest() { + Assertions.assertThrows(NotFoundException.class, () -> filmService.get(0L)); + } + + @Test + @Order(1) + void createFilmTest() { + final GenreEntity genre = new GenreEntity(null, "genre"); + + filmService.create(new FilmEntity(null, "name", genre, 100d, 20)); + filmService.create(new FilmEntity(null, "name", genre, 100d, 20)); + final FilmEntity last = filmService.create(new FilmEntity(null, "name", genre, 100d, 20)); + Assertions.assertEquals(3, filmService.getAll().size()); + Assertions.assertEquals(last, filmService.get(3L)); + } + + @Test + @Order(2) + void updateFilmTest() { + final String testName = "TEST"; + final GenreEntity testGenre = new GenreEntity(null, "test"); + final Double testPrice = 123d; + final int testDiscount = 12; + final FilmEntity entity = filmService.get(3L); + final String oldName = entity.getName(); + final GenreEntity oldGenre = entity.getGenre(); + final Double oldPrice = entity.getPrice(); + final int oldDiscount = entity.getDiscount(); + final FilmEntity newEntity = filmService.update(3L, + new FilmEntity(3L, testName, testGenre, testPrice, testDiscount)); + Assertions.assertEquals(3, filmService.getAll().size()); + Assertions.assertEquals(newEntity, filmService.get(3L)); + Assertions.assertEquals(testName, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + Assertions.assertEquals(testGenre, newEntity.getGenre()); + Assertions.assertNotEquals(oldGenre, newEntity.getGenre()); + Assertions.assertEquals(testPrice, newEntity.getPrice()); + Assertions.assertNotEquals(oldPrice, newEntity.getPrice()); + Assertions.assertEquals(testDiscount, newEntity.getDiscount()); + Assertions.assertNotEquals(oldDiscount, newEntity.getDiscount()); + } + + @Test + @Order(3) + void deleteFilmTest() { + final GenreEntity genre = new GenreEntity(null, "genre"); + + filmService.delete(3L); + Assertions.assertEquals(2, filmService.getAll().size()); + final FilmEntity last = filmService.get(2L); + Assertions.assertEquals(2L, last.getId()); + + final FilmEntity newEntity = filmService.create(new FilmEntity(null, "film", genre, 500d, 10)); + Assertions.assertEquals(3, filmService.getAll().size()); + Assertions.assertEquals(4L, newEntity.getId()); + } +} diff --git a/Laba2/Лекция2-src/src/test/java/com/example/demo/GenreServiceTests.java b/Laba2/Лекция2-src/src/test/java/com/example/demo/GenreServiceTests.java new file mode 100644 index 0000000..5b589a6 --- /dev/null +++ b/Laba2/Лекция2-src/src/test/java/com/example/demo/GenreServiceTests.java @@ -0,0 +1,61 @@ +package com.example.demo; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class GenreServiceTests { + @Autowired + private GenreService genreService; + + @Test + void getGenreTest() { + Assertions.assertThrows(NotFoundException.class, () -> genreService.get(0L)); + } + + @Test + @Order(1) + void createGenreTest() { + genreService.create(new GenreEntity(null, "Drama")); + genreService.create(new GenreEntity(null, "Comedy")); + final GenreEntity last = genreService.create(new GenreEntity(null, "Triller")); + Assertions.assertEquals(3, genreService.getAll().size()); + Assertions.assertEquals(last, genreService.get(3L)); + } + + @Test + @Order(2) + void updateGenreTest() { + final String test = "TEST"; + final GenreEntity entity = genreService.get(3L); + final String oldName = entity.getName(); + final GenreEntity newEntity = genreService.update(3L, new GenreEntity(1L, test)); + Assertions.assertEquals(3, genreService.getAll().size()); + Assertions.assertEquals(newEntity, genreService.get(3L)); + Assertions.assertEquals(test, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + } + + @Test + @Order(3) + void deleteGenreTest() { + genreService.delete(3L); + Assertions.assertEquals(2, genreService.getAll().size()); + final GenreEntity last = genreService.get(2L); + Assertions.assertEquals(2L, last.getId()); + + final GenreEntity newEntity = genreService.create(new GenreEntity(null, "Drama")); + Assertions.assertEquals(3, genreService.getAll().size()); + Assertions.assertEquals(4L, newEntity.getId()); + } +} diff --git a/Laba2/Лекция2-src/src/test/java/com/example/demo/SubscribeServiceTests.java b/Laba2/Лекция2-src/src/test/java/com/example/demo/SubscribeServiceTests.java new file mode 100644 index 0000000..1ea30b8 --- /dev/null +++ b/Laba2/Лекция2-src/src/test/java/com/example/demo/SubscribeServiceTests.java @@ -0,0 +1,65 @@ +package com.example.demo; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.subscribes.service.SubscribeService; +import com.example.demo.subscribes.model.SubscribeEntity; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +public class SubscribeServiceTests { + @Autowired + private SubscribeService subscribeService; + + @Test + void getSubscribeTest() { + Assertions.assertThrows(NotFoundException.class, () -> subscribeService.get(0L)); + } + + @Test + @Order(1) + void createSubscribeTest() { + subscribeService.create(new SubscribeEntity(null, "sub1", 100d)); + subscribeService.create(new SubscribeEntity(null, "sub2", 200d)); + final SubscribeEntity last = subscribeService.create(new SubscribeEntity(null, "sub3", 300d)); + Assertions.assertEquals(3, subscribeService.getAll().size()); + Assertions.assertEquals(last, subscribeService.get(3L)); + } + + @Test + @Order(2) + void updateSubscribeTest() { + final String testName = "TEST"; + final Double testPrice = 123d; + final SubscribeEntity entity = subscribeService.get(3L); + final String oldName = entity.getName(); + final Double oldPrice = entity.getCost(); + final SubscribeEntity newEntity = subscribeService.update(3L, new SubscribeEntity(3L, testName, testPrice)); + Assertions.assertEquals(3, subscribeService.getAll().size()); + Assertions.assertEquals(newEntity, subscribeService.get(3L)); + Assertions.assertEquals(testName, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + Assertions.assertEquals(testPrice, newEntity.getCost()); + Assertions.assertNotEquals(oldPrice, newEntity.getCost()); + } + + @Test + @Order(3) + void deleteSubscribeTest() { + subscribeService.delete(3L); + Assertions.assertEquals(2, subscribeService.getAll().size()); + final SubscribeEntity last = subscribeService.get(2L); + Assertions.assertEquals(2L, last.getId()); + + final SubscribeEntity newEntity = subscribeService.create(new SubscribeEntity(null, "sub8", 500d)); + Assertions.assertEquals(3, subscribeService.getAll().size()); + Assertions.assertEquals(4L, newEntity.getId()); + } +} diff --git a/Laba2/Лекция2-src/src/test/java/com/example/demo/UsersServiceTests.java b/Laba2/Лекция2-src/src/test/java/com/example/demo/UsersServiceTests.java new file mode 100644 index 0000000..a46c8df --- /dev/null +++ b/Laba2/Лекция2-src/src/test/java/com/example/demo/UsersServiceTests.java @@ -0,0 +1,110 @@ +package com.example.demo; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import java.util.ArrayList; +import java.util.List; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.subscribes.model.SubscribeEntity; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class UsersServiceTests { + @Autowired + private UserService userService; + + final List emptyList = new ArrayList(); + final GenreEntity genre = new GenreEntity(null, "genre"); + final FilmEntity film = new FilmEntity(null, "name", genre, 100d, 10); + + @Test + void getUserTest() { + Assertions.assertThrows(NotFoundException.class, () -> userService.get(0L)); + } + + @Test + @Order(1) + void createUserTest() { + final SubscribeEntity sub = new SubscribeEntity(null, "sub", 100d); + + userService.create(new UserEntity(null, sub, "name", "mail", "phone", emptyList)); + userService.create(new UserEntity(null, sub, "name", "mail", "phone", emptyList)); + final UserEntity last = userService.create(new UserEntity(null, sub, "name", "mail", "phone", emptyList)); + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertEquals(last, userService.get(3L)); + } + + @Test + @Order(2) + void updateUserTest() { + final SubscribeEntity testSub = new SubscribeEntity(null, "test", 100d); + final String testName = "TEST"; + final String testMail = "TEST"; + final String testPhone = "TEST"; + final UserEntity entity = userService.get(3L); + final SubscribeEntity oldSub = entity.getSubscribe(); + final String oldName = entity.getName(); + final String oldMail = entity.getMail(); + final String oldPhone = entity.getPhone(); + final UserEntity newEntity = userService.update(3L, + new UserEntity(3L, testSub, testName, testMail, testPhone, emptyList)); + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertEquals(newEntity, userService.get(3L)); + Assertions.assertEquals(testSub, newEntity.getSubscribe()); + Assertions.assertNotEquals(oldSub, newEntity.getSubscribe()); + Assertions.assertEquals(testName, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + Assertions.assertEquals(testMail, newEntity.getMail()); + Assertions.assertNotEquals(oldMail, newEntity.getMail()); + Assertions.assertEquals(testPhone, newEntity.getPhone()); + Assertions.assertNotEquals(oldPhone, newEntity.getPhone()); + } + + @Test + @Order(3) + void deleteUserTest() { + final SubscribeEntity sub = new SubscribeEntity(null, "sub", 100d); + + userService.delete(3L); + Assertions.assertEquals(2, userService.getAll().size()); + final UserEntity last = userService.get(2L); + Assertions.assertEquals(2L, last.getId()); + + final UserEntity newEntity = userService.create(new UserEntity(null, sub, "name", "mail", "phone", emptyList)); + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertEquals(4L, newEntity.getId()); + } + + @Test + @Order(4) + void addFilmTest() { + + userService.addFilm(4L, film); + userService.addFilm(4L, film); + + Assertions.assertEquals(true, userService.get(4L).getListFilms().contains(film)); + Assertions.assertEquals(1, userService.get(4L).getListFilms().size()); + } + + @Test + @Order(5) + void deleteFilmTest() { + Assertions.assertEquals(true, userService.get(4L).getListFilms().contains(film)); + Assertions.assertEquals(1, userService.get(4L).getListFilms().size()); + + userService.deleteFilm(4L, film); + + Assertions.assertEquals(false, userService.get(4L).getListFilms().contains(film)); + Assertions.assertEquals(0, userService.get(4L).getListFilms().size()); + } +} \ No newline at end of file diff --git a/Laba3/Лекция3-src/.gitignore b/Laba3/Лекция3-src/.gitignore new file mode 100644 index 0000000..546ecee --- /dev/null +++ b/Laba3/Лекция3-src/.gitignore @@ -0,0 +1,36 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +data.*.db \ No newline at end of file diff --git a/Laba3/Лекция3-src/.vscode/extensions.json b/Laba3/Лекция3-src/.vscode/extensions.json new file mode 100644 index 0000000..42cf79d --- /dev/null +++ b/Laba3/Лекция3-src/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + // fronted + "AndersEAndersen.html-class-suggestions", + "dbaeumer.vscode-eslint", + // backend + "vscjava.vscode-java-pack", + "vmware.vscode-boot-dev-pack", + "vscjava.vscode-gradle", + "redhat.vscode-xml" + ] +} \ No newline at end of file diff --git a/Laba3/Лекция3-src/.vscode/launch.json b/Laba3/Лекция3-src/.vscode/launch.json new file mode 100644 index 0000000..60a4fe1 --- /dev/null +++ b/Laba3/Лекция3-src/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "configurations": [ + { + "type": "java", + "name": "Demo", + "request": "launch", + "cwd": "${workspaceFolder}", + "mainClass": "com.example.demo.DemoApplication", + "projectName": "lec3", + "args": "--populate", + "envFile": "${workspaceFolder}/.env" + }, + { + "type": "java", + "name": "Spring Boot-DemoApplication<Лекция3-src>", + "request": "launch", + "cwd": "${workspaceFolder}", + "mainClass": "com.example.demo.DemoApplication", + "projectName": "Лекция3-src", + "args": "", + "envFile": "${workspaceFolder}/.env" + } + ] +} \ No newline at end of file diff --git a/Laba3/Лекция3-src/.vscode/settings.json b/Laba3/Лекция3-src/.vscode/settings.json new file mode 100644 index 0000000..eab3db6 --- /dev/null +++ b/Laba3/Лекция3-src/.vscode/settings.json @@ -0,0 +1,21 @@ +{ + "editor.tabSize": 4, + "editor.detectIndentation": false, + "editor.insertSpaces": true, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.formatOnType": false, + "java.compile.nullAnalysis.mode": "disabled", + "java.configuration.updateBuildConfiguration": "automatic", + "[java]": { + "editor.pasteAs.enabled": false, + }, + "gradle.nestedProjects": true, + "java.saveActions.organizeImports": true, + "java.dependency.packagePresentation": "hierarchical", + "spring-boot.ls.problem.boot2.JAVA_CONSTRUCTOR_PARAMETER_INJECTION": "WARNING", + "spring.initializr.defaultLanguage": "Java", + "java.format.settings.url": ".vscode/eclipse-formatter.xml", + "java.project.explorer.showNonJavaResources": true, + "java.codeGeneration.hashCodeEquals.useJava7Objects": true, +} \ No newline at end of file diff --git a/Laba3/Лекция3-src/build.gradle b/Laba3/Лекция3-src/build.gradle new file mode 100644 index 0000000..1f5a14c --- /dev/null +++ b/Laba3/Лекция3-src/build.gradle @@ -0,0 +1,43 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.4' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +defaultTasks 'bootRun' + +jar { + enabled = false +} + +bootJar { + archiveFileName = String.format('%s-%s.jar', rootProject.name, version) +} + +assert System.properties['java.specification.version'] == '17' || '21' +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + implementation 'org.modelmapper:modelmapper:3.2.0' + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'com.h2database:h2:2.2.224' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/Laba3/Лекция3-src/gradle/wrapper/gradle-wrapper.jar b/Laba3/Лекция3-src/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/Laba3/Лекция3-src/gradle/wrapper/gradle-wrapper.properties b/Laba3/Лекция3-src/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/Laba3/Лекция3-src/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Laba3/Лекция3-src/gradlew b/Laba3/Лекция3-src/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/Laba3/Лекция3-src/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Laba3/Лекция3-src/gradlew.bat b/Laba3/Лекция3-src/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/Laba3/Лекция3-src/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Laba3/Лекция3-src/readme.md b/Laba3/Лекция3-src/readme.md new file mode 100644 index 0000000..6eea3e5 --- /dev/null +++ b/Laba3/Лекция3-src/readme.md @@ -0,0 +1,18 @@ +Swagger UI: \ +http://localhost:8080/swagger-ui/index.html + +H2 Console: \ +http://localhost:8080/h2-console + +JDBC URL: jdbc:h2:file:./data \ +User Name: sa \ +Password: password + +Почитать: + +- Односторонние и двусторонние связи https://www.baeldung.com/jpa-hibernate-associations +- Getters и Setters для двусторонних связей https://en.wikibooks.org/wiki/Java_Persistence/OneToMany#Getters_and_Setters +- Многие-ко-многим с доп. атрибутами https://thorben-janssen.com/hibernate-tip-many-to-many-association-with-additional-attributes/ +- Многие-ко-многим с доп. атрибутами https://www.baeldung.com/jpa-many-to-many +- Каскадное удаление сущностей со связями многие-ко-многим https://www.baeldung.com/jpa-remove-entity-many-to-many +- Выбор типа коллекции для отношений вида ко-многим в JPA https://thorben-janssen.com/association-mappings-bag-list-set/ diff --git a/Laba3/Лекция3-src/settings.gradle b/Laba3/Лекция3-src/settings.gradle new file mode 100644 index 0000000..0a383dd --- /dev/null +++ b/Laba3/Лекция3-src/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'demo' diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/DemoApplication.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..e89a1ae --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,74 @@ +package com.example.demo; + +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.service.SubscribeService; + +@SpringBootApplication +public class DemoApplication implements CommandLineRunner { + private final Logger log = LoggerFactory.getLogger(DemoApplication.class); + + private final UserService userService; + private final FilmService filmService; + private final GenreService genreService; + private final SubscribeService subscribeService; + + public DemoApplication( + UserService userService, + FilmService filmService, + GenreService genreService, + SubscribeService subscribeService) { + this.userService = userService; + this.filmService = filmService; + this.genreService = genreService; + this.subscribeService = subscribeService; + } + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @Override + public void run(String... args) throws Exception { + if (args.length > 0 && Objects.equals("--populate", args[0])) { + log.info("Create default genre values"); + + // final var genre1 = genreService.create(new GenreEntity("жанр1")); + // final var genre2 = genreService.create(new GenreEntity("жанр2")); + // final var genre3 = genreService.create(new GenreEntity("жанр3")); + + // log.info("Create default film values"); + // final var film1 = filmService.create(new FilmEntity("film1", genre1, 100d, + // 20)); + // final var film2 = filmService.create(new FilmEntity("film2", genre1, 100d, + // 20)); + // final var film3 = filmService.create(new FilmEntity("film3", genre2, 100d, + // 20)); + // final var film4 = filmService.create(new FilmEntity("film4", genre3, 100d, + // 20)); + // final var film5 = filmService.create(new FilmEntity("film5", genre1, 100d, + // 20)); + + log.info("Create default subscribe values"); + final var subscribe1 = subscribeService.create(new SubscribeEntity("Подписка 1", 100d)); + final var subscribe2 = subscribeService.create(new SubscribeEntity("Подписка 2", 200d)); + + log.info("Create default user values"); + final var user1 = userService.create(new UserEntity(subscribe1, "user1", "mail", "phone")); + userService.create(new UserEntity(subscribe2, "user2", "mail", "phone")); + } + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/Constants.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/Constants.java new file mode 100644 index 0000000..2474c0f --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/Constants.java @@ -0,0 +1,10 @@ +package com.example.demo.core.configuration; + +public class Constants { + public static final String SEQUENCE_NAME = "hibernate_sequence"; + + public static final String API_URL = "/api/1.0"; + + private Constants() { + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java new file mode 100644 index 0000000..a5ad6f3 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java @@ -0,0 +1,13 @@ +package com.example.demo.core.configuration; + +import org.modelmapper.ModelMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MapperConfiguration { + @Bean + ModelMapper modelMapper() { + return new ModelMapper(); + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java new file mode 100644 index 0000000..762e85a --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java @@ -0,0 +1,15 @@ +package com.example.demo.core.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.NonNull; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfiguration implements WebMvcConfigurer { + @Override + public void addCorsMappings(@NonNull CorsRegistry registry) { + registry.addMapping("/**") + .allowedMethods("GET", "POST", "PUT", "DELETE"); + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/core/error/NotFoundException.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/core/error/NotFoundException.java new file mode 100644 index 0000000..a61d118 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/core/error/NotFoundException.java @@ -0,0 +1,7 @@ +package com.example.demo.core.error; + +public class NotFoundException extends RuntimeException { + public NotFoundException(Class clazz, Long id) { + super(String.format("%s with id [%s] is not found or not exists", clazz.getSimpleName(), id)); + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/core/model/BaseEntity.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/core/model/BaseEntity.java new file mode 100644 index 0000000..eba74ad --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/core/model/BaseEntity.java @@ -0,0 +1,28 @@ +package com.example.demo.core.model; + +import com.example.demo.core.configuration.Constants; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.SequenceGenerator; + +@MappedSuperclass +public abstract class BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = Constants.SEQUENCE_NAME) + @SequenceGenerator(name = Constants.SEQUENCE_NAME, sequenceName = Constants.SEQUENCE_NAME, allocationSize = 1) + protected Long id; + + protected BaseEntity() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/films/api/FilmController.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/api/FilmController.java new file mode 100644 index 0000000..35e93bf --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/api/FilmController.java @@ -0,0 +1,73 @@ +package com.example.demo.films.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.genres.service.GenreService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/film") +public class FilmController { + private final FilmService filmService; + private final GenreService genreService; + private final ModelMapper modelMapper; + + public FilmController(FilmService filmService, GenreService genreService, ModelMapper modelMapper) { + this.filmService = filmService; + this.genreService = genreService; + this.modelMapper = modelMapper; + } + + private FilmDto toDto(FilmEntity entity) { + return modelMapper.map(entity, FilmDto.class); + } + + private FilmEntity toEntity(FilmDto dto) { + final FilmEntity entity = modelMapper.map(dto, FilmEntity.class); + entity.setGenre(genreService.get(dto.getGenreId())); + return entity; + } + + @GetMapping + public List getAll() { + return filmService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{id}") + public FilmDto get(@PathVariable(name = "id") Long id) { + return toDto(filmService.get(id)); + } + + @PostMapping + public FilmDto create(@RequestBody @Valid FilmDto dto) { + return toDto(filmService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public FilmDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid FilmDto dto) { + return toDto(filmService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public FilmDto delete(@PathVariable(name = "id") Long id) { + return toDto(filmService.delete(id)); + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/films/api/FilmDto.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/api/FilmDto.java new file mode 100644 index 0000000..fafcd95 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/api/FilmDto.java @@ -0,0 +1,59 @@ +package com.example.demo.films.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +public class FilmDto { + private Long id; + @NotNull + private String name; + @NotNull + @Min(1) + private Long genreId; + @NotNull + @Min(1) + private Double price; + @NotNull + @Min(1) + private Integer discount; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getGenreId() { + return genreId; + } + + public void setGenreId(Long genreId) { + this.genreId = genreId; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getDiscount() { + return discount; + } + + public void setDiscount(Integer discount) { + this.discount = discount; + } + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Double getSum() { + return price - ((price * discount) * 0.01); + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/films/model/FilmEntity.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/model/FilmEntity.java new file mode 100644 index 0000000..048036d --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/model/FilmEntity.java @@ -0,0 +1,106 @@ +package com.example.demo.films.model; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.userFilm.model.UserFilmEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.Table; + +@Entity +@Table(name = "films") +public class FilmEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 100) + private String name; + @ManyToOne + @JoinColumn(name = "genreId", nullable = false) + private GenreEntity genre; + @Column(nullable = false) + private Double price; + @Column(nullable = false) + private Integer discount; + @OneToMany(mappedBy = "film") + @OrderBy("id ASC") + private Set userFilms = new HashSet<>(); + + public FilmEntity() { + } + + public FilmEntity(String name, GenreEntity genre, Double price, Integer discount) { + this.name = name; + this.genre = genre; + this.price = price; + this.discount = discount; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public GenreEntity getGenre() { + return genre; + } + + public void setGenre(GenreEntity genre) { + this.genre = genre; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getDiscount() { + return discount; + } + + public void setDiscount(Integer discount) { + this.discount = discount; + } + + public Set getUserFilms() { + return userFilms; + } + + public void addUser(UserFilmEntity userFilm) { + if (userFilm.getFilm() != this) { + userFilm.setFilm(this); + } + userFilms.add(userFilm); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, genre, price, discount); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final FilmEntity other = (FilmEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getName(), name) + && Objects.equals(other.getGenre(), genre) + && Objects.equals(other.getPrice(), price) + && Objects.equals(other.getDiscount(), discount); + } +} \ No newline at end of file diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/films/model/UserFilmEntity.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/model/UserFilmEntity.java new file mode 100644 index 0000000..944d2cf --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/model/UserFilmEntity.java @@ -0,0 +1,15 @@ +package com.example.demo.films.model; + +public class UserFilmEntity { + + public FilmEntity getFilm() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getFilm'"); + } + + public void setFilm(FilmEntity filmEntity) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setFilm'"); + } + +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/films/repository/FilmRepository.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/repository/FilmRepository.java new file mode 100644 index 0000000..4b0d4bd --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/repository/FilmRepository.java @@ -0,0 +1,11 @@ +package com.example.demo.films.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +import com.example.demo.films.model.FilmEntity; + +public interface FilmRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); +} \ No newline at end of file diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/films/service/FilmService.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/service/FilmService.java new file mode 100644 index 0000000..001f0de --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/films/service/FilmService.java @@ -0,0 +1,64 @@ +package com.example.demo.films.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.repository.FilmRepository; + +@Service +public class FilmService { + private final FilmRepository repository; + + public FilmService(FilmRepository repository) { + this.repository = repository; + } + + private void checkName(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Type with name %s is already exists", name)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public FilmEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(FilmEntity.class, id)); + } + + @Transactional + public FilmEntity create(FilmEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkName(entity.getName()); + return repository.save(entity); + } + + @Transactional + public FilmEntity update(Long id, FilmEntity entity) { + final FilmEntity existsEntity = get(id); + existsEntity.setName(entity.getName()); + existsEntity.setGenre(entity.getGenre()); + existsEntity.setPrice(entity.getPrice()); + existsEntity.setDiscount(entity.getDiscount()); + return repository.save(existsEntity); + } + + @Transactional + public FilmEntity delete(Long id) { + final FilmEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreController.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreController.java new file mode 100644 index 0000000..3d483b1 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreController.java @@ -0,0 +1,68 @@ +package com.example.demo.genres.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/genre") +public class GenreController { + private final GenreService genreService; + private final ModelMapper modelMapper; + + public GenreController(GenreService genreService, ModelMapper modelMapper) { + this.genreService = genreService; + this.modelMapper = modelMapper; + } + + private GenreDto toDto(GenreEntity entity) { + return modelMapper.map(entity, GenreDto.class); + } + + private GenreEntity toEntity(GenreDto dto) { + return modelMapper.map(dto, GenreEntity.class); + } + + @GetMapping + public List getAll() { + return genreService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{id}") + public GenreDto get(@PathVariable(name = "id") Long id) { + return toDto(genreService.get(id)); + } + + @PostMapping + public GenreDto create(@RequestBody @Valid GenreDto dto) { + return toDto(genreService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public GenreDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid GenreDto dto) { + return toDto(genreService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public GenreDto delete(@PathVariable(name = "id") Long id) { + return toDto(genreService.delete(id)); + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreDto.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreDto.java new file mode 100644 index 0000000..c743956 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreDto.java @@ -0,0 +1,30 @@ +package com.example.demo.genres.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public class GenreDto { + private Long id; + @NotNull + @Size(min = 5, max = 50) + private String name; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/model/GenreEntity.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/model/GenreEntity.java new file mode 100644 index 0000000..07cf9bd --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/model/GenreEntity.java @@ -0,0 +1,47 @@ +package com.example.demo.genres.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = "genres") +public class GenreEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 50) + private String name; + + public GenreEntity() { + } + + public GenreEntity(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final GenreEntity other = (GenreEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getName(), name); + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java new file mode 100644 index 0000000..5a52ab6 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java @@ -0,0 +1,11 @@ +package com.example.demo.genres.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +import com.example.demo.genres.model.GenreEntity; + +public interface GenreRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); +} \ No newline at end of file diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/service/GenreService.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/service/GenreService.java new file mode 100644 index 0000000..a19e39b --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/genres/service/GenreService.java @@ -0,0 +1,62 @@ +package com.example.demo.genres.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.repository.GenreRepository; + +@Service +public class GenreService { + private final GenreRepository repository; + + public GenreService(GenreRepository repository) { + this.repository = repository; + } + + private void checkName(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Type with name %s is already exists", name)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public GenreEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(GenreEntity.class, id)); + } + + @Transactional + public GenreEntity create(GenreEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkName(entity.getName()); + return repository.save(entity); + } + + @Transactional + public GenreEntity update(Long id, GenreEntity entity) { + final GenreEntity existsEntity = get(id); + checkName(entity.getName()); + existsEntity.setName(entity.getName()); + return repository.save(existsEntity); + } + + @Transactional + public GenreEntity delete(Long id) { + final GenreEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java new file mode 100644 index 0000000..595897f --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java @@ -0,0 +1,68 @@ +package com.example.demo.subscribes.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.service.SubscribeService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/subscribe") +public class SubscribeController { + private final SubscribeService subscribeService; + private final ModelMapper modelMapper; + + public SubscribeController(SubscribeService subscribeService, ModelMapper modelMapper) { + this.subscribeService = subscribeService; + this.modelMapper = modelMapper; + } + + private SubscribeDto toDto(SubscribeEntity entity) { + return modelMapper.map(entity, SubscribeDto.class); + } + + private SubscribeEntity toEntity(SubscribeDto dto) { + return modelMapper.map(dto, SubscribeEntity.class); + } + + @GetMapping + public List getAll() { + return subscribeService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{id}") + public SubscribeDto get(@PathVariable(name = "id") Long id) { + return toDto(subscribeService.get(id)); + } + + @PostMapping + public SubscribeDto create(@RequestBody @Valid SubscribeDto dto) { + return toDto(subscribeService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public SubscribeDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid SubscribeDto dto) { + return toDto(subscribeService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public SubscribeDto delete(@PathVariable(name = "id") Long id) { + return toDto(subscribeService.delete(id)); + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java new file mode 100644 index 0000000..24cbfd3 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java @@ -0,0 +1,42 @@ +package com.example.demo.subscribes.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Size; + +public class SubscribeDto { + private Long id; + @NotNull + @Size(min = 3, max = 50) + private String name; + @NotNull + @Min(1) + private Double cost; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Double getCost() { + return cost; + } + + public void setCost(Double cost) { + this.cost = cost; + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java new file mode 100644 index 0000000..d8e99db --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java @@ -0,0 +1,60 @@ +package com.example.demo.subscribes.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = "subscribes") +public class SubscribeEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 50) + private String name; + @Column(nullable = false) + private Double cost; + + public SubscribeEntity() { + } + + public SubscribeEntity(String name, Double cost) { + this.name = name; + this.cost = cost; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Double getCost() { + return cost; + } + + public void setCost(Double cost) { + this.cost = cost; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, cost); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final SubscribeEntity other = (SubscribeEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getName(), name) + && Objects.equals(other.getCost(), cost); + } + +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java new file mode 100644 index 0000000..5ea8080 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java @@ -0,0 +1,11 @@ +package com.example.demo.subscribes.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +import com.example.demo.subscribes.model.SubscribeEntity; + +public interface SubscribeRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); +} \ No newline at end of file diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java new file mode 100644 index 0000000..7f3754e --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java @@ -0,0 +1,62 @@ +package com.example.demo.subscribes.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.repository.SubscribeRepository; + +@Service +public class SubscribeService { + private final SubscribeRepository repository; + + public SubscribeService(SubscribeRepository repository) { + this.repository = repository; + } + + private void checkName(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Type with name %s is already exists", name)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public SubscribeEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(SubscribeEntity.class, id)); + } + + @Transactional + public SubscribeEntity create(SubscribeEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkName(entity.getName()); + return repository.save(entity); + } + + @Transactional + public SubscribeEntity update(Long id, SubscribeEntity entity) { + final SubscribeEntity existsEntity = get(id); + checkName(entity.getName()); + existsEntity.setName(entity.getName()); + return repository.save(existsEntity); + } + + @Transactional + public SubscribeEntity delete(Long id) { + final SubscribeEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/userFilm/model/UserFilmEntity.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/userFilm/model/UserFilmEntity.java new file mode 100644 index 0000000..90a591f --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/userFilm/model/UserFilmEntity.java @@ -0,0 +1,95 @@ +package com.example.demo.userFilm.model; + +import com.example.demo.films.model.FilmEntity; +import com.example.demo.users.model.UserEntity; + +import java.util.Objects; + +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users_films") +public class UserFilmEntity { + @EmbeddedId + private UserFilmId id = new UserFilmId(); + @ManyToOne + @MapsId("userId") + @JoinColumn(name = "user_id") + private UserEntity user; + @ManyToOne + @MapsId("filmId") + @JoinColumn(name = "film_id") + private FilmEntity film; + private boolean viewed; + + public UserFilmEntity() { + } + + public UserFilmEntity(UserEntity user, FilmEntity film, boolean viewed) { + this.user = user; + this.film = film; + this.viewed = viewed; + } + + public UserFilmId getId() { + return id; + } + + public void setId(UserFilmId id) { + this.id = id; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + if (!user.getUserFilms().contains(this)) { + user.getUserFilms().add(this); + } + } + + public FilmEntity getFilm() { + return film; + } + + public void setFilm(FilmEntity film) { + this.film = film; + if (!film.getUserFilms().contains(this)) { + film.getUserFilms().add(this); + } + } + + public boolean isViewed() { + return viewed; + } + + public void setActive(boolean viewed) { + this.viewed = viewed; + } + + @Override + public int hashCode() { + return Objects.hash(id, user.getId(), film.getId(), viewed); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserFilmEntity other = (UserFilmEntity) obj; + return Objects.equals(id, other.id) + && Objects.equals(user.getId(), other.user.getId()) + && Objects.equals(film.getId(), other.film.getId()) + && viewed == other.viewed; + } + +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/userFilm/model/UserFilmId.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/userFilm/model/UserFilmId.java new file mode 100644 index 0000000..5667384 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/userFilm/model/UserFilmId.java @@ -0,0 +1,55 @@ +package com.example.demo.userFilm.model; + +import java.util.Objects; +import java.util.Optional; + +import jakarta.persistence.Embeddable; + +@Embeddable +public class UserFilmId { + private Long userId; + private Long filmId; + + public UserFilmId() { + } + + public UserFilmId(Long userId, Long filmId) { + this.userId = userId; + this.filmId = filmId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getFilmId() { + return filmId; + } + + public void setFilmId(Long filmId) { + this.filmId = filmId; + } + + @Override + public int hashCode() { + return Objects.hash( + Optional.ofNullable(userId).orElse(0L), + Optional.ofNullable(filmId).orElse(0L)); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserFilmId other = (UserFilmId) obj; + return Objects.equals(userId, other.userId) + && Objects.equals(filmId, other.filmId); + } + +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/users/api/UserController.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/users/api/UserController.java new file mode 100644 index 0000000..b62cf83 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/users/api/UserController.java @@ -0,0 +1,80 @@ +package com.example.demo.users.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; +import com.example.demo.films.service.FilmService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/user") +public class UserController { + private final UserService userService; + private final FilmService filmService; + private final ModelMapper modelMapper; + + public UserController(UserService userService, FilmService filmService, ModelMapper modelMapper) { + this.userService = userService; + this.filmService = filmService; + this.modelMapper = modelMapper; + } + + private UserDto toDto(UserEntity entity) { + return modelMapper.map(entity, UserDto.class); + } + + private UserEntity toEntity(UserDto dto) { + return modelMapper.map(dto, UserEntity.class); + } + + @GetMapping + public List getAll() { + return userService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{id}") + public UserDto get(@PathVariable(name = "id") Long id) { + return toDto(userService.get(id)); + } + + @PostMapping + public UserDto create(@RequestBody @Valid UserDto dto) { + return toDto(userService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public UserDto update(@PathVariable(name = "id") Long id, @RequestBody UserDto dto) { + return toDto(userService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public UserDto delete(@PathVariable(name = "id") Long id) { + return toDto(userService.delete(id)); + } + + @PutMapping("/addFilm/{id}") + public UserDto addFilm(@PathVariable(name = "id") Long id, @RequestBody Long filmId) { + return toDto(userService.addFilm(id, filmService.get(filmId))); + } + + @PutMapping("/deleteFilm/{id}") + public UserDto deleteFilm(@PathVariable(name = "id") Long id, @RequestBody Long filmId) { + return toDto(userService.deleteFilm(id, filmService.get(filmId))); + } + +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/users/api/UserDto.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/users/api/UserDto.java new file mode 100644 index 0000000..2009716 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/users/api/UserDto.java @@ -0,0 +1,89 @@ +package com.example.demo.users.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Min; +import com.example.demo.films.model.FilmEntity; + +public class UserDto { + @JsonProperty(access = Access.READ_ONLY) + private Long id; + @NotBlank + @Size(min = 3, max = 50) + private String name; + @NotBlank + @Size(min = 3, max = 50) + private String mail; + @NotBlank + @Size(min = 8, max = 30) + private String phone; + @NotNull + @Min(1) + private Long suscribeId; + + private final List listFilms = new ArrayList(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public Long getSubscribeId() { + return suscribeId; + } + + public void setSubscribe(Long subscribeId) { + this.suscribeId = subscribeId; + } + + public List getListFilms() { + return listFilms; + } + + public void setListFilms(List listFilms) { + this.listFilms.clear(); + this.listFilms.addAll(listFilms); + } + + public void addFilm(FilmEntity film) { + this.listFilms.add(film); + } + + public void deletefilm(FilmEntity film) { + this.listFilms.remove(film); + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/users/model/UserEntity.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/users/model/UserEntity.java new file mode 100644 index 0000000..41273d7 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/users/model/UserEntity.java @@ -0,0 +1,116 @@ +package com.example.demo.users.model; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.userFilm.model.UserFilmEntity; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users") +public class UserEntity extends BaseEntity { + + @Column(nullable = false, unique = true, length = 50) + private String name; + @Column(nullable = false, unique = true, length = 50) + private String mail; + @Column(nullable = false, unique = true, length = 20) + private String phone; + @ManyToOne + @JoinColumn(name = "subscribeId", nullable = false) + private SubscribeEntity subscribe; + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + @OrderBy("id ASC") + private Set userFilms = new HashSet<>(); + + public UserEntity() { + } + + public UserEntity(SubscribeEntity subscribe, String name, String mail, String phone) { + this.subscribe = subscribe; + this.name = name; + this.mail = mail; + this.phone = phone; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public Set getUserFilms() { + return userFilms; + } + + public void addFilm(UserFilmEntity userFilm) { + if (userFilm.getUser() != this) { + userFilm.setUser(this); + } + userFilms.add(userFilm); + } + + public void deleteFilm(UserFilmEntity userFilm) { + if (userFilm.getUser() != this) { + return; + } + userFilms.remove(userFilm); + } + + public SubscribeEntity getSubscribe() { + return subscribe; + } + + public void setSubscribe(SubscribeEntity subscribe) { + this.subscribe = subscribe; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, mail, phone, userFilms, subscribe); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserEntity other = (UserEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getName(), name) + && Objects.equals(other.getMail(), mail) + && Objects.equals(other.getPhone(), phone) + && Objects.equals(other.getSubscribe(), subscribe) + && Objects.equals(other.getUserFilms(), userFilms); + } +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/users/repository/UserRepository.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/users/repository/UserRepository.java new file mode 100644 index 0000000..25e7ba2 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/users/repository/UserRepository.java @@ -0,0 +1,11 @@ +package com.example.demo.users.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +import com.example.demo.users.model.UserEntity; + +public interface UserRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); +} diff --git a/Laba3/Лекция3-src/src/main/java/com/example/demo/users/service/UserService.java b/Laba3/Лекция3-src/src/main/java/com/example/demo/users/service/UserService.java new file mode 100644 index 0000000..1657b82 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/java/com/example/demo/users/service/UserService.java @@ -0,0 +1,218 @@ +package com.example.demo.users.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.modelmapper.internal.util.Objects; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.repository.UserRepository; +import com.example.demo.userFilm.model.UserFilmEntity; + +@Service +public class UserService { + // private final UserRepository repository; + + // public UserService(UserRepository repository) { + // this.repository = repository; + // } + + // private void checkName(String name) { + // if (repository.findByNameIgnoreCase(name).isPresent()) { + // throw new IllegalArgumentException( + // String.format("Type with name %s is already exists", name)); + // } + // } + + // @Transactional(readOnly = true) + // public List getAll() { + // return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + // } + + // @Transactional(readOnly = true) + // public UserEntity get(long id) { + // return repository.findById(id) + // .orElseThrow(() -> new NotFoundException(UserEntity.class, id)); + // } + + // @Transactional + // public UserEntity create(UserEntity entity) { + // if (entity == null) { + // throw new IllegalArgumentException("Entity is null"); + // } + // checkName(entity.getName()); + // return repository.save(entity); + // } + + // @Transactional + // public UserEntity update(long id, UserEntity entity) { + // final UserEntity existsEntity = get(id); + // checkName(entity.getName()); + // existsEntity.setName(entity.getName()); + // existsEntity.setMail(entity.getMail()); + // existsEntity.setPhone(entity.getPhone()); + // existsEntity.setSubscribe(entity.getSubscribe()); + // repository.save(existsEntity); + // return existsEntity; + // } + + // @Transactional + // public UserEntity delete(long id) { + // final UserEntity existsEntity = get(id); + // repository.delete(existsEntity); + // return existsEntity; + // } + + // @Transactional(readOnly = true) + // public List getUserFilms(long id) { + // return get(id).getUserFilms().stream().toList(); + // } + + // @Transactional + // public UserEntity addFilm(Long id, UserFilmEntity film1) { + // final UserEntity existsEntity = get(id); + // if (!existsEntity.getUserFilms().contains(film1)) + // existsEntity.addFilm(film1); + // return repository.save(existsEntity); + // } + + // @Transactional + // public UserEntity deleteFilm(Long id, UserFilmEntity film) { + // final UserEntity existsEntity = get(id); + // if (existsEntity.getUserFilms().contains(film)) + // existsEntity.deleteFilm(film); + // return repository.save(existsEntity); + // } + + + + + + + + + + + + + + + + private final UserRepository repository; + private final FilmService filmService; + + public UserService( + UserRepository repository, + FilmService filmService) { + this.repository = repository; + this.filmService = filmService; + } + + private void checkLogin(String login) { + if (repository.findByNameIgnoreCase(login).isPresent()) { + throw new IllegalArgumentException( + String.format("User with login %s is already exists", login)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public UserEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(UserEntity.class, id)); + } + + @Transactional + public UserEntity create(UserEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkLogin(entity.getName()); + repository.save(entity); + filmService.getAll().forEach(film -> { + final UserFilmEntity userFilm = new UserFilmEntity(entity, film, true); + userFilm.setUser(entity); + userFilm.setFilm(film); + }); + return repository.save(entity); + } + + @Transactional + public UserEntity update(long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + checkLogin(entity.getName()); + existsEntity.setName(entity.getName()); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public UserEntity delete(long id) { + final UserEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } + + @Transactional(readOnly = true) + public List getUserFilms(long id) { + return get(id).getUserFilms().stream() + .filter(UserFilmEntity::isViewed) + .map(UserFilmEntity::getFilm) + .toList(); + } + + private List changeUserFilmsState( + UserEntity existsUser, + List filmsIds, + boolean state) { + return existsUser.getUserFilms().stream() + .filter(film -> Objects.nonNull(film.getFilm())) + .filter(film -> filmsIds.contains(film.getFilm().getId())) + .filter(film -> film.isViewed() == !state) + .map(film -> { + film.setActive(state); + return film.getFilm(); + }) + .toList(); + } + + @Transactional + public List enableUserFilms(long id, List filmsIds) { + final UserEntity existsUser = get(id); + final List changedFilms = changeUserFilmsState( + existsUser, filmsIds, true); + repository.save(existsUser); + return changedFilms; + } + + @Transactional + public List disableUserFilms(long id, List filmsIds) { + final UserEntity existsUser = get(id); + final List changedFilms = changeUserFilmsState( + existsUser, filmsIds, false); + repository.save(existsUser); + return changedFilms; + } + + @Transactional + public List deleteAllUserFilms(long id) { + final UserEntity existsUser = get(id); + final List films = existsUser.getUserFilms().stream() + .filter(film -> Objects.nonNull(film.getFilm())) + .toList(); + films.forEach(existsUser::deleteFilm); + repository.save(existsUser); + return films.stream() + .map(UserFilmEntity::getFilm) + .toList(); + } +} diff --git a/Laba3/Лекция3-src/src/main/resources/application.properties b/Laba3/Лекция3-src/src/main/resources/application.properties new file mode 100644 index 0000000..62ab433 --- /dev/null +++ b/Laba3/Лекция3-src/src/main/resources/application.properties @@ -0,0 +1,20 @@ +# Server +spring.main.banner-mode=off +server.port=8080 + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:file:./data +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false +# spring.jpa.show-sql=true +# spring.jpa.properties.hibernate.format_sql=true + +# H2 console +spring.h2.console.enabled=true \ No newline at end of file diff --git a/Laba3/Лекция3-src/src/test/java/com/example/demo/FilmServiceTests.java b/Laba3/Лекция3-src/src/test/java/com/example/demo/FilmServiceTests.java new file mode 100644 index 0000000..bcb5bf3 --- /dev/null +++ b/Laba3/Лекция3-src/src/test/java/com/example/demo/FilmServiceTests.java @@ -0,0 +1,102 @@ +package com.example.demo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; + +import org.springframework.dao.DataIntegrityViolationException; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class FilmServiceTests { + + @Autowired + private GenreService genreService; + + @Autowired + private FilmService filmService; + private FilmEntity film; + + @BeforeEach + void createData() { + removeData(); + + final GenreEntity genre = genreService.create(new GenreEntity("Ноутбук")); + + film = filmService.create(new FilmEntity("name3", genre, 100d, 20)); + filmService.create(new FilmEntity("name31", genre, 100d, 20)); + filmService.create(new FilmEntity("name32", genre, 100d, 20)); + } + + @AfterEach + void removeData() { + genreService.getAll().forEach(item -> genreService.delete(item.getId())); + filmService.getAll().forEach(item -> filmService.delete(item.getId())); + } + + @Test + @Order(1) + void createTest() { + Assertions.assertEquals(3, filmService.getAll().size()); + Assertions.assertEquals(film, filmService.get(film.getId())); + } + + @Test + void createNotUniqueTest() { + final FilmEntity nonUniquefilm = new FilmEntity("film1", new GenreEntity("genre1"), 100d, 20); + Assertions.assertThrows(IllegalArgumentException.class, () -> filmService.create(nonUniquefilm)); + } + + @Test + void createNullableTest() { + final FilmEntity nullablefilm = new FilmEntity(null, null, null, null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> filmService.create(nullablefilm)); + } + + @Test + void updateTest() { + final String testName = "TEST"; + final GenreEntity testGenre = new GenreEntity("TEST"); + final Double testPrice = 333d; + final int testDiscount = 100; + + final String oldName = film.getName(); + final GenreEntity oldGenre = film.getGenre(); + final Double oldPrice = film.getPrice(); + final int oldDiscount = film.getDiscount(); + final FilmEntity newEntity = filmService.update(film.getId(), + new FilmEntity(testName, testGenre, testPrice, testDiscount)); + Assertions.assertEquals(3, filmService.getAll().size()); + Assertions.assertEquals(newEntity, filmService.get(film.getId())); + Assertions.assertEquals(testName, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + Assertions.assertEquals(testGenre, newEntity.getGenre()); + Assertions.assertNotEquals(oldGenre, newEntity.getGenre()); + Assertions.assertEquals(testPrice, newEntity.getPrice()); + Assertions.assertNotEquals(oldPrice, newEntity.getPrice()); + Assertions.assertEquals(testDiscount, newEntity.getDiscount()); + Assertions.assertNotEquals(oldDiscount, newEntity.getDiscount()); + } + + @Test + void deleteTest() { + filmService.delete(film.getId()); + Assertions.assertEquals(2, filmService.getAll().size()); + + final FilmEntity newEntity = filmService + .create(new FilmEntity(film.getName(), film.getGenre(), film.getPrice(), film.getDiscount())); + Assertions.assertEquals(3, filmService.getAll().size()); + Assertions.assertNotEquals(film.getId(), newEntity.getId()); + } +} diff --git a/Laba3/Лекция3-src/src/test/java/com/example/demo/GenreServiceTests.java b/Laba3/Лекция3-src/src/test/java/com/example/demo/GenreServiceTests.java new file mode 100644 index 0000000..858b9b1 --- /dev/null +++ b/Laba3/Лекция3-src/src/test/java/com/example/demo/GenreServiceTests.java @@ -0,0 +1,79 @@ +package com.example.demo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; + +@SpringBootTest +class GenreServiceTests { + @Autowired + private GenreService genreService; + + private GenreEntity genre; + + @BeforeEach + void createData() { + removeData(); + + genre = genreService.create(new GenreEntity("жанр1")); + genreService.create(new GenreEntity("жанр2")); + genreService.create(new GenreEntity("жанр3")); + } + + @AfterEach + void removeData() { + genreService.getAll().forEach(item -> genreService.delete(item.getId())); + } + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> genreService.get(0L)); + } + + @Test + void createTest() { + Assertions.assertEquals(3, genreService.getAll().size()); + Assertions.assertEquals(genre, genreService.get(genre.getId())); + } + + @Test + void createNotUniqueTest() { + final GenreEntity nonUniquegenre = new GenreEntity("Ноутбук"); + Assertions.assertThrows(IllegalArgumentException.class, () -> genreService.create(nonUniquegenre)); + } + + @Test + void createNullableTest() { + final GenreEntity nullablegenre = new GenreEntity(null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> genreService.create(nullablegenre)); + } + + @Test + void updateTest() { + final String test = "TEST"; + final String oldName = genre.getName(); + final GenreEntity newEntity = genreService.update(genre.getId(), new GenreEntity(test)); + Assertions.assertEquals(3, genreService.getAll().size()); + Assertions.assertEquals(newEntity, genreService.get(genre.getId())); + Assertions.assertEquals(test, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + } + + @Test + void deleteTest() { + genreService.delete(genre.getId()); + Assertions.assertEquals(2, genreService.getAll().size()); + + final GenreEntity newEntity = genreService.create(new GenreEntity(genre.getName())); + Assertions.assertEquals(3, genreService.getAll().size()); + Assertions.assertNotEquals(genre.getId(), newEntity.getId()); + } +} diff --git a/Laba3/Лекция3-src/src/test/resources/application.properties b/Laba3/Лекция3-src/src/test/resources/application.properties new file mode 100644 index 0000000..d5f355c --- /dev/null +++ b/Laba3/Лекция3-src/src/test/resources/application.properties @@ -0,0 +1,14 @@ +# Server +spring.main.banner-mode=off + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false \ No newline at end of file diff --git a/laba3.1/Лекция4-src/.gitignore b/laba3.1/Лекция4-src/.gitignore new file mode 100644 index 0000000..546ecee --- /dev/null +++ b/laba3.1/Лекция4-src/.gitignore @@ -0,0 +1,36 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +data.*.db \ No newline at end of file diff --git a/laba3.1/Лекция4-src/.vscode/extensions.json b/laba3.1/Лекция4-src/.vscode/extensions.json new file mode 100644 index 0000000..42cf79d --- /dev/null +++ b/laba3.1/Лекция4-src/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + // fronted + "AndersEAndersen.html-class-suggestions", + "dbaeumer.vscode-eslint", + // backend + "vscjava.vscode-java-pack", + "vmware.vscode-boot-dev-pack", + "vscjava.vscode-gradle", + "redhat.vscode-xml" + ] +} \ No newline at end of file diff --git a/laba3.1/Лекция4-src/.vscode/launch.json b/laba3.1/Лекция4-src/.vscode/launch.json new file mode 100644 index 0000000..5992c40 --- /dev/null +++ b/laba3.1/Лекция4-src/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "configurations": [ + { + "type": "java", + "name": "Demo", + "request": "launch", + "cwd": "${workspaceFolder}", + "mainClass": "com.example.demo.DemoApplication", + "projectName": "lec4", + "args": "", + "envFile": "${workspaceFolder}/.env" + }, + { + "type": "java", + "name": "Demo (populate)", + "request": "launch", + "cwd": "${workspaceFolder}", + "mainClass": "com.example.demo.DemoApplication", + "projectName": "lec4", + "args": "--populate", + "envFile": "${workspaceFolder}/.env" + } + ] +} \ No newline at end of file diff --git a/laba3.1/Лекция4-src/.vscode/settings.json b/laba3.1/Лекция4-src/.vscode/settings.json new file mode 100644 index 0000000..eab3db6 --- /dev/null +++ b/laba3.1/Лекция4-src/.vscode/settings.json @@ -0,0 +1,21 @@ +{ + "editor.tabSize": 4, + "editor.detectIndentation": false, + "editor.insertSpaces": true, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.formatOnType": false, + "java.compile.nullAnalysis.mode": "disabled", + "java.configuration.updateBuildConfiguration": "automatic", + "[java]": { + "editor.pasteAs.enabled": false, + }, + "gradle.nestedProjects": true, + "java.saveActions.organizeImports": true, + "java.dependency.packagePresentation": "hierarchical", + "spring-boot.ls.problem.boot2.JAVA_CONSTRUCTOR_PARAMETER_INJECTION": "WARNING", + "spring.initializr.defaultLanguage": "Java", + "java.format.settings.url": ".vscode/eclipse-formatter.xml", + "java.project.explorer.showNonJavaResources": true, + "java.codeGeneration.hashCodeEquals.useJava7Objects": true, +} \ No newline at end of file diff --git a/laba3.1/Лекция4-src/build.gradle b/laba3.1/Лекция4-src/build.gradle new file mode 100644 index 0000000..66b678e --- /dev/null +++ b/laba3.1/Лекция4-src/build.gradle @@ -0,0 +1,68 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.4' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +defaultTasks 'bootRun' + +ext { + springProfiles = [] + if (project.hasProperty('prod')) { + springProfiles.add('prod') + } else { + springProfiles.add('dev') + } +} + +bootRun { + def currentProfiles = springProfiles.join(',') + println('Current profiles are: ' + currentProfiles) + def currentArgs = ['--spring.profiles.active=' + currentProfiles] + if (project.hasProperty('args')) { + currentArgs.addAll(project.args.split(',')) + } + args currentArgs +} + +jar { + enabled = false +} + +bootJar { + archiveFileName = String.format('%s-%s.jar', rootProject.name, version) +} + +assert System.properties['java.specification.version'] == '17' || '21' +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +dependencies { + if (project.hasProperty('prod')) { + implementation 'org.postgresql:postgresql:42.7.3' + } else { + implementation 'com.h2database:h2:2.2.224' + } + + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + implementation 'org.modelmapper:modelmapper:3.2.0' + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.liquibase:liquibase-core:4.26.0' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} \ No newline at end of file diff --git a/laba3.1/Лекция4-src/gradle/wrapper/gradle-wrapper.jar b/laba3.1/Лекция4-src/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/laba3.1/Лекция4-src/gradle/wrapper/gradle-wrapper.properties b/laba3.1/Лекция4-src/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/laba3.1/Лекция4-src/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/laba3.1/Лекция4-src/gradlew b/laba3.1/Лекция4-src/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/laba3.1/Лекция4-src/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/laba3.1/Лекция4-src/gradlew.bat b/laba3.1/Лекция4-src/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/laba3.1/Лекция4-src/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/laba3.1/Лекция4-src/readme.md b/laba3.1/Лекция4-src/readme.md new file mode 100644 index 0000000..8eef312 --- /dev/null +++ b/laba3.1/Лекция4-src/readme.md @@ -0,0 +1,28 @@ +Swagger UI: \ +http://localhost:8080/swagger-ui/index.html + +H2 Console: \ +http://localhost:8080/h2-console + +JDBC URL: jdbc:h2:file:./data \ +User Name: sa \ +Password: password + +Для профиля prod необходимо наличие СУБД PostgreSQL и БД demo-app. + +Создать БД можно так: \ +createdb -h localhost -U postgres demo-app + +Пример запуска приложения с профилем prod и заполнением БД с помощью аргумента --populate: + +- nix => bash ./gradlew.bat -Pprod -Pargs='--populate' +- windows =>.\gradlew.bat -Pprod -Pargs='--populate' + +Почитать: + +- Односторонние и двусторонние связи https://www.baeldung.com/jpa-hibernate-associations +- Getters и Setters для двусторонних связей https://en.wikibooks.org/wiki/Java_Persistence/OneToMany#Getters_and_Setters +- Многие-ко-многим с доп. атрибутами https://thorben-janssen.com/hibernate-tip-many-to-many-association-with-additional-attributes/ +- Многие-ко-многим с доп. атрибутами https://www.baeldung.com/jpa-many-to-many +- Каскадное удаление сущностей со связями многие-ко-многим https://www.baeldung.com/jpa-remove-entity-many-to-many +- Выбор типа коллекции для отношений вида ко-многим в JPA https://thorben-janssen.com/association-mappings-bag-list-set/ diff --git a/laba3.1/Лекция4-src/settings.gradle b/laba3.1/Лекция4-src/settings.gradle new file mode 100644 index 0000000..0a383dd --- /dev/null +++ b/laba3.1/Лекция4-src/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'demo' diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/DemoApplication.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..cf48dfa --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,74 @@ +package com.example.demo; + +import java.util.Arrays; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.service.OrderService; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@SpringBootApplication +public class DemoApplication implements CommandLineRunner { + private final Logger log = LoggerFactory.getLogger(DemoApplication.class); + + private final TypeService typeService; + private final SubscriptionService subscriptionService; + private final UserService userService; + private final OrderService orderService; + + public DemoApplication( + TypeService typeService, + SubscriptionService subscriptionService, + UserService userService, + OrderService orderService) { + this.typeService = typeService; + this.subscriptionService = subscriptionService; + this.userService = userService; + this.orderService = orderService; + } + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @Override + public void run(String... args) throws Exception { + if (args.length > 0 && Arrays.asList(args).contains("--populate")) { + log.info("Create default type values"); + final var type1 = typeService.create(new TypeEntity("Ноутбук")); + final var type2 = typeService.create(new TypeEntity("Телефон")); + final var type3 = typeService.create(new TypeEntity("Игровая приставка")); + + log.info("Create default subscription values"); + subscriptionService.create(new SubscriptionEntity("Подписка 1")); + subscriptionService.create(new SubscriptionEntity("Подписка 2")); + subscriptionService.create(new SubscriptionEntity("Подписка 3")); + + log.info("Create default user values"); + final var user1 = userService.create(new UserEntity("user1")); + userService.create(new UserEntity("user2")); + + log.info("Create default order values"); + final var orders = List.of( + new OrderEntity(type1, 49999.00, 20), + new OrderEntity(type1, 129999.00, 3), + new OrderEntity(type2, 15450.50, 30), + new OrderEntity(type2, 69900.50, 10), + new OrderEntity(type2, 150000.00, 6), + new OrderEntity(type3, 75000.00, 6), + new OrderEntity(type3, 67800.00, 3)); + orders.forEach(order -> orderService.create(user1.getId(), order)); + } + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/api/PageDto.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/api/PageDto.java new file mode 100644 index 0000000..4cae429 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/api/PageDto.java @@ -0,0 +1,97 @@ +package com.example.demo.core.api; + +import java.util.ArrayList; +import java.util.List; + +public class PageDto { + private List items = new ArrayList<>(); + private int itemsCount; + private int currentPage; + private int currentSize; + private int totalPages; + private long totalItems; + private boolean isFirst; + private boolean isLast; + private boolean hasNext; + private boolean hasPrevious; + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public int getItemsCount() { + return itemsCount; + } + + public void setItemsCount(int itemsCount) { + this.itemsCount = itemsCount; + } + + public int getCurrentPage() { + return currentPage; + } + + public void setCurrentPage(int currentPage) { + this.currentPage = currentPage; + } + + public int getCurrentSize() { + return currentSize; + } + + public void setCurrentSize(int currentSize) { + this.currentSize = currentSize; + } + + public int getTotalPages() { + return totalPages; + } + + public void setTotalPages(int totalPages) { + this.totalPages = totalPages; + } + + public long getTotalItems() { + return totalItems; + } + + public void setTotalItems(long totalItems) { + this.totalItems = totalItems; + } + + public boolean isFirst() { + return isFirst; + } + + public void setFirst(boolean isFirst) { + this.isFirst = isFirst; + } + + public boolean isLast() { + return isLast; + } + + public void setLast(boolean isLast) { + this.isLast = isLast; + } + + public boolean isHasNext() { + return hasNext; + } + + public void setHasNext(boolean hasNext) { + this.hasNext = hasNext; + } + + public boolean isHasPrevious() { + return hasPrevious; + } + + public void setHasPrevious(boolean hasPrevious) { + this.hasPrevious = hasPrevious; + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/api/PageDtoMapper.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/api/PageDtoMapper.java new file mode 100644 index 0000000..e8d3dd0 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/api/PageDtoMapper.java @@ -0,0 +1,25 @@ +package com.example.demo.core.api; + +import java.util.function.Function; + +import org.springframework.data.domain.Page; + +public class PageDtoMapper { + private PageDtoMapper() { + } + + public static PageDto toDto(Page page, Function mapper) { + final PageDto dto = new PageDto<>(); + dto.setItems(page.getContent().stream().map(mapper::apply).toList()); + dto.setItemsCount(page.getNumberOfElements()); + dto.setCurrentPage(page.getNumber()); + dto.setCurrentSize(page.getSize()); + dto.setTotalPages(page.getTotalPages()); + dto.setTotalItems(page.getTotalElements()); + dto.setFirst(page.isFirst()); + dto.setLast(page.isLast()); + dto.setHasNext(page.hasNext()); + dto.setHasPrevious(page.hasPrevious()); + return dto; + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/Constants.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/Constants.java new file mode 100644 index 0000000..4bd6a8d --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/Constants.java @@ -0,0 +1,12 @@ +package com.example.demo.core.configuration; + +public class Constants { + public static final String SEQUENCE_NAME = "hibernate_sequence"; + + public static final String API_URL = "/api/1.0"; + + public static final String DEFAULT_PAGE_SIZE = "5"; + + private Constants() { + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java new file mode 100644 index 0000000..a5ad6f3 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java @@ -0,0 +1,13 @@ +package com.example.demo.core.configuration; + +import org.modelmapper.ModelMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MapperConfiguration { + @Bean + ModelMapper modelMapper() { + return new ModelMapper(); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java new file mode 100644 index 0000000..762e85a --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java @@ -0,0 +1,15 @@ +package com.example.demo.core.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.NonNull; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfiguration implements WebMvcConfigurer { + @Override + public void addCorsMappings(@NonNull CorsRegistry registry) { + registry.addMapping("/**") + .allowedMethods("GET", "POST", "PUT", "DELETE"); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/AdviceController.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/AdviceController.java new file mode 100644 index 0000000..9aa33cc --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/AdviceController.java @@ -0,0 +1,54 @@ +package com.example.demo.core.error; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class AdviceController { + private final Logger log = LoggerFactory.getLogger(AdviceController.class); + + public static ErrorCauseDto getRootCause(Throwable throwable) { + Throwable rootCause = throwable; + while (rootCause.getCause() != null && rootCause.getCause() != rootCause) { + rootCause = rootCause.getCause(); + } + final StackTraceElement firstError = rootCause.getStackTrace()[0]; + return new ErrorCauseDto( + rootCause.getClass().getName(), + firstError.getMethodName(), + firstError.getFileName(), + firstError.getLineNumber()); + } + + private ResponseEntity handleException(Throwable throwable, HttpStatusCode httpCode) { + log.error("{}", throwable.getMessage()); + throwable.printStackTrace(); + final ErrorDto errorDto = new ErrorDto(throwable.getMessage(), AdviceController.getRootCause(throwable)); + return new ResponseEntity<>(errorDto, httpCode); + } + + @ExceptionHandler(NotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public ResponseEntity handleNotFoundException(Throwable throwable) { + return handleException(throwable, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleDataIntegrityViolationException(Throwable throwable) { + return handleException(throwable, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity handleAnyException(Throwable throwable) { + return handleException(throwable, HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/ErrorCauseDto.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/ErrorCauseDto.java new file mode 100644 index 0000000..602ce70 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/ErrorCauseDto.java @@ -0,0 +1,31 @@ +package com.example.demo.core.error; + +class ErrorCauseDto { + private String exception; + private String methodName; + private String fineName; + private int lineNumber; + + ErrorCauseDto(String exception, String methodName, String fineName, int lineNumber) { + this.exception = exception; + this.methodName = methodName; + this.fineName = fineName; + this.lineNumber = lineNumber; + } + + public String getException() { + return exception; + } + + public String getMethodName() { + return methodName; + } + + public String getFineName() { + return fineName; + } + + public int getLineNumber() { + return lineNumber; + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/ErrorDto.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/ErrorDto.java new file mode 100644 index 0000000..457975c --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/ErrorDto.java @@ -0,0 +1,20 @@ +package com.example.demo.core.error; + +public class ErrorDto { + private String error; + private ErrorCauseDto rootCause; + + public ErrorDto(String error, ErrorCauseDto rootCause) { + this.error = error; + this.rootCause = rootCause; + } + + public String getError() { + return error; + } + + public ErrorCauseDto getRootCause() { + return rootCause; + } + +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/NotFoundException.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/NotFoundException.java new file mode 100644 index 0000000..a61d118 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/error/NotFoundException.java @@ -0,0 +1,7 @@ +package com.example.demo.core.error; + +public class NotFoundException extends RuntimeException { + public NotFoundException(Class clazz, Long id) { + super(String.format("%s with id [%s] is not found or not exists", clazz.getSimpleName(), id)); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/model/BaseEntity.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/model/BaseEntity.java new file mode 100644 index 0000000..eba74ad --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/core/model/BaseEntity.java @@ -0,0 +1,28 @@ +package com.example.demo.core.model; + +import com.example.demo.core.configuration.Constants; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.SequenceGenerator; + +@MappedSuperclass +public abstract class BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = Constants.SEQUENCE_NAME) + @SequenceGenerator(name = Constants.SEQUENCE_NAME, sequenceName = Constants.SEQUENCE_NAME, allocationSize = 1) + protected Long id; + + protected BaseEntity() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderController.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderController.java new file mode 100644 index 0000000..c8675f8 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderController.java @@ -0,0 +1,98 @@ +package com.example.demo.orders.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.api.PageDto; +import com.example.demo.core.api.PageDtoMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.model.OrderGrouped; +import com.example.demo.orders.service.OrderService; +import com.example.demo.types.service.TypeService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/user/{user}/order") +public class OrderController { + private final OrderService orderService; + private final TypeService typeService; + private final ModelMapper modelMapper; + + public OrderController(OrderService orderService, TypeService typeService, ModelMapper modelMapper) { + this.orderService = orderService; + this.typeService = typeService; + this.modelMapper = modelMapper; + } + + private OrderDto toDto(OrderEntity entity) { + return modelMapper.map(entity, OrderDto.class); + } + + private OrderEntity toEntity(OrderDto dto) { + final OrderEntity entity = modelMapper.map(dto, OrderEntity.class); + entity.setType(typeService.get(dto.getTypeId())); + return entity; + } + + private OrderGroupedDto toGroupedDto(OrderGrouped entity) { + return modelMapper.map(entity, OrderGroupedDto.class); + } + + @GetMapping + public PageDto getAll( + @PathVariable(name = "user") Long userId, + @RequestParam(name = "typeId", defaultValue = "0") Long typeId, + @RequestParam(name = "page", defaultValue = "0") int page, + @RequestParam(name = "size", defaultValue = Constants.DEFAULT_PAGE_SIZE) int size) { + return PageDtoMapper.toDto(orderService.getAll(userId, typeId, page, size), this::toDto); + } + + @GetMapping("/{id}") + public OrderDto get( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "id") Long id) { + return toDto(orderService.get(userId, id)); + } + + @PostMapping + public OrderDto create( + @PathVariable(name = "user") Long userId, + @RequestBody @Valid OrderDto dto) { + return toDto(orderService.create(userId, toEntity(dto))); + } + + @PutMapping("/{id}") + public OrderDto update( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "id") Long id, + @RequestBody @Valid OrderDto dto) { + return toDto(orderService.update(userId, id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public OrderDto delete( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "id") Long id) { + return toDto(orderService.delete(userId, id)); + } + + @GetMapping("/total") + public List getMethodName(@PathVariable(name = "user") Long userId) { + return orderService.getTotal(userId).stream() + .map(this::toGroupedDto) + .toList(); + } + +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderDto.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderDto.java new file mode 100644 index 0000000..78e0ca2 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderDto.java @@ -0,0 +1,57 @@ +package com.example.demo.orders.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +public class OrderDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long id; + @NotNull + @Min(1) + private Long typeId; + @NotNull + @Min(1) + private Double price; + @NotNull + @Min(1) + private Integer count; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getTypeId() { + return typeId; + } + + public void setTypeId(Long typeId) { + this.typeId = typeId; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Double getSum() { + return price * count; + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderGroupedDto.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderGroupedDto.java new file mode 100644 index 0000000..e073ed7 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/api/OrderGroupedDto.java @@ -0,0 +1,31 @@ +package com.example.demo.orders.api; + +public class OrderGroupedDto { + private Long typeId; + private Long totalPrice; + private Integer totalCount; + + public Long getTypeId() { + return typeId; + } + + public void setTypeId(Long typeId) { + this.typeId = typeId; + } + + public Long getTotalPrice() { + return totalPrice; + } + + public void setTotalPrice(Long totalPrice) { + this.totalPrice = totalPrice; + } + + public Integer getTotalCount() { + return totalCount; + } + + public void setTotalCount(Integer totalCount) { + this.totalCount = totalCount; + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/model/OrderEntity.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/model/OrderEntity.java new file mode 100644 index 0000000..d4e6b90 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/model/OrderEntity.java @@ -0,0 +1,91 @@ +package com.example.demo.orders.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.users.model.UserEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "orders") +public class OrderEntity extends BaseEntity { + @ManyToOne + @JoinColumn(name = "typeId", nullable = false) + private TypeEntity type; + @ManyToOne + @JoinColumn(name = "userId", nullable = false) + private UserEntity user; + @Column(nullable = false) + private Double price; + @Column(nullable = false) + private Integer count; + + public OrderEntity() { + } + + public OrderEntity(TypeEntity type, Double price, Integer count) { + this.type = type; + this.price = price; + this.count = count; + } + + public TypeEntity getType() { + return type; + } + + public void setType(TypeEntity type) { + this.type = type; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + if (!user.getOrders().contains(this)) { + user.getOrders().add(this); + } + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + @Override + public int hashCode() { + return Objects.hash(id, type, user.getId(), price, count); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final OrderEntity other = (OrderEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getType(), type) + && Objects.equals(other.getUser().getId(), user.getId()) + && Objects.equals(other.getPrice(), price) + && Objects.equals(other.getCount(), count); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/model/OrderGrouped.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/model/OrderGrouped.java new file mode 100644 index 0000000..4d55f18 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/model/OrderGrouped.java @@ -0,0 +1,11 @@ +package com.example.demo.orders.model; + +import com.example.demo.types.model.TypeEntity; + +public interface OrderGrouped { + TypeEntity getType(); + + double getTotalPrice(); + + int getTotalCount(); +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/repository/OrderRepository.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/repository/OrderRepository.java new file mode 100644 index 0000000..81b7b7a --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/repository/OrderRepository.java @@ -0,0 +1,41 @@ +package com.example.demo.orders.repository; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.model.OrderGrouped; + +public interface OrderRepository + extends CrudRepository, PagingAndSortingRepository { + Optional findOneByUserIdAndId(long userId, long id); + + List findByUserId(long userId); + + Page findByUserId(long userId, Pageable pageable); + + List findByUserIdAndTypeId(long userId, long typeId); + + Page findByUserIdAndTypeId(long userId, long typeId, Pageable pageable); + + // select + // tpe.name, + // coalesce(sum(order.price), 0), + // coalesce(sum(order.count), 0) + // from types as tpe + // left join orders as order on tpe.id = order.type_id and order.user_id = ? + // group by tpe.name order by tpe.id + @Query("select " + + "t as type, " + + "coalesce(sum(o.price), 0) as totalPrice, " + + "coalesce(sum(o.count), 0) as totalCount " + + "from TypeEntity t left join OrderEntity o on o.type = t and o.user.id = ?1 " + + "group by t order by t.id") + List getOrdersTotalByType(long userId); +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/service/OrderService.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/service/OrderService.java new file mode 100644 index 0000000..21a5cbe --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/orders/service/OrderService.java @@ -0,0 +1,88 @@ +package com.example.demo.orders.service; + +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.model.OrderGrouped; +import com.example.demo.orders.repository.OrderRepository; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@Service +public class OrderService { + private final OrderRepository repository; + private final UserService userService; + + public OrderService(OrderRepository repository, UserService userService) { + this.repository = repository; + this.userService = userService; + } + + @Transactional(readOnly = true) + public List getAll(long userId, long typeId) { + userService.get(userId); + if (typeId <= 0L) { + return repository.findByUserId(userId); + } else { + return repository.findByUserIdAndTypeId(userId, typeId); + } + } + + @Transactional(readOnly = true) + public Page getAll(long userId, long typeId, int page, int size) { + final Pageable pageRequest = PageRequest.of(page, size); + userService.get(userId); + if (typeId <= 0L) { + return repository.findByUserId(userId, pageRequest); + } + return repository.findByUserIdAndTypeId(userId, typeId, pageRequest); + } + + @Transactional(readOnly = true) + public OrderEntity get(long userId, long id) { + userService.get(userId); + return repository.findOneByUserIdAndId(userId, id) + .orElseThrow(() -> new NotFoundException(OrderEntity.class, id)); + } + + @Transactional + public OrderEntity create(long userId, OrderEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + final UserEntity existsUser = userService.get(userId); + entity.setUser(existsUser); + return repository.save(entity); + } + + @Transactional + public OrderEntity update(long userId, long id, OrderEntity entity) { + userService.get(userId); + final OrderEntity existsEntity = get(userId, id); + existsEntity.setType(entity.getType()); + existsEntity.setPrice(entity.getPrice()); + existsEntity.setCount(entity.getCount()); + return repository.save(existsEntity); + } + + @Transactional + public OrderEntity delete(long userId, long id) { + userService.get(userId); + final OrderEntity existsEntity = get(userId, id); + repository.delete(existsEntity); + return existsEntity; + } + + @Transactional(readOnly = true) + public List getTotal(long userId) { + userService.get(userId); + return repository.getOrdersTotalByType(userId); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/api/SubscriptionController.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/api/SubscriptionController.java new file mode 100644 index 0000000..1a7dd9b --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/api/SubscriptionController.java @@ -0,0 +1,68 @@ +package com.example.demo.subscriptions.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/subsciption") +public class SubscriptionController { + private final SubscriptionService subscriptionService; + private final ModelMapper modelMapper; + + public SubscriptionController(SubscriptionService subscriptionService, ModelMapper modelMapper) { + this.subscriptionService = subscriptionService; + this.modelMapper = modelMapper; + } + + private SubscriptionDto toDto(SubscriptionEntity entity) { + return modelMapper.map(entity, SubscriptionDto.class); + } + + private SubscriptionEntity toEntity(SubscriptionDto dto) { + return modelMapper.map(dto, SubscriptionEntity.class); + } + + @GetMapping + public List getAll() { + return subscriptionService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{id}") + public SubscriptionDto get(@PathVariable(name = "id") Long id) { + return toDto(subscriptionService.get(id)); + } + + @PostMapping + public SubscriptionDto create(@RequestBody @Valid SubscriptionDto dto) { + return toDto(subscriptionService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public SubscriptionDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid SubscriptionDto dto) { + return toDto(subscriptionService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public SubscriptionDto delete(@PathVariable(name = "id") Long id) { + return toDto(subscriptionService.delete(id)); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/api/SubscriptionDto.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/api/SubscriptionDto.java new file mode 100644 index 0000000..bae4693 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/api/SubscriptionDto.java @@ -0,0 +1,30 @@ +package com.example.demo.subscriptions.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class SubscriptionDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long id; + @NotBlank + @Size(min = 5, max = 50) + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/model/SubscriptionEntity.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/model/SubscriptionEntity.java new file mode 100644 index 0000000..59214e4 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/model/SubscriptionEntity.java @@ -0,0 +1,67 @@ +package com.example.demo.subscriptions.model; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.usersubscription.model.UserSubscriptionEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.Table; + +@Entity +@Table(name = "subscriptions") +public class SubscriptionEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 50) + private String name; + @OneToMany(mappedBy = "subscription") + @OrderBy("id ASC") + private Set userSubscriptions = new HashSet<>(); + + public SubscriptionEntity() { + } + + public SubscriptionEntity(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getUserSubscriptions() { + return userSubscriptions; + } + + public void addUser(UserSubscriptionEntity userSubscription) { + if (userSubscription.getSubscription() != this) { + userSubscription.setSubscription(this); + } + userSubscriptions.add(userSubscription); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, userSubscriptions); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + SubscriptionEntity other = (SubscriptionEntity) obj; + return Objects.equals(id, other.id) + && Objects.equals(name, other.name) + && Objects.equals(userSubscriptions, other.userSubscriptions); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/repository/SubscriptionRepository.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/repository/SubscriptionRepository.java new file mode 100644 index 0000000..0ce4dc9 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/repository/SubscriptionRepository.java @@ -0,0 +1,11 @@ +package com.example.demo.subscriptions.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +import com.example.demo.subscriptions.model.SubscriptionEntity; + +public interface SubscriptionRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/service/SubscriptionService.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/service/SubscriptionService.java new file mode 100644 index 0000000..03cfbc7 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/subscriptions/service/SubscriptionService.java @@ -0,0 +1,63 @@ +package com.example.demo.subscriptions.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.repository.SubscriptionRepository; + +@Service +public class SubscriptionService { + private final SubscriptionRepository repository; + + public SubscriptionService(SubscriptionRepository repository) { + this.repository = repository; + } + + private void checkName(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Type with name %s is already exists", name)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public SubscriptionEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(SubscriptionEntity.class, id)); + } + + @Transactional + public SubscriptionEntity create(SubscriptionEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkName(entity.getName()); + return repository.save(entity); + } + + @Transactional + public SubscriptionEntity update(Long id, SubscriptionEntity entity) { + final SubscriptionEntity existsEntity = get(id); + checkName(entity.getName()); + existsEntity.setName(entity.getName()); + return repository.save(existsEntity); + } + + @Transactional + public SubscriptionEntity delete(Long id) { + final SubscriptionEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } + +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/api/TypeController.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/api/TypeController.java new file mode 100644 index 0000000..9966a15 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/api/TypeController.java @@ -0,0 +1,68 @@ +package com.example.demo.types.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/type") +public class TypeController { + private final TypeService typeService; + private final ModelMapper modelMapper; + + public TypeController(TypeService typeService, ModelMapper modelMapper) { + this.typeService = typeService; + this.modelMapper = modelMapper; + } + + private TypeDto toDto(TypeEntity entity) { + return modelMapper.map(entity, TypeDto.class); + } + + private TypeEntity toEntity(TypeDto dto) { + return modelMapper.map(dto, TypeEntity.class); + } + + @GetMapping + public List getAll() { + return typeService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{id}") + public TypeDto get(@PathVariable(name = "id") Long id) { + return toDto(typeService.get(id)); + } + + @PostMapping + public TypeDto create(@RequestBody @Valid TypeDto dto) { + return toDto(typeService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public TypeDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid TypeDto dto) { + return toDto(typeService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public TypeDto delete(@PathVariable(name = "id") Long id) { + return toDto(typeService.delete(id)); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/api/TypeDto.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/api/TypeDto.java new file mode 100644 index 0000000..9ed9864 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/api/TypeDto.java @@ -0,0 +1,30 @@ +package com.example.demo.types.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class TypeDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long id; + @NotBlank + @Size(min = 5, max = 50) + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/model/TypeEntity.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/model/TypeEntity.java new file mode 100644 index 0000000..14df9ea --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/model/TypeEntity.java @@ -0,0 +1,48 @@ +package com.example.demo.types.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = "types") +public class TypeEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 50) + private String name; + + public TypeEntity() { + } + + public TypeEntity(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final TypeEntity other = (TypeEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getName(), name); + } + +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/repository/TypeRepository.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/repository/TypeRepository.java new file mode 100644 index 0000000..9fea284 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/repository/TypeRepository.java @@ -0,0 +1,11 @@ +package com.example.demo.types.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +import com.example.demo.types.model.TypeEntity; + +public interface TypeRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/service/TypeService.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/service/TypeService.java new file mode 100644 index 0000000..f098a64 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/types/service/TypeService.java @@ -0,0 +1,62 @@ +package com.example.demo.types.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.repository.TypeRepository; + +@Service +public class TypeService { + private final TypeRepository repository; + + public TypeService(TypeRepository repository) { + this.repository = repository; + } + + private void checkName(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Type with name %s is already exists", name)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public TypeEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(TypeEntity.class, id)); + } + + @Transactional + public TypeEntity create(TypeEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkName(entity.getName()); + return repository.save(entity); + } + + @Transactional + public TypeEntity update(Long id, TypeEntity entity) { + final TypeEntity existsEntity = get(id); + checkName(entity.getName()); + existsEntity.setName(entity.getName()); + return repository.save(existsEntity); + } + + @Transactional + public TypeEntity delete(Long id) { + final TypeEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/api/UserController.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/api/UserController.java new file mode 100644 index 0000000..75e0f35 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/api/UserController.java @@ -0,0 +1,109 @@ +package com.example.demo.users.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.api.PageDto; +import com.example.demo.core.api.PageDtoMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.subscriptions.api.SubscriptionDto; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/user") +public class UserController { + private final UserService userService; + private final ModelMapper modelMapper; + + public UserController(UserService userService, ModelMapper modelMapper) { + this.userService = userService; + this.modelMapper = modelMapper; + } + + private UserDto toDto(UserEntity entity) { + return modelMapper.map(entity, UserDto.class); + } + + private SubscriptionDto toSubscriptionDto(SubscriptionEntity entity) { + return modelMapper.map(entity, SubscriptionDto.class); + } + + private UserEntity toEntity(UserDto dto) { + return modelMapper.map(dto, UserEntity.class); + } + + @GetMapping + public PageDto getAll( + @RequestParam(name = "page", defaultValue = "0") int page, + @RequestParam(name = "size", defaultValue = Constants.DEFAULT_PAGE_SIZE) int size) { + return PageDtoMapper.toDto(userService.getAll(page, size), this::toDto); + } + + @GetMapping("/{id}") + public UserDto get(@PathVariable(name = "id") Long id) { + return toDto(userService.get(id)); + } + + @PostMapping + public UserDto create(@RequestBody @Valid UserDto dto) { + return toDto(userService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public UserDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid UserDto dto) { + return toDto(userService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public UserDto delete(@PathVariable(name = "id") Long id) { + return toDto(userService.delete(id)); + } + + @GetMapping("/{id}/subscription") + public List getUserSubscriptions(@PathVariable(name = "id") Long id) { + return userService.getUserSubscriptions(id).stream() + .map(this::toSubscriptionDto) + .toList(); + } + + @PostMapping("/{id}/subscription") + public List enableUserSubscriptions( + @PathVariable(name = "id") Long id, + @RequestParam(name = "subscriptions", defaultValue = "") List subscriptionsIds) { + return userService.enableUserSubscriptions(id, subscriptionsIds).stream() + .map(this::toSubscriptionDto) + .toList(); + } + + @DeleteMapping("/{id}/subscription") + public List disableUserSubscriptions( + @PathVariable(name = "id") Long id, + @RequestParam(name = "subscriptions", defaultValue = "") List subscriptionsIds) { + return userService.disableUserSubscriptions(id, subscriptionsIds).stream() + .map(this::toSubscriptionDto) + .toList(); + } + + @DeleteMapping("/{id}/subscription/all") + public List deleteAllUserSubscriptions(@PathVariable(name = "id") Long id) { + return userService.deleteAllUserSubscriptions(id).stream() + .map(this::toSubscriptionDto) + .toList(); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/api/UserDto.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/api/UserDto.java new file mode 100644 index 0000000..7288ee9 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/api/UserDto.java @@ -0,0 +1,31 @@ +package com.example.demo.users.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class UserDto { + @JsonProperty(access = Access.READ_ONLY) + private Long id; + @NotBlank + @Size(min = 3, max = 20) + private String login; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/model/UserEntity.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/model/UserEntity.java new file mode 100644 index 0000000..e1c48f7 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/model/UserEntity.java @@ -0,0 +1,91 @@ +package com.example.demo.users.model; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.usersubscription.model.UserSubscriptionEntity; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users") +public class UserEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 20) + private String login; + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + @OrderBy("id ASC") + private Set orders = new HashSet<>(); + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + @OrderBy("id ASC") + private Set userSubscriptions = new HashSet<>(); + + public UserEntity() { + } + + public UserEntity(String login) { + this.login = login; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public Set getOrders() { + return orders; + } + + public void addOrder(OrderEntity order) { + if (order.getUser() != this) { + order.setUser(this); + } + orders.add(order); + } + + public Set getUserSubscriptions() { + return userSubscriptions; + } + + public void addSubscription(UserSubscriptionEntity userSubscription) { + if (userSubscription.getUser() != this) { + userSubscription.setUser(this); + } + userSubscriptions.add(userSubscription); + } + + public void deleteSubscription(UserSubscriptionEntity userSubscription) { + if (userSubscription.getUser() != this) { + return; + } + userSubscriptions.remove(userSubscription); + } + + @Override + public int hashCode() { + return Objects.hash(id, login, orders, userSubscriptions); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserEntity other = (UserEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getLogin(), login) + && Objects.equals(other.getOrders(), orders) + && Objects.equals(other.getUserSubscriptions(), userSubscriptions); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/repository/UserRepository.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/repository/UserRepository.java new file mode 100644 index 0000000..9bf3cae --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/repository/UserRepository.java @@ -0,0 +1,13 @@ +package com.example.demo.users.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.example.demo.users.model.UserEntity; + +public interface UserRepository + extends CrudRepository, PagingAndSortingRepository { + Optional findByLoginIgnoreCase(String login); +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/service/UserService.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/service/UserService.java new file mode 100644 index 0000000..00fd3ef --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/users/service/UserService.java @@ -0,0 +1,138 @@ +package com.example.demo.users.service; + +import java.util.List; +import java.util.Objects; +import java.util.stream.StreamSupport; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.repository.UserRepository; +import com.example.demo.usersubscription.model.UserSubscriptionEntity; + +@Service +public class UserService { + private final UserRepository repository; + private final SubscriptionService subscriptionService; + + public UserService( + UserRepository repository, + SubscriptionService subscriptionService) { + this.repository = repository; + this.subscriptionService = subscriptionService; + } + + private void checkLogin(String login) { + if (repository.findByLoginIgnoreCase(login).isPresent()) { + throw new IllegalArgumentException( + String.format("User with login %s is already exists", login)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public Page getAll(int page, int size) { + return repository.findAll(PageRequest.of(page, size)); + } + + @Transactional(readOnly = true) + public UserEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(UserEntity.class, id)); + } + + @Transactional + public UserEntity create(UserEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkLogin(entity.getLogin()); + repository.save(entity); + subscriptionService.getAll().forEach(subscription -> { + final UserSubscriptionEntity userSubscription = new UserSubscriptionEntity(entity, subscription, true); + userSubscription.setUser(entity); + userSubscription.setSubscription(subscription); + }); + return repository.save(entity); + } + + @Transactional + public UserEntity update(long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + checkLogin(entity.getLogin()); + existsEntity.setLogin(entity.getLogin()); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public UserEntity delete(long id) { + final UserEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } + + @Transactional(readOnly = true) + public List getUserSubscriptions(long id) { + return get(id).getUserSubscriptions().stream() + .filter(UserSubscriptionEntity::isActive) + .map(UserSubscriptionEntity::getSubscription) + .toList(); + } + + private List changeUserSubscriptionsState( + UserEntity existsUser, + List subscriptionsIds, + boolean state) { + return existsUser.getUserSubscriptions().stream() + .filter(subscription -> Objects.nonNull(subscription.getSubscription())) + .filter(subscription -> subscriptionsIds.contains(subscription.getSubscription().getId())) + .filter(subscription -> subscription.isActive() == !state) + .map(subscription -> { + subscription.setActive(state); + return subscription.getSubscription(); + }) + .toList(); + } + + @Transactional + public List enableUserSubscriptions(long id, List subscriptionsIds) { + final UserEntity existsUser = get(id); + final List changedSubscriptions = changeUserSubscriptionsState( + existsUser, subscriptionsIds, true); + repository.save(existsUser); + return changedSubscriptions; + } + + @Transactional + public List disableUserSubscriptions(long id, List subscriptionsIds) { + final UserEntity existsUser = get(id); + final List changedSubscriptions = changeUserSubscriptionsState( + existsUser, subscriptionsIds, false); + repository.save(existsUser); + return changedSubscriptions; + } + + @Transactional + public List deleteAllUserSubscriptions(long id) { + final UserEntity existsUser = get(id); + final List subscriptions = existsUser.getUserSubscriptions().stream() + .filter(subscription -> Objects.nonNull(subscription.getSubscription())) + .toList(); + subscriptions.forEach(existsUser::deleteSubscription); + repository.save(existsUser); + return subscriptions.stream() + .map(UserSubscriptionEntity::getSubscription) + .toList(); + } +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionEntity.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionEntity.java new file mode 100644 index 0000000..1f280f9 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionEntity.java @@ -0,0 +1,95 @@ +package com.example.demo.usersubscription.model; + +import java.util.Objects; + +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.users.model.UserEntity; + +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users_subscriptions") +public class UserSubscriptionEntity { + @EmbeddedId + private UserSubscriptionId id = new UserSubscriptionId(); + @ManyToOne + @MapsId("userId") + @JoinColumn(name = "user_id") + private UserEntity user; + @ManyToOne + @MapsId("subscriptionId") + @JoinColumn(name = "subscription_id") + private SubscriptionEntity subscription; + private boolean active; + + public UserSubscriptionEntity() { + } + + public UserSubscriptionEntity(UserEntity user, SubscriptionEntity subscription, boolean active) { + this.user = user; + this.subscription = subscription; + this.active = active; + } + + public UserSubscriptionId getId() { + return id; + } + + public void setId(UserSubscriptionId id) { + this.id = id; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + if (!user.getUserSubscriptions().contains(this)) { + user.getUserSubscriptions().add(this); + } + } + + public SubscriptionEntity getSubscription() { + return subscription; + } + + public void setSubscription(SubscriptionEntity subscription) { + this.subscription = subscription; + if (!subscription.getUserSubscriptions().contains(this)) { + subscription.getUserSubscriptions().add(this); + } + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + @Override + public int hashCode() { + return Objects.hash(id, user.getId(), subscription.getId(), active); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserSubscriptionEntity other = (UserSubscriptionEntity) obj; + return Objects.equals(id, other.id) + && Objects.equals(user.getId(), other.user.getId()) + && Objects.equals(subscription.getId(), other.subscription.getId()) + && active == other.active; + } + +} diff --git a/laba3.1/Лекция4-src/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionId.java b/laba3.1/Лекция4-src/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionId.java new file mode 100644 index 0000000..0b23f19 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionId.java @@ -0,0 +1,55 @@ +package com.example.demo.usersubscription.model; + +import java.util.Objects; +import java.util.Optional; + +import jakarta.persistence.Embeddable; + +@Embeddable +public class UserSubscriptionId { + private Long userId; + private Long subscriptionId; + + public UserSubscriptionId() { + } + + public UserSubscriptionId(Long userId, Long subscriptionId) { + this.userId = userId; + this.subscriptionId = subscriptionId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getSubscriptionId() { + return subscriptionId; + } + + public void setSubscriptionId(Long subscriptionId) { + this.subscriptionId = subscriptionId; + } + + @Override + public int hashCode() { + return Objects.hash( + Optional.ofNullable(userId).orElse(0L), + Optional.ofNullable(subscriptionId).orElse(0L)); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserSubscriptionId other = (UserSubscriptionId) obj; + return Objects.equals(userId, other.userId) + && Objects.equals(subscriptionId, other.subscriptionId); + } + +} diff --git a/laba3.1/Лекция4-src/src/main/resources/application-dev.properties b/laba3.1/Лекция4-src/src/main/resources/application-dev.properties new file mode 100644 index 0000000..4ed3960 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/application-dev.properties @@ -0,0 +1,8 @@ +# Dev H2 JPA Settings +spring.datasource.url=jdbc:h2:file:./data +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver + +# H2 console +spring.h2.console.enabled=true \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/application-prod.properties b/laba3.1/Лекция4-src/src/main/resources/application-prod.properties new file mode 100644 index 0000000..aaf9d7d --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/application-prod.properties @@ -0,0 +1,9 @@ +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=INFO + +# Prod PostgreSQL JPA Settings +spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/demo-app +spring.datasource.username=postgres +spring.datasource.password=postgres +spring.datasource.driver-class-name=org.postgresql.Driver diff --git a/laba3.1/Лекция4-src/src/main/resources/application.properties b/laba3.1/Лекция4-src/src/main/resources/application.properties new file mode 100644 index 0000000..8f5373c --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/application.properties @@ -0,0 +1,21 @@ +# Server +spring.main.banner-mode=off +server.port=8080 + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# Set default orofile +spring.profiles.default=dev + +# Common JPA Settings +spring.jpa.hibernate.ddl-auto=validate +spring.jpa.open-in-view=false +# spring.jpa.show-sql=true +# spring.jpa.properties.hibernate.format_sql=true + +# Liquibase Settings +spring.liquibase.enabled=true +spring.liquibase.drop-first=false +spring.liquibase.change-log=classpath:db/changelog.xml \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/0-sequence.xml b/laba3.1/Лекция4-src/src/main/resources/db/0-sequence.xml new file mode 100644 index 0000000..a790c65 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/0-sequence.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/1-types-data.xml b/laba3.1/Лекция4-src/src/main/resources/db/1-types-data.xml new file mode 100644 index 0000000..3af16a8 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/1-types-data.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/1-types-schema.xml b/laba3.1/Лекция4-src/src/main/resources/db/1-types-schema.xml new file mode 100644 index 0000000..152834b --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/1-types-schema.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/2-items-data.xml b/laba3.1/Лекция4-src/src/main/resources/db/2-items-data.xml new file mode 100644 index 0000000..8688505 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/2-items-data.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/2-items-schema.xml b/laba3.1/Лекция4-src/src/main/resources/db/2-items-schema.xml new file mode 100644 index 0000000..85ab9e2 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/2-items-schema.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/3-users-constraint.xml b/laba3.1/Лекция4-src/src/main/resources/db/3-users-constraint.xml new file mode 100644 index 0000000..69b1dfb --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/3-users-constraint.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/3-users-data.xml b/laba3.1/Лекция4-src/src/main/resources/db/3-users-data.xml new file mode 100644 index 0000000..516f72c --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/3-users-data.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + user_id IS NULL + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/3-users-schema.xml b/laba3.1/Лекция4-src/src/main/resources/db/3-users-schema.xml new file mode 100644 index 0000000..88ade2d --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/3-users-schema.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/4-items-to-orders.xml b/laba3.1/Лекция4-src/src/main/resources/db/4-items-to-orders.xml new file mode 100644 index 0000000..639b9c5 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/4-items-to-orders.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/5-subscriptions-data.xml b/laba3.1/Лекция4-src/src/main/resources/db/5-subscriptions-data.xml new file mode 100644 index 0000000..0c42c84 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/5-subscriptions-data.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/5-subscriptions-schema.xml b/laba3.1/Лекция4-src/src/main/resources/db/5-subscriptions-schema.xml new file mode 100644 index 0000000..f6cfeb9 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/5-subscriptions-schema.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/6-users-subscriptions-data.xml b/laba3.1/Лекция4-src/src/main/resources/db/6-users-subscriptions-data.xml new file mode 100644 index 0000000..0eab1dd --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/6-users-subscriptions-data.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/6-users-subscriptions-schema.xml b/laba3.1/Лекция4-src/src/main/resources/db/6-users-subscriptions-schema.xml new file mode 100644 index 0000000..49624e0 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/6-users-subscriptions-schema.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/main/resources/db/changelog.xml b/laba3.1/Лекция4-src/src/main/resources/db/changelog.xml new file mode 100644 index 0000000..2075b39 --- /dev/null +++ b/laba3.1/Лекция4-src/src/main/resources/db/changelog.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/laba3.1/Лекция4-src/src/test/java/com/example/demo/TypeServiceTests.java b/laba3.1/Лекция4-src/src/test/java/com/example/demo/TypeServiceTests.java new file mode 100644 index 0000000..10043cd --- /dev/null +++ b/laba3.1/Лекция4-src/src/test/java/com/example/demo/TypeServiceTests.java @@ -0,0 +1,79 @@ +package com.example.demo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; + +@SpringBootTest +class TypeServiceTests { + @Autowired + private TypeService typeService; + + private TypeEntity type; + + @BeforeEach + void createData() { + removeData(); + + type = typeService.create(new TypeEntity("Ноутбук")); + typeService.create(new TypeEntity("Телефон")); + typeService.create(new TypeEntity("Игровая приставка")); + } + + @AfterEach + void removeData() { + typeService.getAll().forEach(item -> typeService.delete(item.getId())); + } + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> typeService.get(0L)); + } + + @Test + void createTest() { + Assertions.assertEquals(3, typeService.getAll().size()); + Assertions.assertEquals(type, typeService.get(type.getId())); + } + + @Test + void createNotUniqueTest() { + final TypeEntity nonUniqueType = new TypeEntity("Ноутбук"); + Assertions.assertThrows(IllegalArgumentException.class, () -> typeService.create(nonUniqueType)); + } + + @Test + void createNullableTest() { + final TypeEntity nullableType = new TypeEntity(null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> typeService.create(nullableType)); + } + + @Test + void updateTest() { + final String test = "TEST"; + final String oldName = type.getName(); + final TypeEntity newEntity = typeService.update(type.getId(), new TypeEntity(test)); + Assertions.assertEquals(3, typeService.getAll().size()); + Assertions.assertEquals(newEntity, typeService.get(type.getId())); + Assertions.assertEquals(test, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + } + + @Test + void deleteTest() { + typeService.delete(type.getId()); + Assertions.assertEquals(2, typeService.getAll().size()); + + final TypeEntity newEntity = typeService.create(new TypeEntity(type.getName())); + Assertions.assertEquals(3, typeService.getAll().size()); + Assertions.assertNotEquals(type.getId(), newEntity.getId()); + } +} diff --git a/laba3.1/Лекция4-src/src/test/java/com/example/demo/UserOrderServiceTest.java b/laba3.1/Лекция4-src/src/test/java/com/example/demo/UserOrderServiceTest.java new file mode 100644 index 0000000..5305797 --- /dev/null +++ b/laba3.1/Лекция4-src/src/test/java/com/example/demo/UserOrderServiceTest.java @@ -0,0 +1,140 @@ +package com.example.demo; + +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.service.OrderService; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.persistence.EntityManager; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class UserOrderServiceTest { + @Autowired + private EntityManager entityManager; + @Autowired + private TypeService typeService; + @Autowired + private SubscriptionService subscriptionService; + @Autowired + private OrderService orderService; + @Autowired + private UserService userService; + + private TypeEntity type1; + private TypeEntity type2; + private TypeEntity type3; + + private SubscriptionEntity subscription; + + private UserEntity user1; + private UserEntity user2; + + @BeforeEach + void createData() { + removeData(); + + type1 = typeService.create(new TypeEntity("Ноутбук")); + type2 = typeService.create(new TypeEntity("Телефон")); + type3 = typeService.create(new TypeEntity("Игровая приставка")); + + subscription = subscriptionService.create(new SubscriptionEntity("Подписка 1")); + subscriptionService.create(new SubscriptionEntity("Подписка 2")); + subscriptionService.create(new SubscriptionEntity("Подписка 3")); + + user1 = userService.create(new UserEntity("user1")); + user2 = userService.create(new UserEntity("user2")); + + final var orders = List.of( + new OrderEntity(type1, 49999.00, 20), + new OrderEntity(type1, 129999.00, 3), + new OrderEntity(type2, 15450.50, 30), + new OrderEntity(type2, 69900.50, 10), + new OrderEntity(type2, 150000.00, 6), + new OrderEntity(type3, 75000.00, 6), + new OrderEntity(type3, 67800.00, 3)); + orders.forEach(order -> orderService.create(user1.getId(), order)); + } + + @AfterEach + void removeData() { + userService.getAll().forEach(item -> userService.delete(item.getId())); + typeService.getAll().forEach(item -> typeService.delete(item.getId())); + subscriptionService.getAll().forEach(item -> subscriptionService.delete(item.getId())); + } + + @Test + @Order(1) + void createTest() { + Assertions.assertEquals(7, orderService.getAll(user1.getId(), 0).size()); + Assertions.assertEquals(0, orderService.getAll(user2.getId(), 0).size()); + } + + @Test + @Order(2) + void orderFilterTest() { + Assertions.assertEquals(2, orderService.getAll(user1.getId(), type1.getId()).size()); + Assertions.assertEquals(3, orderService.getAll(user1.getId(), type2.getId()).size()); + Assertions.assertEquals(2, orderService.getAll(user1.getId(), type3.getId()).size()); + } + + @Test + @Order(3) + void subscriptionsTest() { + Assertions.assertEquals(3, userService.getUserSubscriptions(user1.getId()).size()); + } + + @Test + @Order(4) + void subscriptionDisableTest() { + userService.disableUserSubscriptions(user1.getId(), List.of(subscription.getId())); + Assertions.assertEquals(subscriptionService.getAll().size() - 1, + userService.getUserSubscriptions(user1.getId()).size()); + } + + @Test + @Order(5) + void subscriptionEnableTest() { + userService.enableUserSubscriptions(user1.getId(), List.of(subscription.getId())); + Assertions.assertEquals(subscriptionService.getAll().size(), + userService.getUserSubscriptions(user1.getId()).size()); + } + + @Test + @Order(6) + void subscriptionsDeleteTest() { + userService.deleteAllUserSubscriptions(user1.getId()); + Assertions.assertTrue(userService.getUserSubscriptions(user1.getId()).isEmpty()); + } + + @Test + @Order(6) + void userCascadeDeleteTest() { + userService.delete(user1.getId()); + final var orders = entityManager.createQuery( + "select count(o) from OrderEntity o where o.user.id = :userId"); + orders.setParameter("userId", user1.getId()); + Assertions.assertEquals(0, orders.getFirstResult()); + final var subscriptions = entityManager.createQuery( + "select count(us) from UserSubscriptionEntity us where us.user.id = :userId"); + subscriptions.setParameter("userId", user1.getId()); + Assertions.assertEquals(0, subscriptions.getFirstResult()); + + } +} diff --git a/laba3.1/Лекция4-src/src/test/resources/application.properties b/laba3.1/Лекция4-src/src/test/resources/application.properties new file mode 100644 index 0000000..75a566b --- /dev/null +++ b/laba3.1/Лекция4-src/src/test/resources/application.properties @@ -0,0 +1,20 @@ +# Server +spring.main.banner-mode=off + +# Set default orofile +spring.profiles.default=dev + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false + +# Liquibase Settings +spring.liquibase.enabled=false \ No newline at end of file diff --git a/laba3.2/.vscode/launch.json b/laba3.2/.vscode/launch.json new file mode 100644 index 0000000..d4c695a --- /dev/null +++ b/laba3.2/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Current File", + "request": "launch", + "mainClass": "${file}" + }, + { + "type": "java", + "name": "DemoApplication", + "request": "launch", + "mainClass": "com.example.demo.DemoApplication", + "projectName": "Лекция3-src" + } + ] +} \ No newline at end of file diff --git a/laba3.2/.vscode/settings.json b/laba3.2/.vscode/settings.json new file mode 100644 index 0000000..a5eed8b --- /dev/null +++ b/laba3.2/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.dependency.packagePresentation": "hierarchical" +} \ No newline at end of file diff --git a/laba3.2/data.mv.db b/laba3.2/data.mv.db new file mode 100644 index 0000000000000000000000000000000000000000..8f6262fb488be1e53fc3e53dbaed5be5fcf85b71 GIT binary patch literal 49152 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 S{NJugnx>DKKA#", + "request": "launch", + "cwd": "${workspaceFolder}", + "mainClass": "com.example.demo.DemoApplication", + "projectName": "Лекция3-src", + "args": "", + "envFile": "${workspaceFolder}/.env" + } + ] +} \ No newline at end of file diff --git a/laba3.2/Лекция3-src/.vscode/settings.json b/laba3.2/Лекция3-src/.vscode/settings.json new file mode 100644 index 0000000..eab3db6 --- /dev/null +++ b/laba3.2/Лекция3-src/.vscode/settings.json @@ -0,0 +1,21 @@ +{ + "editor.tabSize": 4, + "editor.detectIndentation": false, + "editor.insertSpaces": true, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.formatOnType": false, + "java.compile.nullAnalysis.mode": "disabled", + "java.configuration.updateBuildConfiguration": "automatic", + "[java]": { + "editor.pasteAs.enabled": false, + }, + "gradle.nestedProjects": true, + "java.saveActions.organizeImports": true, + "java.dependency.packagePresentation": "hierarchical", + "spring-boot.ls.problem.boot2.JAVA_CONSTRUCTOR_PARAMETER_INJECTION": "WARNING", + "spring.initializr.defaultLanguage": "Java", + "java.format.settings.url": ".vscode/eclipse-formatter.xml", + "java.project.explorer.showNonJavaResources": true, + "java.codeGeneration.hashCodeEquals.useJava7Objects": true, +} \ No newline at end of file diff --git a/laba3.2/Лекция3-src/build.gradle b/laba3.2/Лекция3-src/build.gradle new file mode 100644 index 0000000..1f5a14c --- /dev/null +++ b/laba3.2/Лекция3-src/build.gradle @@ -0,0 +1,43 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.4' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +defaultTasks 'bootRun' + +jar { + enabled = false +} + +bootJar { + archiveFileName = String.format('%s-%s.jar', rootProject.name, version) +} + +assert System.properties['java.specification.version'] == '17' || '21' +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + implementation 'org.modelmapper:modelmapper:3.2.0' + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'com.h2database:h2:2.2.224' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/laba3.2/Лекция3-src/gradle/wrapper/gradle-wrapper.jar b/laba3.2/Лекция3-src/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/laba3.2/Лекция3-src/gradle/wrapper/gradle-wrapper.properties b/laba3.2/Лекция3-src/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/laba3.2/Лекция3-src/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/laba3.2/Лекция3-src/gradlew b/laba3.2/Лекция3-src/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/laba3.2/Лекция3-src/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/laba3.2/Лекция3-src/gradlew.bat b/laba3.2/Лекция3-src/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/laba3.2/Лекция3-src/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/laba3.2/Лекция3-src/package-lock.json b/laba3.2/Лекция3-src/package-lock.json new file mode 100644 index 0000000..3863983 --- /dev/null +++ b/laba3.2/Лекция3-src/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "Лекция3-src", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/laba3.2/Лекция3-src/readme.md b/laba3.2/Лекция3-src/readme.md new file mode 100644 index 0000000..6eea3e5 --- /dev/null +++ b/laba3.2/Лекция3-src/readme.md @@ -0,0 +1,18 @@ +Swagger UI: \ +http://localhost:8080/swagger-ui/index.html + +H2 Console: \ +http://localhost:8080/h2-console + +JDBC URL: jdbc:h2:file:./data \ +User Name: sa \ +Password: password + +Почитать: + +- Односторонние и двусторонние связи https://www.baeldung.com/jpa-hibernate-associations +- Getters и Setters для двусторонних связей https://en.wikibooks.org/wiki/Java_Persistence/OneToMany#Getters_and_Setters +- Многие-ко-многим с доп. атрибутами https://thorben-janssen.com/hibernate-tip-many-to-many-association-with-additional-attributes/ +- Многие-ко-многим с доп. атрибутами https://www.baeldung.com/jpa-many-to-many +- Каскадное удаление сущностей со связями многие-ко-многим https://www.baeldung.com/jpa-remove-entity-many-to-many +- Выбор типа коллекции для отношений вида ко-многим в JPA https://thorben-janssen.com/association-mappings-bag-list-set/ diff --git a/laba3.2/Лекция3-src/settings.gradle b/laba3.2/Лекция3-src/settings.gradle new file mode 100644 index 0000000..0a383dd --- /dev/null +++ b/laba3.2/Лекция3-src/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'demo' diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/DemoApplication.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..7a305a3 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,79 @@ +package com.example.demo; + +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.genres.service.GenreService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.service.SubscribeService; +import com.example.demo.userfilms.model.UserFilmEntity; +import com.example.demo.userfilms.service.UserFilmService; +import com.example.demo.genres.model.GenreEntity; + +@SpringBootApplication +public class DemoApplication implements CommandLineRunner { + private final Logger log = LoggerFactory.getLogger(DemoApplication.class); + + private final FilmService filmService; + private final UserService userService; + private final SubscribeService subscribeService; + private final GenreService genreService; + private final UserFilmService userFilmService; + + public DemoApplication( + FilmService filmService, + UserService userService, + SubscribeService subscribeService, + GenreService genreService, + UserFilmService userFilmService) { + this.filmService = filmService; + this.userService = userService; + this.subscribeService = subscribeService; + this.genreService = genreService; + this.userFilmService = userFilmService; + } + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @Override + public void run(String... args) throws Exception { + if (args.length > 0 && Objects.equals("--populate", args[0])) { + log.info("Create default type values"); + final var genre1 = genreService.create(new GenreEntity("Комедия")); + final var genre2 = genreService.create(new GenreEntity("Триллер")); + final var genre3 = genreService.create(new GenreEntity("Драма")); + + log.info("Create default film values"); + final var film1 = filmService.create(new FilmEntity("film 1", genre1, 123d, 10)); + final var film2 = filmService.create(new FilmEntity("film 2", genre2, 123d, 10)); + final var film3 = filmService.create(new FilmEntity("film 3", genre3, 123d, 10)); + final var film4 = filmService.create(new FilmEntity("film 4", genre3, 123d, 10)); + final var film5 = filmService.create(new FilmEntity("film 5", genre3, 123d, 10)); + + log.info("Create default subscribe values"); + final var subscribe1 = subscribeService.create(new SubscribeEntity("ffffff", 100d)); + + log.info("Create default user values"); + final var user = userService.create(new UserEntity("user1", subscribe1, "mail1", "phone1")); + userService.create(new UserEntity("user2", subscribe1, "mail2", "phone2")); + + log.info("Create default userFilms"); + userFilmService.create(new UserFilmEntity(user, film1, false)); + userFilmService.create(new UserFilmEntity(user, film2, false)); + userFilmService.create(new UserFilmEntity(user, film3, false)); + userFilmService.create(new UserFilmEntity(user, film4, false)); + userFilmService.create(new UserFilmEntity(user, film5, false)); + } + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/Constants.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/Constants.java new file mode 100644 index 0000000..2474c0f --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/Constants.java @@ -0,0 +1,10 @@ +package com.example.demo.core.configuration; + +public class Constants { + public static final String SEQUENCE_NAME = "hibernate_sequence"; + + public static final String API_URL = "/api/1.0"; + + private Constants() { + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java new file mode 100644 index 0000000..a5ad6f3 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java @@ -0,0 +1,13 @@ +package com.example.demo.core.configuration; + +import org.modelmapper.ModelMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MapperConfiguration { + @Bean + ModelMapper modelMapper() { + return new ModelMapper(); + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java new file mode 100644 index 0000000..762e85a --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/configuration/WebConfiguration.java @@ -0,0 +1,15 @@ +package com.example.demo.core.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.NonNull; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfiguration implements WebMvcConfigurer { + @Override + public void addCorsMappings(@NonNull CorsRegistry registry) { + registry.addMapping("/**") + .allowedMethods("GET", "POST", "PUT", "DELETE"); + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/error/NotFoundException.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/error/NotFoundException.java new file mode 100644 index 0000000..a61d118 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/error/NotFoundException.java @@ -0,0 +1,7 @@ +package com.example.demo.core.error; + +public class NotFoundException extends RuntimeException { + public NotFoundException(Class clazz, Long id) { + super(String.format("%s with id [%s] is not found or not exists", clazz.getSimpleName(), id)); + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/model/BaseEntity.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/model/BaseEntity.java new file mode 100644 index 0000000..eba74ad --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/core/model/BaseEntity.java @@ -0,0 +1,28 @@ +package com.example.demo.core.model; + +import com.example.demo.core.configuration.Constants; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.SequenceGenerator; + +@MappedSuperclass +public abstract class BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = Constants.SEQUENCE_NAME) + @SequenceGenerator(name = Constants.SEQUENCE_NAME, sequenceName = Constants.SEQUENCE_NAME, allocationSize = 1) + protected Long id; + + protected BaseEntity() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/api/FilmController.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/api/FilmController.java new file mode 100644 index 0000000..35e93bf --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/api/FilmController.java @@ -0,0 +1,73 @@ +package com.example.demo.films.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.genres.service.GenreService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/film") +public class FilmController { + private final FilmService filmService; + private final GenreService genreService; + private final ModelMapper modelMapper; + + public FilmController(FilmService filmService, GenreService genreService, ModelMapper modelMapper) { + this.filmService = filmService; + this.genreService = genreService; + this.modelMapper = modelMapper; + } + + private FilmDto toDto(FilmEntity entity) { + return modelMapper.map(entity, FilmDto.class); + } + + private FilmEntity toEntity(FilmDto dto) { + final FilmEntity entity = modelMapper.map(dto, FilmEntity.class); + entity.setGenre(genreService.get(dto.getGenreId())); + return entity; + } + + @GetMapping + public List getAll() { + return filmService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{id}") + public FilmDto get(@PathVariable(name = "id") Long id) { + return toDto(filmService.get(id)); + } + + @PostMapping + public FilmDto create(@RequestBody @Valid FilmDto dto) { + return toDto(filmService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public FilmDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid FilmDto dto) { + return toDto(filmService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public FilmDto delete(@PathVariable(name = "id") Long id) { + return toDto(filmService.delete(id)); + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/api/FilmDto.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/api/FilmDto.java new file mode 100644 index 0000000..0a5ca24 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/api/FilmDto.java @@ -0,0 +1,70 @@ +package com.example.demo.films.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public class FilmDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long id; + @NotBlank + @Size(min = 5, max = 50) + private String name; + @NotNull + @Min(1) + private Long genreId; + @NotNull + @Min(1) + private Double price; + @NotNull + @Min(1) + private Integer discount; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getGenreId() { + return genreId; + } + + public void setGenreId(Long genreId) { + this.genreId = genreId; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getDiscount() { + return discount; + } + + public void setDiscount(Integer discount) { + this.discount = discount; + } + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Double getSum() { + return price - ((price * discount) * 0.01); + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/model/FilmEntity.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/model/FilmEntity.java new file mode 100644 index 0000000..5f41b62 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/model/FilmEntity.java @@ -0,0 +1,87 @@ +package com.example.demo.films.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.genres.model.GenreEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "films") +public class FilmEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 50) + private String name; + @ManyToOne + @JoinColumn(name = "genreId", nullable = false) + private GenreEntity genre; + @Column(nullable = false) + private Double price; + @Column(nullable = false) + private Integer discount; + + public FilmEntity() { + } + + public FilmEntity(String name, GenreEntity genre, Double price, Integer discount) { + this.name = name; + this.genre = genre; + this.price = price; + this.discount = discount; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public GenreEntity getGenre() { + return genre; + } + + public void setGenre(GenreEntity genre) { + this.genre = genre; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getDiscount() { + return discount; + } + + public void setDiscount(Integer discount) { + this.discount = discount; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, genre, price, discount); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + FilmEntity other = (FilmEntity) obj; + return Objects.equals(id, other.id) + && Objects.equals(name, other.name) + && Objects.equals(genre, other.genre) + && Objects.equals(price, other.price) + && Objects.equals(discount, other.discount); + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/repository/FilmRepository.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/repository/FilmRepository.java new file mode 100644 index 0000000..495e722 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/repository/FilmRepository.java @@ -0,0 +1,14 @@ +package com.example.demo.films.repository; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +import com.example.demo.films.model.FilmEntity; + +public interface FilmRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); + + List findByGenreId(long genreId); +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/service/FilmService.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/service/FilmService.java new file mode 100644 index 0000000..ed82706 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/films/service/FilmService.java @@ -0,0 +1,72 @@ +package com.example.demo.films.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.repository.FilmRepository; + +@Service +public class FilmService { + private final FilmRepository repository; + + public FilmService(FilmRepository repository) { + this.repository = repository; + } + + private void checkName(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Type with name %s is already exists", name)); + } + } + + // @Transactional(readOnly = true) + // public List getAll(long genreId) { + // return repository.findByGenreId(genreId); + // } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), + false).toList(); + } + + @Transactional(readOnly = true) + public FilmEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(FilmEntity.class, id)); + } + + @Transactional + public FilmEntity create(FilmEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkName(entity.getName()); + return repository.save(entity); + } + + @Transactional + public FilmEntity update(Long id, FilmEntity entity) { + final FilmEntity existsEntity = get(id); + checkName(entity.getName()); + existsEntity.setName(entity.getName()); + existsEntity.setPrice(entity.getPrice()); + existsEntity.setDiscount(entity.getDiscount()); + existsEntity.setGenre(entity.getGenre()); + return repository.save(existsEntity); + } + + @Transactional + public FilmEntity delete(Long id) { + final FilmEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } + +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreController.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreController.java new file mode 100644 index 0000000..3d483b1 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreController.java @@ -0,0 +1,68 @@ +package com.example.demo.genres.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/genre") +public class GenreController { + private final GenreService genreService; + private final ModelMapper modelMapper; + + public GenreController(GenreService genreService, ModelMapper modelMapper) { + this.genreService = genreService; + this.modelMapper = modelMapper; + } + + private GenreDto toDto(GenreEntity entity) { + return modelMapper.map(entity, GenreDto.class); + } + + private GenreEntity toEntity(GenreDto dto) { + return modelMapper.map(dto, GenreEntity.class); + } + + @GetMapping + public List getAll() { + return genreService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{id}") + public GenreDto get(@PathVariable(name = "id") Long id) { + return toDto(genreService.get(id)); + } + + @PostMapping + public GenreDto create(@RequestBody @Valid GenreDto dto) { + return toDto(genreService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public GenreDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid GenreDto dto) { + return toDto(genreService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public GenreDto delete(@PathVariable(name = "id") Long id) { + return toDto(genreService.delete(id)); + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreDto.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreDto.java new file mode 100644 index 0000000..6598b12 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/api/GenreDto.java @@ -0,0 +1,30 @@ +package com.example.demo.genres.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class GenreDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long id; + @NotBlank + @Size(min = 5, max = 50) + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/model/GenreEntity.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/model/GenreEntity.java new file mode 100644 index 0000000..b36a926 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/model/GenreEntity.java @@ -0,0 +1,48 @@ +package com.example.demo.genres.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = "genres") +public class GenreEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 50) + private String name; + + public GenreEntity() { + } + + public GenreEntity(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final GenreEntity other = (GenreEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getName(), name); + } + +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java new file mode 100644 index 0000000..13d6932 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/repository/GenreRepository.java @@ -0,0 +1,11 @@ +package com.example.demo.genres.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +import com.example.demo.genres.model.GenreEntity; + +public interface GenreRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/service/GenreService.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/service/GenreService.java new file mode 100644 index 0000000..57358b0 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/genres/service/GenreService.java @@ -0,0 +1,62 @@ +package com.example.demo.genres.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.repository.GenreRepository; + +@Service +public class GenreService { + private final GenreRepository repository; + + public GenreService(GenreRepository repository) { + this.repository = repository; + } + + private void checkName(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Genre with name %s is already exists", name)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public GenreEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(GenreEntity.class, id)); + } + + @Transactional + public GenreEntity create(GenreEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkName(entity.getName()); + return repository.save(entity); + } + + @Transactional + public GenreEntity update(Long id, GenreEntity entity) { + final GenreEntity existsEntity = get(id); + checkName(entity.getName()); + existsEntity.setName(entity.getName()); + return repository.save(existsEntity); + } + + @Transactional + public GenreEntity delete(Long id) { + final GenreEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java new file mode 100644 index 0000000..595897f --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeController.java @@ -0,0 +1,68 @@ +package com.example.demo.subscribes.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.service.SubscribeService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/subscribe") +public class SubscribeController { + private final SubscribeService subscribeService; + private final ModelMapper modelMapper; + + public SubscribeController(SubscribeService subscribeService, ModelMapper modelMapper) { + this.subscribeService = subscribeService; + this.modelMapper = modelMapper; + } + + private SubscribeDto toDto(SubscribeEntity entity) { + return modelMapper.map(entity, SubscribeDto.class); + } + + private SubscribeEntity toEntity(SubscribeDto dto) { + return modelMapper.map(dto, SubscribeEntity.class); + } + + @GetMapping + public List getAll() { + return subscribeService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{id}") + public SubscribeDto get(@PathVariable(name = "id") Long id) { + return toDto(subscribeService.get(id)); + } + + @PostMapping + public SubscribeDto create(@RequestBody @Valid SubscribeDto dto) { + return toDto(subscribeService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public SubscribeDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid SubscribeDto dto) { + return toDto(subscribeService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public SubscribeDto delete(@PathVariable(name = "id") Long id) { + return toDto(subscribeService.delete(id)); + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java new file mode 100644 index 0000000..2a203b2 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/api/SubscribeDto.java @@ -0,0 +1,43 @@ +package com.example.demo.subscribes.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public class SubscribeDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long id; + @NotBlank + @Size(min = 5, max = 50) + private String name; + @NotNull + @Min(1) + private Double price; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java new file mode 100644 index 0000000..60bd966 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/model/SubscribeEntity.java @@ -0,0 +1,60 @@ +package com.example.demo.subscribes.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = "subscribes") +public class SubscribeEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 50) + private String name; + @Column(nullable = false) + private Double price; + + public SubscribeEntity() { + } + + public SubscribeEntity(String name, Double price) { + this.name = name; + this.price = price; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, price); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final SubscribeEntity other = (SubscribeEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getName(), name) + && Objects.equals(other.getPrice(), price); + } + +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java new file mode 100644 index 0000000..0dbfd6e --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/repository/SubscribeRepository.java @@ -0,0 +1,11 @@ +package com.example.demo.subscribes.repository; + +import java.util.Optional; + +import com.example.demo.subscribes.model.SubscribeEntity; + +import org.springframework.data.repository.CrudRepository; + +public interface SubscribeRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java new file mode 100644 index 0000000..72c99a1 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/subscribes/service/SubscribeService.java @@ -0,0 +1,63 @@ +package com.example.demo.subscribes.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.repository.SubscribeRepository; + +@Service +public class SubscribeService { + private final SubscribeRepository repository; + + public SubscribeService(SubscribeRepository repository) { + this.repository = repository; + } + + private void checkName(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Subscribe with name %s is already exists", name)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public SubscribeEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(SubscribeEntity.class, id)); + } + + @Transactional + public SubscribeEntity create(SubscribeEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkName(entity.getName()); + return repository.save(entity); + } + + @Transactional + public SubscribeEntity update(Long id, SubscribeEntity entity) { + final SubscribeEntity existsEntity = get(id); + checkName(entity.getName()); + existsEntity.setName(entity.getName()); + existsEntity.setPrice(entity.getPrice()); + return repository.save(existsEntity); + } + + @Transactional + public SubscribeEntity delete(Long id) { + final SubscribeEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmController.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmController.java new file mode 100644 index 0000000..2c2b29c --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmController.java @@ -0,0 +1,105 @@ +package com.example.demo.userfilms.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.films.service.FilmService; +import com.example.demo.userfilms.model.UserFilmEntity; +import com.example.demo.userfilms.model.UserFilmGrouped; +import com.example.demo.userfilms.service.UserFilmService; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/user/{user}/userfilm") +public class UserFilmController { + private final UserFilmService userFilmService; + private final UserService userService; + private final FilmService filmService; + private final ModelMapper modelMapper; + + public UserFilmController(UserFilmService userFilmService, UserService userService, FilmService filmService, + ModelMapper modelMapper) { + this.userFilmService = userFilmService; + this.userService = userService; + this.filmService = filmService; + this.modelMapper = modelMapper; + } + + private UserFilmDto toDto(UserFilmEntity userFilmEntity) { + return modelMapper.map(userFilmEntity, UserFilmDto.class); + } + + private UserFilmGroupedDto toGroupedDto(UserFilmGrouped entity) { + return modelMapper.map(entity, UserFilmGroupedDto.class); + } + + private UserFilmEntity toEntity(UserFilmDto dto) { + final UserFilmEntity entity = modelMapper.map(dto, UserFilmEntity.class); + entity.setUser(userService.get(dto.getUserId())); + entity.setFilm(filmService.get(dto.getFilmId())); + return entity; + } + + @GetMapping + public List getAll( + @PathVariable(name = "user") Long userId) { + return userFilmService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{film}") + public UserFilmDto get( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "film") Long filmId) { + return toDto(userFilmService.get(userId, filmId)); + } + + @PostMapping("/{film}") + public UserFilmDto create( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "film") Long filmId) { + return toDto( + userFilmService.create(new UserFilmEntity(userService.get(userId), filmService.get(filmId), false))); + } + + @PutMapping("/{id}") + public UserFilmDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid UserFilmDto dto) { + return toDto(userFilmService.update(id, toEntity(dto))); + } + + @PutMapping("/{id}/view") + public UserFilmDto updateViewed( + @PathVariable(name = "id") Long id, + @PathVariable(name = "view") Boolean view) { + return toDto(userFilmService.viewed(id, view)); + } + + @DeleteMapping("/{film}") + public UserFilmDto delete( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "film") Long filmId) { + return toDto(userFilmService.delete(userId, filmId)); + } + + @GetMapping("/total") + public List getMethodName(@PathVariable(name = "user") Long userId) { + return userFilmService.getTotal(userId).stream() + .map(this::toGroupedDto) + .toList(); + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmDto.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmDto.java new file mode 100644 index 0000000..c90dd1a --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmDto.java @@ -0,0 +1,50 @@ +package com.example.demo.userfilms.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +public class UserFilmDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long id; + @NotNull + @Min(1) + private Long userId; + @NotNull + @Min(1) + private Long filmId; + private boolean viewed; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getFilmId() { + return filmId; + } + + public void setFilmId(Long filmId) { + this.filmId = filmId; + } + + public Boolean getViewed() { + return viewed; + } + + public void setViewed(Boolean viewed) { + this.viewed = viewed; + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmGroupedDto.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmGroupedDto.java new file mode 100644 index 0000000..cc945b3 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/api/UserFilmGroupedDto.java @@ -0,0 +1,22 @@ +package com.example.demo.userfilms.api; + +public class UserFilmGroupedDto { + private Long filmCount; + private String genreName; + + public Long getfilmCount() { + return filmCount; + } + + public void setfilmCount(Long filmCount) { + this.filmCount = filmCount; + } + + public String getGenreName() { + return genreName; + } + + public void setGenreName(String genreName) { + this.genreName = genreName; + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/model/UserFilmEntity.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/model/UserFilmEntity.java new file mode 100644 index 0000000..f1519da --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/model/UserFilmEntity.java @@ -0,0 +1,87 @@ +package com.example.demo.userfilms.model; + +import com.example.demo.users.model.UserEntity; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.films.model.FilmEntity; + +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users_films") +public class UserFilmEntity extends BaseEntity { + private Long id; + @ManyToOne + @JoinColumn(name = "userId", nullable = false) + private UserEntity user; + @ManyToOne + @JoinColumn(name = "filmId", nullable = false) + private FilmEntity film; + private boolean viewed; + + public UserFilmEntity() { + } + + public UserFilmEntity(UserEntity user, FilmEntity film, boolean viewed) { + this.user = user; + this.film = film; + this.viewed = viewed; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + + } + + public FilmEntity getFilm() { + return film; + } + + public void setFilm(FilmEntity film) { + this.film = film; + + } + + public boolean isViewed() { + return viewed; + } + + public void setViewed(boolean viewed) { + this.viewed = viewed; + } + + @Override + public int hashCode() { + return Objects.hash(id, user.getId(), film.getId(), viewed); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserFilmEntity other = (UserFilmEntity) obj; + return Objects.equals(id, other.id) + && Objects.equals(user.getId(), other.user.getId()) + && Objects.equals(film.getId(), other.film.getId()) + && viewed == other.viewed; + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/model/UserFilmGrouped.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/model/UserFilmGrouped.java new file mode 100644 index 0000000..ee1b1d0 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/model/UserFilmGrouped.java @@ -0,0 +1,9 @@ +package com.example.demo.userfilms.model; + +import com.example.demo.genres.model.GenreEntity; + +public interface UserFilmGrouped { + Long getCount(); + + GenreEntity getGenre(); +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/repository/UserFilmRepository.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/repository/UserFilmRepository.java new file mode 100644 index 0000000..29980ce --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/repository/UserFilmRepository.java @@ -0,0 +1,30 @@ +package com.example.demo.userfilms.repository; + +import com.example.demo.userfilms.model.UserFilmEntity; +import com.example.demo.userfilms.model.UserFilmGrouped; + +import org.springframework.data.jpa.repository.Query; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +public interface UserFilmRepository extends CrudRepository { + Optional findOneByUserIdAndFilmId(long userId, long filmId); + + List findByUserId(long userId); + + List findByFilmId(long filmId); + + @Query("select " + + "count(*) as count, " + + "g as genre " + + "from FilmEntity f " + + "left join GenreEntity g " + + "on f.genre = g " + + "right join UserFilmEntity uf " + + "on uf.film = f and uf.user.id = ?1 " + + "group by g order by g.name") + List getFilmsTotalByGenre(long userId); +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/service/UserFilmService.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/service/UserFilmService.java new file mode 100644 index 0000000..be91860 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/userfilms/service/UserFilmService.java @@ -0,0 +1,125 @@ +package com.example.demo.userfilms.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.userfilms.model.UserFilmEntity; +import com.example.demo.userfilms.model.UserFilmGrouped; +import com.example.demo.userfilms.repository.UserFilmRepository; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@Service +public class UserFilmService { + private final UserFilmRepository userFilmRepository; + private final FilmService filmService; + private final UserService userService; + + public UserFilmService( + UserFilmRepository userFilmRepository, FilmService filmService, UserService userService) { + this.userFilmRepository = userFilmRepository; + this.filmService = filmService; + this.userService = userService; + } + + private void checkUserFilm(UserEntity user, FilmEntity film) { + if (userFilmRepository.findOneByUserIdAndFilmId(user.getId(), film.getId()).isPresent()) { + throw new IllegalArgumentException( + String.format("UserFilm with login is already exists")); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(userFilmRepository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public UserFilmEntity get(Long id) { + return userFilmRepository.findById(id) + .orElseThrow(() -> new NotFoundException(UserFilmEntity.class, id)); + } + + @Transactional(readOnly = true) + public UserFilmEntity get(Long userId, Long filmId) { + return userFilmRepository.findOneByUserIdAndFilmId(userId, filmId) + .orElseThrow(() -> new NotFoundException(UserFilmEntity.class, userId)); + } + + @Transactional + public UserFilmEntity create(UserFilmEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkUserFilm(entity.getUser(), entity.getFilm()); + return userFilmRepository.save(entity); + } + + @Transactional + public UserFilmEntity update(Long id, UserFilmEntity entity) { + final UserFilmEntity existsEntity = get(id); + existsEntity.setUser(entity.getUser()); + existsEntity.setFilm(entity.getFilm()); + existsEntity.setViewed(entity.isViewed()); + userFilmRepository.save(existsEntity); + return existsEntity; + } + + @Transactional + public UserFilmEntity delete(long userId, long filmId) { + final UserFilmEntity existsEntity = userFilmRepository.findOneByUserIdAndFilmId(userId, filmId).get(); + userFilmRepository.delete(existsEntity); + return existsEntity; + } + + @Transactional + public UserFilmEntity delete(long id) { + final UserFilmEntity existsEntity = get(id); + userFilmRepository.delete(existsEntity); + return existsEntity; + } + + @Transactional + public List addAllFilm(UserEntity user) { + List list = new ArrayList(); + filmService.getAll().forEach(film -> { + final UserFilmEntity userFilm = new UserFilmEntity(user, film, true); + userFilm.setUser(user); + userFilm.setFilm(film); + list.add(userFilm); + }); + userFilmRepository.saveAll(list); + return list; + } + + @Transactional + public UserFilmEntity update(long id, UserFilmEntity entity) { + final UserFilmEntity existsEntity = get(id); + existsEntity.setUser(entity.getUser()); + existsEntity.setFilm(entity.getFilm()); + existsEntity.setViewed(entity.isViewed()); + userFilmRepository.save(existsEntity); + return existsEntity; + } + + @Transactional + public UserFilmEntity viewed(long id, Boolean viewed) { + final UserFilmEntity existsEntity = get(id); + existsEntity.setViewed(viewed); + userFilmRepository.save(existsEntity); + return existsEntity; + } + + @Transactional(readOnly = true) + public List getTotal(long userId) { + userService.get(userId); + return userFilmRepository.getFilmsTotalByGenre(userId); + } +} \ No newline at end of file diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/api/UserController.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/api/UserController.java new file mode 100644 index 0000000..2bffe31 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/api/UserController.java @@ -0,0 +1,73 @@ +package com.example.demo.users.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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 com.example.demo.core.configuration.Constants; +import com.example.demo.subscribes.service.SubscribeService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/user") +public class UserController { + private final UserService userService; + private final SubscribeService subscribeService; + private final ModelMapper modelMapper; + + public UserController(UserService userService, SubscribeService subscribeService, ModelMapper modelMapper) { + this.userService = userService; + this.subscribeService = subscribeService; + this.modelMapper = modelMapper; + } + + private UserDto toDto(UserEntity entity) { + return modelMapper.map(entity, UserDto.class); + } + + private UserEntity toEntity(UserDto dto) { + final UserEntity entity = modelMapper.map(dto, UserEntity.class); + entity.setSubscribe(subscribeService.get(dto.getSubscribeId())); + return entity; + } + + @GetMapping + public List getAll() { + return userService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{id}") + public UserDto get(@PathVariable(name = "id") Long id) { + return toDto(userService.get(id)); + } + + @PostMapping + public UserDto create(@RequestBody @Valid UserDto dto) { + return toDto(userService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public UserDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid UserDto dto) { + return toDto(userService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public UserDto delete(@PathVariable(name = "id") Long id) { + return toDto(userService.delete(id)); + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/api/UserDto.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/api/UserDto.java new file mode 100644 index 0000000..9f927f1 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/api/UserDto.java @@ -0,0 +1,66 @@ +package com.example.demo.users.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public class UserDto { + @JsonProperty(access = Access.READ_ONLY) + private Long id; + @NotBlank + @Size(min = 3, max = 20) + private String login; + @NotBlank + @Size(min = 3, max = 50) + private String mail; + @NotBlank + @Size(min = 8, max = 30) + private String phone; + @NotNull + @Min(1) + private Long subscribeId; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public Long getSubscribeId() { + return subscribeId; + } + + public void setSubscribeId(Long subscribeId) { + this.subscribeId = subscribeId; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/model/UserEntity.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/model/UserEntity.java new file mode 100644 index 0000000..6bc5957 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/model/UserEntity.java @@ -0,0 +1,87 @@ +package com.example.demo.users.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.subscribes.model.SubscribeEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users") +public class UserEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 20) + private String login; + @ManyToOne + @JoinColumn(name = "subscribeId", nullable = false) + private SubscribeEntity subscribe; + @Column(nullable = false, unique = true, length = 50) + private String mail; + @Column(nullable = false, unique = true, length = 20) + private String phone; + + public UserEntity() { + } + + public UserEntity(String login, SubscribeEntity subscribe, String mail, String phone) { + this.login = login; + this.subscribe = subscribe; + this.mail = mail; + this.phone = phone; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public void setSubscribe(SubscribeEntity subscribe) { + this.subscribe = subscribe; + } + + public SubscribeEntity getSubscribe() { + return subscribe; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + @Override + public int hashCode() { + return Objects.hash(id, login, mail, phone, subscribe); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserEntity other = (UserEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getLogin(), login) + && Objects.equals(other.getMail(), mail) + && Objects.equals(other.getPhone(), phone) + && Objects.equals(other.getSubscribe(), subscribe); + } +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/repository/UserRepository.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/repository/UserRepository.java new file mode 100644 index 0000000..bc03567 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/repository/UserRepository.java @@ -0,0 +1,14 @@ +package com.example.demo.users.repository; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +import com.example.demo.users.model.UserEntity; + +public interface UserRepository extends CrudRepository { + Optional findByLoginIgnoreCase(String login); + + List findBySubscribeId(long subscribeId); +} diff --git a/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/service/UserService.java b/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/service/UserService.java new file mode 100644 index 0000000..7ec93b3 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/java/com/example/demo/users/service/UserService.java @@ -0,0 +1,73 @@ +package com.example.demo.users.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.repository.UserRepository; + +@Service +public class UserService { + private final UserRepository userRepository; + + public UserService( + UserRepository userRepository) { + this.userRepository = userRepository; + } + + private void checkLogin(String login) { + if (userRepository.findByLoginIgnoreCase(login).isPresent()) { + throw new IllegalArgumentException( + String.format("User with login %s is already exists", login)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(userRepository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public UserEntity get(long id) { + return userRepository.findById(id) + .orElseThrow(() -> new NotFoundException(UserEntity.class, id)); + } + + @Transactional + public UserEntity create(UserEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkLogin(entity.getLogin()); + userRepository.save(entity); + // filmService.getAll().forEach(film -> { + // final UserFilmEntity userFilm = new UserFilmEntity(entity, film, true); + // userFilm.setUser(entity); + // userFilm.setFilm(film); + // }); + return userRepository.save(entity); + } + + @Transactional + public UserEntity update(long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + checkLogin(entity.getLogin()); + existsEntity.setLogin(entity.getLogin()); + existsEntity.setMail(entity.getMail()); + existsEntity.setPhone(entity.getPhone()); + existsEntity.setSubscribe(entity.getSubscribe()); + userRepository.save(existsEntity); + return existsEntity; + } + + @Transactional + public UserEntity delete(long id) { + final UserEntity existsEntity = get(id); + userRepository.delete(existsEntity); + return existsEntity; + } +} diff --git a/laba3.2/Лекция3-src/src/main/resources/application.properties b/laba3.2/Лекция3-src/src/main/resources/application.properties new file mode 100644 index 0000000..62ab433 --- /dev/null +++ b/laba3.2/Лекция3-src/src/main/resources/application.properties @@ -0,0 +1,20 @@ +# Server +spring.main.banner-mode=off +server.port=8080 + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:file:./data +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false +# spring.jpa.show-sql=true +# spring.jpa.properties.hibernate.format_sql=true + +# H2 console +spring.h2.console.enabled=true \ No newline at end of file diff --git a/laba3.2/Лекция3-src/src/test/java/com/example/demo/FilmServiceTests.java b/laba3.2/Лекция3-src/src/test/java/com/example/demo/FilmServiceTests.java new file mode 100644 index 0000000..8aa4f04 --- /dev/null +++ b/laba3.2/Лекция3-src/src/test/java/com/example/demo/FilmServiceTests.java @@ -0,0 +1,103 @@ +package com.example.demo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; + +@SpringBootTest +public class FilmServiceTests { + @Autowired + private GenreService genreService; + private GenreEntity genre; + @Autowired + private FilmService filmService; + private FilmEntity film; + + @BeforeEach + void createData() { + removeData(); + + genre = genreService.create(new GenreEntity("sub 1")); + genreService.create(new GenreEntity("sub 2")); + genreService.create(new GenreEntity("sub 3")); + + film = filmService.create(new FilmEntity("film 1", genre, 100d, 10)); + filmService.create(new FilmEntity("film 2", genre, 100d, 10)); + filmService.create(new FilmEntity("film 3", genre, 100d, 10)); + } + + @AfterEach + void removeData() { + filmService.getAll().forEach(item -> filmService.delete(item.getId())); + genreService.getAll().forEach(item -> genreService.delete(item.getId())); + } + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> filmService.get(0L)); + } + + @Test + void createTest() { + Assertions.assertEquals(3, filmService.getAll().size()); + Assertions.assertEquals(film, filmService.get(film.getId())); + } + + @Test + void createNotUniqueTest() { + final FilmEntity nonUniqueFilm = new FilmEntity("film 1", genre, 100d, 10); + Assertions.assertThrows(IllegalArgumentException.class, () -> filmService.create(nonUniqueFilm)); + } + + @Test + void createNullableTest() { + final FilmEntity nullableFilm = new FilmEntity(null, null, null, null); + Assertions.assertThrows(DataIntegrityViolationException.class, + () -> filmService.create(nullableFilm)); + } + + @Test + void updateTest() { + final String testName = "TEST"; + final GenreEntity testGenre = genreService.getAll().get(1); + final Double testPrice = 543d; + final Integer testDiscount = 99; + final String oldName = film.getName(); + final GenreEntity oldGenre = film.getGenre(); + final Double oldPrice = film.getPrice(); + final Integer oldDiscount = film.getDiscount(); + final FilmEntity newEntity = filmService.update(film.getId(), + new FilmEntity(testName, testGenre, testPrice, testDiscount)); + Assertions.assertEquals(3, filmService.getAll().size()); + Assertions.assertEquals(newEntity, filmService.get(film.getId())); + Assertions.assertEquals(testName, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + Assertions.assertEquals(testGenre, newEntity.getGenre()); + Assertions.assertNotEquals(oldGenre, newEntity.getGenre()); + Assertions.assertEquals(testPrice, newEntity.getPrice()); + Assertions.assertNotEquals(oldPrice, newEntity.getPrice()); + Assertions.assertEquals(testDiscount, newEntity.getDiscount()); + Assertions.assertNotEquals(oldDiscount, newEntity.getDiscount()); + } + + @Test + void deleteTest() { + filmService.delete(film.getId()); + Assertions.assertEquals(2, filmService.getAll().size()); + + final FilmEntity newEntity = filmService + .create(new FilmEntity(film.getName(), film.getGenre(), film.getPrice(), film.getDiscount())); + Assertions.assertEquals(3, filmService.getAll().size()); + Assertions.assertNotEquals(film.getId(), newEntity.getId()); + } +} diff --git a/laba3.2/Лекция3-src/src/test/java/com/example/demo/GServiceTests.java b/laba3.2/Лекция3-src/src/test/java/com/example/demo/GServiceTests.java new file mode 100644 index 0000000..4a43c64 --- /dev/null +++ b/laba3.2/Лекция3-src/src/test/java/com/example/demo/GServiceTests.java @@ -0,0 +1,82 @@ +package com.example.demo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; + +@SpringBootTest +public class GServiceTests { + @Autowired + private GenreService genreService; + + private GenreEntity genre; + + @BeforeEach + void createData() { + removeData(); + + genre = genreService.create(new GenreEntity("sub 1")); + genreService.create(new GenreEntity("sub 2")); + genreService.create(new GenreEntity("sub 3")); + } + + @AfterEach + void removeData() { + genreService.getAll().forEach(item -> genreService.delete(item.getId())); + } + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> genreService.get(0L)); + } + + @Test + void createTest() { + Assertions.assertEquals(3, genreService.getAll().size()); + Assertions.assertEquals(genre, genreService.get(genre.getId())); + } + + @Test + void createNotUniqueTest() { + final GenreEntity nonUniqueGenre = new GenreEntity("sub 1"); + Assertions.assertThrows(IllegalArgumentException.class, () -> genreService.create(nonUniqueGenre)); + } + + @Test + void createNullableTest() { + final GenreEntity nullableGenre = new GenreEntity(null); + Assertions.assertThrows(DataIntegrityViolationException.class, + () -> genreService.create(nullableGenre)); + } + + @Test + void updateTest() { + final String testName = "TEST"; + final String oldName = genre.getName(); + final GenreEntity newEntity = genreService.update(genre.getId(), + new GenreEntity(testName)); + Assertions.assertEquals(3, genreService.getAll().size()); + Assertions.assertEquals(newEntity, genreService.get(genre.getId())); + Assertions.assertEquals(testName, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + } + + @Test + void deleteTest() { + genreService.delete(genre.getId()); + Assertions.assertEquals(2, genreService.getAll().size()); + + final GenreEntity newEntity = genreService + .create(new GenreEntity(genre.getName())); + Assertions.assertEquals(3, genreService.getAll().size()); + Assertions.assertNotEquals(genre.getId(), newEntity.getId()); + } +} diff --git a/laba3.2/Лекция3-src/src/test/java/com/example/demo/SubscribeServiceTests.java b/laba3.2/Лекция3-src/src/test/java/com/example/demo/SubscribeServiceTests.java new file mode 100644 index 0000000..2836efd --- /dev/null +++ b/laba3.2/Лекция3-src/src/test/java/com/example/demo/SubscribeServiceTests.java @@ -0,0 +1,86 @@ +package com.example.demo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.service.SubscribeService; + +@SpringBootTest +class SubscribeServiceTests { + @Autowired + private SubscribeService subscribeService; + + private SubscribeEntity subscribe; + + @BeforeEach + void createData() { + removeData(); + + subscribe = subscribeService.create(new SubscribeEntity("sub 1", 100d)); + subscribeService.create(new SubscribeEntity("sub 2", 100d)); + subscribeService.create(new SubscribeEntity("sub 3", 100d)); + } + + @AfterEach + void removeData() { + subscribeService.getAll().forEach(item -> subscribeService.delete(item.getId())); + } + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> subscribeService.get(0L)); + } + + @Test + void createTest() { + Assertions.assertEquals(3, subscribeService.getAll().size()); + Assertions.assertEquals(subscribe, subscribeService.get(subscribe.getId())); + } + + @Test + void createNotUniqueTest() { + final SubscribeEntity nonUniqueSubscribe = new SubscribeEntity("sub 1", 100d); + Assertions.assertThrows(IllegalArgumentException.class, () -> subscribeService.create(nonUniqueSubscribe)); + } + + @Test + void createNullableTest() { + final SubscribeEntity nullableSubscribe = new SubscribeEntity(null, null); + Assertions.assertThrows(DataIntegrityViolationException.class, + () -> subscribeService.create(nullableSubscribe)); + } + + @Test + void updateTest() { + final String testName = "TEST"; + final Double testPrice = 765d; + final String oldName = subscribe.getName(); + final Double oldPrice = subscribe.getPrice(); + final SubscribeEntity newEntity = subscribeService.update(subscribe.getId(), + new SubscribeEntity(testName, testPrice)); + Assertions.assertEquals(3, subscribeService.getAll().size()); + Assertions.assertEquals(newEntity, subscribeService.get(subscribe.getId())); + Assertions.assertEquals(testName, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + Assertions.assertEquals(testPrice, newEntity.getPrice()); + Assertions.assertNotEquals(oldPrice, newEntity.getPrice()); + } + + @Test + void deleteTest() { + subscribeService.delete(subscribe.getId()); + Assertions.assertEquals(2, subscribeService.getAll().size()); + + final SubscribeEntity newEntity = subscribeService + .create(new SubscribeEntity(subscribe.getName(), subscribe.getPrice())); + Assertions.assertEquals(3, subscribeService.getAll().size()); + Assertions.assertNotEquals(subscribe.getId(), newEntity.getId()); + } +} diff --git a/laba3.2/Лекция3-src/src/test/java/com/example/demo/UserFilmServiceTests.java b/laba3.2/Лекция3-src/src/test/java/com/example/demo/UserFilmServiceTests.java new file mode 100644 index 0000000..b2ec1ad --- /dev/null +++ b/laba3.2/Лекция3-src/src/test/java/com/example/demo/UserFilmServiceTests.java @@ -0,0 +1,141 @@ +package com.example.demo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.service.SubscribeService; +import com.example.demo.userfilms.model.UserFilmEntity; +import com.example.demo.userfilms.service.UserFilmService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@SpringBootTest +public class UserFilmServiceTests { + @Autowired + private GenreService genreService; + private GenreEntity genre; + @Autowired + private FilmService filmService; + private FilmEntity film; + private FilmEntity film2; + @Autowired + private SubscribeService subscribeService; + private SubscribeEntity subscribe; + @Autowired + private UserService userService; + private UserEntity user; + private UserEntity user2; + + @Autowired + private UserFilmService userFilmService; + private UserFilmEntity userFilm; + + @BeforeEach + void createData() { + removeData(); + + genre = genreService.create(new GenreEntity("genre 1")); + genreService.create(new GenreEntity("genre 2")); + genreService.create(new GenreEntity("genre 3")); + + film = filmService.create(new FilmEntity("film 1", genre, 100d, 10)); + film2 = filmService.create(new FilmEntity("film 2", genre, + 100d, 10)); + filmService.create(new FilmEntity("film 3", genre, + 100d, 10)); + + subscribe = subscribeService.create(new SubscribeEntity("sub 1", 100d)); + subscribeService.create(new SubscribeEntity("sub 2", 100d)); + subscribeService.create(new SubscribeEntity("sub 3", 100d)); + + user = userService.create(new UserEntity("login 1", subscribe, "mail1", + "phone1")); + user2 = userService.create(new UserEntity("login 2", subscribe, "mail2", + "phone2")); + userService.create(new UserEntity("login 3", subscribe, "mail3", "phone3")); + + userFilm = userFilmService.create(new UserFilmEntity(user, film, false)); + userFilmService.create(new UserFilmEntity(user2, film, true)); + } + + @AfterEach + void removeData() { + userFilmService.getAll().forEach(item -> userFilmService.delete(item.getId())); + userService.getAll().forEach(item -> userService.delete(item.getId())); + subscribeService.getAll().forEach(item -> subscribeService.delete(item.getId())); + filmService.getAll().forEach(item -> filmService.delete(item.getId())); + genreService.getAll().forEach(item -> genreService.delete(item.getId())); + } + + @Test + void getTest() { + + } + + @Test + void createTest() { + Assertions.assertEquals(2, userFilmService.getAll().size()); + Assertions.assertEquals(userFilm, userFilmService.get(userFilm.getId())); + } + + @Test + void createNotUniqueTest() { + final UserFilmEntity nonUniqueUser = new UserFilmEntity(user, film, false); + Assertions.assertThrows(IllegalArgumentException.class, () -> userFilmService.create(nonUniqueUser)); + } + + @Test + void createNullableTest() { + final UserFilmEntity nullableUserFilm = new UserFilmEntity(null, null, true); + Assertions.assertThrows( + NullPointerException.class, + () -> userFilmService.create(nullableUserFilm)); + } + + @Test + void updateTest() { + final UserEntity testUser = user2; + final FilmEntity testFilm = film2; + final Boolean testView = true; + final UserEntity oldUser = userFilm.getUser(); + final FilmEntity oldFilm = userFilm.getFilm(); + final Boolean oldView = userFilm.isViewed(); + final UserFilmEntity newEntity = userFilmService.update(userFilm.getId(), + new UserFilmEntity(testUser, testFilm, testView)); + + Assertions.assertEquals(2, userFilmService.getAll().size()); + Assertions.assertEquals(newEntity, userFilmService.get(userFilm.getId())); + Assertions.assertEquals(testUser, newEntity.getUser()); + Assertions.assertNotEquals(oldUser, newEntity.getUser()); + Assertions.assertEquals(testFilm, newEntity.getFilm()); + Assertions.assertNotEquals(oldFilm, newEntity.getFilm()); + Assertions.assertEquals(testView, newEntity.isViewed()); + Assertions.assertNotEquals(oldView, newEntity.isViewed()); + } + + @Test + void deleteTest() { + userFilmService.delete(userFilm.getId()); + Assertions.assertEquals(1, userFilmService.getAll().size()); + + final UserFilmEntity newEntity = userFilmService + .create(new UserFilmEntity(userFilm.getUser(), userFilm.getFilm(), userFilm.isViewed())); + Assertions.assertEquals(2, userFilmService.getAll().size()); + Assertions.assertNotEquals(userFilm.getId(), newEntity.getId()); + } + + @Test + void QueryTest() { + Assertions.assertEquals(2, userFilmService.getTotal(user.getId()).size()); + Assertions.assertEquals(genre, userFilmService.getTotal(user.getId()).get(1).getGenre()); + Assertions.assertEquals(1, userFilmService.getTotal(user.getId()).get(1).getCount()); + } +} diff --git a/laba3.2/Лекция3-src/src/test/java/com/example/demo/UserServiceTests.java b/laba3.2/Лекция3-src/src/test/java/com/example/demo/UserServiceTests.java new file mode 100644 index 0000000..43237fd --- /dev/null +++ b/laba3.2/Лекция3-src/src/test/java/com/example/demo/UserServiceTests.java @@ -0,0 +1,142 @@ +package com.example.demo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.films.model.FilmEntity; +import com.example.demo.films.service.FilmService; +import com.example.demo.genres.model.GenreEntity; +import com.example.demo.genres.service.GenreService; +import com.example.demo.subscribes.model.SubscribeEntity; +import com.example.demo.subscribes.service.SubscribeService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.persistence.EntityManager; + +@SpringBootTest +public class UserServiceTests { + @Autowired + private EntityManager entityManager; + @Autowired + private GenreService genreService; + private GenreEntity genre; + @Autowired + private FilmService filmService; + @Autowired + private SubscribeService subscribeService; + private SubscribeEntity subscribe; + private SubscribeEntity subscribe2; + @Autowired + private UserService userService; + private UserEntity user; + private UserEntity user2; + + @BeforeEach + void createData() { + removeData(); + + genre = genreService.create(new GenreEntity("genre 1")); + genreService.create(new GenreEntity("genre 2")); + genreService.create(new GenreEntity("genre 3")); + + filmService.create(new FilmEntity("film 1", genre, 100d, 10)); + filmService.create(new FilmEntity("film 2", genre, + 100d, 10)); + filmService.create(new FilmEntity("film 3", genre, + 100d, 10)); + + subscribe = subscribeService.create(new SubscribeEntity("sub 1", 100d)); + subscribe2 = subscribeService.create(new SubscribeEntity("sub 2", 100d)); + subscribeService.create(new SubscribeEntity("sub 3", 100d)); + + user = userService.create(new UserEntity("login 1", subscribe, "mail1", + "phone1")); + user2 = userService.create(new UserEntity("login 2", subscribe, "mail2", + "phone2")); + userService.create(new UserEntity("login 3", subscribe, "mail3", "phone3")); + } + + @AfterEach + void removeData() { + userService.getAll().forEach(item -> userService.delete(item.getId())); + subscribeService.getAll().forEach(item -> subscribeService.delete(item.getId())); + filmService.getAll().forEach(item -> filmService.delete(item.getId())); + genreService.getAll().forEach(item -> genreService.delete(item.getId())); + } + + @Test + void getTest() { + + } + + @Test + void createTest() { + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertEquals(user, userService.get(user.getId())); + } + + @Test + void createNotUniqueTest() { + final UserEntity nonUniqueUser = new UserEntity("login 1", subscribe, + "mail1", "phone1"); + Assertions.assertThrows(IllegalArgumentException.class, () -> userService.create(nonUniqueUser)); + } + + @Test + void createNullableTest() { + final UserEntity nullableUser = new UserEntity(null, null, null, null); + Assertions.assertThrows(DataIntegrityViolationException.class, + () -> userService.create(nullableUser)); + } + + @Test + void updateTest() { + final String testLogin = "TEST"; + final SubscribeEntity testSubscribe = subscribe2; + final String testMail = "TEST"; + final String testPhone = "TEST"; + final String oldLogin = user.getLogin(); + final SubscribeEntity oldSubscribe = user.getSubscribe(); + final String oldMail = user.getMail(); + final String oldPhone = user.getPhone(); + final UserEntity newEntity = userService.update(user.getId(), + new UserEntity(testLogin, testSubscribe, testMail, testPhone)); + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertEquals(newEntity, userService.get(user.getId())); + Assertions.assertEquals(testLogin, newEntity.getLogin()); + Assertions.assertNotEquals(oldLogin, newEntity.getLogin()); + Assertions.assertEquals(testSubscribe, newEntity.getSubscribe()); + Assertions.assertNotEquals(oldSubscribe, newEntity.getSubscribe()); + Assertions.assertEquals(testMail, newEntity.getMail()); + Assertions.assertNotEquals(oldMail, newEntity.getMail()); + Assertions.assertEquals(testPhone, newEntity.getPhone()); + Assertions.assertNotEquals(oldPhone, newEntity.getPhone()); + } + + @Test + void deleteTest() { + userService.delete(user.getId()); + Assertions.assertEquals(2, userService.getAll().size()); + + final UserEntity newEntity = userService + .create(new UserEntity(user.getLogin(), user.getSubscribe(), user.getMail(), + user.getPhone())); + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertNotEquals(user.getId(), newEntity.getId()); + } + + @Test + void userCascadeDeleteTest() { + userService.delete(user2.getId()); + final var films = entityManager.createQuery( + "select count(us) from UserFilmEntity us where us.user.id = :userId"); + films.setParameter("userId", user2.getId()); + Assertions.assertEquals(0, films.getFirstResult()); + } +} diff --git a/laba3.2/Лекция3-src/src/test/resources/application.properties b/laba3.2/Лекция3-src/src/test/resources/application.properties new file mode 100644 index 0000000..d5f355c --- /dev/null +++ b/laba3.2/Лекция3-src/src/test/resources/application.properties @@ -0,0 +1,14 @@ +# Server +spring.main.banner-mode=off + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false \ No newline at end of file diff --git a/laba4/.gitignore b/laba4/.gitignore new file mode 100644 index 0000000..546ecee --- /dev/null +++ b/laba4/.gitignore @@ -0,0 +1,36 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +data.*.db \ No newline at end of file diff --git a/laba4/.vscode/extensions.json b/laba4/.vscode/extensions.json new file mode 100644 index 0000000..42cf79d --- /dev/null +++ b/laba4/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + // fronted + "AndersEAndersen.html-class-suggestions", + "dbaeumer.vscode-eslint", + // backend + "vscjava.vscode-java-pack", + "vmware.vscode-boot-dev-pack", + "vscjava.vscode-gradle", + "redhat.vscode-xml" + ] +} \ No newline at end of file diff --git a/laba4/.vscode/launch.json b/laba4/.vscode/launch.json new file mode 100644 index 0000000..27aa918 --- /dev/null +++ b/laba4/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "configurations": [ + { + "type": "java", + "name": "Demo", + "request": "launch", + "cwd": "${workspaceFolder}", + "mainClass": "com.example.demo.DemoApplication", + "projectName": "lec5", + "args": "--populate", + "envFile": "${workspaceFolder}/.env" + } + ] +} \ No newline at end of file diff --git a/laba4/.vscode/settings.json b/laba4/.vscode/settings.json new file mode 100644 index 0000000..95915c9 --- /dev/null +++ b/laba4/.vscode/settings.json @@ -0,0 +1,24 @@ +{ + "editor.tabSize": 4, + "editor.detectIndentation": false, + "editor.insertSpaces": true, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.formatOnType": false, + "java.compile.nullAnalysis.mode": "disabled", + "java.configuration.updateBuildConfiguration": "automatic", + "[java]": { + "editor.pasteAs.enabled": false, + }, + "gradle.nestedProjects": true, + "java.saveActions.organizeImports": true, + "java.dependency.packagePresentation": "hierarchical", + "spring-boot.ls.problem.boot2.JAVA_CONSTRUCTOR_PARAMETER_INJECTION": "WARNING", + "spring.initializr.defaultLanguage": "Java", + "java.format.settings.url": ".vscode/eclipse-formatter.xml", + "java.project.explorer.showNonJavaResources": true, + "java.codeGeneration.hashCodeEquals.useJava7Objects": true, + "cSpell.words": [ + "classappend" + ], +} \ No newline at end of file diff --git a/laba4/build.gradle b/laba4/build.gradle new file mode 100644 index 0000000..cf1172b --- /dev/null +++ b/laba4/build.gradle @@ -0,0 +1,48 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.4' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +defaultTasks 'bootRun' + +jar { + enabled = false +} + +bootJar { + archiveFileName = String.format('%s-%s.jar', rootProject.name, version) +} + +assert System.properties['java.specification.version'] == '17' || '21' +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.modelmapper:modelmapper:3.2.0' + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'com.h2database:h2:2.2.224' + + implementation 'org.springframework.boot:spring-boot-devtools' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.3.0' + runtimeOnly 'org.webjars.npm:bootstrap:5.3.3' + runtimeOnly 'org.webjars.npm:bootstrap-icons:1.11.3' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/laba4/gradle/wrapper/gradle-wrapper.jar b/laba4/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/laba4/gradle/wrapper/gradle-wrapper.properties b/laba4/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/laba4/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/laba4/gradlew b/laba4/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/laba4/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/laba4/gradlew.bat b/laba4/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/laba4/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/laba4/readme.md b/laba4/readme.md new file mode 100644 index 0000000..ac4a1d0 --- /dev/null +++ b/laba4/readme.md @@ -0,0 +1,15 @@ +H2 Console: \ +http://localhost:8080/h2-console + +JDBC URL: jdbc:h2:file:./data \ +User Name: sa \ +Password: password + +Почитать: + +- Spring Boot CRUD Application with Thymeleaf https://www.baeldung.com/spring-boot-crud-thymeleaf +- Thymeleaf Layout Dialect https://ultraq.github.io/thymeleaf-layout-dialect/ +- Tutorial: Using Thymeleaf https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#introducing-thymeleaf +- Working with Cookies in Spring MVC using @CookieValue Annotation https://www.geeksforgeeks.org/working-with-cookies-in-spring-mvc-using-cookievalue-annotation/ +- Session Attributes in Spring MVC https://www.baeldung.com/spring-mvc-session-attributes +- LazyInitializationException – What it is and the best way to fix it https://thorben-janssen.com/lazyinitializationexception/ diff --git a/laba4/settings.gradle b/laba4/settings.gradle new file mode 100644 index 0000000..0a383dd --- /dev/null +++ b/laba4/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'demo' diff --git a/laba4/src/main/java/com/example/demo/DemoApplication.java b/laba4/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..e54d422 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,78 @@ +package com.example.demo; + +import java.util.List; +import java.util.Objects; +import java.util.stream.IntStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.service.OrderService; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@SpringBootApplication +public class DemoApplication implements CommandLineRunner { + private final Logger log = LoggerFactory.getLogger(DemoApplication.class); + + private final TypeService typeService; + private final SubscriptionService subscriptionService; + private final UserService userService; + private final OrderService orderService; + + public DemoApplication( + TypeService typeService, + SubscriptionService subscriptionService, + UserService userService, + OrderService orderService) { + this.typeService = typeService; + this.subscriptionService = subscriptionService; + this.userService = userService; + this.orderService = orderService; + } + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @Override + public void run(String... args) throws Exception { + if (args.length > 0 && Objects.equals("--populate", args[0])) { + log.info("Create default type values"); + final var type1 = typeService.create(new TypeEntity("Ноутбук")); + final var type2 = typeService.create(new TypeEntity("Телефон")); + final var type3 = typeService.create(new TypeEntity("Игровая приставка")); + + log.info("Create default subscription values"); + subscriptionService.create(new SubscriptionEntity("Подписка 1")); + subscriptionService.create(new SubscriptionEntity("Подписка 2")); + subscriptionService.create(new SubscriptionEntity("Подписка 3")); + + log.info("Create default user values"); + final var user1 = userService.create(new UserEntity("user1")); + + IntStream.range(2, 27) + .forEach(value -> userService.create( + new UserEntity("user".concat(String.valueOf(value))))); + + log.info("Create default order values"); + final var orders = List.of( + new OrderEntity(type1, 49999.00, 20), + new OrderEntity(type1, 129999.00, 3), + new OrderEntity(type2, 15450.50, 30), + new OrderEntity(type2, 69900.50, 10), + new OrderEntity(type2, 150000.00, 6), + new OrderEntity(type3, 75000.00, 6), + new OrderEntity(type3, 67800.00, 3)); + orders.forEach(order -> orderService.create(user1.getId(), order)); + } + } +} diff --git a/laba4/src/main/java/com/example/demo/core/api/GlobalController.java b/laba4/src/main/java/com/example/demo/core/api/GlobalController.java new file mode 100644 index 0000000..5e7a996 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/core/api/GlobalController.java @@ -0,0 +1,43 @@ +package com.example.demo.core.api; + +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.ModelAttribute; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.core.session.SessionCart; +import com.example.demo.users.service.UserService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; + +@ControllerAdvice +public class GlobalController { + private final UserService userService; + private final SessionCart cart; + + public GlobalController( + UserService userService, + SessionCart cart) { + this.userService = userService; + this.cart = cart; + } + + @ModelAttribute("servletPath") + String getRequestServletPath(HttpServletRequest request) { + return request.getServletPath(); + } + + @ModelAttribute("totalCart") + double getTotalCart(HttpSession session) { + return cart.getSum(); + } + + @ModelAttribute("userName") + String getUserName(@CookieValue(value = Constants.USER_COOKIE, defaultValue = "0") long userId) { + if (userId <= 0) { + return ""; + } + return userService.get(userId).getLogin(); + } +} diff --git a/laba4/src/main/java/com/example/demo/core/api/PageAttributesMapper.java b/laba4/src/main/java/com/example/demo/core/api/PageAttributesMapper.java new file mode 100644 index 0000000..74ee38d --- /dev/null +++ b/laba4/src/main/java/com/example/demo/core/api/PageAttributesMapper.java @@ -0,0 +1,18 @@ +package com.example.demo.core.api; + +import java.util.Map; +import java.util.function.Function; + +import org.springframework.data.domain.Page; + +public class PageAttributesMapper { + private PageAttributesMapper() { + } + + public static Map toAttributes(Page page, Function mapper) { + return Map.of( + "items", page.getContent().stream().map(mapper::apply).toList(), + "currentPage", page.getNumber(), + "totalPages", page.getTotalPages()); + } +} diff --git a/laba4/src/main/java/com/example/demo/core/configuration/Constants.java b/laba4/src/main/java/com/example/demo/core/configuration/Constants.java new file mode 100644 index 0000000..f4e00a8 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/core/configuration/Constants.java @@ -0,0 +1,16 @@ +package com.example.demo.core.configuration; + +public class Constants { + public static final String SEQUENCE_NAME = "hibernate_sequence"; + + public static final int DEFUALT_PAGE_SIZE = 5; + + public static final String REDIRECT_VIEW = "redirect:"; + + public static final String ADMIN_PREFIX = "/admin"; + + public static final String USER_COOKIE = "userIdCookie"; + + private Constants() { + } +} diff --git a/laba4/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java b/laba4/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java new file mode 100644 index 0000000..44defae --- /dev/null +++ b/laba4/src/main/java/com/example/demo/core/configuration/MapperConfiguration.java @@ -0,0 +1,23 @@ +package com.example.demo.core.configuration; + +import org.modelmapper.ModelMapper; +import org.modelmapper.PropertyMap; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.example.demo.core.model.BaseEntity; + +@Configuration +public class MapperConfiguration { + @Bean + ModelMapper modelMapper() { + final ModelMapper mapper = new ModelMapper(); + mapper.addMappings(new PropertyMap() { + @Override + protected void configure() { + skip(destination.getId()); + } + }); + return mapper; + } +} diff --git a/laba4/src/main/java/com/example/demo/core/error/AdviceController.java b/laba4/src/main/java/com/example/demo/core/error/AdviceController.java new file mode 100644 index 0000000..65f0c3e --- /dev/null +++ b/laba4/src/main/java/com/example/demo/core/error/AdviceController.java @@ -0,0 +1,53 @@ +package com.example.demo.core.error; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.servlet.ModelAndView; + +import jakarta.servlet.http.HttpServletRequest; + +@ControllerAdvice +public class AdviceController { + private final Logger log = LoggerFactory.getLogger(AdviceController.class); + + private static Throwable getRootCause(Throwable throwable) { + Throwable rootCause = throwable; + while (rootCause.getCause() != null && rootCause.getCause() != rootCause) { + rootCause = rootCause.getCause(); + } + return rootCause; + } + + private static Map getAttributes(HttpServletRequest request, Throwable throwable) { + final Throwable rootCause = getRootCause(throwable); + final StackTraceElement firstError = rootCause.getStackTrace()[0]; + return Map.of( + "message", rootCause.getMessage(), + "url", request.getRequestURL(), + "exception", rootCause.getClass().getName(), + "file", firstError.getFileName(), + "method", firstError.getMethodName(), + "line", firstError.getLineNumber()); + } + + @ExceptionHandler(value = Exception.class) + public ModelAndView defaultErrorHandler(HttpServletRequest request, Throwable throwable) throws Throwable { + if (AnnotationUtils.findAnnotation(throwable.getClass(), + ResponseStatus.class) != null) { + throw throwable; + } + + log.error("{}", throwable.getMessage()); + throwable.printStackTrace(); + final ModelAndView model = new ModelAndView(); + model.addAllObjects(getAttributes(request, throwable)); + model.setViewName("error"); + return model; + } +} diff --git a/laba4/src/main/java/com/example/demo/core/error/NotFoundException.java b/laba4/src/main/java/com/example/demo/core/error/NotFoundException.java new file mode 100644 index 0000000..a61d118 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/core/error/NotFoundException.java @@ -0,0 +1,7 @@ +package com.example.demo.core.error; + +public class NotFoundException extends RuntimeException { + public NotFoundException(Class clazz, Long id) { + super(String.format("%s with id [%s] is not found or not exists", clazz.getSimpleName(), id)); + } +} diff --git a/laba4/src/main/java/com/example/demo/core/model/BaseEntity.java b/laba4/src/main/java/com/example/demo/core/model/BaseEntity.java new file mode 100644 index 0000000..eba74ad --- /dev/null +++ b/laba4/src/main/java/com/example/demo/core/model/BaseEntity.java @@ -0,0 +1,28 @@ +package com.example.demo.core.model; + +import com.example.demo.core.configuration.Constants; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.SequenceGenerator; + +@MappedSuperclass +public abstract class BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = Constants.SEQUENCE_NAME) + @SequenceGenerator(name = Constants.SEQUENCE_NAME, sequenceName = Constants.SEQUENCE_NAME, allocationSize = 1) + protected Long id; + + protected BaseEntity() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/laba4/src/main/java/com/example/demo/core/session/SessionCart.java b/laba4/src/main/java/com/example/demo/core/session/SessionCart.java new file mode 100644 index 0000000..09f7d23 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/core/session/SessionCart.java @@ -0,0 +1,14 @@ +package com.example.demo.core.session; + +import java.util.HashMap; + +import com.example.demo.users.api.UserCartDto; + +public class SessionCart extends HashMap { + public double getSum() { + return this.values().stream() + .map(item -> item.getCount() * item.getPrice()) + .mapToDouble(Double::doubleValue) + .sum(); + } +} diff --git a/laba4/src/main/java/com/example/demo/core/session/SessionHelper.java b/laba4/src/main/java/com/example/demo/core/session/SessionHelper.java new file mode 100644 index 0000000..6ee3d8c --- /dev/null +++ b/laba4/src/main/java/com/example/demo/core/session/SessionHelper.java @@ -0,0 +1,16 @@ +package com.example.demo.core.session; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.web.context.WebApplicationContext; + +@Configuration +public class SessionHelper { + @Bean + @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) + SessionCart todos() { + return new SessionCart(); + } +} diff --git a/laba4/src/main/java/com/example/demo/films/model/FilmEntity.java b/laba4/src/main/java/com/example/demo/films/model/FilmEntity.java new file mode 100644 index 0000000..825030a --- /dev/null +++ b/laba4/src/main/java/com/example/demo/films/model/FilmEntity.java @@ -0,0 +1,63 @@ +package com.example.demo.films.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.types.model.TypeEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "films") +public class FilmEntity extends BaseEntity { + @ManyToOne + @JoinColumn(name = "typeId", nullable = false) + private TypeEntity type; + @Column(nullable = false) + private Double price; + + public FilmEntity() { + } + + public FilmEntity(TypeEntity type, Double price) { + this.type = type; + this.price = price; + } + + public TypeEntity getType() { + return type; + } + + public void setType(TypeEntity type) { + this.type = type; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + @Override + public int hashCode() { + return Objects.hash(id, type, price); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final FilmEntity other = (FilmEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getType(), type) + && Objects.equals(other.getPrice(), price); + } +} diff --git a/laba4/src/main/java/com/example/demo/orders/api/OrderDto.java b/laba4/src/main/java/com/example/demo/orders/api/OrderDto.java new file mode 100644 index 0000000..8ba4b7c --- /dev/null +++ b/laba4/src/main/java/com/example/demo/orders/api/OrderDto.java @@ -0,0 +1,53 @@ +package com.example.demo.orders.api; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +public class OrderDto { + private Long id; + @NotNull + @Min(1) + private String typeName; + @NotNull + @Min(1) + private Double price; + @NotNull + @Min(1) + private Integer count; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTypeName() { + return typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public Double getSum() { + return price * count; + } +} diff --git a/laba4/src/main/java/com/example/demo/orders/api/OrderGroupedDto.java b/laba4/src/main/java/com/example/demo/orders/api/OrderGroupedDto.java new file mode 100644 index 0000000..ddb116e --- /dev/null +++ b/laba4/src/main/java/com/example/demo/orders/api/OrderGroupedDto.java @@ -0,0 +1,31 @@ +package com.example.demo.orders.api; + +public class OrderGroupedDto { + private String typeName; + private Long totalPrice; + private Integer totalCount; + + public String getTypeName() { + return typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } + + public Long getTotalPrice() { + return totalPrice; + } + + public void setTotalPrice(Long totalPrice) { + this.totalPrice = totalPrice; + } + + public Integer getTotalCount() { + return totalCount; + } + + public void setTotalCount(Integer totalCount) { + this.totalCount = totalCount; + } +} diff --git a/laba4/src/main/java/com/example/demo/orders/model/OrderEntity.java b/laba4/src/main/java/com/example/demo/orders/model/OrderEntity.java new file mode 100644 index 0000000..d4e6b90 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/orders/model/OrderEntity.java @@ -0,0 +1,91 @@ +package com.example.demo.orders.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.users.model.UserEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "orders") +public class OrderEntity extends BaseEntity { + @ManyToOne + @JoinColumn(name = "typeId", nullable = false) + private TypeEntity type; + @ManyToOne + @JoinColumn(name = "userId", nullable = false) + private UserEntity user; + @Column(nullable = false) + private Double price; + @Column(nullable = false) + private Integer count; + + public OrderEntity() { + } + + public OrderEntity(TypeEntity type, Double price, Integer count) { + this.type = type; + this.price = price; + this.count = count; + } + + public TypeEntity getType() { + return type; + } + + public void setType(TypeEntity type) { + this.type = type; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + if (!user.getOrders().contains(this)) { + user.getOrders().add(this); + } + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + @Override + public int hashCode() { + return Objects.hash(id, type, user.getId(), price, count); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final OrderEntity other = (OrderEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getType(), type) + && Objects.equals(other.getUser().getId(), user.getId()) + && Objects.equals(other.getPrice(), price) + && Objects.equals(other.getCount(), count); + } +} diff --git a/laba4/src/main/java/com/example/demo/orders/model/OrderGrouped.java b/laba4/src/main/java/com/example/demo/orders/model/OrderGrouped.java new file mode 100644 index 0000000..4d55f18 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/orders/model/OrderGrouped.java @@ -0,0 +1,11 @@ +package com.example.demo.orders.model; + +import com.example.demo.types.model.TypeEntity; + +public interface OrderGrouped { + TypeEntity getType(); + + double getTotalPrice(); + + int getTotalCount(); +} diff --git a/laba4/src/main/java/com/example/demo/orders/repository/OrderRepository.java b/laba4/src/main/java/com/example/demo/orders/repository/OrderRepository.java new file mode 100644 index 0000000..3ef17fa --- /dev/null +++ b/laba4/src/main/java/com/example/demo/orders/repository/OrderRepository.java @@ -0,0 +1,42 @@ +package com.example.demo.orders.repository; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.model.OrderGrouped; + +public interface OrderRepository + extends CrudRepository, PagingAndSortingRepository { + + Optional findOneByUserIdAndId(long userId, long id); + + List findByUserId(long userId); + + Page findByUserId(long userId, Pageable pageable); + + List findByUserIdAndTypeId(long userId, long typeId); + + Page findByUserIdAndTypeId(long userId, long typeId, Pageable pageable); + + // select + // tpe.name, + // coalesce(sum(order.price), 0), + // coalesce(sum(order.count), 0) + // from types as tpe + // left join orders as order on tpe.id = order.type_id and order.user_id = ? + // group by tpe.name order by tpe.id + @Query("select " + + "t as type, " + + "coalesce(sum(o.price), 0) as totalPrice, " + + "coalesce(sum(o.count), 0) as totalCount " + + "from TypeEntity t left join OrderEntity o on o.type = t and o.user.id = ?1 " + + "group by t order by t.id") + List getOrdersTotalByType(long userId); +} diff --git a/laba4/src/main/java/com/example/demo/orders/service/OrderService.java b/laba4/src/main/java/com/example/demo/orders/service/OrderService.java new file mode 100644 index 0000000..69cb502 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/orders/service/OrderService.java @@ -0,0 +1,101 @@ +package com.example.demo.orders.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.data.domain.Page; +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 com.example.demo.core.error.NotFoundException; +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.model.OrderGrouped; +import com.example.demo.orders.repository.OrderRepository; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@Service +public class OrderService { + private final OrderRepository repository; + private final UserService userService; + + public OrderService(OrderRepository repository, UserService userService) { + this.repository = repository; + this.userService = userService; + } + + @Transactional(readOnly = true) + public List getAll(long userId, long typeId) { + userService.get(userId); + if (typeId <= 0L) { + return repository.findByUserId(userId); + } else { + return repository.findByUserIdAndTypeId(userId, typeId); + } + } + + @Transactional(readOnly = true) + public Page getAll(long userId, long typeId, int page, int size) { + final Pageable pageable = PageRequest.of(page, size, Sort.by("id")); + userService.get(userId); + if (typeId <= 0L) { + return repository.findByUserId(userId, pageable); + } else { + return repository.findByUserIdAndTypeId(userId, typeId, pageable); + } + } + + @Transactional(readOnly = true) + public OrderEntity get(long userId, long id) { + userService.get(userId); + return repository.findOneByUserIdAndId(userId, id) + .orElseThrow(() -> new NotFoundException(OrderEntity.class, id)); + } + + @Transactional + public OrderEntity create(long userId, OrderEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + final UserEntity existsUser = userService.get(userId); + entity.setUser(existsUser); + return repository.save(entity); + } + + @Transactional + public List createAll(long userId, List entities) { + if (entities == null || entities.isEmpty()) { + throw new IllegalArgumentException("Orders list is null or empty"); + } + final UserEntity existsUser = userService.get(userId); + entities.forEach(entity -> entity.setUser(existsUser)); + return StreamSupport.stream(repository.saveAll(entities).spliterator(), false).toList(); + } + + @Transactional + public OrderEntity update(long userId, long id, OrderEntity entity) { + userService.get(userId); + final OrderEntity existsEntity = get(userId, id); + existsEntity.setType(entity.getType()); + existsEntity.setPrice(entity.getPrice()); + existsEntity.setCount(entity.getCount()); + return repository.save(existsEntity); + } + + @Transactional + public OrderEntity delete(long userId, long id) { + userService.get(userId); + final OrderEntity existsEntity = get(userId, id); + repository.delete(existsEntity); + return existsEntity; + } + + @Transactional(readOnly = true) + public List getTotal(long userId) { + userService.get(userId); + return repository.getOrdersTotalByType(userId); + } +} diff --git a/laba4/src/main/java/com/example/demo/subscriptions/api/SubscriptionController.java b/laba4/src/main/java/com/example/demo/subscriptions/api/SubscriptionController.java new file mode 100644 index 0000000..1ddcc7b --- /dev/null +++ b/laba4/src/main/java/com/example/demo/subscriptions/api/SubscriptionController.java @@ -0,0 +1,104 @@ +package com.example.demo.subscriptions.api; + +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; + +import jakarta.validation.Valid; + +@Controller +@RequestMapping(SubscriptionController.URL) +public class SubscriptionController { + public static final String URL = Constants.ADMIN_PREFIX + "/subscription"; + private static final String SUBSCRIPTION_VIEW = "subscription"; + private static final String SUBSCRIPTION_EDIT_VIEW = "subscription-edit"; + private static final String SUBSCRIPTION_ATTRIBUTE = "subscription"; + + private final SubscriptionService subscriptionService; + private final ModelMapper modelMapper; + + public SubscriptionController(SubscriptionService subscriptionService, ModelMapper modelMapper) { + this.subscriptionService = subscriptionService; + this.modelMapper = modelMapper; + } + + private SubscriptionDto toDto(SubscriptionEntity entity) { + return modelMapper.map(entity, SubscriptionDto.class); + } + + private SubscriptionEntity toEntity(SubscriptionDto dto) { + return modelMapper.map(dto, SubscriptionEntity.class); + } + + @GetMapping + public String getAll(Model model) { + model.addAttribute( + "items", + subscriptionService.getAll().stream() + .map(this::toDto) + .toList()); + return SUBSCRIPTION_VIEW; + } + + @GetMapping("/edit/") + public String create(Model model) { + model.addAttribute(SUBSCRIPTION_ATTRIBUTE, new SubscriptionDto()); + return SUBSCRIPTION_EDIT_VIEW; + } + + @PostMapping("/edit/") + public String create( + @ModelAttribute(name = SUBSCRIPTION_ATTRIBUTE) @Valid SubscriptionDto subscription, + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + return SUBSCRIPTION_EDIT_VIEW; + } + subscriptionService.create(toEntity(subscription)); + return Constants.REDIRECT_VIEW + URL; + } + + @GetMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + Model model) { + if (id <= 0) { + throw new IllegalArgumentException(); + } + model.addAttribute(SUBSCRIPTION_ATTRIBUTE, toDto(subscriptionService.get(id))); + return SUBSCRIPTION_EDIT_VIEW; + } + + @PostMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + @ModelAttribute(name = SUBSCRIPTION_ATTRIBUTE) @Valid SubscriptionDto subscription, + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + return SUBSCRIPTION_EDIT_VIEW; + } + if (id <= 0) { + throw new IllegalArgumentException(); + } + subscriptionService.update(id, toEntity(subscription)); + return Constants.REDIRECT_VIEW + URL; + } + + @PostMapping("/delete/{id}") + public String delete( + @PathVariable(name = "id") Long id) { + subscriptionService.delete(id); + return Constants.REDIRECT_VIEW + URL; + } +} diff --git a/laba4/src/main/java/com/example/demo/subscriptions/api/SubscriptionDto.java b/laba4/src/main/java/com/example/demo/subscriptions/api/SubscriptionDto.java new file mode 100644 index 0000000..76e6fc6 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/subscriptions/api/SubscriptionDto.java @@ -0,0 +1,27 @@ +package com.example.demo.subscriptions.api; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class SubscriptionDto { + private Long id; + @NotBlank + @Size(min = 5, max = 50) + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/laba4/src/main/java/com/example/demo/subscriptions/model/SubscriptionEntity.java b/laba4/src/main/java/com/example/demo/subscriptions/model/SubscriptionEntity.java new file mode 100644 index 0000000..59214e4 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/subscriptions/model/SubscriptionEntity.java @@ -0,0 +1,67 @@ +package com.example.demo.subscriptions.model; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.usersubscription.model.UserSubscriptionEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.Table; + +@Entity +@Table(name = "subscriptions") +public class SubscriptionEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 50) + private String name; + @OneToMany(mappedBy = "subscription") + @OrderBy("id ASC") + private Set userSubscriptions = new HashSet<>(); + + public SubscriptionEntity() { + } + + public SubscriptionEntity(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getUserSubscriptions() { + return userSubscriptions; + } + + public void addUser(UserSubscriptionEntity userSubscription) { + if (userSubscription.getSubscription() != this) { + userSubscription.setSubscription(this); + } + userSubscriptions.add(userSubscription); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, userSubscriptions); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + SubscriptionEntity other = (SubscriptionEntity) obj; + return Objects.equals(id, other.id) + && Objects.equals(name, other.name) + && Objects.equals(userSubscriptions, other.userSubscriptions); + } +} diff --git a/laba4/src/main/java/com/example/demo/subscriptions/repository/SubscriptionRepository.java b/laba4/src/main/java/com/example/demo/subscriptions/repository/SubscriptionRepository.java new file mode 100644 index 0000000..5cc12c6 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/subscriptions/repository/SubscriptionRepository.java @@ -0,0 +1,13 @@ +package com.example.demo.subscriptions.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.example.demo.subscriptions.model.SubscriptionEntity; + +public interface SubscriptionRepository + extends CrudRepository, PagingAndSortingRepository { + Optional findByNameIgnoreCase(String name); +} diff --git a/laba4/src/main/java/com/example/demo/subscriptions/service/SubscriptionService.java b/laba4/src/main/java/com/example/demo/subscriptions/service/SubscriptionService.java new file mode 100644 index 0000000..11f15a1 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/subscriptions/service/SubscriptionService.java @@ -0,0 +1,66 @@ +package com.example.demo.subscriptions.service; + +import java.util.List; +import java.util.Optional; +import java.util.stream.StreamSupport; + +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.repository.SubscriptionRepository; + +@Service +public class SubscriptionService { + private final SubscriptionRepository repository; + + public SubscriptionService(SubscriptionRepository repository) { + this.repository = repository; + } + + private void checkName(Long id, String name) { + final Optional existsType = repository.findByNameIgnoreCase(name); + if (existsType.isPresent() && !existsType.get().getId().equals(id)) { + throw new IllegalArgumentException( + String.format("Subscription with name %s is already exists", name)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll(Sort.by("id")).spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public SubscriptionEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(SubscriptionEntity.class, id)); + } + + @Transactional + public SubscriptionEntity create(SubscriptionEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkName(null, entity.getName()); + return repository.save(entity); + } + + @Transactional + public SubscriptionEntity update(Long id, SubscriptionEntity entity) { + final SubscriptionEntity existsEntity = get(id); + checkName(id, entity.getName()); + existsEntity.setName(entity.getName()); + return repository.save(existsEntity); + } + + @Transactional + public SubscriptionEntity delete(Long id) { + final SubscriptionEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } + +} diff --git a/laba4/src/main/java/com/example/demo/types/api/TypeController.java b/laba4/src/main/java/com/example/demo/types/api/TypeController.java new file mode 100644 index 0000000..802c978 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/types/api/TypeController.java @@ -0,0 +1,104 @@ +package com.example.demo.types.api; + +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; + +import jakarta.validation.Valid; + +@Controller +@RequestMapping(TypeController.URL) +public class TypeController { + public static final String URL = Constants.ADMIN_PREFIX + "/type"; + private static final String TYPE_VIEW = "type"; + private static final String TYPE_EDIT_VIEW = "type-edit"; + private static final String TYPE_ATTRIBUTE = "type"; + + private final TypeService typeService; + private final ModelMapper modelMapper; + + public TypeController(TypeService typeService, ModelMapper modelMapper) { + this.typeService = typeService; + this.modelMapper = modelMapper; + } + + private TypeDto toDto(TypeEntity entity) { + return modelMapper.map(entity, TypeDto.class); + } + + private TypeEntity toEntity(TypeDto dto) { + return modelMapper.map(dto, TypeEntity.class); + } + + @GetMapping + public String getAll(Model model) { + model.addAttribute( + "items", + typeService.getAll().stream() + .map(this::toDto) + .toList()); + return TYPE_VIEW; + } + + @GetMapping("/edit/") + public String create(Model model) { + model.addAttribute(TYPE_ATTRIBUTE, new TypeDto()); + return TYPE_EDIT_VIEW; + } + + @PostMapping("/edit/") + public String create( + @ModelAttribute(name = TYPE_ATTRIBUTE) @Valid TypeDto type, + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + return TYPE_EDIT_VIEW; + } + typeService.create(toEntity(type)); + return Constants.REDIRECT_VIEW + URL; + } + + @GetMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + Model model) { + if (id <= 0) { + throw new IllegalArgumentException(); + } + model.addAttribute(TYPE_ATTRIBUTE, toDto(typeService.get(id))); + return TYPE_EDIT_VIEW; + } + + @PostMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + @ModelAttribute(name = TYPE_ATTRIBUTE) @Valid TypeDto type, + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + return TYPE_EDIT_VIEW; + } + if (id <= 0) { + throw new IllegalArgumentException(); + } + typeService.update(id, toEntity(type)); + return Constants.REDIRECT_VIEW + URL; + } + + @PostMapping("/delete/{id}") + public String delete( + @PathVariable(name = "id") Long id) { + typeService.delete(id); + return Constants.REDIRECT_VIEW + URL; + } +} diff --git a/laba4/src/main/java/com/example/demo/types/api/TypeDto.java b/laba4/src/main/java/com/example/demo/types/api/TypeDto.java new file mode 100644 index 0000000..ed3350d --- /dev/null +++ b/laba4/src/main/java/com/example/demo/types/api/TypeDto.java @@ -0,0 +1,27 @@ +package com.example.demo.types.api; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class TypeDto { + private Long id; + @NotBlank + @Size(min = 5, max = 50) + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/laba4/src/main/java/com/example/demo/types/model/TypeEntity.java b/laba4/src/main/java/com/example/demo/types/model/TypeEntity.java new file mode 100644 index 0000000..14df9ea --- /dev/null +++ b/laba4/src/main/java/com/example/demo/types/model/TypeEntity.java @@ -0,0 +1,48 @@ +package com.example.demo.types.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = "types") +public class TypeEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 50) + private String name; + + public TypeEntity() { + } + + public TypeEntity(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final TypeEntity other = (TypeEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getName(), name); + } + +} diff --git a/laba4/src/main/java/com/example/demo/types/repository/TypeRepository.java b/laba4/src/main/java/com/example/demo/types/repository/TypeRepository.java new file mode 100644 index 0000000..fb863a7 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/types/repository/TypeRepository.java @@ -0,0 +1,13 @@ +package com.example.demo.types.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.example.demo.types.model.TypeEntity; + +public interface TypeRepository + extends CrudRepository, PagingAndSortingRepository { + Optional findByNameIgnoreCase(String name); +} diff --git a/laba4/src/main/java/com/example/demo/types/service/TypeService.java b/laba4/src/main/java/com/example/demo/types/service/TypeService.java new file mode 100644 index 0000000..3ec0d2b --- /dev/null +++ b/laba4/src/main/java/com/example/demo/types/service/TypeService.java @@ -0,0 +1,75 @@ +package com.example.demo.types.service; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.StreamSupport; + +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.repository.TypeRepository; + +@Service +public class TypeService { + private final TypeRepository repository; + + public TypeService(TypeRepository repository) { + this.repository = repository; + } + + private void checkName(Long id, String name) { + final Optional existsType = repository.findByNameIgnoreCase(name); + if (existsType.isPresent() && !existsType.get().getId().equals(id)) { + throw new IllegalArgumentException( + String.format("Type with name %s is already exists", name)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll(Sort.by("id")).spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public List getByIds(Collection ids) { + final List types = StreamSupport.stream(repository.findAllById(ids).spliterator(), false).toList(); + if (types.size() < ids.size()) { + throw new IllegalArgumentException("Invalid type"); + } + return types; + } + + @Transactional(readOnly = true) + public TypeEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(TypeEntity.class, id)); + } + + @Transactional + public TypeEntity create(TypeEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkName(null, entity.getName()); + return repository.save(entity); + } + + @Transactional + public TypeEntity update(Long id, TypeEntity entity) { + final TypeEntity existsEntity = get(id); + checkName(id, entity.getName()); + existsEntity.setName(entity.getName()); + return repository.save(existsEntity); + } + + @Transactional + public TypeEntity delete(Long id) { + final TypeEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } +} diff --git a/laba4/src/main/java/com/example/demo/users/api/UserCartController.java b/laba4/src/main/java/com/example/demo/users/api/UserCartController.java new file mode 100644 index 0000000..16a31c7 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/api/UserCartController.java @@ -0,0 +1,151 @@ +package com.example.demo.users.api; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.SessionAttributes; +import org.springframework.web.bind.support.SessionStatus; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.core.session.SessionCart; +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.service.OrderService; +import com.example.demo.types.api.TypeDto; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; + +import jakarta.validation.Valid; + +@Controller +@RequestMapping(UserCartController.URL) +@SessionAttributes("types") +public class UserCartController { + public static final String URL = "/cart"; + private static final String ORDER_VIEW = "cart"; + private static final String ORDER_ATTRIBUTE = "order"; + private static final String CART_ATTRIBUTE = "cart"; + + private final TypeService typeService; + private final OrderService orderService; + private final SessionCart cart; + private final ModelMapper modelMapper; + + public UserCartController( + TypeService typeService, + OrderService orderService, + SessionCart cart, + ModelMapper modelMapper) { + this.typeService = typeService; + this.orderService = orderService; + this.cart = cart; + this.modelMapper = modelMapper; + } + + private TypeDto toTypeDto(TypeEntity entity) { + return modelMapper.map(entity, TypeDto.class); + } + + private List toOrderEntities(Collection dtos) { + final Set typeIds = dtos.stream() + .map(UserCartDto::getType) + .collect(Collectors.toSet()); + final Map types = typeService.getByIds(typeIds).stream() + .collect(Collectors.toMap(TypeEntity::getId, Function.identity())); + return dtos.stream() + .map(dto -> { + final OrderEntity entity = modelMapper.map(dto, OrderEntity.class); + entity.setType(types.get(dto.getType())); + return entity; + }) + .toList(); + } + + @GetMapping + public String getCart(Model model) { + model.addAttribute("types", + typeService.getAll().stream() + .map(this::toTypeDto) + .toList()); + model.addAttribute(ORDER_ATTRIBUTE, new UserCartDto()); + model.addAttribute(CART_ATTRIBUTE, cart.values()); + return ORDER_VIEW; + } + + @PostMapping + public String addOrderToCart( + @ModelAttribute(name = ORDER_ATTRIBUTE) @Valid UserCartDto order, + BindingResult bindingResult, + SessionStatus status, + Model model) { + if (bindingResult.hasErrors()) { + return ORDER_VIEW; + } + status.setComplete(); + order.setTypeName(typeService.get(order.getType()).getName()); + cart.computeIfPresent(order.hashCode(), (key, value) -> { + value.setCount(value.getCount() + order.getCount()); + return value; + }); + cart.putIfAbsent(order.hashCode(), order); + return Constants.REDIRECT_VIEW + URL; + } + + @PostMapping("/save") + public String saveCart( + @CookieValue(value = Constants.USER_COOKIE, defaultValue = "0") long userId, + Model model) { + orderService.createAll(userId, toOrderEntities(cart.values())); + cart.clear(); + return Constants.REDIRECT_VIEW + URL; + } + + @PostMapping("/clear") + public String clearCart() { + cart.clear(); + return Constants.REDIRECT_VIEW + URL; + } + + @PostMapping("/increase") + public String increaseCartCount( + @RequestParam(name = "type") Long type, + @RequestParam(name = "price") Double price) { + cart.computeIfPresent(Objects.hash(type, price), (key, value) -> { + value.setCount(value.getCount() + 1); + return value; + }); + return Constants.REDIRECT_VIEW + URL; + } + + @PostMapping("/decrease") + public String decreaseCartCount( + @RequestParam(name = "type") Long type, + @RequestParam(name = "price") Double price) { + cart.computeIfPresent(Objects.hash(type, price), (key, value) -> { + value.setCount(value.getCount() - 1); + return value; + }); + final Map filteredCart = cart.entrySet() + .stream() + .filter(item -> item.getValue().getCount() > 0) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + cart.clear(); + cart.putAll(filteredCart); + return Constants.REDIRECT_VIEW + URL; + } +} diff --git a/laba4/src/main/java/com/example/demo/users/api/UserCartDto.java b/laba4/src/main/java/com/example/demo/users/api/UserCartDto.java new file mode 100644 index 0000000..584adf4 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/api/UserCartDto.java @@ -0,0 +1,65 @@ +package com.example.demo.users.api; + +import java.util.Objects; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +public class UserCartDto { + @NotNull + private Long type; + private String typeName; + @NotNull + @Min(1000) + private Double price; + @NotNull + @Min(1) + private Integer count; + + public Long getType() { + return type; + } + + public void setType(Long typeId) { + this.type = typeId; + } + + public String getTypeName() { + return typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + @Override + public int hashCode() { + return Objects.hash(type, price); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserCartDto other = (UserCartDto) obj; + return Objects.equals(type, other.type) && Objects.equals(price, other.price); + } +} diff --git a/laba4/src/main/java/com/example/demo/users/api/UserController.java b/laba4/src/main/java/com/example/demo/users/api/UserController.java new file mode 100644 index 0000000..7c5b23f --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/api/UserController.java @@ -0,0 +1,127 @@ +package com.example.demo.users.api; + +import java.util.Map; + +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import com.example.demo.core.api.PageAttributesMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@Controller +@RequestMapping(UserController.URL) +public class UserController { + public static final String URL = Constants.ADMIN_PREFIX + "/user"; + private static final String USER_VIEW = "user"; + private static final String USER_EDIT_VIEW = "user-edit"; + private static final String PAGE_ATTRIBUTE = "page"; + private static final String USER_ATTRIBUTE = "user"; + + private final UserService userService; + private final ModelMapper modelMapper; + + public UserController(UserService userService, ModelMapper modelMapper) { + this.userService = userService; + this.modelMapper = modelMapper; + } + + private UserDto toDto(UserEntity entity) { + return modelMapper.map(entity, UserDto.class); + } + + private UserEntity toEntity(UserDto dto) { + return modelMapper.map(dto, UserEntity.class); + } + + @GetMapping + public String getAll( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + final Map attributes = PageAttributesMapper.toAttributes( + userService.getAll(page, Constants.DEFUALT_PAGE_SIZE), this::toDto); + model.addAllAttributes(attributes); + model.addAttribute(PAGE_ATTRIBUTE, page); + return USER_VIEW; + } + + @GetMapping("/edit/") + public String create( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + model.addAttribute(USER_ATTRIBUTE, new UserDto()); + model.addAttribute(PAGE_ATTRIBUTE, page); + return USER_EDIT_VIEW; + } + + @PostMapping("/edit/") + public String create( + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = USER_ATTRIBUTE) @Valid UserDto user, + BindingResult bindingResult, + Model model, + RedirectAttributes redirectAttributes) { + if (bindingResult.hasErrors()) { + model.addAttribute(PAGE_ATTRIBUTE, page); + return USER_EDIT_VIEW; + } + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + userService.create(toEntity(user)); + return Constants.REDIRECT_VIEW + URL; + } + + @GetMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + Model model) { + if (id <= 0) { + throw new IllegalArgumentException(); + } + model.addAttribute(USER_ATTRIBUTE, toDto(userService.get(id))); + model.addAttribute(PAGE_ATTRIBUTE, page); + return USER_EDIT_VIEW; + } + + @PostMapping("/edit/{id}") + public String update( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @ModelAttribute(name = USER_ATTRIBUTE) @Valid UserDto user, + BindingResult bindingResult, + Model model, + RedirectAttributes redirectAttributes) { + if (bindingResult.hasErrors()) { + model.addAttribute(PAGE_ATTRIBUTE, page); + return USER_EDIT_VIEW; + } + if (id <= 0) { + throw new IllegalArgumentException(); + } + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + userService.update(id, toEntity(user)); + return Constants.REDIRECT_VIEW + URL; + } + + @PostMapping("/delete/{id}") + public String delete( + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + userService.delete(id); + return Constants.REDIRECT_VIEW + URL; + } +} diff --git a/laba4/src/main/java/com/example/demo/users/api/UserDto.java b/laba4/src/main/java/com/example/demo/users/api/UserDto.java new file mode 100644 index 0000000..abf6df8 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/api/UserDto.java @@ -0,0 +1,27 @@ +package com.example.demo.users.api; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class UserDto { + private Long id; + @NotBlank + @Size(min = 3, max = 20) + private String login; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } +} diff --git a/laba4/src/main/java/com/example/demo/users/api/UserLoginController.java b/laba4/src/main/java/com/example/demo/users/api/UserLoginController.java new file mode 100644 index 0000000..76d4916 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/api/UserLoginController.java @@ -0,0 +1,59 @@ +package com.example.demo.users.api; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; + +@Controller +public class UserLoginController { + public static final String LOGIN_URL = "/login"; + + private static final String LOGIN_VIEW = "login"; + private static final String USER_ATTRIBUTE = "user"; + + private final UserService userService; + + public UserLoginController(UserService userService) { + this.userService = userService; + } + + @GetMapping(LOGIN_URL) + public String login(Model model) { + model.addAttribute(USER_ATTRIBUTE, new UserLoginDto()); + return LOGIN_VIEW; + } + + @PostMapping(LOGIN_URL) + // Порядок аргументов важен! + public String login( + HttpServletResponse response, + @ModelAttribute(name = USER_ATTRIBUTE) @Valid UserLoginDto user, + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + return LOGIN_VIEW; + } + final UserEntity existsUser = userService.getByLogin(user.getLogin()); + final Cookie cookie = new Cookie(Constants.USER_COOKIE, existsUser.getId().toString()); + response.addCookie(cookie); + return Constants.REDIRECT_VIEW + "/"; + } + + @PostMapping("/logout") + public String logout(HttpServletResponse response) { + final Cookie cookie = new Cookie(Constants.USER_COOKIE, "0"); + response.addCookie(cookie); + return Constants.REDIRECT_VIEW + "/"; + } +} diff --git a/laba4/src/main/java/com/example/demo/users/api/UserLoginDto.java b/laba4/src/main/java/com/example/demo/users/api/UserLoginDto.java new file mode 100644 index 0000000..5949c3d --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/api/UserLoginDto.java @@ -0,0 +1,29 @@ +package com.example.demo.users.api; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class UserLoginDto { + @NotBlank + @Size(min = 3, max = 20) + private String login; + @NotBlank + @Size(min = 3, max = 20) + private String password; + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/laba4/src/main/java/com/example/demo/users/api/UserProfileController.java b/laba4/src/main/java/com/example/demo/users/api/UserProfileController.java new file mode 100644 index 0000000..c00af8f --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/api/UserProfileController.java @@ -0,0 +1,133 @@ +package com.example.demo.users.api; + +import java.util.stream.Collectors; + +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import com.example.demo.core.api.PageAttributesMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.orders.api.OrderDto; +import com.example.demo.orders.api.OrderGroupedDto; +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.model.OrderGrouped; +import com.example.demo.orders.service.OrderService; +import com.example.demo.types.api.TypeDto; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; +import com.example.demo.users.model.UserSubscriptionWithStatus; +import com.example.demo.users.service.UserService; + +import jakarta.validation.Valid; + +@Controller +public class UserProfileController { + private static final String PROFILE_VIEW = "profile"; + + private static final String PAGE_ATTRIBUTE = "page"; + private static final String TYPEID_ATTRIBUTE = "typeId"; + private static final String PROFILE_ATTRIBUTE = "profile"; + + private final OrderService orderService; + private final TypeService typeService; + private final UserService userService; + private final ModelMapper modelMapper; + + public UserProfileController( + OrderService orderService, + TypeService typeService, + UserService userService, + ModelMapper modelMapper) { + this.orderService = orderService; + this.typeService = typeService; + this.userService = userService; + this.modelMapper = modelMapper; + } + + private OrderDto toDto(OrderEntity entity) { + return modelMapper.map(entity, OrderDto.class); + } + + private OrderGroupedDto toGroupedDto(OrderGrouped entity) { + return modelMapper.map(entity, OrderGroupedDto.class); + } + + private TypeDto toTypeDto(TypeEntity entity) { + return modelMapper.map(entity, TypeDto.class); + } + + private UserSubscriptionDto tSubscriptionDto(UserSubscriptionWithStatus entity) { + return modelMapper.map(entity, UserSubscriptionDto.class); + } + + private UserSubscriptionWithStatus toSubscriptionWithStatus(UserSubscriptionDto dto) { + return modelMapper.map(dto, UserSubscriptionWithStatus.class); + } + + @GetMapping + public String getProfile( + @CookieValue(value = Constants.USER_COOKIE, defaultValue = "0") long userId, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @RequestParam(name = TYPEID_ATTRIBUTE, defaultValue = "0") int typeId, + Model model) { + if (userId <= 0) { + return Constants.REDIRECT_VIEW + UserLoginController.LOGIN_URL; + } + model.addAttribute(PAGE_ATTRIBUTE, page); + model.addAttribute(TYPEID_ATTRIBUTE, typeId); + model.addAllAttributes(PageAttributesMapper.toAttributes( + orderService.getAll(userId, typeId, page, Constants.DEFUALT_PAGE_SIZE), + this::toDto)); + model.addAttribute("stats", + orderService.getTotal(userId).stream() + .map(this::toGroupedDto) + .toList()); + model.addAttribute("types", + typeService.getAll().stream() + .map(this::toTypeDto) + .toList()); + model.addAttribute(PROFILE_ATTRIBUTE, + new UserProfileDto(userService.getUserSubscriptions(userId).stream() + .map(this::tSubscriptionDto) + .toList())); + return PROFILE_VIEW; + } + + @PostMapping + public String saveProfile( + @CookieValue(value = Constants.USER_COOKIE, defaultValue = "0") long userId, + @ModelAttribute(name = PROFILE_ATTRIBUTE) @Valid UserProfileDto profile, + BindingResult bindResult, + Model model) { + if (bindResult.hasErrors()) { + return PROFILE_VIEW; + } + userService.saveUserSubscriptions(userId, + profile.getSubscriptions().stream() + .map(this::toSubscriptionWithStatus) + .collect(Collectors.toSet())); + return Constants.REDIRECT_VIEW + "/"; + } + + @PostMapping("/delete/{id}") + public String deleteOrder( + @CookieValue(value = Constants.USER_COOKIE, defaultValue = "0") long userId, + @PathVariable(name = "id") Long id, + @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page, + @RequestParam(name = TYPEID_ATTRIBUTE, defaultValue = "0") int typeId, + RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page); + redirectAttributes.addAttribute(TYPEID_ATTRIBUTE, typeId); + orderService.delete(userId, id); + return Constants.REDIRECT_VIEW + "/"; + } +} diff --git a/laba4/src/main/java/com/example/demo/users/api/UserProfileDto.java b/laba4/src/main/java/com/example/demo/users/api/UserProfileDto.java new file mode 100644 index 0000000..0a72573 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/api/UserProfileDto.java @@ -0,0 +1,23 @@ +package com.example.demo.users.api; + +import java.util.ArrayList; +import java.util.List; + +public class UserProfileDto { + private List subscriptions = new ArrayList<>(); + + public UserProfileDto() { + } + + public UserProfileDto(List subscriptions) { + this.subscriptions = subscriptions; + } + + public List getSubscriptions() { + return subscriptions; + } + + public void setSubscriptions(List subscriptions) { + this.subscriptions = subscriptions; + } +} diff --git a/laba4/src/main/java/com/example/demo/users/api/UserSubscriptionDto.java b/laba4/src/main/java/com/example/demo/users/api/UserSubscriptionDto.java new file mode 100644 index 0000000..1c5a865 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/api/UserSubscriptionDto.java @@ -0,0 +1,31 @@ +package com.example.demo.users.api; + +public class UserSubscriptionDto { + private Long id; + private String name; + private boolean active; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } +} diff --git a/laba4/src/main/java/com/example/demo/users/model/UserEntity.java b/laba4/src/main/java/com/example/demo/users/model/UserEntity.java new file mode 100644 index 0000000..e1c48f7 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/model/UserEntity.java @@ -0,0 +1,91 @@ +package com.example.demo.users.model; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.usersubscription.model.UserSubscriptionEntity; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users") +public class UserEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 20) + private String login; + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + @OrderBy("id ASC") + private Set orders = new HashSet<>(); + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + @OrderBy("id ASC") + private Set userSubscriptions = new HashSet<>(); + + public UserEntity() { + } + + public UserEntity(String login) { + this.login = login; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public Set getOrders() { + return orders; + } + + public void addOrder(OrderEntity order) { + if (order.getUser() != this) { + order.setUser(this); + } + orders.add(order); + } + + public Set getUserSubscriptions() { + return userSubscriptions; + } + + public void addSubscription(UserSubscriptionEntity userSubscription) { + if (userSubscription.getUser() != this) { + userSubscription.setUser(this); + } + userSubscriptions.add(userSubscription); + } + + public void deleteSubscription(UserSubscriptionEntity userSubscription) { + if (userSubscription.getUser() != this) { + return; + } + userSubscriptions.remove(userSubscription); + } + + @Override + public int hashCode() { + return Objects.hash(id, login, orders, userSubscriptions); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserEntity other = (UserEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getLogin(), login) + && Objects.equals(other.getOrders(), orders) + && Objects.equals(other.getUserSubscriptions(), userSubscriptions); + } +} diff --git a/laba4/src/main/java/com/example/demo/users/model/UserSubscriptionWithStatus.java b/laba4/src/main/java/com/example/demo/users/model/UserSubscriptionWithStatus.java new file mode 100644 index 0000000..a460e81 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/model/UserSubscriptionWithStatus.java @@ -0,0 +1,60 @@ +package com.example.demo.users.model; + +import java.util.Objects; + +import com.example.demo.subscriptions.model.SubscriptionEntity; + +public class UserSubscriptionWithStatus { + private Long id; + private String name; + private boolean active; + + public UserSubscriptionWithStatus() { + } + + public UserSubscriptionWithStatus(SubscriptionEntity entity, boolean active) { + this.id = entity.getId(); + this.name = entity.getName(); + this.active = active; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, active); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserSubscriptionWithStatus other = (UserSubscriptionWithStatus) obj; + return Objects.equals(id, other.id) && Objects.equals(name, other.name) && active == other.active; + } + +} diff --git a/laba4/src/main/java/com/example/demo/users/repository/UserRepository.java b/laba4/src/main/java/com/example/demo/users/repository/UserRepository.java new file mode 100644 index 0000000..9bf3cae --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/repository/UserRepository.java @@ -0,0 +1,13 @@ +package com.example.demo.users.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.example.demo.users.model.UserEntity; + +public interface UserRepository + extends CrudRepository, PagingAndSortingRepository { + Optional findByLoginIgnoreCase(String login); +} diff --git a/laba4/src/main/java/com/example/demo/users/service/UserService.java b/laba4/src/main/java/com/example/demo/users/service/UserService.java new file mode 100644 index 0000000..315be1d --- /dev/null +++ b/laba4/src/main/java/com/example/demo/users/service/UserService.java @@ -0,0 +1,141 @@ +package com.example.demo.users.service; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.model.UserSubscriptionWithStatus; +import com.example.demo.users.repository.UserRepository; +import com.example.demo.usersubscription.model.UserSubscriptionEntity; + +@Service +public class UserService { + private final UserRepository repository; + private final SubscriptionService subscriptionService; + + public UserService( + UserRepository repository, + SubscriptionService subscriptionService) { + this.repository = repository; + this.subscriptionService = subscriptionService; + } + + private void checkLogin(Long id, String login) { + final Optional existsUser = repository.findByLoginIgnoreCase(login); + if (existsUser.isPresent() && !existsUser.get().getId().equals(id)) { + throw new IllegalArgumentException( + String.format("User with login %s is already exists", login)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public Page getAll(int page, int size) { + return repository.findAll(PageRequest.of(page, size, Sort.by("id"))); + } + + @Transactional(readOnly = true) + public UserEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(UserEntity.class, id)); + } + + @Transactional(readOnly = true) + public UserEntity getByLogin(String login) { + return repository.findByLoginIgnoreCase(login) + .orElseThrow(() -> new IllegalArgumentException("Invalid login")); + } + + @Transactional + public UserEntity create(UserEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkLogin(null, entity.getLogin()); + repository.save(entity); + subscriptionService.getAll().forEach(subscription -> { + final UserSubscriptionEntity userSubscription = new UserSubscriptionEntity(entity, subscription, true); + userSubscription.setUser(entity); + userSubscription.setSubscription(subscription); + }); + return repository.save(entity); + } + + @Transactional + public UserEntity update(long id, UserEntity entity) { + final UserEntity existsEntity = get(id); + checkLogin(id, entity.getLogin()); + existsEntity.setLogin(entity.getLogin()); + repository.save(existsEntity); + return existsEntity; + } + + @Transactional + public UserEntity delete(long id) { + final UserEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } + + private Map getUserSubscriptionsMap(Set subscriptions) { + return Optional.ofNullable(subscriptions).orElse(Collections.emptySet()).stream() + .collect(Collectors.toMap(item -> item.getSubscription().getId(), Function.identity())); + } + + @Transactional(readOnly = true) + public List getUserSubscriptions(long id) { + final Map userSubscriptions = getUserSubscriptionsMap( + get(id).getUserSubscriptions()); + return subscriptionService.getAll().stream() + .map(item -> { + final boolean active = Optional.ofNullable(userSubscriptions.get(item.getId())) + .map(UserSubscriptionEntity::isActive) + .orElse(false); + return new UserSubscriptionWithStatus(item, active); + }) + .toList(); + } + + @Transactional + public void saveUserSubscriptions(long id, Set subscriptions) { + final UserEntity existsUser = get(id); + final Map existsSubscriptions = subscriptionService.getAll().stream() + .collect(Collectors.toMap(SubscriptionEntity::getId, Function.identity())); + final Map userSubscriptions = getUserSubscriptionsMap( + existsUser.getUserSubscriptions()); + subscriptions.stream() + .filter(item -> existsSubscriptions.keySet().contains(item.getId())) + .forEach(item -> { + userSubscriptions.computeIfAbsent(item.getId(), key -> { + final UserSubscriptionEntity subscription = new UserSubscriptionEntity( + existsUser, existsSubscriptions.get(key), item.isActive()); + existsUser.addSubscription(subscription); + return subscription; + }); + userSubscriptions.computeIfPresent(item.getId(), (key, value) -> { + value.setActive(item.isActive()); + return value; + }); + }); + repository.save(existsUser); + } +} diff --git a/laba4/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionEntity.java b/laba4/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionEntity.java new file mode 100644 index 0000000..1f280f9 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionEntity.java @@ -0,0 +1,95 @@ +package com.example.demo.usersubscription.model; + +import java.util.Objects; + +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.users.model.UserEntity; + +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users_subscriptions") +public class UserSubscriptionEntity { + @EmbeddedId + private UserSubscriptionId id = new UserSubscriptionId(); + @ManyToOne + @MapsId("userId") + @JoinColumn(name = "user_id") + private UserEntity user; + @ManyToOne + @MapsId("subscriptionId") + @JoinColumn(name = "subscription_id") + private SubscriptionEntity subscription; + private boolean active; + + public UserSubscriptionEntity() { + } + + public UserSubscriptionEntity(UserEntity user, SubscriptionEntity subscription, boolean active) { + this.user = user; + this.subscription = subscription; + this.active = active; + } + + public UserSubscriptionId getId() { + return id; + } + + public void setId(UserSubscriptionId id) { + this.id = id; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + if (!user.getUserSubscriptions().contains(this)) { + user.getUserSubscriptions().add(this); + } + } + + public SubscriptionEntity getSubscription() { + return subscription; + } + + public void setSubscription(SubscriptionEntity subscription) { + this.subscription = subscription; + if (!subscription.getUserSubscriptions().contains(this)) { + subscription.getUserSubscriptions().add(this); + } + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + @Override + public int hashCode() { + return Objects.hash(id, user.getId(), subscription.getId(), active); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserSubscriptionEntity other = (UserSubscriptionEntity) obj; + return Objects.equals(id, other.id) + && Objects.equals(user.getId(), other.user.getId()) + && Objects.equals(subscription.getId(), other.subscription.getId()) + && active == other.active; + } + +} diff --git a/laba4/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionId.java b/laba4/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionId.java new file mode 100644 index 0000000..0b23f19 --- /dev/null +++ b/laba4/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionId.java @@ -0,0 +1,55 @@ +package com.example.demo.usersubscription.model; + +import java.util.Objects; +import java.util.Optional; + +import jakarta.persistence.Embeddable; + +@Embeddable +public class UserSubscriptionId { + private Long userId; + private Long subscriptionId; + + public UserSubscriptionId() { + } + + public UserSubscriptionId(Long userId, Long subscriptionId) { + this.userId = userId; + this.subscriptionId = subscriptionId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getSubscriptionId() { + return subscriptionId; + } + + public void setSubscriptionId(Long subscriptionId) { + this.subscriptionId = subscriptionId; + } + + @Override + public int hashCode() { + return Objects.hash( + Optional.ofNullable(userId).orElse(0L), + Optional.ofNullable(subscriptionId).orElse(0L)); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserSubscriptionId other = (UserSubscriptionId) obj; + return Objects.equals(userId, other.userId) + && Objects.equals(subscriptionId, other.subscriptionId); + } + +} diff --git a/laba4/src/main/resources/application.properties b/laba4/src/main/resources/application.properties new file mode 100644 index 0000000..62ab433 --- /dev/null +++ b/laba4/src/main/resources/application.properties @@ -0,0 +1,20 @@ +# Server +spring.main.banner-mode=off +server.port=8080 + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:file:./data +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false +# spring.jpa.show-sql=true +# spring.jpa.properties.hibernate.format_sql=true + +# H2 console +spring.h2.console.enabled=true \ No newline at end of file diff --git a/laba4/src/main/resources/public/css/style.css b/laba4/src/main/resources/public/css/style.css new file mode 100644 index 0000000..7423490 --- /dev/null +++ b/laba4/src/main/resources/public/css/style.css @@ -0,0 +1,67 @@ +html, +body { + height: 100%; +} + +h1 { + font-size: 1.5em; +} + +h2 { + font-size: 1.25em; +} + +h3 { + font-size: 1.1em; +} + +td form { + margin: 0; + padding: 0; + margin-top: -.25em; +} + +.button-fixed-width { + width: 150px; +} + +.button-link { + padding: 0; +} + +.invalid-feedback { + display: block; +} + +.w-10 { + width: 10% !important; +} + +.my-navbar { + background-color: #3c3c3c !important; +} + +.my-navbar .link a:hover { + text-decoration: underline; +} + +.my-navbar .logo { + width: 26px; + height: 26px; +} + +.my-footer { + background-color: #2c2c2c; + height: 32px; + color: rgba(255, 255, 255, 0.5); +} + +.cart-image { + width: 3.1rem; + padding: 0.25rem; + border-radius: 0.5rem; +} + +.cart-item { + height: auto; +} \ No newline at end of file diff --git a/laba4/src/main/resources/public/favicon.svg b/laba4/src/main/resources/public/favicon.svg new file mode 100644 index 0000000..7e1bd9a --- /dev/null +++ b/laba4/src/main/resources/public/favicon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/cart.html b/laba4/src/main/resources/templates/cart.html new file mode 100644 index 0000000..835acd2 --- /dev/null +++ b/laba4/src/main/resources/templates/cart.html @@ -0,0 +1,83 @@ + + + + + Корзина + + + +

+
+
+ Корзина +
+ +
+
+
+
+
+ [[${cartItem.typeName}]] [[${cartItem.price}]] * [[${cartItem.count}]] = + [[${cartItem.price * cartItem.count}]] +
+
+
+ +
+
+ +
+
+
+
+
+ Итого: [[${totalCart}]] ₽ +
+
+
+ +
+
+
+
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ +
+
+
+ + + + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/default.html b/laba4/src/main/resources/templates/default.html new file mode 100644 index 0000000..7e928b7 --- /dev/null +++ b/laba4/src/main/resources/templates/default.html @@ -0,0 +1,65 @@ + + + + + + + + My shop + + + + + + + + +
+
+
+ Автор, [[${#dates.year(#dates.createNow())}]] +
+ + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/error.html b/laba4/src/main/resources/templates/error.html new file mode 100644 index 0000000..faa6b0a --- /dev/null +++ b/laba4/src/main/resources/templates/error.html @@ -0,0 +1,37 @@ + + + + + Ошибка + + + +
+
    + +
  • + Неизвестная ошибка +
  • +
    + +
  • + Ошибка: [[${message}]] +
  • +
    + +
  • + Адрес: [[${url}]] +
  • +
  • + Класс исключения: [[${exception}]] +
  • +
  • + [[${method}]] ([[${file}]]:[[${line}]]) +
  • +
    +
+ На главную +
+ + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/login.html b/laba4/src/main/resources/templates/login.html new file mode 100644 index 0000000..a4d3ce5 --- /dev/null +++ b/laba4/src/main/resources/templates/login.html @@ -0,0 +1,30 @@ + + + + + Вход + + + +
+
+
+ + +
+
+
+ + +
+
+
+ +
+
+
+ + + + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/orders.html b/laba4/src/main/resources/templates/orders.html new file mode 100644 index 0000000..821dd62 --- /dev/null +++ b/laba4/src/main/resources/templates/orders.html @@ -0,0 +1,61 @@ + + + + + + +

Данные отсутствуют

+ +
+
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
IDТип заказаЦенаКоличествоСумма
+
+ + + +
+
+
+ + +
+ + + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/pagination.html b/laba4/src/main/resources/templates/pagination.html new file mode 100644 index 0000000..b11664a --- /dev/null +++ b/laba4/src/main/resources/templates/pagination.html @@ -0,0 +1,51 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/profile.html b/laba4/src/main/resources/templates/profile.html new file mode 100644 index 0000000..f8ea866 --- /dev/null +++ b/laba4/src/main/resources/templates/profile.html @@ -0,0 +1,60 @@ + + + + + Личный кабинет + + + +
+ + +
+
+ +
+
+
    +
  • + [[${stat.typeName}]]: [[${stat.totalPrice}]] ₽ ([[${stat.totalCount}]] + шт.) +
  • +
+
+
+
+
+ + + +
+ +
+
+
+ + +
+ + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/subscription-edit.html b/laba4/src/main/resources/templates/subscription-edit.html new file mode 100644 index 0000000..6d940ec --- /dev/null +++ b/laba4/src/main/resources/templates/subscription-edit.html @@ -0,0 +1,29 @@ + + + + + Редакторовать списки рассылки + + + +
+
+
+ + +
+
+ + +
+
+
+ + Отмена +
+
+
+ + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/subscription.html b/laba4/src/main/resources/templates/subscription.html new file mode 100644 index 0000000..b3123a9 --- /dev/null +++ b/laba4/src/main/resources/templates/subscription.html @@ -0,0 +1,51 @@ + + + + + Списки рассылки + + + +
+ +

Данные отсутствуют

+ +

Списки рассылки

+ + + + + + + + + + + + + + + + + + + +
IDСписок рассылки
+
+ +
+
+
+ +
+
+
+
+
+ + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/type-edit.html b/laba4/src/main/resources/templates/type-edit.html new file mode 100644 index 0000000..91f03ca --- /dev/null +++ b/laba4/src/main/resources/templates/type-edit.html @@ -0,0 +1,28 @@ + + + + + Редакторовать тип заказа + + + +
+
+
+ + +
+
+ + +
+
+
+ + Отмена +
+
+
+ + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/type.html b/laba4/src/main/resources/templates/type.html new file mode 100644 index 0000000..ec0909e --- /dev/null +++ b/laba4/src/main/resources/templates/type.html @@ -0,0 +1,50 @@ + + + + + Типы заказов + + + +
+ +

Данные отсутствуют

+ +

Типы заказов

+ + + + + + + + + + + + + + + + + + + +
IDТип заказа
+
+ +
+
+
+ +
+
+
+
+
+ + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/user-edit.html b/laba4/src/main/resources/templates/user-edit.html new file mode 100644 index 0000000..659be54 --- /dev/null +++ b/laba4/src/main/resources/templates/user-edit.html @@ -0,0 +1,29 @@ + + + + + Редакторовать пользователя + + + +
+
+
+ + +
+
+ + +
+
+
+ + Отмена +
+
+
+ + + \ No newline at end of file diff --git a/laba4/src/main/resources/templates/user.html b/laba4/src/main/resources/templates/user.html new file mode 100644 index 0000000..cce1336 --- /dev/null +++ b/laba4/src/main/resources/templates/user.html @@ -0,0 +1,56 @@ + + + + + Пользователи + + + +
+ +

Данные отсутствуют

+ +

Пользователи

+ + + + + + + + + + + + + + + + + + + +
IDИмя пользователя
+
+ + +
+
+
+ + +
+
+
+ + +
+ + + \ No newline at end of file diff --git a/laba4/src/test/java/com/example/demo/TypeServiceTests.java b/laba4/src/test/java/com/example/demo/TypeServiceTests.java new file mode 100644 index 0000000..10043cd --- /dev/null +++ b/laba4/src/test/java/com/example/demo/TypeServiceTests.java @@ -0,0 +1,79 @@ +package com.example.demo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; + +@SpringBootTest +class TypeServiceTests { + @Autowired + private TypeService typeService; + + private TypeEntity type; + + @BeforeEach + void createData() { + removeData(); + + type = typeService.create(new TypeEntity("Ноутбук")); + typeService.create(new TypeEntity("Телефон")); + typeService.create(new TypeEntity("Игровая приставка")); + } + + @AfterEach + void removeData() { + typeService.getAll().forEach(item -> typeService.delete(item.getId())); + } + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> typeService.get(0L)); + } + + @Test + void createTest() { + Assertions.assertEquals(3, typeService.getAll().size()); + Assertions.assertEquals(type, typeService.get(type.getId())); + } + + @Test + void createNotUniqueTest() { + final TypeEntity nonUniqueType = new TypeEntity("Ноутбук"); + Assertions.assertThrows(IllegalArgumentException.class, () -> typeService.create(nonUniqueType)); + } + + @Test + void createNullableTest() { + final TypeEntity nullableType = new TypeEntity(null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> typeService.create(nullableType)); + } + + @Test + void updateTest() { + final String test = "TEST"; + final String oldName = type.getName(); + final TypeEntity newEntity = typeService.update(type.getId(), new TypeEntity(test)); + Assertions.assertEquals(3, typeService.getAll().size()); + Assertions.assertEquals(newEntity, typeService.get(type.getId())); + Assertions.assertEquals(test, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + } + + @Test + void deleteTest() { + typeService.delete(type.getId()); + Assertions.assertEquals(2, typeService.getAll().size()); + + final TypeEntity newEntity = typeService.create(new TypeEntity(type.getName())); + Assertions.assertEquals(3, typeService.getAll().size()); + Assertions.assertNotEquals(type.getId(), newEntity.getId()); + } +} diff --git a/laba4/src/test/java/com/example/demo/UserOrderServiceTest.java b/laba4/src/test/java/com/example/demo/UserOrderServiceTest.java new file mode 100644 index 0000000..866f095 --- /dev/null +++ b/laba4/src/test/java/com/example/demo/UserOrderServiceTest.java @@ -0,0 +1,132 @@ +package com.example.demo; + +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.service.OrderService; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.model.UserSubscriptionWithStatus; +import com.example.demo.users.service.UserService; + +import jakarta.persistence.EntityManager; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class UserOrderServiceTest { + @Autowired + private EntityManager entityManager; + @Autowired + private TypeService typeService; + @Autowired + private SubscriptionService subscriptionService; + @Autowired + private OrderService orderService; + @Autowired + private UserService userService; + + private TypeEntity type1; + private TypeEntity type2; + private TypeEntity type3; + + private UserEntity user1; + private UserEntity user2; + + @BeforeEach + void createData() { + removeData(); + + type1 = typeService.create(new TypeEntity("Ноутбук")); + type2 = typeService.create(new TypeEntity("Телефон")); + type3 = typeService.create(new TypeEntity("Игровая приставка")); + + subscriptionService.create(new SubscriptionEntity("Подписка 1")); + subscriptionService.create(new SubscriptionEntity("Подписка 2")); + subscriptionService.create(new SubscriptionEntity("Подписка 3")); + + user1 = userService.create(new UserEntity("user1")); + user2 = userService.create(new UserEntity("user2")); + + final var orders = List.of( + new OrderEntity(type1, 49999.00, 20), + new OrderEntity(type1, 129999.00, 3), + new OrderEntity(type2, 15450.50, 30), + new OrderEntity(type2, 69900.50, 10), + new OrderEntity(type2, 150000.00, 6), + new OrderEntity(type3, 75000.00, 6), + new OrderEntity(type3, 67800.00, 3)); + orders.forEach(order -> orderService.create(user1.getId(), order)); + } + + @AfterEach + void removeData() { + userService.getAll().forEach(item -> userService.delete(item.getId())); + typeService.getAll().forEach(item -> typeService.delete(item.getId())); + subscriptionService.getAll().forEach(item -> subscriptionService.delete(item.getId())); + } + + @Test + @Order(1) + void createTest() { + Assertions.assertEquals(7, orderService.getAll(user1.getId(), 0).size()); + Assertions.assertEquals(0, orderService.getAll(user2.getId(), 0).size()); + } + + @Test + @Order(2) + void orderFilterTest() { + Assertions.assertEquals(2, orderService.getAll(user1.getId(), type1.getId()).size()); + Assertions.assertEquals(3, orderService.getAll(user1.getId(), type2.getId()).size()); + Assertions.assertEquals(2, orderService.getAll(user1.getId(), type3.getId()).size()); + } + + @Test + @Order(3) + void subscriptionsTest() { + Assertions.assertEquals(3, userService.getUserSubscriptions(user1.getId()).size()); + + final UserSubscriptionWithStatus subscription = userService.getUserSubscriptions(user1.getId()).get(0); + subscription.setActive(false); + userService.saveUserSubscriptions(user1.getId(), Set.of(subscription)); + Assertions.assertEquals(2, + userService.getUserSubscriptions(user1.getId()).stream() + .filter(UserSubscriptionWithStatus::isActive) + .count()); + + subscription.setActive(true); + userService.saveUserSubscriptions(user1.getId(), Set.of(subscription)); + Assertions.assertEquals(3, + userService.getUserSubscriptions(user1.getId()).stream() + .filter(UserSubscriptionWithStatus::isActive) + .count()); + } + + @Test + @Order(6) + void userCascadeDeleteTest() { + userService.delete(user1.getId()); + final var orders = entityManager.createQuery( + "select count(o) from OrderEntity o where o.user.id = :userId"); + orders.setParameter("userId", user1.getId()); + Assertions.assertEquals(0, orders.getFirstResult()); + final var subscriptions = entityManager.createQuery( + "select count(us) from UserSubscriptionEntity us where us.user.id = :userId"); + subscriptions.setParameter("userId", user1.getId()); + Assertions.assertEquals(0, subscriptions.getFirstResult()); + + } +} diff --git a/laba4/src/test/resources/application.properties b/laba4/src/test/resources/application.properties new file mode 100644 index 0000000..d5f355c --- /dev/null +++ b/laba4/src/test/resources/application.properties @@ -0,0 +1,14 @@ +# Server +spring.main.banner-mode=off + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false \ No newline at end of file