From 22dd4af048db081b8279e5bfcee1c758e54a3121 Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Fri, 21 Aug 2026 23:09:01 +0300 Subject: [PATCH 01/16] =?UTF-8?q?=D1=83=D0=B1=D0=BE=D1=80=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/main-service/pom.xml | 74 ++++--------------- core/pom.xml | 50 +++++++++++++ infra/config-repo/application.yml | 5 +- infra/config-repo/core/main-service.yaml | 2 + infra/config-server/pom.xml | 12 --- .../src/main/resources/application.yml | 9 ++- infra/discovery-server/pom.xml | 12 --- .../src/main/resources/application.yml | 23 +++--- infra/gateway-server/pom.xml | 8 -- .../src/main/resources/application.yml | 6 +- infra/pom.xml | 13 ++++ pom.xml | 1 + 12 files changed, 103 insertions(+), 112 deletions(-) diff --git a/core/main-service/pom.xml b/core/main-service/pom.xml index 2098d21..a6098ed 100644 --- a/core/main-service/pom.xml +++ b/core/main-service/pom.xml @@ -13,12 +13,6 @@ main-service jar - - 21 - 21 - UTF-8 - - ru.practicum @@ -30,12 +24,7 @@ stat-client 0.0.1-SNAPSHOT - - org.projectlombok - lombok - - - + org.springframework.boot spring-boot-starter-web @@ -53,21 +42,18 @@ spring-boot-starter-test test + + + org.projectlombok + lombok + + + org.zalando logbook-spring-boot-starter 4.0.3 - - org.postgresql - postgresql - runtime - - - com.h2database - h2 - runtime - io.hypersistence hypersistence-utils-hibernate-63 @@ -76,39 +62,14 @@ - org.springframework.cloud - spring-cloud-starter-netflix-eureka-client - - - org.springframework.cloud - spring-cloud-starter-config - - - - org.springframework.cloud - spring-cloud-starter-loadbalancer - - - com.github.ben-manes.caffeine - caffeine - - - - org.springframework.retry - spring-retry - - - org.springframework.boot - spring-boot-starter-aop - - - - org.springframework.boot - spring-boot-starter-actuator + org.postgresql + postgresql + runtime - io.micrometer - micrometer-registry-prometheus + com.h2database + h2 + runtime @@ -118,13 +79,6 @@ org.springframework.boot spring-boot-maven-plugin - - org.apache.maven.plugins - maven-surefire-plugin - - -Duser.language=ru -Duser.country=RU - - diff --git a/core/pom.xml b/core/pom.xml index 144fd02..6f1efc4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -17,4 +17,54 @@ main-service + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-config + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.github.ben-manes.caffeine + caffeine + + + + org.springframework.retry + spring-retry + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-actuator + + + io.micrometer + micrometer-registry-prometheus + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + -Duser.language=ru -Duser.country=RU + + + + + \ No newline at end of file diff --git a/infra/config-repo/application.yml b/infra/config-repo/application.yml index 9b5ac24..0a1d54f 100644 --- a/infra/config-repo/application.yml +++ b/infra/config-repo/application.yml @@ -1,9 +1,10 @@ spring: + output: + ansi: + enabled: ALWAYS main: banner-mode: off log-startup-info: false - jackson: - serialization.INDENT_OUTPUT: true logging: level: diff --git a/infra/config-repo/core/main-service.yaml b/infra/config-repo/core/main-service.yaml index 15b541d..df0e0e4 100644 --- a/infra/config-repo/core/main-service.yaml +++ b/infra/config-repo/core/main-service.yaml @@ -2,6 +2,8 @@ spring: sql.init: mode: ALWAYS schema-locations: classpath:schema-postgres.sql + jackson: + serialization.INDENT_OUTPUT: true datasource: driver-class-name: org.postgresql.Driver url: jdbc:postgresql://localhost:6541/ewm-main diff --git a/infra/config-server/pom.xml b/infra/config-server/pom.xml index 65ffdf8..7220a8e 100644 --- a/infra/config-server/pom.xml +++ b/infra/config-server/pom.xml @@ -22,18 +22,6 @@ org.springframework.cloud spring-cloud-starter-netflix-eureka-client - - org.springframework.cloud - spring-cloud-starter-loadbalancer - - - com.github.ben-manes.caffeine - caffeine - - - org.springframework.retry - spring-retry - diff --git a/infra/config-server/src/main/resources/application.yml b/infra/config-server/src/main/resources/application.yml index 35e6da8..4722886 100644 --- a/infra/config-server/src/main/resources/application.yml +++ b/infra/config-server/src/main/resources/application.yml @@ -2,13 +2,14 @@ server: port: 0 spring: + application: + name: config-server + output: + ansi: + enabled: ALWAYS main: banner-mode: off log-startup-info: false - jackson: - serialization.INDENT_OUTPUT: true - application: - name: config-server profiles: active: native cloud: diff --git a/infra/discovery-server/pom.xml b/infra/discovery-server/pom.xml index 98616f4..5f2fb1c 100644 --- a/infra/discovery-server/pom.xml +++ b/infra/discovery-server/pom.xml @@ -17,18 +17,6 @@ org.springframework.cloud spring-cloud-starter-netflix-eureka-server - - org.springframework.retry - spring-retry - - - org.springframework.cloud - spring-cloud-starter-loadbalancer - - - com.github.ben-manes.caffeine - caffeine - diff --git a/infra/discovery-server/src/main/resources/application.yml b/infra/discovery-server/src/main/resources/application.yml index 4d26970..277b2e1 100644 --- a/infra/discovery-server/src/main/resources/application.yml +++ b/infra/discovery-server/src/main/resources/application.yml @@ -2,13 +2,14 @@ server: port: 8761 spring: + application: + name: discovery-server + output: + ansi: + enabled: ALWAYS main: banner-mode: off log-startup-info: false - jackson: - serialization.INDENT_OUTPUT: true - application: - name: discovery-server eureka: client: @@ -17,14 +18,14 @@ eureka: register-with-eureka: false fetch-registry: false -logging: - level: - root: info - org: - springframework: error - management: endpoints: web: exposure: - include: health, metrics, prometheus, info, mappings \ No newline at end of file + include: health, metrics, prometheus, info, mappings + +logging: + level: + root: info + org: + springframework: error \ No newline at end of file diff --git a/infra/gateway-server/pom.xml b/infra/gateway-server/pom.xml index e260520..c13b18a 100644 --- a/infra/gateway-server/pom.xml +++ b/infra/gateway-server/pom.xml @@ -18,10 +18,6 @@ org.springframework.cloud spring-cloud-starter-gateway-server-webflux - - org.springframework.cloud - spring-cloud-starter-loadbalancer - org.springframework.cloud spring-cloud-starter-netflix-eureka-client @@ -30,10 +26,6 @@ org.springframework.cloud spring-cloud-starter-config - - com.github.ben-manes.caffeine - caffeine - org.projectlombok lombok diff --git a/infra/gateway-server/src/main/resources/application.yml b/infra/gateway-server/src/main/resources/application.yml index 3b2e0a2..206f54c 100644 --- a/infra/gateway-server/src/main/resources/application.yml +++ b/infra/gateway-server/src/main/resources/application.yml @@ -10,9 +10,9 @@ spring: service-id: config-server retry: initial-interval: 1000 - max-interval: 5000 - multiplier: 1.5 - max-attempts: 6 + max-interval: 10000 + multiplier: 2.0 + max-attempts: 20 eureka: client: diff --git a/infra/pom.xml b/infra/pom.xml index e6130e1..3d41116 100644 --- a/infra/pom.xml +++ b/infra/pom.xml @@ -20,6 +20,19 @@ + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.github.ben-manes.caffeine + caffeine + + + org.springframework.retry + spring-retry + + org.springframework.boot spring-boot-starter-actuator diff --git a/pom.xml b/pom.xml index 343ca5b..be45625 100644 --- a/pom.xml +++ b/pom.xml @@ -167,6 +167,7 @@ + check From 7ca98e5a1481b1917c17fb51b2965eb2b440af0d Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Wed, 26 Aug 2026 22:35:19 +0300 Subject: [PATCH 02/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Запуск проекта Тесты пока не проходят --- core/aggregation-api/pom.xml | 41 ++ .../client/RemoteCallExecutor.java | 39 ++ .../aggregation/client/RemoteCallResult.java | 14 + .../client/RemoteExceptionMapper.java | 58 ++ .../aggregation/client/event/EventClient.java | 31 ++ .../client/event/EventFallbackFactory.java | 34 ++ .../client/request/RequestClient.java | 35 ++ .../request/RequestFallbackFactory.java | 41 ++ .../aggregation/client/user/UserClient.java | 27 + .../client/user/UserFallbackFactory.java | 36 ++ .../dto/category/CategoryDto.java | 6 +- .../dto/category/NewCategoryDto.java | 6 +- .../dto/compilation/CompilationDto.java | 10 +- .../compilation/CompilationSearchFilter.java | 2 +- .../dto/compilation/CompilationUpdateDto.java | 2 +- .../dto/compilation/NewCompilationDto.java | 2 +- .../dto/event/request}/AdminGetDto.java | 16 +- .../dto/event/request}/FreeGetDto.java | 14 +- .../dto/event/request}/NewEventDto.java | 4 +- .../request}/UpdateEventAdminRequest.java | 6 +- .../request}/UpdateEventUserRequest.java | 14 +- .../dto/event/response/EventFullDto.java | 76 +++ .../dto/event/response}/EventShortDto.java | 6 +- .../aggregation/dto/rating/RatingRequest.java | 13 + .../dto/rating/RatingResponse.java | 13 + .../dto/rating/RatingUpdateRequest.java | 22 + .../dto/request/EventRequestCount.java | 13 + .../EventRequestStatusUpdateRequest.java | 22 + .../EventRequestStatusUpdateResult.java | 17 + .../dto/request/ParticipationRequestDto.java | 22 + .../aggregation}/dto/user/NewUserRequest.java | 7 +- .../aggregation/dto/user/UserDto.java | 17 + .../aggregation}/dto/user/UserShortDto.java | 9 +- .../aggregation/enums/AdminStateAction.java | 6 + .../aggregation}/enums/EventState.java | 2 +- .../enums/ParticipationStatus.java | 8 + .../aggregation}/enums/Reaction.java | 2 +- .../enums/RequestUpdateStatus.java | 8 + .../aggregation/enums/UserStateAction.java | 8 + .../aggregation}/error/ApiError.java | 4 +- .../error/GeneralErrorHandler.java} | 179 +++--- .../error/exception/BusinessException.java | 7 + .../error/exception/ConflictException.java | 8 + .../error/exception/NotFoundException.java | 8 + .../exception/ServiceProcessingException.java | 7 + .../ServiceUnavailableException.java | 7 + .../processing/EventProcessingException.java | 11 + .../processing/RatingProcessingException.java | 11 + .../RequestProcessingException.java | 11 + .../processing/UserProcessingException.java | 11 + .../exception/stats}/HitRequestException.java | 2 +- .../stats/StatsResponseException.java | 14 + .../EventServiceUnavailableException.java | 16 + .../RatingServiceUnavailableException.java | 16 + .../RequestServiceUnavailableException.java | 16 + .../UserServiceUnavailableException.java | 16 + .../aggregation/model}/BaseEntity.java | 2 +- .../aggregation}/model/Location.java | 2 +- .../repository/EventFeignRepository.java | 14 + .../repository/EventFeignRepositoryImpl.java | 93 ++++ .../repository/RequestFeignRepository.java | 19 + .../RequestFeignRepositoryImpl.java | 100 ++++ .../repository/UserFeignRepository.java | 16 + .../repository/UserFeignRepositoryImpl.java | 94 ++++ core/event-service/pom.xml | 132 +++++ .../practicum/ewm/events/EventServiceApp.java | 28 + .../events/config/EventServiceConfig.java} | 4 +- .../admin/AdminCategoryController.java | 8 +- .../admin/AdminCompilationController.java | 10 +- .../admin/AdminEventController.java | 43 +- .../free/FreeCategoryController.java | 8 +- .../free/FreeCompilationController.java | 14 +- .../controller/free/FreeEventController.java | 52 +- .../system/SystemEventController.java | 28 + .../system/SystemRatingController.java | 28 + .../controller/user/UserEventController.java | 42 +- .../ewm/events/entity}/Category.java | 4 +- .../ewm/events/entity}/Compilation.java | 4 +- .../practicum/ewm/events/entity}/Event.java | 10 +- .../ewm/events}/mapper/CategoryMapper.java | 14 +- .../ewm/events}/mapper/CompilationMapper.java | 14 +- .../ewm/events}/mapper/EventMapper.java | 39 +- .../ewm/events}/mapper/StateMapper.java | 10 +- .../ewm/events/mapper/UserMapper.java | 28 + .../practicum/ewm/events/model/EventData.java | 18 + .../ru/practicum/ewm/events/model/User.java | 19 + .../repository}/CategoryRepository.java | 4 +- .../repository}/CompilationRepository.java | 4 +- .../events/repository}/EventRepository.java | 16 +- .../service/category/CategoryService.java | 12 +- .../service/category/CategoryServiceImpl.java | 19 +- .../compilation/CompilationService.java | 14 +- .../compilation/CompilationServiceImpl.java | 228 ++++++++ .../events}/service/event/EventService.java | 18 +- .../service/event/EventServiceImpl.java | 514 ++++++++++++++++++ .../events/service/rating/RatingService.java | 10 + .../service/rating/RatingServiceImpl.java | 27 + .../service/request/RequestService.java | 18 + .../service/request/RequestServiceImpl.java | 51 ++ .../specification/EventSpecifications.java | 14 +- .../events}/specification/SpecBuilder.java | 3 +- .../events/statistic/StatRestRepository.java} | 15 +- .../main/resources/application-test.yaml.bak} | 0 .../src/main/resources/application.yml | 2 +- .../src/main/resources/schema-h2.sql.bak} | 0 .../src/main/resources/schema-postgres.sql | 47 ++ .../main/resources/schema-postgres.sql.bak} | 0 .../java/ru/practicum/ewm/MainServiceApp.java | 13 - .../practicum/ewm/dto/event/EventFullDto.java | 56 -- .../ewm/dto/rating/RatingRequest.java | 18 - .../ewm/dto/rating/RatingResponse.java | 18 - .../EventRequestStatusUpdateRequest.java | 17 - .../EventRequestStatusUpdateResult.java | 12 - .../dto/request/ParticipationRequestDto.java | 14 - .../ru/practicum/ewm/dto/user/UserDto.java | 11 - .../practicum/ewm/mapper/RequestMapper.java | 31 -- .../ewm/model/enums/AdminStateAction.java | 5 - .../ewm/model/enums/ParticipationStatus.java | 5 - .../ewm/model/enums/RequestUpdateStatus.java | 5 - .../ewm/model/enums/UserStateAction.java | 5 - .../compilation/CompilationServiceImpl.java | 206 ------- .../ewm/service/event/EventServiceImpl.java | 465 ---------------- .../ewm/service/rating/RatingService.java | 11 - .../ewm/service/rating/RatingServiceImpl.java | 94 ---- .../service/request/EventRequestCount.java | 9 - .../ewm/service/user/UserService.java | 18 - .../ewm/service/user/UserServiceImpl.java | 60 -- .../error/exception/ConflictException.java | 8 - .../error/exception/NotFoundException.java | 8 - .../exception/StatResponseException.java | 14 - .../test/java/ru/practicum/ewm/TestUtils.java | 103 ---- .../admin/AdminCategoryControllerTest.java | 67 --- .../admin/AdminUserControllerTest.java | 85 --- .../ru/practicum/ewm/dto/NewEventDtoTest.java | 200 ------- .../ewm/service/AdminCategoryServiceTest.java | 112 ---- .../ewm/service/AdminUserServiceTest.java | 101 ---- .../ewm/service/EventServiceImplTest.java | 162 ------ core/pom.xml | 44 +- core/rating-service/pom.xml | 121 +++++ .../ewm/ratings/RatingServiceApp.java | 27 + .../ratings/controller}/RatingController.java | 9 +- .../practicum/ewm/ratings/entity}/Rating.java | 14 +- .../ratings/repository}/RatingRepository.java | 8 +- .../ewm/ratings/service/RatingService.java | 14 + .../ratings/service/RatingServiceImpl.java | 97 ++++ .../src/main/resources/application.yml | 31 ++ .../src/main/resources/schema-postgres.sql | 8 + core/request-service/pom.xml | 121 +++++ .../ewm/requests/RequestServiceApp.java | 27 + .../controller/AdminRequestController.java | 29 + .../controller}/UserRequestController.java | 40 +- .../entity}/ParticipationRequest.java | 12 +- .../ewm/requests/mapper/RequestMapper.java | 33 ++ .../ewm/requests/mapper/UserMapper.java | 19 + .../ru/practicum/ewm/requests/model/User.java | 19 + .../repository}/RequestRepository.java | 24 +- .../ewm/requests/service}/RequestService.java | 19 +- .../requests/service}/RequestServiceImpl.java | 105 ++-- .../src/main/resources/application.yml | 31 ++ .../src/main/resources/schema-postgres.sql | 9 + core/{main-service => user-service}/pom.xml | 53 +- .../practicum/ewm/users/UserServiceApp.java | 27 + .../controller}/AdminUserController.java | 50 +- .../ru/practicum/ewm/users/entity}/User.java | 4 +- .../ewm/users}/mapper/UserMapper.java | 11 +- .../ewm/users/repository}/UserRepository.java | 4 +- .../ewm/users/service/UserService.java | 26 + .../ewm/users/service/UserServiceImpl.java | 81 +++ .../src/main/resources/application.yml | 31 ++ .../src/main/resources/schema-postgres.sql | 7 + docker-compose.yml | 13 +- docker-init.sql | 20 + infra/config-repo/core/event-service.yml | 52 ++ infra/config-repo/core/main-service.yaml | 23 - infra/config-repo/core/rating-service.yml | 52 ++ infra/config-repo/core/request-service.yml | 52 ++ infra/config-repo/core/user-service.yml | 52 ++ infra/config-repo/infra/gateway-server.yml | 23 +- .../src/main/resources/application.yml | 3 + pom.xml | 2 +- 180 files changed, 3928 insertions(+), 2418 deletions(-) create mode 100644 core/aggregation-api/pom.xml create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallExecutor.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallResult.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserFallbackFactory.java rename core/{main-service/src/main/java/ru/practicum/ewm => aggregation-api/src/main/java/ru/practicum/aggregation}/dto/category/CategoryDto.java (66%) rename core/{main-service/src/main/java/ru/practicum/ewm => aggregation-api/src/main/java/ru/practicum/aggregation}/dto/category/NewCategoryDto.java (71%) rename core/{main-service/src/main/java/ru/practicum/ewm => aggregation-api/src/main/java/ru/practicum/aggregation}/dto/compilation/CompilationDto.java (53%) rename core/{main-service/src/main/java/ru/practicum/ewm => aggregation-api/src/main/java/ru/practicum/aggregation}/dto/compilation/CompilationSearchFilter.java (85%) rename core/{main-service/src/main/java/ru/practicum/ewm => aggregation-api/src/main/java/ru/practicum/aggregation}/dto/compilation/CompilationUpdateDto.java (88%) rename core/{main-service/src/main/java/ru/practicum/ewm => aggregation-api/src/main/java/ru/practicum/aggregation}/dto/compilation/NewCompilationDto.java (90%) rename core/{main-service/src/main/java/ru/practicum/ewm/dto/event => aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request}/AdminGetDto.java (54%) rename core/{main-service/src/main/java/ru/practicum/ewm/dto/event => aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request}/FreeGetDto.java (65%) rename core/{main-service/src/main/java/ru/practicum/ewm/dto/event => aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request}/NewEventDto.java (89%) rename core/{main-service/src/main/java/ru/practicum/ewm/dto/event => aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request}/UpdateEventAdminRequest.java (75%) rename core/{main-service/src/main/java/ru/practicum/ewm/dto/event => aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request}/UpdateEventUserRequest.java (63%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventFullDto.java rename core/{main-service/src/main/java/ru/practicum/ewm/dto/event => aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response}/EventShortDto.java (63%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingRequest.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingResponse.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingUpdateRequest.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestCount.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateRequest.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateResult.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/ParticipationRequestDto.java rename core/{main-service/src/main/java/ru/practicum/ewm => aggregation-api/src/main/java/ru/practicum/aggregation}/dto/user/NewUserRequest.java (76%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserDto.java rename core/{main-service/src/main/java/ru/practicum/ewm => aggregation-api/src/main/java/ru/practicum/aggregation}/dto/user/UserShortDto.java (73%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/AdminStateAction.java rename core/{main-service/src/main/java/ru/practicum/ewm/model => aggregation-api/src/main/java/ru/practicum/aggregation}/enums/EventState.java (60%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/ParticipationStatus.java rename core/{main-service/src/main/java/ru/practicum/ewm/model => aggregation-api/src/main/java/ru/practicum/aggregation}/enums/Reaction.java (52%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/RequestUpdateStatus.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/UserStateAction.java rename core/{main-service/src/main/java/ru/practicum/ewm/util => aggregation-api/src/main/java/ru/practicum/aggregation}/error/ApiError.java (87%) rename core/{main-service/src/main/java/ru/practicum/ewm/util/error/MainServiceErrorHandler.java => aggregation-api/src/main/java/ru/practicum/aggregation/error/GeneralErrorHandler.java} (50%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/BusinessException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ConflictException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/NotFoundException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceProcessingException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceUnavailableException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/EventProcessingException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RatingProcessingException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RequestProcessingException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/UserProcessingException.java rename core/{main-service/src/main/java/ru/practicum/ewm/util/error/exception => aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats}/HitRequestException.java (78%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats/StatsResponseException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/EventServiceUnavailableException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RatingServiceUnavailableException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RequestServiceUnavailableException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/UserServiceUnavailableException.java rename core/{main-service/src/main/java/ru/practicum/ewm/util/entity => aggregation-api/src/main/java/ru/practicum/aggregation/model}/BaseEntity.java (96%) rename core/{main-service/src/main/java/ru/practicum/ewm => aggregation-api/src/main/java/ru/practicum/aggregation}/model/Location.java (80%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepository.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java create mode 100644 core/event-service/pom.xml create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java rename core/{main-service/src/main/java/ru/practicum/ewm/util/config/MainServiceConfig.java => event-service/src/main/java/ru/practicum/ewm/events/config/EventServiceConfig.java} (91%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/controller/admin/AdminCategoryController.java (88%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/controller/admin/AdminCompilationController.java (82%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/controller/admin/AdminEventController.java (78%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/controller/free/FreeCategoryController.java (90%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/controller/free/FreeCompilationController.java (87%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/controller/free/FreeEventController.java (80%) create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/controller/user/UserEventController.java (82%) rename core/{main-service/src/main/java/ru/practicum/ewm/model => event-service/src/main/java/ru/practicum/ewm/events/entity}/Category.java (82%) rename core/{main-service/src/main/java/ru/practicum/ewm/model => event-service/src/main/java/ru/practicum/ewm/events/entity}/Compilation.java (88%) rename core/{main-service/src/main/java/ru/practicum/ewm/model => event-service/src/main/java/ru/practicum/ewm/events/entity}/Event.java (93%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/mapper/CategoryMapper.java (58%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/mapper/CompilationMapper.java (69%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/mapper/EventMapper.java (76%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/mapper/StateMapper.java (72%) create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/mapper/UserMapper.java create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/model/EventData.java create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java rename core/{main-service/src/main/java/ru/practicum/ewm/dao => event-service/src/main/java/ru/practicum/ewm/events/repository}/CategoryRepository.java (66%) rename core/{main-service/src/main/java/ru/practicum/ewm/dao => event-service/src/main/java/ru/practicum/ewm/events/repository}/CompilationRepository.java (76%) rename core/{main-service/src/main/java/ru/practicum/ewm/dao => event-service/src/main/java/ru/practicum/ewm/events/repository}/EventRepository.java (50%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/service/category/CategoryService.java (60%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/service/category/CategoryServiceImpl.java (82%) rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/service/compilation/CompilationService.java (50%) create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java rename core/{main-service/src/main/java/ru/practicum/ewm => event-service/src/main/java/ru/practicum/ewm/events}/service/event/EventService.java (77%) create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingService.java create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java create mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java rename core/{main-service/src/main/java/ru/practicum/ewm/util => event-service/src/main/java/ru/practicum/ewm/events}/specification/EventSpecifications.java (91%) rename core/{main-service/src/main/java/ru/practicum/ewm/util => event-service/src/main/java/ru/practicum/ewm/events}/specification/SpecBuilder.java (93%) rename core/{main-service/src/main/java/ru/practicum/ewm/util/statistic/StatRepository.java => event-service/src/main/java/ru/practicum/ewm/events/statistic/StatRestRepository.java} (85%) rename core/{main-service/src/main/resources/application-test.yaml => event-service/src/main/resources/application-test.yaml.bak} (100%) rename core/{main-service => event-service}/src/main/resources/application.yml (97%) rename core/{main-service/src/main/resources/schema-h2.sql => event-service/src/main/resources/schema-h2.sql.bak} (100%) create mode 100644 core/event-service/src/main/resources/schema-postgres.sql rename core/{main-service/src/main/resources/schema-postgres.sql => event-service/src/main/resources/schema-postgres.sql.bak} (100%) delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/MainServiceApp.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/dto/event/EventFullDto.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/dto/rating/RatingRequest.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/dto/rating/RatingResponse.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/dto/request/EventRequestStatusUpdateRequest.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/dto/request/EventRequestStatusUpdateResult.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/dto/request/ParticipationRequestDto.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/dto/user/UserDto.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/mapper/RequestMapper.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/model/enums/AdminStateAction.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/model/enums/ParticipationStatus.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/model/enums/RequestUpdateStatus.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/model/enums/UserStateAction.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/service/compilation/CompilationServiceImpl.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/service/event/EventServiceImpl.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/service/rating/RatingService.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/service/rating/RatingServiceImpl.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/service/request/EventRequestCount.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/service/user/UserService.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/service/user/UserServiceImpl.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/ConflictException.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/NotFoundException.java delete mode 100644 core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/StatResponseException.java delete mode 100644 core/main-service/src/test/java/ru/practicum/ewm/TestUtils.java delete mode 100644 core/main-service/src/test/java/ru/practicum/ewm/controller/admin/AdminCategoryControllerTest.java delete mode 100644 core/main-service/src/test/java/ru/practicum/ewm/controller/admin/AdminUserControllerTest.java delete mode 100644 core/main-service/src/test/java/ru/practicum/ewm/dto/NewEventDtoTest.java delete mode 100644 core/main-service/src/test/java/ru/practicum/ewm/service/AdminCategoryServiceTest.java delete mode 100644 core/main-service/src/test/java/ru/practicum/ewm/service/AdminUserServiceTest.java delete mode 100644 core/main-service/src/test/java/ru/practicum/ewm/service/EventServiceImplTest.java create mode 100644 core/rating-service/pom.xml create mode 100644 core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java rename core/{main-service/src/main/java/ru/practicum/ewm/controller/rating => rating-service/src/main/java/ru/practicum/ewm/ratings/controller}/RatingController.java (80%) rename core/{main-service/src/main/java/ru/practicum/ewm/model => rating-service/src/main/java/ru/practicum/ewm/ratings/entity}/Rating.java (70%) rename core/{main-service/src/main/java/ru/practicum/ewm/dao => rating-service/src/main/java/ru/practicum/ewm/ratings/repository}/RatingRepository.java (63%) create mode 100644 core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java create mode 100644 core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java create mode 100644 core/rating-service/src/main/resources/application.yml create mode 100644 core/rating-service/src/main/resources/schema-postgres.sql create mode 100644 core/request-service/pom.xml create mode 100644 core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java create mode 100644 core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java rename core/{main-service/src/main/java/ru/practicum/ewm/controller/user => request-service/src/main/java/ru/practicum/ewm/requests/controller}/UserRequestController.java (68%) rename core/{main-service/src/main/java/ru/practicum/ewm/model => request-service/src/main/java/ru/practicum/ewm/requests/entity}/ParticipationRequest.java (73%) create mode 100644 core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java create mode 100644 core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java create mode 100644 core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java rename core/{main-service/src/main/java/ru/practicum/ewm/dao => request-service/src/main/java/ru/practicum/ewm/requests/repository}/RequestRepository.java (63%) rename core/{main-service/src/main/java/ru/practicum/ewm/service/request => request-service/src/main/java/ru/practicum/ewm/requests/service}/RequestService.java (51%) rename core/{main-service/src/main/java/ru/practicum/ewm/service/request => request-service/src/main/java/ru/practicum/ewm/requests/service}/RequestServiceImpl.java (63%) create mode 100644 core/request-service/src/main/resources/application.yml create mode 100644 core/request-service/src/main/resources/schema-postgres.sql rename core/{main-service => user-service}/pom.xml (64%) create mode 100644 core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java rename core/{main-service/src/main/java/ru/practicum/ewm/controller/admin => user-service/src/main/java/ru/practicum/ewm/users/controller}/AdminUserController.java (60%) rename core/{main-service/src/main/java/ru/practicum/ewm/model => user-service/src/main/java/ru/practicum/ewm/users/entity}/User.java (86%) rename core/{main-service/src/main/java/ru/practicum/ewm => user-service/src/main/java/ru/practicum/ewm/users}/mapper/UserMapper.java (72%) rename core/{main-service/src/main/java/ru/practicum/ewm/dao => user-service/src/main/java/ru/practicum/ewm/users/repository}/UserRepository.java (78%) create mode 100644 core/user-service/src/main/java/ru/practicum/ewm/users/service/UserService.java create mode 100644 core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java create mode 100644 core/user-service/src/main/resources/application.yml create mode 100644 core/user-service/src/main/resources/schema-postgres.sql create mode 100644 docker-init.sql create mode 100644 infra/config-repo/core/event-service.yml delete mode 100644 infra/config-repo/core/main-service.yaml create mode 100644 infra/config-repo/core/rating-service.yml create mode 100644 infra/config-repo/core/request-service.yml create mode 100644 infra/config-repo/core/user-service.yml diff --git a/core/aggregation-api/pom.xml b/core/aggregation-api/pom.xml new file mode 100644 index 0000000..b6f4eff --- /dev/null +++ b/core/aggregation-api/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + ru.practicum + core + 0.0.1-SNAPSHOT + + + aggregation-api + jar + + + + jakarta.servlet + jakarta.servlet-api + + + jakarta.validation + jakarta.validation-api + + + jakarta.persistence + jakarta.persistence-api + + + org.hibernate.orm + hibernate-core + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + \ No newline at end of file diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallExecutor.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallExecutor.java new file mode 100644 index 0000000..f52467d --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallExecutor.java @@ -0,0 +1,39 @@ +package ru.practicum.aggregation.client; + +import lombok.experimental.UtilityClass; +import org.springframework.lang.NonNull; +import ru.practicum.aggregation.error.exception.ServiceProcessingException; +import ru.practicum.aggregation.error.exception.ServiceUnavailableException; + +import java.util.function.Supplier; + +@UtilityClass +public class RemoteCallExecutor { + + @NonNull + public RemoteCallResult execute(@NonNull Supplier action) { + try { + return new RemoteCallResult.Success<>(action.get()); + + } catch (ServiceProcessingException ex) { + return new RemoteCallResult.Failure<>(ex.getMessage()); + + } catch (ServiceUnavailableException ex) { + return new RemoteCallResult.Degraded<>(ex); + } + } + + @NonNull + public static RemoteCallResult executeVoid(@NonNull Runnable function) { + try { + function.run(); + return new RemoteCallResult.Success<>(null); + + } catch (ServiceProcessingException ex) { + return new RemoteCallResult.Failure<>(ex.getMessage()); + + } catch (ServiceUnavailableException ex) { + return new RemoteCallResult.Degraded<>(ex); + } + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallResult.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallResult.java new file mode 100644 index 0000000..58a1a73 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallResult.java @@ -0,0 +1,14 @@ +package ru.practicum.aggregation.client; + +@SuppressWarnings("unused") +public sealed interface RemoteCallResult + permits RemoteCallResult.Success, + RemoteCallResult.Failure, + RemoteCallResult.Degraded { + + record Success(T value) implements RemoteCallResult { } + + record Failure(String message) implements RemoteCallResult { } + + record Degraded(Throwable cause) implements RemoteCallResult { } +} \ No newline at end of file diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java new file mode 100644 index 0000000..4d802c3 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java @@ -0,0 +1,58 @@ +package ru.practicum.aggregation.client; + +import feign.FeignException; +import lombok.NonNull; +import lombok.experimental.UtilityClass; +import ru.practicum.aggregation.error.exception.processing.EventProcessingException; +import ru.practicum.aggregation.error.exception.processing.RatingProcessingException; +import ru.practicum.aggregation.error.exception.processing.RequestProcessingException; +import ru.practicum.aggregation.error.exception.processing.UserProcessingException; +import ru.practicum.aggregation.error.exception.unavailable.EventServiceUnavailableException; +import ru.practicum.aggregation.error.exception.unavailable.RatingServiceUnavailableException; +import ru.practicum.aggregation.error.exception.unavailable.RequestServiceUnavailableException; +import ru.practicum.aggregation.error.exception.unavailable.UserServiceUnavailableException; + +@UtilityClass +public class RemoteExceptionMapper { + + public RuntimeException mapUserException(@NonNull Throwable cause) { + if (cause instanceof FeignException ex) { + return switch (ex.status()) { + case 404, 409 -> new UserProcessingException(ex.getMessage()); + default -> new UserServiceUnavailableException(ex); + }; + } + return new UserServiceUnavailableException(cause); + } + + public RuntimeException mapEventException(@NonNull Throwable cause) { + if (cause instanceof FeignException ex) { + return switch (ex.status()) { + case 400, 404, 409 -> new EventProcessingException(ex.getMessage()); + default -> new EventServiceUnavailableException(ex); + }; + } + return new EventServiceUnavailableException(cause); + } + + public RuntimeException mapRequestException(@NonNull Throwable cause) { + if (cause instanceof FeignException ex) { + return switch (ex.status()) { + case 404, 409 -> new RequestProcessingException(ex.getMessage()); + default -> new RequestServiceUnavailableException(ex); + }; + } + return new RequestServiceUnavailableException(cause); + } + + public RuntimeException mapRatingException(@NonNull Throwable cause) { + if (cause instanceof FeignException ex) { + return switch (ex.status()) { + case 400, 404 -> new RatingProcessingException(ex.getMessage()); + default -> new RatingServiceUnavailableException(ex); + }; + } + return new RatingServiceUnavailableException(cause); + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java new file mode 100644 index 0000000..c9cb5a7 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java @@ -0,0 +1,31 @@ +package ru.practicum.aggregation.client.event; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; + +@FeignClient( + name = "event-service", + fallbackFactory = EventFallbackFactory.class +) +public interface EventClient { + + @GetMapping("/users/{userId}/events/{eventId}") + EventFullDto userFindEventById(@PathVariable @Positive Long userId, + @PathVariable @Positive Long eventId); + + + @PatchMapping("/system/events/ratings") + void systemUpdateRating(@RequestBody @NotNull @Valid RatingUpdateRequest request); + + @GetMapping("/system/events/{eventId}") + EventFullDto systemFindEventById(@PathVariable @Positive Long eventId); + +} \ No newline at end of file diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java new file mode 100644 index 0000000..950c447 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java @@ -0,0 +1,34 @@ +package ru.practicum.aggregation.client.event; + +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; +import ru.practicum.aggregation.client.RemoteExceptionMapper; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; + +@Component +public class EventFallbackFactory implements FallbackFactory { + + @Override + public EventClient create(Throwable cause) { + + return new EventClient() { + + @Override + public EventFullDto userFindEventById(Long userId, Long eventId) { + throw RemoteExceptionMapper.mapRequestException(cause); + } + + @Override + public void systemUpdateRating(RatingUpdateRequest request) { + throw RemoteExceptionMapper.mapRequestException(cause); + } + + @Override + public EventFullDto systemFindEventById(Long eventId) { + throw RemoteExceptionMapper.mapRequestException(cause); + } + }; + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java new file mode 100644 index 0000000..5120413 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java @@ -0,0 +1,35 @@ +package ru.practicum.aggregation.client.request; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; +import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.enums.ParticipationStatus; + +import java.util.List; + +@FeignClient( + name = "request-service", + fallbackFactory = RequestFallbackFactory.class +) +public interface RequestClient { + + @GetMapping("/users/{userId}/requests/{eventId}") + List findByEventId(@PathVariable @Positive Long userId, + @PathVariable @Positive Long eventId); + + @PatchMapping("/users/{userId}/requests/events/{eventId}/status") + EventRequestStatusUpdateResult updateStatusRequest( + @PathVariable @Positive Long userId, + @PathVariable @Positive Long eventId, + @RequestBody @NotNull EventRequestStatusUpdateRequest request + ); + + @GetMapping("/admin/requests/count") + List getCount(@RequestParam List eventIds, + @RequestParam ParticipationStatus status); +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java new file mode 100644 index 0000000..0e378f2 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java @@ -0,0 +1,41 @@ +package ru.practicum.aggregation.client.request; + +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; +import ru.practicum.aggregation.client.RemoteExceptionMapper; +import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.enums.ParticipationStatus; + +import java.util.List; + +@Component +public class RequestFallbackFactory implements FallbackFactory { + + @Override + public RequestClient create(Throwable cause) { + + return new RequestClient() { + + @Override + public List findByEventId(Long userId, Long eventId) { + throw RemoteExceptionMapper.mapRequestException(cause); + } + + @Override + public EventRequestStatusUpdateResult updateStatusRequest(Long userId, + Long eventId, + EventRequestStatusUpdateRequest request) { + throw RemoteExceptionMapper.mapRequestException(cause); + } + + @Override + public List getCount(List eventIds, ParticipationStatus status) { + throw RemoteExceptionMapper.mapRequestException(cause); + } + }; + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java new file mode 100644 index 0000000..59b2f80 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java @@ -0,0 +1,27 @@ +package ru.practicum.aggregation.client.user; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import ru.practicum.aggregation.dto.user.UserDto; +import ru.practicum.aggregation.dto.user.UserShortDto; + +import java.util.List; + +@FeignClient( + name = "user-service", + fallbackFactory = UserFallbackFactory.class +) +public interface UserClient { + + @GetMapping("/admin/users/{userId}") + UserDto getUser(@PathVariable Long userId); + + @GetMapping("/admin/users/{userId}/check") + void checkUser(@PathVariable Long userId); + + @GetMapping("/admin/users/short") + List getShortUsersByIds(@RequestParam List userIds); + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserFallbackFactory.java new file mode 100644 index 0000000..02902e0 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserFallbackFactory.java @@ -0,0 +1,36 @@ +package ru.practicum.aggregation.client.user; + +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; +import ru.practicum.aggregation.client.RemoteExceptionMapper; +import ru.practicum.aggregation.dto.user.UserDto; +import ru.practicum.aggregation.dto.user.UserShortDto; + +import java.util.List; + +@Component +public class UserFallbackFactory implements FallbackFactory { + + @Override + public UserClient create(Throwable cause) { + + return new UserClient() { + + @Override + public UserDto getUser(Long userId) { + throw RemoteExceptionMapper.mapUserException(cause); + } + + @Override + public void checkUser(Long userId) { + throw RemoteExceptionMapper.mapUserException(cause); + } + + @Override + public List getShortUsersByIds(List userIds) { + throw RemoteExceptionMapper.mapUserException(cause); + } + }; + } + +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/category/CategoryDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/CategoryDto.java similarity index 66% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/category/CategoryDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/CategoryDto.java index 3ff7521..f815385 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/category/CategoryDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/CategoryDto.java @@ -1,9 +1,13 @@ -package ru.practicum.ewm.dto.category; +package ru.practicum.aggregation.dto.category; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import lombok.Builder; +/** + * Long id
+ * String name + */ @Builder public record CategoryDto( diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/category/NewCategoryDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/NewCategoryDto.java similarity index 71% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/category/NewCategoryDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/NewCategoryDto.java index 2d5e215..d8e6cc0 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/category/NewCategoryDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/NewCategoryDto.java @@ -1,8 +1,12 @@ -package ru.practicum.ewm.dto.category; +package ru.practicum.aggregation.dto.category; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; +/** + * + * @param name + */ public record NewCategoryDto( @NotBlank diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/CompilationDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationDto.java similarity index 53% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/CompilationDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationDto.java index 127b712..4f92a8f 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/CompilationDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationDto.java @@ -1,13 +1,19 @@ -package ru.practicum.ewm.dto.compilation; +package ru.practicum.aggregation.dto.compilation; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import ru.practicum.ewm.dto.event.EventShortDto; +import ru.practicum.aggregation.dto.event.response.EventShortDto; import java.util.List; +/** + * Long id
+ * boolean pinned
+ * String title
+ * List<{@link EventShortDto}> events + */ @Data @Builder @AllArgsConstructor diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/CompilationSearchFilter.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationSearchFilter.java similarity index 85% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/CompilationSearchFilter.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationSearchFilter.java index 68cf761..7ae7fd8 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/CompilationSearchFilter.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationSearchFilter.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.dto.compilation; +package ru.practicum.aggregation.dto.compilation; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/CompilationUpdateDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationUpdateDto.java similarity index 88% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/CompilationUpdateDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationUpdateDto.java index 2f98b0e..b8182fa 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/CompilationUpdateDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationUpdateDto.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.dto.compilation; +package ru.practicum.aggregation.dto.compilation; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/NewCompilationDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/NewCompilationDto.java similarity index 90% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/NewCompilationDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/NewCompilationDto.java index f6ded8b..b1f8eb5 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/compilation/NewCompilationDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/NewCompilationDto.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.dto.compilation; +package ru.practicum.aggregation.dto.compilation; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/AdminGetDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/AdminGetDto.java similarity index 54% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/event/AdminGetDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/AdminGetDto.java index c165994..d0db3f0 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/AdminGetDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/AdminGetDto.java @@ -1,13 +1,21 @@ -package ru.practicum.ewm.dto.event; +package ru.practicum.aggregation.dto.event.request; -import jakarta.validation.constraints.NotNull; import lombok.Builder; -import ru.practicum.ewm.model.enums.EventState; +import ru.practicum.aggregation.enums.EventState; import java.time.LocalDateTime; import java.util.List; -@NotNull +/** + * + * @param users + * @param states + * @param categories + * @param rangeStart + * @param rangeEnd + * @param from + * @param size + */ @Builder public record AdminGetDto( List users, diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/FreeGetDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/FreeGetDto.java similarity index 65% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/event/FreeGetDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/FreeGetDto.java index 0343925..f388b7a 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/FreeGetDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/FreeGetDto.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.dto.event; +package ru.practicum.aggregation.dto.event.request; import jakarta.validation.constraints.NotNull; import lombok.Builder; @@ -6,6 +6,18 @@ import java.time.LocalDateTime; import java.util.List; +/** + * + * @param text + * @param categories + * @param paid + * @param rangeStart + * @param rangeEnd + * @param onlyAvailable + * @param sort + * @param from + * @param size + */ @NotNull @Builder public record FreeGetDto( diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/NewEventDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/NewEventDto.java similarity index 89% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/event/NewEventDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/NewEventDto.java index fe9ff80..1a067df 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/NewEventDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/NewEventDto.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.dto.event; +package ru.practicum.aggregation.dto.event.request; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -7,7 +7,7 @@ import lombok.Data; import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; -import ru.practicum.ewm.model.Location; +import ru.practicum.aggregation.model.Location; import java.time.LocalDateTime; diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/UpdateEventAdminRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventAdminRequest.java similarity index 75% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/event/UpdateEventAdminRequest.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventAdminRequest.java index 14c73ec..492150d 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/UpdateEventAdminRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventAdminRequest.java @@ -1,9 +1,9 @@ -package ru.practicum.ewm.dto.event; +package ru.practicum.aggregation.dto.event.request; import jakarta.validation.constraints.Size; import lombok.Builder; -import ru.practicum.ewm.model.Location; -import ru.practicum.ewm.model.enums.AdminStateAction; +import ru.practicum.aggregation.enums.AdminStateAction; +import ru.practicum.aggregation.model.Location; import java.time.LocalDateTime; diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/UpdateEventUserRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventUserRequest.java similarity index 63% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/event/UpdateEventUserRequest.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventUserRequest.java index 45aa833..a928f84 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/UpdateEventUserRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventUserRequest.java @@ -1,11 +1,13 @@ -package ru.practicum.ewm.dto.event; +package ru.practicum.aggregation.dto.event.request; import jakarta.validation.constraints.Size; -import ru.practicum.ewm.model.Location; -import ru.practicum.ewm.model.enums.UserStateAction; +import lombok.Builder; +import ru.practicum.aggregation.enums.UserStateAction; +import ru.practicum.aggregation.model.Location; import java.time.LocalDateTime; +@Builder public record UpdateEventUserRequest( @Size(min = 20, max = 2000) @@ -26,9 +28,9 @@ public record UpdateEventUserRequest( Boolean requestModeration, - @Size(min = 3, max = 120) - String title, + UserStateAction stateAction, - UserStateAction stateAction + @Size(min = 3, max = 120) + String title ) { } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventFullDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventFullDto.java new file mode 100644 index 0000000..6792e2f --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventFullDto.java @@ -0,0 +1,76 @@ +package ru.practicum.aggregation.dto.event.response; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import ru.practicum.aggregation.dto.category.CategoryDto; +import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.enums.EventState; +import ru.practicum.aggregation.model.Location; + +import java.time.LocalDateTime; + +/** + * @apiNote + * Long id
+ * String annotation
+ * CategoryDto category
+ * Long confirmedRequests
+ * LocalDateTime createdOn
+ * String description
+ * LocalDateTime eventDate
+ * UserShortDto initiator
+ * Location location
+ * boolean paid
+ * Integer participantLimit
+ * LocalDateTime publishedOn
+ * boolean requestModeration
+ * EventState state
+ * String title
+ * Long views
+ * long rate + */ +@Builder +public record EventFullDto( + + Long id, + + @NotBlank + String annotation, + + @NotNull + CategoryDto category, + + Long confirmedRequests, + + LocalDateTime createdOn, + + String description, + + @NotNull + LocalDateTime eventDate, + + @NotNull + UserShortDto initiator, + + @NotNull + Location location, + + boolean paid, + + Integer participantLimit, + + LocalDateTime publishedOn, + + boolean requestModeration, + + EventState state, + + @NotBlank + String title, + + Long views, + + long rate +) { +} \ No newline at end of file diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/EventShortDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventShortDto.java similarity index 63% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/event/EventShortDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventShortDto.java index 6f2be95..3033b53 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/EventShortDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventShortDto.java @@ -1,8 +1,8 @@ -package ru.practicum.ewm.dto.event; +package ru.practicum.aggregation.dto.event.response; import lombok.Builder; -import ru.practicum.ewm.dto.category.CategoryDto; -import ru.practicum.ewm.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.category.CategoryDto; +import ru.practicum.aggregation.dto.user.UserShortDto; import java.time.LocalDateTime; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingRequest.java new file mode 100644 index 0000000..30cee2c --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingRequest.java @@ -0,0 +1,13 @@ +package ru.practicum.aggregation.dto.rating; + +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import ru.practicum.aggregation.enums.Reaction; + +@Builder +public record RatingRequest( + + @NotNull + Reaction reaction +) { +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingResponse.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingResponse.java new file mode 100644 index 0000000..d9398fb --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingResponse.java @@ -0,0 +1,13 @@ +package ru.practicum.aggregation.dto.rating; + +import lombok.Builder; +import ru.practicum.aggregation.enums.Reaction; + +@Builder +public record RatingResponse( + Long id, + Long userId, + Long eventId, + Reaction reaction +) { +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingUpdateRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingUpdateRequest.java new file mode 100644 index 0000000..d9a51a1 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingUpdateRequest.java @@ -0,0 +1,22 @@ +package ru.practicum.aggregation.dto.rating; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.Builder; + +/** + * @apiNote + * {@link Long} eventId
+ * {@link Long} rate + */ +@Builder +public record RatingUpdateRequest( + + @Positive + Long eventId, + + @NotNull + Long rate + +) { +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestCount.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestCount.java new file mode 100644 index 0000000..fd4c948 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestCount.java @@ -0,0 +1,13 @@ +package ru.practicum.aggregation.dto.request; + +/** + * @apiNote + * Long getEventId()
+ * Long getCount() + */ +public interface EventRequestCount { + + Long getEventId(); + + Long getCount(); +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateRequest.java new file mode 100644 index 0000000..5af1364 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateRequest.java @@ -0,0 +1,22 @@ +package ru.practicum.aggregation.dto.request; + +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import ru.practicum.aggregation.enums.RequestUpdateStatus; + +import java.util.List; + +/** + * @apiNote + * List<{@link Long}> requestIds
+ * {@link RequestUpdateStatus} status + */ +@Builder +public record EventRequestStatusUpdateRequest( + + List requestIds, + + @NotNull + RequestUpdateStatus status +) { +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateResult.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateResult.java new file mode 100644 index 0000000..810edca --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateResult.java @@ -0,0 +1,17 @@ +package ru.practicum.aggregation.dto.request; + +import lombok.Builder; + +import java.util.List; + +/** + * @apiNote + * List<{@link ParticipationRequestDto}> confirmedRequests
+ * List<{@link ParticipationRequestDto}> rejectedRequests
+ */ +@Builder +public record EventRequestStatusUpdateResult( + List confirmedRequests, + List rejectedRequests +) { +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/ParticipationRequestDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/ParticipationRequestDto.java new file mode 100644 index 0000000..0e77ccb --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/ParticipationRequestDto.java @@ -0,0 +1,22 @@ +package ru.practicum.aggregation.dto.request; + +import lombok.Builder; +import ru.practicum.aggregation.enums.ParticipationStatus; + +/** + * @apiNote + * {@link Long} id
+ * {@link String} created
+ * {@link Long} event
+ * {@link Long} requester
+ * {@link ParticipationStatus} status
+ */ +@Builder +public record ParticipationRequestDto( + Long id, + String created, + Long event, + Long requester, + ParticipationStatus status +) { +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/user/NewUserRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/NewUserRequest.java similarity index 76% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/user/NewUserRequest.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/NewUserRequest.java index c1820d7..2ab60b5 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/user/NewUserRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/NewUserRequest.java @@ -1,10 +1,15 @@ -package ru.practicum.ewm.dto.user; +package ru.practicum.aggregation.dto.user; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import lombok.Builder; +/** + * @apiNote + * String name
+ * String email + */ @Builder public record NewUserRequest( diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserDto.java new file mode 100644 index 0000000..8a862b2 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserDto.java @@ -0,0 +1,17 @@ +package ru.practicum.aggregation.dto.user; + +import lombok.Builder; + +/** + * @apiNote + * Long id
+ * String name
+ * String email + */ +@Builder +public record UserDto( + Long id, + String name, + String email +) { +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/user/UserShortDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserShortDto.java similarity index 73% rename from core/main-service/src/main/java/ru/practicum/ewm/dto/user/UserShortDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserShortDto.java index 7c8dc62..7123cb3 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/user/UserShortDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserShortDto.java @@ -1,12 +1,15 @@ -package ru.practicum.ewm.dto.user; +package ru.practicum.aggregation.dto.user; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Builder; -@NotNull +/** + * @apiNote Long id
+ * String name
+ * String email + */ @Builder public record UserShortDto( diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/AdminStateAction.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/AdminStateAction.java new file mode 100644 index 0000000..cb0c234 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/AdminStateAction.java @@ -0,0 +1,6 @@ +package ru.practicum.aggregation.enums; + +public enum AdminStateAction { + PUBLISH_EVENT, + REJECT_EVENT +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/EventState.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/EventState.java similarity index 60% rename from core/main-service/src/main/java/ru/practicum/ewm/model/enums/EventState.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/EventState.java index 73987d3..fedcf90 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/EventState.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/EventState.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.model.enums; +package ru.practicum.aggregation.enums; public enum EventState { PENDING, PUBLISHED, CANCELED diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/ParticipationStatus.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/ParticipationStatus.java new file mode 100644 index 0000000..8f09c57 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/ParticipationStatus.java @@ -0,0 +1,8 @@ +package ru.practicum.aggregation.enums; + +/** + * PENDING, CONFIRMED, REJECTED, CANCELED + */ +public enum ParticipationStatus { + PENDING, CONFIRMED, REJECTED, CANCELED +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/Reaction.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/Reaction.java similarity index 52% rename from core/main-service/src/main/java/ru/practicum/ewm/model/enums/Reaction.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/Reaction.java index d379a2c..a5d1060 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/Reaction.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/Reaction.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.model.enums; +package ru.practicum.aggregation.enums; public enum Reaction { LIKE, DISLIKE diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/RequestUpdateStatus.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/RequestUpdateStatus.java new file mode 100644 index 0000000..863e5f0 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/RequestUpdateStatus.java @@ -0,0 +1,8 @@ +package ru.practicum.aggregation.enums; + +/** + * CONFIRMED, REJECTED + */ +public enum RequestUpdateStatus { + CONFIRMED, REJECTED +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/UserStateAction.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/UserStateAction.java new file mode 100644 index 0000000..8f5e6a2 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/UserStateAction.java @@ -0,0 +1,8 @@ +package ru.practicum.aggregation.enums; + +public enum UserStateAction { + SEND_TO_REVIEW, + CANCEL_REVIEW, + PUBLISH_EVENT, + REJECT_EVENT +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/util/error/ApiError.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/ApiError.java similarity index 87% rename from core/main-service/src/main/java/ru/practicum/ewm/util/error/ApiError.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/error/ApiError.java index 3d8f3f9..37fc981 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/util/error/ApiError.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/ApiError.java @@ -1,5 +1,6 @@ -package ru.practicum.ewm.util.error; +package ru.practicum.aggregation.error; +import lombok.Builder; import org.springframework.http.HttpStatus; import java.time.LocalDateTime; @@ -11,6 +12,7 @@ * @param errors Список стектрейсов или описания ошибок * @param timestamp Дата и время когда произошла ошибка (в формате "yyyy-MM-dd HH:mm:ss") */ +@Builder(toBuilder = true) public record ApiError( HttpStatus status, String reason, diff --git a/core/main-service/src/main/java/ru/practicum/ewm/util/error/MainServiceErrorHandler.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/GeneralErrorHandler.java similarity index 50% rename from core/main-service/src/main/java/ru/practicum/ewm/util/error/MainServiceErrorHandler.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/error/GeneralErrorHandler.java index a31a2c5..7abf542 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/util/error/MainServiceErrorHandler.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/GeneralErrorHandler.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.util.error; +package ru.practicum.aggregation.error; import jakarta.validation.ValidationException; import lombok.extern.slf4j.Slf4j; @@ -10,10 +10,10 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.HandlerMethodValidationException; -import ru.practicum.ewm.util.error.exception.ConflictException; -import ru.practicum.ewm.util.error.exception.HitRequestException; -import ru.practicum.ewm.util.error.exception.NotFoundException; -import ru.practicum.ewm.util.error.exception.StatResponseException; +import ru.practicum.aggregation.error.exception.ConflictException; +import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.error.exception.stats.HitRequestException; +import ru.practicum.aggregation.error.exception.stats.StatsResponseException; import java.io.PrintWriter; import java.io.StringWriter; @@ -24,153 +24,154 @@ @Slf4j @RestControllerAdvice -public class MainServiceErrorHandler { +public class GeneralErrorHandler { @ExceptionHandler @ResponseStatus(INTERNAL_SERVER_ERROR) public ApiError handleSomeError(final Exception ex) { String stackTrace = getStackTrace(ex); - return new ApiError( - INTERNAL_SERVER_ERROR, - "Непредвиденная ошибка", - ex.getMessage(), - stackTrace, - LocalDateTime.now() - ); + return ApiError.builder() + .status(INTERNAL_SERVER_ERROR) + .reason("Непредвиденная ошибка") + .message(ex.getMessage()) + .errors(stackTrace) + .timestamp(LocalDateTime.now()) + .build(); } @ExceptionHandler @ResponseStatus(INTERNAL_SERVER_ERROR) - public ApiError handleHitRequestException(HitRequestException e) { - log.info("500 {}", e.getMessage(), e); - String stackTrace = getStackTrace(e); - return new ApiError( - INTERNAL_SERVER_ERROR, - "Stat-server error ....", - e.getMessage(), - stackTrace, - LocalDateTime.now() - ); + public ApiError handleHitRequestException(HitRequestException ex) { + log.info("500 {}", ex.getMessage(), ex); + String stackTrace = getStackTrace(ex); + return ApiError.builder() + .status(INTERNAL_SERVER_ERROR) + .reason("Stat-server error ....") + .message(ex.getMessage()) + .errors(stackTrace) + .timestamp(LocalDateTime.now()) + .build(); } @ExceptionHandler @ResponseStatus(INTERNAL_SERVER_ERROR) - public ApiError handleStatResponseException(StatResponseException ex) { + public ApiError handleStatResponseException(StatsResponseException ex) { log.info("500 {}", ex.getMessage(), ex); String stackTrace = getStackTrace(ex); - return new ApiError( - INTERNAL_SERVER_ERROR, - "Stat-server error ....", - ex.getMessage(), - stackTrace, - LocalDateTime.now() - ); + return ApiError.builder() + .status(INTERNAL_SERVER_ERROR) + .reason("Stat-server error ....") + .message(ex.getMessage()) + .errors(stackTrace) + .timestamp(LocalDateTime.now()) + .build(); } @ExceptionHandler @ResponseStatus(BAD_REQUEST) - public ApiError handleMissingServletRequestParameter(@NonNull final MissingServletRequestParameterException ex) { + public ApiError handleMissingServletRequestParameter( + @NonNull final MissingServletRequestParameterException ex) { log.warn("400 Bad Request (MissingServletRequestParameter): {}", ex.getMessage()); String stackTrace = getStackTrace(ex); - return new ApiError( - BAD_REQUEST, - "MissingServletRequestParameter", - ex.getMessage(), - stackTrace, - LocalDateTime.now() - ); + return ApiError.builder() + .status(BAD_REQUEST) + .reason("MissingServletRequestParameter") + .message(ex.getMessage()) + .errors(stackTrace) + .timestamp(LocalDateTime.now()) + .build(); } @ExceptionHandler @ResponseStatus(BAD_REQUEST) public ApiError handleMethodArgumentNotValid(@NonNull MethodArgumentNotValidException ex) { log.warn("400 Bad Request (ArgumentNotValid): {}", ex.getMessage()); - return new ApiError( - BAD_REQUEST, - "Incorrectly made request.", - "Field: " + Objects.requireNonNull(ex.getBindingResult().getFieldError()).getField() - + ". Error: " + ex.getBindingResult().getFieldError().getDefaultMessage(), - getStackTrace(ex), - LocalDateTime.now() - ); + return ApiError.builder() + .status(BAD_REQUEST) + .reason("Incorrectly made request.") + .message("Field: " + Objects.requireNonNull(ex.getBindingResult().getFieldError()).getField() + + ". Error: " + ex.getBindingResult().getFieldError().getDefaultMessage()) + .errors(getStackTrace(ex)) + .timestamp(LocalDateTime.now()) + .build(); } @ExceptionHandler @ResponseStatus(BAD_REQUEST) public ApiError handleHandlerMethodValidationException(@NonNull HandlerMethodValidationException ex) { log.warn("400 Bad Request (HandlerMethodValidationException): {}", ex.getMessage()); - return new ApiError( - BAD_REQUEST, - "HandlerMethodValidationException", - ex.getMessage(), - getStackTrace(ex), - LocalDateTime.now() - ); + return ApiError.builder() + .status(BAD_REQUEST) + .reason("HandlerMethodValidationException") + .message(ex.getMessage()) + .errors(getStackTrace(ex)) + .timestamp(LocalDateTime.now()) + .build(); } @ExceptionHandler @ResponseStatus(BAD_REQUEST) public ApiError handleIllegalArgumentException(@NonNull IllegalArgumentException ex) { log.warn("400 Bad Request (IllegalArgument): {}", ex.getMessage()); - return new ApiError( - BAD_REQUEST, - "Incorrectly made request.", - ex.getMessage(), - getStackTrace(ex), - LocalDateTime.now() - ); + return ApiError.builder() + .status(BAD_REQUEST) + .reason("Incorrectly made request.") + .message(ex.getMessage()) + .errors(getStackTrace(ex)) + .timestamp(LocalDateTime.now()) + .build(); } @ExceptionHandler @ResponseStatus(BAD_REQUEST) public ApiError handleValidationException(@NonNull ValidationException ex) { log.warn("400 Bad Request (ValidationException): {}", ex.getMessage()); - return new ApiError( - BAD_REQUEST, - "Incorrectly made request.", - ex.getMessage(), - getStackTrace(ex), - LocalDateTime.now() - ); + return ApiError.builder() + .status(BAD_REQUEST) + .reason("Incorrectly made request.") + .message(ex.getMessage()) + .errors(getStackTrace(ex)) + .timestamp(LocalDateTime.now()) + .build(); } @ExceptionHandler @ResponseStatus(NOT_FOUND) public ApiError handleNotFound(@NonNull NotFoundException ex) { log.warn("404 Not Found: {}", ex.getMessage()); - return new ApiError( - NOT_FOUND, - "The required object was not found.", - ex.getMessage(), - getStackTrace(ex), - LocalDateTime.now() - ); + return ApiError.builder() + .status(NOT_FOUND) + .reason("The required object was not found.") + .message(ex.getMessage()) + .errors(getStackTrace(ex)) + .timestamp(LocalDateTime.now()) + .build(); } @ExceptionHandler @ResponseStatus(CONFLICT) public ApiError handleConflict(@NonNull ConflictException ex) { log.warn("409 Conflict: {}", ex.getMessage()); - return new ApiError( - CONFLICT, - "For the requested operation the conditions are not met.", - ex.getMessage(), - getStackTrace(ex), - LocalDateTime.now() - ); + return ApiError.builder() + .status(CONFLICT) + .reason("For the requested operation the conditions are not met.") + .message(ex.getMessage()) + .errors(getStackTrace(ex)) + .timestamp(LocalDateTime.now()) + .build(); } @ExceptionHandler @ResponseStatus(CONFLICT) public ApiError handleHttpMessageNotReadableException(@NonNull final HttpMessageNotReadableException ex) { log.warn("409 CONFLICT: Required request body is missing or invalid"); - return new ApiError( - BAD_REQUEST, - "Incorrectly made request.", - ex.getMessage(), - getStackTrace(ex), - LocalDateTime.now() - ); + return ApiError.builder() + .status(CONFLICT) + .reason("Incorrectly made request.") + .message(ex.getMessage()) + .errors(getStackTrace(ex)) + .timestamp(LocalDateTime.now()) + .build(); } private String getStackTrace(@NonNull Exception e) { diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/BusinessException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/BusinessException.java new file mode 100644 index 0000000..22772fb --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/BusinessException.java @@ -0,0 +1,7 @@ +package ru.practicum.aggregation.error.exception; + +public class BusinessException extends RuntimeException { + public BusinessException(String message) { + super(message); + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ConflictException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ConflictException.java new file mode 100644 index 0000000..270e413 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ConflictException.java @@ -0,0 +1,8 @@ +package ru.practicum.aggregation.error.exception; + +public class ConflictException extends BusinessException { + + public ConflictException(String message) { + super(message); + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/NotFoundException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/NotFoundException.java new file mode 100644 index 0000000..9657f0d --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/NotFoundException.java @@ -0,0 +1,8 @@ +package ru.practicum.aggregation.error.exception; + +public class NotFoundException extends BusinessException { + + public NotFoundException(String message) { + super(message); + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceProcessingException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceProcessingException.java new file mode 100644 index 0000000..15ffe52 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceProcessingException.java @@ -0,0 +1,7 @@ +package ru.practicum.aggregation.error.exception; + +public class ServiceProcessingException extends BusinessException { + public ServiceProcessingException(String message) { + super(message); + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceUnavailableException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceUnavailableException.java new file mode 100644 index 0000000..0994c1e --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceUnavailableException.java @@ -0,0 +1,7 @@ +package ru.practicum.aggregation.error.exception; + +public class ServiceUnavailableException extends RuntimeException { + public ServiceUnavailableException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/EventProcessingException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/EventProcessingException.java new file mode 100644 index 0000000..2a32039 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/EventProcessingException.java @@ -0,0 +1,11 @@ +package ru.practicum.aggregation.error.exception.processing; + +import ru.practicum.aggregation.error.exception.ServiceProcessingException; + +public class EventProcessingException extends ServiceProcessingException { + + public EventProcessingException(String message) { + super(message); + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RatingProcessingException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RatingProcessingException.java new file mode 100644 index 0000000..b37d1c4 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RatingProcessingException.java @@ -0,0 +1,11 @@ +package ru.practicum.aggregation.error.exception.processing; + +import ru.practicum.aggregation.error.exception.ServiceProcessingException; + +public class RatingProcessingException extends ServiceProcessingException { + + public RatingProcessingException(String message) { + super(message); + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RequestProcessingException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RequestProcessingException.java new file mode 100644 index 0000000..3dfbacb --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RequestProcessingException.java @@ -0,0 +1,11 @@ +package ru.practicum.aggregation.error.exception.processing; + +import ru.practicum.aggregation.error.exception.ServiceProcessingException; + +public class RequestProcessingException extends ServiceProcessingException { + + public RequestProcessingException(String message) { + super(message); + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/UserProcessingException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/UserProcessingException.java new file mode 100644 index 0000000..13deea1 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/UserProcessingException.java @@ -0,0 +1,11 @@ +package ru.practicum.aggregation.error.exception.processing; + +import ru.practicum.aggregation.error.exception.ServiceProcessingException; + +public class UserProcessingException extends ServiceProcessingException { + + public UserProcessingException(String message) { + super(message); + } + +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/HitRequestException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats/HitRequestException.java similarity index 78% rename from core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/HitRequestException.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats/HitRequestException.java index a0232af..6ce1164 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/HitRequestException.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats/HitRequestException.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.util.error.exception; +package ru.practicum.aggregation.error.exception.stats; public class HitRequestException extends RuntimeException { diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats/StatsResponseException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats/StatsResponseException.java new file mode 100644 index 0000000..bfa1cf3 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats/StatsResponseException.java @@ -0,0 +1,14 @@ +package ru.practicum.aggregation.error.exception.stats; + +public class StatsResponseException extends RuntimeException { + + private static final String MESSAGE = "Не удалось получить статистику"; + + public StatsResponseException(Exception ex) { + super(MESSAGE, ex); + } + + public StatsResponseException() { + super(MESSAGE); + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/EventServiceUnavailableException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/EventServiceUnavailableException.java new file mode 100644 index 0000000..a18b2c3 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/EventServiceUnavailableException.java @@ -0,0 +1,16 @@ +package ru.practicum.aggregation.error.exception.unavailable; + +import ru.practicum.aggregation.error.exception.ServiceUnavailableException; + +public class EventServiceUnavailableException extends ServiceUnavailableException { + + public EventServiceUnavailableException(Throwable cause) { + super(""" + Сервис событий временно недоступен. + Повторите попытку позже. + """, + cause + ); + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RatingServiceUnavailableException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RatingServiceUnavailableException.java new file mode 100644 index 0000000..b078963 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RatingServiceUnavailableException.java @@ -0,0 +1,16 @@ +package ru.practicum.aggregation.error.exception.unavailable; + +import ru.practicum.aggregation.error.exception.ServiceUnavailableException; + +public class RatingServiceUnavailableException extends ServiceUnavailableException { + + public RatingServiceUnavailableException(Throwable cause) { + super(""" + Сервис рейтингов временно недоступен. + Повторите попытку позже. + """, + cause + ); + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RequestServiceUnavailableException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RequestServiceUnavailableException.java new file mode 100644 index 0000000..d009904 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RequestServiceUnavailableException.java @@ -0,0 +1,16 @@ +package ru.practicum.aggregation.error.exception.unavailable; + +import ru.practicum.aggregation.error.exception.ServiceUnavailableException; + +public class RequestServiceUnavailableException extends ServiceUnavailableException { + + public RequestServiceUnavailableException(Throwable cause) { + super(""" + Сервис запросов на участие временно недоступен. + Повторите попытку позже. + """, + cause + ); + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/UserServiceUnavailableException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/UserServiceUnavailableException.java new file mode 100644 index 0000000..74fc25d --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/UserServiceUnavailableException.java @@ -0,0 +1,16 @@ +package ru.practicum.aggregation.error.exception.unavailable; + +import ru.practicum.aggregation.error.exception.ServiceUnavailableException; + +public class UserServiceUnavailableException extends ServiceUnavailableException { + + public UserServiceUnavailableException(Throwable cause) { + super(""" + Сервис пользователей временно недоступен. + Повторите попытку позже. + """, + cause + ); + } + +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/util/entity/BaseEntity.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/BaseEntity.java similarity index 96% rename from core/main-service/src/main/java/ru/practicum/ewm/util/entity/BaseEntity.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/model/BaseEntity.java index 15f4e23..ba33c63 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/util/entity/BaseEntity.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/BaseEntity.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.util.entity; +package ru.practicum.aggregation.model; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/Location.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/Location.java similarity index 80% rename from core/main-service/src/main/java/ru/practicum/ewm/model/Location.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/model/Location.java index fb54d09..7bd05a7 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/Location.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/Location.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.model; +package ru.practicum.aggregation.model; import lombok.Builder; import lombok.Data; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java new file mode 100644 index 0000000..c236fa3 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java @@ -0,0 +1,14 @@ +package ru.practicum.aggregation.repository; + +import org.springframework.lang.NonNull; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; + +public interface EventFeignRepository { + + void updateRating(@NonNull RatingUpdateRequest request); + + EventFullDto userFindEventById(long userId, long eventId); + + EventFullDto systemFindEventById(long eventId); +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java new file mode 100644 index 0000000..d9dc7e7 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java @@ -0,0 +1,93 @@ +package ru.practicum.aggregation.repository; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import ru.practicum.aggregation.client.RemoteCallExecutor; +import ru.practicum.aggregation.client.RemoteCallResult; +import ru.practicum.aggregation.client.event.EventClient; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.aggregation.error.exception.processing.UserProcessingException; +import ru.practicum.aggregation.error.exception.unavailable.UserServiceUnavailableException; + +@Slf4j +@Component +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class EventFeignRepositoryImpl implements EventFeignRepository { + + EventClient eventClient; + + @SuppressWarnings("unused") + @Override + public void updateRating(@NonNull RatingUpdateRequest request) { + var eventId = request.eventId(); + switch (RemoteCallExecutor.executeVoid(() -> eventClient.systemUpdateRating(request))) { + case RemoteCallResult.Success(var nullable) -> { + } + case RemoteCallResult.Failure(var message) -> { + log.warn(""" + Бизнес-исключение + Не удалось обновить рейтинг события с id={} + """, eventId); + throw new UserProcessingException(message); + } + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Не удалось обновить рейтинг события с id={} + """, eventId); + throw new UserServiceUnavailableException(cause); + } + } + } + + @Override + public EventFullDto userFindEventById(long userId, long eventId) { + return switch (RemoteCallExecutor.execute(() -> eventClient.userFindEventById(userId, eventId))) { + case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Failure(var message) -> { + log.warn(""" + Бизнес-исключение + Пользователю с id={} + Не удалось получить событие с id={} + """, userId, eventId); + throw new UserProcessingException(message); + } + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Пользователю с id={} + Не удалось получить событие с id={} + """, userId, eventId); + throw new UserServiceUnavailableException(cause); + } + }; + } + + @Override + public EventFullDto systemFindEventById(long eventId) { + return switch (RemoteCallExecutor.execute(() -> eventClient.systemFindEventById(eventId))) { + case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Failure(var message) -> { + log.warn(""" + Бизнес-исключение + Не удалось получить событие с id={} + """, eventId); + throw new UserProcessingException(message); + } + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Не удалось получить событие с id={} + """, eventId); + throw new UserServiceUnavailableException(cause); + } + }; + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java new file mode 100644 index 0000000..6e9c430 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java @@ -0,0 +1,19 @@ +package ru.practicum.aggregation.repository; + +import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.request.ParticipationRequestDto; + +import java.util.List; + +public interface RequestFeignRepository { + + List getConfirmedRequestsCount(List eventIds); + + List findByEventId(long userId, long eventId); + + EventRequestStatusUpdateResult updateStatusRequest(Long userId, + Long eventId, + EventRequestStatusUpdateRequest status); +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java new file mode 100644 index 0000000..692d38d --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java @@ -0,0 +1,100 @@ +package ru.practicum.aggregation.repository; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import ru.practicum.aggregation.client.RemoteCallExecutor; +import ru.practicum.aggregation.client.RemoteCallResult; +import ru.practicum.aggregation.client.request.RequestClient; +import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.enums.ParticipationStatus; +import ru.practicum.aggregation.error.exception.processing.RequestProcessingException; +import ru.practicum.aggregation.error.exception.unavailable.RequestServiceUnavailableException; + +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class RequestFeignRepositoryImpl implements RequestFeignRepository { + + RequestClient requestClient; + + @Override + public List getConfirmedRequestsCount(List eventIds) { + return switch (RemoteCallExecutor.execute(() -> + requestClient.getCount(eventIds, ParticipationStatus.CONFIRMED))) { + case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Failure(var message) -> { + log.warn(""" + Бизнес-исключение + Не удалось получить количество подтвержденных заявок на участие в событиях + eventIds: {} + """, eventIds); + throw new RequestProcessingException(message); + } + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Не удалось получить количество подтвержденных заявок на участие в событиях + eventIds: {} + """, eventIds); + throw new RequestServiceUnavailableException(cause); + } + }; + } + + @Override + public List findByEventId(long userId, long eventId) { + return switch (RemoteCallExecutor.execute(() -> requestClient.findByEventId(userId, eventId))) { + case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Failure(var message) -> { + log.warn(""" + Бизнес-исключение + Не удалось получить запросы пользователей на участие в событии с id={} + """, eventId); + throw new RequestProcessingException(message); + } + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Не удалось получить запросы пользователей на участие в событии с id={} + """, eventId); + throw new RequestServiceUnavailableException(cause); + } + }; + } + + @Override + public EventRequestStatusUpdateResult updateStatusRequest(Long userId, + Long eventId, + EventRequestStatusUpdateRequest status) { + return switch (RemoteCallExecutor.execute(() -> + requestClient.updateStatusRequest(userId, eventId, status))) { + case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Failure(var message) -> { + log.warn(""" + Бизнес-исключение + Не удалось получить запросы пользователей на участие + в событии с id={}, для пользователя с id={} + """, eventId, userId); + throw new RequestProcessingException(message); + } + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Не удалось получить запросы пользователей на участие + в событии с id={}, для пользователя с id={} + """, eventId, userId); + throw new RequestServiceUnavailableException(cause); + } + }; + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepository.java new file mode 100644 index 0000000..d68ea0b --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepository.java @@ -0,0 +1,16 @@ +package ru.practicum.aggregation.repository; + +import ru.practicum.aggregation.dto.user.UserDto; +import ru.practicum.aggregation.dto.user.UserShortDto; + +import java.util.List; + +public interface UserFeignRepository { + + UserDto getUserDtoById(long userId); + + List getUsersByIds(List userIds); + + void checkUser(Long userId); + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java new file mode 100644 index 0000000..7113602 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java @@ -0,0 +1,94 @@ +package ru.practicum.aggregation.repository; + +import lombok.AccessLevel; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import ru.practicum.aggregation.client.RemoteCallExecutor; +import ru.practicum.aggregation.client.RemoteCallResult; +import ru.practicum.aggregation.client.user.UserClient; +import ru.practicum.aggregation.dto.user.UserDto; +import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.error.exception.processing.UserProcessingException; +import ru.practicum.aggregation.error.exception.unavailable.UserServiceUnavailableException; + +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class UserFeignRepositoryImpl implements UserFeignRepository { + + UserClient userClient; + + @Override + public @NonNull UserDto getUserDtoById(long userId) { + return switch (RemoteCallExecutor.execute(() -> userClient.getUser(userId))) { + case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Failure(var message) -> { + log.warn(""" + Бизнес-исключение + Не удалось получить пользователя с id={} + """, userId); + throw new UserProcessingException(message); + } + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Не удалось получить пользователя с id={} + """, userId); + throw new UserServiceUnavailableException(cause); + } + }; + } + + @Override + public List getUsersByIds(List userIds) { + return switch (RemoteCallExecutor.execute(() -> userClient.getShortUsersByIds(userIds))) { + case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Failure(var message) -> { + log.warn(""" + Бизнес-исключение + Не удалось получить краткий список пользователей + userIds: {} + """, userIds); + throw new UserProcessingException(message); + } + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Не удалось получить краткий список пользователей + userIds: {} + """, userIds); + throw new UserServiceUnavailableException(cause); + } + }; + } + + @SuppressWarnings("unused") + @Override + public void checkUser(Long userId) { + switch (RemoteCallExecutor.executeVoid(() -> userClient.checkUser(userId))) { + case RemoteCallResult.Success(var nullable) -> { + } + case RemoteCallResult.Failure(var message) -> { + log.warn(""" + Бизнес-исключение + Не удалось проверить наличие пользователя с id={} + """, userId); + throw new UserProcessingException(message); + } + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Не удалось проверить наличие пользователя с id={} + """, userId); + throw new UserServiceUnavailableException(cause); + } + } + } + +} diff --git a/core/event-service/pom.xml b/core/event-service/pom.xml new file mode 100644 index 0000000..adb167a --- /dev/null +++ b/core/event-service/pom.xml @@ -0,0 +1,132 @@ + + + 4.0.0 + + + ru.practicum + core + 0.0.1-SNAPSHOT + + + event-service + jar + + + + ru.practicum + stat-dto + 0.0.1-SNAPSHOT + + + ru.practicum + stat-client + 0.0.1-SNAPSHOT + + + ru.practicum + aggregation-api + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.zalando + logbook-spring-boot-starter + 4.0.3 + + + io.hypersistence + hypersistence-utils-hibernate-63 + 3.15.2 + compile + + + + org.postgresql + postgresql + runtime + + + com.h2database + h2 + runtime + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-config + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.github.ben-manes.caffeine + caffeine + + + + org.springframework.retry + spring-retry + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-actuator + + + io.micrometer + micrometer-registry-prometheus + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java b/core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java new file mode 100644 index 0000000..c1c1862 --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java @@ -0,0 +1,28 @@ +package ru.practicum.ewm.events; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; +import ru.practicum.aggregation.client.event.EventClient; +import ru.practicum.aggregation.client.request.RequestClient; +import ru.practicum.aggregation.client.user.UserClient; + +@SpringBootApplication +@EnableFeignClients(clients = { + UserClient.class, + EventClient.class, + RequestClient.class +}) +@ComponentScan(value = { + "ru.practicum.ewm.events", + "ru.practicum.stat.client", + "ru.practicum.aggregation.client", + "ru.practicum.aggregation.error", + "ru.practicum.aggregation.repository" +}) +public class EventServiceApp { + public static void main(String[] args) { + SpringApplication.run(EventServiceApp.class, args); + } +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/util/config/MainServiceConfig.java b/core/event-service/src/main/java/ru/practicum/ewm/events/config/EventServiceConfig.java similarity index 91% rename from core/main-service/src/main/java/ru/practicum/ewm/util/config/MainServiceConfig.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/config/EventServiceConfig.java index a39ef11..1fc9973 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/util/config/MainServiceConfig.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/config/EventServiceConfig.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.util.config; +package ru.practicum.ewm.events.config; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; @@ -9,7 +9,7 @@ import java.time.format.DateTimeFormatter; @SpringBootConfiguration -public class MainServiceConfig { +public class EventServiceConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { diff --git a/core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCategoryController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCategoryController.java similarity index 88% rename from core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCategoryController.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCategoryController.java index 341ae49..3175d67 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCategoryController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCategoryController.java @@ -1,12 +1,12 @@ -package ru.practicum.ewm.controller.admin; +package ru.practicum.ewm.events.controller.admin; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.ewm.dto.category.CategoryDto; -import ru.practicum.ewm.dto.category.NewCategoryDto; -import ru.practicum.ewm.service.category.CategoryService; +import ru.practicum.aggregation.dto.category.CategoryDto; +import ru.practicum.aggregation.dto.category.NewCategoryDto; +import ru.practicum.ewm.events.service.category.CategoryService; @RestController @RequestMapping(path = "/admin/categories") diff --git a/core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCompilationController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCompilationController.java similarity index 82% rename from core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCompilationController.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCompilationController.java index a68b957..a2f6ac6 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCompilationController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCompilationController.java @@ -1,13 +1,13 @@ -package ru.practicum.ewm.controller.admin; +package ru.practicum.ewm.events.controller.admin; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.ewm.dto.compilation.CompilationDto; -import ru.practicum.ewm.dto.compilation.CompilationUpdateDto; -import ru.practicum.ewm.dto.compilation.NewCompilationDto; -import ru.practicum.ewm.service.compilation.CompilationService; +import ru.practicum.aggregation.dto.compilation.CompilationDto; +import ru.practicum.aggregation.dto.compilation.CompilationUpdateDto; +import ru.practicum.aggregation.dto.compilation.NewCompilationDto; +import ru.practicum.ewm.events.service.compilation.CompilationService; @RestController @RequestMapping(path = "/admin/compilations") diff --git a/core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminEventController.java similarity index 78% rename from core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminEventController.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminEventController.java index 9aafe96..11dd290 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminEventController.java @@ -1,14 +1,14 @@ -package ru.practicum.ewm.controller.admin; +package ru.practicum.ewm.events.controller.admin; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; -import ru.practicum.ewm.dto.event.AdminGetDto; -import ru.practicum.ewm.dto.event.EventFullDto; -import ru.practicum.ewm.dto.event.UpdateEventAdminRequest; -import ru.practicum.ewm.model.enums.EventState; -import ru.practicum.ewm.service.event.EventService; +import ru.practicum.aggregation.dto.event.request.AdminGetDto; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.event.request.UpdateEventAdminRequest; +import ru.practicum.aggregation.enums.EventState; +import ru.practicum.ewm.events.service.event.EventService; import java.time.LocalDateTime; import java.util.List; @@ -39,13 +39,29 @@ public class AdminEventController { */ @GetMapping public List adminGetEvents( - @RequestParam(required = false) List users, - @RequestParam(required = false) List states, - @RequestParam(required = false) List categories, - @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeStart, - @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeEnd, - @RequestParam(required = false, defaultValue = "0") Integer from, - @RequestParam(required = false, defaultValue = "10") Integer size) { + @RequestParam(required = false) + List users, + + @RequestParam(required = false) + List states, + + @RequestParam(required = false) + List categories, + + @RequestParam(required = false) + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + LocalDateTime rangeStart, + + @RequestParam(required = false) + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + LocalDateTime rangeEnd, + + @RequestParam(required = false, defaultValue = "0") + Integer from, + + @RequestParam(required = false, defaultValue = "10") + Integer size + ) { AdminGetDto getDto = AdminGetDto.builder() .users(users) @@ -79,4 +95,5 @@ public EventFullDto adminUpdateEvent( return eventService.adminUpdateEvent(eventId, request); } + } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/controller/free/FreeCategoryController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCategoryController.java similarity index 90% rename from core/main-service/src/main/java/ru/practicum/ewm/controller/free/FreeCategoryController.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCategoryController.java index f3eb20b..e9a3dc9 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/controller/free/FreeCategoryController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCategoryController.java @@ -1,12 +1,12 @@ -package ru.practicum.ewm.controller.free; +package ru.practicum.ewm.events.controller.free; import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.PositiveOrZero; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; -import ru.practicum.ewm.dto.category.CategoryDto; -import ru.practicum.ewm.service.category.CategoryService; +import ru.practicum.aggregation.dto.category.CategoryDto; +import ru.practicum.ewm.events.service.category.CategoryService; import java.util.List; @@ -32,6 +32,7 @@ public class FreeCategoryController { @GetMapping public List findAll(@RequestParam(defaultValue = "0") @PositiveOrZero Integer from, @RequestParam(defaultValue = "10") @Positive Integer size) { + return categoryService.findAll(from, size); } @@ -47,4 +48,5 @@ public List findAll(@RequestParam(defaultValue = "0") @PositiveOrZe public CategoryDto findById(@PathVariable @Positive Long catId) { return categoryService.findById(catId); } + } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/controller/free/FreeCompilationController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCompilationController.java similarity index 87% rename from core/main-service/src/main/java/ru/practicum/ewm/controller/free/FreeCompilationController.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCompilationController.java index 40450ac..a0680f3 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/controller/free/FreeCompilationController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCompilationController.java @@ -1,11 +1,11 @@ -package ru.practicum.ewm.controller.free; +package ru.practicum.ewm.events.controller.free; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import ru.practicum.ewm.dto.compilation.CompilationDto; -import ru.practicum.ewm.dto.compilation.CompilationSearchFilter; -import ru.practicum.ewm.service.compilation.CompilationService; +import ru.practicum.aggregation.dto.compilation.CompilationDto; +import ru.practicum.aggregation.dto.compilation.CompilationSearchFilter; +import ru.practicum.ewm.events.service.compilation.CompilationService; import java.util.List; @@ -34,8 +34,8 @@ public List getCompilations( @RequestParam(required = false) Boolean pinned, @RequestParam(defaultValue = "0") Integer from, @RequestParam(defaultValue = "10") Integer size, - HttpServletRequest request) { - + HttpServletRequest request + ) { CompilationSearchFilter filter = CompilationSearchFilter.builder() .pinned(pinned) .from(from) @@ -55,6 +55,8 @@ public List getCompilations( @GetMapping("/{compId}") public CompilationDto getCompilationById(@PathVariable Long compId, HttpServletRequest request) { + return compilationService.getById(compId, request); } + } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/controller/free/FreeEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeEventController.java similarity index 80% rename from core/main-service/src/main/java/ru/practicum/ewm/controller/free/FreeEventController.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeEventController.java index 996383e..9f4de43 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/controller/free/FreeEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeEventController.java @@ -1,13 +1,13 @@ -package ru.practicum.ewm.controller.free; +package ru.practicum.ewm.events.controller.free; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; -import ru.practicum.ewm.dto.event.EventFullDto; -import ru.practicum.ewm.dto.event.EventShortDto; -import ru.practicum.ewm.dto.event.FreeGetDto; -import ru.practicum.ewm.service.event.EventService; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.event.response.EventShortDto; +import ru.practicum.aggregation.dto.event.request.FreeGetDto; +import ru.practicum.ewm.events.service.event.EventService; import java.time.LocalDateTime; import java.util.List; @@ -47,18 +47,38 @@ public class FreeEventController { */ @GetMapping public List getFreeEvents( - @RequestParam(required = false) String text, - @RequestParam(required = false) List categories, - @RequestParam(required = false) Boolean paid, - @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeStart, - @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeEnd, - @RequestParam(required = false, defaultValue = "false") Boolean onlyAvailable, - @RequestParam(required = false) FreeGetDto.FreeEventSort sort, - @RequestParam(required = false, defaultValue = "0") Integer from, - @RequestParam(required = false, defaultValue = "10") Integer size, + + @RequestParam(required = false) + String text, + + @RequestParam(required = false) + List categories, + + @RequestParam(required = false) + Boolean paid, + + @RequestParam(required = false) + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + LocalDateTime rangeStart, + + @RequestParam(required = false) + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + LocalDateTime rangeEnd, + + @RequestParam(required = false, defaultValue = "false") + Boolean onlyAvailable, + + @RequestParam(required = false) + FreeGetDto.FreeEventSort sort, + + @RequestParam(required = false, defaultValue = "0") + Integer from, + + @RequestParam(required = false, defaultValue = "10") + Integer size, + HttpServletRequest request ) { - FreeGetDto freeGetDto = FreeGetDto.builder() .text(text) .categories(categories) @@ -84,6 +104,8 @@ public List getFreeEvents( @GetMapping(value = "/{eventId}") public EventFullDto getFreeEventById(@PathVariable Long eventId, HttpServletRequest request) { + return eventService.getFreeEventById(eventId, request); } + } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java new file mode 100644 index 0000000..81b35da --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java @@ -0,0 +1,28 @@ +package ru.practicum.ewm.events.controller.system; + +import jakarta.validation.constraints.Positive; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.ewm.events.service.event.EventService; + +@Slf4j +@RestController +@RequestMapping("/system/events") +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class SystemEventController { + + EventService eventService; + + @GetMapping("/{eventId}") + EventFullDto systemFindEventById(@PathVariable @Positive Long eventId) { + return eventService.findEventById(eventId); + } +} diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java new file mode 100644 index 0000000..4e3a714 --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java @@ -0,0 +1,28 @@ +package ru.practicum.ewm.events.controller.system; + +import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.ewm.events.service.rating.RatingService; + +@Slf4j +@RestController +@RequestMapping(path = "/system/events/ratings") +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class SystemRatingController { + + RatingService ratingService; + + @PatchMapping + public void updateRating(@RequestBody @NotNull RatingUpdateRequest request) { + ratingService.updateRating(request); + } +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/controller/user/UserEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java similarity index 82% rename from core/main-service/src/main/java/ru/practicum/ewm/controller/user/UserEventController.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java index 005aaad..8f08a44 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/controller/user/UserEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.controller.user; +package ru.practicum.ewm.events.controller.user; import jakarta.validation.Valid; import jakarta.validation.constraints.Positive; @@ -6,15 +6,15 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.ewm.dto.event.EventFullDto; -import ru.practicum.ewm.dto.event.EventShortDto; -import ru.practicum.ewm.dto.event.NewEventDto; -import ru.practicum.ewm.dto.event.UpdateEventUserRequest; -import ru.practicum.ewm.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.ewm.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.ewm.dto.request.ParticipationRequestDto; -import ru.practicum.ewm.service.event.EventService; -import ru.practicum.ewm.service.request.RequestService; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.event.response.EventShortDto; +import ru.practicum.aggregation.dto.event.request.NewEventDto; +import ru.practicum.aggregation.dto.event.request.UpdateEventUserRequest; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.ewm.events.service.event.EventService; +import ru.practicum.ewm.events.service.request.RequestService; import java.util.List; @@ -37,9 +37,11 @@ public class UserEventController { * @return List<{@link EventShortDto}> */ @GetMapping - public List findEventsByUserId(@PathVariable @Positive Long userId, - @RequestParam(defaultValue = "0") @PositiveOrZero Integer from, - @RequestParam(defaultValue = "10") @Positive Integer size) { + public List findEventsByUserId( + @PathVariable @Positive Long userId, + @RequestParam(defaultValue = "0") @PositiveOrZero Integer from, + @RequestParam(defaultValue = "10") @Positive Integer size + ) { return eventService.findByUserId(userId, from, size); } @@ -53,7 +55,8 @@ public List findEventsByUserId(@PathVariable @Positive Long userI @GetMapping("/{eventId}") public EventFullDto findEventById(@PathVariable @Positive Long userId, @PathVariable @Positive Long eventId) { - return eventService.findEventById(userId, eventId); + + return eventService.findEventByUserIdAndEventId(userId, eventId); } /** @@ -93,6 +96,7 @@ public EventFullDto addEvent(@PathVariable @Positive Long userId, public EventFullDto patchEvent(@PathVariable @Positive Long userId, @PathVariable @Positive Long eventId, @RequestBody @Valid UpdateEventUserRequest request) { + return eventService.patchEvent(userId, eventId, request); } @@ -108,6 +112,7 @@ public EventFullDto patchEvent(@PathVariable @Positive Long userId, @GetMapping("/{eventId}/requests") public List getRequests(@PathVariable @Positive Long userId, @PathVariable @Positive Long eventId) { + return requestService.findByEventId(userId, eventId); } @@ -131,9 +136,12 @@ public List getRequests(@PathVariable @Positive Long us * @return {@link EventRequestStatusUpdateResult} */ @PatchMapping("/{eventId}/requests") - public EventRequestStatusUpdateResult patchRequests(@PathVariable @Positive Long userId, - @PathVariable @Positive Long eventId, - @RequestBody @Valid EventRequestStatusUpdateRequest status) { + public EventRequestStatusUpdateResult patchRequests( + @PathVariable @Positive Long userId, + @PathVariable @Positive Long eventId, + @RequestBody @Valid EventRequestStatusUpdateRequest status + ) { return requestService.updateStatusRequest(userId, eventId, status); } + } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/Category.java b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Category.java similarity index 82% rename from core/main-service/src/main/java/ru/practicum/ewm/model/Category.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/entity/Category.java index d21838c..2d4ba2b 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/Category.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Category.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.model; +package ru.practicum.ewm.events.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -7,7 +7,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.SuperBuilder; -import ru.practicum.ewm.util.entity.BaseEntity; +import ru.practicum.aggregation.model.BaseEntity; @Getter @Setter diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/Compilation.java b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Compilation.java similarity index 88% rename from core/main-service/src/main/java/ru/practicum/ewm/model/Compilation.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/entity/Compilation.java index 5d02b70..a9d9a4b 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/Compilation.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Compilation.java @@ -1,10 +1,10 @@ -package ru.practicum.ewm.model; +package ru.practicum.ewm.events.entity; import jakarta.persistence.*; import lombok.*; import lombok.experimental.FieldDefaults; import lombok.experimental.SuperBuilder; -import ru.practicum.ewm.util.entity.BaseEntity; +import ru.practicum.aggregation.model.BaseEntity; import java.util.HashSet; import java.util.Set; diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/Event.java b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Event.java similarity index 93% rename from core/main-service/src/main/java/ru/practicum/ewm/model/Event.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/entity/Event.java index 32f8cf0..bf5d112 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/Event.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Event.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.model; +package ru.practicum.ewm.events.entity; import io.hypersistence.utils.hibernate.type.json.JsonBinaryType; import jakarta.persistence.*; @@ -11,8 +11,9 @@ import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.annotations.Type; import org.hibernate.type.SqlTypes; -import ru.practicum.ewm.model.enums.EventState; -import ru.practicum.ewm.util.entity.BaseEntity; +import ru.practicum.aggregation.enums.EventState; +import ru.practicum.aggregation.model.BaseEntity; +import ru.practicum.aggregation.model.Location; import java.time.LocalDateTime; import java.util.List; @@ -48,9 +49,8 @@ public class Event extends BaseEntity { LocalDateTime eventDate; /// Пользователь инициатор события - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "initiator_id") - User initiator; + Long initiatorId; /// Широта и долгота места проведения события @Type(JsonBinaryType.class) diff --git a/core/main-service/src/main/java/ru/practicum/ewm/mapper/CategoryMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CategoryMapper.java similarity index 58% rename from core/main-service/src/main/java/ru/practicum/ewm/mapper/CategoryMapper.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CategoryMapper.java index 448c6d4..4fc2527 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/mapper/CategoryMapper.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CategoryMapper.java @@ -1,10 +1,10 @@ -package ru.practicum.ewm.mapper; +package ru.practicum.ewm.events.mapper; import lombok.experimental.UtilityClass; import org.springframework.lang.NonNull; -import ru.practicum.ewm.dto.category.CategoryDto; -import ru.practicum.ewm.dto.category.NewCategoryDto; -import ru.practicum.ewm.model.Category; +import ru.practicum.aggregation.dto.category.CategoryDto; +import ru.practicum.aggregation.dto.category.NewCategoryDto; +import ru.practicum.ewm.events.entity.Category; @UtilityClass public class CategoryMapper { @@ -15,12 +15,6 @@ public CategoryDto toDto(@NonNull Category category) { .build(); } - public Category toEntity(@NonNull CategoryDto categoryDto) { - return Category.builder() - .name(categoryDto.name()) - .build(); - } - public Category toEntity(@NonNull NewCategoryDto newCategoryDto) { return Category.builder() .name(newCategoryDto.name()) diff --git a/core/main-service/src/main/java/ru/practicum/ewm/mapper/CompilationMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CompilationMapper.java similarity index 69% rename from core/main-service/src/main/java/ru/practicum/ewm/mapper/CompilationMapper.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CompilationMapper.java index db94788..e62dbce 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/mapper/CompilationMapper.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CompilationMapper.java @@ -1,11 +1,12 @@ -package ru.practicum.ewm.mapper; +package ru.practicum.ewm.events.mapper; import lombok.experimental.UtilityClass; import org.springframework.lang.NonNull; -import ru.practicum.ewm.dto.compilation.CompilationDto; -import ru.practicum.ewm.dto.compilation.NewCompilationDto; -import ru.practicum.ewm.model.Compilation; -import ru.practicum.ewm.model.Event; +import ru.practicum.aggregation.dto.compilation.CompilationDto; +import ru.practicum.aggregation.dto.compilation.NewCompilationDto; +import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.ewm.events.entity.Compilation; +import ru.practicum.ewm.events.entity.Event; import java.util.HashSet; import java.util.Map; @@ -15,6 +16,7 @@ public class CompilationMapper { public CompilationDto toCompilationDto(@NonNull Compilation compilation, + @NonNull Map eventIdToInitiator, @NonNull Map confirmedRequests, @NonNull Map views) { return CompilationDto.builder() @@ -24,6 +26,7 @@ public CompilationDto toCompilationDto(@NonNull Compilation compilation, .events(compilation.getEvents().stream() .map(event -> EventMapper.toEventShortDto( event, + eventIdToInitiator.get(event.getId()), confirmedRequests.getOrDefault(event.getId(), 0L), views.getOrDefault(event.getId(), 0L) )) @@ -38,4 +41,5 @@ public Compilation toEntity(@NonNull NewCompilationDto dto, Set events) { .events(events != null ? events : new HashSet<>()) .build(); } + } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/mapper/EventMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java similarity index 76% rename from core/main-service/src/main/java/ru/practicum/ewm/mapper/EventMapper.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java index de4cbbd..98e0692 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/mapper/EventMapper.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java @@ -1,12 +1,17 @@ -package ru.practicum.ewm.mapper; +package ru.practicum.ewm.events.mapper; import lombok.experimental.UtilityClass; import org.springframework.lang.NonNull; -import ru.practicum.ewm.dto.event.*; -import ru.practicum.ewm.model.Category; -import ru.practicum.ewm.model.Event; -import ru.practicum.ewm.model.User; -import ru.practicum.ewm.model.enums.EventState; +import ru.practicum.aggregation.dto.event.request.NewEventDto; +import ru.practicum.aggregation.dto.event.request.UpdateEventAdminRequest; +import ru.practicum.aggregation.dto.event.request.UpdateEventUserRequest; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.event.response.EventShortDto; +import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.enums.EventState; +import ru.practicum.ewm.events.entity.Category; +import ru.practicum.ewm.events.entity.Event; +import ru.practicum.ewm.events.model.EventData; import java.time.LocalDateTime; import java.util.Optional; @@ -14,14 +19,17 @@ @UtilityClass public class EventMapper { - public EventShortDto toEventShortDto(@NonNull Event event, long confirmedRequests, long views) { + public EventShortDto toEventShortDto(@NonNull Event event, + UserShortDto initiator, + long confirmedRequests, + long views) { return EventShortDto.builder() .annotation(event.getAnnotation()) .category(CategoryMapper.toDto(event.getCategory())) .confirmedRequests(confirmedRequests) .eventDate(event.getEventDate()) .id(event.getId()) - .initiator(UserMapper.toUserShortDto(event.getInitiator())) + .initiator(initiator) .paid(event.isPaid()) .title(event.getTitle()) .views(views) @@ -29,16 +37,17 @@ public EventShortDto toEventShortDto(@NonNull Event event, long confirmedRequest .build(); } - public EventFullDto toEventFullDto(@NonNull Event event, long confirmedRequests, long views) { + public EventFullDto toEventFullDto(@NonNull Event event, + @NonNull EventData eventData) { return EventFullDto.builder() .id(event.getId()) .annotation(event.getAnnotation()) .category(CategoryMapper.toDto(event.getCategory())) - .confirmedRequests(confirmedRequests) + .confirmedRequests(eventData.confirmedRequests()) .createdOn(event.getCreatedOn()) .description(event.getDescription()) .eventDate(event.getEventDate()) - .initiator(UserMapper.toUserShortDto(event.getInitiator())) + .initiator(eventData.initiator()) .location(event.getLocation()) .paid(event.isPaid()) .participantLimit(event.getParticipantLimit()) @@ -46,7 +55,7 @@ public EventFullDto toEventFullDto(@NonNull Event event, long confirmedRequests, .requestModeration(event.isRequestModeration()) .state(event.getState()) .title(event.getTitle()) - .views(views) + .views(eventData.views()) .rate(event.getRate()) .build(); } @@ -54,7 +63,7 @@ public EventFullDto toEventFullDto(@NonNull Event event, long confirmedRequests, public Event toEntity(@NonNull NewEventDto newEventDto, Category category, LocalDateTime createdOn, - User initiator, + Long initiatorId, LocalDateTime publishedOn, EventState state) { return Event.builder() @@ -63,7 +72,7 @@ public Event toEntity(@NonNull NewEventDto newEventDto, .createdOn(createdOn) .description(newEventDto.description()) .eventDate(newEventDto.eventDate()) - .initiator(initiator) + .initiatorId(initiatorId) .location(newEventDto.location()) .paid(newEventDto.paid() != null && newEventDto.paid()) .participantLimit(newEventDto.participantLimit() == null ? 0 : newEventDto.participantLimit()) @@ -75,6 +84,7 @@ public Event toEntity(@NonNull NewEventDto newEventDto, .build(); } + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") public static Event update(@NonNull Event oldEvent, @NonNull UpdateEventAdminRequest request, EventState state, @@ -143,4 +153,5 @@ public void merge(Event event, UpdateEventUserRequest dto) { event.setRequestModeration(dto.requestModeration()); } } + } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/mapper/StateMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/StateMapper.java similarity index 72% rename from core/main-service/src/main/java/ru/practicum/ewm/mapper/StateMapper.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/mapper/StateMapper.java index 3acbea8..4448b98 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/mapper/StateMapper.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/StateMapper.java @@ -1,12 +1,14 @@ -package ru.practicum.ewm.mapper; +package ru.practicum.ewm.events.mapper; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; -import ru.practicum.ewm.model.enums.EventState; -import ru.practicum.ewm.model.enums.UserStateAction; +import ru.practicum.aggregation.enums.EventState; +import ru.practicum.aggregation.enums.UserStateAction; @Component public class StateMapper { + @Nullable public static EventState mapUserEventAction(UserStateAction action) { return switch (action) { case CANCEL_REVIEW -> EventState.CANCELED; @@ -15,6 +17,7 @@ public static EventState mapUserEventAction(UserStateAction action) { }; } + @Nullable public static EventState mapAdminEventAction(UserStateAction action) { return switch (action) { case PUBLISH_EVENT -> EventState.PUBLISHED; @@ -22,4 +25,5 @@ public static EventState mapAdminEventAction(UserStateAction action) { case null, default -> null; }; } + } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/UserMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/UserMapper.java new file mode 100644 index 0000000..beafc5e --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/UserMapper.java @@ -0,0 +1,28 @@ +package ru.practicum.ewm.events.mapper; + +import lombok.experimental.UtilityClass; +import org.springframework.lang.NonNull; +import ru.practicum.aggregation.dto.user.UserDto; +import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.ewm.events.model.User; + +@UtilityClass +public class UserMapper { + + public UserShortDto toUserShortDto(@NonNull User user) { + return UserShortDto.builder() + .id(user.getId()) + .name(user.getName()) + .email(user.getEmail()) + .build(); + } + + public User toEntity(@NonNull UserDto userDto) { + return User.builder() + .id(userDto.id()) + .name(userDto.name()) + .email(userDto.email()) + .build(); + } + +} \ No newline at end of file diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/model/EventData.java b/core/event-service/src/main/java/ru/practicum/ewm/events/model/EventData.java new file mode 100644 index 0000000..3bb3cf5 --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/model/EventData.java @@ -0,0 +1,18 @@ +package ru.practicum.ewm.events.model; + +import lombok.Builder; +import ru.practicum.aggregation.dto.user.UserShortDto; + +/** + * @apiNote + * UserShortDto initiator
+ * long confirmedRequests
+ * long views + */ +@Builder +public record EventData( + UserShortDto initiator, + long confirmedRequests, + long views +) { +} \ No newline at end of file diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java b/core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java new file mode 100644 index 0000000..e69bf2f --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java @@ -0,0 +1,19 @@ +package ru.practicum.ewm.events.model; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.FieldDefaults; +import lombok.experimental.SuperBuilder; +import ru.practicum.aggregation.model.BaseEntity; + +@Getter +@Setter +@SuperBuilder +@NoArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class User extends BaseEntity { + String name; + String email; +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dao/CategoryRepository.java b/core/event-service/src/main/java/ru/practicum/ewm/events/repository/CategoryRepository.java similarity index 66% rename from core/main-service/src/main/java/ru/practicum/ewm/dao/CategoryRepository.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/repository/CategoryRepository.java index 48dac91..49b7fec 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dao/CategoryRepository.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/repository/CategoryRepository.java @@ -1,7 +1,7 @@ -package ru.practicum.ewm.dao; +package ru.practicum.ewm.events.repository; import org.springframework.data.jpa.repository.JpaRepository; -import ru.practicum.ewm.model.Category; +import ru.practicum.ewm.events.entity.Category; public interface CategoryRepository extends JpaRepository { diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dao/CompilationRepository.java b/core/event-service/src/main/java/ru/practicum/ewm/events/repository/CompilationRepository.java similarity index 76% rename from core/main-service/src/main/java/ru/practicum/ewm/dao/CompilationRepository.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/repository/CompilationRepository.java index 0bbb5aa..c860baa 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dao/CompilationRepository.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/repository/CompilationRepository.java @@ -1,9 +1,9 @@ -package ru.practicum.ewm.dao; +package ru.practicum.ewm.events.repository; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; -import ru.practicum.ewm.model.Compilation; +import ru.practicum.ewm.events.entity.Compilation; public interface CompilationRepository extends JpaRepository { diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dao/EventRepository.java b/core/event-service/src/main/java/ru/practicum/ewm/events/repository/EventRepository.java similarity index 50% rename from core/main-service/src/main/java/ru/practicum/ewm/dao/EventRepository.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/repository/EventRepository.java index 452a0bc..66e6bec 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dao/EventRepository.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/repository/EventRepository.java @@ -1,20 +1,16 @@ -package ru.practicum.ewm.dao; +package ru.practicum.ewm.events.repository; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import ru.practicum.ewm.model.Event; -import ru.practicum.ewm.model.enums.EventState; +import ru.practicum.aggregation.enums.EventState; +import ru.practicum.ewm.events.entity.Event; -import java.util.Collection; -import java.util.Optional; +import java.util.List; -public interface EventRepository extends JpaRepository, - JpaSpecificationExecutor { +public interface EventRepository extends JpaRepository, JpaSpecificationExecutor { - Collection findByInitiatorId(Long userId, PageRequest pageRequest); - - Optional findByIdAndState(Long eventId, EventState state); + List findByInitiatorId(Long userId, PageRequest pageRequest); boolean existsByCategoryId(Long categoryId); diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/category/CategoryService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryService.java similarity index 60% rename from core/main-service/src/main/java/ru/practicum/ewm/service/category/CategoryService.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryService.java index c1843cf..ed48928 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/category/CategoryService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryService.java @@ -1,23 +1,23 @@ -package ru.practicum.ewm.service.category; +package ru.practicum.ewm.events.service.category; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.ewm.dto.category.CategoryDto; -import ru.practicum.ewm.dto.category.NewCategoryDto; +import ru.practicum.aggregation.dto.category.CategoryDto; +import ru.practicum.aggregation.dto.category.NewCategoryDto; import java.util.List; +@Transactional public interface CategoryService { - @Transactional CategoryDto adminAddNewCategory(NewCategoryDto newCategoryDto); + @Transactional(readOnly = true) List findAll(Integer from, Integer size); + @Transactional(readOnly = true) CategoryDto findById(Long catId); - @Transactional CategoryDto updateCategory(Long catId, CategoryDto categoryDto); - @Transactional void deleteCategory(Long catId); } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/category/CategoryServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryServiceImpl.java similarity index 82% rename from core/main-service/src/main/java/ru/practicum/ewm/service/category/CategoryServiceImpl.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryServiceImpl.java index 28e435e..f8d664f 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/category/CategoryServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryServiceImpl.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.service.category; +package ru.practicum.ewm.events.service.category; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -7,14 +7,14 @@ import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.ewm.dao.CategoryRepository; -import ru.practicum.ewm.dao.EventRepository; -import ru.practicum.ewm.dto.category.CategoryDto; -import ru.practicum.ewm.dto.category.NewCategoryDto; -import ru.practicum.ewm.mapper.CategoryMapper; -import ru.practicum.ewm.model.Category; -import ru.practicum.ewm.util.error.exception.ConflictException; -import ru.practicum.ewm.util.error.exception.NotFoundException; +import ru.practicum.aggregation.dto.category.CategoryDto; +import ru.practicum.aggregation.dto.category.NewCategoryDto; +import ru.practicum.aggregation.error.exception.ConflictException; +import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.ewm.events.mapper.CategoryMapper; +import ru.practicum.ewm.events.entity.Category; +import ru.practicum.ewm.events.repository.CategoryRepository; +import ru.practicum.ewm.events.repository.EventRepository; import java.util.List; import java.util.stream.Collectors; @@ -84,4 +84,5 @@ private Category getCategoryById(long categoryId) { () -> new NotFoundException("Категория с id=" + categoryId + " не найдена") ); } + } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/compilation/CompilationService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationService.java similarity index 50% rename from core/main-service/src/main/java/ru/practicum/ewm/service/compilation/CompilationService.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationService.java index 8af94ca..f47bd42 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/compilation/CompilationService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationService.java @@ -1,15 +1,18 @@ -package ru.practicum.ewm.service.compilation; +package ru.practicum.ewm.events.service.compilation; import jakarta.servlet.http.HttpServletRequest; -import ru.practicum.ewm.dto.compilation.CompilationDto; -import ru.practicum.ewm.dto.compilation.CompilationSearchFilter; -import ru.practicum.ewm.dto.compilation.CompilationUpdateDto; -import ru.practicum.ewm.dto.compilation.NewCompilationDto; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.aggregation.dto.compilation.CompilationDto; +import ru.practicum.aggregation.dto.compilation.CompilationSearchFilter; +import ru.practicum.aggregation.dto.compilation.CompilationUpdateDto; +import ru.practicum.aggregation.dto.compilation.NewCompilationDto; import java.util.List; +@Transactional public interface CompilationService { + @Transactional(readOnly = true) CompilationDto getById(Long compilationId, HttpServletRequest request); void delById(Long compilationId); @@ -18,5 +21,6 @@ public interface CompilationService { CompilationDto updateCompilation(Long compilationId, CompilationUpdateDto compilation); + @Transactional(readOnly = true) List getByFilter(CompilationSearchFilter filter, HttpServletRequest request); } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java new file mode 100644 index 0000000..a9aeba6 --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java @@ -0,0 +1,228 @@ +package ru.practicum.ewm.events.service.compilation; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Service; +import ru.practicum.aggregation.dto.compilation.CompilationDto; +import ru.practicum.aggregation.dto.compilation.CompilationSearchFilter; +import ru.practicum.aggregation.dto.compilation.CompilationUpdateDto; +import ru.practicum.aggregation.dto.compilation.NewCompilationDto; +import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.model.BaseEntity; +import ru.practicum.aggregation.repository.RequestFeignRepository; +import ru.practicum.aggregation.repository.UserFeignRepository; +import ru.practicum.ewm.events.mapper.CompilationMapper; +import ru.practicum.ewm.events.entity.Compilation; +import ru.practicum.ewm.events.entity.Event; +import ru.practicum.ewm.events.repository.CompilationRepository; +import ru.practicum.ewm.events.repository.EventRepository; +import ru.practicum.ewm.events.statistic.StatRestRepository; +import ru.practicum.stat.dto.ViewStatsDto; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class CompilationServiceImpl implements CompilationService { + + EventRepository eventRepository; + CompilationRepository compilationRepository; + + StatRestRepository statRestRepository; + + UserFeignRepository userFeignRepository; + RequestFeignRepository requestFeignRepository; + + @Override + public CompilationDto getById(Long compilationId, HttpServletRequest request) { + statRestRepository.sendHitRequest(request); + + var compilation = getCompilationById(compilationId); + var confirmedRequests = getConfirmedRequests(List.of(compilation)); + var views = getViews(List.of(compilation)); + var events = compilation.getEvents(); + var eventIdToInitiator = eventIdToInitiatorMap(events); + + return CompilationMapper.toCompilationDto(compilation, eventIdToInitiator, confirmedRequests, views); + } + + @Override + public void delById(Long compilationId) { + getCompilationById(compilationId); + compilationRepository.deleteById(compilationId); + } + + @Override + public CompilationDto addCompilation(@NonNull NewCompilationDto compilationDto) { + Set events = new HashSet<>(); + + if (compilationDto.getEvents() != null && !compilationDto.getEvents().isEmpty()) { + events = new HashSet<>(eventRepository.findAllById(compilationDto.getEvents())); + + if (events.size() < compilationDto.getEvents().size()) { + throw new NotFoundException("Одно или несколько событий не найдены"); + } + } + + var savedCompilation = compilationRepository.save(CompilationMapper.toEntity(compilationDto, events)); + + Map confirmedRequests = new HashMap<>(); + savedCompilation.getEvents().forEach(event -> confirmedRequests.put(event.getId(), 0L)); + + Map views = new HashMap<>(); + savedCompilation.getEvents().forEach(event -> views.put(event.getId(), 0L)); + + var eventIdToInitiator = eventIdToInitiatorMap(events); + + return CompilationMapper.toCompilationDto( + savedCompilation, eventIdToInitiator, confirmedRequests, views); + } + + @Override + public CompilationDto updateCompilation(Long compilationId, + @NonNull CompilationUpdateDto compilationUpdateDto) { + var compilationInDb = getCompilationById(compilationId); + + if (compilationUpdateDto.getEvents() != null) { + List eventsUpdate = new ArrayList<>(); + + if (!compilationUpdateDto.getEvents().isEmpty()) { + eventsUpdate = eventRepository.findAllById(compilationUpdateDto.getEvents()); + if (eventsUpdate.size() < compilationUpdateDto.getEvents().size()) { + throw new NotFoundException("Одно или несколько событий не найдены"); + } + } + compilationInDb.setEvents(new HashSet<>(eventsUpdate)); + } + + if (compilationUpdateDto.getTitle() != null && !compilationUpdateDto.getTitle().isBlank()) { + compilationInDb.setTitle(compilationUpdateDto.getTitle()); + } + + if (compilationUpdateDto.getPinned() != null) { + compilationInDb.setPinned(compilationUpdateDto.getPinned()); + } + + var confirmedRequests = getConfirmedRequests(List.of(compilationInDb)); + var views = getViews(List.of(compilationInDb)); + var compilation = getCompilationById(compilationId); + var events = compilation.getEvents(); + var eventIdToInitiator = eventIdToInitiatorMap(events); + + return CompilationMapper.toCompilationDto( + compilationInDb, eventIdToInitiator, confirmedRequests, views); + } + + @NonNull + private Map eventIdToInitiatorMap(@NonNull Set events) { + var initiatorIds = events.stream().map(Event::getInitiatorId).toList(); + var initiatorMap = userFeignRepository.getUsersByIds(initiatorIds) + .stream() + .collect(Collectors.toMap(UserShortDto::id, Function.identity())); + + return events.stream() + .collect(Collectors.toMap(BaseEntity::getId, e -> + initiatorMap.get(e.getInitiatorId()) + ) + ); + } + + @Override + public List getByFilter(@NonNull CompilationSearchFilter filter, + HttpServletRequest request) { + var pageable = PageRequest.of(filter.getFrom() / filter.getSize(), filter.getSize()); + Page compilationsPage; + + if (filter.getPinned() != null) { + compilationsPage = compilationRepository.findAllByPinned(filter.getPinned(), pageable); + } else { + compilationsPage = compilationRepository.findAll(pageable); + } + + List compilations = compilationsPage.getContent(); + + if (compilations.isEmpty()) { + return Collections.emptyList(); + } + + Map allConfirmedRequests = getConfirmedRequests(compilations); + Map allViews = getViews(compilations); + + var result = new ArrayList(); + for (Compilation compilation : compilations) { + + var events = compilation.getEvents(); + var eventIdToInitiator = eventIdToInitiatorMap(events); + + result.add(CompilationMapper.toCompilationDto( + compilation, eventIdToInitiator, allConfirmedRequests, allViews)); + } + + return result; + } + + @NonNull + private Compilation getCompilationById(long compilationId) { + return compilationRepository.findById(compilationId).orElseThrow( + () -> new NotFoundException("Подборка с id=" + compilationId + " не найдена") + ); + } + + /// Map + @NonNull + private Map getConfirmedRequests(@NonNull Collection compilations) { + List events = compilations.stream() + .flatMap(c -> c.getEvents().stream()) + .toList(); + + if (events.isEmpty()) return Collections.emptyMap(); + + List eventRequestCountList = requestFeignRepository + .getConfirmedRequestsCount(events.stream().map(Event::getId).toList()); + + return eventRequestCountList.stream() + .collect(Collectors.toMap( + EventRequestCount::getEventId, + EventRequestCount::getCount, + (existing, replacement) -> existing + ) + ); + } + + /// Map + @NonNull + private Map getViews(@NonNull Collection compilations) { + + // Все уникальные события + List allEvents = compilations.stream() + .flatMap(c -> c.getEvents().stream()) + .distinct() + .toList(); + + if (allEvents.isEmpty()) return Collections.emptyMap(); + + // Все URIs + List uris = allEvents.stream().map(e -> "/events/" + e.getId()).toList(); + + List stats = statRestRepository.getStat(uris, false); + + // Map + return stats.stream().collect(Collectors.toMap(statsDto -> + Long.parseLong(statsDto.getUri().replace("/events/", "")), + ViewStatsDto::getHits, + (a, b) -> a + ) + ); + } + +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/event/EventService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java similarity index 77% rename from core/main-service/src/main/java/ru/practicum/ewm/service/event/EventService.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java index af8549f..ceb7d90 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/event/EventService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java @@ -1,12 +1,14 @@ -package ru.practicum.ewm.service.event; +package ru.practicum.ewm.events.service.event; import jakarta.servlet.http.HttpServletRequest; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.ewm.dto.event.*; +import ru.practicum.aggregation.dto.event.request.*; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.event.response.EventShortDto; import java.util.List; -@Transactional +@Transactional(readOnly = true) public interface EventService { /// Получение событий незарегистрированным пользователем с возможностью фильтрации @@ -19,21 +21,27 @@ public interface EventService { EventFullDto getFreeEventById(Long eventId, HttpServletRequest request); /// Добавление нового события зарегистрированным пользователем + @Transactional EventFullDto userAddNewEvent(Long userId, NewEventDto newEventDto); /// Поиск событий администратором - @Transactional(readOnly = true) List adminGetEvents(AdminGetDto adminGetDto); /// Редактирование администратором данных события и его статуса (отклонение/публикация) + @Transactional EventFullDto adminUpdateEvent(Long eventId, UpdateEventAdminRequest request); /// Получение событий, добавленных текущим пользователем List findByUserId(Long userId, Integer from, Integer size); /// Получение полной информации о событии добавленном текущим пользователем - EventFullDto findEventById(Long userId, Long eventId); + EventFullDto findEventByUserIdAndEventId(Long userId, Long eventId); + + /// Получение полной информации о событии по ID + EventFullDto findEventById(long eventId); /// Изменение события добавленного текущим пользователем + @Transactional EventFullDto patchEvent(Long userId, Long eventId, UpdateEventUserRequest request); + } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java new file mode 100644 index 0000000..70f65af --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java @@ -0,0 +1,514 @@ +package ru.practicum.ewm.events.service.event; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.ValidationException; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Service; +import ru.practicum.aggregation.dto.event.request.*; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.event.response.EventShortDto; +import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.enums.AdminStateAction; +import ru.practicum.aggregation.enums.EventState; +import ru.practicum.aggregation.enums.UserStateAction; +import ru.practicum.aggregation.error.exception.ConflictException; +import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.repository.RequestFeignRepositoryImpl; +import ru.practicum.aggregation.repository.UserFeignRepositoryImpl; +import ru.practicum.ewm.events.entity.Category; +import ru.practicum.ewm.events.entity.Event; +import ru.practicum.ewm.events.mapper.EventMapper; +import ru.practicum.ewm.events.mapper.StateMapper; +import ru.practicum.ewm.events.mapper.UserMapper; +import ru.practicum.ewm.events.model.EventData; +import ru.practicum.ewm.events.model.User; +import ru.practicum.ewm.events.repository.CategoryRepository; +import ru.practicum.ewm.events.repository.EventRepository; +import ru.practicum.ewm.events.specification.EventSpecifications; +import ru.practicum.ewm.events.specification.SpecBuilder; +import ru.practicum.ewm.events.statistic.StatRestRepository; +import ru.practicum.stat.dto.ViewStatsDto; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class EventServiceImpl implements EventService { + + StatRestRepository statRepository; + EventRepository eventRepository; + CategoryRepository categoryRepository; + + UserFeignRepositoryImpl userFeignRepository; + RequestFeignRepositoryImpl requestFeignRepository; + + private static final String EVENTS_PATH = "/events/"; + private static final int HOURS_BEFORE_START = 2; + + @Override + public List getFreeEvents(@NonNull FreeGetDto dto, HttpServletRequest request) { + + if (dto.rangeStart() != null && dto.rangeEnd() != null) { + if (dto.rangeEnd().isBefore(dto.rangeStart())) { + throw new ValidationException("Окончание события не может быть раньше начала"); + } + } + + statRepository.sendHitRequest(request); + + SpecBuilder builder = SpecBuilder.builder() + .and(EventSpecifications.isPublished()) + .andIf(dto.text() != null && !dto.text().isBlank(), + () -> EventSpecifications.textContains(dto.text())) + .andIf(dto.categories() != null && !dto.categories().isEmpty(), + () -> EventSpecifications.hasCategories(dto.categories())) + .andIf(dto.paid() != null, + () -> EventSpecifications.isPaid(dto.paid())) + .andIf(Boolean.TRUE.equals(dto.onlyAvailable()), + () -> EventSpecifications.onlyAvailable(true)); + + boolean hasStart = dto.rangeStart() != null; + boolean hasEnd = dto.rangeEnd() != null; + + if (!hasStart && !hasEnd) { + builder.and(EventSpecifications.eventDateAfterNow(LocalDateTime.now())); + } else { + builder + .andIf(hasStart, + () -> EventSpecifications.dateAfter(dto.rangeStart())) + .andIf(hasEnd, + () -> EventSpecifications.dateBefore(dto.rangeEnd())); + } + + Specification spec = builder.build(); + + Sort sort = Sort.unsorted(); + + if (dto.sort() != null) { + switch (dto.sort()) { + case EVENT_DATE -> sort = Sort.by("eventDate").ascending(); + case VIEWS -> sort = Sort.by("views").descending(); + } + } + + Pageable pageable = PageRequest.of((dto.from() / dto.size()), dto.size(), sort); + + List events = eventRepository.findAll(spec, pageable).getContent(); + if (events.isEmpty()) { + return Collections.emptyList(); + } + + List eventRequestCountList = requestFeignRepository.getConfirmedRequestsCount( + events.stream().map(Event::getId).toList()); + + Map requestCountMap = new HashMap<>(); + if (!eventRequestCountList.isEmpty()) { + eventRequestCountList.forEach(eventRequestCount -> + requestCountMap.put(eventRequestCount.getEventId(), eventRequestCount.getCount()) + ); + } + + List uris = events.stream().map(event -> EVENTS_PATH + event.getId()).toList(); + List stats = statRepository.getStat( + uris, dto.rangeStart(), dto.rangeEnd(), false); + + Map initiators = getUserShortDtoMap(events); + + return events.stream() + .map(event -> { + var userId = requestCountMap.get(event.getInitiatorId()); + var initiator = initiators.get(userId); + var count = requestCountMap.getOrDefault(event.getId(), 0L); + return EventMapper.toEventShortDto(event, initiator, count, stats.size()); + }) + .toList(); + } + + @Override + public EventFullDto getFreeEventById(Long eventId, HttpServletRequest request) { + if (!eventRepository.existsByIdAndState(eventId, EventState.PUBLISHED)) { + throw new NotFoundException( + "Событие с id=%s не существует или не опубликовано.".formatted(eventId)); + } + + statRepository.sendHitRequest(request); + + var event = getEventById(eventId); + var eventData = EventData.builder() + .initiator(UserMapper.toUserShortDto(getUserById(event.getInitiatorId()))) + .confirmedRequests(getConfirmedRequestsCountByEvent(eventId)) + .views(statRepository.getStat(List.of(request.getRequestURI()), true) + .getFirst().getHits()) + .build(); + + return EventMapper.toEventFullDto(event, eventData); + } + + @Override + public EventFullDto userAddNewEvent(Long userId, @NonNull NewEventDto newEventDto) { + if (newEventDto.participantLimit() != null && newEventDto.participantLimit() < 0) { + throw new ValidationException( + "Ограничение на количество участников должно быть положительным числом"); + } + + if (newEventDto.eventDate() != null + && newEventDto.eventDate().isBefore(LocalDateTime.now().plusHours(2))) { + throw new ValidationException( + "Начало события не может быть раньше, чем через два часа от текущего момента"); + } + + userFeignRepository.checkUser(userId); + + var category = getCategoryById(newEventDto.category()); + var event = eventRepository.save( + EventMapper.toEntity( + newEventDto, + category, + LocalDateTime.now(), + userId, + null, + EventState.PENDING + ) + ); + var initiator = UserMapper.toUserShortDto(getUserById(event.getInitiatorId())); + + var eventData = EventData.builder() + .initiator(initiator) + .confirmedRequests(0L) + .views(0L) + .build(); + + return EventMapper.toEventFullDto(event, eventData); + } + + @Override + public List adminGetEvents(@NonNull AdminGetDto dto) { + Specification spec = SpecBuilder.builder() + .andIf(dto.users() != null && !dto.users().isEmpty(), + () -> EventSpecifications.hasUsers(dto.users())) + .andIf(dto.states() != null && !dto.states().isEmpty(), + () -> EventSpecifications.hasStates(dto.states())) + .andIf(dto.categories() != null && !dto.categories().isEmpty(), + () -> EventSpecifications.hasCategories(dto.categories())) + .andIf(dto.rangeStart() != null, + () -> EventSpecifications.dateAfter(dto.rangeStart())) + .andIf(dto.rangeEnd() != null, + () -> EventSpecifications.dateBefore(dto.rangeEnd())) + .build(); + + Pageable pageable = PageRequest.of( + dto.from() / dto.size(), + dto.size() + ); + + List events = eventRepository.findAll(spec, pageable).getContent(); + if (events.isEmpty()) { + return Collections.emptyList(); + } + + List eventRequestCountList = requestFeignRepository.getConfirmedRequestsCount( + events.stream().map(Event::getId).toList()); + + Map requestCountMap = new HashMap<>(); + if (!eventRequestCountList.isEmpty()) { + eventRequestCountList.forEach(eventRequestCount -> + requestCountMap.put(eventRequestCount.getEventId(), eventRequestCount.getCount()) + ); + } + + List uris = events.stream().map(event -> EVENTS_PATH + event.getId()).toList(); + List stats = statRepository + .getStat(uris, dto.rangeStart(), dto.rangeEnd(), false); + + Map initiators = getUserShortDtoMap(events); + + return events.stream() + .map(event -> { + var userId = requestCountMap.get(event.getInitiatorId()); + var eventData = EventData.builder() + .initiator(initiators.get(userId)) + .confirmedRequests(getConfirmedRequestsCountFromMap( + requestCountMap, event.getId())) + .views(getHits(stats, event.getId())) + .build(); + return EventMapper.toEventFullDto(event, eventData); + }) + .toList(); + } + + @NonNull + private Map getUserShortDtoMap(@NonNull List events) { + List userIds = events.stream().map(Event::getInitiatorId).toList(); + List users = userFeignRepository.getUsersByIds(userIds); + return users.stream() + .collect(Collectors.toMap(UserShortDto::id, user -> user)); + } + + @Override + public EventFullDto adminUpdateEvent(Long eventId, @NonNull UpdateEventAdminRequest request) { + Event oldEvent = getEventById(eventId); + Event newEvent; + + if (request.eventDate() != null && request.eventDate() + .isBefore(LocalDateTime.now().plusHours(HOURS_BEFORE_START))) { + throw new ValidationException( + "Дата события не может быть раньше, чем через два часа от текущего момента"); + } + + if (request.stateAction() == null) { + newEvent = EventMapper.update( + oldEvent, + request, + oldEvent.getState(), + oldEvent.getPublishedOn(), + request.category() == null ? + Optional.empty() : + Optional.of(getCategoryById(request.category())) + ); + } else { + + if (request.stateAction().equals(AdminStateAction.PUBLISH_EVENT) && + oldEvent.getState().equals(EventState.PUBLISHED)) { + throw new ConflictException("Событие с id=%s уже опубликовано" + .formatted(oldEvent.getId()) + ); + } + + if (request.stateAction().equals(AdminStateAction.PUBLISH_EVENT) && + oldEvent.getState().equals(EventState.CANCELED)) { + throw new ConflictException("Публикация события с id=%s уже отменена пользователем" + .formatted(oldEvent.getId()) + ); + } + + if (request.stateAction().equals(AdminStateAction.REJECT_EVENT) && + oldEvent.getState().equals(EventState.PUBLISHED)) { + throw new ConflictException("Событие с id=%s уже опубликовано, отмена не возможна" + .formatted(oldEvent.getId()) + ); + } + + newEvent = EventMapper.update( + oldEvent, + request, + request.stateAction().equals(AdminStateAction.REJECT_EVENT) + ? EventState.CANCELED : EventState.PUBLISHED, + request.stateAction().equals(AdminStateAction.REJECT_EVENT) + ? null : LocalDateTime.now(), + request.category() == null + ? Optional.empty() : Optional.of(getCategoryById(request.category())) + ); + } + + var event = eventRepository.save(newEvent); + + return EventMapper.toEventFullDto(event, getEventData(event)); + } + + @Override + public List findByUserId(Long userId, Integer from, Integer size) { + userFeignRepository.checkUser(userId); + + PageRequest pageRequest = PageRequest.of(from / size, size); + List events = eventRepository.findByInitiatorId(userId, pageRequest); + if (events.isEmpty()) { + return Collections.emptyList(); + } + + List eventRequestCountList = requestFeignRepository.getConfirmedRequestsCount( + events.stream().map(Event::getId).toList()); + + Map requestCountMap = new HashMap<>(); + if (!eventRequestCountList.isEmpty()) { + eventRequestCountList.forEach(eventRequestCount -> + requestCountMap.put(eventRequestCount.getEventId(), eventRequestCount.getCount()) + ); + } + + List uris = events.stream().map(event -> EVENTS_PATH + event.getId()).toList(); + List stats = statRepository.getStat(uris, true); + + Map initiators = getUserShortDtoMap(events); + + return events.stream() + .map(event -> { + var initiator = initiators.get(userId); + var count = getConfirmedRequestsCountFromMap(requestCountMap, event.getId()); + return EventMapper.toEventShortDto( + event, initiator, count, getHits(stats, event.getId())); + }) + .toList(); + } + + @Override + public EventFullDto findEventByUserIdAndEventId(Long userId, Long eventId) { + userFeignRepository.checkUser(userId); + + Event event = getEventById(eventId); + + if (!event.getInitiatorId().equals(userId)) { + throw new ConflictException("Пользователь должен быть инициатором"); + } + + return EventMapper.toEventFullDto(event, getEventData(event)); + } + + @Override + public EventFullDto findEventById(long eventId) { + Event event = getEventById(eventId); + + return EventMapper.toEventFullDto(event, getEventData(event)); + } + + @Override + public EventFullDto patchEvent(Long userId, Long eventId, @NonNull UpdateEventUserRequest request) { + if (request.participantLimit() != null && request.participantLimit() < 0) { + throw new ValidationException( + "Ограничение на количество участников должно быть положительным числом"); + } + + userFeignRepository.checkUser(userId); + return patchEvent(eventId, request, false); + } + + private EventData getEventData(@NonNull Event event) { + return EventData.builder() + .initiator(UserMapper.toUserShortDto(getUserById(event.getInitiatorId()))) + .confirmedRequests(getConfirmedRequestsCountByEvent(event.getId())) + .views(getHits(event.getId())) + .build(); + } + + @SuppressWarnings("SameParameterValue") + private EventFullDto patchEvent(Long eventId, + @NonNull UpdateEventUserRequest request, + boolean isAdmin) { + try { + Event event = getEventById(eventId); + + if (!isAdmin && event.getState() == EventState.PUBLISHED) { + throw new ConflictException("Нельзя редактировать опубликованное событие"); + } + + if (request.eventDate() != null) { + LocalDateTime eventDateTime = request.eventDate(); + LocalDateTime minDateTime = LocalDateTime.now().plusHours(HOURS_BEFORE_START); + + if (!isAdmin && eventDateTime.isBefore(minDateTime)) { + throw new ValidationException( + String.format( + "Дата события должна быть не ранее чем за %d часа(ов) до начала", + HOURS_BEFORE_START + ) + ); + } + } + + UserStateAction action = request.stateAction(); + + if (action != null) { + EventState newState = isAdmin + ? StateMapper.mapAdminEventAction(action) + : StateMapper.mapUserEventAction(action); + + if (EventState.PUBLISHED.equals(newState)) { + event.setPublishedOn(LocalDateTime.from(Instant.now())); + } + if (newState != null) { + event.setState(newState); + } + } + + if (request.category() != null) { + Category category = getCategoryById(request.category()); + event.setCategory(category); + } + + EventMapper.merge(event, request); + + Event patched = eventRepository.save(event); + + log.info("Событие обновлено: {}", patched.getId()); + + return EventMapper.toEventFullDto(event, getEventData(event)); + + } catch (DataIntegrityViolationException e) { + log.debug("Конфликт во время обновления события {}", request, e); + throw new ConflictException("Конфликт с другим событием"); + } + } + + @NonNull + private User getUserById(long userId) { + var userDto = userFeignRepository.getUserDtoById(userId); + return UserMapper.toEntity(userDto); + } + + @NonNull + private Event getEventById(long eventId) { + return eventRepository.findById(eventId).orElseThrow( + () -> new NotFoundException("Событие с id=%s не найдено".formatted(eventId)) + ); + } + + @NonNull + private Category getCategoryById(long categoryId) { + return categoryRepository.findById(categoryId).orElseThrow( + () -> new NotFoundException("Категория с id=%s не найдена".formatted(categoryId)) + ); + } + + private long getConfirmedRequestsCountByEvent(Long eventId) { + return requestFeignRepository.getConfirmedRequestsCount(List.of(eventId)).stream() + .filter(requestCount -> requestCount.getEventId().equals(eventId)) + .findAny() + .map(EventRequestCount::getCount) + .orElse(0L); + } + + private long getHits(long eventId) { + List stats = statRepository.getStat(List.of(EVENTS_PATH + eventId), true); + if (stats.isEmpty()) { + return 0; + } + return stats.getFirst().getHits(); + } + + private long getHits(@NonNull List stats, long eventId) { + if (stats.isEmpty()) { + return 0; + } + for (ViewStatsDto stat : stats) { + if (stat.getUri().equals(EVENTS_PATH + eventId)) { + return stat.getHits(); + } + } + return 0; + } + + private long getConfirmedRequestsCountFromMap(@NonNull Map requestCountMap, long eventId) { + if (requestCountMap.isEmpty()) { + return 0; + } + if (requestCountMap.containsKey(eventId)) { + return requestCountMap.get(eventId); + } + return 0; + } + +} diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingService.java new file mode 100644 index 0000000..fe0b349 --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingService.java @@ -0,0 +1,10 @@ +package ru.practicum.ewm.events.service.rating; + +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; + +@Transactional +public interface RatingService { + + void updateRating(RatingUpdateRequest request); +} diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java new file mode 100644 index 0000000..5b34a33 --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java @@ -0,0 +1,27 @@ +package ru.practicum.ewm.events.service.rating; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Service; +import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.ewm.events.entity.Event; +import ru.practicum.ewm.events.repository.EventRepository; + +@Service +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class RatingServiceImpl implements RatingService { + + EventRepository eventRepository; + + @Override + public void updateRating(@NonNull RatingUpdateRequest request) { + Event event = eventRepository.findById(request.eventId()).orElseThrow(() -> + new NotFoundException("Event with id " + request.eventId() + " not found")); + event.setRate(request.rate()); + eventRepository.save(event); + } +} diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java new file mode 100644 index 0000000..2d0c02e --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java @@ -0,0 +1,18 @@ +package ru.practicum.ewm.events.service.request; + +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.request.ParticipationRequestDto; + +import java.util.List; + +@Transactional +public interface RequestService { + + @Transactional(readOnly = true) + List findByEventId(Long userId, Long eventId); + + EventRequestStatusUpdateResult updateStatusRequest(Long userId, Long eventId, + EventRequestStatusUpdateRequest status); +} diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java new file mode 100644 index 0000000..187b9f7 --- /dev/null +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java @@ -0,0 +1,51 @@ +package ru.practicum.ewm.events.service.request; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import org.springframework.stereotype.Service; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.repository.RequestFeignRepository; +import ru.practicum.aggregation.repository.UserFeignRepositoryImpl; +import ru.practicum.ewm.events.repository.EventRepository; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class RequestServiceImpl implements RequestService { + + EventRepository eventRepository; + + RequestFeignRepository requestFeignRepository; + UserFeignRepositoryImpl userFeignRepository; + + @Override + public List findByEventId(Long userId, Long eventId) { + checkEvent(eventId); + userFeignRepository.checkUser(userId); + + return requestFeignRepository.findByEventId(userId, eventId); + } + + @Override + public EventRequestStatusUpdateResult updateStatusRequest(Long userId, + Long eventId, + EventRequestStatusUpdateRequest status + ) { + checkEvent(eventId); + userFeignRepository.checkUser(userId); + return requestFeignRepository.updateStatusRequest(userId, eventId, status); + } + + private void checkEvent(Long eventId) { + if (!eventRepository.existsById(eventId)) { + throw new NotFoundException("Событие с id=%s не найдено".formatted(eventId)); + } + } + +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/util/specification/EventSpecifications.java b/core/event-service/src/main/java/ru/practicum/ewm/events/specification/EventSpecifications.java similarity index 91% rename from core/main-service/src/main/java/ru/practicum/ewm/util/specification/EventSpecifications.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/specification/EventSpecifications.java index fe01617..6716094 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/util/specification/EventSpecifications.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/specification/EventSpecifications.java @@ -1,12 +1,13 @@ -package ru.practicum.ewm.util.specification; +package ru.practicum.ewm.events.specification; import jakarta.persistence.criteria.Join; import lombok.experimental.UtilityClass; import org.springframework.data.jpa.domain.Specification; -import ru.practicum.ewm.model.Category; -import ru.practicum.ewm.model.Event; -import ru.practicum.ewm.model.User; -import ru.practicum.ewm.model.enums.EventState; +import org.springframework.lang.NonNull; +import ru.practicum.aggregation.enums.EventState; +import ru.practicum.ewm.events.entity.Category; +import ru.practicum.ewm.events.entity.Event; +import ru.practicum.ewm.events.model.User; import java.time.LocalDateTime; import java.util.List; @@ -106,6 +107,7 @@ public Specification eventDateAfterNow(LocalDateTime now) { } /// initiator + @NonNull public static Specification hasUsers(List userIds) { return (root, query, cb) -> { if (userIds == null || userIds.isEmpty()) { @@ -118,6 +120,7 @@ public static Specification hasUsers(List userIds) { } /// По статусам + @NonNull public static Specification hasStates(List states) { return (root, query, cb) -> { if (states == null || states.isEmpty()) { @@ -127,4 +130,5 @@ public static Specification hasStates(List states) { return root.get("state").in(states); }; } + } \ No newline at end of file diff --git a/core/main-service/src/main/java/ru/practicum/ewm/util/specification/SpecBuilder.java b/core/event-service/src/main/java/ru/practicum/ewm/events/specification/SpecBuilder.java similarity index 93% rename from core/main-service/src/main/java/ru/practicum/ewm/util/specification/SpecBuilder.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/specification/SpecBuilder.java index 1ec02c1..e7d546f 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/util/specification/SpecBuilder.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/specification/SpecBuilder.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.util.specification; +package ru.practicum.ewm.events.specification; import org.springframework.data.jpa.domain.Specification; @@ -33,4 +33,5 @@ public SpecBuilder and(Specification other) { public Specification build() { return spec; } + } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/util/statistic/StatRepository.java b/core/event-service/src/main/java/ru/practicum/ewm/events/statistic/StatRestRepository.java similarity index 85% rename from core/main-service/src/main/java/ru/practicum/ewm/util/statistic/StatRepository.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/statistic/StatRestRepository.java index 0f9d137..443b0b6 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/util/statistic/StatRepository.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/statistic/StatRestRepository.java @@ -1,11 +1,11 @@ -package ru.practicum.ewm.util.statistic; +package ru.practicum.ewm.events.statistic; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import ru.practicum.ewm.util.error.exception.HitRequestException; -import ru.practicum.ewm.util.error.exception.StatResponseException; +import ru.practicum.aggregation.error.exception.stats.HitRequestException; +import ru.practicum.aggregation.error.exception.stats.StatsResponseException; import ru.practicum.stat.client.StatClient; import ru.practicum.stat.dto.EndpointHitDto; import ru.practicum.stat.dto.StatsRequest; @@ -18,7 +18,7 @@ @Component @RequiredArgsConstructor -public class StatRepository { +public class StatRestRepository { private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private final LocalDateTime startUnixEpoch = LocalDateTime.parse("1970-01-01T00:00:00"); @@ -47,7 +47,7 @@ public List getStat(List statUris, LocalDateTime rangeEnd, boolean uniqe) { if (statUris == null || statUris.isEmpty()) { - throw new StatResponseException(); + throw new StatsResponseException(); } if (rangeStart == null) { @@ -69,11 +69,11 @@ public List getStat(List statUris, try { stats = statClient.getStat(request); } catch (Exception ex) { - throw new StatResponseException(ex); + throw new StatsResponseException(ex); } if (stats == null) { - throw new StatResponseException(); + throw new StatsResponseException(); } return stats; @@ -82,4 +82,5 @@ public List getStat(List statUris, public List getStat(List statUris, boolean uniqe) { return getStat(statUris, null, null, uniqe); } + } diff --git a/core/main-service/src/main/resources/application-test.yaml b/core/event-service/src/main/resources/application-test.yaml.bak similarity index 100% rename from core/main-service/src/main/resources/application-test.yaml rename to core/event-service/src/main/resources/application-test.yaml.bak diff --git a/core/main-service/src/main/resources/application.yml b/core/event-service/src/main/resources/application.yml similarity index 97% rename from core/main-service/src/main/resources/application.yml rename to core/event-service/src/main/resources/application.yml index 8c9a66f..18d7df8 100644 --- a/core/main-service/src/main/resources/application.yml +++ b/core/event-service/src/main/resources/application.yml @@ -3,7 +3,7 @@ server: spring: application: - name: main-service + name: event-service config: import: "optional:configserver:" cloud: diff --git a/core/main-service/src/main/resources/schema-h2.sql b/core/event-service/src/main/resources/schema-h2.sql.bak similarity index 100% rename from core/main-service/src/main/resources/schema-h2.sql rename to core/event-service/src/main/resources/schema-h2.sql.bak diff --git a/core/event-service/src/main/resources/schema-postgres.sql b/core/event-service/src/main/resources/schema-postgres.sql new file mode 100644 index 0000000..1345dd0 --- /dev/null +++ b/core/event-service/src/main/resources/schema-postgres.sql @@ -0,0 +1,47 @@ +CREATE TABLE IF NOT EXISTS categories ( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + name varchar(50) NOT NULL, + CONSTRAINT pk_categories PRIMARY KEY (id) +); +CREATE UNIQUE INDEX IF NOT EXISTS idx_categories_name ON categories (name); + +CREATE TABLE IF NOT EXISTS events ( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + annotation varchar(2000) NOT NULL, + created_on timestamp(3) without time zone NOT NULL, + description varchar(7000) NOT NULL, + event_date timestamp(3) without time zone NOT NULL, + location jsonb, + paid boolean NOT NULL, + participant_limit integer NOT NULL, + published_on timestamp(3) without time zone, + request_moderation boolean NOT NULL, + state varchar(255) check (state in ('PENDING','PUBLISHED','CANCELED')) NOT NULL, + title varchar(120) NOT NULL, + category_id bigint, + initiator_id bigint, + rate bigint NOT NULL DEFAULT 0, + CONSTRAINT pk_events PRIMARY KEY (id), + CONSTRAINT fk_events_categories FOREIGN KEY (category_id) + REFERENCES categories (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION +); + +CREATE TABLE IF NOT EXISTS compilations ( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + pinned boolean NOT NULL, + title varchar(50) NOT NULL, + CONSTRAINT pk_compilations PRIMARY KEY (id) +); + +CREATE TABLE IF NOT EXISTS compilation_events ( + compilations_id bigint NOT NULL, + events_id bigint NOT NULL, + CONSTRAINT fk_events_compilations FOREIGN KEY (compilations_id) + REFERENCES compilations (id) MATCH SIMPLE + ON DELETE CASCADE, + CONSTRAINT fk_compilations_events FOREIGN KEY (events_id) + REFERENCES events (id) MATCH SIMPLE + ON DELETE CASCADE +); \ No newline at end of file diff --git a/core/main-service/src/main/resources/schema-postgres.sql b/core/event-service/src/main/resources/schema-postgres.sql.bak similarity index 100% rename from core/main-service/src/main/resources/schema-postgres.sql rename to core/event-service/src/main/resources/schema-postgres.sql.bak diff --git a/core/main-service/src/main/java/ru/practicum/ewm/MainServiceApp.java b/core/main-service/src/main/java/ru/practicum/ewm/MainServiceApp.java deleted file mode 100644 index eab9033..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/MainServiceApp.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.practicum.ewm; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; - -@SpringBootApplication -@ComponentScan(value = {"ru.practicum.ewm", "ru.practicum.stat.client"}) -public class MainServiceApp { - public static void main(String[] args) { - SpringApplication.run(MainServiceApp.class, args); - } -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/EventFullDto.java b/core/main-service/src/main/java/ru/practicum/ewm/dto/event/EventFullDto.java deleted file mode 100644 index 7fc1da1..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/event/EventFullDto.java +++ /dev/null @@ -1,56 +0,0 @@ -package ru.practicum.ewm.dto.event; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.Builder; -import ru.practicum.ewm.dto.category.CategoryDto; -import ru.practicum.ewm.dto.user.UserShortDto; -import ru.practicum.ewm.model.Location; -import ru.practicum.ewm.model.enums.EventState; - -import java.time.LocalDateTime; - -@Builder -public record EventFullDto( - - Long id, - - @NotBlank - String annotation, - - @NotNull - CategoryDto category, - - Long confirmedRequests, - - LocalDateTime createdOn, - - String description, - - @NotNull - LocalDateTime eventDate, - - @NotNull - UserShortDto initiator, - - @NotNull - Location location, - - boolean paid, - - Integer participantLimit, - - LocalDateTime publishedOn, - - boolean requestModeration, - - EventState state, - - @NotBlank - String title, - - Long views, - - long rate -) { -} \ No newline at end of file diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/rating/RatingRequest.java b/core/main-service/src/main/java/ru/practicum/ewm/dto/rating/RatingRequest.java deleted file mode 100644 index 1a46e1a..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/rating/RatingRequest.java +++ /dev/null @@ -1,18 +0,0 @@ -package ru.practicum.ewm.dto.rating; - -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.ewm.model.enums.Reaction; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class RatingRequest { - - @NotNull - private Reaction reaction; -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/rating/RatingResponse.java b/core/main-service/src/main/java/ru/practicum/ewm/dto/rating/RatingResponse.java deleted file mode 100644 index 3118cfd..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/rating/RatingResponse.java +++ /dev/null @@ -1,18 +0,0 @@ -package ru.practicum.ewm.dto.rating; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.ewm.model.enums.Reaction; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class RatingResponse { - private Long id; - private Long userId; - private Long eventId; - private Reaction reaction; -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/request/EventRequestStatusUpdateRequest.java b/core/main-service/src/main/java/ru/practicum/ewm/dto/request/EventRequestStatusUpdateRequest.java deleted file mode 100644 index f2809f8..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/request/EventRequestStatusUpdateRequest.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.practicum.ewm.dto.request; - -import jakarta.validation.constraints.NotNull; -import lombok.Builder; -import ru.practicum.ewm.model.enums.RequestUpdateStatus; - -import java.util.List; - -@Builder -public record EventRequestStatusUpdateRequest( - - List requestIds, - - @NotNull - RequestUpdateStatus status -) { -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/request/EventRequestStatusUpdateResult.java b/core/main-service/src/main/java/ru/practicum/ewm/dto/request/EventRequestStatusUpdateResult.java deleted file mode 100644 index 42af240..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/request/EventRequestStatusUpdateResult.java +++ /dev/null @@ -1,12 +0,0 @@ -package ru.practicum.ewm.dto.request; - -import lombok.Builder; - -import java.util.List; - -@Builder -public record EventRequestStatusUpdateResult( - List confirmedRequests, - List rejectedRequests -) { -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/request/ParticipationRequestDto.java b/core/main-service/src/main/java/ru/practicum/ewm/dto/request/ParticipationRequestDto.java deleted file mode 100644 index 336e79d..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/request/ParticipationRequestDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.practicum.ewm.dto.request; - -import lombok.Builder; -import ru.practicum.ewm.model.enums.ParticipationStatus; - -@Builder -public record ParticipationRequestDto( - Long id, - String created, - Long event, - Long requester, - ParticipationStatus status -) { -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dto/user/UserDto.java b/core/main-service/src/main/java/ru/practicum/ewm/dto/user/UserDto.java deleted file mode 100644 index 80725ca..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/dto/user/UserDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.practicum.ewm.dto.user; - -import lombok.Builder; - -@Builder -public record UserDto( - Long id, - String name, - String email -) { -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/mapper/RequestMapper.java b/core/main-service/src/main/java/ru/practicum/ewm/mapper/RequestMapper.java deleted file mode 100644 index dc76c23..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/mapper/RequestMapper.java +++ /dev/null @@ -1,31 +0,0 @@ -package ru.practicum.ewm.mapper; - -import org.springframework.stereotype.Component; -import ru.practicum.ewm.dto.request.ParticipationRequestDto; -import ru.practicum.ewm.model.ParticipationRequest; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - - -@Component -public class RequestMapper { - - public static ParticipationRequestDto toParticipationRequestDto(ParticipationRequest participationRequest) { - if (participationRequest == null) return null; - return ParticipationRequestDto.builder() - .event(participationRequest.getEvent().getId()) - .requester(participationRequest.getRequester().getId()) - .status(participationRequest.getStatus()) - .created(formatDateTime(participationRequest.getCreated())) - .id(participationRequest.getId()) - .build(); - } - - private static String formatDateTime(LocalDateTime dateTime) { - if (dateTime == null) return null; - - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"); - return dateTime.format(formatter); - } -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/AdminStateAction.java b/core/main-service/src/main/java/ru/practicum/ewm/model/enums/AdminStateAction.java deleted file mode 100644 index 88f2fa6..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/AdminStateAction.java +++ /dev/null @@ -1,5 +0,0 @@ -package ru.practicum.ewm.model.enums; - -public enum AdminStateAction { - PUBLISH_EVENT, REJECT_EVENT -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/ParticipationStatus.java b/core/main-service/src/main/java/ru/practicum/ewm/model/enums/ParticipationStatus.java deleted file mode 100644 index b16afe2..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/ParticipationStatus.java +++ /dev/null @@ -1,5 +0,0 @@ -package ru.practicum.ewm.model.enums; - -public enum ParticipationStatus { - PENDING, CONFIRMED, REJECTED, CANCELED -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/RequestUpdateStatus.java b/core/main-service/src/main/java/ru/practicum/ewm/model/enums/RequestUpdateStatus.java deleted file mode 100644 index 37d71e0..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/RequestUpdateStatus.java +++ /dev/null @@ -1,5 +0,0 @@ -package ru.practicum.ewm.model.enums; - -public enum RequestUpdateStatus { - CONFIRMED, REJECTED -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/UserStateAction.java b/core/main-service/src/main/java/ru/practicum/ewm/model/enums/UserStateAction.java deleted file mode 100644 index ce9624b..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/enums/UserStateAction.java +++ /dev/null @@ -1,5 +0,0 @@ -package ru.practicum.ewm.model.enums; - -public enum UserStateAction { - SEND_TO_REVIEW, CANCEL_REVIEW, PUBLISH_EVENT, REJECT_EVENT -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/compilation/CompilationServiceImpl.java b/core/main-service/src/main/java/ru/practicum/ewm/service/compilation/CompilationServiceImpl.java deleted file mode 100644 index 90f4cff..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/compilation/CompilationServiceImpl.java +++ /dev/null @@ -1,206 +0,0 @@ -package ru.practicum.ewm.service.compilation; - -import jakarta.servlet.http.HttpServletRequest; -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import ru.practicum.ewm.dao.CompilationRepository; -import ru.practicum.ewm.dao.EventRepository; -import ru.practicum.ewm.dao.RequestRepository; -import ru.practicum.ewm.dto.compilation.CompilationDto; -import ru.practicum.ewm.dto.compilation.CompilationSearchFilter; -import ru.practicum.ewm.dto.compilation.CompilationUpdateDto; -import ru.practicum.ewm.dto.compilation.NewCompilationDto; -import ru.practicum.ewm.mapper.CompilationMapper; -import ru.practicum.ewm.model.Compilation; -import ru.practicum.ewm.model.Event; -import ru.practicum.ewm.model.enums.ParticipationStatus; -import ru.practicum.ewm.service.request.EventRequestCount; -import ru.practicum.ewm.util.error.exception.NotFoundException; -import ru.practicum.ewm.util.statistic.StatRepository; -import ru.practicum.stat.dto.ViewStatsDto; - -import java.util.*; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class CompilationServiceImpl implements CompilationService { - - CompilationRepository compilationRepository; - EventRepository eventRepository; - StatRepository statRepository; - RequestRepository requestRepository; - - @Override - public CompilationDto getById(Long compilationId, HttpServletRequest request) { - statRepository.sendHitRequest(request); - - Compilation compilation = getCompilationById(compilationId); - - return CompilationMapper.toCompilationDto( - compilation, - getConfirmedRequests(List.of(compilation)), - getViews(List.of(compilation)) - ); - } - - @Override - @Transactional - public void delById(Long compilationId) { - getCompilationById(compilationId); - compilationRepository.deleteById(compilationId); - } - - @Override - @Transactional - public CompilationDto addCompilation(@NonNull NewCompilationDto compilationDto) { - Set events = new HashSet<>(); - - if (compilationDto.getEvents() != null && !compilationDto.getEvents().isEmpty()) { - events = new HashSet<>(eventRepository.findAllById(compilationDto.getEvents())); - - if (events.size() < compilationDto.getEvents().size()) { - throw new NotFoundException("Одно или несколько событий не найдены"); - } - } - - Compilation compilation = CompilationMapper.toEntity(compilationDto, events); - Compilation savedCompilation = compilationRepository.save(compilation); - - Map confirmedRequests = new HashMap<>(); - savedCompilation.getEvents().forEach(event -> confirmedRequests.put(event.getId(), 0L)); - - Map views = new HashMap<>(); - savedCompilation.getEvents().forEach(event -> views.put(event.getId(), 0L)); - - return CompilationMapper.toCompilationDto( - savedCompilation, - confirmedRequests, - views - ); - } - - @Override - @Transactional - public CompilationDto updateCompilation(Long compilationId, @NonNull CompilationUpdateDto compilationUpdateDto) { - Compilation compilationInDb = getCompilationById(compilationId); - - if (compilationUpdateDto.getEvents() != null) { - List eventsUpdate = new ArrayList<>(); - - if (!compilationUpdateDto.getEvents().isEmpty()) { - eventsUpdate = eventRepository.findAllById(compilationUpdateDto.getEvents()); - if (eventsUpdate.size() < compilationUpdateDto.getEvents().size()) { - throw new NotFoundException("Одно или несколько событий не найдены"); - } - } - compilationInDb.setEvents(new HashSet<>(eventsUpdate)); - - } - - if (compilationUpdateDto.getTitle() != null && !compilationUpdateDto.getTitle().isBlank()) { - compilationInDb.setTitle(compilationUpdateDto.getTitle()); - } - - if (compilationUpdateDto.getPinned() != null) { - compilationInDb.setPinned(compilationUpdateDto.getPinned()); - } - - return CompilationMapper.toCompilationDto( - compilationInDb, getConfirmedRequests(List.of(compilationInDb)), getViews(List.of(compilationInDb))); - } - - @Override - public List getByFilter(@NonNull CompilationSearchFilter filter, HttpServletRequest request) { - Pageable pageable = PageRequest.of(filter.getFrom() / filter.getSize(), filter.getSize()); - Page compilationsPage; - - if (filter.getPinned() != null) { - compilationsPage = compilationRepository.findAllByPinned(filter.getPinned(), pageable); - } else { - compilationsPage = compilationRepository.findAll(pageable); - } - - List compilations = compilationsPage.getContent(); - - if (compilations.isEmpty()) { - return Collections.emptyList(); - } - - Map allConfirmedRequests = getConfirmedRequests(compilations); - Map allViews = getViews(compilations); - - // Теперь маппим, просто подставляя готовые Map - return compilations.stream() - .map(compilation -> CompilationMapper.toCompilationDto( - compilation, - allConfirmedRequests, - allViews - )) - .toList(); - } - - @NonNull - private Compilation getCompilationById(long compilationId) { - return compilationRepository.findById(compilationId).orElseThrow( - () -> new NotFoundException("Подборка с id=" + compilationId + " не найдена") - ); - } - - /// Map - @NonNull - private Map getConfirmedRequests(Collection compilations) { - List events = compilations.stream() - .flatMap(c -> c.getEvents().stream()) - .toList(); - - if (events.isEmpty()) return Collections.emptyMap(); - - List eventRequestCountList = requestRepository.countConfirmedRequestsByEventIds( - events.stream().map(Event::getId).toList(), - ParticipationStatus.CONFIRMED); - - return eventRequestCountList.stream() - .collect(Collectors.toMap( - EventRequestCount::getEventId, - EventRequestCount::getCount, - (existing, replacement) -> existing - )); - } - - /// Map - @NonNull - private Map getViews(Collection compilations) { - // Все уникальные события - List allEvents = compilations.stream() - .flatMap(c -> c.getEvents().stream()) - .distinct() - .toList(); - - if (allEvents.isEmpty()) return Collections.emptyMap(); - - // Все URIs - List uris = allEvents.stream() - .map(e -> "/events/" + e.getId()) - .toList(); - - List stats = statRepository.getStat(uris, false); - - // Map - return stats.stream() - .collect(Collectors.toMap( - s -> Long.parseLong(s.getUri().replace("/events/", "")), - ViewStatsDto::getHits, - (a, b) -> a - )); - } -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/event/EventServiceImpl.java b/core/main-service/src/main/java/ru/practicum/ewm/service/event/EventServiceImpl.java deleted file mode 100644 index 038df7a..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/event/EventServiceImpl.java +++ /dev/null @@ -1,465 +0,0 @@ -package ru.practicum.ewm.service.event; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.validation.ValidationException; -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; -import lombok.extern.slf4j.Slf4j; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Service; -import ru.practicum.ewm.dao.CategoryRepository; -import ru.practicum.ewm.dao.EventRepository; -import ru.practicum.ewm.dao.RequestRepository; -import ru.practicum.ewm.dao.UserRepository; -import ru.practicum.ewm.dto.event.*; -import ru.practicum.ewm.mapper.EventMapper; -import ru.practicum.ewm.mapper.StateMapper; -import ru.practicum.ewm.model.Category; -import ru.practicum.ewm.model.Event; -import ru.practicum.ewm.model.User; -import ru.practicum.ewm.model.enums.AdminStateAction; -import ru.practicum.ewm.model.enums.EventState; -import ru.practicum.ewm.model.enums.ParticipationStatus; -import ru.practicum.ewm.model.enums.UserStateAction; -import ru.practicum.ewm.service.request.EventRequestCount; -import ru.practicum.ewm.util.error.exception.ConflictException; -import ru.practicum.ewm.util.error.exception.NotFoundException; -import ru.practicum.ewm.util.specification.EventSpecifications; -import ru.practicum.ewm.util.specification.SpecBuilder; -import ru.practicum.ewm.util.statistic.StatRepository; -import ru.practicum.stat.dto.ViewStatsDto; - -import java.time.Instant; -import java.time.LocalDateTime; -import java.util.*; - -@Slf4j -@Service -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class EventServiceImpl implements EventService { - - StatRepository statRepository; - EventRepository eventRepository; - UserRepository userRepository; - CategoryRepository categoryRepository; - RequestRepository requestRepository; - - @Override - public List getFreeEvents(@NonNull FreeGetDto dto, HttpServletRequest request) { - - if (dto.rangeStart() != null && dto.rangeEnd() != null) { - if (dto.rangeEnd().isBefore(dto.rangeStart())) { - throw new ValidationException("Окончание события не может быть раньше начала"); - } - } - - statRepository.sendHitRequest(request); - - SpecBuilder builder = SpecBuilder.builder() - .and(EventSpecifications.isPublished()) - .andIf(dto.text() != null && !dto.text().isBlank(), - () -> EventSpecifications.textContains(dto.text())) - .andIf(dto.categories() != null && !dto.categories().isEmpty(), - () -> EventSpecifications.hasCategories(dto.categories())) - .andIf(dto.paid() != null, - () -> EventSpecifications.isPaid(dto.paid())) - .andIf(Boolean.TRUE.equals(dto.onlyAvailable()), - () -> EventSpecifications.onlyAvailable(true)); - - boolean hasStart = dto.rangeStart() != null; - boolean hasEnd = dto.rangeEnd() != null; - - if (!hasStart && !hasEnd) { - builder.and(EventSpecifications.eventDateAfterNow(LocalDateTime.now())); - } else { - builder - .andIf(hasStart, - () -> EventSpecifications.dateAfter(dto.rangeStart())) - .andIf(hasEnd, - () -> EventSpecifications.dateBefore(dto.rangeEnd())); - } - - Specification spec = builder.build(); - - Sort sort = Sort.unsorted(); - - if (dto.sort() != null) { - switch (dto.sort()) { - case EVENT_DATE -> sort = Sort.by("eventDate").ascending(); - case VIEWS -> sort = Sort.by("views").descending(); - } - } - - Pageable pageable = PageRequest.of( - dto.from() / dto.size(), - dto.size(), - sort - ); - - List events = eventRepository.findAll(spec, pageable).getContent(); - if (events.isEmpty()) { - return Collections.emptyList(); - } - - List eventRequestCountList = requestRepository.countConfirmedRequestsByEventIds( - events.stream().map(Event::getId).toList(), ParticipationStatus.CONFIRMED); - - Map requestCountMap = new HashMap<>(); - if (!eventRequestCountList.isEmpty()) { - eventRequestCountList.forEach(eventRequestCount -> { - requestCountMap.put(eventRequestCount.getEventId(), eventRequestCount.getCount()); - }); - } - - List uris = events.stream().map(event -> "/events/" + event.getId()).toList(); - List stats = statRepository.getStat( - uris, - dto.rangeStart(), - dto.rangeEnd(), - false); - - return events.stream() - .map(event -> EventMapper.toEventShortDto( - event, - requestCountMap.get(event.getId()) == null ? 0L : requestCountMap.get(event.getId()), - stats.size() - ) - ).toList(); - } - - @Override - public EventFullDto getFreeEventById(Long eventId, HttpServletRequest request) { - if (!eventRepository.existsByIdAndState(eventId, EventState.PUBLISHED)) { - throw new NotFoundException("Событие с id=" + eventId + " не существует или не опубликовано."); - } - statRepository.sendHitRequest(request); - Event event = getEventById(eventId); - - long views = statRepository.getStat(List.of(request.getRequestURI()), true).getFirst().getHits(); - - long confirmedRequests = getConfirmedRequests(eventId); - - return EventMapper.toEventFullDto(event, confirmedRequests, views); - } - - @Override - public EventFullDto userAddNewEvent(Long userId, @NonNull NewEventDto newEventDto) { - if (newEventDto.participantLimit() != null && newEventDto.participantLimit() < 0) { - throw new ValidationException("Ограничение на количество участников должно быть положительным числом"); - } - - if (newEventDto.eventDate() != null && newEventDto.eventDate().isBefore(LocalDateTime.now().plusHours(2))) { - throw new ValidationException("Начало события не может быть раньше, " + - "чем через два часа от текущего момента"); - } - - User initiator = getUserById(userId); - Category category = getCategoryById(newEventDto.category()); - - Event event = EventMapper.toEntity( - newEventDto, - category, - LocalDateTime.now(), - initiator, - null, - EventState.PENDING - ); - - return EventMapper.toEventFullDto(eventRepository.save(event), 0L, 0L); - } - - @Override - public List adminGetEvents(@NonNull AdminGetDto dto) { - Specification spec = SpecBuilder.builder() - .andIf(dto.users() != null && !dto.users().isEmpty(), - () -> EventSpecifications.hasUsers(dto.users())) - .andIf(dto.states() != null && !dto.states().isEmpty(), - () -> EventSpecifications.hasStates(dto.states())) - .andIf(dto.categories() != null && !dto.categories().isEmpty(), - () -> EventSpecifications.hasCategories(dto.categories())) - .andIf(dto.rangeStart() != null, - () -> EventSpecifications.dateAfter(dto.rangeStart())) - .andIf(dto.rangeEnd() != null, - () -> EventSpecifications.dateBefore(dto.rangeEnd())) - .build(); - - Pageable pageable = PageRequest.of( - dto.from() / dto.size(), - dto.size() - ); - - List events = eventRepository.findAll(spec, pageable).getContent(); - if (events.isEmpty()) { - return Collections.emptyList(); - } - - List eventRequestCountList = requestRepository.countConfirmedRequestsByEventIds( - events.stream().map(Event::getId).toList(), ParticipationStatus.CONFIRMED); - - Map requestCountMap = new HashMap<>(); - if (!eventRequestCountList.isEmpty()) { - eventRequestCountList.forEach(eventRequestCount -> - requestCountMap.put(eventRequestCount.getEventId(), eventRequestCount.getCount()) - ); - } - - List uris = events.stream().map(event -> "/events/" + event.getId()).toList(); - List stats = statRepository.getStat( - uris, - dto.rangeStart(), - dto.rangeEnd(), - false); - - return events.stream() - .map(event -> EventMapper.toEventFullDto( - event, - getConfirmedRequests(requestCountMap, event.getId()), - getHits(stats, event.getId()) - ) - ).toList(); - } - - @Override - public EventFullDto adminUpdateEvent(Long eventId, @NonNull UpdateEventAdminRequest request) { - Event oldEvent = getEventById(eventId); - Event newEvent; - - if (request.eventDate() != null && request.eventDate().isBefore(LocalDateTime.now().plusHours(2))) { - throw new ValidationException("Дата события должна быть не может быть раньше, " + - "чем через два часа от текущего момента"); - } - - if (request.stateAction() == null) { - newEvent = EventMapper.update( - oldEvent, - request, - oldEvent.getState(), - oldEvent.getPublishedOn(), - request.category() == null ? - Optional.empty() : - Optional.of(getCategoryById(request.category())) - ); - } else { - - if (request.stateAction().equals(AdminStateAction.PUBLISH_EVENT) && - oldEvent.getState().equals(EventState.PUBLISHED)) { - throw new ConflictException("Событие с id=" + oldEvent.getId() + " уже опубликовано"); - } - - if (request.stateAction().equals(AdminStateAction.PUBLISH_EVENT) && - oldEvent.getState().equals(EventState.CANCELED)) { - throw new ConflictException("Публикация события с id=" + oldEvent.getId() + - " уже отменена пользователем"); - } - - if (request.stateAction().equals(AdminStateAction.REJECT_EVENT) && - oldEvent.getState().equals(EventState.PUBLISHED)) { - throw new ConflictException("Событие с id=" + oldEvent.getId() + - " уже опубликовано, отмена не возможна"); - } - - newEvent = EventMapper.update( - oldEvent, - request, - request.stateAction().equals(AdminStateAction.REJECT_EVENT) ? - EventState.CANCELED : EventState.PUBLISHED, - request.stateAction().equals(AdminStateAction.REJECT_EVENT) ? - null : LocalDateTime.now(), - request.category() == null ? - Optional.empty() : - Optional.of(getCategoryById(request.category())) - ); - } - - return EventMapper.toEventFullDto( - eventRepository.save(newEvent), - getConfirmedRequests(oldEvent.getId()), - getHits(oldEvent.getId()) - ); - } - - @Override - public List findByUserId(Long userId, Integer from, Integer size) { - checkUser(userId); - - PageRequest pageRequest = PageRequest.of(from / size, size); - Collection events = eventRepository.findByInitiatorId(userId, pageRequest); - if (events.isEmpty()) { - return Collections.emptyList(); - } - - List eventRequestCountList = requestRepository.countConfirmedRequestsByEventIds( - events.stream().map(Event::getId).toList(), ParticipationStatus.CONFIRMED); - - Map requestCountMap = new HashMap<>(); - if (!eventRequestCountList.isEmpty()) { - eventRequestCountList.forEach(eventRequestCount -> - requestCountMap.put(eventRequestCount.getEventId(), eventRequestCount.getCount()) - ); - } - - List uris = events.stream().map(event -> "/events/" + event.getId()).toList(); - List stats = statRepository.getStat(uris, true); - - return events.stream() - .map(event -> EventMapper.toEventShortDto( - event, - getConfirmedRequests(requestCountMap, event.getId()), - getHits(stats, event.getId()) - ) - ).toList(); - } - - @Override - public EventFullDto findEventById(Long userId, Long eventId) { - checkUser(userId); - - Event event = getEventById(eventId); - - if (!event.getInitiator().getId().equals(userId)) { - throw new ConflictException("Пользователь должен быть инициатором"); - } - - return EventMapper.toEventFullDto( - event, - getConfirmedRequests(event.getId()), - getHits(event.getId()) - ); - } - - @Override - public EventFullDto patchEvent(Long userId, Long eventId, @NonNull UpdateEventUserRequest request) { - if (request.participantLimit() != null && request.participantLimit() < 0) { - throw new ValidationException("Ограничение на количество участников должно быть положительным числом"); - } - - checkUser(userId); - return patchEvent(eventId, request, 2, false); - } - - private EventFullDto patchEvent(Long eventId, @NonNull UpdateEventUserRequest request, long hoursBeforeStart, - boolean isAdmin) { - try { - Event event = getEventById(eventId); - - if (!isAdmin && event.getState() == EventState.PUBLISHED) { - throw new ConflictException("Нельзя редактировать опубликованное событие"); - } - - if (request.eventDate() != null) { - LocalDateTime eventDateTime = request.eventDate(); - LocalDateTime minDateTime = LocalDateTime.now().plusHours(hoursBeforeStart); - - if (!isAdmin && eventDateTime.isBefore(minDateTime)) { - throw new ValidationException( - String.format("Дата события должна быть не ранее чем за %d часа(ов) до начала", - hoursBeforeStart) - ); - } - } - - UserStateAction action = request.stateAction(); - - if (action != null) { - EventState newState = isAdmin - ? StateMapper.mapAdminEventAction(action) - : StateMapper.mapUserEventAction(action); - - if (EventState.PUBLISHED.equals(newState)) { - event.setPublishedOn(LocalDateTime.from(Instant.now())); - } - if (newState != null) { - event.setState(newState); - } - } - - if (request.category() != null) { - Category category = getCategoryById(request.category()); - event.setCategory(category); - } - - EventMapper.merge(event, request); - - Event patched = eventRepository.save(event); - - log.info("Ивент обновлен: {}", patched.getId()); - - return EventMapper.toEventFullDto( - event, - getConfirmedRequests(event.getId()), - getHits(event.getId()) - ); - - } catch (DataIntegrityViolationException e) { - log.debug("Конфликт вовремя обновления ивента {}", request, e); - throw new ConflictException("Конфликт с другим ивентом"); - } - } - - private void checkUser(Long userId) { - if (!userRepository.existsById(userId)) { - throw new NotFoundException("Пользователь с id=" + userId + " не найден"); - } - } - - @NonNull - private User getUserById(long userId) { - return userRepository.findById(userId) - .orElseThrow(() -> new NotFoundException("Пользователь с id=" + userId + " не найден")); - } - - @NonNull - private Event getEventById(long eventId) { - return eventRepository.findById(eventId).orElseThrow( - () -> new NotFoundException("Событие с id=" + eventId + " не найдено") - ); - } - - @NonNull - private Category getCategoryById(long categoryId) { - return categoryRepository.findById(categoryId).orElseThrow( - () -> new NotFoundException("Категория с id=" + categoryId + " не найдена") - ); - } - - private long getConfirmedRequests(Long eventId) { - return requestRepository.countByEventIdAndStatus(eventId, ParticipationStatus.CONFIRMED); - } - - private long getHits(long eventId) { - List stats = statRepository.getStat(List.of("/events/" + eventId), true); - if (stats.isEmpty()) { - return 0; - } - return stats.getFirst().getHits(); - } - - private long getHits(@NonNull List stats, long eventId) { - if (stats.isEmpty()) { - return 0; - } - for (ViewStatsDto stat : stats) { - if (stat.getUri().equals("/events/" + eventId)) { - return stat.getHits(); - } - } - return 0; - } - - private long getConfirmedRequests(@NonNull Map requestCountMap, long eventId) { - if (requestCountMap.isEmpty()) { - return 0; - } - if (requestCountMap.containsKey(eventId)) { - return requestCountMap.get(eventId); - } - return 0; - } -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/rating/RatingService.java b/core/main-service/src/main/java/ru/practicum/ewm/service/rating/RatingService.java deleted file mode 100644 index 27f61d9..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/rating/RatingService.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.practicum.ewm.service.rating; - -import ru.practicum.ewm.dto.rating.RatingRequest; -import ru.practicum.ewm.dto.rating.RatingResponse; - -public interface RatingService { - - RatingResponse addOrUpdateReaction(Long userId, Long eventId, RatingRequest request); - - void removeReaction(Long userId, Long eventId); -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/rating/RatingServiceImpl.java b/core/main-service/src/main/java/ru/practicum/ewm/service/rating/RatingServiceImpl.java deleted file mode 100644 index df506dc..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/rating/RatingServiceImpl.java +++ /dev/null @@ -1,94 +0,0 @@ -package ru.practicum.ewm.service.rating; - -import jakarta.validation.ValidationException; -import lombok.RequiredArgsConstructor; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import ru.practicum.ewm.dao.EventRepository; -import ru.practicum.ewm.dao.RatingRepository; -import ru.practicum.ewm.dao.UserRepository; -import ru.practicum.ewm.dto.rating.RatingRequest; -import ru.practicum.ewm.dto.rating.RatingResponse; -import ru.practicum.ewm.model.Event; -import ru.practicum.ewm.model.Rating; -import ru.practicum.ewm.model.User; -import ru.practicum.ewm.model.enums.EventState; -import ru.practicum.ewm.model.enums.Reaction; -import ru.practicum.ewm.util.error.exception.ConflictException; -import ru.practicum.ewm.util.error.exception.NotFoundException; - -import java.util.Objects; - -@Service -@RequiredArgsConstructor -@Transactional -public class RatingServiceImpl implements RatingService { - - private final RatingRepository ratingRepository; - private final UserRepository userRepository; - private final EventRepository eventRepository; - - @Override - public RatingResponse addOrUpdateReaction(Long userId, Long eventId, RatingRequest request) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new NotFoundException("Пользователь с id=" + userId + " не найден")); - Event event = eventRepository.findByIdAndState(eventId, EventState.PUBLISHED) - .orElseThrow(() -> - new NotFoundException("Событие с id=" + eventId + " не существует или не опубликовано.") - ); - User initiator = event.getInitiator(); - if (Objects.equals(user.getId(), initiator.getId())) { - throw new ValidationException("Нельзя ставить реакции своим событиям"); - } - - Rating rating = ratingRepository.findByUserIdAndEventId(userId, eventId).orElse(null); - - if (rating != null) { - if (rating.getReaction() == request.getReaction()) { - ratingRepository.delete(rating); - updateEventRate(event); - throw new ConflictException("Reaction removed"); - } else { - rating.setReaction(request.getReaction()); - ratingRepository.save(rating); - updateEventRate(event); - return mapToResponse(rating); - } - } else { - rating = Rating.builder() - .user(user) - .event(event) - .reaction(request.getReaction()) - .build(); - ratingRepository.save(rating); - updateEventRate(event); - return mapToResponse(rating); - } - } - - @Override - public void removeReaction(Long userId, Long eventId) { - Rating rating = ratingRepository.findByUserIdAndEventId(userId, eventId) - .orElseThrow(() -> new NotFoundException("Реакция не найдена")); - ratingRepository.delete(rating); - Event event = rating.getEvent(); - updateEventRate(event); - } - - private void updateEventRate(@NonNull Event event) { - long likes = ratingRepository.countByEventIdAndReaction(event.getId(), Reaction.LIKE); - long dislikes = ratingRepository.countByEventIdAndReaction(event.getId(), Reaction.DISLIKE); - event.setRate(likes - dislikes); - eventRepository.save(event); - } - - private RatingResponse mapToResponse(@NonNull Rating rating) { - return RatingResponse.builder() - .id(rating.getId()) - .userId(rating.getUser().getId()) - .eventId(rating.getEvent().getId()) - .reaction(rating.getReaction()) - .build(); - } -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/request/EventRequestCount.java b/core/main-service/src/main/java/ru/practicum/ewm/service/request/EventRequestCount.java deleted file mode 100644 index a7d0684..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/request/EventRequestCount.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.practicum.ewm.service.request; - - -public interface EventRequestCount { - - Long getEventId(); - - Long getCount(); -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/user/UserService.java b/core/main-service/src/main/java/ru/practicum/ewm/service/user/UserService.java deleted file mode 100644 index d10ee41..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/user/UserService.java +++ /dev/null @@ -1,18 +0,0 @@ -package ru.practicum.ewm.service.user; - -import jakarta.transaction.Transactional; -import ru.practicum.ewm.dto.user.NewUserRequest; -import ru.practicum.ewm.dto.user.UserDto; - -import java.util.List; - -public interface UserService { - - @Transactional - UserDto adminAddNewUser(NewUserRequest newUserRequest); - - List getUsers(List ids, int from, int size); - - @Transactional - void deleteUser(Long userId); -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/user/UserServiceImpl.java b/core/main-service/src/main/java/ru/practicum/ewm/service/user/UserServiceImpl.java deleted file mode 100644 index 31cd7cd..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/user/UserServiceImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -package ru.practicum.ewm.service.user; - -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.PageRequest; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import ru.practicum.ewm.dao.UserRepository; -import ru.practicum.ewm.dto.user.NewUserRequest; -import ru.practicum.ewm.dto.user.UserDto; -import ru.practicum.ewm.mapper.UserMapper; -import ru.practicum.ewm.model.User; -import ru.practicum.ewm.util.error.exception.ConflictException; -import ru.practicum.ewm.util.error.exception.NotFoundException; - -import java.util.List; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -public class UserServiceImpl implements UserService { - - private final UserRepository userRepository; - - @Override - public UserDto adminAddNewUser(@NonNull NewUserRequest newUserRequest) { - if (userRepository.existsByEmail(newUserRequest.email())) { - throw new ConflictException("Пользователь с такой почтой " + newUserRequest.email() + " уже существует"); - } - User user = UserMapper.toEntity(newUserRequest); - return UserMapper.toUserDto(userRepository.save(user)); - } - - @Override - public List getUsers(List ids, int from, int size) { - PageRequest page = PageRequest.of(from / size, size); - List users; - if (ids == null || ids.isEmpty()) { - users = userRepository.findAll(page).getContent(); - } else { - users = userRepository.findAllByIdIn(ids, page); - } - return users.stream() - .map(UserMapper::toUserDto) - .collect(Collectors.toList()); - } - - @Override - @Transactional - public void deleteUser(Long userId) { - checkUser(userId); - userRepository.deleteById(userId); - } - - private void checkUser(Long userId) { - if (!userRepository.existsById(userId)) { - throw new NotFoundException("Пользователь с id=" + userId + " не найден"); - } - } -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/ConflictException.java b/core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/ConflictException.java deleted file mode 100644 index 8848991..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/ConflictException.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.practicum.ewm.util.error.exception; - -public class ConflictException extends RuntimeException { - - public ConflictException(String message) { - super(message); - } -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/NotFoundException.java b/core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/NotFoundException.java deleted file mode 100644 index a9d3922..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/NotFoundException.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.practicum.ewm.util.error.exception; - -public class NotFoundException extends RuntimeException { - - public NotFoundException(String message) { - super(message); - } -} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/StatResponseException.java b/core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/StatResponseException.java deleted file mode 100644 index f2b13cb..0000000 --- a/core/main-service/src/main/java/ru/practicum/ewm/util/error/exception/StatResponseException.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.practicum.ewm.util.error.exception; - -public class StatResponseException extends RuntimeException { - - private static final String MESSAGE = "Не удалось получить статистику"; - - public StatResponseException(Exception ex) { - super(MESSAGE, ex); - } - - public StatResponseException() { - super(MESSAGE); - } -} diff --git a/core/main-service/src/test/java/ru/practicum/ewm/TestUtils.java b/core/main-service/src/test/java/ru/practicum/ewm/TestUtils.java deleted file mode 100644 index 80e8ba9..0000000 --- a/core/main-service/src/test/java/ru/practicum/ewm/TestUtils.java +++ /dev/null @@ -1,103 +0,0 @@ -package ru.practicum.ewm; - -import org.springframework.boot.test.context.TestComponent; -import ru.practicum.ewm.model.Category; -import ru.practicum.ewm.model.Event; -import ru.practicum.ewm.model.Location; -import ru.practicum.ewm.model.User; -import ru.practicum.ewm.model.enums.EventState; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Random; - -@SuppressWarnings("unused") -@TestComponent -public class TestUtils { - - public final LocalDateTime pastDate = LocalDateTime.parse("2023-01-01T00:00:01"); - public final LocalDateTime futureDate = LocalDateTime.parse("2028-01-01T00:00:01"); - - private final Random random = new Random(); - - public String generateRandomText(int length, String addAfterStr, Character... addSynbols) { - java.util.List symbols = new ArrayList<>(List.of( - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', - 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', - 'u', 'v', 'w', 'x', 'y', 'z' - )); - symbols.addAll(Arrays.asList(addSynbols)); - StringBuilder sb = new StringBuilder(length); - - for (int i = 0; i < length; i++) { - int index = random.nextInt(symbols.size()); - sb.append(symbols.get(index)); - } - - return sb.append(addAfterStr).toString(); - } - - public Location generateLocation() { - return Location.builder() - .lat(random.nextFloat()) - .lon(random.nextFloat()) - .build(); - } - - public User generateUser() { - return User.builder() - .name(generateRandomText(15, "", ' ')) - .email(generateRandomText(15, "@mail.ru")) - .build(); - } - - public Category generateCategory() { - return Category.builder() - .name(generateRandomText(10, "")) - .build(); - } - - public Event generateEvent(User initiator, - Category category, - LocalDateTime eventDate, - EventState eventState) { - return Event.builder() - .annotation(generateRandomText(100, "", ' ')) - .category(category) - .createdOn(eventDate.minusDays(1)) - .description(generateRandomText(200, "", ' ')) - .eventDate(eventDate) - .initiator(initiator) - .location(generateLocation()) - .paid(random.nextBoolean()) - .participantLimit(random.nextInt(100)) - .publishedOn(eventDate.plusHours(3)) - .requestModeration(random.nextBoolean()) - .state(eventState) - .title(generateRandomText(15, "", ' ')) - .rate(0) - .build(); - } - - public Event generateEvent(LocalDateTime eventDate, EventState eventState) { - return generateEvent(generateUser(), generateCategory(), eventDate, eventState); - } - - public Event generateEvent(EventState eventState) { - return generateEvent(pastDate, eventState); - } - - public Event generateEvent() { - return generateEvent(pastDate, EventState.PUBLISHED); - } - - public Event getNewCopyOfEvent(Event event) { - return event.toBuilder().id(null).build(); - } - - public Event getNewCopyOfEvent(Event event, EventState eventState) { - return event.toBuilder().id(null).state(eventState).build(); - } -} \ No newline at end of file diff --git a/core/main-service/src/test/java/ru/practicum/ewm/controller/admin/AdminCategoryControllerTest.java b/core/main-service/src/test/java/ru/practicum/ewm/controller/admin/AdminCategoryControllerTest.java deleted file mode 100644 index 58fb804..0000000 --- a/core/main-service/src/test/java/ru/practicum/ewm/controller/admin/AdminCategoryControllerTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package ru.practicum.ewm.controller.admin; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import ru.practicum.ewm.dto.category.CategoryDto; -import ru.practicum.ewm.dto.category.NewCategoryDto; -import ru.practicum.ewm.service.category.CategoryService; - -import static org.mockito.Mockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -@ExtendWith(MockitoExtension.class) -class AdminCategoryControllerTest { - - @Mock - private CategoryService categoryService; - - @InjectMocks - private AdminCategoryController controller; - - private MockMvc mockMvc; - - private final ObjectMapper objectMapper = new ObjectMapper(); - - @BeforeEach - void setUp() { - mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); - } - - @Test - void createCategory_blankName_shouldReturn400() throws Exception { - NewCategoryDto newCat = new NewCategoryDto(""); - - mockMvc.perform(post("/admin/categories") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(newCat))) - .andExpect(status().isBadRequest()); - } - - @Test - void updateCategory_validData_shouldReturn200() throws Exception { - CategoryDto update = CategoryDto.builder().id(1L).name("Выставки").build(); - CategoryDto updated = CategoryDto.builder().id(1L).name("Выставки").build(); - when(categoryService.updateCategory(1L, update)).thenReturn(updated); - - mockMvc.perform(patch("/admin/categories/1") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(update))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.name").value("Выставки")); - } - - @Test - void deleteCategory_existing_shouldReturn204() throws Exception { - mockMvc.perform(delete("/admin/categories/1")) - .andExpect(status().isNoContent()); - } -} \ No newline at end of file diff --git a/core/main-service/src/test/java/ru/practicum/ewm/controller/admin/AdminUserControllerTest.java b/core/main-service/src/test/java/ru/practicum/ewm/controller/admin/AdminUserControllerTest.java deleted file mode 100644 index 65dc166..0000000 --- a/core/main-service/src/test/java/ru/practicum/ewm/controller/admin/AdminUserControllerTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package ru.practicum.ewm.controller.admin; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import ru.practicum.ewm.dto.user.NewUserRequest; -import ru.practicum.ewm.dto.user.UserDto; -import ru.practicum.ewm.service.user.UserService; - -import java.util.List; - -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -@ExtendWith(MockitoExtension.class) -class AdminUserControllerTest { - - @Mock - private UserService userService; - - @InjectMocks - private AdminUserController controller; - - private MockMvc mockMvc; - - private final ObjectMapper objectMapper = new ObjectMapper(); - - @BeforeEach - void setUp() { - mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); - } - - @Test - void createUser_validRequest_shouldReturn201() throws Exception { - NewUserRequest request = new NewUserRequest("Test User", "user@example.com"); - UserDto userDto = new UserDto(1L, "Test User", "user@example.com"); - when(userService.adminAddNewUser(any(NewUserRequest.class))).thenReturn(userDto); - - mockMvc.perform(post("/admin/users") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isCreated()) - .andExpect(jsonPath("$.id").value(1L)) - .andExpect(jsonPath("$.name").value("Test User")) - .andExpect(jsonPath("$.email").value("user@example.com")); - } - - @Test - void createUser_blankName_shouldReturn400() throws Exception { - NewUserRequest request = new NewUserRequest("", "user@example.com"); - - mockMvc.perform(post("/admin/users") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isBadRequest()); - } - - @Test - void getUsers_shouldReturnList() throws Exception { - UserDto user = new UserDto(1L, "Test User", "user@example.com"); - when(userService.getUsers(any(), anyInt(), anyInt())).thenReturn(List.of(user)); - - mockMvc.perform(get("/admin/users") - .param("from", "0") - .param("size", "10")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$[0].id").value(1L)) - .andExpect(jsonPath("$[0].name").value("Test User")); - } - - @Test - void deleteUser_existingUser_shouldReturn204() throws Exception { - mockMvc.perform(delete("/admin/users/1")) - .andExpect(status().isNoContent()); - } -} \ No newline at end of file diff --git a/core/main-service/src/test/java/ru/practicum/ewm/dto/NewEventDtoTest.java b/core/main-service/src/test/java/ru/practicum/ewm/dto/NewEventDtoTest.java deleted file mode 100644 index bec8633..0000000 --- a/core/main-service/src/test/java/ru/practicum/ewm/dto/NewEventDtoTest.java +++ /dev/null @@ -1,200 +0,0 @@ -package ru.practicum.ewm.dto; - -import jakarta.validation.ConstraintViolation; -import jakarta.validation.Validation; -import jakarta.validation.Validator; -import jakarta.validation.ValidatorFactory; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import ru.practicum.ewm.dto.event.NewEventDto; -import ru.practicum.ewm.model.Location; - -import java.time.LocalDateTime; - -import static org.junit.jupiter.api.Assertions.*; - -/// generated by Alisa -class NewEventDtoTest { - - private Validator validator; - - private final String validAnnotation = "Valid annotation text with more than 20 characters"; - private final String validDescription = "Valid description with more than 20 characters and less than 7000 chars"; - - @BeforeEach - void setUp() { - try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) { - validator = factory.getValidator(); - } - } - - @Test - void whenValidDto_thenNoViolations() { - NewEventDto dto = NewEventDto.builder() - .annotation(validAnnotation) - .category(1L) - .description(validDescription) - .eventDate(LocalDateTime.now().plusDays(1)) - .location(Location.builder() - .lat(55.7558F) - .lon(37.6173F) - .build()) - .paid(true) - .participantLimit(100) - .requestModeration(true) - .title("Valid title") - .build(); - - var violations = validator.validate(dto); - - assertEquals(0, violations.size()); - } - - @Test - void whenAnnotationTooShort_thenViolation() { - NewEventDto dto = NewEventDto.builder() - .annotation("short") - .category(1L) - .description(validDescription) - .eventDate(LocalDateTime.now().plusDays(1)) - .location(Location.builder() - .lat(55.7558F) - .lon(37.6173F) - .build()) - .paid(true) - .participantLimit(100) - .requestModeration(true) - .title("Valid title") - .build(); - - var violations = validator.validate(dto); - - assertEquals(1, violations.size()); - ConstraintViolation violation = violations.iterator().next(); - assertEquals("annotation", violation.getPropertyPath().toString()); - assertEquals("размер должен находиться в диапазоне от 20 до 2000", violation.getMessage()); - } - - @Test - void whenCategoryIsNull_thenViolation() { - NewEventDto dto = NewEventDto.builder() - .annotation(validAnnotation) - .category(null) - .description(validDescription) - .eventDate(LocalDateTime.now().plusDays(1)) - .location(Location.builder() - .lat(55.7558F) - .lon(37.6173F) - .build()) - .paid(true) - .participantLimit(100) - .requestModeration(true) - .title("Valid title") - .build(); - - var violations = validator.validate(dto); - - assertEquals(1, violations.size()); - ConstraintViolation violation = violations.iterator().next(); - assertEquals("category", violation.getPropertyPath().toString()); - assertEquals("не должно равняться null", violation.getMessage()); - } - - @Test - void whenDescriptionTooLong_thenViolation() { - String longDescription = "A".repeat(7001); - NewEventDto dto = NewEventDto.builder() - .annotation(validAnnotation) - .category(1L) - .description(longDescription) - .eventDate(LocalDateTime.now().plusDays(1)) - .location(Location.builder() - .lat(55.7558F) - .lon(37.6173F) - .build()) - .paid(true) - .participantLimit(100) - .requestModeration(true) - .title("Valid title") - .build(); - - var violations = validator.validate(dto); - - assertEquals(1, violations.size()); - ConstraintViolation violation = violations.iterator().next(); - assertEquals("description", violation.getPropertyPath().toString()); - assertEquals("размер должен находиться в диапазоне от 20 до 7000", violation.getMessage()); - } - - @Test - void whenEventDateIsNull_thenViolation() { - NewEventDto dto = NewEventDto.builder() - .annotation(validAnnotation) - .category(1L) - .description(validDescription) - .eventDate(null) - .location(Location.builder() - .lat(55.7558F) - .lon(37.6173F) - .build()) - .paid(true) - .participantLimit(100) - .requestModeration(true) - .title("Valid title") - .build(); - - var violations = validator.validate(dto); - - assertEquals(1, violations.size()); - ConstraintViolation violation = violations.iterator().next(); - assertEquals("eventDate", violation.getPropertyPath().toString()); - assertEquals("не должно равняться null", violation.getMessage()); - } - - @Test - void whenLocationIsNull_thenViolation() { - NewEventDto dto = NewEventDto.builder() - .annotation(validAnnotation) - .category(1L) - .description(validDescription) - .eventDate(LocalDateTime.now().plusDays(1)) - .location(null) - .paid(true) - .participantLimit(100) - .requestModeration(true) - .title("Valid title") - .build(); - - var violations = validator.validate(dto); - - assertEquals(1, violations.size()); - ConstraintViolation violation = violations.iterator().next(); - assertEquals("location", violation.getPropertyPath().toString()); - assertEquals("не должно равняться null", violation.getMessage()); - } - - @Test - void whenTitleTooShort_thenViolation() { - NewEventDto dto = NewEventDto.builder() - .annotation(validAnnotation) - .category(1L) - .description(validDescription) - .eventDate(LocalDateTime.now().plusDays(1)) - .location(Location.builder() - .lat(55.7558F) - .lon(37.6173F) - .build()) - .paid(true) - .participantLimit(100) - .requestModeration(true) - .title("ab") - .build(); - - var violations = validator.validate(dto); - - assertEquals(1, violations.size()); - ConstraintViolation violation = violations.iterator().next(); - assertEquals("title", violation.getPropertyPath().toString()); - assertEquals("размер должен находиться в диапазоне от 3 до 120", violation.getMessage()); - } -} diff --git a/core/main-service/src/test/java/ru/practicum/ewm/service/AdminCategoryServiceTest.java b/core/main-service/src/test/java/ru/practicum/ewm/service/AdminCategoryServiceTest.java deleted file mode 100644 index 3da291e..0000000 --- a/core/main-service/src/test/java/ru/practicum/ewm/service/AdminCategoryServiceTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package ru.practicum.ewm.service; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import ru.practicum.ewm.dao.CategoryRepository; -import ru.practicum.ewm.dao.EventRepository; -import ru.practicum.ewm.dto.category.CategoryDto; -import ru.practicum.ewm.dto.category.NewCategoryDto; -import ru.practicum.ewm.model.Category; -import ru.practicum.ewm.service.category.CategoryServiceImpl; -import ru.practicum.ewm.util.error.exception.ConflictException; -import ru.practicum.ewm.util.error.exception.NotFoundException; - -import java.util.Optional; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -class AdminCategoryServiceTest { - - @Mock - private CategoryRepository categoryRepository; - @Mock - private EventRepository eventRepository; - - @InjectMocks - private CategoryServiceImpl categoryService; - - private Category category; - private NewCategoryDto newCategoryDto; - private CategoryDto updateDto; - - @BeforeEach - void setUp() { - category = Category.builder() - .id(1L) - .name("Концерты") - .build(); - newCategoryDto = new NewCategoryDto("Концерты"); - updateDto = CategoryDto.builder() - .id(1L) - .name("Выставки") - .build(); - } - - @Test - void createCategory_success() { - when(categoryRepository.existsByName("Концерты")).thenReturn(false); - when(categoryRepository.save(any(Category.class))).thenReturn(category); - - CategoryDto result = categoryService.adminAddNewCategory(newCategoryDto); - - assertThat(result.id()).isEqualTo(1L); - assertThat(result.name()).isEqualTo("Концерты"); - } - - @Test - void createCategory_duplicateName_shouldThrowConflict() { - when(categoryRepository.existsByName("Концерты")).thenReturn(true); - - assertThatThrownBy(() -> categoryService.adminAddNewCategory(newCategoryDto)) - .isInstanceOf(ConflictException.class) - .hasMessageContaining("уже существует"); - } - - @Test - void updateCategory_success() { - when(categoryRepository.findById(1L)).thenReturn(Optional.of(category)); - when(categoryRepository.existsByName("Выставки")).thenReturn(false); - when(categoryRepository.save(any(Category.class))).thenReturn(category); - - CategoryDto result = categoryService.updateCategory(1L, updateDto); - - assertThat(result.name()).isEqualTo("Выставки"); - verify(categoryRepository).save(category); - } - - @Test - void updateCategory_notFound_shouldThrowNotFound() { - when(categoryRepository.findById(999L)).thenReturn(Optional.empty()); - - assertThatThrownBy(() -> categoryService.updateCategory(999L, updateDto)) - .isInstanceOf(NotFoundException.class); - } - - @Test - void deleteCategory_noEvents_success() { - when(categoryRepository.findById(1L)).thenReturn(Optional.of(category)); - when(eventRepository.existsByCategoryId(1L)).thenReturn(false); - - categoryService.deleteCategory(1L); - - verify(categoryRepository).delete(category); - } - - @Test - void deleteCategory_withEvents_shouldThrowConflict() { - when(categoryRepository.findById(1L)).thenReturn(Optional.of(category)); - when(eventRepository.existsByCategoryId(1L)).thenReturn(true); - - assertThatThrownBy(() -> categoryService.deleteCategory(1L)) - .isInstanceOf(ConflictException.class) - .hasMessageContaining("The category is not empty"); - verify(categoryRepository, never()).delete(any()); - } -} \ No newline at end of file diff --git a/core/main-service/src/test/java/ru/practicum/ewm/service/AdminUserServiceTest.java b/core/main-service/src/test/java/ru/practicum/ewm/service/AdminUserServiceTest.java deleted file mode 100644 index ec176d8..0000000 --- a/core/main-service/src/test/java/ru/practicum/ewm/service/AdminUserServiceTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package ru.practicum.ewm.service; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.domain.PageRequest; -import ru.practicum.ewm.dao.UserRepository; -import ru.practicum.ewm.dto.user.NewUserRequest; -import ru.practicum.ewm.dto.user.UserDto; -import ru.practicum.ewm.model.User; -import ru.practicum.ewm.service.user.UserServiceImpl; -import ru.practicum.ewm.util.error.exception.NotFoundException; - -import java.util.List; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.eq; - -@ExtendWith(MockitoExtension.class) -class AdminUserServiceTest { - - @Mock - private UserRepository userRepository; - - @InjectMocks - private UserServiceImpl userService; - - private User user; - private NewUserRequest newUserRequest; - - @BeforeEach - void setUp() { - user = User.builder() - .id(1L) - .name("Test User") - .email("user@example.com") - .build(); - newUserRequest = NewUserRequest.builder() - .name("Test User") - .email("user@example.com") - .build(); - } - - @Test - void createUser_shouldReturnUserDto() { - when(userRepository.save(any(User.class))).thenReturn(user); - - UserDto result = userService.adminAddNewUser(newUserRequest); - - assertThat(result.id()).isEqualTo(1L); - assertThat(result.name()).isEqualTo("Test User"); - assertThat(result.email()).isEqualTo("user@example.com"); - verify(userRepository).save(any(User.class)); - } - - @Test - void getUsers_withoutIds_shouldReturnAll() { - when(userRepository.findAll(any(PageRequest.class))) - .thenReturn(org.springframework.data.domain.Page.empty()); - - List result = userService.getUsers(null, 0, 10); - - assertThat(result).isEmpty(); - verify(userRepository).findAll(any(PageRequest.class)); - } - - @Test - void getUsers_withIds_shouldFilterByIds() { - when(userRepository.findAllByIdIn(eq(List.of(1L)), any(PageRequest.class))) - .thenReturn(List.of(user)); - - List result = userService.getUsers(List.of(1L), 0, 10); - - assertThat(result).hasSize(1); - assertThat(result.getFirst().id()).isEqualTo(1L); - } - - @Test - void deleteUser_existingUser_shouldSucceed() { - when(userRepository.existsById(1L)).thenReturn(true); - doNothing().when(userRepository).deleteById(1L); - - assertThatCode(() -> userService.deleteUser(1L)).doesNotThrowAnyException(); - verify(userRepository).deleteById(1L); - } - - @Test - void deleteUser_notFound_shouldThrowNotFoundException() { - when(userRepository.existsById(999L)).thenReturn(false); - - assertThatThrownBy(() -> userService.deleteUser(999L)) - .isInstanceOf(NotFoundException.class) - .hasMessageContaining("Пользователь с id=999 не найден"); - verify(userRepository, never()).deleteById(any()); - } -} \ No newline at end of file diff --git a/core/main-service/src/test/java/ru/practicum/ewm/service/EventServiceImplTest.java b/core/main-service/src/test/java/ru/practicum/ewm/service/EventServiceImplTest.java deleted file mode 100644 index 9c97663..0000000 --- a/core/main-service/src/test/java/ru/practicum/ewm/service/EventServiceImplTest.java +++ /dev/null @@ -1,162 +0,0 @@ -package ru.practicum.ewm.service; - -import jakarta.servlet.http.HttpServletRequest; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentMatchers; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import ru.practicum.ewm.dao.EventRepository; -import ru.practicum.ewm.dao.RequestRepository; -import ru.practicum.ewm.dto.event.EventFullDto; -import ru.practicum.ewm.dto.event.EventShortDto; -import ru.practicum.ewm.dto.event.FreeGetDto; -import ru.practicum.ewm.mapper.EventMapper; -import ru.practicum.ewm.model.Category; -import ru.practicum.ewm.model.Event; -import ru.practicum.ewm.model.Location; -import ru.practicum.ewm.model.User; -import ru.practicum.ewm.model.enums.EventState; -import ru.practicum.ewm.model.enums.ParticipationStatus; -import ru.practicum.ewm.service.event.EventServiceImpl; -import ru.practicum.ewm.util.statistic.StatRepository; -import ru.practicum.stat.dto.ViewStatsDto; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; -import static org.hamcrest.Matchers.contains; -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -class EventServiceImplTest { - - @Mock - StatRepository statRepository; - - @Mock - EventRepository eventRepository; - - @Mock - RequestRepository requestRepository; - - @Mock - HttpServletRequest httpServletRequest; - - @InjectMocks - EventServiceImpl eventService; - - private static Event event; - - @BeforeAll - static void setup() { - event = Event.builder() - .id(1L) - .annotation("annotation".repeat(10)) - .category(Category.builder().id(1L).name("category").build()) - .createdOn(LocalDateTime.parse("2026-11-10T19:00:00")) - .description("description".repeat(10)) - .eventDate(LocalDateTime.parse("2027-01-01T00:00:01")) - .initiator(User.builder().id(1L).name("User").email("user@email.ry").build()) - .location(Location.builder().lat(25.55F).lon(35.77F).build()) - .paid(true) - .participantLimit(30) - .publishedOn(LocalDateTime.parse("2026-12-10T21:00:00")) - .requestModeration(true) - .state(EventState.PUBLISHED) - .title("title") - .rate(0) - .build(); - } - - @Nested - @DisplayName("Получение списка публичных событий") - class GetFreeEvents { - - @Test - void basicFlow() { - // region setup - FreeGetDto getDto = FreeGetDto.builder() - .text("0") - .categories(List.of(0)) - .paid(true) - .onlyAvailable(true) - .rangeStart(LocalDateTime.parse("2026-12-31T23:59:00")) - .rangeEnd(LocalDateTime.parse("2027-01-01T02:00:00")) - .sort(FreeGetDto.FreeEventSort.EVENT_DATE) - .from(0) - .size(1) - .build(); - - Page page = new PageImpl<>(List.of(event)); - // endregion setup - - doNothing().when(statRepository).sendHitRequest(httpServletRequest); - when(eventRepository.findAll( - ArgumentMatchers.>any(), - ArgumentMatchers.any(Pageable.class) - )).thenReturn(page); - - List result = eventService.getFreeEvents(getDto, httpServletRequest); - - assertThat(result, contains(EventMapper.toEventShortDto(event, 0L, 0L))); - verify(statRepository).sendHitRequest(httpServletRequest); - } - - @Test - void whenNoDateRangeProvided() { - FreeGetDto dto = FreeGetDto.builder() - .from(0) - .size(10) - .build(); - - when(eventRepository.findAll( - ArgumentMatchers.>any(), - ArgumentMatchers.any(Pageable.class) - )).thenReturn(Page.empty()); - - eventService.getFreeEvents(dto, httpServletRequest); - - verify(eventRepository).findAll( - ArgumentMatchers.>any(), - ArgumentMatchers.any(Pageable.class) - ); - } - } - - @Nested - @DisplayName("Получение публичного события по id") - class GetFreeEventById { - - @Test - void basicFlow() { - doNothing().when(statRepository).sendHitRequest(httpServletRequest); - when(eventRepository.existsByIdAndState(event.getId(), EventState.PUBLISHED)).thenReturn(Boolean.TRUE); - when(eventRepository.findById(event.getId())).thenReturn(Optional.of(event)); - when(httpServletRequest.getRequestURI()).thenReturn("/events"); - when(statRepository.getStat(List.of("/events"), true)) - .thenReturn(List.of(ViewStatsDto.builder() - .hits(0L) - .build() - )); - when(requestRepository.countByEventIdAndStatus(event.getId(), ParticipationStatus.CONFIRMED)) - .thenReturn(0); - - EventFullDto result = eventService.getFreeEventById(event.getId(), httpServletRequest); - - assertThat(result, is(EventMapper.toEventFullDto(event, 0L, 0L))); - verify(statRepository).sendHitRequest(httpServletRequest); - } - } -} \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml index 6f1efc4..fbe6d11 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -14,44 +14,20 @@ pom - main-service + aggregation-api + event-service + request-service + user-service + rating-service + - org.springframework.cloud - spring-cloud-starter-netflix-eureka-client - - - org.springframework.cloud - spring-cloud-starter-config - - - - org.springframework.cloud - spring-cloud-starter-loadbalancer - - - com.github.ben-manes.caffeine - caffeine - - - - org.springframework.retry - spring-retry - - - org.springframework.boot - spring-boot-starter-aop - - - - org.springframework.boot - spring-boot-starter-actuator - - - io.micrometer - micrometer-registry-prometheus + org.projectlombok + lombok + provided + true diff --git a/core/rating-service/pom.xml b/core/rating-service/pom.xml new file mode 100644 index 0000000..87552cc --- /dev/null +++ b/core/rating-service/pom.xml @@ -0,0 +1,121 @@ + + + 4.0.0 + + + ru.practicum + core + 0.0.1-SNAPSHOT + + + rating-service + + + + ru.practicum + aggregation-api + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.zalando + logbook-spring-boot-starter + 4.0.3 + + + io.hypersistence + hypersistence-utils-hibernate-63 + 3.15.2 + compile + + + + org.postgresql + postgresql + runtime + + + com.h2database + h2 + runtime + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-config + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.github.ben-manes.caffeine + caffeine + + + + org.springframework.retry + spring-retry + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-actuator + + + io.micrometer + micrometer-registry-prometheus + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java new file mode 100644 index 0000000..1e36bc3 --- /dev/null +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java @@ -0,0 +1,27 @@ +package ru.practicum.ewm.ratings; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; +import ru.practicum.aggregation.client.event.EventClient; +import ru.practicum.aggregation.client.request.RequestClient; +import ru.practicum.aggregation.client.user.UserClient; + +@SpringBootApplication +@EnableFeignClients(clients = { + UserClient.class, + EventClient.class, + RequestClient.class +}) +@ComponentScan(value = { + "ru.practicum.ewm.ratings", + "ru.practicum.aggregation.client", + "ru.practicum.aggregation.error", + "ru.practicum.aggregation.repository" +}) +public class RatingServiceApp { + public static void main(String[] args) { + SpringApplication.run(RatingServiceApp.class, args); + } +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/controller/rating/RatingController.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/RatingController.java similarity index 80% rename from core/main-service/src/main/java/ru/practicum/ewm/controller/rating/RatingController.java rename to core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/RatingController.java index 0940438..cf0bb8f 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/controller/rating/RatingController.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/RatingController.java @@ -1,12 +1,12 @@ -package ru.practicum.ewm.controller.rating; +package ru.practicum.ewm.ratings.controller; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.ewm.dto.rating.RatingRequest; -import ru.practicum.ewm.dto.rating.RatingResponse; -import ru.practicum.ewm.service.rating.RatingService; +import ru.practicum.aggregation.dto.rating.RatingRequest; +import ru.practicum.aggregation.dto.rating.RatingResponse; +import ru.practicum.ewm.ratings.service.RatingService; @RestController @RequestMapping("/users/{userId}/events/{eventId}/likes") @@ -29,4 +29,5 @@ public void removeReaction(@PathVariable Long userId, @PathVariable Long eventId) { ratingService.removeReaction(userId, eventId); } + } \ No newline at end of file diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/Rating.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/entity/Rating.java similarity index 70% rename from core/main-service/src/main/java/ru/practicum/ewm/model/Rating.java rename to core/rating-service/src/main/java/ru/practicum/ewm/ratings/entity/Rating.java index f10552c..2b76a7a 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/Rating.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/entity/Rating.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.model; +package ru.practicum.ewm.ratings.entity; import jakarta.persistence.*; import lombok.AccessLevel; @@ -7,8 +7,8 @@ import lombok.Setter; import lombok.experimental.FieldDefaults; import lombok.experimental.SuperBuilder; -import ru.practicum.ewm.model.enums.Reaction; -import ru.practicum.ewm.util.entity.BaseEntity; +import ru.practicum.aggregation.enums.Reaction; +import ru.practicum.aggregation.model.BaseEntity; @Getter @Setter @@ -21,15 +21,13 @@ @FieldDefaults(level = AccessLevel.PRIVATE) public class Rating extends BaseEntity { - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) - private User user; + Long userId; - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "event_id", nullable = false) - private Event event; + Long eventId; @Enumerated(EnumType.STRING) @Column(nullable = false) - private Reaction reaction; + Reaction reaction; } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dao/RatingRepository.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/repository/RatingRepository.java similarity index 63% rename from core/main-service/src/main/java/ru/practicum/ewm/dao/RatingRepository.java rename to core/rating-service/src/main/java/ru/practicum/ewm/ratings/repository/RatingRepository.java index 418c9cd..91753f2 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dao/RatingRepository.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/repository/RatingRepository.java @@ -1,8 +1,8 @@ -package ru.practicum.ewm.dao; +package ru.practicum.ewm.ratings.repository; import org.springframework.data.jpa.repository.JpaRepository; -import ru.practicum.ewm.model.Rating; -import ru.practicum.ewm.model.enums.Reaction; +import ru.practicum.aggregation.enums.Reaction; +import ru.practicum.ewm.ratings.entity.Rating; import java.util.Optional; @@ -10,7 +10,5 @@ public interface RatingRepository extends JpaRepository { Optional findByUserIdAndEventId(Long userId, Long eventId); - boolean existsByUserIdAndEventId(Long userId, Long eventId); - long countByEventIdAndReaction(Long eventId, Reaction reaction); } diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java new file mode 100644 index 0000000..43ae650 --- /dev/null +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java @@ -0,0 +1,14 @@ +package ru.practicum.ewm.ratings.service; + +import org.springframework.lang.NonNull; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.aggregation.dto.rating.RatingRequest; +import ru.practicum.aggregation.dto.rating.RatingResponse; + +@Transactional +public interface RatingService { + + RatingResponse addOrUpdateReaction(long userId, long eventId, @NonNull RatingRequest request); + + void removeReaction(long userId, long eventId); +} diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java new file mode 100644 index 0000000..8c47897 --- /dev/null +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java @@ -0,0 +1,97 @@ +package ru.practicum.ewm.ratings.service; + +import jakarta.validation.ValidationException; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Service; +import ru.practicum.aggregation.dto.rating.RatingRequest; +import ru.practicum.aggregation.dto.rating.RatingResponse; +import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.aggregation.enums.Reaction; +import ru.practicum.aggregation.error.exception.ConflictException; +import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.repository.EventFeignRepository; +import ru.practicum.aggregation.repository.UserFeignRepository; +import ru.practicum.ewm.ratings.entity.Rating; +import ru.practicum.ewm.ratings.repository.RatingRepository; + +import java.util.Objects; + +@Service +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class RatingServiceImpl implements RatingService { + + RatingRepository ratingRepository; + + UserFeignRepository userFeignRepository; + EventFeignRepository eventFeignRepository; + + @Override + public RatingResponse addOrUpdateReaction(long userId, long eventId, @NonNull RatingRequest request) { + var user = userFeignRepository.getUserDtoById(userId); + var event = eventFeignRepository.userFindEventById(userId, eventId); + var initiator = event.initiator(); + + if (Objects.equals(user.id(), initiator.id())) { + throw new ValidationException("Нельзя ставить реакции своим событиям"); + } + + var rating = ratingRepository.findByUserIdAndEventId(userId, eventId).orElse(null); + var requestReaction = request.reaction(); + + if (rating != null) { + if (rating.getReaction() == requestReaction) { + ratingRepository.delete(rating); + updateEventRate(eventId); + throw new ConflictException("Reaction removed"); + } else { + rating.setReaction(requestReaction); + ratingRepository.save(rating); + updateEventRate(eventId); + return mapToResponse(rating); + } + } else { + rating = Rating.builder() + .userId(userId) + .eventId(eventId) + .reaction(requestReaction) + .build(); + ratingRepository.save(rating); + updateEventRate(eventId); + return mapToResponse(rating); + } + } + + @Override + public void removeReaction(long userId, long eventId) { + Rating rating = ratingRepository.findByUserIdAndEventId(userId, eventId) + .orElseThrow(() -> new NotFoundException("Реакция не найдена")); + ratingRepository.delete(rating); + updateEventRate(eventId); + } + + private void updateEventRate(long eventId) { + long likes = ratingRepository.countByEventIdAndReaction(eventId, Reaction.LIKE); + long dislikes = ratingRepository.countByEventIdAndReaction(eventId, Reaction.DISLIKE); + long rate = likes - dislikes; + + var request = RatingUpdateRequest.builder() + .eventId(eventId) + .rate(rate) + .build(); + + eventFeignRepository.updateRating(request); + } + + private RatingResponse mapToResponse(@NonNull Rating rating) { + return RatingResponse.builder() + .id(rating.getId()) + .userId(rating.getUserId()) + .eventId(rating.getEventId()) + .reaction(rating.getReaction()) + .build(); + } +} diff --git a/core/rating-service/src/main/resources/application.yml b/core/rating-service/src/main/resources/application.yml new file mode 100644 index 0000000..338ad89 --- /dev/null +++ b/core/rating-service/src/main/resources/application.yml @@ -0,0 +1,31 @@ +server: + port: 0 + +spring: + application: + name: rating-service + config: + import: "optional:configserver:" + cloud: + config: + discovery: + enabled: true + service-id: config-server + fail-fast: true + retry: + initial-interval: 1000 + max-interval: 10000 + multiplier: 2.0 + max-attempts: 20 + +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8761/eureka/ + register-with-eureka: true + fetch-registry: true + instance: + prefer-ip-address: true + instance-id: ${spring.application.name}:${random.value} + lease-renewal-interval-in-seconds: 5 + lease-expiration-duration-in-seconds: 10 diff --git a/core/rating-service/src/main/resources/schema-postgres.sql b/core/rating-service/src/main/resources/schema-postgres.sql new file mode 100644 index 0000000..83fc95d --- /dev/null +++ b/core/rating-service/src/main/resources/schema-postgres.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS ratings ( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + user_id bigint NOT NULL, + event_id bigint NOT NULL, + reaction varchar(10) NOT NULL CHECK (reaction IN ('LIKE', 'DISLIKE')), + CONSTRAINT pk_ratings PRIMARY KEY (id), + CONSTRAINT uq_ratings_user_event UNIQUE (user_id, event_id) +); diff --git a/core/request-service/pom.xml b/core/request-service/pom.xml new file mode 100644 index 0000000..e02eb3f --- /dev/null +++ b/core/request-service/pom.xml @@ -0,0 +1,121 @@ + + + 4.0.0 + + + ru.practicum + core + 0.0.1-SNAPSHOT + + + request-service + + + + ru.practicum + aggregation-api + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.zalando + logbook-spring-boot-starter + 4.0.3 + + + io.hypersistence + hypersistence-utils-hibernate-63 + 3.15.2 + compile + + + + org.postgresql + postgresql + runtime + + + com.h2database + h2 + runtime + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-config + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.github.ben-manes.caffeine + caffeine + + + + org.springframework.retry + spring-retry + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-actuator + + + io.micrometer + micrometer-registry-prometheus + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java new file mode 100644 index 0000000..d9a1eef --- /dev/null +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java @@ -0,0 +1,27 @@ +package ru.practicum.ewm.requests; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; +import ru.practicum.aggregation.client.event.EventClient; +import ru.practicum.aggregation.client.request.RequestClient; +import ru.practicum.aggregation.client.user.UserClient; + +@SpringBootApplication +@EnableFeignClients(clients = { + UserClient.class, + EventClient.class, + RequestClient.class +}) +@ComponentScan(value = { + "ru.practicum.ewm.requests", + "ru.practicum.aggregation.client", + "ru.practicum.aggregation.error", + "ru.practicum.aggregation.repository" +}) +public class RequestServiceApp { + public static void main(String[] args) { + SpringApplication.run(RequestServiceApp.class, args); + } +} diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java new file mode 100644 index 0000000..36b315d --- /dev/null +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java @@ -0,0 +1,29 @@ +package ru.practicum.ewm.requests.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.enums.ParticipationStatus; +import ru.practicum.ewm.requests.service.RequestService; + +import java.util.List; + +@Slf4j +@RestController +@RequestMapping(path = "/admin/requests") +@RequiredArgsConstructor +public class AdminRequestController { + + private final RequestService requestService; + + @GetMapping("/count") + public List getCount(@RequestParam List eventIds, + @RequestParam ParticipationStatus status) { + return requestService.getRequestsCount(eventIds, status); + } + +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/controller/user/UserRequestController.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java similarity index 68% rename from core/main-service/src/main/java/ru/practicum/ewm/controller/user/UserRequestController.java rename to core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java index d970679..d3f545f 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/controller/user/UserRequestController.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java @@ -1,12 +1,15 @@ -package ru.practicum.ewm.controller.user; +package ru.practicum.ewm.requests.controller; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.ewm.dto.request.ParticipationRequestDto; -import ru.practicum.ewm.service.request.RequestService; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.ewm.requests.service.RequestService; import java.util.List; @@ -15,7 +18,8 @@ @RequestMapping(path = "/users/{userId}/requests") @RequiredArgsConstructor public class UserRequestController { - private final RequestService service; + + private final RequestService requestService; /** * Получение информации о заявках текущего пользователя на участие в чужих событиях @@ -27,7 +31,19 @@ public class UserRequestController { */ @GetMapping public List findByRequesterId(@PathVariable @Positive Long userId) { - return service.findByRequesterId(userId); + return requestService.findByRequesterId(userId); + } + + /** + * + * @param userId id текущего пользователя + * @param eventId id события + * @return List<{@link ParticipationRequestDto}> + */ + @GetMapping("/{eventId}") + public List findByEventId(@PathVariable @Positive Long userId, + @PathVariable @Positive Long eventId) { + return requestService.findByEventId(userId, eventId); } /** @@ -54,7 +70,16 @@ public List findByRequesterId(@PathVariable @Positive L @ResponseStatus(HttpStatus.CREATED) public ParticipationRequestDto addParticipationRequest(@PathVariable @Positive Long userId, @RequestParam @Positive Long eventId) { - return service.addParticipationRequest(userId, eventId); + return requestService.addParticipationRequest(userId, eventId); + } + + @PatchMapping("/events/{eventId}/status") + public EventRequestStatusUpdateResult updateStatusRequest( + @PathVariable @Positive Long userId, + @PathVariable @Positive Long eventId, + @RequestBody @NotNull EventRequestStatusUpdateRequest request + ) { + return requestService.updateStatusRequest(userId, eventId, request); } /** @@ -67,6 +92,7 @@ public ParticipationRequestDto addParticipationRequest(@PathVariable @Positive L @PatchMapping("/{requestId}/cancel") public ParticipationRequestDto cancelParticipationRequest(@PathVariable @Positive Long userId, @PathVariable @Positive Long requestId) { - return service.cancelParticipationRequest(userId, requestId); + return requestService.cancelParticipationRequest(userId, requestId); } + } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/ParticipationRequest.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/entity/ParticipationRequest.java similarity index 73% rename from core/main-service/src/main/java/ru/practicum/ewm/model/ParticipationRequest.java rename to core/request-service/src/main/java/ru/practicum/ewm/requests/entity/ParticipationRequest.java index 67bf763..319cd8f 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/ParticipationRequest.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/entity/ParticipationRequest.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.model; +package ru.practicum.ewm.requests.entity; import jakarta.persistence.*; import lombok.AccessLevel; @@ -7,8 +7,8 @@ import lombok.Setter; import lombok.experimental.FieldDefaults; import lombok.experimental.SuperBuilder; -import ru.practicum.ewm.model.enums.ParticipationStatus; -import ru.practicum.ewm.util.entity.BaseEntity; +import ru.practicum.aggregation.enums.ParticipationStatus; +import ru.practicum.aggregation.model.BaseEntity; import java.time.LocalDateTime; @@ -23,13 +23,11 @@ public class ParticipationRequest extends BaseEntity { LocalDateTime created; - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "event_id", nullable = false) - Event event; + Long eventId; - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "requester_id", nullable = false) - User requester; + Long requesterId; @Enumerated(EnumType.STRING) @Column(nullable = false) diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java new file mode 100644 index 0000000..728dd59 --- /dev/null +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java @@ -0,0 +1,33 @@ +package ru.practicum.ewm.requests.mapper; + +import lombok.experimental.UtilityClass; +import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.ewm.requests.entity.ParticipationRequest; + +import java.time.format.DateTimeFormatter; + +@UtilityClass +public class RequestMapper { + + public ParticipationRequestDto toParticipationRequestDto( + ParticipationRequest participationRequest) { + + if (participationRequest == null) { + return null; + } + + var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"); + + var dateTime = participationRequest.getCreated() == null ? + null : participationRequest.getCreated().format(formatter); + + return ParticipationRequestDto.builder() + .event(participationRequest.getEventId()) + .requester(participationRequest.getRequesterId()) + .status(participationRequest.getStatus()) + .created(dateTime) + .id(participationRequest.getId()) + .build(); + } + +} diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java new file mode 100644 index 0000000..84fbb84 --- /dev/null +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java @@ -0,0 +1,19 @@ +package ru.practicum.ewm.requests.mapper; + +import lombok.experimental.UtilityClass; +import org.springframework.lang.NonNull; +import ru.practicum.aggregation.dto.user.UserDto; +import ru.practicum.ewm.requests.model.User; + +@UtilityClass +public class UserMapper { + + public User toEntity(@NonNull UserDto userDto) { + return User.builder() + .id(userDto.id()) + .name(userDto.name()) + .email(userDto.email()) + .build(); + } + +} \ No newline at end of file diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java new file mode 100644 index 0000000..90715ed --- /dev/null +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java @@ -0,0 +1,19 @@ +package ru.practicum.ewm.requests.model; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.FieldDefaults; +import lombok.experimental.SuperBuilder; +import ru.practicum.aggregation.model.BaseEntity; + +@Getter +@Setter +@SuperBuilder +@NoArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class User extends BaseEntity { + String name; + String email; +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dao/RequestRepository.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/repository/RequestRepository.java similarity index 63% rename from core/main-service/src/main/java/ru/practicum/ewm/dao/RequestRepository.java rename to core/request-service/src/main/java/ru/practicum/ewm/requests/repository/RequestRepository.java index 73c41a5..be7a1b3 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dao/RequestRepository.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/repository/RequestRepository.java @@ -1,12 +1,12 @@ -package ru.practicum.ewm.dao; +package ru.practicum.ewm.requests.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.ewm.model.ParticipationRequest; -import ru.practicum.ewm.model.enums.ParticipationStatus; -import ru.practicum.ewm.service.request.EventRequestCount; +import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.enums.ParticipationStatus; +import ru.practicum.ewm.requests.entity.ParticipationRequest; import java.util.List; @@ -21,21 +21,21 @@ public interface RequestRepository extends JpaRepository countConfirmedRequestsByEventIds(List eventIds, ParticipationStatus status); + List getCountByEventIdsAndStatus(List eventIds, ParticipationStatus status); @Modifying @Transactional @Query(""" UPDATE ParticipationRequest pr SET pr.status = 'REJECTED' - WHERE pr.event.id = :eventId + WHERE pr.eventId = :eventId AND pr.status=:status """) int rejectPendingRequests(Long eventId, ParticipationStatus status); diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/request/RequestService.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java similarity index 51% rename from core/main-service/src/main/java/ru/practicum/ewm/service/request/RequestService.java rename to core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java index 50cff58..681352d 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/request/RequestService.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java @@ -1,22 +1,29 @@ -package ru.practicum.ewm.service.request; +package ru.practicum.ewm.requests.service; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.ewm.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.ewm.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.ewm.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.enums.ParticipationStatus; import java.util.List; @Transactional public interface RequestService { + @Transactional(readOnly = true) List findByEventId(Long userId, Long eventId); + @Transactional(readOnly = true) + List findByRequesterId(Long userId); + + @Transactional(readOnly = true) + List getRequestsCount(List eventIds, ParticipationStatus status); + EventRequestStatusUpdateResult updateStatusRequest(Long userId, Long eventId, EventRequestStatusUpdateRequest request); - List findByRequesterId(Long userId); - ParticipationRequestDto addParticipationRequest(Long userId, Long eventId); ParticipationRequestDto cancelParticipationRequest(Long userId, Long requestId); diff --git a/core/main-service/src/main/java/ru/practicum/ewm/service/request/RequestServiceImpl.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java similarity index 63% rename from core/main-service/src/main/java/ru/practicum/ewm/service/request/RequestServiceImpl.java rename to core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java index bed101c..02c1e5d 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/service/request/RequestServiceImpl.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.service.request; +package ru.practicum.ewm.requests.service; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -6,21 +6,22 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import ru.practicum.ewm.dao.EventRepository; -import ru.practicum.ewm.dao.RequestRepository; -import ru.practicum.ewm.dao.UserRepository; -import ru.practicum.ewm.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.ewm.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.ewm.dto.request.ParticipationRequestDto; -import ru.practicum.ewm.mapper.RequestMapper; -import ru.practicum.ewm.model.Event; -import ru.practicum.ewm.model.ParticipationRequest; -import ru.practicum.ewm.model.User; -import ru.practicum.ewm.model.enums.EventState; -import ru.practicum.ewm.model.enums.ParticipationStatus; -import ru.practicum.ewm.util.error.exception.ConflictException; -import ru.practicum.ewm.util.error.exception.NotFoundException; +import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.enums.EventState; +import ru.practicum.aggregation.enums.ParticipationStatus; +import ru.practicum.aggregation.error.exception.ConflictException; +import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.repository.EventFeignRepository; +import ru.practicum.aggregation.repository.UserFeignRepository; +import ru.practicum.ewm.requests.entity.ParticipationRequest; +import ru.practicum.ewm.requests.mapper.RequestMapper; +import ru.practicum.ewm.requests.mapper.UserMapper; +import ru.practicum.ewm.requests.model.User; +import ru.practicum.ewm.requests.repository.RequestRepository; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; @@ -35,12 +36,13 @@ public class RequestServiceImpl implements RequestService { RequestRepository requestRepository; - UserRepository userRepository; - EventRepository eventRepository; + + UserFeignRepository userFeignRepository; + EventFeignRepository eventFeignRepository; public List findByEventId(Long userId, Long eventId) { - Event event = getEventById(eventId); - if (!event.getInitiator().getId().equals(userId)) { + var event = getEventById(eventId); + if (!event.initiator().id().equals(userId)) { throw new NotFoundException("Событие не найдено"); } @@ -52,16 +54,16 @@ public List findByEventId(Long userId, Long eventId) { public EventRequestStatusUpdateResult updateStatusRequest(Long userId, Long eventId, EventRequestStatusUpdateRequest request) { - Event event = getEventById(eventId); - if (!event.getInitiator().getId().equals(userId)) { + var event = getEventById(eventId); + if (!event.initiator().id().equals(userId)) { throw new NotFoundException("Событие не найдено"); } - int limit = event.getParticipantLimit(); + int limit = event.participantLimit(); List confirmedRequests = new ArrayList<>(); List rejectedRequests = new ArrayList<>(); - boolean isModerationOff = !event.isRequestModeration() || limit == 0; + boolean isModerationOff = !event.requestModeration() || limit == 0; boolean idsEmpty = request.requestIds() == null || request.requestIds().isEmpty(); if (isModerationOff || idsEmpty) { @@ -71,19 +73,23 @@ public EventRequestStatusUpdateResult updateStatusRequest(Long userId, Long even .build(); } - int countConfirmed = requestRepository.countByEventIdAndStatus(eventId, ParticipationStatus.CONFIRMED); + int countConfirmed = requestRepository + .countByEventIdAndStatus(eventId, ParticipationStatus.CONFIRMED); List requests = requestRepository.findAllByIdIn(request.requestIds()); - if (request.status().name().equals(ParticipationStatus.CONFIRMED.name()) && countConfirmed >= limit) { + if (request.status().name().equals(ParticipationStatus.CONFIRMED.name()) && + countConfirmed >= limit) { throw new ConflictException("Достигнут лимит подтвержденных заявок"); } + boolean confirmed = request.status().name().equals("CONFIRMED"); + for (ParticipationRequest pr : requests) { if (!pr.getStatus().equals(ParticipationStatus.PENDING)) { throw new ConflictException("Статус можно изменить только у заявок в состоянии рассмотрения"); } - if (request.status().name().equals(ParticipationStatus.CONFIRMED.name()) && countConfirmed < limit) { + if (confirmed && countConfirmed < limit) { pr.setStatus(ParticipationStatus.CONFIRMED); countConfirmed++; confirmedRequests.add(RequestMapper.toParticipationRequestDto(pr)); @@ -96,7 +102,7 @@ public EventRequestStatusUpdateResult updateStatusRequest(Long userId, Long even requestRepository.saveAll(requests); // если в процессе лимит превышен - отклоняем все оставшиеся заявки - if (request.status().name().equals(ParticipationStatus.CONFIRMED.name()) && countConfirmed >= limit) { + if (confirmed && countConfirmed >= limit) { if (requestRepository.rejectPendingRequests(eventId, ParticipationStatus.PENDING) < 0) { throw new RuntimeException("Не удалось отклонить заявку"); } @@ -115,12 +121,11 @@ public List findByRequesterId(Long userId) { .toList(); } - @Transactional public ParticipationRequestDto addParticipationRequest(Long userId, Long eventId) { User requester = getUserById(userId); - Event event = getEventById(eventId); + var event = getEventById(eventId); - if (!EventState.PUBLISHED.equals(event.getState())) { + if (!EventState.PUBLISHED.equals(event.state())) { throw new ConflictException("Нельзя участвовать в неопубликованном событии"); } @@ -128,16 +133,19 @@ public ParticipationRequestDto addParticipationRequest(Long userId, Long eventId throw new ConflictException("Запрос уже существует"); } - if (event.getInitiator().getId().equals(userId)) { - throw new ConflictException("Инициатор события не может добавить запрос на участие в своём событии"); + if (event.initiator().id().equals(userId)) { + throw new ConflictException( + "Инициатор события не может добавить запрос на участие в своём событии"); } - int limit = event.getParticipantLimit(); + int limit = event.participantLimit(); if (limit != 0) { - long confirmedCount = requestRepository.countByEventIdAndStatus(eventId, ParticipationStatus.CONFIRMED); + long confirmedCount = requestRepository + .countByEventIdAndStatus(eventId, ParticipationStatus.CONFIRMED); - if (event.isRequestModeration()) { - long pendingCount = requestRepository.countByEventIdAndStatus(eventId, ParticipationStatus.PENDING); + if (event.requestModeration()) { + long pendingCount = requestRepository + .countByEventIdAndStatus(eventId, ParticipationStatus.PENDING); if (confirmedCount + pendingCount >= limit) { throw new ConflictException("Достигнут лимит запросов на участие"); } @@ -150,15 +158,15 @@ public ParticipationRequestDto addParticipationRequest(Long userId, Long eventId ParticipationStatus status; - if (!event.isRequestModeration() || limit == 0) { + if (!event.requestModeration() || limit == 0) { status = ParticipationStatus.CONFIRMED; } else { status = ParticipationStatus.PENDING; } ParticipationRequest request = ParticipationRequest.builder() - .requester(requester) - .event(event) + .requesterId(requester.getId()) + .eventId(event.id()) .status(status) .created(LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS)) .build(); @@ -169,24 +177,26 @@ public ParticipationRequestDto addParticipationRequest(Long userId, Long eventId public ParticipationRequestDto cancelParticipationRequest(Long userId, Long requestId) { ParticipationRequest request = getRequestById(requestId); - if (!request.getRequester().getId().equals(userId)) { + if (!request.getRequesterId().equals(userId)) { throw new ConflictException("Нельзя отменить чужую заявку"); } request.setStatus(ParticipationStatus.CANCELED); return RequestMapper.toParticipationRequestDto(requestRepository.save(request)); } + @Override + public List getRequestsCount(List eventIds, ParticipationStatus status) { + return requestRepository.getCountByEventIdsAndStatus(eventIds, status); + } + @NonNull private User getUserById(long userId) { - return userRepository.findById(userId) - .orElseThrow(() -> new NotFoundException("Пользователь с id=" + userId + " не найден")); + return UserMapper.toEntity(userFeignRepository.getUserDtoById(userId)); } @NonNull - private Event getEventById(long eventId) { - return eventRepository.findById(eventId).orElseThrow( - () -> new NotFoundException("Событие с id=" + eventId + " не найдено") - ); + private EventFullDto getEventById(long eventId) { + return eventFeignRepository.systemFindEventById(eventId); } @NonNull @@ -195,4 +205,5 @@ private ParticipationRequest getRequestById(Long requestId) { () -> new NotFoundException("Заявка с id=" + requestId + " не найдена") ); } + } diff --git a/core/request-service/src/main/resources/application.yml b/core/request-service/src/main/resources/application.yml new file mode 100644 index 0000000..a4f1bec --- /dev/null +++ b/core/request-service/src/main/resources/application.yml @@ -0,0 +1,31 @@ +server: + port: 0 + +spring: + application: + name: request-service + config: + import: "optional:configserver:" + cloud: + config: + discovery: + enabled: true + service-id: config-server + fail-fast: true + retry: + initial-interval: 1000 + max-interval: 10000 + multiplier: 2.0 + max-attempts: 20 + +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8761/eureka/ + register-with-eureka: true + fetch-registry: true + instance: + prefer-ip-address: true + instance-id: ${spring.application.name}:${random.value} + lease-renewal-interval-in-seconds: 5 + lease-expiration-duration-in-seconds: 10 diff --git a/core/request-service/src/main/resources/schema-postgres.sql b/core/request-service/src/main/resources/schema-postgres.sql new file mode 100644 index 0000000..e46e196 --- /dev/null +++ b/core/request-service/src/main/resources/schema-postgres.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS requests ( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + created timestamp(3) without time zone, + status varchar(255) check (status in ('PENDING', 'CONFIRMED', 'REJECTED', 'CANCELED')), + event_id bigint, + requester_id bigint, + CONSTRAINT pk_requests PRIMARY KEY (id) +); + diff --git a/core/main-service/pom.xml b/core/user-service/pom.xml similarity index 64% rename from core/main-service/pom.xml rename to core/user-service/pom.xml index a6098ed..ffefe46 100644 --- a/core/main-service/pom.xml +++ b/core/user-service/pom.xml @@ -3,25 +3,18 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - ru.practicum core 0.0.1-SNAPSHOT - main-service - jar + user-service ru.practicum - stat-dto - 0.0.1-SNAPSHOT - - - ru.practicum - stat-client + aggregation-api 0.0.1-SNAPSHOT @@ -43,12 +36,6 @@ test - - org.projectlombok - lombok - - - org.zalando logbook-spring-boot-starter @@ -71,6 +58,42 @@ h2 runtime + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-config + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.github.ben-manes.caffeine + caffeine + + + + org.springframework.retry + spring-retry + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-actuator + + + io.micrometer + micrometer-registry-prometheus + diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java b/core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java new file mode 100644 index 0000000..b859b39 --- /dev/null +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java @@ -0,0 +1,27 @@ +package ru.practicum.ewm.users; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; +import ru.practicum.aggregation.client.event.EventClient; +import ru.practicum.aggregation.client.request.RequestClient; +import ru.practicum.aggregation.client.user.UserClient; + +@SpringBootApplication +@EnableFeignClients(clients = { + UserClient.class, + EventClient.class, + RequestClient.class +}) +@ComponentScan(value = { + "ru.practicum.ewm.users", + "ru.practicum.aggregation.client", + "ru.practicum.aggregation.error", + "ru.practicum.aggregation.repository" +}) +public class UserServiceApp { + public static void main(String[] args) { + SpringApplication.run(UserServiceApp.class, args); + } +} diff --git a/core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminUserController.java b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java similarity index 60% rename from core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminUserController.java rename to core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java index cc5a305..007d66a 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminUserController.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java @@ -1,12 +1,13 @@ -package ru.practicum.ewm.controller.admin; +package ru.practicum.ewm.users.controller; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.ewm.dto.user.NewUserRequest; -import ru.practicum.ewm.dto.user.UserDto; -import ru.practicum.ewm.service.user.UserService; +import ru.practicum.aggregation.dto.user.NewUserRequest; +import ru.practicum.aggregation.dto.user.UserDto; +import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.ewm.users.service.UserService; import java.util.List; @@ -25,8 +26,40 @@ public class AdminUserController { */ @PostMapping @ResponseStatus(HttpStatus.CREATED) - public UserDto addNewUser(@RequestBody @Valid NewUserRequest newUserRequest) { - return userService.adminAddNewUser(newUserRequest); + public UserDto createUser(@RequestBody @Valid NewUserRequest newUserRequest) { + return userService.createUser(newUserRequest); + } + + /** + * Получение пользователя по ID + * + * @param userId ID пользователя + * @return {@link UserDto} + */ + @GetMapping("/{userId}") + public UserDto getUser(@PathVariable Long userId) { + return userService.getUser(userId); + } + + /** + * Проверка наличия пользователя по ID + * + * @param userId ID пользователя + */ + @GetMapping("/{userId}/check") + public void checkUser(@PathVariable Long userId) { + userService.remoteCheckUser(userId); + } + + /** + * Получение краткого списка пользователей + * + * @param userIds id пользователей + * @return {@link UserShortDto} + */ + @GetMapping("/short") + public List getShortUsersByIds(@RequestParam List userIds) { + return userService.getShortUsersByIds(userIds); } /** @@ -44,12 +77,12 @@ public UserDto addNewUser(@RequestBody @Valid NewUserRequest newUserRequest) { * @return List<{@link UserDto}> */ @GetMapping - public List getUsers( + public List findUsers( @RequestParam(required = false) List ids, @RequestParam(defaultValue = "0") int from, @RequestParam(defaultValue = "10") int size ) { - return userService.getUsers(ids, from, size); + return userService.findUsers(ids, from, size); } /** @@ -62,4 +95,5 @@ public List getUsers( public void deleteUser(@PathVariable Long userId) { userService.deleteUser(userId); } + } diff --git a/core/main-service/src/main/java/ru/practicum/ewm/model/User.java b/core/user-service/src/main/java/ru/practicum/ewm/users/entity/User.java similarity index 86% rename from core/main-service/src/main/java/ru/practicum/ewm/model/User.java rename to core/user-service/src/main/java/ru/practicum/ewm/users/entity/User.java index a2815eb..325f148 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/model/User.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/entity/User.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.model; +package ru.practicum.ewm.users.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -9,7 +9,7 @@ import lombok.Setter; import lombok.experimental.FieldDefaults; import lombok.experimental.SuperBuilder; -import ru.practicum.ewm.util.entity.BaseEntity; +import ru.practicum.aggregation.model.BaseEntity; @Getter @Setter diff --git a/core/main-service/src/main/java/ru/practicum/ewm/mapper/UserMapper.java b/core/user-service/src/main/java/ru/practicum/ewm/users/mapper/UserMapper.java similarity index 72% rename from core/main-service/src/main/java/ru/practicum/ewm/mapper/UserMapper.java rename to core/user-service/src/main/java/ru/practicum/ewm/users/mapper/UserMapper.java index cfa070f..abddc80 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/mapper/UserMapper.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/mapper/UserMapper.java @@ -1,11 +1,11 @@ -package ru.practicum.ewm.mapper; +package ru.practicum.ewm.users.mapper; import lombok.experimental.UtilityClass; import org.springframework.lang.NonNull; -import ru.practicum.ewm.dto.user.NewUserRequest; -import ru.practicum.ewm.dto.user.UserDto; -import ru.practicum.ewm.dto.user.UserShortDto; -import ru.practicum.ewm.model.User; +import ru.practicum.aggregation.dto.user.NewUserRequest; +import ru.practicum.aggregation.dto.user.UserDto; +import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.ewm.users.entity.User; @UtilityClass public class UserMapper { @@ -32,4 +32,5 @@ public User toEntity(@NonNull NewUserRequest newUserRequest) { .email(newUserRequest.email()) .build(); } + } \ No newline at end of file diff --git a/core/main-service/src/main/java/ru/practicum/ewm/dao/UserRepository.java b/core/user-service/src/main/java/ru/practicum/ewm/users/repository/UserRepository.java similarity index 78% rename from core/main-service/src/main/java/ru/practicum/ewm/dao/UserRepository.java rename to core/user-service/src/main/java/ru/practicum/ewm/users/repository/UserRepository.java index b312916..ad71e36 100644 --- a/core/main-service/src/main/java/ru/practicum/ewm/dao/UserRepository.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/repository/UserRepository.java @@ -1,8 +1,8 @@ -package ru.practicum.ewm.dao; +package ru.practicum.ewm.users.repository; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; -import ru.practicum.ewm.model.User; +import ru.practicum.ewm.users.entity.User; import java.util.List; diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserService.java b/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserService.java new file mode 100644 index 0000000..b373110 --- /dev/null +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserService.java @@ -0,0 +1,26 @@ +package ru.practicum.ewm.users.service; + +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.aggregation.dto.user.NewUserRequest; +import ru.practicum.aggregation.dto.user.UserDto; +import ru.practicum.aggregation.dto.user.UserShortDto; + +import java.util.List; + +@Transactional(readOnly = true) +public interface UserService { + + @Transactional + UserDto createUser(NewUserRequest newUserRequest); + + UserDto getUser(long userId); + + void remoteCheckUser(long userId); + + List getShortUsersByIds(List userIds); + + List findUsers(List ids, int from, int size); + + @Transactional + void deleteUser(long userId); +} diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java b/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java new file mode 100644 index 0000000..66945d5 --- /dev/null +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java @@ -0,0 +1,81 @@ +package ru.practicum.ewm.users.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Service; +import ru.practicum.aggregation.dto.user.NewUserRequest; +import ru.practicum.aggregation.dto.user.UserDto; +import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.error.exception.ConflictException; +import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.ewm.users.entity.User; +import ru.practicum.ewm.users.mapper.UserMapper; +import ru.practicum.ewm.users.repository.UserRepository; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class UserServiceImpl implements UserService { + + private final UserRepository userRepository; + + @Override + public UserDto createUser(@NonNull NewUserRequest newUserRequest) { + if (userRepository.existsByEmail(newUserRequest.email())) { + throw new ConflictException( + "Пользователь с такой почтой " + newUserRequest.email() + " уже существует"); + } + User user = UserMapper.toEntity(newUserRequest); + return UserMapper.toUserDto(userRepository.save(user)); + } + + @Override + public UserDto getUser(long userId) { + return UserMapper.toUserDto(userRepository.findById(userId).orElseThrow(() -> + new NotFoundException("Пользователь с id=%s не найден".formatted(userId)) + ) + ); + } + + @Override + public void remoteCheckUser(long userId) { + checkUser(userId); + } + + @Override + public List getShortUsersByIds(List userIds) { + return userRepository.findAllById(userIds).stream() + .map(UserMapper::toUserShortDto) + .toList(); + } + + @Override + public List findUsers(List ids, int from, int size) { + PageRequest page = PageRequest.of(from / size, size); + List users; + if (ids == null || ids.isEmpty()) { + users = userRepository.findAll(page).getContent(); + } else { + users = userRepository.findAllByIdIn(ids, page); + } + return users.stream() + .map(UserMapper::toUserDto) + .collect(Collectors.toList()); + } + + @Override + public void deleteUser(long userId) { + checkUser(userId); + userRepository.deleteById(userId); + } + + private void checkUser(long userId) { + if (!userRepository.existsById(userId)) { + throw new NotFoundException("Пользователь с id=%s не найден".formatted(userId)); + } + } + +} diff --git a/core/user-service/src/main/resources/application.yml b/core/user-service/src/main/resources/application.yml new file mode 100644 index 0000000..1ef89d8 --- /dev/null +++ b/core/user-service/src/main/resources/application.yml @@ -0,0 +1,31 @@ +server: + port: 0 + +spring: + application: + name: user-service + config: + import: "optional:configserver:" + cloud: + config: + discovery: + enabled: true + service-id: config-server + fail-fast: true + retry: + initial-interval: 1000 + max-interval: 10000 + multiplier: 2.0 + max-attempts: 20 + +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8761/eureka/ + register-with-eureka: true + fetch-registry: true + instance: + prefer-ip-address: true + instance-id: ${spring.application.name}:${random.value} + lease-renewal-interval-in-seconds: 5 + lease-expiration-duration-in-seconds: 10 diff --git a/core/user-service/src/main/resources/schema-postgres.sql b/core/user-service/src/main/resources/schema-postgres.sql new file mode 100644 index 0000000..63894cb --- /dev/null +++ b/core/user-service/src/main/resources/schema-postgres.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS users ( + id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + email varchar(255) NOT NULL, + name varchar(255) NOT NULL, + CONSTRAINT pk_users PRIMARY KEY (id) +); +CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email ON users (email); diff --git a/docker-compose.yml b/docker-compose.yml index 7245a40..4a7e415 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,7 @@ +volumes: + postgres_main-db_data: + postgres_stats-db_data: + services: main-db: image: postgres:16.1 @@ -8,10 +12,13 @@ services: - POSTGRES_DB=ewm-main - POSTGRES_USER=dbuser - POSTGRES_PASSWORD=12345 + volumes: + - ./docker-init.sql:/docker-entrypoint-initdb.d/docker-init.sql:ro + - postgres_main-db_data:/var/lib/postgresql/data healthcheck: - test: pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER - timeout: 5s + test: [ "CMD-SHELL", "pg_isready -U dbuser -d ewm-main" ] interval: 5s + timeout: 5s retries: 10 stats-db: @@ -23,6 +30,8 @@ services: - POSTGRES_DB=ewm-stats - POSTGRES_USER=dbuser - POSTGRES_PASSWORD=12345 + volumes: + - postgres_stats-db_data:/var/lib/postgresql/data healthcheck: test: pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER timeout: 5s diff --git a/docker-init.sql b/docker-init.sql new file mode 100644 index 0000000..731876d --- /dev/null +++ b/docker-init.sql @@ -0,0 +1,20 @@ +CREATE DATABASE "ewm-users"; +CREATE DATABASE "ewm-events"; +CREATE DATABASE "ewm-requests"; +CREATE DATABASE "ewm-ratings"; + +\c "ewm-users" +CREATE SCHEMA IF NOT EXISTS user_service; +GRANT ALL PRIVILEGES ON SCHEMA user_service TO dbuser; + +\c "ewm-events" +CREATE SCHEMA IF NOT EXISTS event_service; +GRANT ALL PRIVILEGES ON SCHEMA event_service TO dbuser; + +\c "ewm-requests" +CREATE SCHEMA IF NOT EXISTS request_service; +GRANT ALL PRIVILEGES ON SCHEMA request_service TO dbuser; + +\c "ewm-ratings" +CREATE SCHEMA IF NOT EXISTS rating_service; +GRANT ALL PRIVILEGES ON SCHEMA rating_service TO dbuser; diff --git a/infra/config-repo/core/event-service.yml b/infra/config-repo/core/event-service.yml new file mode 100644 index 0000000..4cee133 --- /dev/null +++ b/infra/config-repo/core/event-service.yml @@ -0,0 +1,52 @@ +spring: + sql.init: + mode: ALWAYS + schema-locations: classpath:schema-postgres.sql + jackson: + serialization.INDENT_OUTPUT: true + datasource: + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:6541/ewm-events?currentSchema=event_service + username: dbuser + password: 12345 + jpa: + generate-ddl: false + hibernate.ddl-auto: validate + show-sql: false + properties: + hibernate: + format_sql: true + cloud: + openfeign: + client: + config: + default: + connectTimeout: 2000 + readTimeout: 3000 + loggerLevel: basic + circuitbreaker: + enabled: true + +resilience4j: + circuitbreaker: + configs: + default: + sliding-window-size: 5 + failure-rate-threshold: 50 + minimum-number-of-calls: 3 + slow-call-duration-threshold: 3s + slow-call-rate-threshold: 50 + wait-duration-in-open-state: 10s + permitted-number-of-calls-in-half-open-state: 3 + ignore-exceptions: + - ru.practicum.aggregation.error.exception.BusinessException + - jakarta.validation.ValidationException + timelimiter: + configs: + default: + timeout-duration: 3s + +#logging: +# org: +# zalando: +# logbook: trace \ No newline at end of file diff --git a/infra/config-repo/core/main-service.yaml b/infra/config-repo/core/main-service.yaml deleted file mode 100644 index df0e0e4..0000000 --- a/infra/config-repo/core/main-service.yaml +++ /dev/null @@ -1,23 +0,0 @@ -spring: - sql.init: - mode: ALWAYS - schema-locations: classpath:schema-postgres.sql - jackson: - serialization.INDENT_OUTPUT: true - datasource: - driver-class-name: org.postgresql.Driver - url: jdbc:postgresql://localhost:6541/ewm-main - username: dbuser - password: 12345 - jpa: - generate-ddl: false - hibernate.ddl-auto: validate - show-sql: true - properties: - hibernate: - format_sql: true - -logging: - org: - zalando: - logbook: trace diff --git a/infra/config-repo/core/rating-service.yml b/infra/config-repo/core/rating-service.yml new file mode 100644 index 0000000..4d4b25f --- /dev/null +++ b/infra/config-repo/core/rating-service.yml @@ -0,0 +1,52 @@ +spring: + sql.init: + mode: ALWAYS + schema-locations: classpath:schema-postgres.sql + jackson: + serialization.INDENT_OUTPUT: true + datasource: + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:6541/ewm-ratings?currentSchema=rating_service + username: dbuser + password: 12345 + jpa: + generate-ddl: false + hibernate.ddl-auto: validate + show-sql: false + properties: + hibernate: + format_sql: true + cloud: + openfeign: + client: + config: + default: + connectTimeout: 2000 + readTimeout: 3000 + loggerLevel: basic + circuitbreaker: + enabled: true + +resilience4j: + circuitbreaker: + configs: + default: + sliding-window-size: 5 + failure-rate-threshold: 50 + minimum-number-of-calls: 3 + slow-call-duration-threshold: 3s + slow-call-rate-threshold: 50 + wait-duration-in-open-state: 10s + permitted-number-of-calls-in-half-open-state: 3 + ignore-exceptions: + - ru.practicum.aggregation.error.exception.BusinessException + - jakarta.validation.ValidationException + timelimiter: + configs: + default: + timeout-duration: 3s + +#logging: +# org: +# zalando: +# logbook: trace diff --git a/infra/config-repo/core/request-service.yml b/infra/config-repo/core/request-service.yml new file mode 100644 index 0000000..5496a3c --- /dev/null +++ b/infra/config-repo/core/request-service.yml @@ -0,0 +1,52 @@ +spring: + sql.init: + mode: ALWAYS + schema-locations: classpath:schema-postgres.sql + jackson: + serialization.INDENT_OUTPUT: true + datasource: + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:6541/ewm-requests?currentSchema=request_service + username: dbuser + password: 12345 + jpa: + generate-ddl: false + hibernate.ddl-auto: validate + show-sql: false + properties: + hibernate: + format_sql: true + cloud: + openfeign: + client: + config: + default: + connectTimeout: 2000 + readTimeout: 3000 + loggerLevel: basic + circuitbreaker: + enabled: true + +resilience4j: + circuitbreaker: + configs: + default: + sliding-window-size: 5 + failure-rate-threshold: 50 + minimum-number-of-calls: 3 + slow-call-duration-threshold: 3s + slow-call-rate-threshold: 50 + wait-duration-in-open-state: 10s + permitted-number-of-calls-in-half-open-state: 3 + ignore-exceptions: + - ru.practicum.aggregation.error.exception.BusinessException + - jakarta.validation.ValidationException + timelimiter: + configs: + default: + timeout-duration: 3s + +#logging: +# org: +# zalando: +# logbook: trace diff --git a/infra/config-repo/core/user-service.yml b/infra/config-repo/core/user-service.yml new file mode 100644 index 0000000..590cfdf --- /dev/null +++ b/infra/config-repo/core/user-service.yml @@ -0,0 +1,52 @@ +spring: + sql.init: + mode: ALWAYS + schema-locations: classpath:schema-postgres.sql + jackson: + serialization.INDENT_OUTPUT: true + datasource: + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:6541/ewm-users?currentSchema=user_service + username: dbuser + password: 12345 + jpa: + generate-ddl: false + hibernate.ddl-auto: validate + show-sql: false + properties: + hibernate: + format_sql: true + cloud: + openfeign: + client: + config: + default: + connectTimeout: 2000 + readTimeout: 3000 + loggerLevel: basic + circuitbreaker: + enabled: true + +resilience4j: + circuitbreaker: + configs: + default: + sliding-window-size: 5 + failure-rate-threshold: 50 + minimum-number-of-calls: 3 + slow-call-duration-threshold: 3s + slow-call-rate-threshold: 50 + wait-duration-in-open-state: 10s + permitted-number-of-calls-in-half-open-state: 3 + ignore-exceptions: + - ru.practicum.aggregation.error.exception.BusinessException + - jakarta.validation.ValidationException + timelimiter: + configs: + default: + timeout-duration: 3s + +#logging: +# org: +# zalando: +# logbook: trace diff --git a/infra/config-repo/infra/gateway-server.yml b/infra/config-repo/infra/gateway-server.yml index 279928a..c41afc7 100644 --- a/infra/config-repo/infra/gateway-server.yml +++ b/infra/config-repo/infra/gateway-server.yml @@ -1,6 +1,3 @@ -server: - port: 8080 - spring: cloud: gateway: @@ -10,52 +7,52 @@ spring: routes: - id: ratings - uri: lb://main-service + uri: lb://rating-service predicates: - Path=/users/*/events/*/likes/** - id: user-requests - uri: lb://main-service + uri: lb://request-service predicates: - Path=/users/*/requests/** - id: user-events - uri: lb://main-service + uri: lb://event-service predicates: - Path=/users/*/events/** - id: admin-users - uri: lb://main-service + uri: lb://user-service predicates: - Path=/admin/users/** - id: admin-categories - uri: lb://main-service + uri: lb://event-service predicates: - Path=/admin/categories/** - id: admin-compilations - uri: lb://main-service + uri: lb://event-service predicates: - Path=/admin/compilations/** - id: admin-events - uri: lb://main-service + uri: lb://event-service predicates: - Path=/admin/events/** - id: categories - uri: lb://main-service + uri: lb://event-service predicates: - Path=/categories/** - id: compilations - uri: lb://main-service + uri: lb://event-service predicates: - Path=/compilations/** - id: events - uri: lb://main-service + uri: lb://event-service predicates: - Path=/events/** diff --git a/infra/gateway-server/src/main/resources/application.yml b/infra/gateway-server/src/main/resources/application.yml index 206f54c..63212b3 100644 --- a/infra/gateway-server/src/main/resources/application.yml +++ b/infra/gateway-server/src/main/resources/application.yml @@ -1,3 +1,6 @@ +server: + port: 8080 + spring: application: name: gateway-server diff --git a/pom.xml b/pom.xml index be45625..73091ef 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ stats infra core - + ru.practicum explore-with-me From 2de98a1ea29b6b08acde30baa8379ed01a47d933 Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Wed, 26 Aug 2026 22:50:26 +0300 Subject: [PATCH 03/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправление ошибки в EventFallbackFactory --- .../client/RemoteExceptionMapper.java | 12 ------------ .../client/event/EventFallbackFactory.java | 6 +++--- .../processing/RatingProcessingException.java | 11 ----------- .../RatingServiceUnavailableException.java | 16 ---------------- 4 files changed, 3 insertions(+), 42 deletions(-) delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RatingProcessingException.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RatingServiceUnavailableException.java diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java index 4d802c3..4719b28 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java @@ -4,11 +4,9 @@ import lombok.NonNull; import lombok.experimental.UtilityClass; import ru.practicum.aggregation.error.exception.processing.EventProcessingException; -import ru.practicum.aggregation.error.exception.processing.RatingProcessingException; import ru.practicum.aggregation.error.exception.processing.RequestProcessingException; import ru.practicum.aggregation.error.exception.processing.UserProcessingException; import ru.practicum.aggregation.error.exception.unavailable.EventServiceUnavailableException; -import ru.practicum.aggregation.error.exception.unavailable.RatingServiceUnavailableException; import ru.practicum.aggregation.error.exception.unavailable.RequestServiceUnavailableException; import ru.practicum.aggregation.error.exception.unavailable.UserServiceUnavailableException; @@ -45,14 +43,4 @@ public RuntimeException mapRequestException(@NonNull Throwable cause) { return new RequestServiceUnavailableException(cause); } - public RuntimeException mapRatingException(@NonNull Throwable cause) { - if (cause instanceof FeignException ex) { - return switch (ex.status()) { - case 400, 404 -> new RatingProcessingException(ex.getMessage()); - default -> new RatingServiceUnavailableException(ex); - }; - } - return new RatingServiceUnavailableException(cause); - } - } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java index 950c447..27a5b5a 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java @@ -16,17 +16,17 @@ public EventClient create(Throwable cause) { @Override public EventFullDto userFindEventById(Long userId, Long eventId) { - throw RemoteExceptionMapper.mapRequestException(cause); + throw RemoteExceptionMapper.mapEventException(cause); } @Override public void systemUpdateRating(RatingUpdateRequest request) { - throw RemoteExceptionMapper.mapRequestException(cause); + throw RemoteExceptionMapper.mapEventException(cause); } @Override public EventFullDto systemFindEventById(Long eventId) { - throw RemoteExceptionMapper.mapRequestException(cause); + throw RemoteExceptionMapper.mapEventException(cause); } }; } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RatingProcessingException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RatingProcessingException.java deleted file mode 100644 index b37d1c4..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RatingProcessingException.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.practicum.aggregation.error.exception.processing; - -import ru.practicum.aggregation.error.exception.ServiceProcessingException; - -public class RatingProcessingException extends ServiceProcessingException { - - public RatingProcessingException(String message) { - super(message); - } - -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RatingServiceUnavailableException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RatingServiceUnavailableException.java deleted file mode 100644 index b078963..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RatingServiceUnavailableException.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.practicum.aggregation.error.exception.unavailable; - -import ru.practicum.aggregation.error.exception.ServiceUnavailableException; - -public class RatingServiceUnavailableException extends ServiceUnavailableException { - - public RatingServiceUnavailableException(Throwable cause) { - super(""" - Сервис рейтингов временно недоступен. - Повторите попытку позже. - """, - cause - ); - } - -} From c168bee642d2492bd2cbfa9de9f6bd3b4c53a406 Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Wed, 26 Aug 2026 23:13:15 +0300 Subject: [PATCH 04/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Рефакторинг EventMapper под использование EventData --- .../ewm/events/mapper/CompilationMapper.java | 10 +++++--- .../ewm/events/mapper/EventMapper.java | 11 ++++----- .../service/event/EventServiceImpl.java | 24 ++++++++++++------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CompilationMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CompilationMapper.java index e62dbce..d196147 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CompilationMapper.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CompilationMapper.java @@ -7,6 +7,7 @@ import ru.practicum.aggregation.dto.user.UserShortDto; import ru.practicum.ewm.events.entity.Compilation; import ru.practicum.ewm.events.entity.Event; +import ru.practicum.ewm.events.model.EventData; import java.util.HashSet; import java.util.Map; @@ -26,9 +27,12 @@ public CompilationDto toCompilationDto(@NonNull Compilation compilation, .events(compilation.getEvents().stream() .map(event -> EventMapper.toEventShortDto( event, - eventIdToInitiator.get(event.getId()), - confirmedRequests.getOrDefault(event.getId(), 0L), - views.getOrDefault(event.getId(), 0L) + EventData.builder() + .initiator(eventIdToInitiator.get(event.getId())) + .confirmedRequests(confirmedRequests + .getOrDefault(event.getId(), 0L)) + .views(views.getOrDefault(event.getId(), 0L)) + .build() )) .toList()) .build(); diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java index 98e0692..4047a22 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java @@ -7,7 +7,6 @@ import ru.practicum.aggregation.dto.event.request.UpdateEventUserRequest; import ru.practicum.aggregation.dto.event.response.EventFullDto; import ru.practicum.aggregation.dto.event.response.EventShortDto; -import ru.practicum.aggregation.dto.user.UserShortDto; import ru.practicum.aggregation.enums.EventState; import ru.practicum.ewm.events.entity.Category; import ru.practicum.ewm.events.entity.Event; @@ -20,19 +19,17 @@ public class EventMapper { public EventShortDto toEventShortDto(@NonNull Event event, - UserShortDto initiator, - long confirmedRequests, - long views) { + @NonNull EventData eventData) { return EventShortDto.builder() .annotation(event.getAnnotation()) .category(CategoryMapper.toDto(event.getCategory())) - .confirmedRequests(confirmedRequests) + .confirmedRequests(eventData.confirmedRequests()) .eventDate(event.getEventDate()) .id(event.getId()) - .initiator(initiator) + .initiator(eventData.initiator()) .paid(event.isPaid()) .title(event.getTitle()) - .views(views) + .views(eventData.views()) .rate(event.getRate()) .build(); } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java index 70f65af..4ecef4d 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java @@ -113,8 +113,8 @@ public List getFreeEvents(@NonNull FreeGetDto dto, HttpServletReq return Collections.emptyList(); } - List eventRequestCountList = requestFeignRepository.getConfirmedRequestsCount( - events.stream().map(Event::getId).toList()); + List eventRequestCountList = requestFeignRepository + .getConfirmedRequestsCount(events.stream().map(Event::getId).toList()); Map requestCountMap = new HashMap<>(); if (!eventRequestCountList.isEmpty()) { @@ -132,9 +132,12 @@ public List getFreeEvents(@NonNull FreeGetDto dto, HttpServletReq return events.stream() .map(event -> { var userId = requestCountMap.get(event.getInitiatorId()); - var initiator = initiators.get(userId); - var count = requestCountMap.getOrDefault(event.getId(), 0L); - return EventMapper.toEventShortDto(event, initiator, count, stats.size()); + var eventData = EventData.builder() + .initiator(initiators.get(userId)) + .confirmedRequests(requestCountMap.getOrDefault(event.getId(), 0L)) + .views(stats.size()) + .build(); + return EventMapper.toEventShortDto(event, eventData); }) .toList(); } @@ -347,10 +350,13 @@ public List findByUserId(Long userId, Integer from, Integer size) return events.stream() .map(event -> { - var initiator = initiators.get(userId); - var count = getConfirmedRequestsCountFromMap(requestCountMap, event.getId()); - return EventMapper.toEventShortDto( - event, initiator, count, getHits(stats, event.getId())); + var eventData = EventData.builder() + .initiator(initiators.get(userId)) + .confirmedRequests( + getConfirmedRequestsCountFromMap(requestCountMap, event.getId())) + .views(getHits(stats, event.getId())) + .build(); + return EventMapper.toEventShortDto(event, eventData); }) .toList(); } From 1278c73e1e65003e94bd1d8866156f37ba2971f1 Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Thu, 27 Aug 2026 14:54:14 +0300 Subject: [PATCH 05/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Рефакторинг структуры DTO Добавлен общий JacksonConfiguration Большинство тестов проходит --- core/aggregation-api/pom.xml | 7 ++++ .../aggregation/client/event/EventClient.java | 4 +-- .../client/event/EventFallbackFactory.java | 4 +-- .../client/request/RequestClient.java | 8 ++--- .../request/RequestFallbackFactory.java | 8 ++--- .../aggregation/client/user/UserClient.java | 4 +-- .../client/user/UserFallbackFactory.java | 4 +-- .../config/JacksonConfiguration.java} | 4 +-- .../category/{ => come}/NewCategoryDto.java | 2 +- .../category/{ => output}/CategoryDto.java | 2 +- .../dto/compilation/CompilationDto.java | 26 -------------- .../{ => come/create}/NewCompilationDto.java | 2 +- .../get}/CompilationSearchFilter.java | 2 +- .../update}/CompilationUpdateDto.java | 8 ++++- .../compilation/output/CompilationDto.java | 22 ++++++++++++ .../{request => come/create}/NewEventDto.java | 4 +-- .../update}/UpdateEventAdminRequest.java | 4 +-- .../update}/UpdateEventUserRequest.java | 4 +-- .../{response => output}/EventFullDto.java | 11 +++--- .../dto/event/output/EventShortDto.java | 35 +++++++++++++++++++ .../dto/event/response/EventShortDto.java | 22 ------------ .../EventRequestStatusUpdateRequest.java | 6 ++-- .../EventRequestStatusUpdateResult.java | 2 +- .../output/ParticipationRequestDto.java | 22 ++++++++++++ .../dto/rating/RatingResponse.java | 13 ------- .../create/RatingCreateRequest.java} | 4 +-- .../update}/RatingUpdateRequest.java | 2 +- .../dto/rating/output/RatingResponse.java | 20 +++++++++++ .../dto/request/ParticipationRequestDto.java | 22 ------------ .../aggregation/dto/user/UserDto.java | 17 --------- .../aggregation/dto/user/UserShortDto.java | 26 -------------- .../dto/user/{ => come}/NewUserRequest.java | 2 +- .../aggregation/dto/user/output/UserDto.java | 17 +++++++++ .../dto/user/output/UserShortDto.java | 15 ++++++++ .../data/AdminGetData.java} | 4 +-- .../data/FreeGetData.java} | 4 +-- .../model/{ => entity}/BaseEntity.java | 2 +- .../model/{ => entity}/Location.java | 2 +- .../repository}/EventRequestCount.java | 2 +- .../repository/EventFeignRepository.java | 4 +-- .../repository/EventFeignRepositoryImpl.java | 4 +-- .../repository/RequestFeignRepository.java | 8 ++--- .../RequestFeignRepositoryImpl.java | 8 ++--- .../repository/UserFeignRepository.java | 4 +-- .../repository/UserFeignRepositoryImpl.java | 4 +-- .../practicum/ewm/events/EventServiceApp.java | 4 +++ .../admin/AdminCategoryController.java | 4 +-- .../admin/AdminCompilationController.java | 6 ++-- .../admin/AdminEventController.java | 8 ++--- .../free/FreeCategoryController.java | 2 +- .../free/FreeCompilationController.java | 4 +-- .../controller/free/FreeEventController.java | 12 +++---- .../system/SystemEventController.java | 2 +- .../system/SystemRatingController.java | 2 +- .../controller/user/UserEventController.java | 14 ++++---- .../practicum/ewm/events/entity/Category.java | 2 +- .../ewm/events/entity/Compilation.java | 2 +- .../ru/practicum/ewm/events/entity/Event.java | 22 ++++++++++-- .../ewm/events/mapper/CategoryMapper.java | 4 +-- .../ewm/events/mapper/CompilationMapper.java | 6 ++-- .../ewm/events/mapper/EventMapper.java | 10 +++--- .../ewm/events/mapper/UserMapper.java | 16 ++------- .../practicum/ewm/events/model/EventData.java | 2 +- .../ru/practicum/ewm/events/model/User.java | 22 ++++-------- .../service/category/CategoryService.java | 4 +-- .../service/category/CategoryServiceImpl.java | 4 +-- .../compilation/CompilationService.java | 8 ++--- .../compilation/CompilationServiceImpl.java | 14 ++++---- .../events/service/event/EventService.java | 14 +++++--- .../service/event/EventServiceImpl.java | 25 +++++++------ .../events/service/rating/RatingService.java | 2 +- .../service/rating/RatingServiceImpl.java | 2 +- .../service/request/RequestService.java | 6 ++-- .../service/request/RequestServiceImpl.java | 6 ++-- .../specification/EventSpecifications.java | 4 +-- .../ewm/ratings/RatingServiceApp.java | 4 +++ .../ratings/controller/RatingController.java | 6 ++-- .../practicum/ewm/ratings/entity/Rating.java | 2 +- .../ewm/ratings/service/RatingService.java | 6 ++-- .../ratings/service/RatingServiceImpl.java | 8 ++--- .../ewm/requests/RequestServiceApp.java | 4 +++ .../controller/AdminRequestController.java | 2 +- .../controller/UserRequestController.java | 6 ++-- .../requests/entity/ParticipationRequest.java | 2 +- .../ewm/requests/mapper/RequestMapper.java | 2 +- .../ewm/requests/mapper/UserMapper.java | 2 +- .../ru/practicum/ewm/requests/model/User.java | 2 +- .../repository/RequestRepository.java | 2 +- .../ewm/requests/service/RequestService.java | 8 ++--- .../requests/service/RequestServiceImpl.java | 10 +++--- .../practicum/ewm/users/UserServiceApp.java | 4 +++ .../users/controller/AdminUserController.java | 6 ++-- .../ru/practicum/ewm/users/entity/User.java | 2 +- .../ewm/users/mapper/UserMapper.java | 7 ++-- .../ewm/users/service/UserService.java | 6 ++-- .../ewm/users/service/UserServiceImpl.java | 6 ++-- 96 files changed, 384 insertions(+), 347 deletions(-) rename core/{event-service/src/main/java/ru/practicum/ewm/events/config/EventServiceConfig.java => aggregation-api/src/main/java/ru/practicum/aggregation/config/JacksonConfiguration.java} (91%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/{ => come}/NewCategoryDto.java (80%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/{ => output}/CategoryDto.java (84%) delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationDto.java rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/{ => come/create}/NewCompilationDto.java (88%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/{ => come/get}/CompilationSearchFilter.java (83%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/{ => come/update}/CompilationUpdateDto.java (64%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/output/CompilationDto.java rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/{request => come/create}/NewEventDto.java (88%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/{request => come/update}/UpdateEventAdminRequest.java (82%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/{request => come/update}/UpdateEventUserRequest.java (82%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/{response => output}/EventFullDto.java (83%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/output/EventShortDto.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventShortDto.java rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/{request => participation/come}/EventRequestStatusUpdateRequest.java (64%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/{request => participation/output}/EventRequestStatusUpdateResult.java (86%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/ParticipationRequestDto.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingResponse.java rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/{RatingRequest.java => come/create/RatingCreateRequest.java} (64%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/{ => come/update}/RatingUpdateRequest.java (84%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/output/RatingResponse.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/ParticipationRequestDto.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserDto.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserShortDto.java rename core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/{ => come}/NewUserRequest.java (88%) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/output/UserDto.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/output/UserShortDto.java rename core/aggregation-api/src/main/java/ru/practicum/aggregation/{dto/event/request/AdminGetDto.java => model/data/AdminGetData.java} (84%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/{dto/event/request/FreeGetDto.java => model/data/FreeGetData.java} (87%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/model/{ => entity}/BaseEntity.java (95%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/model/{ => entity}/Location.java (77%) rename core/aggregation-api/src/main/java/ru/practicum/aggregation/{dto/request => model/repository}/EventRequestCount.java (74%) diff --git a/core/aggregation-api/pom.xml b/core/aggregation-api/pom.xml index b6f4eff..205e35e 100644 --- a/core/aggregation-api/pom.xml +++ b/core/aggregation-api/pom.xml @@ -26,11 +26,18 @@ jakarta.persistence jakarta.persistence-api + org.hibernate.orm hibernate-core + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.springframework.cloud diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java index c9cb5a7..e0beb50 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java @@ -8,8 +8,8 @@ import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; -import ru.practicum.aggregation.dto.event.response.EventFullDto; -import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; @FeignClient( name = "event-service", diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java index 27a5b5a..68b7e02 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java @@ -3,8 +3,8 @@ import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.stereotype.Component; import ru.practicum.aggregation.client.RemoteExceptionMapper; -import ru.practicum.aggregation.dto.event.response.EventFullDto; -import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; @Component public class EventFallbackFactory implements FallbackFactory { diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java index 5120413..863e724 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java @@ -4,10 +4,10 @@ import jakarta.validation.constraints.Positive; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.request.EventRequestCount; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.model.repository.EventRequestCount; +import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.aggregation.enums.ParticipationStatus; import java.util.List; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java index 0e378f2..88bd0d4 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java @@ -3,10 +3,10 @@ import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.stereotype.Component; import ru.practicum.aggregation.client.RemoteExceptionMapper; -import ru.practicum.aggregation.dto.request.EventRequestCount; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.model.repository.EventRequestCount; +import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.aggregation.enums.ParticipationStatus; import java.util.List; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java index 59b2f80..ac722e3 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java @@ -4,8 +4,8 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; -import ru.practicum.aggregation.dto.user.UserDto; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.user.output.UserDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import java.util.List; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserFallbackFactory.java index 02902e0..30273d2 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserFallbackFactory.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserFallbackFactory.java @@ -3,8 +3,8 @@ import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.stereotype.Component; import ru.practicum.aggregation.client.RemoteExceptionMapper; -import ru.practicum.aggregation.dto.user.UserDto; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.user.output.UserDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import java.util.List; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/config/EventServiceConfig.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/config/JacksonConfiguration.java similarity index 91% rename from core/event-service/src/main/java/ru/practicum/ewm/events/config/EventServiceConfig.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/config/JacksonConfiguration.java index 1fc9973..b87276d 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/config/EventServiceConfig.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/config/JacksonConfiguration.java @@ -1,4 +1,4 @@ -package ru.practicum.ewm.events.config; +package ru.practicum.aggregation.config; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; @@ -9,7 +9,7 @@ import java.time.format.DateTimeFormatter; @SpringBootConfiguration -public class EventServiceConfig { +public class JacksonConfiguration { @Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/NewCategoryDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/come/NewCategoryDto.java similarity index 80% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/NewCategoryDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/come/NewCategoryDto.java index d8e6cc0..e3252bf 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/NewCategoryDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/come/NewCategoryDto.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.category; +package ru.practicum.aggregation.dto.category.come; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/CategoryDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/output/CategoryDto.java similarity index 84% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/CategoryDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/output/CategoryDto.java index f815385..5dfdf7d 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/CategoryDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/output/CategoryDto.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.category; +package ru.practicum.aggregation.dto.category.output; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationDto.java deleted file mode 100644 index 4f92a8f..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.practicum.aggregation.dto.compilation; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.aggregation.dto.event.response.EventShortDto; - -import java.util.List; - -/** - * Long id
- * boolean pinned
- * String title
- * List<{@link EventShortDto}> events - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class CompilationDto { - private Long id; - private boolean pinned; - private String title; - private List events; -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/NewCompilationDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/create/NewCompilationDto.java similarity index 88% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/NewCompilationDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/create/NewCompilationDto.java index b1f8eb5..452da4a 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/NewCompilationDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/create/NewCompilationDto.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.compilation; +package ru.practicum.aggregation.dto.compilation.come.create; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationSearchFilter.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/get/CompilationSearchFilter.java similarity index 83% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationSearchFilter.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/get/CompilationSearchFilter.java index 7ae7fd8..c29c638 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationSearchFilter.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/get/CompilationSearchFilter.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.compilation; +package ru.practicum.aggregation.dto.compilation.come.get; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationUpdateDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/update/CompilationUpdateDto.java similarity index 64% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationUpdateDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/update/CompilationUpdateDto.java index b8182fa..e9e09e1 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/CompilationUpdateDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/update/CompilationUpdateDto.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.compilation; +package ru.practicum.aggregation.dto.compilation.come.update; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; @@ -8,6 +8,12 @@ import java.util.Set; +/** + * @apiNote + * {@link Set}<{@link Long}> events
+ * {@link Boolean} pinned
+ * {@link String} title + */ @Data @Builder @AllArgsConstructor diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/output/CompilationDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/output/CompilationDto.java new file mode 100644 index 0000000..9723569 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/output/CompilationDto.java @@ -0,0 +1,22 @@ +package ru.practicum.aggregation.dto.compilation.output; + +import lombok.Builder; +import ru.practicum.aggregation.dto.event.output.EventShortDto; + +import java.util.List; + +/** + * @apiNote + * Long id
+ * boolean pinned
+ * String title
+ * List<{@link EventShortDto}> events + */ +@Builder +public record CompilationDto( + Long id, + boolean pinned, + String title, + List events +) { +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/NewEventDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/create/NewEventDto.java similarity index 88% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/NewEventDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/create/NewEventDto.java index 1a067df..fecf8a5 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/NewEventDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/create/NewEventDto.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.event.request; +package ru.practicum.aggregation.dto.event.come.create; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -7,7 +7,7 @@ import lombok.Data; import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; -import ru.practicum.aggregation.model.Location; +import ru.practicum.aggregation.model.entity.Location; import java.time.LocalDateTime; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventAdminRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/update/UpdateEventAdminRequest.java similarity index 82% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventAdminRequest.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/update/UpdateEventAdminRequest.java index 492150d..a011313 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventAdminRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/update/UpdateEventAdminRequest.java @@ -1,9 +1,9 @@ -package ru.practicum.aggregation.dto.event.request; +package ru.practicum.aggregation.dto.event.come.update; import jakarta.validation.constraints.Size; import lombok.Builder; import ru.practicum.aggregation.enums.AdminStateAction; -import ru.practicum.aggregation.model.Location; +import ru.practicum.aggregation.model.entity.Location; import java.time.LocalDateTime; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventUserRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/update/UpdateEventUserRequest.java similarity index 82% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventUserRequest.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/update/UpdateEventUserRequest.java index a928f84..0294d96 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/UpdateEventUserRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/update/UpdateEventUserRequest.java @@ -1,9 +1,9 @@ -package ru.practicum.aggregation.dto.event.request; +package ru.practicum.aggregation.dto.event.come.update; import jakarta.validation.constraints.Size; import lombok.Builder; import ru.practicum.aggregation.enums.UserStateAction; -import ru.practicum.aggregation.model.Location; +import ru.practicum.aggregation.model.entity.Location; import java.time.LocalDateTime; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventFullDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/output/EventFullDto.java similarity index 83% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventFullDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/output/EventFullDto.java index 6792e2f..492adf3 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventFullDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/output/EventFullDto.java @@ -1,18 +1,17 @@ -package ru.practicum.aggregation.dto.event.response; +package ru.practicum.aggregation.dto.event.output; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Builder; -import ru.practicum.aggregation.dto.category.CategoryDto; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.category.output.CategoryDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.aggregation.enums.EventState; -import ru.practicum.aggregation.model.Location; +import ru.practicum.aggregation.model.entity.Location; import java.time.LocalDateTime; /** - * @apiNote - * Long id
+ * @apiNote Long id
* String annotation
* CategoryDto category
* Long confirmedRequests
diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/output/EventShortDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/output/EventShortDto.java new file mode 100644 index 0000000..d074fe0 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/output/EventShortDto.java @@ -0,0 +1,35 @@ +package ru.practicum.aggregation.dto.event.output; + +import lombok.Builder; +import ru.practicum.aggregation.dto.category.output.CategoryDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; + +import java.time.LocalDateTime; + +/** + * @apiNote + * long id
+ * {@link String} annotation
+ * {@link CategoryDto} category
+ * long confirmedRequests
+ * {@link LocalDateTime} eventDate
+ * {@link UserShortDto} initiator
+ * boolean paid
+ * {@link String} title
+ * long views
+ * long rate + */ +@Builder +public record EventShortDto( + long id, + String annotation, + CategoryDto category, + long confirmedRequests, + LocalDateTime eventDate, + UserShortDto initiator, + boolean paid, + String title, + long views, + long rate +) { +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventShortDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventShortDto.java deleted file mode 100644 index 3033b53..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/response/EventShortDto.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.practicum.aggregation.dto.event.response; - -import lombok.Builder; -import ru.practicum.aggregation.dto.category.CategoryDto; -import ru.practicum.aggregation.dto.user.UserShortDto; - -import java.time.LocalDateTime; - -@Builder -public record EventShortDto( - long id, - String annotation, - CategoryDto category, - long confirmedRequests, - LocalDateTime eventDate, - UserShortDto initiator, - boolean paid, - String title, - long views, - long rate -) { -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/come/EventRequestStatusUpdateRequest.java similarity index 64% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateRequest.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/come/EventRequestStatusUpdateRequest.java index 5af1364..6625add 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/come/EventRequestStatusUpdateRequest.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.request; +package ru.practicum.aggregation.dto.participation.come; import jakarta.validation.constraints.NotNull; import lombok.Builder; @@ -8,8 +8,8 @@ /** * @apiNote - * List<{@link Long}> requestIds
- * {@link RequestUpdateStatus} status + * {@link List}<{@link Long}> requestIds
+ * {@link RequestUpdateStatus} status */ @Builder public record EventRequestStatusUpdateRequest( diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateResult.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/EventRequestStatusUpdateResult.java similarity index 86% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateResult.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/EventRequestStatusUpdateResult.java index 810edca..d38da61 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestStatusUpdateResult.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/EventRequestStatusUpdateResult.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.request; +package ru.practicum.aggregation.dto.participation.output; import lombok.Builder; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/ParticipationRequestDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/ParticipationRequestDto.java new file mode 100644 index 0000000..b5fe351 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/ParticipationRequestDto.java @@ -0,0 +1,22 @@ +package ru.practicum.aggregation.dto.participation.output; + +import lombok.Builder; +import ru.practicum.aggregation.enums.ParticipationStatus; + +/** + * @apiNote + * {@link Long} id
+ * {@link String} created
+ * {@link Long} event
+ * {@link Long} requester
+ * {@link ParticipationStatus} status + */ +@Builder +public record ParticipationRequestDto( + Long id, + String created, + Long event, + Long requester, + ParticipationStatus status +) { +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingResponse.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingResponse.java deleted file mode 100644 index d9398fb..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.practicum.aggregation.dto.rating; - -import lombok.Builder; -import ru.practicum.aggregation.enums.Reaction; - -@Builder -public record RatingResponse( - Long id, - Long userId, - Long eventId, - Reaction reaction -) { -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/come/create/RatingCreateRequest.java similarity index 64% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingRequest.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/come/create/RatingCreateRequest.java index 30cee2c..3dd7002 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/come/create/RatingCreateRequest.java @@ -1,11 +1,11 @@ -package ru.practicum.aggregation.dto.rating; +package ru.practicum.aggregation.dto.rating.come.create; import jakarta.validation.constraints.NotNull; import lombok.Builder; import ru.practicum.aggregation.enums.Reaction; @Builder -public record RatingRequest( +public record RatingCreateRequest( @NotNull Reaction reaction diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingUpdateRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/come/update/RatingUpdateRequest.java similarity index 84% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingUpdateRequest.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/come/update/RatingUpdateRequest.java index d9a51a1..02cad12 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/RatingUpdateRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/come/update/RatingUpdateRequest.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.rating; +package ru.practicum.aggregation.dto.rating.come.update; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/output/RatingResponse.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/output/RatingResponse.java new file mode 100644 index 0000000..072a729 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/output/RatingResponse.java @@ -0,0 +1,20 @@ +package ru.practicum.aggregation.dto.rating.output; + +import lombok.Builder; +import ru.practicum.aggregation.enums.Reaction; + +/** + * @apiNote + * Long id
+ * Long userId
+ * Long eventId
+ * {@link Reaction} reaction + */ +@Builder +public record RatingResponse( + Long id, + Long userId, + Long eventId, + Reaction reaction +) { +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/ParticipationRequestDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/ParticipationRequestDto.java deleted file mode 100644 index 0e77ccb..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/ParticipationRequestDto.java +++ /dev/null @@ -1,22 +0,0 @@ -package ru.practicum.aggregation.dto.request; - -import lombok.Builder; -import ru.practicum.aggregation.enums.ParticipationStatus; - -/** - * @apiNote - * {@link Long} id
- * {@link String} created
- * {@link Long} event
- * {@link Long} requester
- * {@link ParticipationStatus} status
- */ -@Builder -public record ParticipationRequestDto( - Long id, - String created, - Long event, - Long requester, - ParticipationStatus status -) { -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserDto.java deleted file mode 100644 index 8a862b2..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserDto.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.practicum.aggregation.dto.user; - -import lombok.Builder; - -/** - * @apiNote - * Long id
- * String name
- * String email - */ -@Builder -public record UserDto( - Long id, - String name, - String email -) { -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserShortDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserShortDto.java deleted file mode 100644 index 7123cb3..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/UserShortDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.practicum.aggregation.dto.user; - -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Size; -import lombok.Builder; - -/** - * @apiNote Long id
- * String name
- * String email - */ -@Builder -public record UserShortDto( - - Long id, - - @NotBlank - String name, - - @Email - @Size(min = 6, max = 255) - @NotBlank - String email -) { -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/NewUserRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/come/NewUserRequest.java similarity index 88% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/NewUserRequest.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/come/NewUserRequest.java index 2ab60b5..e70262c 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/NewUserRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/come/NewUserRequest.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.user; +package ru.practicum.aggregation.dto.user.come; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/output/UserDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/output/UserDto.java new file mode 100644 index 0000000..5c381ff --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/output/UserDto.java @@ -0,0 +1,17 @@ +package ru.practicum.aggregation.dto.user.output; + +import lombok.Builder; + +/** + * @apiNote + * {@link Long} id
+ * {@link String} name
+ * {@link String} email + */ +@Builder +public record UserDto( + Long id, + String name, + String email +) { +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/output/UserShortDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/output/UserShortDto.java new file mode 100644 index 0000000..6a4398e --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/output/UserShortDto.java @@ -0,0 +1,15 @@ +package ru.practicum.aggregation.dto.user.output; + +import lombok.Builder; + +/** + * @apiNote + * Long id
+ * String name + */ +@Builder +public record UserShortDto( + Long id, + String name +) { +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/AdminGetDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/AdminGetData.java similarity index 84% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/AdminGetDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/AdminGetData.java index d0db3f0..3b10340 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/AdminGetDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/AdminGetData.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.event.request; +package ru.practicum.aggregation.model.data; import lombok.Builder; import ru.practicum.aggregation.enums.EventState; @@ -17,7 +17,7 @@ * @param size */ @Builder -public record AdminGetDto( +public record AdminGetData( List users, List states, List categories, diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/FreeGetDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/FreeGetData.java similarity index 87% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/FreeGetDto.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/FreeGetData.java index f388b7a..3a8df93 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/request/FreeGetDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/FreeGetData.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.event.request; +package ru.practicum.aggregation.model.data; import jakarta.validation.constraints.NotNull; import lombok.Builder; @@ -20,7 +20,7 @@ */ @NotNull @Builder -public record FreeGetDto( +public record FreeGetData( String text, List categories, Boolean paid, diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/BaseEntity.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/entity/BaseEntity.java similarity index 95% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/model/BaseEntity.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/model/entity/BaseEntity.java index ba33c63..f68622e 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/BaseEntity.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/entity/BaseEntity.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.model; +package ru.practicum.aggregation.model.entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/Location.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/entity/Location.java similarity index 77% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/model/Location.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/model/entity/Location.java index 7bd05a7..e004da6 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/Location.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/entity/Location.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.model; +package ru.practicum.aggregation.model.entity; import lombok.Builder; import lombok.Data; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestCount.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java similarity index 74% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestCount.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java index fd4c948..f701fae 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/request/EventRequestCount.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.dto.request; +package ru.practicum.aggregation.model.repository; /** * @apiNote diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java index c236fa3..04441ea 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java @@ -1,8 +1,8 @@ package ru.practicum.aggregation.repository; import org.springframework.lang.NonNull; -import ru.practicum.aggregation.dto.event.response.EventFullDto; -import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; public interface EventFeignRepository { diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java index d9dc7e7..2abc917 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java @@ -9,8 +9,8 @@ import ru.practicum.aggregation.client.RemoteCallExecutor; import ru.practicum.aggregation.client.RemoteCallResult; import ru.practicum.aggregation.client.event.EventClient; -import ru.practicum.aggregation.dto.event.response.EventFullDto; -import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; import ru.practicum.aggregation.error.exception.processing.UserProcessingException; import ru.practicum.aggregation.error.exception.unavailable.UserServiceUnavailableException; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java index 6e9c430..91ebad9 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java @@ -1,9 +1,9 @@ package ru.practicum.aggregation.repository; -import ru.practicum.aggregation.dto.request.EventRequestCount; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.model.repository.EventRequestCount; +import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import java.util.List; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java index 692d38d..77ad41b 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java @@ -8,10 +8,10 @@ import ru.practicum.aggregation.client.RemoteCallExecutor; import ru.practicum.aggregation.client.RemoteCallResult; import ru.practicum.aggregation.client.request.RequestClient; -import ru.practicum.aggregation.dto.request.EventRequestCount; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.model.repository.EventRequestCount; +import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.aggregation.enums.ParticipationStatus; import ru.practicum.aggregation.error.exception.processing.RequestProcessingException; import ru.practicum.aggregation.error.exception.unavailable.RequestServiceUnavailableException; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepository.java index d68ea0b..9ce82eb 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepository.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepository.java @@ -1,7 +1,7 @@ package ru.practicum.aggregation.repository; -import ru.practicum.aggregation.dto.user.UserDto; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.user.output.UserDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import java.util.List; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java index 7113602..90fd64a 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java @@ -9,8 +9,8 @@ import ru.practicum.aggregation.client.RemoteCallExecutor; import ru.practicum.aggregation.client.RemoteCallResult; import ru.practicum.aggregation.client.user.UserClient; -import ru.practicum.aggregation.dto.user.UserDto; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.user.output.UserDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.aggregation.error.exception.processing.UserProcessingException; import ru.practicum.aggregation.error.exception.unavailable.UserServiceUnavailableException; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java b/core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java index c1c1862..d3f291a 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java @@ -2,11 +2,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.ComponentScan; import ru.practicum.aggregation.client.event.EventClient; import ru.practicum.aggregation.client.request.RequestClient; import ru.practicum.aggregation.client.user.UserClient; +import ru.practicum.aggregation.config.JacksonConfiguration; @SpringBootApplication @EnableFeignClients(clients = { @@ -17,10 +19,12 @@ @ComponentScan(value = { "ru.practicum.ewm.events", "ru.practicum.stat.client", + "ru.practicum.aggregation.config", "ru.practicum.aggregation.client", "ru.practicum.aggregation.error", "ru.practicum.aggregation.repository" }) +@ConfigurationPropertiesScan(basePackageClasses = JacksonConfiguration.class) public class EventServiceApp { public static void main(String[] args) { SpringApplication.run(EventServiceApp.class, args); diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCategoryController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCategoryController.java index 3175d67..e800010 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCategoryController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCategoryController.java @@ -4,8 +4,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.category.CategoryDto; -import ru.practicum.aggregation.dto.category.NewCategoryDto; +import ru.practicum.aggregation.dto.category.output.CategoryDto; +import ru.practicum.aggregation.dto.category.come.NewCategoryDto; import ru.practicum.ewm.events.service.category.CategoryService; @RestController diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCompilationController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCompilationController.java index a2f6ac6..5c4a1d4 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCompilationController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCompilationController.java @@ -4,9 +4,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.compilation.CompilationDto; -import ru.practicum.aggregation.dto.compilation.CompilationUpdateDto; -import ru.practicum.aggregation.dto.compilation.NewCompilationDto; +import ru.practicum.aggregation.dto.compilation.output.CompilationDto; +import ru.practicum.aggregation.dto.compilation.come.update.CompilationUpdateDto; +import ru.practicum.aggregation.dto.compilation.come.create.NewCompilationDto; import ru.practicum.ewm.events.service.compilation.CompilationService; @RestController diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminEventController.java index 11dd290..f5fe408 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminEventController.java @@ -4,9 +4,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.event.request.AdminGetDto; -import ru.practicum.aggregation.dto.event.response.EventFullDto; -import ru.practicum.aggregation.dto.event.request.UpdateEventAdminRequest; +import ru.practicum.aggregation.model.data.AdminGetData; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.dto.event.come.update.UpdateEventAdminRequest; import ru.practicum.aggregation.enums.EventState; import ru.practicum.ewm.events.service.event.EventService; @@ -63,7 +63,7 @@ public List adminGetEvents( Integer size ) { - AdminGetDto getDto = AdminGetDto.builder() + AdminGetData getDto = AdminGetData.builder() .users(users) .states(states) .categories(categories) diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCategoryController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCategoryController.java index e9a3dc9..3b408e7 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCategoryController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCategoryController.java @@ -5,7 +5,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.category.CategoryDto; +import ru.practicum.aggregation.dto.category.output.CategoryDto; import ru.practicum.ewm.events.service.category.CategoryService; import java.util.List; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCompilationController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCompilationController.java index a0680f3..16f2022 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCompilationController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCompilationController.java @@ -3,8 +3,8 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.compilation.CompilationDto; -import ru.practicum.aggregation.dto.compilation.CompilationSearchFilter; +import ru.practicum.aggregation.dto.compilation.output.CompilationDto; +import ru.practicum.aggregation.dto.compilation.come.get.CompilationSearchFilter; import ru.practicum.ewm.events.service.compilation.CompilationService; import java.util.List; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeEventController.java index 9f4de43..f3bf238 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeEventController.java @@ -4,9 +4,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.event.response.EventFullDto; -import ru.practicum.aggregation.dto.event.response.EventShortDto; -import ru.practicum.aggregation.dto.event.request.FreeGetDto; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.dto.event.output.EventShortDto; +import ru.practicum.aggregation.model.data.FreeGetData; import ru.practicum.ewm.events.service.event.EventService; import java.time.LocalDateTime; @@ -69,7 +69,7 @@ public List getFreeEvents( Boolean onlyAvailable, @RequestParam(required = false) - FreeGetDto.FreeEventSort sort, + FreeGetData.FreeEventSort sort, @RequestParam(required = false, defaultValue = "0") Integer from, @@ -79,7 +79,7 @@ public List getFreeEvents( HttpServletRequest request ) { - FreeGetDto freeGetDto = FreeGetDto.builder() + FreeGetData freeGetData = FreeGetData.builder() .text(text) .categories(categories) .paid(paid) @@ -91,7 +91,7 @@ public List getFreeEvents( .size(size) .build(); - return eventService.getFreeEvents(freeGetDto, request); + return eventService.getFreeEvents(freeGetData, request); } /** diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java index 81b35da..09cea82 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java @@ -9,7 +9,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import ru.practicum.aggregation.dto.event.response.EventFullDto; +import ru.practicum.aggregation.dto.event.output.EventFullDto; import ru.practicum.ewm.events.service.event.EventService; @Slf4j diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java index 4e3a714..c8cfd71 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java @@ -9,7 +9,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; import ru.practicum.ewm.events.service.rating.RatingService; @Slf4j diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java index 8f08a44..94917f7 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java @@ -6,13 +6,13 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.event.response.EventFullDto; -import ru.practicum.aggregation.dto.event.response.EventShortDto; -import ru.practicum.aggregation.dto.event.request.NewEventDto; -import ru.practicum.aggregation.dto.event.request.UpdateEventUserRequest; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.dto.event.output.EventShortDto; +import ru.practicum.aggregation.dto.event.come.create.NewEventDto; +import ru.practicum.aggregation.dto.event.come.update.UpdateEventUserRequest; +import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.ewm.events.service.event.EventService; import ru.practicum.ewm.events.service.request.RequestService; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Category.java b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Category.java index 2d4ba2b..9d849e2 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Category.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Category.java @@ -7,7 +7,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.SuperBuilder; -import ru.practicum.aggregation.model.BaseEntity; +import ru.practicum.aggregation.model.entity.BaseEntity; @Getter @Setter diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Compilation.java b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Compilation.java index a9d9a4b..f24e433 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Compilation.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Compilation.java @@ -4,7 +4,7 @@ import lombok.*; import lombok.experimental.FieldDefaults; import lombok.experimental.SuperBuilder; -import ru.practicum.aggregation.model.BaseEntity; +import ru.practicum.aggregation.model.entity.BaseEntity; import java.util.HashSet; import java.util.Set; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Event.java b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Event.java index bf5d112..dfb5b95 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Event.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Event.java @@ -12,12 +12,30 @@ import org.hibernate.annotations.Type; import org.hibernate.type.SqlTypes; import ru.practicum.aggregation.enums.EventState; -import ru.practicum.aggregation.model.BaseEntity; -import ru.practicum.aggregation.model.Location; +import ru.practicum.aggregation.model.entity.BaseEntity; +import ru.practicum.aggregation.model.entity.Location; import java.time.LocalDateTime; import java.util.List; +/** + * @apiNote + * {@link String} annotation
+ * {@link Category} category
+ * {@link LocalDateTime} createdOn
+ * {@link String} description
+ * {@link LocalDateTime} eventDate
+ * {@link Long} initiatorId
+ * {@link Location} location
+ * boolean paid
+ * int participantLimit
+ * {@link LocalDateTime} publishedOn
+ * boolean requestModeration
+ * {@link EventState} state
+ * {@link String} title
+ * {@link List}<{@link Compilation}> compilations
+ * long rate + */ @Getter @Setter @Entity diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CategoryMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CategoryMapper.java index 4fc2527..6a7a38d 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CategoryMapper.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CategoryMapper.java @@ -2,8 +2,8 @@ import lombok.experimental.UtilityClass; import org.springframework.lang.NonNull; -import ru.practicum.aggregation.dto.category.CategoryDto; -import ru.practicum.aggregation.dto.category.NewCategoryDto; +import ru.practicum.aggregation.dto.category.output.CategoryDto; +import ru.practicum.aggregation.dto.category.come.NewCategoryDto; import ru.practicum.ewm.events.entity.Category; @UtilityClass diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CompilationMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CompilationMapper.java index d196147..3078c24 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CompilationMapper.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/CompilationMapper.java @@ -2,9 +2,9 @@ import lombok.experimental.UtilityClass; import org.springframework.lang.NonNull; -import ru.practicum.aggregation.dto.compilation.CompilationDto; -import ru.practicum.aggregation.dto.compilation.NewCompilationDto; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.compilation.output.CompilationDto; +import ru.practicum.aggregation.dto.compilation.come.create.NewCompilationDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.ewm.events.entity.Compilation; import ru.practicum.ewm.events.entity.Event; import ru.practicum.ewm.events.model.EventData; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java index 4047a22..5247ded 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java @@ -2,11 +2,11 @@ import lombok.experimental.UtilityClass; import org.springframework.lang.NonNull; -import ru.practicum.aggregation.dto.event.request.NewEventDto; -import ru.practicum.aggregation.dto.event.request.UpdateEventAdminRequest; -import ru.practicum.aggregation.dto.event.request.UpdateEventUserRequest; -import ru.practicum.aggregation.dto.event.response.EventFullDto; -import ru.practicum.aggregation.dto.event.response.EventShortDto; +import ru.practicum.aggregation.dto.event.come.create.NewEventDto; +import ru.practicum.aggregation.dto.event.come.update.UpdateEventAdminRequest; +import ru.practicum.aggregation.dto.event.come.update.UpdateEventUserRequest; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.dto.event.output.EventShortDto; import ru.practicum.aggregation.enums.EventState; import ru.practicum.ewm.events.entity.Category; import ru.practicum.ewm.events.entity.Event; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/UserMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/UserMapper.java index beafc5e..555f175 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/UserMapper.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/UserMapper.java @@ -2,26 +2,16 @@ import lombok.experimental.UtilityClass; import org.springframework.lang.NonNull; -import ru.practicum.aggregation.dto.user.UserDto; -import ru.practicum.aggregation.dto.user.UserShortDto; -import ru.practicum.ewm.events.model.User; +import ru.practicum.aggregation.dto.user.output.UserDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; @UtilityClass public class UserMapper { - public UserShortDto toUserShortDto(@NonNull User user) { + public UserShortDto toUserShortDto(@NonNull UserDto userDto) { return UserShortDto.builder() - .id(user.getId()) - .name(user.getName()) - .email(user.getEmail()) - .build(); - } - - public User toEntity(@NonNull UserDto userDto) { - return User.builder() .id(userDto.id()) .name(userDto.name()) - .email(userDto.email()) .build(); } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/model/EventData.java b/core/event-service/src/main/java/ru/practicum/ewm/events/model/EventData.java index 3bb3cf5..49c8111 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/model/EventData.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/model/EventData.java @@ -1,7 +1,7 @@ package ru.practicum.ewm.events.model; import lombok.Builder; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; /** * @apiNote diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java b/core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java index e69bf2f..1cface3 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java @@ -1,19 +1,11 @@ package ru.practicum.ewm.events.model; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.experimental.FieldDefaults; -import lombok.experimental.SuperBuilder; -import ru.practicum.aggregation.model.BaseEntity; +import lombok.Builder; -@Getter -@Setter -@SuperBuilder -@NoArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE) -public class User extends BaseEntity { - String name; - String email; +@Builder +public record User( + Long id, + String name, + String email +) { } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryService.java index ed48928..714399a 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryService.java @@ -1,8 +1,8 @@ package ru.practicum.ewm.events.service.category; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.dto.category.CategoryDto; -import ru.practicum.aggregation.dto.category.NewCategoryDto; +import ru.practicum.aggregation.dto.category.output.CategoryDto; +import ru.practicum.aggregation.dto.category.come.NewCategoryDto; import java.util.List; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryServiceImpl.java index f8d664f..d5508c8 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryServiceImpl.java @@ -7,8 +7,8 @@ import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.dto.category.CategoryDto; -import ru.practicum.aggregation.dto.category.NewCategoryDto; +import ru.practicum.aggregation.dto.category.output.CategoryDto; +import ru.practicum.aggregation.dto.category.come.NewCategoryDto; import ru.practicum.aggregation.error.exception.ConflictException; import ru.practicum.aggregation.error.exception.NotFoundException; import ru.practicum.ewm.events.mapper.CategoryMapper; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationService.java index f47bd42..217f2be 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationService.java @@ -2,10 +2,10 @@ import jakarta.servlet.http.HttpServletRequest; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.dto.compilation.CompilationDto; -import ru.practicum.aggregation.dto.compilation.CompilationSearchFilter; -import ru.practicum.aggregation.dto.compilation.CompilationUpdateDto; -import ru.practicum.aggregation.dto.compilation.NewCompilationDto; +import ru.practicum.aggregation.dto.compilation.output.CompilationDto; +import ru.practicum.aggregation.dto.compilation.come.get.CompilationSearchFilter; +import ru.practicum.aggregation.dto.compilation.come.update.CompilationUpdateDto; +import ru.practicum.aggregation.dto.compilation.come.create.NewCompilationDto; import java.util.List; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java index a9aeba6..d3bbc86 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java @@ -8,14 +8,14 @@ import org.springframework.data.domain.PageRequest; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; -import ru.practicum.aggregation.dto.compilation.CompilationDto; -import ru.practicum.aggregation.dto.compilation.CompilationSearchFilter; -import ru.practicum.aggregation.dto.compilation.CompilationUpdateDto; -import ru.practicum.aggregation.dto.compilation.NewCompilationDto; -import ru.practicum.aggregation.dto.request.EventRequestCount; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.compilation.output.CompilationDto; +import ru.practicum.aggregation.dto.compilation.come.get.CompilationSearchFilter; +import ru.practicum.aggregation.dto.compilation.come.update.CompilationUpdateDto; +import ru.practicum.aggregation.dto.compilation.come.create.NewCompilationDto; +import ru.practicum.aggregation.model.repository.EventRequestCount; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.aggregation.error.exception.NotFoundException; -import ru.practicum.aggregation.model.BaseEntity; +import ru.practicum.aggregation.model.entity.BaseEntity; import ru.practicum.aggregation.repository.RequestFeignRepository; import ru.practicum.aggregation.repository.UserFeignRepository; import ru.practicum.ewm.events.mapper.CompilationMapper; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java index ceb7d90..c6fa2ca 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java @@ -2,9 +2,13 @@ import jakarta.servlet.http.HttpServletRequest; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.dto.event.request.*; -import ru.practicum.aggregation.dto.event.response.EventFullDto; -import ru.practicum.aggregation.dto.event.response.EventShortDto; +import ru.practicum.aggregation.dto.event.come.create.NewEventDto; +import ru.practicum.aggregation.model.data.AdminGetData; +import ru.practicum.aggregation.model.data.FreeGetData; +import ru.practicum.aggregation.dto.event.come.update.UpdateEventAdminRequest; +import ru.practicum.aggregation.dto.event.come.update.UpdateEventUserRequest; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.dto.event.output.EventShortDto; import java.util.List; @@ -12,7 +16,7 @@ public interface EventService { /// Получение событий незарегистрированным пользователем с возможностью фильтрации - List getFreeEvents(FreeGetDto freeGetDto, HttpServletRequest request); + List getFreeEvents(FreeGetData freeGetData, HttpServletRequest request); /** * Получение незарегистрированным пользователем подробной информации об опубликованном событии @@ -25,7 +29,7 @@ public interface EventService { EventFullDto userAddNewEvent(Long userId, NewEventDto newEventDto); /// Поиск событий администратором - List adminGetEvents(AdminGetDto adminGetDto); + List adminGetEvents(AdminGetData adminGetData); /// Редактирование администратором данных события и его статуса (отклонение/публикация) @Transactional diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java index 4ecef4d..bb08851 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java @@ -13,11 +13,16 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; -import ru.practicum.aggregation.dto.event.request.*; -import ru.practicum.aggregation.dto.event.response.EventFullDto; -import ru.practicum.aggregation.dto.event.response.EventShortDto; -import ru.practicum.aggregation.dto.request.EventRequestCount; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.event.come.create.NewEventDto; +import ru.practicum.aggregation.model.data.AdminGetData; +import ru.practicum.aggregation.model.data.FreeGetData; +import ru.practicum.aggregation.dto.event.come.update.UpdateEventAdminRequest; +import ru.practicum.aggregation.dto.event.come.update.UpdateEventUserRequest; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.dto.event.output.EventShortDto; +import ru.practicum.aggregation.model.repository.EventRequestCount; +import ru.practicum.aggregation.dto.user.output.UserDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.aggregation.enums.AdminStateAction; import ru.practicum.aggregation.enums.EventState; import ru.practicum.aggregation.enums.UserStateAction; @@ -31,7 +36,6 @@ import ru.practicum.ewm.events.mapper.StateMapper; import ru.practicum.ewm.events.mapper.UserMapper; import ru.practicum.ewm.events.model.EventData; -import ru.practicum.ewm.events.model.User; import ru.practicum.ewm.events.repository.CategoryRepository; import ru.practicum.ewm.events.repository.EventRepository; import ru.practicum.ewm.events.specification.EventSpecifications; @@ -61,7 +65,7 @@ public class EventServiceImpl implements EventService { private static final int HOURS_BEFORE_START = 2; @Override - public List getFreeEvents(@NonNull FreeGetDto dto, HttpServletRequest request) { + public List getFreeEvents(@NonNull FreeGetData dto, HttpServletRequest request) { if (dto.rangeStart() != null && dto.rangeEnd() != null) { if (dto.rangeEnd().isBefore(dto.rangeStart())) { @@ -200,7 +204,7 @@ public EventFullDto userAddNewEvent(Long userId, @NonNull NewEventDto newEventDt } @Override - public List adminGetEvents(@NonNull AdminGetDto dto) { + public List adminGetEvents(@NonNull AdminGetData dto) { Specification spec = SpecBuilder.builder() .andIf(dto.users() != null && !dto.users().isEmpty(), () -> EventSpecifications.hasUsers(dto.users())) @@ -460,9 +464,8 @@ private EventFullDto patchEvent(Long eventId, } @NonNull - private User getUserById(long userId) { - var userDto = userFeignRepository.getUserDtoById(userId); - return UserMapper.toEntity(userDto); + private UserDto getUserById(long userId) { + return userFeignRepository.getUserDtoById(userId); } @NonNull diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingService.java index fe0b349..e53168f 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingService.java @@ -1,7 +1,7 @@ package ru.practicum.ewm.events.service.rating; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; @Transactional public interface RatingService { diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java index 5b34a33..aef0d9f 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java @@ -5,7 +5,7 @@ import lombok.experimental.FieldDefaults; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; -import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; import ru.practicum.aggregation.error.exception.NotFoundException; import ru.practicum.ewm.events.entity.Event; import ru.practicum.ewm.events.repository.EventRepository; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java index 2d0c02e..a759897 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java @@ -1,9 +1,9 @@ package ru.practicum.ewm.events.service.request; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import java.util.List; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java index 187b9f7..9adba51 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java @@ -4,9 +4,9 @@ import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; import org.springframework.stereotype.Service; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.aggregation.error.exception.NotFoundException; import ru.practicum.aggregation.repository.RequestFeignRepository; import ru.practicum.aggregation.repository.UserFeignRepositoryImpl; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/specification/EventSpecifications.java b/core/event-service/src/main/java/ru/practicum/ewm/events/specification/EventSpecifications.java index 6716094..9687d4b 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/specification/EventSpecifications.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/specification/EventSpecifications.java @@ -7,7 +7,6 @@ import ru.practicum.aggregation.enums.EventState; import ru.practicum.ewm.events.entity.Category; import ru.practicum.ewm.events.entity.Event; -import ru.practicum.ewm.events.model.User; import java.time.LocalDateTime; import java.util.List; @@ -114,8 +113,7 @@ public static Specification hasUsers(List userIds) { return null; } - Join userJoin = root.join("initiator"); - return userJoin.get("id").in(userIds); + return root.get("initiatorId").in(userIds); }; } diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java index 1e36bc3..ced4f45 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java @@ -2,11 +2,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.ComponentScan; import ru.practicum.aggregation.client.event.EventClient; import ru.practicum.aggregation.client.request.RequestClient; import ru.practicum.aggregation.client.user.UserClient; +import ru.practicum.aggregation.config.JacksonConfiguration; @SpringBootApplication @EnableFeignClients(clients = { @@ -16,10 +18,12 @@ }) @ComponentScan(value = { "ru.practicum.ewm.ratings", + "ru.practicum.aggregation.config", "ru.practicum.aggregation.client", "ru.practicum.aggregation.error", "ru.practicum.aggregation.repository" }) +@ConfigurationPropertiesScan(basePackageClasses = JacksonConfiguration.class) public class RatingServiceApp { public static void main(String[] args) { SpringApplication.run(RatingServiceApp.class, args); diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/RatingController.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/RatingController.java index cf0bb8f..543f0a1 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/RatingController.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/RatingController.java @@ -4,8 +4,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.rating.RatingRequest; -import ru.practicum.aggregation.dto.rating.RatingResponse; +import ru.practicum.aggregation.dto.rating.come.create.RatingCreateRequest; +import ru.practicum.aggregation.dto.rating.output.RatingResponse; import ru.practicum.ewm.ratings.service.RatingService; @RestController @@ -19,7 +19,7 @@ public class RatingController { @ResponseStatus(HttpStatus.CREATED) public RatingResponse addReaction(@PathVariable Long userId, @PathVariable Long eventId, - @Valid @RequestBody RatingRequest request) { + @Valid @RequestBody RatingCreateRequest request) { return ratingService.addOrUpdateReaction(userId, eventId, request); } diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/entity/Rating.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/entity/Rating.java index 2b76a7a..ad15357 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/entity/Rating.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/entity/Rating.java @@ -8,7 +8,7 @@ import lombok.experimental.FieldDefaults; import lombok.experimental.SuperBuilder; import ru.practicum.aggregation.enums.Reaction; -import ru.practicum.aggregation.model.BaseEntity; +import ru.practicum.aggregation.model.entity.BaseEntity; @Getter @Setter diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java index 43ae650..4d5331b 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java @@ -2,13 +2,13 @@ import org.springframework.lang.NonNull; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.dto.rating.RatingRequest; -import ru.practicum.aggregation.dto.rating.RatingResponse; +import ru.practicum.aggregation.dto.rating.come.create.RatingCreateRequest; +import ru.practicum.aggregation.dto.rating.output.RatingResponse; @Transactional public interface RatingService { - RatingResponse addOrUpdateReaction(long userId, long eventId, @NonNull RatingRequest request); + RatingResponse addOrUpdateReaction(long userId, long eventId, @NonNull RatingCreateRequest request); void removeReaction(long userId, long eventId); } diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java index 8c47897..942f703 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java @@ -6,9 +6,9 @@ import lombok.experimental.FieldDefaults; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; -import ru.practicum.aggregation.dto.rating.RatingRequest; -import ru.practicum.aggregation.dto.rating.RatingResponse; -import ru.practicum.aggregation.dto.rating.RatingUpdateRequest; +import ru.practicum.aggregation.dto.rating.come.create.RatingCreateRequest; +import ru.practicum.aggregation.dto.rating.output.RatingResponse; +import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; import ru.practicum.aggregation.enums.Reaction; import ru.practicum.aggregation.error.exception.ConflictException; import ru.practicum.aggregation.error.exception.NotFoundException; @@ -30,7 +30,7 @@ public class RatingServiceImpl implements RatingService { EventFeignRepository eventFeignRepository; @Override - public RatingResponse addOrUpdateReaction(long userId, long eventId, @NonNull RatingRequest request) { + public RatingResponse addOrUpdateReaction(long userId, long eventId, @NonNull RatingCreateRequest request) { var user = userFeignRepository.getUserDtoById(userId); var event = eventFeignRepository.userFindEventById(userId, eventId); var initiator = event.initiator(); diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java index d9a1eef..7ce77d9 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java @@ -2,11 +2,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.ComponentScan; import ru.practicum.aggregation.client.event.EventClient; import ru.practicum.aggregation.client.request.RequestClient; import ru.practicum.aggregation.client.user.UserClient; +import ru.practicum.aggregation.config.JacksonConfiguration; @SpringBootApplication @EnableFeignClients(clients = { @@ -16,10 +18,12 @@ }) @ComponentScan(value = { "ru.practicum.ewm.requests", + "ru.practicum.aggregation.config", "ru.practicum.aggregation.client", "ru.practicum.aggregation.error", "ru.practicum.aggregation.repository" }) +@ConfigurationPropertiesScan(basePackageClasses = JacksonConfiguration.class) public class RequestServiceApp { public static void main(String[] args) { SpringApplication.run(RequestServiceApp.class, args); diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java index 36b315d..68c9d58 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java @@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.enums.ParticipationStatus; import ru.practicum.ewm.requests.service.RequestService; diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java index d3f545f..985f333 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java @@ -6,9 +6,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.ewm.requests.service.RequestService; import java.util.List; diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/entity/ParticipationRequest.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/entity/ParticipationRequest.java index 319cd8f..02d2b3b 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/entity/ParticipationRequest.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/entity/ParticipationRequest.java @@ -8,7 +8,7 @@ import lombok.experimental.FieldDefaults; import lombok.experimental.SuperBuilder; import ru.practicum.aggregation.enums.ParticipationStatus; -import ru.practicum.aggregation.model.BaseEntity; +import ru.practicum.aggregation.model.entity.BaseEntity; import java.time.LocalDateTime; diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java index 728dd59..c0da7c4 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java @@ -1,7 +1,7 @@ package ru.practicum.ewm.requests.mapper; import lombok.experimental.UtilityClass; -import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.ewm.requests.entity.ParticipationRequest; import java.time.format.DateTimeFormatter; diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java index 84fbb84..aed5565 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java @@ -2,7 +2,7 @@ import lombok.experimental.UtilityClass; import org.springframework.lang.NonNull; -import ru.practicum.aggregation.dto.user.UserDto; +import ru.practicum.aggregation.dto.user.output.UserDto; import ru.practicum.ewm.requests.model.User; @UtilityClass diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java index 90715ed..30bb13c 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java @@ -6,7 +6,7 @@ import lombok.Setter; import lombok.experimental.FieldDefaults; import lombok.experimental.SuperBuilder; -import ru.practicum.aggregation.model.BaseEntity; +import ru.practicum.aggregation.model.entity.BaseEntity; @Getter @Setter diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/repository/RequestRepository.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/repository/RequestRepository.java index be7a1b3..fdef561 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/repository/RequestRepository.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/repository/RequestRepository.java @@ -4,7 +4,7 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.dto.request.EventRequestCount; +import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.enums.ParticipationStatus; import ru.practicum.ewm.requests.entity.ParticipationRequest; diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java index 681352d..f1fc235 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java @@ -1,10 +1,10 @@ package ru.practicum.ewm.requests.service; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.dto.request.EventRequestCount; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.model.repository.EventRequestCount; +import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.aggregation.enums.ParticipationStatus; import java.util.List; diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java index 02c1e5d..ebdafbc 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java @@ -6,11 +6,11 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; -import ru.practicum.aggregation.dto.event.response.EventFullDto; -import ru.practicum.aggregation.dto.request.EventRequestCount; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateRequest; -import ru.practicum.aggregation.dto.request.EventRequestStatusUpdateResult; -import ru.practicum.aggregation.dto.request.ParticipationRequestDto; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.model.repository.EventRequestCount; +import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.aggregation.enums.EventState; import ru.practicum.aggregation.enums.ParticipationStatus; import ru.practicum.aggregation.error.exception.ConflictException; diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java b/core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java index b859b39..7c7655a 100644 --- a/core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java @@ -2,11 +2,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.ComponentScan; import ru.practicum.aggregation.client.event.EventClient; import ru.practicum.aggregation.client.request.RequestClient; import ru.practicum.aggregation.client.user.UserClient; +import ru.practicum.aggregation.config.JacksonConfiguration; @SpringBootApplication @EnableFeignClients(clients = { @@ -16,10 +18,12 @@ }) @ComponentScan(value = { "ru.practicum.ewm.users", + "ru.practicum.aggregation.config", "ru.practicum.aggregation.client", "ru.practicum.aggregation.error", "ru.practicum.aggregation.repository" }) +@ConfigurationPropertiesScan(basePackageClasses = JacksonConfiguration.class) public class UserServiceApp { public static void main(String[] args) { SpringApplication.run(UserServiceApp.class, args); diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java index 007d66a..028b015 100644 --- a/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java @@ -4,9 +4,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.user.NewUserRequest; -import ru.practicum.aggregation.dto.user.UserDto; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.user.come.NewUserRequest; +import ru.practicum.aggregation.dto.user.output.UserDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.ewm.users.service.UserService; import java.util.List; diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/entity/User.java b/core/user-service/src/main/java/ru/practicum/ewm/users/entity/User.java index 325f148..bf2a932 100644 --- a/core/user-service/src/main/java/ru/practicum/ewm/users/entity/User.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/entity/User.java @@ -9,7 +9,7 @@ import lombok.Setter; import lombok.experimental.FieldDefaults; import lombok.experimental.SuperBuilder; -import ru.practicum.aggregation.model.BaseEntity; +import ru.practicum.aggregation.model.entity.BaseEntity; @Getter @Setter diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/mapper/UserMapper.java b/core/user-service/src/main/java/ru/practicum/ewm/users/mapper/UserMapper.java index abddc80..c705582 100644 --- a/core/user-service/src/main/java/ru/practicum/ewm/users/mapper/UserMapper.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/mapper/UserMapper.java @@ -2,9 +2,9 @@ import lombok.experimental.UtilityClass; import org.springframework.lang.NonNull; -import ru.practicum.aggregation.dto.user.NewUserRequest; -import ru.practicum.aggregation.dto.user.UserDto; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.user.come.NewUserRequest; +import ru.practicum.aggregation.dto.user.output.UserDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.ewm.users.entity.User; @UtilityClass @@ -14,7 +14,6 @@ public UserShortDto toUserShortDto(@NonNull User user) { return UserShortDto.builder() .id(user.getId()) .name(user.getName()) - .email(user.getEmail()) .build(); } diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserService.java b/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserService.java index b373110..5531170 100644 --- a/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserService.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserService.java @@ -1,9 +1,9 @@ package ru.practicum.ewm.users.service; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.dto.user.NewUserRequest; -import ru.practicum.aggregation.dto.user.UserDto; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.user.come.NewUserRequest; +import ru.practicum.aggregation.dto.user.output.UserDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import java.util.List; diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java b/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java index 66945d5..a726a0b 100644 --- a/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java @@ -4,9 +4,9 @@ import org.springframework.data.domain.PageRequest; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; -import ru.practicum.aggregation.dto.user.NewUserRequest; -import ru.practicum.aggregation.dto.user.UserDto; -import ru.practicum.aggregation.dto.user.UserShortDto; +import ru.practicum.aggregation.dto.user.come.NewUserRequest; +import ru.practicum.aggregation.dto.user.output.UserDto; +import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.aggregation.error.exception.ConflictException; import ru.practicum.aggregation.error.exception.NotFoundException; import ru.practicum.ewm.users.entity.User; From ad5ed7b07aede1f1829aead699a821e5e91b226c Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Fri, 28 Aug 2026 23:58:56 +0300 Subject: [PATCH 06/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Рефакторинг путей и маршрутов Большинство тестов проходит --- .../client/RemoteCallExecutor.java | 8 +- .../aggregation/client/RemoteCallResult.java | 14 +- .../client/RemoteExceptionMapper.java | 22 ++- .../aggregation/client/event/EventClient.java | 8 +- .../client/request/RequestClient.java | 22 ++- .../request/RequestFallbackFactory.java | 10 +- .../aggregation/client/user/UserClient.java | 4 +- .../come/create/NewCompilationDto.java | 6 + .../come/create/RatingCreateRequest.java | 4 + .../aggregation/enums/AdminStateAction.java | 4 + .../practicum/aggregation/enums/Reaction.java | 3 + .../error/GeneralErrorHandler.java | 4 +- .../error/exception/BusinessException.java | 7 - .../error/exception/ConflictException.java | 8 - .../error/exception/NotFoundException.java | 8 - .../exception/ServiceProcessingException.java | 7 - .../bussines/ServiceProcessingException.java | 7 + .../bussines/cause/BadRequestException.java | 9 ++ .../bussines/cause/ConflictException.java | 10 ++ .../bussines/cause/NotFoundException.java | 10 ++ .../processing/EventProcessingException.java | 11 -- .../RequestProcessingException.java | 11 -- .../processing/UserProcessingException.java | 11 -- .../EventServiceUnavailableException.java | 2 - .../RequestServiceUnavailableException.java | 2 - .../ServiceUnavailableException.java | 2 +- .../UserServiceUnavailableException.java | 2 - .../aggregation/model/entity/Location.java | 5 + .../repository/EventFeignRepositoryImpl.java | 41 ++--- .../repository/RequestFeignRepository.java | 7 +- .../RequestFeignRepositoryImpl.java | 57 +++---- .../repository/UserFeignRepositoryImpl.java | 41 ++--- .../controller/user/UserEventController.java | 4 +- .../ru/practicum/ewm/events/model/User.java | 11 -- .../service/category/CategoryServiceImpl.java | 4 +- .../compilation/CompilationServiceImpl.java | 2 +- .../service/event/EventServiceImpl.java | 15 +- .../service/rating/RatingServiceImpl.java | 2 +- .../service/request/RequestService.java | 3 +- .../service/request/RequestServiceImpl.java | 18 ++- ...troller.java => UserRatingController.java} | 2 +- .../ratings/service/RatingServiceImpl.java | 19 ++- .../controller/AdminRequestController.java | 29 ---- .../controller/SystemRequestController.java | 54 +++++++ .../controller/UserRequestController.java | 24 --- .../ewm/requests/mapper/UserMapper.java | 19 --- .../ru/practicum/ewm/requests/model/User.java | 19 --- .../ewm/requests/service/RequestService.java | 7 +- .../requests/service/RequestServiceImpl.java | 27 ++-- .../users/controller/AdminUserController.java | 22 --- .../controller/SystemUserController.java | 38 +++++ .../ewm/users/service/UserServiceImpl.java | 4 +- infra/config-repo/application.yml | 4 +- infra/config-repo/core/event-service.yml | 2 +- infra/config-repo/core/rating-service.yml | 2 +- infra/config-repo/core/request-service.yml | 2 +- infra/config-repo/core/user-service.yml | 2 +- infra/config-repo/infra/gateway-server.yml | 145 ++++++++++-------- .../src/main/resources/application.yml | 6 + .../ewm/infra/gateway/error/ApiError.java | 24 +++ .../gateway/error/GeneralErrorHandler.java | 40 +++++ 61 files changed, 494 insertions(+), 423 deletions(-) delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/BusinessException.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ConflictException.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/NotFoundException.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceProcessingException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/ServiceProcessingException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/BadRequestException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/ConflictException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/NotFoundException.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/EventProcessingException.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RequestProcessingException.java delete mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/UserProcessingException.java rename core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/{ => unavailable}/ServiceUnavailableException.java (73%) delete mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java rename core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/{RatingController.java => UserRatingController.java} (96%) delete mode 100644 core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java create mode 100644 core/request-service/src/main/java/ru/practicum/ewm/requests/controller/SystemRequestController.java delete mode 100644 core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java delete mode 100644 core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java create mode 100644 core/user-service/src/main/java/ru/practicum/ewm/users/controller/SystemUserController.java create mode 100644 infra/gateway-server/src/main/java/ru/practicum/ewm/infra/gateway/error/ApiError.java create mode 100644 infra/gateway-server/src/main/java/ru/practicum/ewm/infra/gateway/error/GeneralErrorHandler.java diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallExecutor.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallExecutor.java index f52467d..3dfa43f 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallExecutor.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallExecutor.java @@ -2,8 +2,8 @@ import lombok.experimental.UtilityClass; import org.springframework.lang.NonNull; -import ru.practicum.aggregation.error.exception.ServiceProcessingException; -import ru.practicum.aggregation.error.exception.ServiceUnavailableException; +import ru.practicum.aggregation.error.exception.bussines.ServiceProcessingException; +import ru.practicum.aggregation.error.exception.unavailable.ServiceUnavailableException; import java.util.function.Supplier; @@ -16,7 +16,7 @@ public RemoteCallResult execute(@NonNull Supplier action) { return new RemoteCallResult.Success<>(action.get()); } catch (ServiceProcessingException ex) { - return new RemoteCallResult.Failure<>(ex.getMessage()); + return new RemoteCallResult.Failure<>(ex); } catch (ServiceUnavailableException ex) { return new RemoteCallResult.Degraded<>(ex); @@ -30,7 +30,7 @@ public static RemoteCallResult executeVoid(@NonNull Runnable function) { return new RemoteCallResult.Success<>(null); } catch (ServiceProcessingException ex) { - return new RemoteCallResult.Failure<>(ex.getMessage()); + return new RemoteCallResult.Failure<>(ex); } catch (ServiceUnavailableException ex) { return new RemoteCallResult.Degraded<>(ex); diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallResult.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallResult.java index 58a1a73..2ba2f77 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallResult.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteCallResult.java @@ -1,14 +1,22 @@ package ru.practicum.aggregation.client; +import ru.practicum.aggregation.error.exception.bussines.ServiceProcessingException; + @SuppressWarnings("unused") public sealed interface RemoteCallResult permits RemoteCallResult.Success, RemoteCallResult.Failure, RemoteCallResult.Degraded { - record Success(T value) implements RemoteCallResult { } + record Success(T value) + implements RemoteCallResult { + } - record Failure(String message) implements RemoteCallResult { } + record Failure(ServiceProcessingException ex) + implements RemoteCallResult { + } - record Degraded(Throwable cause) implements RemoteCallResult { } + record Degraded(Throwable cause) + implements RemoteCallResult { + } } \ No newline at end of file diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java index 4719b28..b30f1da 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/RemoteExceptionMapper.java @@ -3,9 +3,9 @@ import feign.FeignException; import lombok.NonNull; import lombok.experimental.UtilityClass; -import ru.practicum.aggregation.error.exception.processing.EventProcessingException; -import ru.practicum.aggregation.error.exception.processing.RequestProcessingException; -import ru.practicum.aggregation.error.exception.processing.UserProcessingException; +import ru.practicum.aggregation.error.exception.bussines.cause.BadRequestException; +import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException; +import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; import ru.practicum.aggregation.error.exception.unavailable.EventServiceUnavailableException; import ru.practicum.aggregation.error.exception.unavailable.RequestServiceUnavailableException; import ru.practicum.aggregation.error.exception.unavailable.UserServiceUnavailableException; @@ -16,8 +16,10 @@ public class RemoteExceptionMapper { public RuntimeException mapUserException(@NonNull Throwable cause) { if (cause instanceof FeignException ex) { return switch (ex.status()) { - case 404, 409 -> new UserProcessingException(ex.getMessage()); - default -> new UserServiceUnavailableException(ex); + case 400 -> new BadRequestException(ex.getMessage()); + case 404 -> new NotFoundException(ex.getMessage()); + case 409 -> new ConflictException(ex.getMessage()); + default -> new EventServiceUnavailableException(ex); }; } return new UserServiceUnavailableException(cause); @@ -26,7 +28,9 @@ public RuntimeException mapUserException(@NonNull Throwable cause) { public RuntimeException mapEventException(@NonNull Throwable cause) { if (cause instanceof FeignException ex) { return switch (ex.status()) { - case 400, 404, 409 -> new EventProcessingException(ex.getMessage()); + case 400 -> new BadRequestException(ex.getMessage()); + case 404 -> new NotFoundException(ex.getMessage()); + case 409 -> new ConflictException(ex.getMessage()); default -> new EventServiceUnavailableException(ex); }; } @@ -36,8 +40,10 @@ public RuntimeException mapEventException(@NonNull Throwable cause) { public RuntimeException mapRequestException(@NonNull Throwable cause) { if (cause instanceof FeignException ex) { return switch (ex.status()) { - case 404, 409 -> new RequestProcessingException(ex.getMessage()); - default -> new RequestServiceUnavailableException(ex); + case 400 -> new BadRequestException(ex.getMessage()); + case 404 -> new NotFoundException(ex.getMessage()); + case 409 -> new ConflictException(ex.getMessage()); + default -> new EventServiceUnavailableException(ex); }; } return new RequestServiceUnavailableException(cause); diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java index e0beb50..570c057 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java @@ -2,7 +2,6 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Positive; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -18,14 +17,13 @@ public interface EventClient { @GetMapping("/users/{userId}/events/{eventId}") - EventFullDto userFindEventById(@PathVariable @Positive Long userId, - @PathVariable @Positive Long eventId); - + EventFullDto userFindEventById(@PathVariable Long userId, + @PathVariable Long eventId); @PatchMapping("/system/events/ratings") void systemUpdateRating(@RequestBody @NotNull @Valid RatingUpdateRequest request); @GetMapping("/system/events/{eventId}") - EventFullDto systemFindEventById(@PathVariable @Positive Long eventId); + EventFullDto systemFindEventById(@PathVariable Long eventId); } \ No newline at end of file diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java index 863e724..8c984cf 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java @@ -1,14 +1,12 @@ package ru.practicum.aggregation.client.request; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; -import ru.practicum.aggregation.enums.ParticipationStatus; +import ru.practicum.aggregation.model.repository.EventRequestCount; import java.util.List; @@ -18,18 +16,16 @@ ) public interface RequestClient { - @GetMapping("/users/{userId}/requests/{eventId}") - List findByEventId(@PathVariable @Positive Long userId, - @PathVariable @Positive Long eventId); + @GetMapping("/system/requests/users/{userId}/events/{eventId}") + List findByUserIdAndEventId(@PathVariable @Positive Long userId, + @PathVariable @Positive Long eventId); - @PatchMapping("/users/{userId}/requests/events/{eventId}/status") + @PatchMapping("/system/requests/events/{eventId}/status") EventRequestStatusUpdateResult updateStatusRequest( - @PathVariable @Positive Long userId, - @PathVariable @Positive Long eventId, - @RequestBody @NotNull EventRequestStatusUpdateRequest request + @PathVariable Long eventId, + @RequestBody EventRequestStatusUpdateRequest request ); - @GetMapping("/admin/requests/count") - List getCount(@RequestParam List eventIds, - @RequestParam ParticipationStatus status); + @GetMapping("/system/requests/count") + List getConfirmedRequestsCount(@RequestParam List eventIds); } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java index 88bd0d4..2392ead 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java @@ -3,11 +3,10 @@ import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.stereotype.Component; import ru.practicum.aggregation.client.RemoteExceptionMapper; -import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; -import ru.practicum.aggregation.enums.ParticipationStatus; +import ru.practicum.aggregation.model.repository.EventRequestCount; import java.util.List; @@ -20,19 +19,18 @@ public RequestClient create(Throwable cause) { return new RequestClient() { @Override - public List findByEventId(Long userId, Long eventId) { + public List findByUserIdAndEventId(Long userId, Long eventId) { throw RemoteExceptionMapper.mapRequestException(cause); } @Override - public EventRequestStatusUpdateResult updateStatusRequest(Long userId, - Long eventId, + public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, EventRequestStatusUpdateRequest request) { throw RemoteExceptionMapper.mapRequestException(cause); } @Override - public List getCount(List eventIds, ParticipationStatus status) { + public List getConfirmedRequestsCount(List eventIds) { throw RemoteExceptionMapper.mapRequestException(cause); } }; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java index ac722e3..99d8b55 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java @@ -18,10 +18,10 @@ public interface UserClient { @GetMapping("/admin/users/{userId}") UserDto getUser(@PathVariable Long userId); - @GetMapping("/admin/users/{userId}/check") + @GetMapping("/system/users/{userId}/check") void checkUser(@PathVariable Long userId); - @GetMapping("/admin/users/short") + @GetMapping("/system/users/short") List getShortUsersByIds(@RequestParam List userIds); } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/create/NewCompilationDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/create/NewCompilationDto.java index 452da4a..37ce490 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/create/NewCompilationDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/create/NewCompilationDto.java @@ -9,6 +9,12 @@ import java.util.Set; +/** + * @apiNote + * {@link Set}<{@link Long}> events
+ * boolean pinned
+ * {@link String} title + */ @Data @Builder @AllArgsConstructor diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/come/create/RatingCreateRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/come/create/RatingCreateRequest.java index 3dd7002..3665164 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/come/create/RatingCreateRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/rating/come/create/RatingCreateRequest.java @@ -4,6 +4,10 @@ import lombok.Builder; import ru.practicum.aggregation.enums.Reaction; +/** + * @apiNote + * {@link Reaction} reaction + */ @Builder public record RatingCreateRequest( diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/AdminStateAction.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/AdminStateAction.java index cb0c234..248e447 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/AdminStateAction.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/AdminStateAction.java @@ -1,5 +1,9 @@ package ru.practicum.aggregation.enums; +/** + * PUBLISH_EVENT, + * REJECT_EVENT + */ public enum AdminStateAction { PUBLISH_EVENT, REJECT_EVENT diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/Reaction.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/Reaction.java index a5d1060..b34bd86 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/Reaction.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/enums/Reaction.java @@ -1,5 +1,8 @@ package ru.practicum.aggregation.enums; +/** + * LIKE, DISLIKE + */ public enum Reaction { LIKE, DISLIKE } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/GeneralErrorHandler.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/GeneralErrorHandler.java index 7abf542..fe23e0d 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/GeneralErrorHandler.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/GeneralErrorHandler.java @@ -10,8 +10,8 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.HandlerMethodValidationException; -import ru.practicum.aggregation.error.exception.ConflictException; -import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException; +import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; import ru.practicum.aggregation.error.exception.stats.HitRequestException; import ru.practicum.aggregation.error.exception.stats.StatsResponseException; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/BusinessException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/BusinessException.java deleted file mode 100644 index 22772fb..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/BusinessException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.aggregation.error.exception; - -public class BusinessException extends RuntimeException { - public BusinessException(String message) { - super(message); - } -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ConflictException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ConflictException.java deleted file mode 100644 index 270e413..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ConflictException.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.practicum.aggregation.error.exception; - -public class ConflictException extends BusinessException { - - public ConflictException(String message) { - super(message); - } -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/NotFoundException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/NotFoundException.java deleted file mode 100644 index 9657f0d..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/NotFoundException.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.practicum.aggregation.error.exception; - -public class NotFoundException extends BusinessException { - - public NotFoundException(String message) { - super(message); - } -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceProcessingException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceProcessingException.java deleted file mode 100644 index 15ffe52..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceProcessingException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.aggregation.error.exception; - -public class ServiceProcessingException extends BusinessException { - public ServiceProcessingException(String message) { - super(message); - } -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/ServiceProcessingException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/ServiceProcessingException.java new file mode 100644 index 0000000..be4f363 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/ServiceProcessingException.java @@ -0,0 +1,7 @@ +package ru.practicum.aggregation.error.exception.bussines; + +public class ServiceProcessingException extends RuntimeException { + public ServiceProcessingException(String message) { + super(message); + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/BadRequestException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/BadRequestException.java new file mode 100644 index 0000000..fe513ae --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/BadRequestException.java @@ -0,0 +1,9 @@ +package ru.practicum.aggregation.error.exception.bussines.cause; + +import ru.practicum.aggregation.error.exception.bussines.ServiceProcessingException; + +public class BadRequestException extends ServiceProcessingException { + public BadRequestException(String message) { + super(message); + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/ConflictException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/ConflictException.java new file mode 100644 index 0000000..afaac10 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/ConflictException.java @@ -0,0 +1,10 @@ +package ru.practicum.aggregation.error.exception.bussines.cause; + +import ru.practicum.aggregation.error.exception.bussines.ServiceProcessingException; + +public class ConflictException extends ServiceProcessingException { + + public ConflictException(String message) { + super(message); + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/NotFoundException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/NotFoundException.java new file mode 100644 index 0000000..358d7e0 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/bussines/cause/NotFoundException.java @@ -0,0 +1,10 @@ +package ru.practicum.aggregation.error.exception.bussines.cause; + +import ru.practicum.aggregation.error.exception.bussines.ServiceProcessingException; + +public class NotFoundException extends ServiceProcessingException { + + public NotFoundException(String message) { + super(message); + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/EventProcessingException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/EventProcessingException.java deleted file mode 100644 index 2a32039..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/EventProcessingException.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.practicum.aggregation.error.exception.processing; - -import ru.practicum.aggregation.error.exception.ServiceProcessingException; - -public class EventProcessingException extends ServiceProcessingException { - - public EventProcessingException(String message) { - super(message); - } - -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RequestProcessingException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RequestProcessingException.java deleted file mode 100644 index 3dfbacb..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/RequestProcessingException.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.practicum.aggregation.error.exception.processing; - -import ru.practicum.aggregation.error.exception.ServiceProcessingException; - -public class RequestProcessingException extends ServiceProcessingException { - - public RequestProcessingException(String message) { - super(message); - } - -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/UserProcessingException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/UserProcessingException.java deleted file mode 100644 index 13deea1..0000000 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/processing/UserProcessingException.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.practicum.aggregation.error.exception.processing; - -import ru.practicum.aggregation.error.exception.ServiceProcessingException; - -public class UserProcessingException extends ServiceProcessingException { - - public UserProcessingException(String message) { - super(message); - } - -} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/EventServiceUnavailableException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/EventServiceUnavailableException.java index a18b2c3..1699e94 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/EventServiceUnavailableException.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/EventServiceUnavailableException.java @@ -1,7 +1,5 @@ package ru.practicum.aggregation.error.exception.unavailable; -import ru.practicum.aggregation.error.exception.ServiceUnavailableException; - public class EventServiceUnavailableException extends ServiceUnavailableException { public EventServiceUnavailableException(Throwable cause) { diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RequestServiceUnavailableException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RequestServiceUnavailableException.java index d009904..f4a092e 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RequestServiceUnavailableException.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/RequestServiceUnavailableException.java @@ -1,7 +1,5 @@ package ru.practicum.aggregation.error.exception.unavailable; -import ru.practicum.aggregation.error.exception.ServiceUnavailableException; - public class RequestServiceUnavailableException extends ServiceUnavailableException { public RequestServiceUnavailableException(Throwable cause) { diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceUnavailableException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/ServiceUnavailableException.java similarity index 73% rename from core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceUnavailableException.java rename to core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/ServiceUnavailableException.java index 0994c1e..c964a1c 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/ServiceUnavailableException.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/ServiceUnavailableException.java @@ -1,4 +1,4 @@ -package ru.practicum.aggregation.error.exception; +package ru.practicum.aggregation.error.exception.unavailable; public class ServiceUnavailableException extends RuntimeException { public ServiceUnavailableException(String message, Throwable cause) { diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/UserServiceUnavailableException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/UserServiceUnavailableException.java index 74fc25d..a615002 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/UserServiceUnavailableException.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/UserServiceUnavailableException.java @@ -1,7 +1,5 @@ package ru.practicum.aggregation.error.exception.unavailable; -import ru.practicum.aggregation.error.exception.ServiceUnavailableException; - public class UserServiceUnavailableException extends ServiceUnavailableException { public UserServiceUnavailableException(Throwable cause) { diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/entity/Location.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/entity/Location.java index e004da6..b524bbc 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/entity/Location.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/entity/Location.java @@ -5,6 +5,11 @@ import java.io.Serializable; +/** + * @apiNote + * {@link Float} lat
+ * {@link Float} lon + */ @Data @Builder public class Location implements Serializable { diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java index 2abc917..7946721 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java @@ -11,7 +11,6 @@ import ru.practicum.aggregation.client.event.EventClient; import ru.practicum.aggregation.dto.event.output.EventFullDto; import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; -import ru.practicum.aggregation.error.exception.processing.UserProcessingException; import ru.practicum.aggregation.error.exception.unavailable.UserServiceUnavailableException; @Slf4j @@ -25,22 +24,24 @@ public class EventFeignRepositoryImpl implements EventFeignRepository { @SuppressWarnings("unused") @Override public void updateRating(@NonNull RatingUpdateRequest request) { + log.info(""" + PLEASE WAITING + Система обновляет рейтинг + request: {}""", request); var eventId = request.eventId(); switch (RemoteCallExecutor.executeVoid(() -> eventClient.systemUpdateRating(request))) { case RemoteCallResult.Success(var nullable) -> { } - case RemoteCallResult.Failure(var message) -> { + case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение - Не удалось обновить рейтинг события с id={} - """, eventId); - throw new UserProcessingException(message); + Не удалось обновить рейтинг события с id={}""", eventId); + throw exeption; } case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация - Не удалось обновить рейтинг события с id={} - """, eventId); + Не удалось обновить рейтинг события с id={}""", eventId); throw new UserServiceUnavailableException(cause); } } @@ -48,22 +49,23 @@ public void updateRating(@NonNull RatingUpdateRequest request) { @Override public EventFullDto userFindEventById(long userId, long eventId) { + log.info(""" + PLEASE WAITING + Пользователь с id={} ищет событие с id={}""", userId, eventId); return switch (RemoteCallExecutor.execute(() -> eventClient.userFindEventById(userId, eventId))) { case RemoteCallResult.Success(var response) -> response; - case RemoteCallResult.Failure(var message) -> { + case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение Пользователю с id={} - Не удалось получить событие с id={} - """, userId, eventId); - throw new UserProcessingException(message); + Не удалось получить событие с id={}""", userId, eventId); + throw exeption; } case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация Пользователю с id={} - Не удалось получить событие с id={} - """, userId, eventId); + Не удалось получить событие с id={}""", userId, eventId); throw new UserServiceUnavailableException(cause); } }; @@ -71,20 +73,21 @@ public EventFullDto userFindEventById(long userId, long eventId) { @Override public EventFullDto systemFindEventById(long eventId) { + log.info(""" + PLEASE WAITING + Система ищет событие с id={}""", eventId); return switch (RemoteCallExecutor.execute(() -> eventClient.systemFindEventById(eventId))) { case RemoteCallResult.Success(var response) -> response; - case RemoteCallResult.Failure(var message) -> { + case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение - Не удалось получить событие с id={} - """, eventId); - throw new UserProcessingException(message); + Не удалось получить событие с id={}""", eventId); + throw exeption; } case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация - Не удалось получить событие с id={} - """, eventId); + Не удалось получить событие с id={}""", eventId); throw new UserServiceUnavailableException(cause); } }; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java index 91ebad9..4bbeab4 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java @@ -1,9 +1,9 @@ package ru.practicum.aggregation.repository; -import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; +import ru.practicum.aggregation.model.repository.EventRequestCount; import java.util.List; @@ -11,9 +11,8 @@ public interface RequestFeignRepository { List getConfirmedRequestsCount(List eventIds); - List findByEventId(long userId, long eventId); + List findByUserIdAndEventId(long userId, long eventId); - EventRequestStatusUpdateResult updateStatusRequest(Long userId, - Long eventId, + EventRequestStatusUpdateResult updateStatusRequest(Long eventId, EventRequestStatusUpdateRequest status); } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java index 77ad41b..2ee7dad 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java @@ -8,13 +8,11 @@ import ru.practicum.aggregation.client.RemoteCallExecutor; import ru.practicum.aggregation.client.RemoteCallResult; import ru.practicum.aggregation.client.request.RequestClient; -import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; -import ru.practicum.aggregation.enums.ParticipationStatus; -import ru.practicum.aggregation.error.exception.processing.RequestProcessingException; import ru.practicum.aggregation.error.exception.unavailable.RequestServiceUnavailableException; +import ru.practicum.aggregation.model.repository.EventRequestCount; import java.util.List; @@ -28,70 +26,75 @@ public class RequestFeignRepositoryImpl implements RequestFeignRepository { @Override public List getConfirmedRequestsCount(List eventIds) { + log.info(""" + PLEASE WAITING + Система получает количество подтвержденных запросов на участие в событиях + eventIds: {}""", eventIds); return switch (RemoteCallExecutor.execute(() -> - requestClient.getCount(eventIds, ParticipationStatus.CONFIRMED))) { + requestClient.getConfirmedRequestsCount(eventIds))) { case RemoteCallResult.Success(var response) -> response; - case RemoteCallResult.Failure(var message) -> { + case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение Не удалось получить количество подтвержденных заявок на участие в событиях - eventIds: {} - """, eventIds); - throw new RequestProcessingException(message); + eventIds: {}""", eventIds); + throw exeption; } case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация Не удалось получить количество подтвержденных заявок на участие в событиях - eventIds: {} - """, eventIds); + eventIds: {}""", eventIds); throw new RequestServiceUnavailableException(cause); } }; } @Override - public List findByEventId(long userId, long eventId) { - return switch (RemoteCallExecutor.execute(() -> requestClient.findByEventId(userId, eventId))) { + public List findByUserIdAndEventId(long userId, long eventId) { + log.info(""" + PLEASE WAITING + Система ищет запрос на участие пользователя с id={} в событии с id={}""", userId, eventId); + return switch (RemoteCallExecutor.execute(() -> requestClient + .findByUserIdAndEventId(userId, eventId))) { case RemoteCallResult.Success(var response) -> response; - case RemoteCallResult.Failure(var message) -> { + case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение - Не удалось получить запросы пользователей на участие в событии с id={} - """, eventId); - throw new RequestProcessingException(message); + Не удалось получить запросы пользователей на участие в событии с id={}""", eventId); + throw exeption; } case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация - Не удалось получить запросы пользователей на участие в событии с id={} - """, eventId); + Не удалось получить запросы пользователей на участие в событии с id={}""", eventId); throw new RequestServiceUnavailableException(cause); } }; } @Override - public EventRequestStatusUpdateResult updateStatusRequest(Long userId, - Long eventId, + public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, EventRequestStatusUpdateRequest status) { + log.info(""" + PLEASE WAITING + Система обновляет запрос на участие в событии с id={} + status: {}""", eventId, status); return switch (RemoteCallExecutor.execute(() -> - requestClient.updateStatusRequest(userId, eventId, status))) { + requestClient.updateStatusRequest(eventId, status))) { case RemoteCallResult.Success(var response) -> response; - case RemoteCallResult.Failure(var message) -> { + case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение Не удалось получить запросы пользователей на участие - в событии с id={}, для пользователя с id={} - """, eventId, userId); - throw new RequestProcessingException(message); + в событии с id={}""", eventId); + throw exeption; } case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация Не удалось получить запросы пользователей на участие - в событии с id={}, для пользователя с id={} - """, eventId, userId); + в событии с id={}""", eventId); throw new RequestServiceUnavailableException(cause); } }; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java index 90fd64a..af22f8a 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java @@ -11,7 +11,6 @@ import ru.practicum.aggregation.client.user.UserClient; import ru.practicum.aggregation.dto.user.output.UserDto; import ru.practicum.aggregation.dto.user.output.UserShortDto; -import ru.practicum.aggregation.error.exception.processing.UserProcessingException; import ru.practicum.aggregation.error.exception.unavailable.UserServiceUnavailableException; import java.util.List; @@ -26,20 +25,21 @@ public class UserFeignRepositoryImpl implements UserFeignRepository { @Override public @NonNull UserDto getUserDtoById(long userId) { + log.info(""" + PLEASE WAITING + Администратор получает пользователя с id={}""", userId); return switch (RemoteCallExecutor.execute(() -> userClient.getUser(userId))) { case RemoteCallResult.Success(var response) -> response; - case RemoteCallResult.Failure(var message) -> { + case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение - Не удалось получить пользователя с id={} - """, userId); - throw new UserProcessingException(message); + Не удалось получить пользователя с id={}""", userId); + throw exeption; } case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация - Не удалось получить пользователя с id={} - """, userId); + Не удалось получить пользователя с id={}""", userId); throw new UserServiceUnavailableException(cause); } }; @@ -47,22 +47,24 @@ public class UserFeignRepositoryImpl implements UserFeignRepository { @Override public List getUsersByIds(List userIds) { + log.info(""" + PLEASE WAITING + Система получает пользователей по ID + userids: {}""", userIds); return switch (RemoteCallExecutor.execute(() -> userClient.getShortUsersByIds(userIds))) { case RemoteCallResult.Success(var response) -> response; - case RemoteCallResult.Failure(var message) -> { + case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение Не удалось получить краткий список пользователей - userIds: {} - """, userIds); - throw new UserProcessingException(message); + userIds: {}""", userIds); + throw exeption; } case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация Не удалось получить краткий список пользователей - userIds: {} - """, userIds); + userIds: {}""", userIds); throw new UserServiceUnavailableException(cause); } }; @@ -71,21 +73,22 @@ public List getUsersByIds(List userIds) { @SuppressWarnings("unused") @Override public void checkUser(Long userId) { + log.info(""" + PLEASE WAITING + Система проверяет наличие пользователя с id={}""", userId); switch (RemoteCallExecutor.executeVoid(() -> userClient.checkUser(userId))) { case RemoteCallResult.Success(var nullable) -> { } - case RemoteCallResult.Failure(var message) -> { + case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение - Не удалось проверить наличие пользователя с id={} - """, userId); - throw new UserProcessingException(message); + Не удалось проверить наличие пользователя с id={}""", userId); + throw exeption; } case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация - Не удалось проверить наличие пользователя с id={} - """, userId); + Не удалось проверить наличие пользователя с id={}""", userId); throw new UserServiceUnavailableException(cause); } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java index 94917f7..51f68c7 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java @@ -6,10 +6,10 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.event.output.EventFullDto; -import ru.practicum.aggregation.dto.event.output.EventShortDto; import ru.practicum.aggregation.dto.event.come.create.NewEventDto; import ru.practicum.aggregation.dto.event.come.update.UpdateEventUserRequest; +import ru.practicum.aggregation.dto.event.output.EventFullDto; +import ru.practicum.aggregation.dto.event.output.EventShortDto; import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java b/core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java deleted file mode 100644 index 1cface3..0000000 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/model/User.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.practicum.ewm.events.model; - -import lombok.Builder; - -@Builder -public record User( - Long id, - String name, - String email -) { -} diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryServiceImpl.java index d5508c8..b0919b0 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/category/CategoryServiceImpl.java @@ -9,8 +9,8 @@ import org.springframework.transaction.annotation.Transactional; import ru.practicum.aggregation.dto.category.output.CategoryDto; import ru.practicum.aggregation.dto.category.come.NewCategoryDto; -import ru.practicum.aggregation.error.exception.ConflictException; -import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException; +import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; import ru.practicum.ewm.events.mapper.CategoryMapper; import ru.practicum.ewm.events.entity.Category; import ru.practicum.ewm.events.repository.CategoryRepository; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java index d3bbc86..bee308c 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java @@ -14,7 +14,7 @@ import ru.practicum.aggregation.dto.compilation.come.create.NewCompilationDto; import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.dto.user.output.UserShortDto; -import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; import ru.practicum.aggregation.model.entity.BaseEntity; import ru.practicum.aggregation.repository.RequestFeignRepository; import ru.practicum.aggregation.repository.UserFeignRepository; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java index bb08851..135c0f8 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java @@ -14,20 +14,20 @@ import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import ru.practicum.aggregation.dto.event.come.create.NewEventDto; -import ru.practicum.aggregation.model.data.AdminGetData; -import ru.practicum.aggregation.model.data.FreeGetData; import ru.practicum.aggregation.dto.event.come.update.UpdateEventAdminRequest; import ru.practicum.aggregation.dto.event.come.update.UpdateEventUserRequest; import ru.practicum.aggregation.dto.event.output.EventFullDto; import ru.practicum.aggregation.dto.event.output.EventShortDto; -import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.dto.user.output.UserDto; import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.aggregation.enums.AdminStateAction; import ru.practicum.aggregation.enums.EventState; import ru.practicum.aggregation.enums.UserStateAction; -import ru.practicum.aggregation.error.exception.ConflictException; -import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException; +import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; +import ru.practicum.aggregation.model.data.AdminGetData; +import ru.practicum.aggregation.model.data.FreeGetData; +import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.repository.RequestFeignRepositoryImpl; import ru.practicum.aggregation.repository.UserFeignRepositoryImpl; import ru.practicum.ewm.events.entity.Category; @@ -482,8 +482,9 @@ private Category getCategoryById(long categoryId) { ); } - private long getConfirmedRequestsCountByEvent(Long eventId) { - return requestFeignRepository.getConfirmedRequestsCount(List.of(eventId)).stream() + private long getConfirmedRequestsCountByEvent(long eventId) { + var eventIds = requestFeignRepository.getConfirmedRequestsCount(List.of(eventId)); + return eventIds.stream() .filter(requestCount -> requestCount.getEventId().equals(eventId)) .findAny() .map(EventRequestCount::getCount) diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java index aef0d9f..01dc010 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java @@ -6,7 +6,7 @@ import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; -import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; import ru.practicum.ewm.events.entity.Event; import ru.practicum.ewm.events.repository.EventRepository; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java index a759897..2bc98a4 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java @@ -13,6 +13,7 @@ public interface RequestService { @Transactional(readOnly = true) List findByEventId(Long userId, Long eventId); - EventRequestStatusUpdateResult updateStatusRequest(Long userId, Long eventId, + EventRequestStatusUpdateResult updateStatusRequest(Long userId, + Long eventId, EventRequestStatusUpdateRequest status); } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java index 9adba51..0b14033 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java @@ -7,7 +7,8 @@ import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; -import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException; +import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; import ru.practicum.aggregation.repository.RequestFeignRepository; import ru.practicum.aggregation.repository.UserFeignRepositoryImpl; import ru.practicum.ewm.events.repository.EventRepository; @@ -29,7 +30,7 @@ public List findByEventId(Long userId, Long eventId) { checkEvent(eventId); userFeignRepository.checkUser(userId); - return requestFeignRepository.findByEventId(userId, eventId); + return requestFeignRepository.findByUserIdAndEventId(userId, eventId); } @Override @@ -37,9 +38,18 @@ public EventRequestStatusUpdateResult updateStatusRequest(Long userId, Long eventId, EventRequestStatusUpdateRequest status ) { - checkEvent(eventId); userFeignRepository.checkUser(userId); - return requestFeignRepository.updateStatusRequest(userId, eventId, status); + var event = eventRepository.findById(eventId).orElseThrow( + () -> new NotFoundException("Событие с id=%s не найдено".formatted(eventId)) + ); + + if (!event.getInitiatorId().equals(userId)) { + throw new ConflictException("Пользователь с id=%s не является инициатором события с id=%s" + .formatted(eventId, userId) + ); + } + + return requestFeignRepository.updateStatusRequest(eventId, status); } private void checkEvent(Long eventId) { diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/RatingController.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/UserRatingController.java similarity index 96% rename from core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/RatingController.java rename to core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/UserRatingController.java index 543f0a1..f03b759 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/RatingController.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/UserRatingController.java @@ -11,7 +11,7 @@ @RestController @RequestMapping("/users/{userId}/events/{eventId}/likes") @RequiredArgsConstructor -public class RatingController { +public class UserRatingController { private final RatingService ratingService; diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java index 942f703..ea7f710 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java @@ -10,8 +10,8 @@ import ru.practicum.aggregation.dto.rating.output.RatingResponse; import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; import ru.practicum.aggregation.enums.Reaction; -import ru.practicum.aggregation.error.exception.ConflictException; -import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException; +import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; import ru.practicum.aggregation.repository.EventFeignRepository; import ru.practicum.aggregation.repository.UserFeignRepository; import ru.practicum.ewm.ratings.entity.Rating; @@ -30,7 +30,10 @@ public class RatingServiceImpl implements RatingService { EventFeignRepository eventFeignRepository; @Override - public RatingResponse addOrUpdateReaction(long userId, long eventId, @NonNull RatingCreateRequest request) { + public RatingResponse addOrUpdateReaction(long userId, + long eventId, + @NonNull RatingCreateRequest request) { + var user = userFeignRepository.getUserDtoById(userId); var event = eventFeignRepository.userFindEventById(userId, eventId); var initiator = event.initiator(); @@ -46,7 +49,8 @@ public RatingResponse addOrUpdateReaction(long userId, long eventId, @NonNull Ra if (rating.getReaction() == requestReaction) { ratingRepository.delete(rating); updateEventRate(eventId); - throw new ConflictException("Reaction removed"); + throw new ConflictException(""" + Реакция пользователя с id=%s событию с id=%s, удалена""".formatted(userId, eventId)); } else { rating.setReaction(requestReaction); ratingRepository.save(rating); @@ -67,8 +71,11 @@ public RatingResponse addOrUpdateReaction(long userId, long eventId, @NonNull Ra @Override public void removeReaction(long userId, long eventId) { - Rating rating = ratingRepository.findByUserIdAndEventId(userId, eventId) - .orElseThrow(() -> new NotFoundException("Реакция не найдена")); + Rating rating = ratingRepository.findByUserIdAndEventId(userId, eventId).orElseThrow(() -> + new NotFoundException( + "Реакция пользователя с id=%s событию с id=%s, не найдена".formatted(userId, eventId) + ) + ); ratingRepository.delete(rating); updateEventRate(eventId); } diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java deleted file mode 100644 index 68c9d58..0000000 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/AdminRequestController.java +++ /dev/null @@ -1,29 +0,0 @@ -package ru.practicum.ewm.requests.controller; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import ru.practicum.aggregation.model.repository.EventRequestCount; -import ru.practicum.aggregation.enums.ParticipationStatus; -import ru.practicum.ewm.requests.service.RequestService; - -import java.util.List; - -@Slf4j -@RestController -@RequestMapping(path = "/admin/requests") -@RequiredArgsConstructor -public class AdminRequestController { - - private final RequestService requestService; - - @GetMapping("/count") - public List getCount(@RequestParam List eventIds, - @RequestParam ParticipationStatus status) { - return requestService.getRequestsCount(eventIds, status); - } - -} diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/SystemRequestController.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/SystemRequestController.java new file mode 100644 index 0000000..61cac1c --- /dev/null +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/SystemRequestController.java @@ -0,0 +1,54 @@ +package ru.practicum.ewm.requests.controller; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; +import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; +import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; +import ru.practicum.aggregation.model.repository.EventRequestCount; +import ru.practicum.ewm.requests.service.RequestService; + +import java.util.List; + +@Slf4j +@RestController +@RequestMapping(path = "/system/requests") +@RequiredArgsConstructor +public class SystemRequestController { + + private final RequestService requestService; + + /** + * Найти количество подтвержденных запросов на участие по ID событий + * @param eventIds ID событий + * @return List.of({@link EventRequestCount}) + */ + @GetMapping("/count") + public List getConfirmedRequestsCount(@RequestParam List eventIds) { + return requestService.getRequestsCount(eventIds); + } + + @PatchMapping("/events/{eventId}/status") + public EventRequestStatusUpdateResult updateStatusRequest( + @PathVariable @Positive Long eventId, + @RequestBody @NotNull EventRequestStatusUpdateRequest request + ) { + return requestService.updateStatusRequest(eventId, request); + } + + /** + * + * @param userId id текущего пользователя + * @param eventId id события + * @return List<{@link ParticipationRequestDto}> + */ + @GetMapping("/users/{userId}/events/{eventId}") + public List findByUserIdAndEventId(@PathVariable @Positive Long userId, + @PathVariable @Positive Long eventId) { + return requestService.findByEventId(userId, eventId); + } + +} diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java index 985f333..10611d1 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java @@ -1,13 +1,10 @@ package ru.practicum.ewm.requests.controller; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; -import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.ewm.requests.service.RequestService; @@ -34,18 +31,6 @@ public List findByRequesterId(@PathVariable @Positive L return requestService.findByRequesterId(userId); } - /** - * - * @param userId id текущего пользователя - * @param eventId id события - * @return List<{@link ParticipationRequestDto}> - */ - @GetMapping("/{eventId}") - public List findByEventId(@PathVariable @Positive Long userId, - @PathVariable @Positive Long eventId) { - return requestService.findByEventId(userId, eventId); - } - /** * Добавление запроса от текущего пользователя на участие в событии *

@@ -73,15 +58,6 @@ public ParticipationRequestDto addParticipationRequest(@PathVariable @Positive L return requestService.addParticipationRequest(userId, eventId); } - @PatchMapping("/events/{eventId}/status") - public EventRequestStatusUpdateResult updateStatusRequest( - @PathVariable @Positive Long userId, - @PathVariable @Positive Long eventId, - @RequestBody @NotNull EventRequestStatusUpdateRequest request - ) { - return requestService.updateStatusRequest(userId, eventId, request); - } - /** * Отмена своего запроса на участие в событии * diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java deleted file mode 100644 index aed5565..0000000 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/UserMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.practicum.ewm.requests.mapper; - -import lombok.experimental.UtilityClass; -import org.springframework.lang.NonNull; -import ru.practicum.aggregation.dto.user.output.UserDto; -import ru.practicum.ewm.requests.model.User; - -@UtilityClass -public class UserMapper { - - public User toEntity(@NonNull UserDto userDto) { - return User.builder() - .id(userDto.id()) - .name(userDto.name()) - .email(userDto.email()) - .build(); - } - -} \ No newline at end of file diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java deleted file mode 100644 index 30bb13c..0000000 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/model/User.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.practicum.ewm.requests.model; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.experimental.FieldDefaults; -import lombok.experimental.SuperBuilder; -import ru.practicum.aggregation.model.entity.BaseEntity; - -@Getter -@Setter -@SuperBuilder -@NoArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE) -public class User extends BaseEntity { - String name; - String email; -} diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java index f1fc235..60e946f 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java @@ -1,11 +1,10 @@ package ru.practicum.ewm.requests.service; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; -import ru.practicum.aggregation.enums.ParticipationStatus; +import ru.practicum.aggregation.model.repository.EventRequestCount; import java.util.List; @@ -19,9 +18,9 @@ public interface RequestService { List findByRequesterId(Long userId); @Transactional(readOnly = true) - List getRequestsCount(List eventIds, ParticipationStatus status); + List getRequestsCount(List eventIds); - EventRequestStatusUpdateResult updateStatusRequest(Long userId, Long eventId, + EventRequestStatusUpdateResult updateStatusRequest(Long eventId, EventRequestStatusUpdateRequest request); ParticipationRequestDto addParticipationRequest(Long userId, Long eventId); diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java index ebdafbc..51c233c 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java @@ -7,20 +7,19 @@ import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import ru.practicum.aggregation.dto.event.output.EventFullDto; -import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; +import ru.practicum.aggregation.dto.user.output.UserDto; import ru.practicum.aggregation.enums.EventState; import ru.practicum.aggregation.enums.ParticipationStatus; -import ru.practicum.aggregation.error.exception.ConflictException; -import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException; +import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; +import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.repository.EventFeignRepository; import ru.practicum.aggregation.repository.UserFeignRepository; import ru.practicum.ewm.requests.entity.ParticipationRequest; import ru.practicum.ewm.requests.mapper.RequestMapper; -import ru.practicum.ewm.requests.mapper.UserMapper; -import ru.practicum.ewm.requests.model.User; import ru.practicum.ewm.requests.repository.RequestRepository; import java.time.LocalDateTime; @@ -52,12 +51,10 @@ public List findByEventId(Long userId, Long eventId) { .toList(); } - public EventRequestStatusUpdateResult updateStatusRequest(Long userId, Long eventId, + public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, + @NonNull EventRequestStatusUpdateRequest request) { var event = getEventById(eventId); - if (!event.initiator().id().equals(userId)) { - throw new NotFoundException("Событие не найдено"); - } int limit = event.participantLimit(); List confirmedRequests = new ArrayList<>(); @@ -122,7 +119,7 @@ public List findByRequesterId(Long userId) { } public ParticipationRequestDto addParticipationRequest(Long userId, Long eventId) { - User requester = getUserById(userId); + UserDto requester = getUserById(userId); var event = getEventById(eventId); if (!EventState.PUBLISHED.equals(event.state())) { @@ -165,7 +162,7 @@ public ParticipationRequestDto addParticipationRequest(Long userId, Long eventId } ParticipationRequest request = ParticipationRequest.builder() - .requesterId(requester.getId()) + .requesterId(requester.id()) .eventId(event.id()) .status(status) .created(LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS)) @@ -185,13 +182,13 @@ public ParticipationRequestDto cancelParticipationRequest(Long userId, Long requ } @Override - public List getRequestsCount(List eventIds, ParticipationStatus status) { - return requestRepository.getCountByEventIdsAndStatus(eventIds, status); + public List getRequestsCount(List eventIds) { + return requestRepository.getCountByEventIdsAndStatus(eventIds, ParticipationStatus.CONFIRMED); } @NonNull - private User getUserById(long userId) { - return UserMapper.toEntity(userFeignRepository.getUserDtoById(userId)); + private UserDto getUserById(long userId) { + return userFeignRepository.getUserDtoById(userId); } @NonNull diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java index 028b015..c872b55 100644 --- a/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java @@ -6,7 +6,6 @@ import org.springframework.web.bind.annotation.*; import ru.practicum.aggregation.dto.user.come.NewUserRequest; import ru.practicum.aggregation.dto.user.output.UserDto; -import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.ewm.users.service.UserService; import java.util.List; @@ -41,27 +40,6 @@ public UserDto getUser(@PathVariable Long userId) { return userService.getUser(userId); } - /** - * Проверка наличия пользователя по ID - * - * @param userId ID пользователя - */ - @GetMapping("/{userId}/check") - public void checkUser(@PathVariable Long userId) { - userService.remoteCheckUser(userId); - } - - /** - * Получение краткого списка пользователей - * - * @param userIds id пользователей - * @return {@link UserShortDto} - */ - @GetMapping("/short") - public List getShortUsersByIds(@RequestParam List userIds) { - return userService.getShortUsersByIds(userIds); - } - /** * Получение информации о пользователях *

diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/controller/SystemUserController.java b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/SystemUserController.java new file mode 100644 index 0000000..020c924 --- /dev/null +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/SystemUserController.java @@ -0,0 +1,38 @@ +package ru.practicum.ewm.users.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import ru.practicum.aggregation.dto.user.output.UserShortDto; +import ru.practicum.ewm.users.service.UserService; + +import java.util.List; + +@RestController +@RequestMapping(path = "/system/users") +@RequiredArgsConstructor +public class SystemUserController { + + private final UserService userService; + + /** + * Проверка наличия пользователя по ID + * + * @param userId ID пользователя + */ + @GetMapping("/{userId}/check") + public void checkUser(@PathVariable Long userId) { + userService.remoteCheckUser(userId); + } + + /** + * Получение краткого списка пользователей + * + * @param userIds id пользователей + * @return {@link UserShortDto} + */ + @GetMapping("/short") + public List getShortUsersByIds(@RequestParam List userIds) { + return userService.getShortUsersByIds(userIds); + } + +} diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java b/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java index a726a0b..fad9fe6 100644 --- a/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/service/UserServiceImpl.java @@ -7,8 +7,8 @@ import ru.practicum.aggregation.dto.user.come.NewUserRequest; import ru.practicum.aggregation.dto.user.output.UserDto; import ru.practicum.aggregation.dto.user.output.UserShortDto; -import ru.practicum.aggregation.error.exception.ConflictException; -import ru.practicum.aggregation.error.exception.NotFoundException; +import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException; +import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; import ru.practicum.ewm.users.entity.User; import ru.practicum.ewm.users.mapper.UserMapper; import ru.practicum.ewm.users.repository.UserRepository; diff --git a/infra/config-repo/application.yml b/infra/config-repo/application.yml index 0a1d54f..b560087 100644 --- a/infra/config-repo/application.yml +++ b/infra/config-repo/application.yml @@ -10,10 +10,10 @@ logging: level: root: info org: - springframework: error + springframework: info management: endpoints: web: exposure: - include: health, metrics, prometheus, info, mappings \ No newline at end of file + include: health, metrics, prometheus, info, mappings, refresh \ No newline at end of file diff --git a/infra/config-repo/core/event-service.yml b/infra/config-repo/core/event-service.yml index 4cee133..503c62d 100644 --- a/infra/config-repo/core/event-service.yml +++ b/infra/config-repo/core/event-service.yml @@ -39,7 +39,7 @@ resilience4j: wait-duration-in-open-state: 10s permitted-number-of-calls-in-half-open-state: 3 ignore-exceptions: - - ru.practicum.aggregation.error.exception.BusinessException + - ru.practicum.aggregation.error.exception.bussines.ServiceProcessingException - jakarta.validation.ValidationException timelimiter: configs: diff --git a/infra/config-repo/core/rating-service.yml b/infra/config-repo/core/rating-service.yml index 4d4b25f..a2f0a10 100644 --- a/infra/config-repo/core/rating-service.yml +++ b/infra/config-repo/core/rating-service.yml @@ -39,7 +39,7 @@ resilience4j: wait-duration-in-open-state: 10s permitted-number-of-calls-in-half-open-state: 3 ignore-exceptions: - - ru.practicum.aggregation.error.exception.BusinessException + - ru.practicum.aggregation.error.exception.bussines.ServiceProcessingException - jakarta.validation.ValidationException timelimiter: configs: diff --git a/infra/config-repo/core/request-service.yml b/infra/config-repo/core/request-service.yml index 5496a3c..addf4e4 100644 --- a/infra/config-repo/core/request-service.yml +++ b/infra/config-repo/core/request-service.yml @@ -39,7 +39,7 @@ resilience4j: wait-duration-in-open-state: 10s permitted-number-of-calls-in-half-open-state: 3 ignore-exceptions: - - ru.practicum.aggregation.error.exception.BusinessException + - ru.practicum.aggregation.error.exception.bussines.ServiceProcessingException - jakarta.validation.ValidationException timelimiter: configs: diff --git a/infra/config-repo/core/user-service.yml b/infra/config-repo/core/user-service.yml index 590cfdf..ed1a7d8 100644 --- a/infra/config-repo/core/user-service.yml +++ b/infra/config-repo/core/user-service.yml @@ -39,7 +39,7 @@ resilience4j: wait-duration-in-open-state: 10s permitted-number-of-calls-in-half-open-state: 3 ignore-exceptions: - - ru.practicum.aggregation.error.exception.BusinessException + - ru.practicum.aggregation.error.exception.bussines.ServiceProcessingException - jakarta.validation.ValidationException timelimiter: configs: diff --git a/infra/config-repo/infra/gateway-server.yml b/infra/config-repo/infra/gateway-server.yml index c41afc7..108cb4d 100644 --- a/infra/config-repo/infra/gateway-server.yml +++ b/infra/config-repo/infra/gateway-server.yml @@ -1,79 +1,94 @@ spring: cloud: gateway: - discovery: - locator: - enabled: false - routes: + server: + webflux: + httpclient: + wiretap: true + httpserver: + wiretap: true + discovery: + locator: + enabled: false + routes: - - id: ratings - uri: lb://rating-service - predicates: - - Path=/users/*/events/*/likes/** + # ==================== ADMIN ==================== - - id: user-requests - uri: lb://request-service - predicates: - - Path=/users/*/requests/** + - id: admin-users + order: 0 + uri: lb://user-service + predicates: + - Path=/admin/users/** - - id: user-events - uri: lb://event-service - predicates: - - Path=/users/*/events/** + - id: admin-categories + order: 0 + uri: lb://event-service + predicates: + - Path=/admin/categories/** - - id: admin-users - uri: lb://user-service - predicates: - - Path=/admin/users/** + - id: admin-compilations + order: 0 + uri: lb://event-service + predicates: + - Path=/admin/compilations/** - - id: admin-categories - uri: lb://event-service - predicates: - - Path=/admin/categories/** + - id: admin-events + order: 0 + uri: lb://event-service + predicates: + - Path=/admin/events/** - - id: admin-compilations - uri: lb://event-service - predicates: - - Path=/admin/compilations/** + # ==================== USER ==================== - - id: admin-events - uri: lb://event-service - predicates: - - Path=/admin/events/** + - id: user-ratings + order: -2 + uri: lb://rating-service + predicates: + - Path=/users/{userId}/events/{eventId}/likes/** - - id: categories - uri: lb://event-service - predicates: - - Path=/categories/** + - id: user-events-requests + order: -1 + uri: lb://event-service + predicates: + - Path=/users/{userId}/events/{eventId}/requests/** - - id: compilations - uri: lb://event-service - predicates: - - Path=/compilations/** + - id: user-events + order: 0 + uri: lb://event-service + predicates: + - Path=/users/{userId}/events/** - - id: events - uri: lb://event-service - predicates: - - Path=/events/** + - id: user-requests + order: 0 + uri: lb://request-service + predicates: + - Path=/users/{userId}/requests/** - globalcors: - add-to-simple-url-handler-mapping: true - cors-configurations: - '[/**]': - allowed-origins: - - http://localhost:3000 - - http://localhost:5173 - - http://localhost:8443 - allowed-methods: - - GET - - POST - - PUT - - PATCH - - DELETE - - OPTIONS - allowed-headers: - - '*' - exposed-headers: - - Location - allow-credentials: true - max-age: 3600 + # ==================== FREE ==================== + + - id: free-events + order: 0 + uri: lb://event-service + predicates: + - Path=/events/** + + - id: free-categories + order: 0 + uri: lb://event-service + predicates: + - Path=/categories/** + + - id: free-compilations + order: 0 + uri: lb://event-service + predicates: + - Path=/compilations/** + +management: + endpoint: + gateway: + access: read-only + endpoints: + web: + exposure: + include: gateway, health, metrics, prometheus, info, mappings, refresh diff --git a/infra/config-server/src/main/resources/application.yml b/infra/config-server/src/main/resources/application.yml index 4722886..de85a5e 100644 --- a/infra/config-server/src/main/resources/application.yml +++ b/infra/config-server/src/main/resources/application.yml @@ -36,6 +36,12 @@ eureka: lease-renewal-interval-in-seconds: 5 lease-expiration-duration-in-seconds: 10 +management: + endpoints: + web: + exposure: + include: health, metrics, prometheus, info, mappings, refresh + logging: level: root: info diff --git a/infra/gateway-server/src/main/java/ru/practicum/ewm/infra/gateway/error/ApiError.java b/infra/gateway-server/src/main/java/ru/practicum/ewm/infra/gateway/error/ApiError.java new file mode 100644 index 0000000..968c7f2 --- /dev/null +++ b/infra/gateway-server/src/main/java/ru/practicum/ewm/infra/gateway/error/ApiError.java @@ -0,0 +1,24 @@ +package ru.practicum.ewm.infra.gateway.error; + +import lombok.Builder; +import org.springframework.http.HttpStatus; + +import java.time.LocalDateTime; + +/** + * @param status Код статуса HTTP-ответа + * @param reason Общее описание причины ошибки + * @param message Сообщение об ошибке + * @param errors Список стектрейсов или описания ошибок + * @param timestamp Дата и время когда произошла ошибка (в формате "yyyy-MM-dd HH:mm:ss") + */ +@Builder(toBuilder = true) +public record ApiError( + HttpStatus status, + String reason, + String message, + String errors, + LocalDateTime timestamp +) { +} + diff --git a/infra/gateway-server/src/main/java/ru/practicum/ewm/infra/gateway/error/GeneralErrorHandler.java b/infra/gateway-server/src/main/java/ru/practicum/ewm/infra/gateway/error/GeneralErrorHandler.java new file mode 100644 index 0000000..8659b4a --- /dev/null +++ b/infra/gateway-server/src/main/java/ru/practicum/ewm/infra/gateway/error/GeneralErrorHandler.java @@ -0,0 +1,40 @@ +package ru.practicum.ewm.infra.gateway.error; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.lang.NonNull; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.reactive.resource.NoResourceFoundException; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.time.LocalDateTime; + +import static org.springframework.http.HttpStatus.*; + +@Slf4j +@RestControllerAdvice +public class GeneralErrorHandler { + + @ExceptionHandler + @ResponseStatus(INTERNAL_SERVER_ERROR) + public ApiError handleNoResourceFoundException(NoResourceFoundException ex) { + log.info("500 Internal Server Error (NoResourceFoundException) {}", ex.getMessage(), ex); + String stackTrace = getStackTrace(ex); + return ApiError.builder() + .status(INTERNAL_SERVER_ERROR) + .reason("Resource not found") + .message(ex.getMessage()) + .errors(stackTrace) + .timestamp(LocalDateTime.now()) + .build(); + } + + private String getStackTrace(@NonNull Exception e) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + return sw.toString(); + } +} From e6784c2055275fd4e93f52292324de2b1e16d4e2 Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Sat, 29 Aug 2026 01:03:13 +0300 Subject: [PATCH 07/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Донастройка stats-server Большинство тестов проходит --- infra/config-repo/stats/stats-server.yml | 14 +++++----- ...StatServerApp.java => StatsServerApp.java} | 4 +-- .../src/main/resources/application-test.yaml | 26 ------------------- .../main/resources/application-test.yaml.bak | 12 +++++++++ .../src/main/resources/application.yaml | 8 +++--- .../{schema.sql => schema-postgres.sql} | 0 6 files changed, 26 insertions(+), 38 deletions(-) rename stats/stats-server/src/main/java/ru/practicum/stat/server/{StatServerApp.java => StatsServerApp.java} (73%) delete mode 100644 stats/stats-server/src/main/resources/application-test.yaml create mode 100644 stats/stats-server/src/main/resources/application-test.yaml.bak rename stats/stats-server/src/main/resources/{schema.sql => schema-postgres.sql} (100%) diff --git a/infra/config-repo/stats/stats-server.yml b/infra/config-repo/stats/stats-server.yml index 1192cf2..92bfb29 100644 --- a/infra/config-repo/stats/stats-server.yml +++ b/infra/config-repo/stats/stats-server.yml @@ -1,16 +1,18 @@ spring: - sql: - init: - mode: always + sql.init: + mode: ALWAYS + schema-locations: classpath:schema-postgres.sql + jackson: + serialization.INDENT_OUTPUT: true datasource: driver-class-name: org.postgresql.Driver url: jdbc:postgresql://localhost:6542/ewm-stats username: dbuser password: 12345 jpa: - hibernate: - ddl-auto: none + generate-ddl: false + hibernate.ddl-auto: validate + show-sql: false properties: hibernate: format_sql: true - show-sql: true diff --git a/stats/stats-server/src/main/java/ru/practicum/stat/server/StatServerApp.java b/stats/stats-server/src/main/java/ru/practicum/stat/server/StatsServerApp.java similarity index 73% rename from stats/stats-server/src/main/java/ru/practicum/stat/server/StatServerApp.java rename to stats/stats-server/src/main/java/ru/practicum/stat/server/StatsServerApp.java index 9a0911f..31068cb 100644 --- a/stats/stats-server/src/main/java/ru/practicum/stat/server/StatServerApp.java +++ b/stats/stats-server/src/main/java/ru/practicum/stat/server/StatsServerApp.java @@ -4,8 +4,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class StatServerApp { +public class StatsServerApp { public static void main(String[] args) { - SpringApplication.run(StatServerApp.class, args); + SpringApplication.run(StatsServerApp.class, args); } } diff --git a/stats/stats-server/src/main/resources/application-test.yaml b/stats/stats-server/src/main/resources/application-test.yaml deleted file mode 100644 index 10090ac..0000000 --- a/stats/stats-server/src/main/resources/application-test.yaml +++ /dev/null @@ -1,26 +0,0 @@ -spring: - config: - import: "optional:configserver:" - cloud: - config: - enabled: false - fail-fast: false - discovery: - enabled: false - application: - name: stats-server - datasource: - driver-class-name: org.h2.Driver - url: jdbc:h2:mem:ewm-stats - jpa: - hibernate: - ddl-auto: none - show-sql: true - -eureka: - client: - enabled: false - -logging: - level: - org.springframework.cloud: DEBUG \ No newline at end of file diff --git a/stats/stats-server/src/main/resources/application-test.yaml.bak b/stats/stats-server/src/main/resources/application-test.yaml.bak new file mode 100644 index 0000000..0a6d29e --- /dev/null +++ b/stats/stats-server/src/main/resources/application-test.yaml.bak @@ -0,0 +1,12 @@ +spring: + datasource: + driver-class-name: org.h2.Driver + url: jdbc:h2:mem:ewm-stats + jpa: + hibernate: + ddl-auto: none + show-sql: true + +logging: + level: + org.springframework.cloud: DEBUG \ No newline at end of file diff --git a/stats/stats-server/src/main/resources/application.yaml b/stats/stats-server/src/main/resources/application.yaml index 26f3ca3..e4f93c2 100644 --- a/stats/stats-server/src/main/resources/application.yaml +++ b/stats/stats-server/src/main/resources/application.yaml @@ -13,10 +13,10 @@ spring: service-id: config-server fail-fast: true retry: - initial-interval: 1000 # начальная задержка (мс) - max-interval: 10000 # максимальная задержка (мс) - multiplier: 2.0 # множитель увеличения задержки - max-attempts: 20 # количество попыток (0 = бесконечно) + initial-interval: 1000 + max-interval: 10000 + multiplier: 2.0 + max-attempts: 20 eureka: client: diff --git a/stats/stats-server/src/main/resources/schema.sql b/stats/stats-server/src/main/resources/schema-postgres.sql similarity index 100% rename from stats/stats-server/src/main/resources/schema.sql rename to stats/stats-server/src/main/resources/schema-postgres.sql From ba403ee2dc480dbcd4d7070e2ff66f59e142914b Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Sat, 29 Aug 2026 04:11:37 +0300 Subject: [PATCH 08/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Устранение длинной цепочки вызовов в RequestServiceImpl.getEventById() Большинство тестов проходит --- .../aggregation/client/event/EventClient.java | 7 ++---- .../client/event/EventFallbackFactory.java | 2 +- .../repository/EventFeignRepository.java | 2 +- .../repository/EventFeignRepositoryImpl.java | 5 ++-- .../system/SystemEventController.java | 11 ++++---- .../events/service/event/EventService.java | 2 +- .../service/event/EventServiceImpl.java | 25 +++++++++++-------- .../requests/service/RequestServiceImpl.java | 3 ++- 8 files changed, 30 insertions(+), 27 deletions(-) diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java index 570c057..4d10c64 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java @@ -3,10 +3,7 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.*; import ru.practicum.aggregation.dto.event.output.EventFullDto; import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; @@ -24,6 +21,6 @@ EventFullDto userFindEventById(@PathVariable Long userId, void systemUpdateRating(@RequestBody @NotNull @Valid RatingUpdateRequest request); @GetMapping("/system/events/{eventId}") - EventFullDto systemFindEventById(@PathVariable Long eventId); + EventFullDto systemFindEventById(@PathVariable Long eventId, @RequestParam Integer confirmets); } \ No newline at end of file diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java index 68b7e02..10291b5 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java @@ -25,7 +25,7 @@ public void systemUpdateRating(RatingUpdateRequest request) { } @Override - public EventFullDto systemFindEventById(Long eventId) { + public EventFullDto systemFindEventById(Long eventId, Integer confirmets) { throw RemoteExceptionMapper.mapEventException(cause); } }; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java index 04441ea..eb8ceec 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java @@ -10,5 +10,5 @@ public interface EventFeignRepository { EventFullDto userFindEventById(long userId, long eventId); - EventFullDto systemFindEventById(long eventId); + EventFullDto systemFindEventById(long eventId, int confirmets); } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java index 7946721..0cadcbb 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java @@ -72,11 +72,12 @@ public EventFullDto userFindEventById(long userId, long eventId) { } @Override - public EventFullDto systemFindEventById(long eventId) { + public EventFullDto systemFindEventById(long eventId, int confirmets) { log.info(""" PLEASE WAITING Система ищет событие с id={}""", eventId); - return switch (RemoteCallExecutor.execute(() -> eventClient.systemFindEventById(eventId))) { + return switch (RemoteCallExecutor.execute(() -> + eventClient.systemFindEventById(eventId, confirmets))) { case RemoteCallResult.Success(var response) -> response; case RemoteCallResult.Failure(var exeption) -> { log.warn(""" diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java index 09cea82..e16b228 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java @@ -1,14 +1,12 @@ package ru.practicum.ewm.events.controller.system; import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import ru.practicum.aggregation.dto.event.output.EventFullDto; import ru.practicum.ewm.events.service.event.EventService; @@ -22,7 +20,8 @@ public class SystemEventController { EventService eventService; @GetMapping("/{eventId}") - EventFullDto systemFindEventById(@PathVariable @Positive Long eventId) { - return eventService.findEventById(eventId); + EventFullDto systemFindEventById(@PathVariable @Positive Long eventId, + @RequestParam @PositiveOrZero Integer confirmets) { + return eventService.findEventById(eventId, confirmets); } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java index c6fa2ca..ec13283 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java @@ -42,7 +42,7 @@ public interface EventService { EventFullDto findEventByUserIdAndEventId(Long userId, Long eventId); /// Получение полной информации о событии по ID - EventFullDto findEventById(long eventId); + EventFullDto findEventById(long eventId, int confirmets); /// Изменение события добавленного текущим пользователем @Transactional diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java index 135c0f8..c270357 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java @@ -258,14 +258,6 @@ public List adminGetEvents(@NonNull AdminGetData dto) { .toList(); } - @NonNull - private Map getUserShortDtoMap(@NonNull List events) { - List userIds = events.stream().map(Event::getInitiatorId).toList(); - List users = userFeignRepository.getUsersByIds(userIds); - return users.stream() - .collect(Collectors.toMap(UserShortDto::id, user -> user)); - } - @Override public EventFullDto adminUpdateEvent(Long eventId, @NonNull UpdateEventAdminRequest request) { Event oldEvent = getEventById(eventId); @@ -379,10 +371,15 @@ public EventFullDto findEventByUserIdAndEventId(Long userId, Long eventId) { } @Override - public EventFullDto findEventById(long eventId) { + public EventFullDto findEventById(long eventId, int confirmets) { Event event = getEventById(eventId); + var eventData = EventData.builder() + .initiator(UserMapper.toUserShortDto(getUserById(event.getInitiatorId()))) + .confirmedRequests(confirmets) + .views(getHits(event.getId())) + .build(); - return EventMapper.toEventFullDto(event, getEventData(event)); + return EventMapper.toEventFullDto(event, eventData); } @Override @@ -396,6 +393,14 @@ public EventFullDto patchEvent(Long userId, Long eventId, @NonNull UpdateEventUs return patchEvent(eventId, request, false); } + @NonNull + private Map getUserShortDtoMap(@NonNull List events) { + List userIds = events.stream().map(Event::getInitiatorId).toList(); + List users = userFeignRepository.getUsersByIds(userIds); + return users.stream() + .collect(Collectors.toMap(UserShortDto::id, user -> user)); + } + private EventData getEventData(@NonNull Event event) { return EventData.builder() .initiator(UserMapper.toUserShortDto(getUserById(event.getInitiatorId()))) diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java index 51c233c..9758e39 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java @@ -193,7 +193,8 @@ private UserDto getUserById(long userId) { @NonNull private EventFullDto getEventById(long eventId) { - return eventFeignRepository.systemFindEventById(eventId); + int confirmets = requestRepository.countByEventIdAndStatus(eventId, ParticipationStatus.CONFIRMED); + return eventFeignRepository.systemFindEventById(eventId, confirmets); } @NonNull From 43065672136631842892ab47e43cc7eb0ee83ad8 Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Mon, 31 Aug 2026 00:51:06 +0300 Subject: [PATCH 09/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправление ошибок из основных тестов Дебагинг метода updateStatusRequest() Рефакторинг RequestMapper Дебаг RequestRepository.getCountByEventIdsAndStatus() EventRequestCount теперь record Переименованы сервисы в event-service Добавлена зависимость okhttp Основные тесты проходят Остались рейтинги --- .../client/request/RequestClient.java | 2 + .../request/RequestFallbackFactory.java | 8 ++- .../come/EventRequestStatusUpdateRequest.java | 8 ++- .../output/ParticipationRequestDto.java | 4 +- .../model/repository/EventRequestCount.java | 14 ++-- .../repository/RequestFeignRepository.java | 2 + .../RequestFeignRepositoryImpl.java | 24 ++++--- .../system/SystemRatingController.java | 6 +- .../controller/user/UserEventController.java | 11 +-- .../ru/practicum/ewm/events/entity/Event.java | 2 +- .../compilation/CompilationServiceImpl.java | 4 +- .../service/event/EventServiceImpl.java | 20 +++--- ...ngService.java => EventRatingService.java} | 2 +- ...eImpl.java => EventRatingServiceImpl.java} | 2 +- ...tService.java => EventRequestService.java} | 8 +-- ...Impl.java => EventRequestServiceImpl.java} | 15 ++-- core/pom.xml | 6 ++ .../controller/SystemRequestController.java | 7 +- .../ewm/requests/mapper/RequestMapper.java | 13 +--- .../repository/RequestRepository.java | 13 ++-- .../ewm/requests/service/RequestService.java | 12 ++-- .../requests/service/RequestServiceImpl.java | 69 +++++++++---------- infra/config-repo/core/event-service.yml | 2 + infra/config-repo/core/rating-service.yml | 2 + infra/config-repo/core/request-service.yml | 2 + infra/config-repo/core/user-service.yml | 2 + 26 files changed, 143 insertions(+), 117 deletions(-) rename core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/{RatingService.java => EventRatingService.java} (87%) rename core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/{RatingServiceImpl.java => EventRatingServiceImpl.java} (93%) rename core/event-service/src/main/java/ru/practicum/ewm/events/service/request/{RequestService.java => EventRequestService.java} (70%) rename core/event-service/src/main/java/ru/practicum/ewm/events/service/request/{RequestServiceImpl.java => EventRequestServiceImpl.java} (79%) diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java index 8c984cf..1e03c4f 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestClient.java @@ -23,6 +23,8 @@ List findByUserIdAndEventId(@PathVariable @Positive Lon @PatchMapping("/system/requests/events/{eventId}/status") EventRequestStatusUpdateResult updateStatusRequest( @PathVariable Long eventId, + @RequestParam Integer participantLimit, + @RequestParam Boolean requestModeration, @RequestBody EventRequestStatusUpdateRequest request ); diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java index 2392ead..a4cd1d0 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/request/RequestFallbackFactory.java @@ -24,8 +24,12 @@ public List findByUserIdAndEventId(Long userId, Long ev } @Override - public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, - EventRequestStatusUpdateRequest request) { + public EventRequestStatusUpdateResult updateStatusRequest( + Long eventId, + Integer participantLimit, + Boolean requestModeration, + EventRequestStatusUpdateRequest request + ) { throw RemoteExceptionMapper.mapRequestException(cause); } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/come/EventRequestStatusUpdateRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/come/EventRequestStatusUpdateRequest.java index 6625add..2a25fad 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/come/EventRequestStatusUpdateRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/come/EventRequestStatusUpdateRequest.java @@ -1,20 +1,22 @@ package ru.practicum.aggregation.dto.participation.come; +import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; import lombok.Builder; import ru.practicum.aggregation.enums.RequestUpdateStatus; import java.util.List; /** - * @apiNote - * {@link List}<{@link Long}> requestIds
+ * @apiNote {@link List}<{@link Long}> requestIds
* {@link RequestUpdateStatus} status */ @Builder public record EventRequestStatusUpdateRequest( - List requestIds, + @NotEmpty + List<@Positive(message = "id запроса на участие должен быть положительным числом") Long> requestIds, @NotNull RequestUpdateStatus status diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/ParticipationRequestDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/ParticipationRequestDto.java index b5fe351..3d8ece5 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/ParticipationRequestDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/ParticipationRequestDto.java @@ -3,6 +3,8 @@ import lombok.Builder; import ru.practicum.aggregation.enums.ParticipationStatus; +import java.time.LocalDateTime; + /** * @apiNote * {@link Long} id
@@ -14,7 +16,7 @@ @Builder public record ParticipationRequestDto( Long id, - String created, + LocalDateTime created, Long event, Long requester, ParticipationStatus status diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java index f701fae..f73802a 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java @@ -1,13 +1,11 @@ package ru.practicum.aggregation.model.repository; - /** * @apiNote - * Long getEventId()
- * Long getCount() + * {@link Long} eventId
+ * {@link Long} count */ -public interface EventRequestCount { - - Long getEventId(); - - Long getCount(); +public record EventRequestCount( + Long eventId, + Long count +) { } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java index 4bbeab4..378845c 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepository.java @@ -14,5 +14,7 @@ public interface RequestFeignRepository { List findByUserIdAndEventId(long userId, long eventId); EventRequestStatusUpdateResult updateStatusRequest(Long eventId, + Integer participantLimit, + Boolean requestModeration, EventRequestStatusUpdateRequest status); } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java index 2ee7dad..07e228a 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java @@ -61,13 +61,15 @@ public List findByUserIdAndEventId(long userId, long ev case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение - Не удалось получить запросы пользователей на участие в событии с id={}""", eventId); + Пользователю с id={} + Не удалось получить запросы на участие в событии с id={}""", userId, eventId); throw exeption; } case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация - Не удалось получить запросы пользователей на участие в событии с id={}""", eventId); + Пользователю с id={} + Не удалось получить запросы на участие в событии с id={}""", userId, eventId); throw new RequestServiceUnavailableException(cause); } }; @@ -75,26 +77,28 @@ public List findByUserIdAndEventId(long userId, long ev @Override public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, - EventRequestStatusUpdateRequest status) { + Integer participantLimit, + Boolean requestModeration, + EventRequestStatusUpdateRequest request) { log.info(""" PLEASE WAITING - Система обновляет запрос на участие в событии с id={} - status: {}""", eventId, status); + Система обновляет статус запросов на участие в событии с id={} + request: {}""", eventId, request); return switch (RemoteCallExecutor.execute(() -> - requestClient.updateStatusRequest(eventId, status))) { + requestClient.updateStatusRequest(eventId, participantLimit, requestModeration, request))) { case RemoteCallResult.Success(var response) -> response; case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение - Не удалось получить запросы пользователей на участие - в событии с id={}""", eventId); + Не удалось обновить статус запросов на участие в событии с id={} + request: {}""", eventId, request); throw exeption; } case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация - Не удалось получить запросы пользователей на участие - в событии с id={}""", eventId); + Не удалось обновить статус запросов на участие в событии с id={} + request: {}""", eventId, request); throw new RequestServiceUnavailableException(cause); } }; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java index c8cfd71..6bd9ceb 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemRatingController.java @@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; -import ru.practicum.ewm.events.service.rating.RatingService; +import ru.practicum.ewm.events.service.rating.EventRatingService; @Slf4j @RestController @@ -19,10 +19,10 @@ @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class SystemRatingController { - RatingService ratingService; + EventRatingService eventRatingService; @PatchMapping public void updateRating(@RequestBody @NotNull RatingUpdateRequest request) { - ratingService.updateRating(request); + eventRatingService.updateRating(request); } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java index 51f68c7..527b464 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java @@ -1,6 +1,7 @@ package ru.practicum.ewm.events.controller.user; import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.PositiveOrZero; import lombok.RequiredArgsConstructor; @@ -14,7 +15,7 @@ import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.ewm.events.service.event.EventService; -import ru.practicum.ewm.events.service.request.RequestService; +import ru.practicum.ewm.events.service.request.EventRequestService; import java.util.List; @@ -24,7 +25,7 @@ public class UserEventController { private final EventService eventService; - private final RequestService requestService; + private final EventRequestService eventRequestService; /** * Получение событий, добавленных текущим пользователем @@ -113,7 +114,7 @@ public EventFullDto patchEvent(@PathVariable @Positive Long userId, public List getRequests(@PathVariable @Positive Long userId, @PathVariable @Positive Long eventId) { - return requestService.findByEventId(userId, eventId); + return eventRequestService.findByEventId(userId, eventId); } /** @@ -139,9 +140,9 @@ public List getRequests(@PathVariable @Positive Long us public EventRequestStatusUpdateResult patchRequests( @PathVariable @Positive Long userId, @PathVariable @Positive Long eventId, - @RequestBody @Valid EventRequestStatusUpdateRequest status + @RequestBody @NotNull @Valid EventRequestStatusUpdateRequest status ) { - return requestService.updateStatusRequest(userId, eventId, status); + return eventRequestService.updateStatusRequest(userId, eventId, status); } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Event.java b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Event.java index dfb5b95..4d09417 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Event.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/entity/Event.java @@ -67,7 +67,7 @@ public class Event extends BaseEntity { LocalDateTime eventDate; /// Пользователь инициатор события - @JoinColumn(name = "initiator_id") + @JoinColumn(name = "initiator_id", nullable = false) Long initiatorId; /// Широта и долгота места проведения события diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java index bee308c..0d06187 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java @@ -192,8 +192,8 @@ private Map getConfirmedRequests(@NonNull Collection co return eventRequestCountList.stream() .collect(Collectors.toMap( - EventRequestCount::getEventId, - EventRequestCount::getCount, + EventRequestCount::eventId, + EventRequestCount::count, (existing, replacement) -> existing ) ); diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java index c270357..7edf1ca 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java @@ -123,7 +123,7 @@ public List getFreeEvents(@NonNull FreeGetData dto, HttpServletRe Map requestCountMap = new HashMap<>(); if (!eventRequestCountList.isEmpty()) { eventRequestCountList.forEach(eventRequestCount -> - requestCountMap.put(eventRequestCount.getEventId(), eventRequestCount.getCount()) + requestCountMap.put(eventRequestCount.eventId(), eventRequestCount.count()) ); } @@ -234,7 +234,7 @@ public List adminGetEvents(@NonNull AdminGetData dto) { Map requestCountMap = new HashMap<>(); if (!eventRequestCountList.isEmpty()) { eventRequestCountList.forEach(eventRequestCount -> - requestCountMap.put(eventRequestCount.getEventId(), eventRequestCount.getCount()) + requestCountMap.put(eventRequestCount.eventId(), eventRequestCount.count()) ); } @@ -246,7 +246,7 @@ public List adminGetEvents(@NonNull AdminGetData dto) { return events.stream() .map(event -> { - var userId = requestCountMap.get(event.getInitiatorId()); + var userId = event.getInitiatorId(); var eventData = EventData.builder() .initiator(initiators.get(userId)) .confirmedRequests(getConfirmedRequestsCountFromMap( @@ -335,7 +335,7 @@ public List findByUserId(Long userId, Integer from, Integer size) Map requestCountMap = new HashMap<>(); if (!eventRequestCountList.isEmpty()) { eventRequestCountList.forEach(eventRequestCount -> - requestCountMap.put(eventRequestCount.getEventId(), eventRequestCount.getCount()) + requestCountMap.put(eventRequestCount.eventId(), eventRequestCount.count()) ); } @@ -488,12 +488,12 @@ private Category getCategoryById(long categoryId) { } private long getConfirmedRequestsCountByEvent(long eventId) { - var eventIds = requestFeignRepository.getConfirmedRequestsCount(List.of(eventId)); - return eventIds.stream() - .filter(requestCount -> requestCount.getEventId().equals(eventId)) - .findAny() - .map(EventRequestCount::getCount) - .orElse(0L); + var counts = requestFeignRepository.getConfirmedRequestsCount(List.of(eventId)); + if (counts.isEmpty()) { + return 0; + } + + return counts.getFirst().count(); } private long getHits(long eventId) { diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/EventRatingService.java similarity index 87% rename from core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingService.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/EventRatingService.java index e53168f..aecad37 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/EventRatingService.java @@ -4,7 +4,7 @@ import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; @Transactional -public interface RatingService { +public interface EventRatingService { void updateRating(RatingUpdateRequest request); } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/EventRatingServiceImpl.java similarity index 93% rename from core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/EventRatingServiceImpl.java index 01dc010..fd9124a 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/RatingServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/EventRatingServiceImpl.java @@ -13,7 +13,7 @@ @Service @RequiredArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class RatingServiceImpl implements RatingService { +public class EventRatingServiceImpl implements EventRatingService { EventRepository eventRepository; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/EventRequestService.java similarity index 70% rename from core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/service/request/EventRequestService.java index 2bc98a4..09f9154 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/EventRequestService.java @@ -8,12 +8,12 @@ import java.util.List; @Transactional -public interface RequestService { +public interface EventRequestService { @Transactional(readOnly = true) - List findByEventId(Long userId, Long eventId); + List findByEventId(long userId, long eventId); - EventRequestStatusUpdateResult updateStatusRequest(Long userId, - Long eventId, + EventRequestStatusUpdateResult updateStatusRequest(long userId, + long eventId, EventRequestStatusUpdateRequest status); } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/EventRequestServiceImpl.java similarity index 79% rename from core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java rename to core/event-service/src/main/java/ru/practicum/ewm/events/service/request/EventRequestServiceImpl.java index 0b14033..c715bf3 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/RequestServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/request/EventRequestServiceImpl.java @@ -18,7 +18,7 @@ @Service @RequiredArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class RequestServiceImpl implements RequestService { +public class EventRequestServiceImpl implements EventRequestService { EventRepository eventRepository; @@ -26,7 +26,7 @@ public class RequestServiceImpl implements RequestService { UserFeignRepositoryImpl userFeignRepository; @Override - public List findByEventId(Long userId, Long eventId) { + public List findByEventId(long userId, long eventId) { checkEvent(eventId); userFeignRepository.checkUser(userId); @@ -34,9 +34,9 @@ public List findByEventId(Long userId, Long eventId) { } @Override - public EventRequestStatusUpdateResult updateStatusRequest(Long userId, - Long eventId, - EventRequestStatusUpdateRequest status + public EventRequestStatusUpdateResult updateStatusRequest(long userId, + long eventId, + EventRequestStatusUpdateRequest request ) { userFeignRepository.checkUser(userId); var event = eventRepository.findById(eventId).orElseThrow( @@ -49,10 +49,11 @@ public EventRequestStatusUpdateResult updateStatusRequest(Long userId, ); } - return requestFeignRepository.updateStatusRequest(eventId, status); + return requestFeignRepository.updateStatusRequest( + eventId, event.getParticipantLimit(), event.isRequestModeration(), request); } - private void checkEvent(Long eventId) { + private void checkEvent(long eventId) { if (!eventRepository.existsById(eventId)) { throw new NotFoundException("Событие с id=%s не найдено".formatted(eventId)); } diff --git a/core/pom.xml b/core/pom.xml index fbe6d11..c91243d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,6 +29,12 @@ provided true + + + + io.github.openfeign + feign-okhttp + diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/SystemRequestController.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/SystemRequestController.java index 61cac1c..ef3ace6 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/SystemRequestController.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/SystemRequestController.java @@ -2,6 +2,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; @@ -34,9 +35,11 @@ public List getConfirmedRequestsCount(@RequestParam List findByUserIdAndEventId(@PathVariable @Positive Long userId, @PathVariable @Positive Long eventId) { - return requestService.findByEventId(userId, eventId); + return requestService.findByUserIdAndEventId(userId, eventId); } } diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java index c0da7c4..2968e48 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/mapper/RequestMapper.java @@ -1,25 +1,18 @@ package ru.practicum.ewm.requests.mapper; +import lombok.NonNull; import lombok.experimental.UtilityClass; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; import ru.practicum.ewm.requests.entity.ParticipationRequest; -import java.time.format.DateTimeFormatter; - @UtilityClass public class RequestMapper { public ParticipationRequestDto toParticipationRequestDto( - ParticipationRequest participationRequest) { - - if (participationRequest == null) { - return null; - } - - var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"); + @NonNull ParticipationRequest participationRequest) { var dateTime = participationRequest.getCreated() == null ? - null : participationRequest.getCreated().format(formatter); + null : participationRequest.getCreated(); return ParticipationRequestDto.builder() .event(participationRequest.getEventId()) diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/repository/RequestRepository.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/repository/RequestRepository.java index fdef561..265b668 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/repository/RequestRepository.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/repository/RequestRepository.java @@ -4,8 +4,8 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.transaction.annotation.Transactional; -import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.enums.ParticipationStatus; +import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.ewm.requests.entity.ParticipationRequest; import java.util.List; @@ -21,12 +21,11 @@ public interface RequestRepository extends JpaRepository getCountByEventIdsAndStatus(List eventIds, ParticipationStatus status); diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java index 60e946f..c6c48db 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestService.java @@ -12,18 +12,20 @@ public interface RequestService { @Transactional(readOnly = true) - List findByEventId(Long userId, Long eventId); + List findByUserIdAndEventId(long userId, long eventId); @Transactional(readOnly = true) - List findByRequesterId(Long userId); + List findByRequesterId(long userId); @Transactional(readOnly = true) List getRequestsCount(List eventIds); - EventRequestStatusUpdateResult updateStatusRequest(Long eventId, + EventRequestStatusUpdateResult updateStatusRequest(long eventId, + int participantLimit, + boolean requestModeration, EventRequestStatusUpdateRequest request); - ParticipationRequestDto addParticipationRequest(Long userId, Long eventId); + ParticipationRequestDto addParticipationRequest(long userId, long eventId); - ParticipationRequestDto cancelParticipationRequest(Long userId, Long requestId); + ParticipationRequestDto cancelParticipationRequest(long userId, long requestId); } diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java index 9758e39..ee38821 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/service/RequestServiceImpl.java @@ -13,6 +13,7 @@ import ru.practicum.aggregation.dto.user.output.UserDto; import ru.practicum.aggregation.enums.EventState; import ru.practicum.aggregation.enums.ParticipationStatus; +import ru.practicum.aggregation.enums.RequestUpdateStatus; import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException; import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; import ru.practicum.aggregation.model.repository.EventRequestCount; @@ -39,54 +40,53 @@ public class RequestServiceImpl implements RequestService { UserFeignRepository userFeignRepository; EventFeignRepository eventFeignRepository; - public List findByEventId(Long userId, Long eventId) { - var event = getEventById(eventId); + public List findByUserIdAndEventId(long userId, long eventId) { + var event = getEventFullDtoById(eventId); if (!event.initiator().id().equals(userId)) { throw new NotFoundException("Событие не найдено"); } - return requestRepository.findByEventId(eventId) - .stream() + var requests = requestRepository.findByEventId(eventId); + + return requests.stream() .map(RequestMapper::toParticipationRequestDto) .toList(); } - public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, - @NonNull - EventRequestStatusUpdateRequest request) { - var event = getEventById(eventId); - - int limit = event.participantLimit(); - List confirmedRequests = new ArrayList<>(); - List rejectedRequests = new ArrayList<>(); + public EventRequestStatusUpdateResult updateStatusRequest( + long eventId, + int participantLimit, + boolean requestModeration, + @NonNull EventRequestStatusUpdateRequest request + ) { + var confirmedRequests = new ArrayList(); + var rejectedRequests = new ArrayList(); - boolean isModerationOff = !event.requestModeration() || limit == 0; - boolean idsEmpty = request.requestIds() == null || request.requestIds().isEmpty(); + boolean isModerationOff = !requestModeration || participantLimit == 0; - if (isModerationOff || idsEmpty) { + if (isModerationOff || request.requestIds().isEmpty()) { return EventRequestStatusUpdateResult.builder() .confirmedRequests(Collections.emptyList()) .rejectedRequests(Collections.emptyList()) .build(); } - int countConfirmed = requestRepository + var countConfirmed = requestRepository .countByEventIdAndStatus(eventId, ParticipationStatus.CONFIRMED); - List requests = requestRepository.findAllByIdIn(request.requestIds()); - if (request.status().name().equals(ParticipationStatus.CONFIRMED.name()) && - countConfirmed >= limit) { + var requests = requestRepository.findAllByIdIn(request.requestIds()); + + boolean isConfirmed = request.status().equals(RequestUpdateStatus.CONFIRMED); + if (isConfirmed && countConfirmed >= participantLimit) { throw new ConflictException("Достигнут лимит подтвержденных заявок"); } - boolean confirmed = request.status().name().equals("CONFIRMED"); - - for (ParticipationRequest pr : requests) { + for (var pr : requests) { if (!pr.getStatus().equals(ParticipationStatus.PENDING)) { throw new ConflictException("Статус можно изменить только у заявок в состоянии рассмотрения"); } - if (confirmed && countConfirmed < limit) { + if (isConfirmed && countConfirmed < participantLimit) { pr.setStatus(ParticipationStatus.CONFIRMED); countConfirmed++; confirmedRequests.add(RequestMapper.toParticipationRequestDto(pr)); @@ -99,7 +99,7 @@ public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, requestRepository.saveAll(requests); // если в процессе лимит превышен - отклоняем все оставшиеся заявки - if (confirmed && countConfirmed >= limit) { + if (isConfirmed && countConfirmed >= participantLimit) { if (requestRepository.rejectPendingRequests(eventId, ParticipationStatus.PENDING) < 0) { throw new RuntimeException("Не удалось отклонить заявку"); } @@ -111,16 +111,16 @@ public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, .build(); } - public List findByRequesterId(Long userId) { + public List findByRequesterId(long userId) { return requestRepository.findByRequesterId(userId) .stream() .map(RequestMapper::toParticipationRequestDto) .toList(); } - public ParticipationRequestDto addParticipationRequest(Long userId, Long eventId) { - UserDto requester = getUserById(userId); - var event = getEventById(eventId); + public ParticipationRequestDto addParticipationRequest(long userId, long eventId) { + var requester = getUserDtoById(userId); + var event = getEventFullDtoById(eventId); if (!EventState.PUBLISHED.equals(event.state())) { throw new ConflictException("Нельзя участвовать в неопубликованном событии"); @@ -137,11 +137,11 @@ public ParticipationRequestDto addParticipationRequest(Long userId, Long eventId int limit = event.participantLimit(); if (limit != 0) { - long confirmedCount = requestRepository + var confirmedCount = requestRepository .countByEventIdAndStatus(eventId, ParticipationStatus.CONFIRMED); if (event.requestModeration()) { - long pendingCount = requestRepository + var pendingCount = requestRepository .countByEventIdAndStatus(eventId, ParticipationStatus.PENDING); if (confirmedCount + pendingCount >= limit) { throw new ConflictException("Достигнут лимит запросов на участие"); @@ -154,7 +154,6 @@ public ParticipationRequestDto addParticipationRequest(Long userId, Long eventId } ParticipationStatus status; - if (!event.requestModeration() || limit == 0) { status = ParticipationStatus.CONFIRMED; } else { @@ -171,7 +170,7 @@ public ParticipationRequestDto addParticipationRequest(Long userId, Long eventId return RequestMapper.toParticipationRequestDto(requestRepository.save(request)); } - public ParticipationRequestDto cancelParticipationRequest(Long userId, Long requestId) { + public ParticipationRequestDto cancelParticipationRequest(long userId, long requestId) { ParticipationRequest request = getRequestById(requestId); if (!request.getRequesterId().equals(userId)) { @@ -187,18 +186,18 @@ public List getRequestsCount(List eventIds) { } @NonNull - private UserDto getUserById(long userId) { + private UserDto getUserDtoById(long userId) { return userFeignRepository.getUserDtoById(userId); } @NonNull - private EventFullDto getEventById(long eventId) { + private EventFullDto getEventFullDtoById(long eventId) { int confirmets = requestRepository.countByEventIdAndStatus(eventId, ParticipationStatus.CONFIRMED); return eventFeignRepository.systemFindEventById(eventId, confirmets); } @NonNull - private ParticipationRequest getRequestById(Long requestId) { + private ParticipationRequest getRequestById(long requestId) { return requestRepository.findById(requestId).orElseThrow( () -> new NotFoundException("Заявка с id=" + requestId + " не найдена") ); diff --git a/infra/config-repo/core/event-service.yml b/infra/config-repo/core/event-service.yml index 503c62d..7202a56 100644 --- a/infra/config-repo/core/event-service.yml +++ b/infra/config-repo/core/event-service.yml @@ -18,6 +18,8 @@ spring: format_sql: true cloud: openfeign: + okhttp: + enabled: true client: config: default: diff --git a/infra/config-repo/core/rating-service.yml b/infra/config-repo/core/rating-service.yml index a2f0a10..04b17f2 100644 --- a/infra/config-repo/core/rating-service.yml +++ b/infra/config-repo/core/rating-service.yml @@ -18,6 +18,8 @@ spring: format_sql: true cloud: openfeign: + okhttp: + enabled: true client: config: default: diff --git a/infra/config-repo/core/request-service.yml b/infra/config-repo/core/request-service.yml index addf4e4..6b62f39 100644 --- a/infra/config-repo/core/request-service.yml +++ b/infra/config-repo/core/request-service.yml @@ -18,6 +18,8 @@ spring: format_sql: true cloud: openfeign: + okhttp: + enabled: true client: config: default: diff --git a/infra/config-repo/core/user-service.yml b/infra/config-repo/core/user-service.yml index ed1a7d8..4106e3d 100644 --- a/infra/config-repo/core/user-service.yml +++ b/infra/config-repo/core/user-service.yml @@ -18,6 +18,8 @@ spring: format_sql: true cloud: openfeign: + okhttp: + enabled: true client: config: default: From 659a78a083b3237cd4f522866e5f79f1b82c5aa5 Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Mon, 31 Aug 2026 12:11:00 +0300 Subject: [PATCH 10/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправление ошибок из тестов рейтингов Дебагинг UserClient и RatingService вс feign эндпоинты теперь /system... Рефакторинг EventService Все тесты проходят --- .../{feature.json => ewm-rating-service.json} | 4 +- .../aggregation/client/event/EventClient.java | 6 +- .../client/event/EventFallbackFactory.java | 7 +- .../aggregation/client/user/UserClient.java | 2 +- .../EventRequestStatusUpdateResult.java | 4 +- .../model/repository/EventRequestCount.java | 3 +- .../repository/EventFeignRepository.java | 7 +- .../repository/EventFeignRepositoryImpl.java | 26 +---- .../system/SystemEventController.java | 2 +- .../ewm/events/mapper/EventMapper.java | 6 +- .../compilation/CompilationServiceImpl.java | 8 +- .../events/service/event/EventService.java | 2 +- .../service/event/EventServiceImpl.java | 101 ++++++++++-------- .../rating/EventRatingServiceImpl.java | 2 +- .../ewm/ratings/service/RatingService.java | 3 +- .../ratings/service/RatingServiceImpl.java | 15 ++- .../controller/SystemUserController.java | 16 ++- 17 files changed, 106 insertions(+), 108 deletions(-) rename .postman/{feature.json => ewm-rating-service.json} (99%) diff --git a/.postman/feature.json b/.postman/ewm-rating-service.json similarity index 99% rename from .postman/feature.json rename to .postman/ewm-rating-service.json index f75e49e..459a353 100644 --- a/.postman/feature.json +++ b/.postman/ewm-rating-service.json @@ -770,7 +770,7 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + "pm.test(\"Ответ должен содержать код статуса 404 и данные в формате json\", function () {\r", " pm.response.to.have.status(404);\r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", @@ -1043,7 +1043,7 @@ "variable": [ { "key": "baseUrl", - "value": "http://localhost:8080" + "value": "" }, { "key": "uid", diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java index 4d10c64..122f44f 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java @@ -13,14 +13,10 @@ ) public interface EventClient { - @GetMapping("/users/{userId}/events/{eventId}") - EventFullDto userFindEventById(@PathVariable Long userId, - @PathVariable Long eventId); - @PatchMapping("/system/events/ratings") void systemUpdateRating(@RequestBody @NotNull @Valid RatingUpdateRequest request); @GetMapping("/system/events/{eventId}") - EventFullDto systemFindEventById(@PathVariable Long eventId, @RequestParam Integer confirmets); + EventFullDto systemFindEventById(@PathVariable Long eventId, @RequestParam Long confirmets); } \ No newline at end of file diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java index 10291b5..1cf6a5e 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java @@ -14,18 +14,13 @@ public EventClient create(Throwable cause) { return new EventClient() { - @Override - public EventFullDto userFindEventById(Long userId, Long eventId) { - throw RemoteExceptionMapper.mapEventException(cause); - } - @Override public void systemUpdateRating(RatingUpdateRequest request) { throw RemoteExceptionMapper.mapEventException(cause); } @Override - public EventFullDto systemFindEventById(Long eventId, Integer confirmets) { + public EventFullDto systemFindEventById(Long eventId, Long confirmets) { throw RemoteExceptionMapper.mapEventException(cause); } }; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java index 99d8b55..fe1e429 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/user/UserClient.java @@ -15,7 +15,7 @@ ) public interface UserClient { - @GetMapping("/admin/users/{userId}") + @GetMapping("/system/users/{userId}") UserDto getUser(@PathVariable Long userId); @GetMapping("/system/users/{userId}/check") diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/EventRequestStatusUpdateResult.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/EventRequestStatusUpdateResult.java index d38da61..7a927f3 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/EventRequestStatusUpdateResult.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/output/EventRequestStatusUpdateResult.java @@ -6,8 +6,8 @@ /** * @apiNote - * List<{@link ParticipationRequestDto}> confirmedRequests
- * List<{@link ParticipationRequestDto}> rejectedRequests
+ * {@link List}<{@link ParticipationRequestDto}> confirmedRequests
+ * {@link List}<{@link ParticipationRequestDto}> rejectedRequests */ @Builder public record EventRequestStatusUpdateResult( diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java index f73802a..4e2cd41 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java @@ -1,8 +1,9 @@ package ru.practicum.aggregation.model.repository; + /** * @apiNote * {@link Long} eventId
- * {@link Long} count + * {@link Integer} count */ public record EventRequestCount( Long eventId, diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java index eb8ceec..d09eb9c 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java @@ -1,14 +1,11 @@ package ru.practicum.aggregation.repository; -import org.springframework.lang.NonNull; import ru.practicum.aggregation.dto.event.output.EventFullDto; import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; public interface EventFeignRepository { - void updateRating(@NonNull RatingUpdateRequest request); + void updateRating(RatingUpdateRequest request); - EventFullDto userFindEventById(long userId, long eventId); - - EventFullDto systemFindEventById(long eventId, int confirmets); + EventFullDto systemFindEventById(long eventId, long confirmets); } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java index 0cadcbb..67ec9bf 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java @@ -48,31 +48,7 @@ public void updateRating(@NonNull RatingUpdateRequest request) { } @Override - public EventFullDto userFindEventById(long userId, long eventId) { - log.info(""" - PLEASE WAITING - Пользователь с id={} ищет событие с id={}""", userId, eventId); - return switch (RemoteCallExecutor.execute(() -> eventClient.userFindEventById(userId, eventId))) { - case RemoteCallResult.Success(var response) -> response; - case RemoteCallResult.Failure(var exeption) -> { - log.warn(""" - Бизнес-исключение - Пользователю с id={} - Не удалось получить событие с id={}""", userId, eventId); - throw exeption; - } - case RemoteCallResult.Degraded(var cause) -> { - log.warn(""" - Деградация - Пользователю с id={} - Не удалось получить событие с id={}""", userId, eventId); - throw new UserServiceUnavailableException(cause); - } - }; - } - - @Override - public EventFullDto systemFindEventById(long eventId, int confirmets) { + public EventFullDto systemFindEventById(long eventId, long confirmets) { log.info(""" PLEASE WAITING Система ищет событие с id={}""", eventId); diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java index e16b228..c3d083c 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java @@ -21,7 +21,7 @@ public class SystemEventController { @GetMapping("/{eventId}") EventFullDto systemFindEventById(@PathVariable @Positive Long eventId, - @RequestParam @PositiveOrZero Integer confirmets) { + @RequestParam @PositiveOrZero Long confirmets) { return eventService.findEventById(eventId, confirmets); } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java index 5247ded..a6d885e 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/mapper/EventMapper.java @@ -72,9 +72,11 @@ public Event toEntity(@NonNull NewEventDto newEventDto, .initiatorId(initiatorId) .location(newEventDto.location()) .paid(newEventDto.paid() != null && newEventDto.paid()) - .participantLimit(newEventDto.participantLimit() == null ? 0 : newEventDto.participantLimit()) + .participantLimit(newEventDto.participantLimit() == null + ? 0 : newEventDto.participantLimit()) .publishedOn(publishedOn) - .requestModeration(newEventDto.requestModeration() == null || newEventDto.requestModeration()) + .requestModeration(newEventDto.requestModeration() == null + || newEventDto.requestModeration()) .state(state) .title(newEventDto.title()) .rate(0) diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java index 0d06187..96acb63 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java @@ -8,19 +8,19 @@ import org.springframework.data.domain.PageRequest; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; -import ru.practicum.aggregation.dto.compilation.output.CompilationDto; +import ru.practicum.aggregation.dto.compilation.come.create.NewCompilationDto; import ru.practicum.aggregation.dto.compilation.come.get.CompilationSearchFilter; import ru.practicum.aggregation.dto.compilation.come.update.CompilationUpdateDto; -import ru.practicum.aggregation.dto.compilation.come.create.NewCompilationDto; -import ru.practicum.aggregation.model.repository.EventRequestCount; +import ru.practicum.aggregation.dto.compilation.output.CompilationDto; import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; import ru.practicum.aggregation.model.entity.BaseEntity; +import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.repository.RequestFeignRepository; import ru.practicum.aggregation.repository.UserFeignRepository; -import ru.practicum.ewm.events.mapper.CompilationMapper; import ru.practicum.ewm.events.entity.Compilation; import ru.practicum.ewm.events.entity.Event; +import ru.practicum.ewm.events.mapper.CompilationMapper; import ru.practicum.ewm.events.repository.CompilationRepository; import ru.practicum.ewm.events.repository.EventRepository; import ru.practicum.ewm.events.statistic.StatRestRepository; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java index ec13283..edbf5e7 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java @@ -42,7 +42,7 @@ public interface EventService { EventFullDto findEventByUserIdAndEventId(Long userId, Long eventId); /// Получение полной информации о событии по ID - EventFullDto findEventById(long eventId, int confirmets); + EventFullDto findEventById(long eventId, long confirmets); /// Изменение события добавленного текущим пользователем @Transactional diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java index 7edf1ca..87a2489 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java @@ -45,7 +45,10 @@ import java.time.Instant; import java.time.LocalDateTime; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; @Slf4j @@ -117,28 +120,24 @@ public List getFreeEvents(@NonNull FreeGetData dto, HttpServletRe return Collections.emptyList(); } - List eventRequestCountList = requestFeignRepository - .getConfirmedRequestsCount(events.stream().map(Event::getId).toList()); + List confirmedRequestsCounts = fetchConfirmedRequestsCount( + events.stream().map(Event::getId).toList()); - Map requestCountMap = new HashMap<>(); - if (!eventRequestCountList.isEmpty()) { - eventRequestCountList.forEach(eventRequestCount -> - requestCountMap.put(eventRequestCount.eventId(), eventRequestCount.count()) - ); - } + Map eventIdToRequestCount = getRequestCountMap(confirmedRequestsCounts); - List uris = events.stream().map(event -> EVENTS_PATH + event.getId()).toList(); - List stats = statRepository.getStat( - uris, dto.rangeStart(), dto.rangeEnd(), false); + List uris = getUris(events); + List stats = statRepository + .getStat(uris, dto.rangeStart(), dto.rangeEnd(), false); Map initiators = getUserShortDtoMap(events); return events.stream() .map(event -> { - var userId = requestCountMap.get(event.getInitiatorId()); + var userId = event.getInitiatorId(); + var count = eventIdToRequestCount.getOrDefault(userId, 0L); var eventData = EventData.builder() .initiator(initiators.get(userId)) - .confirmedRequests(requestCountMap.getOrDefault(event.getId(), 0L)) + .confirmedRequests(count) .views(stats.size()) .build(); return EventMapper.toEventShortDto(event, eventData); @@ -196,7 +195,7 @@ public EventFullDto userAddNewEvent(Long userId, @NonNull NewEventDto newEventDt var eventData = EventData.builder() .initiator(initiator) - .confirmedRequests(0L) + .confirmedRequests(0) .views(0L) .build(); @@ -228,17 +227,12 @@ public List adminGetEvents(@NonNull AdminGetData dto) { return Collections.emptyList(); } - List eventRequestCountList = requestFeignRepository.getConfirmedRequestsCount( + List confirmedRequestsCounts = fetchConfirmedRequestsCount( events.stream().map(Event::getId).toList()); - Map requestCountMap = new HashMap<>(); - if (!eventRequestCountList.isEmpty()) { - eventRequestCountList.forEach(eventRequestCount -> - requestCountMap.put(eventRequestCount.eventId(), eventRequestCount.count()) - ); - } + Map eventIdToRequestCount = getRequestCountMap(confirmedRequestsCounts); - List uris = events.stream().map(event -> EVENTS_PATH + event.getId()).toList(); + List uris = getUris(events); List stats = statRepository .getStat(uris, dto.rangeStart(), dto.rangeEnd(), false); @@ -247,10 +241,10 @@ public List adminGetEvents(@NonNull AdminGetData dto) { return events.stream() .map(event -> { var userId = event.getInitiatorId(); + var count = getConfirmedRequestsCountFromMap(eventIdToRequestCount, event.getId()); var eventData = EventData.builder() .initiator(initiators.get(userId)) - .confirmedRequests(getConfirmedRequestsCountFromMap( - requestCountMap, event.getId())) + .confirmedRequests(count) .views(getHits(stats, event.getId())) .build(); return EventMapper.toEventFullDto(event, eventData); @@ -329,27 +323,22 @@ public List findByUserId(Long userId, Integer from, Integer size) return Collections.emptyList(); } - List eventRequestCountList = requestFeignRepository.getConfirmedRequestsCount( + List confirmedRequestsCounts = fetchConfirmedRequestsCount( events.stream().map(Event::getId).toList()); - Map requestCountMap = new HashMap<>(); - if (!eventRequestCountList.isEmpty()) { - eventRequestCountList.forEach(eventRequestCount -> - requestCountMap.put(eventRequestCount.eventId(), eventRequestCount.count()) - ); - } + Map eventIdToRequestCount = getRequestCountMap(confirmedRequestsCounts); - List uris = events.stream().map(event -> EVENTS_PATH + event.getId()).toList(); + List uris = getUris(events); List stats = statRepository.getStat(uris, true); Map initiators = getUserShortDtoMap(events); return events.stream() .map(event -> { + var count = getConfirmedRequestsCountFromMap(eventIdToRequestCount, event.getId()); var eventData = EventData.builder() .initiator(initiators.get(userId)) - .confirmedRequests( - getConfirmedRequestsCountFromMap(requestCountMap, event.getId())) + .confirmedRequests(count) .views(getHits(stats, event.getId())) .build(); return EventMapper.toEventShortDto(event, eventData); @@ -371,7 +360,7 @@ public EventFullDto findEventByUserIdAndEventId(Long userId, Long eventId) { } @Override - public EventFullDto findEventById(long eventId, int confirmets) { + public EventFullDto findEventById(long eventId, long confirmets) { Event event = getEventById(eventId); var eventData = EventData.builder() .initiator(UserMapper.toUserShortDto(getUserById(event.getInitiatorId()))) @@ -393,6 +382,21 @@ public EventFullDto patchEvent(Long userId, Long eventId, @NonNull UpdateEventUs return patchEvent(eventId, request, false); } + private List fetchConfirmedRequestsCount(List events) { + return requestFeignRepository.getConfirmedRequestsCount(events); + } + + @NonNull + private List getUris(@NonNull List events) { + return events.stream().map(event -> EVENTS_PATH + event.getId()).toList(); + } + + private Map getRequestCountMap(@NonNull List eventRequestCountList) { + return eventRequestCountList.stream() + .collect(Collectors.toMap(EventRequestCount::eventId, EventRequestCount::count, + (a, b) -> a)); + } + @NonNull private Map getUserShortDtoMap(@NonNull List events) { List userIds = events.stream().map(Event::getInitiatorId).toList(); @@ -401,14 +405,6 @@ private Map getUserShortDtoMap(@NonNull List events) .collect(Collectors.toMap(UserShortDto::id, user -> user)); } - private EventData getEventData(@NonNull Event event) { - return EventData.builder() - .initiator(UserMapper.toUserShortDto(getUserById(event.getInitiatorId()))) - .confirmedRequests(getConfirmedRequestsCountByEvent(event.getId())) - .views(getHits(event.getId())) - .build(); - } - @SuppressWarnings("SameParameterValue") private EventFullDto patchEvent(Long eventId, @NonNull UpdateEventUserRequest request, @@ -468,6 +464,14 @@ private EventFullDto patchEvent(Long eventId, } } + private EventData getEventData(@NonNull Event event) { + return EventData.builder() + .initiator(UserMapper.toUserShortDto(getUserById(event.getInitiatorId()))) + .confirmedRequests(getConfirmedRequestsCountByEvent(event.getId())) + .views(getHits(event.getId())) + .build(); + } + @NonNull private UserDto getUserById(long userId) { return userFeignRepository.getUserDtoById(userId); @@ -488,7 +492,7 @@ private Category getCategoryById(long categoryId) { } private long getConfirmedRequestsCountByEvent(long eventId) { - var counts = requestFeignRepository.getConfirmedRequestsCount(List.of(eventId)); + var counts = fetchConfirmedRequestsCount(List.of(eventId)); if (counts.isEmpty()) { return 0; } @@ -497,7 +501,8 @@ private long getConfirmedRequestsCountByEvent(long eventId) { } private long getHits(long eventId) { - List stats = statRepository.getStat(List.of(EVENTS_PATH + eventId), true); + List stats = statRepository.getStat( + List.of(EVENTS_PATH + eventId), true); if (stats.isEmpty()) { return 0; } @@ -516,7 +521,9 @@ private long getHits(@NonNull List stats, long eventId) { return 0; } - private long getConfirmedRequestsCountFromMap(@NonNull Map requestCountMap, long eventId) { + private long getConfirmedRequestsCountFromMap(@NonNull + Map requestCountMap, + long eventId) { if (requestCountMap.isEmpty()) { return 0; } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/EventRatingServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/EventRatingServiceImpl.java index fd9124a..a31ca86 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/EventRatingServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/rating/EventRatingServiceImpl.java @@ -20,7 +20,7 @@ public class EventRatingServiceImpl implements EventRatingService { @Override public void updateRating(@NonNull RatingUpdateRequest request) { Event event = eventRepository.findById(request.eventId()).orElseThrow(() -> - new NotFoundException("Event with id " + request.eventId() + " not found")); + new NotFoundException("Событие с id=%s не найдено".formatted(request.eventId()))); event.setRate(request.rate()); eventRepository.save(event); } diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java index 4d5331b..2266e02 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingService.java @@ -1,6 +1,5 @@ package ru.practicum.ewm.ratings.service; -import org.springframework.lang.NonNull; import org.springframework.transaction.annotation.Transactional; import ru.practicum.aggregation.dto.rating.come.create.RatingCreateRequest; import ru.practicum.aggregation.dto.rating.output.RatingResponse; @@ -8,7 +7,7 @@ @Transactional public interface RatingService { - RatingResponse addOrUpdateReaction(long userId, long eventId, @NonNull RatingCreateRequest request); + RatingResponse addOrUpdateReaction(long userId, long eventId, RatingCreateRequest request); void removeReaction(long userId, long eventId); } diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java index ea7f710..56bc5ab 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java @@ -9,14 +9,18 @@ import ru.practicum.aggregation.dto.rating.come.create.RatingCreateRequest; import ru.practicum.aggregation.dto.rating.output.RatingResponse; import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; +import ru.practicum.aggregation.enums.EventState; import ru.practicum.aggregation.enums.Reaction; import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException; import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; +import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.repository.EventFeignRepository; +import ru.practicum.aggregation.repository.RequestFeignRepository; import ru.practicum.aggregation.repository.UserFeignRepository; import ru.practicum.ewm.ratings.entity.Rating; import ru.practicum.ewm.ratings.repository.RatingRepository; +import java.util.List; import java.util.Objects; @Service @@ -28,6 +32,7 @@ public class RatingServiceImpl implements RatingService { UserFeignRepository userFeignRepository; EventFeignRepository eventFeignRepository; + RequestFeignRepository requestFeignRepository; @Override public RatingResponse addOrUpdateReaction(long userId, @@ -35,13 +40,21 @@ public RatingResponse addOrUpdateReaction(long userId, @NonNull RatingCreateRequest request) { var user = userFeignRepository.getUserDtoById(userId); - var event = eventFeignRepository.userFindEventById(userId, eventId); + var confirmedRequestsCount = requestFeignRepository.getConfirmedRequestsCount(List.of(eventId)); + long contirmets = confirmedRequestsCount.stream() + .map(EventRequestCount::count).findAny() + .orElse(0L); + var event = eventFeignRepository.systemFindEventById(eventId, contirmets); var initiator = event.initiator(); if (Objects.equals(user.id(), initiator.id())) { throw new ValidationException("Нельзя ставить реакции своим событиям"); } + if (!event.state().equals(EventState.PUBLISHED)) { + throw new NotFoundException("Нельзя поставить реакцию не опубликованному событию"); + } + var rating = ratingRepository.findByUserIdAndEventId(userId, eventId).orElse(null); var requestReaction = request.reaction(); diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/controller/SystemUserController.java b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/SystemUserController.java index 020c924..7453c2a 100644 --- a/core/user-service/src/main/java/ru/practicum/ewm/users/controller/SystemUserController.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/SystemUserController.java @@ -1,7 +1,9 @@ package ru.practicum.ewm.users.controller; +import jakarta.validation.constraints.Positive; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import ru.practicum.aggregation.dto.user.output.UserDto; import ru.practicum.aggregation.dto.user.output.UserShortDto; import ru.practicum.ewm.users.service.UserService; @@ -20,10 +22,20 @@ public class SystemUserController { * @param userId ID пользователя */ @GetMapping("/{userId}/check") - public void checkUser(@PathVariable Long userId) { + public void checkUser(@PathVariable @Positive Long userId) { userService.remoteCheckUser(userId); } + /** + * Получение пользователя по ID + * @param userId ID пользователя + * @return {@link UserDto} + */ + @GetMapping("/{userId}") + UserDto getUser(@PathVariable @Positive Long userId) { + return userService.getUser(userId); + } + /** * Получение краткого списка пользователей * @@ -31,7 +43,7 @@ public void checkUser(@PathVariable Long userId) { * @return {@link UserShortDto} */ @GetMapping("/short") - public List getShortUsersByIds(@RequestParam List userIds) { + public List getShortUsersByIds(@RequestParam List<@Positive Long> userIds) { return userService.getShortUsersByIds(userIds); } From 34b36ef73e937658dd85f6a59f257ae8ea88f547 Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Tue, 1 Sep 2026 07:11:23 +0300 Subject: [PATCH 11/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Статистика переехала на Feign Рефакторинг EventService Добавлено логирование в EventService Все тесты проходят --- core/aggregation-api/pom.xml | 6 + .../aggregation/client/stats/StatsClient.java | 31 +++++ .../client/stats/StatsFallbackFactory.java | 36 +++++ .../dto/category/output/CategoryDto.java | 2 + .../come/create/NewCompilationDto.java | 1 + .../come/get/CompilationSearchFilter.java | 7 + .../come/update/CompilationUpdateDto.java | 1 + .../dto/event/come/create/NewEventDto.java | 3 + .../come/update/UpdateEventUserRequest.java | 13 ++ .../come/EventRequestStatusUpdateRequest.java | 2 +- .../stats/StatsResponseException.java | 4 - .../StatsServerUnavailableException.java | 7 + .../aggregation/model/data/AdminGetData.java | 16 ++- .../aggregation/model/data/FreeGetData.java | 22 +-- .../RequestFeignRepositoryImpl.java | 4 +- .../repository/StatsFeignRepository.java | 21 +++ .../repository/StatsFeignRepositoryImpl.java | 110 +++++++++++++++ .../repository/UserFeignRepositoryImpl.java | 2 +- core/event-service/pom.xml | 10 -- .../practicum/ewm/events/EventServiceApp.java | 5 +- .../admin/AdminCategoryController.java | 42 +++--- .../admin/AdminCompilationController.java | 44 +++--- .../admin/AdminEventController.java | 55 ++++---- .../free/FreeCategoryController.java | 26 ++-- .../free/FreeCompilationController.java | 34 +++-- .../controller/free/FreeEventController.java | 67 ++++----- .../controller/user/UserEventController.java | 102 +++++++------- .../compilation/CompilationServiceImpl.java | 8 +- .../events/service/event/EventService.java | 6 +- .../service/event/EventServiceImpl.java | 101 +++++++------- .../events/statistic/StatRestRepository.java | 86 ------------ core/rating-service/pom.xml | 5 - .../ewm/ratings/RatingServiceApp.java | 4 +- core/request-service/pom.xml | 5 - .../ewm/requests/RequestServiceApp.java | 4 +- core/user-service/pom.xml | 5 - .../practicum/ewm/users/UserServiceApp.java | 4 +- infra/config-repo/core/event-service.yml | 9 +- infra/config-repo/core/rating-service.yml | 4 - infra/config-repo/core/request-service.yml | 4 - infra/config-repo/core/user-service.yml | 4 - .../src/main/resources/application.yml | 2 +- stats/pom.xml | 1 - stats/stat-client/pom.xml | 62 --------- .../ru/practicum/stat/client/StatClient.java | 127 ------------------ .../exception/StatsServerUnavailable.java | 7 - .../ru/practicum/stat/dto/EndpointHitDto.java | 1 + stats/stats-server/pom.xml | 4 +- .../JacksonConfiguration.java} | 4 +- .../stat/server/config/WebConfig.java | 20 +++ .../server/controller/StatController.java | 5 +- .../server/{model => entity}/EndpointHit.java | 15 ++- .../server/{util => }/error/ApiError.java | 5 +- .../{util => }/error/StatErrorHandler.java | 18 ++- .../stat/server/mapper/EndpointHitMapper.java | 6 +- .../{dao => repository}/StatRepository.java | 4 +- .../stat/server/service/StatService.java | 9 +- .../src/main/resources/schema-postgres.sql | 4 +- .../server/mapper/EndpointHitMapperTest.java | 30 ----- 59 files changed, 606 insertions(+), 640 deletions(-) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/client/stats/StatsClient.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/client/stats/StatsFallbackFactory.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/StatsServerUnavailableException.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepository.java create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepositoryImpl.java delete mode 100644 core/event-service/src/main/java/ru/practicum/ewm/events/statistic/StatRestRepository.java delete mode 100644 stats/stat-client/pom.xml delete mode 100644 stats/stat-client/src/main/java/ru/practicum/stat/client/StatClient.java delete mode 100644 stats/stat-client/src/main/java/ru/practicum/stat/client/exception/StatsServerUnavailable.java rename stats/stats-server/src/main/java/ru/practicum/stat/server/{util/config/StatServerConfiguration.java => config/JacksonConfiguration.java} (91%) create mode 100644 stats/stats-server/src/main/java/ru/practicum/stat/server/config/WebConfig.java rename stats/stats-server/src/main/java/ru/practicum/stat/server/{model => entity}/EndpointHit.java (68%) rename stats/stats-server/src/main/java/ru/practicum/stat/server/{util => }/error/ApiError.java (73%) rename stats/stats-server/src/main/java/ru/practicum/stat/server/{util => }/error/StatErrorHandler.java (66%) rename stats/stats-server/src/main/java/ru/practicum/stat/server/{dao => repository}/StatRepository.java (93%) delete mode 100644 stats/stats-server/src/test/java/ru/practicum/stat/server/mapper/EndpointHitMapperTest.java diff --git a/core/aggregation-api/pom.xml b/core/aggregation-api/pom.xml index 205e35e..3d1e4eb 100644 --- a/core/aggregation-api/pom.xml +++ b/core/aggregation-api/pom.xml @@ -14,6 +14,12 @@ jar + + ru.practicum + stat-dto + 0.0.1-SNAPSHOT + + jakarta.servlet jakarta.servlet-api diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/stats/StatsClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/stats/StatsClient.java new file mode 100644 index 0000000..77dee9b --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/stats/StatsClient.java @@ -0,0 +1,31 @@ +package ru.practicum.aggregation.client.stats; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import ru.practicum.stat.dto.EndpointHitDto; +import ru.practicum.stat.dto.ViewStatsDto; + +import java.time.LocalDateTime; +import java.util.List; + +@FeignClient( + name = "stats-server", + fallbackFactory = StatsFallbackFactory.class +) +public interface StatsClient { + + @PostMapping("/hit") + void hit(@RequestBody EndpointHitDto endpointHitDto); + + @GetMapping("/stats") + List getStats( + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime start, + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime end, + @RequestParam List uris, + @RequestParam boolean unique); + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/stats/StatsFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/stats/StatsFallbackFactory.java new file mode 100644 index 0000000..5b1afe3 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/stats/StatsFallbackFactory.java @@ -0,0 +1,36 @@ +package ru.practicum.aggregation.client.stats; + +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; +import ru.practicum.aggregation.error.exception.unavailable.StatsServerUnavailableException; +import ru.practicum.stat.dto.EndpointHitDto; +import ru.practicum.stat.dto.ViewStatsDto; + +import java.time.LocalDateTime; +import java.util.List; + +@Component +public class StatsFallbackFactory implements FallbackFactory { + + @Override + public StatsClient create(Throwable cause) { + + return new StatsClient() { + + @Override + public void hit(EndpointHitDto endpointHitDto) { + throw new StatsServerUnavailableException("", cause); + } + + @Override + public List getStats(LocalDateTime start, + LocalDateTime end, + List uris, + boolean unique) { + + throw new StatsServerUnavailableException("", cause); + } + }; + } + +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/output/CategoryDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/output/CategoryDto.java index 5dfdf7d..4a00a95 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/output/CategoryDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/category/output/CategoryDto.java @@ -5,6 +5,8 @@ import lombok.Builder; /** + * Данные категории для изменения категории + * @apiNote * Long id
* String name */ diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/create/NewCompilationDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/create/NewCompilationDto.java index 37ce490..50ae05f 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/create/NewCompilationDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/create/NewCompilationDto.java @@ -10,6 +10,7 @@ import java.util.Set; /** + * данные новой подборки * @apiNote * {@link Set}<{@link Long}> events
* boolean pinned
diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/get/CompilationSearchFilter.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/get/CompilationSearchFilter.java index c29c638..23818ac 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/get/CompilationSearchFilter.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/get/CompilationSearchFilter.java @@ -5,6 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +/** + * {@link Boolean} pinned - искать только закрепленные/не закрепленные подборки
+ * {@link Integer} from - количество элементов, которые нужно пропустить для формирования текущего набора
+ * Default value : 0
+ * {@link Integer} size - количество элементов в наборе
+ * Default value : 10 + */ @Data @Builder @NoArgsConstructor diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/update/CompilationUpdateDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/update/CompilationUpdateDto.java index e9e09e1..da9971c 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/update/CompilationUpdateDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/compilation/come/update/CompilationUpdateDto.java @@ -9,6 +9,7 @@ import java.util.Set; /** + * данные для обновления подборки * @apiNote * {@link Set}<{@link Long}> events
* {@link Boolean} pinned
diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/create/NewEventDto.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/create/NewEventDto.java index fecf8a5..70efdec 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/create/NewEventDto.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/create/NewEventDto.java @@ -11,6 +11,9 @@ import java.time.LocalDateTime; +/** + * данные добавляемого события + */ @Data @Builder() @Accessors(fluent = true) diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/update/UpdateEventUserRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/update/UpdateEventUserRequest.java index 0294d96..e9fa32d 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/update/UpdateEventUserRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/event/come/update/UpdateEventUserRequest.java @@ -7,6 +7,19 @@ import java.time.LocalDateTime; +/** + * данные для обновления события текущим пользователем + * @param annotation + * @param category + * @param description + * @param eventDate + * @param location + * @param paid + * @param participantLimit + * @param requestModeration + * @param stateAction + * @param title + */ @Builder public record UpdateEventUserRequest( diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/come/EventRequestStatusUpdateRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/come/EventRequestStatusUpdateRequest.java index 2a25fad..7c4235a 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/come/EventRequestStatusUpdateRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/participation/come/EventRequestStatusUpdateRequest.java @@ -8,7 +8,7 @@ import java.util.List; -/** +/** Новый статус для заявок на участие в событии текущего пользователя * @apiNote {@link List}<{@link Long}> requestIds
* {@link RequestUpdateStatus} status */ diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats/StatsResponseException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats/StatsResponseException.java index bfa1cf3..53da671 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats/StatsResponseException.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/stats/StatsResponseException.java @@ -4,10 +4,6 @@ public class StatsResponseException extends RuntimeException { private static final String MESSAGE = "Не удалось получить статистику"; - public StatsResponseException(Exception ex) { - super(MESSAGE, ex); - } - public StatsResponseException() { super(MESSAGE); } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/StatsServerUnavailableException.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/StatsServerUnavailableException.java new file mode 100644 index 0000000..3bb526c --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/error/exception/unavailable/StatsServerUnavailableException.java @@ -0,0 +1,7 @@ +package ru.practicum.aggregation.error.exception.unavailable; + +public class StatsServerUnavailableException extends ServiceUnavailableException { + public StatsServerUnavailableException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/AdminGetData.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/AdminGetData.java index 3b10340..abd277f 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/AdminGetData.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/AdminGetData.java @@ -8,13 +8,15 @@ /** * - * @param users - * @param states - * @param categories - * @param rangeStart - * @param rangeEnd - * @param from - * @param size + * @param users список id пользователей, чьи события нужно найти + * @param states список состояний в которых находятся искомые события + * @param categories список id категорий в которых будет вестись поиск + * @param rangeStart дата и время не раньше которых должно произойти событие + * @param rangeEnd дата и время не позже которых должно произойти событие + * @param from количество событий, которые нужно пропустить для формирования текущего набора
+ * Default value : 0 + * @param size количество событий в наборе
+ * Default value : 10 */ @Builder public record AdminGetData( diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/FreeGetData.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/FreeGetData.java index 3a8df93..7f6d6e6 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/FreeGetData.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/FreeGetData.java @@ -8,15 +8,19 @@ /** * - * @param text - * @param categories - * @param paid - * @param rangeStart - * @param rangeEnd - * @param onlyAvailable - * @param sort - * @param from - * @param size + * @param text текст для поиска в содержимом аннотации и подробном описании события + * @param categories список идентификаторов категорий в которых будет вестись поиск + * @param paid поиск только платных/бесплатных событий + * @param rangeStart дата и время не раньше которых должно произойти событие + * @param rangeEnd дата и время не позже которых должно произойти событие + * @param onlyAvailable только события у которых не исчерпан лимит запросов на участие
+ * Default value : false + * @param sort Вариант сортировки: по дате события или по количеству просмотров
+ * Available values : EVENT_DATE, VIEWS + * @param from количество событий, которые нужно пропустить для формирования текущего набора
+ * Default value : 0 + * @param size количество событий в наборе
+ * Default value : 10 */ @NotNull @Builder diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java index 07e228a..4835ac2 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java @@ -83,7 +83,7 @@ public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, log.info(""" PLEASE WAITING Система обновляет статус запросов на участие в событии с id={} - request: {}""", eventId, request); + {}""", eventId, request); return switch (RemoteCallExecutor.execute(() -> requestClient.updateStatusRequest(eventId, participantLimit, requestModeration, request))) { case RemoteCallResult.Success(var response) -> response; @@ -98,7 +98,7 @@ public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, log.warn(""" Деградация Не удалось обновить статус запросов на участие в событии с id={} - request: {}""", eventId, request); + {}""", eventId, request); throw new RequestServiceUnavailableException(cause); } }; diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepository.java new file mode 100644 index 0000000..d156037 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepository.java @@ -0,0 +1,21 @@ +package ru.practicum.aggregation.repository; + +import jakarta.servlet.http.HttpServletRequest; +import ru.practicum.stat.dto.ViewStatsDto; + +import java.time.LocalDateTime; +import java.util.List; + +public interface StatsFeignRepository { + + LocalDateTime startUnixEpoch = LocalDateTime.parse("1970-01-01T00:00:00"); + + void sendHitRequest(HttpServletRequest request); + + List getStat(List statUris, + LocalDateTime rangeStart, + LocalDateTime rangeEnd, + boolean uniqe); + + List getStat(List statUris, boolean uniqe); +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepositoryImpl.java new file mode 100644 index 0000000..f0e7558 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepositoryImpl.java @@ -0,0 +1,110 @@ +package ru.practicum.aggregation.repository; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.env.Environment; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import ru.practicum.aggregation.client.RemoteCallExecutor; +import ru.practicum.aggregation.client.RemoteCallResult; +import ru.practicum.aggregation.client.stats.StatsClient; +import ru.practicum.aggregation.error.exception.stats.StatsResponseException; +import ru.practicum.aggregation.error.exception.unavailable.StatsServerUnavailableException; +import ru.practicum.stat.dto.EndpointHitDto; +import ru.practicum.stat.dto.ViewStatsDto; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class StatsFeignRepositoryImpl implements StatsFeignRepository { + + Environment environment; + StatsClient statsClient; + + @Override + public void sendHitRequest(@NonNull HttpServletRequest request) { + hit(EndpointHitDto.builder() + .ip(request.getRemoteAddr()) + .uri(request.getRequestURI()) + .app(environment.getProperty("spring.application.name")) + .timestamp(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS)) + .build()); + } + + @Override + public List getStat(List statUris, + LocalDateTime rangeStart, + LocalDateTime rangeEnd, + boolean uniqe) { + + if (statUris == null || statUris.isEmpty()) { + throw new StatsResponseException(); + } + + if (rangeStart == null) { + rangeStart = startUnixEpoch.truncatedTo(ChronoUnit.SECONDS); + } + + if (rangeEnd == null) { + rangeEnd = LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS); + } + + return getStatistic(rangeStart, rangeEnd, statUris, uniqe); + } + + @Override + public List getStat(List statUris, boolean uniqe) { + return getStat(statUris, null, null, uniqe); + } + + @SuppressWarnings("unused") + private void hit(EndpointHitDto endpointHitDto) { + log.info(""" + PLEASE WAITING + Система регистрирует статистику + {}""", endpointHitDto); + switch (RemoteCallExecutor.executeVoid(() -> statsClient.hit(endpointHitDto))) { + + case RemoteCallResult.Success(var nullable) -> { + } + case RemoteCallResult.Failure(var exeptionNullable) -> { + } + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Не удалось зарегистрировать статистику + {}""", endpointHitDto); + throw new StatsServerUnavailableException("", cause); + } + } + } + + private List getStatistic(LocalDateTime start, + LocalDateTime end, + List uris, + boolean unique) { + log.info(""" + PLEASE WAITING + Система получает статистику + uris: {}""", uris); + return switch (RemoteCallExecutor.execute(() -> statsClient.getStats(start, end, uris, unique))) { + case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Failure(var exeptionNullable) -> throw exeptionNullable; + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Не удалось получить статистику + uris: {}""", uris); + throw new StatsServerUnavailableException("", cause); + } + }; + } +} diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java index af22f8a..f3bd2b9 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/UserFeignRepositoryImpl.java @@ -27,7 +27,7 @@ public class UserFeignRepositoryImpl implements UserFeignRepository { public @NonNull UserDto getUserDtoById(long userId) { log.info(""" PLEASE WAITING - Администратор получает пользователя с id={}""", userId); + Система получает пользователя с id={}""", userId); return switch (RemoteCallExecutor.execute(() -> userClient.getUser(userId))) { case RemoteCallResult.Success(var response) -> response; case RemoteCallResult.Failure(var exeption) -> { diff --git a/core/event-service/pom.xml b/core/event-service/pom.xml index adb167a..86abe85 100644 --- a/core/event-service/pom.xml +++ b/core/event-service/pom.xml @@ -19,11 +19,6 @@ stat-dto 0.0.1-SNAPSHOT
- - ru.practicum - stat-client - 0.0.1-SNAPSHOT - ru.practicum aggregation-api @@ -48,11 +43,6 @@ test - - org.zalando - logbook-spring-boot-starter - 4.0.3 - io.hypersistence hypersistence-utils-hibernate-63 diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java b/core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java index d3f291a..d7cdb4d 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/EventServiceApp.java @@ -7,6 +7,7 @@ import org.springframework.context.annotation.ComponentScan; import ru.practicum.aggregation.client.event.EventClient; import ru.practicum.aggregation.client.request.RequestClient; +import ru.practicum.aggregation.client.stats.StatsClient; import ru.practicum.aggregation.client.user.UserClient; import ru.practicum.aggregation.config.JacksonConfiguration; @@ -14,11 +15,11 @@ @EnableFeignClients(clients = { UserClient.class, EventClient.class, - RequestClient.class + RequestClient.class, + StatsClient.class }) @ComponentScan(value = { "ru.practicum.ewm.events", - "ru.practicum.stat.client", "ru.practicum.aggregation.config", "ru.practicum.aggregation.client", "ru.practicum.aggregation.error", diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCategoryController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCategoryController.java index e800010..f7504db 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCategoryController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCategoryController.java @@ -1,13 +1,17 @@ package ru.practicum.ewm.events.controller.admin; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; +import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import ru.practicum.aggregation.dto.category.output.CategoryDto; import ru.practicum.aggregation.dto.category.come.NewCategoryDto; import ru.practicum.ewm.events.service.category.CategoryService; +@Slf4j @RestController @RequestMapping(path = "/admin/categories") @RequiredArgsConstructor @@ -16,42 +20,48 @@ public class AdminCategoryController { private final CategoryService categoryService; /** - * Добавление новой категории. Обратите внимание: имя категории должно быть уникальным - * + * Обратите внимание: имя категории должно быть уникальным * @param newCategoryDto данные добавляемой категории - * @return CategoryDto */ @PostMapping @ResponseStatus(HttpStatus.CREATED) - public CategoryDto addNewCategory(@RequestBody @Valid NewCategoryDto newCategoryDto) { + public CategoryDto addNewCategory(@RequestBody @Valid NewCategoryDto newCategoryDto, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Добавление новой категории + {} {}""", request.getMethod(), request.getRequestURI()); + return categoryService.adminAddNewCategory(newCategoryDto); } /** - * Изменение категории - *

* Обратите внимание: имя категории должно быть уникальным - * - * @param catId id категории - * @param categoryDto Данные категории для изменения - * @return CategoryDto */ @PatchMapping("/{catId}") public CategoryDto updateCategory(@PathVariable Long catId, - @RequestBody @Valid CategoryDto categoryDto) { + @RequestBody @Valid CategoryDto categoryDto, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Изменение категории + {} {}""", request.getMethod(), request.getRequestURI()); + return categoryService.updateCategory(catId, categoryDto); } /** - * Удаление категории - *

* Обратите внимание: с категорией не должно быть связано ни одного события. - * - * @param catId id категории */ @DeleteMapping("/{catId}") @ResponseStatus(HttpStatus.NO_CONTENT) - public void deleteCategory(@PathVariable Long catId) { + public void deleteCategory(@PathVariable Long catId, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Удаление категории + {} {}""", request.getMethod(), request.getRequestURI()); + categoryService.deleteCategory(catId); } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCompilationController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCompilationController.java index 5c4a1d4..f212281 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCompilationController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminCompilationController.java @@ -1,7 +1,10 @@ package ru.practicum.ewm.events.controller.admin; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; +import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import ru.practicum.aggregation.dto.compilation.output.CompilationDto; @@ -9,6 +12,7 @@ import ru.practicum.aggregation.dto.compilation.come.create.NewCompilationDto; import ru.practicum.ewm.events.service.compilation.CompilationService; +@Slf4j @RestController @RequestMapping(path = "/admin/compilations") @RequiredArgsConstructor @@ -17,37 +21,41 @@ public class AdminCompilationController { private final CompilationService compilationService; /** - * Добавление новой подборки (подборка может не содержать событий) - * - * @param newCompilationDto данные новой подборки - * @return {@link CompilationDto} + * подборка может не содержать событий */ @PostMapping @ResponseStatus(HttpStatus.CREATED) - public CompilationDto createCompilation(@RequestBody @Valid NewCompilationDto newCompilationDto) { + public CompilationDto createCompilation(@RequestBody @Valid NewCompilationDto newCompilationDto, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Добавление новой подборки + {} {}""", request.getMethod(), request.getRequestURI()); + return compilationService.addCompilation(newCompilationDto); } - /** - * - * @param compilationUpdateDto данные для обновления подборки - * @param compId id подборки - * @return {@link CompilationDto} - */ @PatchMapping("/{compId}") public CompilationDto updateCompilation(@RequestBody @Valid CompilationUpdateDto compilationUpdateDto, - @PathVariable Long compId) { + @PathVariable Long compId, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Обновление подборки + {} {}""", request.getMethod(), request.getRequestURI()); + return compilationService.updateCompilation(compId, compilationUpdateDto); } - /** - * Удаление подборки - * - * @param compId id подборки - */ @DeleteMapping("/{compId}") @ResponseStatus(HttpStatus.NO_CONTENT) - public void delById(@PathVariable Long compId) { + public void delById(@PathVariable Long compId, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Удаление подборки + {} {}""", request.getMethod(), request.getRequestURI()); + compilationService.delById(compId); } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminEventController.java index f5fe408..7b57395 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/admin/AdminEventController.java @@ -1,7 +1,10 @@ package ru.practicum.ewm.events.controller.admin; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; +import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; import ru.practicum.aggregation.model.data.AdminGetData; @@ -13,6 +16,7 @@ import java.time.LocalDateTime; import java.util.List; +@Slf4j @RestController @RequestMapping(path = "/admin/events") @RequiredArgsConstructor @@ -21,21 +25,8 @@ public class AdminEventController { private final EventService eventService; /** - * Поиск событий - *

* Эндпоинт возвращает полную информацию обо всех событиях подходящих под переданные условия * В случае, если по заданным фильтрам не найдено ни одного события, возвращает пустой список - * - * @param users список id пользователей, чьи события нужно найти - * @param states список состояний в которых находятся искомые события - * @param categories список id категорий в которых будет вестись поиск - * @param rangeStart дата и время не раньше которых должно произойти событие - * @param rangeEnd дата и время не позже которых должно произойти событие - * @param from количество событий, которые нужно пропустить для формирования текущего набора - * Default value : 0 - * @param size количество событий в наборе - * Default value : 10 - * @return List<{@link EventFullDto}> */ @GetMapping public List adminGetEvents( @@ -60,9 +51,16 @@ public List adminGetEvents( Integer from, @RequestParam(required = false, defaultValue = "10") - Integer size + Integer size, + + @NonNull HttpServletRequest request ) { + log.info(""" + ENDPOINT + Поиск событий + {} {}""", request.getMethod(), request.getRequestURI()); + AdminGetData getDto = AdminGetData.builder() .users(users) .states(states) @@ -77,23 +75,28 @@ public List adminGetEvents( } /** - * Редактирование данных события и его статуса (отклонение/публикация) - *

- * Редактирование данных любого события администратором. Валидация данных не требуется. Обратите внимание: - * - дата начала изменяемого события должна быть не ранее чем за час от даты публикации. (Ожидается код ошибки 409) - * - событие можно публиковать, только если оно в состоянии ожидания публикации (Ожидается код ошибки 409) - * - событие можно отклонить, только если оно еще не опубликовано (Ожидается код ошибки 409) - * - * @param eventId id события - * @param request Данные HTTP-запроса - * @return {@link EventFullDto} + * Редактирование данных любого события администратором.
+ * Валидация данных не требуется.
+ * Обратите внимание:
+ * - дата начала изменяемого события должна быть не ранее чем за час от даты публикации.
+ * (Ожидается код ошибки 409)
+ * - событие можно публиковать, только если оно в состоянии ожидания публикации
+ * (Ожидается код ошибки 409)
+ * - событие можно отклонить, только если оно еще не опубликовано
+ * (Ожидается код ошибки 409) */ @PatchMapping("/{eventId}") public EventFullDto adminUpdateEvent( @PathVariable Long eventId, - @RequestBody @Valid UpdateEventAdminRequest request) { + @RequestBody @Valid UpdateEventAdminRequest updateRequest, + @NonNull HttpServletRequest request) { + + log.info(""" + ENDPOINT + Редактирование данных события и его статуса (отклонение/публикация) + {} {}""", request.getMethod(), request.getRequestURI()); - return eventService.adminUpdateEvent(eventId, request); + return eventService.adminUpdateEvent(eventId, updateRequest); } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCategoryController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCategoryController.java index 3b408e7..a282d43 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCategoryController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCategoryController.java @@ -1,7 +1,9 @@ package ru.practicum.ewm.events.controller.free; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.PositiveOrZero; +import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; @@ -19,33 +21,37 @@ public class FreeCategoryController { private final CategoryService categoryService; /** - * Получение категорий. - *

* В случае, если по заданным фильтрам не найдено ни одной категории, возвращает пустой список * * @param from количество категорий, которые нужно пропустить для формирования текущего набора * Default value : 0 * @param size количество категорий в наборе * Default value : 10 - * @return List<{@link CategoryDto}> */ @GetMapping public List findAll(@RequestParam(defaultValue = "0") @PositiveOrZero Integer from, - @RequestParam(defaultValue = "10") @Positive Integer size) { + @RequestParam(defaultValue = "10") @Positive Integer size, + @NonNull HttpServletRequest request) { + + log.info(""" + ENDPOINT + Получение категорий + {} {}""", request.getMethod(), request.getRequestURI()); return categoryService.findAll(from, size); } /** - * Получение информации о категории по её идентификатору. - *

* В случае, если категории с заданным id не найдено, возвращает статус код 404 - * - * @param catId id категории - * @return {@link CategoryDto} */ @GetMapping("/{catId}") - public CategoryDto findById(@PathVariable @Positive Long catId) { + public CategoryDto findById(@PathVariable @Positive Long catId, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Получение информации о категории по её идентификатору + {} {}""", request.getMethod(), request.getRequestURI()); + return categoryService.findById(catId); } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCompilationController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCompilationController.java index 16f2022..691086e 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCompilationController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeCompilationController.java @@ -1,7 +1,9 @@ package ru.practicum.ewm.events.controller.free; import jakarta.servlet.http.HttpServletRequest; +import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import ru.practicum.aggregation.dto.compilation.output.CompilationDto; import ru.practicum.aggregation.dto.compilation.come.get.CompilationSearchFilter; @@ -9,6 +11,7 @@ import java.util.List; +@Slf4j @RestController @RequestMapping(path = "/compilations") @RequiredArgsConstructor @@ -17,25 +20,20 @@ public class FreeCompilationController { private final CompilationService compilationService; /** - * Получение подборок событий - *

* В случае, если по заданным фильтрам не найдено ни одной подборки, возвращает пустой список - * - * @param pinned искать только закрепленные/не закрепленные подборки - * @param from количество элементов, которые нужно пропустить для формирования текущего набора - * Default value : 0 - * @param size количество элементов в наборе - * Default value : 10 - * @param request Данные HTTP-запроса - * @return List<{@link CompilationDto}> */ @GetMapping public List getCompilations( @RequestParam(required = false) Boolean pinned, @RequestParam(defaultValue = "0") Integer from, @RequestParam(defaultValue = "10") Integer size, - HttpServletRequest request + @NonNull HttpServletRequest request ) { + log.info(""" + ENDPOINT + Получение подборок событий + {} {}""", request.getMethod(), request.getRequestURI()); + CompilationSearchFilter filter = CompilationSearchFilter.builder() .pinned(pinned) .from(from) @@ -45,16 +43,14 @@ public List getCompilations( return compilationService.getByFilter(filter, request); } - /** - * Получение подборки событий по его id - * - * @param compId id подборки - * @param request Данные HTTP-запроса - * @return {@link CompilationDto} - */ @GetMapping("/{compId}") public CompilationDto getCompilationById(@PathVariable Long compId, - HttpServletRequest request) { + @NonNull HttpServletRequest request) { + + log.info(""" + ENDPOINT + Получение подборки событий по её id + {} {}""", request.getMethod(), request.getRequestURI()); return compilationService.getById(compId, request); } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeEventController.java index f3bf238..728027a 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/free/FreeEventController.java @@ -1,49 +1,43 @@ package ru.practicum.ewm.events.controller.free; import jakarta.servlet.http.HttpServletRequest; +import lombok.AccessLevel; +import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; import ru.practicum.aggregation.dto.event.output.EventFullDto; import ru.practicum.aggregation.dto.event.output.EventShortDto; import ru.practicum.aggregation.model.data.FreeGetData; +import ru.practicum.aggregation.repository.StatsFeignRepository; import ru.practicum.ewm.events.service.event.EventService; import java.time.LocalDateTime; import java.util.List; +@Slf4j @RestController @RequestMapping(path = "/events") @RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class FreeEventController { - private final EventService eventService; + EventService eventService; + StatsFeignRepository statsFeignRepository; /** - * Получение событий с возможностью фильтрации - *

- * Обратите внимание: - * - это публичный эндпоинт, соответственно в выдаче должны быть только опубликованные события - * - текстовый поиск (по аннотации и подробному описанию) должен быть без учета регистра букв + * Обратите внимание:
+ * - это публичный эндпоинт, соответственно в выдаче должны быть только опубликованные события
+ * - текстовый поиск (по аннотации и подробному описанию) должен быть без учета регистра букв
* - если в запросе не указан диапазон дат [rangeStart-rangeEnd], то нужно выгружать события, - * которые произойдут позже текущей даты и времени + * которые произойдут позже текущей даты и времени
* - информация о каждом событии должна включать в себя количество просмотров и количество - * уже одобренных заявок на участие + * уже одобренных заявок на участие
* - информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно - * сохранить в сервисе статистики + * сохранить в сервисе статистики
* В случае, если по заданным фильтрам не найдено ни одного события, возвращает пустой список - * - * @param text текст для поиска в содержимом аннотации и подробном описании события - * @param categories список идентификаторов категорий в которых будет вестись поиск - * @param paid поиск только платных/бесплатных событий - * @param rangeStart дата и время не раньше которых должно произойти событие - * @param rangeEnd дата и время не позже которых должно произойти событие - * @param onlyAvailable только события у которых не исчерпан лимит запросов на участие Default value : false - * @param sort Вариант сортировки: по дате события или по количеству просмотров Available values : EVENT_DATE, VIEWS - * @param from количество событий, которые нужно пропустить для формирования текущего набора Default value : 0 - * @param size количество событий в наборе Default value : 10 - * @param request Данные HTTP-запроса - * @return List<{@link EventShortDto}> */ @GetMapping public List getFreeEvents( @@ -77,7 +71,7 @@ public List getFreeEvents( @RequestParam(required = false, defaultValue = "10") Integer size, - HttpServletRequest request + @NonNull HttpServletRequest request ) { FreeGetData freeGetData = FreeGetData.builder() .text(text) @@ -91,21 +85,30 @@ public List getFreeEvents( .size(size) .build(); - return eventService.getFreeEvents(freeGetData, request); + log.info(""" + ENDPOINT + Получение событий с возможностью фильтрации + {} {}""", request.getMethod(), request.getRequestURI()); + + var result = eventService.getFreeEvents(freeGetData, request); + statsFeignRepository.sendHitRequest(request); + + return result; } - /** - * Получение подробной информации об опубликованном событии по его идентификатору. - * - * @param eventId id события - * @param request Данные HTTP-запроса - * @return {@link EventFullDto} - */ @GetMapping(value = "/{eventId}") public EventFullDto getFreeEventById(@PathVariable Long eventId, - HttpServletRequest request) { + @NonNull HttpServletRequest request) { + + log.info(""" + ENDPOINT + Получение подробной информации об опубликованном событии по его идентификатору. + {} {}""", request.getMethod(), request.getRequestURI()); + + var result = eventService.getFreeEventById(eventId, request); + statsFeignRepository.sendHitRequest(request); - return eventService.getFreeEventById(eventId, request); + return result; } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java index 527b464..591c2da 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/user/UserEventController.java @@ -1,10 +1,13 @@ package ru.practicum.ewm.events.controller.user; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.PositiveOrZero; +import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import ru.practicum.aggregation.dto.event.come.create.NewEventDto; @@ -19,6 +22,7 @@ import java.util.List; +@Slf4j @RestController @RequestMapping(path = "/users/{userId}/events") @RequiredArgsConstructor @@ -28,98 +32,90 @@ public class UserEventController { private final EventRequestService eventRequestService; /** - * Получение событий, добавленных текущим пользователем - * - * @param userId id текущего пользователя - * @param from количество элементов, которые нужно пропустить для формирования текущего набора + * @param from количество элементов, которые нужно пропустить для формирования текущего набора
* Default value : 0 - * @param size количество элементов в наборе + * @param size количество элементов в наборе
* Default value : 10 - * @return List<{@link EventShortDto}> */ @GetMapping public List findEventsByUserId( @PathVariable @Positive Long userId, @RequestParam(defaultValue = "0") @PositiveOrZero Integer from, - @RequestParam(defaultValue = "10") @Positive Integer size + @RequestParam(defaultValue = "10") @Positive Integer size, + @NonNull HttpServletRequest request ) { + log.info(""" + ENDPOINT + Получение событий, добавленных текущим пользователем + {} {}""", request.getMethod(), request.getRequestURI()); + return eventService.findByUserId(userId, from, size); } - /** - * Получение полной информации о событии добавленном текущим пользователем - * - * @param userId id текущего пользователя - * @param eventId id события - * @return {@link EventFullDto} - */ @GetMapping("/{eventId}") public EventFullDto findEventById(@PathVariable @Positive Long userId, - @PathVariable @Positive Long eventId) { - + @PathVariable @Positive Long eventId, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Получение полной информации о событии добавленном текущим пользователем + {} {}""", request.getMethod(), request.getRequestURI()); return eventService.findEventByUserIdAndEventId(userId, eventId); } /** - * Добавление нового события - *

* Обратите внимание: дата и время на которые намечено событие не может быть раньше, * чем через два часа от текущего момента - * - * @param userId id текущего пользователя - * @param newEventDto данные добавляемого события - * @return {@link EventFullDto} */ @PostMapping @ResponseStatus(HttpStatus.CREATED) public EventFullDto addEvent(@PathVariable @Positive Long userId, - @RequestBody @Valid NewEventDto newEventDto) { - + @RequestBody @Valid NewEventDto newEventDto, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Добавление нового события + {} {}""", request.getMethod(), request.getRequestURI()); return eventService.userAddNewEvent(userId, newEventDto); } /** - * Изменение события добавленного текущим пользователем - *

* Обратите внимание: *

- * - изменить можно только отмененные события или события в состоянии ожидания модерации (Ожидается код ошибки 409) + * - изменить можно только отмененные события или события в состоянии ожидания модерации
+ * (Ожидается код ошибки 409) *

- * - дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента + * - дата и время на которые намечено событие не может быть раньше, + * чем через два часа от текущего момента
* (Ожидается код ошибки 409) - * - * @param userId id текущего пользователя - * @param eventId id редактируемого события - * @param request Данные HTTP-запроса - * @return {@link EventFullDto} */ @PatchMapping("/{eventId}") public EventFullDto patchEvent(@PathVariable @Positive Long userId, @PathVariable @Positive Long eventId, - @RequestBody @Valid UpdateEventUserRequest request) { - - return eventService.patchEvent(userId, eventId, request); + @RequestBody @Valid UpdateEventUserRequest updateRequest, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Изменение события добавленного текущим пользователем + {} {}""", request.getMethod(), request.getRequestURI()); + return eventService.patchEvent(userId, eventId, updateRequest); } /** - * Получение информации о запросах на участие в событии текущего пользователя - *

* В случае, если по заданным фильтрам не найдено ни одной заявки, возвращает пустой список - * - * @param userId id текущего пользователя - * @param eventId id события - * @return List<{@link ParticipationRequestDto}> */ @GetMapping("/{eventId}/requests") public List getRequests(@PathVariable @Positive Long userId, - @PathVariable @Positive Long eventId) { - + @PathVariable @Positive Long eventId, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Получение информации о запросах на участие в событии текущего пользователя + {} {}""", request.getMethod(), request.getRequestURI()); return eventRequestService.findByEventId(userId, eventId); } /** - * Изменение статуса (подтверждена, отменена) заявок на участие в событии текущего пользователя - *

* Обратите внимание: *

* - если для события лимит заявок равен 0 или отключена пре-модерация заявок, то подтверждение заявок не требуется @@ -130,19 +126,19 @@ public List getRequests(@PathVariable @Positive Long us *

* - если при подтверждении данной заявки, лимит заявок для события исчерпан, то все неподтверждённые заявки * необходимо отклонить - * - * @param userId id текущего пользователя - * @param eventId id события текущего пользователя - * @param status Новый статус для заявок на участие в событии текущего пользователя - * @return {@link EventRequestStatusUpdateResult} */ @PatchMapping("/{eventId}/requests") public EventRequestStatusUpdateResult patchRequests( @PathVariable @Positive Long userId, @PathVariable @Positive Long eventId, - @RequestBody @NotNull @Valid EventRequestStatusUpdateRequest status + @RequestBody @NotNull @Valid EventRequestStatusUpdateRequest updateRequest, + @NonNull HttpServletRequest request ) { - return eventRequestService.updateStatusRequest(userId, eventId, status); + log.info(""" + ENDPOINT + Изменение статуса (подтверждена, отменена) заявок на участие в событии текущего пользователя + {} {}""", request.getMethod(), request.getRequestURI()); + return eventRequestService.updateStatusRequest(userId, eventId, updateRequest); } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java index 96acb63..5d844ee 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java @@ -17,13 +17,13 @@ import ru.practicum.aggregation.model.entity.BaseEntity; import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.repository.RequestFeignRepository; +import ru.practicum.aggregation.repository.StatsFeignRepository; import ru.practicum.aggregation.repository.UserFeignRepository; import ru.practicum.ewm.events.entity.Compilation; import ru.practicum.ewm.events.entity.Event; import ru.practicum.ewm.events.mapper.CompilationMapper; import ru.practicum.ewm.events.repository.CompilationRepository; import ru.practicum.ewm.events.repository.EventRepository; -import ru.practicum.ewm.events.statistic.StatRestRepository; import ru.practicum.stat.dto.ViewStatsDto; import java.util.*; @@ -38,14 +38,14 @@ public class CompilationServiceImpl implements CompilationService { EventRepository eventRepository; CompilationRepository compilationRepository; - StatRestRepository statRestRepository; + StatsFeignRepository statsFeignRepository; UserFeignRepository userFeignRepository; RequestFeignRepository requestFeignRepository; @Override public CompilationDto getById(Long compilationId, HttpServletRequest request) { - statRestRepository.sendHitRequest(request); + statsFeignRepository.sendHitRequest(request); var compilation = getCompilationById(compilationId); var confirmedRequests = getConfirmedRequests(List.of(compilation)); @@ -214,7 +214,7 @@ private Map getViews(@NonNull Collection compilations) // Все URIs List uris = allEvents.stream().map(e -> "/events/" + e.getId()).toList(); - List stats = statRestRepository.getStat(uris, false); + List stats = statsFeignRepository.getStat(uris, false); // Map return stats.stream().collect(Collectors.toMap(statsDto -> diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java index edbf5e7..c47442a 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java @@ -3,12 +3,12 @@ import jakarta.servlet.http.HttpServletRequest; import org.springframework.transaction.annotation.Transactional; import ru.practicum.aggregation.dto.event.come.create.NewEventDto; -import ru.practicum.aggregation.model.data.AdminGetData; -import ru.practicum.aggregation.model.data.FreeGetData; import ru.practicum.aggregation.dto.event.come.update.UpdateEventAdminRequest; import ru.practicum.aggregation.dto.event.come.update.UpdateEventUserRequest; import ru.practicum.aggregation.dto.event.output.EventFullDto; import ru.practicum.aggregation.dto.event.output.EventShortDto; +import ru.practicum.aggregation.model.data.AdminGetData; +import ru.practicum.aggregation.model.data.FreeGetData; import java.util.List; @@ -47,5 +47,5 @@ public interface EventService { /// Изменение события добавленного текущим пользователем @Transactional EventFullDto patchEvent(Long userId, Long eventId, UpdateEventUserRequest request); - + } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java index 87a2489..07add32 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java @@ -29,6 +29,7 @@ import ru.practicum.aggregation.model.data.FreeGetData; import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.repository.RequestFeignRepositoryImpl; +import ru.practicum.aggregation.repository.StatsFeignRepository; import ru.practicum.aggregation.repository.UserFeignRepositoryImpl; import ru.practicum.ewm.events.entity.Category; import ru.practicum.ewm.events.entity.Event; @@ -40,7 +41,6 @@ import ru.practicum.ewm.events.repository.EventRepository; import ru.practicum.ewm.events.specification.EventSpecifications; import ru.practicum.ewm.events.specification.SpecBuilder; -import ru.practicum.ewm.events.statistic.StatRestRepository; import ru.practicum.stat.dto.ViewStatsDto; import java.time.Instant; @@ -57,7 +57,7 @@ @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class EventServiceImpl implements EventService { - StatRestRepository statRepository; + StatsFeignRepository statsFeignRepository; EventRepository eventRepository; CategoryRepository categoryRepository; @@ -76,8 +76,6 @@ public List getFreeEvents(@NonNull FreeGetData dto, HttpServletRe } } - statRepository.sendHitRequest(request); - SpecBuilder builder = SpecBuilder.builder() .and(EventSpecifications.isPublished()) .andIf(dto.text() != null && !dto.text().isBlank(), @@ -120,13 +118,13 @@ public List getFreeEvents(@NonNull FreeGetData dto, HttpServletRe return Collections.emptyList(); } - List confirmedRequestsCounts = fetchConfirmedRequestsCount( + var confirmedRequestsCounts = fetchConfirmedRequestsCount( events.stream().map(Event::getId).toList()); Map eventIdToRequestCount = getRequestCountMap(confirmedRequestsCounts); List uris = getUris(events); - List stats = statRepository + List stats = statsFeignRepository .getStat(uris, dto.rangeStart(), dto.rangeEnd(), false); Map initiators = getUserShortDtoMap(events); @@ -152,14 +150,20 @@ public EventFullDto getFreeEventById(Long eventId, HttpServletRequest request) { "Событие с id=%s не существует или не опубликовано.".formatted(eventId)); } - statRepository.sendHitRequest(request); - var event = getEventById(eventId); + var initiator = UserMapper.toUserShortDto(getUserById(event.getInitiatorId())); + var uri = List.of(request.getRequestURI()); + var views = statsFeignRepository.getStat(uri, true) + .stream() + .findFirst() + .map(ViewStatsDto::getHits) + .orElse(0L); + var confirmedRequestsCount = getConfirmedRequestsCountByEventId(eventId); + var eventData = EventData.builder() - .initiator(UserMapper.toUserShortDto(getUserById(event.getInitiatorId()))) - .confirmedRequests(getConfirmedRequestsCountByEvent(eventId)) - .views(statRepository.getStat(List.of(request.getRequestURI()), true) - .getFirst().getHits()) + .initiator(initiator) + .confirmedRequests(confirmedRequestsCount) + .views(views) .build(); return EventMapper.toEventFullDto(event, eventData); @@ -196,7 +200,7 @@ public EventFullDto userAddNewEvent(Long userId, @NonNull NewEventDto newEventDt var eventData = EventData.builder() .initiator(initiator) .confirmedRequests(0) - .views(0L) + .views(0) .build(); return EventMapper.toEventFullDto(event, eventData); @@ -233,7 +237,7 @@ public List adminGetEvents(@NonNull AdminGetData dto) { Map eventIdToRequestCount = getRequestCountMap(confirmedRequestsCounts); List uris = getUris(events); - List stats = statRepository + List stats = statsFeignRepository .getStat(uris, dto.rangeStart(), dto.rangeEnd(), false); Map initiators = getUserShortDtoMap(events); @@ -310,7 +314,7 @@ public EventFullDto adminUpdateEvent(Long eventId, @NonNull UpdateEventAdminRequ var event = eventRepository.save(newEvent); - return EventMapper.toEventFullDto(event, getEventData(event)); + return EventMapper.toEventFullDto(event, getEventData(eventId, event.getInitiatorId())); } @Override @@ -329,7 +333,7 @@ public List findByUserId(Long userId, Integer from, Integer size) Map eventIdToRequestCount = getRequestCountMap(confirmedRequestsCounts); List uris = getUris(events); - List stats = statRepository.getStat(uris, true); + List stats = statsFeignRepository.getStat(uris, true); Map initiators = getUserShortDtoMap(events); @@ -351,12 +355,13 @@ public EventFullDto findEventByUserIdAndEventId(Long userId, Long eventId) { userFeignRepository.checkUser(userId); Event event = getEventById(eventId); + var initiatorId = event.getInitiatorId(); - if (!event.getInitiatorId().equals(userId)) { + if (!initiatorId.equals(userId)) { throw new ConflictException("Пользователь должен быть инициатором"); } - return EventMapper.toEventFullDto(event, getEventData(event)); + return EventMapper.toEventFullDto(event, getEventData(eventId, initiatorId)); } @Override @@ -382,29 +387,6 @@ public EventFullDto patchEvent(Long userId, Long eventId, @NonNull UpdateEventUs return patchEvent(eventId, request, false); } - private List fetchConfirmedRequestsCount(List events) { - return requestFeignRepository.getConfirmedRequestsCount(events); - } - - @NonNull - private List getUris(@NonNull List events) { - return events.stream().map(event -> EVENTS_PATH + event.getId()).toList(); - } - - private Map getRequestCountMap(@NonNull List eventRequestCountList) { - return eventRequestCountList.stream() - .collect(Collectors.toMap(EventRequestCount::eventId, EventRequestCount::count, - (a, b) -> a)); - } - - @NonNull - private Map getUserShortDtoMap(@NonNull List events) { - List userIds = events.stream().map(Event::getInitiatorId).toList(); - List users = userFeignRepository.getUsersByIds(userIds); - return users.stream() - .collect(Collectors.toMap(UserShortDto::id, user -> user)); - } - @SuppressWarnings("SameParameterValue") private EventFullDto patchEvent(Long eventId, @NonNull UpdateEventUserRequest request, @@ -456,7 +438,7 @@ private EventFullDto patchEvent(Long eventId, log.info("Событие обновлено: {}", patched.getId()); - return EventMapper.toEventFullDto(event, getEventData(event)); + return EventMapper.toEventFullDto(event, getEventData(eventId, event.getInitiatorId())); } catch (DataIntegrityViolationException e) { log.debug("Конфликт во время обновления события {}", request, e); @@ -464,11 +446,34 @@ private EventFullDto patchEvent(Long eventId, } } - private EventData getEventData(@NonNull Event event) { + private List fetchConfirmedRequestsCount(List events) { + return requestFeignRepository.getConfirmedRequestsCount(events); + } + + @NonNull + private List getUris(@NonNull List events) { + return events.stream().map(event -> EVENTS_PATH + event.getId()).toList(); + } + + private Map getRequestCountMap(@NonNull List eventRequestCountList) { + return eventRequestCountList.stream() + .collect(Collectors.toMap(EventRequestCount::eventId, EventRequestCount::count, + (a, b) -> a)); + } + + @NonNull + private Map getUserShortDtoMap(@NonNull List events) { + List userIds = events.stream().map(Event::getInitiatorId).toList(); + List users = userFeignRepository.getUsersByIds(userIds); + return users.stream() + .collect(Collectors.toMap(UserShortDto::id, user -> user)); + } + + private EventData getEventData(long eventId, long initiatorId) { return EventData.builder() - .initiator(UserMapper.toUserShortDto(getUserById(event.getInitiatorId()))) - .confirmedRequests(getConfirmedRequestsCountByEvent(event.getId())) - .views(getHits(event.getId())) + .initiator(UserMapper.toUserShortDto(getUserById(initiatorId))) + .confirmedRequests(getConfirmedRequestsCountByEventId(eventId)) + .views(getHits(eventId)) .build(); } @@ -491,7 +496,7 @@ private Category getCategoryById(long categoryId) { ); } - private long getConfirmedRequestsCountByEvent(long eventId) { + private long getConfirmedRequestsCountByEventId(long eventId) { var counts = fetchConfirmedRequestsCount(List.of(eventId)); if (counts.isEmpty()) { return 0; @@ -501,7 +506,7 @@ private long getConfirmedRequestsCountByEvent(long eventId) { } private long getHits(long eventId) { - List stats = statRepository.getStat( + List stats = statsFeignRepository.getStat( List.of(EVENTS_PATH + eventId), true); if (stats.isEmpty()) { return 0; diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/statistic/StatRestRepository.java b/core/event-service/src/main/java/ru/practicum/ewm/events/statistic/StatRestRepository.java deleted file mode 100644 index 443b0b6..0000000 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/statistic/StatRestRepository.java +++ /dev/null @@ -1,86 +0,0 @@ -package ru.practicum.ewm.events.statistic; - -import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import ru.practicum.aggregation.error.exception.stats.HitRequestException; -import ru.practicum.aggregation.error.exception.stats.StatsResponseException; -import ru.practicum.stat.client.StatClient; -import ru.practicum.stat.dto.EndpointHitDto; -import ru.practicum.stat.dto.StatsRequest; -import ru.practicum.stat.dto.ViewStatsDto; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; -import java.util.List; - -@Component -@RequiredArgsConstructor -public class StatRestRepository { - - private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - private final LocalDateTime startUnixEpoch = LocalDateTime.parse("1970-01-01T00:00:00"); - - @Value("${spring.application.name}") - private String appName; - - private final StatClient statClient; - - public void sendHitRequest(HttpServletRequest request) { - try { - statClient.hit(EndpointHitDto.builder() - .ip(request.getRemoteAddr()) - .uri(request.getRequestURI()) - .app(appName) - .timestamp(LocalDateTime.now()) - .build() - ); - } catch (Exception ex) { - throw new HitRequestException(ex); - } - } - - public List getStat(List statUris, - LocalDateTime rangeStart, - LocalDateTime rangeEnd, - boolean uniqe) { - if (statUris == null || statUris.isEmpty()) { - throw new StatsResponseException(); - } - - if (rangeStart == null) { - rangeStart = startUnixEpoch; - } - - if (rangeEnd == null) { - rangeEnd = LocalDateTime.now(); - } - - StatsRequest request = StatsRequest.builder() - .uris(statUris) - .start(rangeStart.truncatedTo(ChronoUnit.MILLIS).format(formatter)) - .end(rangeEnd.truncatedTo(ChronoUnit.MILLIS).format(formatter)) - .unique(uniqe) - .build(); - - List stats; - try { - stats = statClient.getStat(request); - } catch (Exception ex) { - throw new StatsResponseException(ex); - } - - if (stats == null) { - throw new StatsResponseException(); - } - - return stats; - } - - public List getStat(List statUris, boolean uniqe) { - return getStat(statUris, null, null, uniqe); - } - -} diff --git a/core/rating-service/pom.xml b/core/rating-service/pom.xml index 87552cc..72e6f4a 100644 --- a/core/rating-service/pom.xml +++ b/core/rating-service/pom.xml @@ -37,11 +37,6 @@ test - - org.zalando - logbook-spring-boot-starter - 4.0.3 - io.hypersistence hypersistence-utils-hibernate-63 diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java index ced4f45..64ea6eb 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/RatingServiceApp.java @@ -7,6 +7,7 @@ import org.springframework.context.annotation.ComponentScan; import ru.practicum.aggregation.client.event.EventClient; import ru.practicum.aggregation.client.request.RequestClient; +import ru.practicum.aggregation.client.stats.StatsClient; import ru.practicum.aggregation.client.user.UserClient; import ru.practicum.aggregation.config.JacksonConfiguration; @@ -14,7 +15,8 @@ @EnableFeignClients(clients = { UserClient.class, EventClient.class, - RequestClient.class + RequestClient.class, + StatsClient.class }) @ComponentScan(value = { "ru.practicum.ewm.ratings", diff --git a/core/request-service/pom.xml b/core/request-service/pom.xml index e02eb3f..589db77 100644 --- a/core/request-service/pom.xml +++ b/core/request-service/pom.xml @@ -37,11 +37,6 @@ test - - org.zalando - logbook-spring-boot-starter - 4.0.3 - io.hypersistence hypersistence-utils-hibernate-63 diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java index 7ce77d9..f5824cf 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/RequestServiceApp.java @@ -7,6 +7,7 @@ import org.springframework.context.annotation.ComponentScan; import ru.practicum.aggregation.client.event.EventClient; import ru.practicum.aggregation.client.request.RequestClient; +import ru.practicum.aggregation.client.stats.StatsClient; import ru.practicum.aggregation.client.user.UserClient; import ru.practicum.aggregation.config.JacksonConfiguration; @@ -14,7 +15,8 @@ @EnableFeignClients(clients = { UserClient.class, EventClient.class, - RequestClient.class + RequestClient.class, + StatsClient.class }) @ComponentScan(value = { "ru.practicum.ewm.requests", diff --git a/core/user-service/pom.xml b/core/user-service/pom.xml index ffefe46..408bf83 100644 --- a/core/user-service/pom.xml +++ b/core/user-service/pom.xml @@ -36,11 +36,6 @@ test - - org.zalando - logbook-spring-boot-starter - 4.0.3 - io.hypersistence hypersistence-utils-hibernate-63 diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java b/core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java index 7c7655a..06801fa 100644 --- a/core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/UserServiceApp.java @@ -7,6 +7,7 @@ import org.springframework.context.annotation.ComponentScan; import ru.practicum.aggregation.client.event.EventClient; import ru.practicum.aggregation.client.request.RequestClient; +import ru.practicum.aggregation.client.stats.StatsClient; import ru.practicum.aggregation.client.user.UserClient; import ru.practicum.aggregation.config.JacksonConfiguration; @@ -14,7 +15,8 @@ @EnableFeignClients(clients = { UserClient.class, EventClient.class, - RequestClient.class + RequestClient.class, + StatsClient.class }) @ComponentScan(value = { "ru.practicum.ewm.users", diff --git a/infra/config-repo/core/event-service.yml b/infra/config-repo/core/event-service.yml index 7202a56..d9ff588 100644 --- a/infra/config-repo/core/event-service.yml +++ b/infra/config-repo/core/event-service.yml @@ -23,8 +23,8 @@ spring: client: config: default: - connectTimeout: 2000 - readTimeout: 3000 + connectTimeout: 3000 + readTimeout: 4000 loggerLevel: basic circuitbreaker: enabled: true @@ -47,8 +47,3 @@ resilience4j: configs: default: timeout-duration: 3s - -#logging: -# org: -# zalando: -# logbook: trace \ No newline at end of file diff --git a/infra/config-repo/core/rating-service.yml b/infra/config-repo/core/rating-service.yml index 04b17f2..d1bc18a 100644 --- a/infra/config-repo/core/rating-service.yml +++ b/infra/config-repo/core/rating-service.yml @@ -48,7 +48,3 @@ resilience4j: default: timeout-duration: 3s -#logging: -# org: -# zalando: -# logbook: trace diff --git a/infra/config-repo/core/request-service.yml b/infra/config-repo/core/request-service.yml index 6b62f39..70d5efd 100644 --- a/infra/config-repo/core/request-service.yml +++ b/infra/config-repo/core/request-service.yml @@ -48,7 +48,3 @@ resilience4j: default: timeout-duration: 3s -#logging: -# org: -# zalando: -# logbook: trace diff --git a/infra/config-repo/core/user-service.yml b/infra/config-repo/core/user-service.yml index 4106e3d..663a106 100644 --- a/infra/config-repo/core/user-service.yml +++ b/infra/config-repo/core/user-service.yml @@ -48,7 +48,3 @@ resilience4j: default: timeout-duration: 3s -#logging: -# org: -# zalando: -# logbook: trace diff --git a/infra/gateway-server/src/main/resources/application.yml b/infra/gateway-server/src/main/resources/application.yml index 63212b3..f5cf8f5 100644 --- a/infra/gateway-server/src/main/resources/application.yml +++ b/infra/gateway-server/src/main/resources/application.yml @@ -5,7 +5,7 @@ spring: application: name: gateway-server config: - import: "optional:configserver:" + import: "configserver:" cloud: config: discovery: diff --git a/stats/pom.xml b/stats/pom.xml index 007e33b..494a796 100644 --- a/stats/pom.xml +++ b/stats/pom.xml @@ -16,7 +16,6 @@ stat-dto stats-server - stat-client \ No newline at end of file diff --git a/stats/stat-client/pom.xml b/stats/stat-client/pom.xml deleted file mode 100644 index ea100b1..0000000 --- a/stats/stat-client/pom.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - 4.0.0 - - ru.practicum - stats - 0.0.1-SNAPSHOT - - - stat-client - jar - - - - ru.practicum - stat-dto - 0.0.1-SNAPSHOT - - - org.projectlombok - lombok - true - - - org.springframework.boot - spring-boot-starter-web - - - org.apache.httpcomponents.client5 - httpclient5 - - - org.springframework.cloud - spring-cloud-commons - - - org.springframework.retry - spring-retry - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.projectlombok - lombok - ${lombok.version} - - - - - - - - \ No newline at end of file diff --git a/stats/stat-client/src/main/java/ru/practicum/stat/client/StatClient.java b/stats/stat-client/src/main/java/ru/practicum/stat/client/StatClient.java deleted file mode 100644 index 579f3a5..0000000 --- a/stats/stat-client/src/main/java/ru/practicum/stat/client/StatClient.java +++ /dev/null @@ -1,127 +0,0 @@ -package ru.practicum.stat.client; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.lang.NonNull; -import org.springframework.retry.backoff.FixedBackOffPolicy; -import org.springframework.retry.policy.MaxAttemptsRetryPolicy; -import org.springframework.retry.support.RetryTemplate; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; -import ru.practicum.stat.client.exception.StatsServerUnavailable; -import ru.practicum.stat.dto.EndpointHitDto; -import ru.practicum.stat.dto.StatsRequest; -import ru.practicum.stat.dto.ViewStatsDto; - -import java.net.URI; -import java.util.List; - -@Slf4j -@Component -public class StatClient { - - private final RestTemplate rest; - - private final RetryTemplate retryTemplate; - - private final DiscoveryClient discoveryClient; - - public StatClient(@NonNull RestTemplateBuilder builder, - @NonNull DiscoveryClient discoveryClient) { - this.discoveryClient = discoveryClient; - this.retryTemplate = createRetryTemplate(); - this.rest = builder.build(); - } - - public void hit(EndpointHitDto endpointHitDto) { - try { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - - HttpEntity requestEntity = new HttpEntity<>(endpointHitDto, headers); - - rest.exchange( - makeUri("/hit"), - HttpMethod.POST, - requestEntity, - Void.class - ); - } catch (Exception e) { - log.error("Ошибка записи: {}", endpointHitDto, e); - } - - } - - public List getStat(StatsRequest statsRequest) { - try { - UriComponentsBuilder builder = UriComponentsBuilder - .fromUri(makeUri("/stats")) - .queryParam("start", statsRequest.getStart()) - .queryParam("end", statsRequest.getEnd()) - .queryParam("unique", statsRequest.getUnique()); - - List uris = statsRequest.getUris(); - - if (uris != null && !uris.isEmpty()) { - builder.queryParam("uris", uris); - } - - URI uri = builder.encode().build().toUri(); - - return rest.exchange( - uri, - HttpMethod.GET, - null, - new ParameterizedTypeReference>() { - } - ).getBody(); - - } catch (Exception e) { - log.error("Ошибка записи: {}", statsRequest, e); - return null; - } - } - - private ServiceInstance getInstance() { - String statsServiceId = "stats-server"; - try { - return discoveryClient - .getInstances(statsServiceId) - .getFirst(); - } catch (Exception exception) { - throw new StatsServerUnavailable( - "Ошибка обнаружения адреса сервиса статистики с id: " + statsServiceId, - exception - ); - } - } - - @NonNull - private URI makeUri(@NonNull String path) { - ServiceInstance instance = retryTemplate.execute(cxt -> getInstance()); - return URI.create("http://" + instance.getHost() + ":" + instance.getPort() + path); - } - - @NonNull - private RetryTemplate createRetryTemplate() { - RetryTemplate retryTemplate = new RetryTemplate(); - - FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); - fixedBackOffPolicy.setBackOffPeriod(3000L); - retryTemplate.setBackOffPolicy(fixedBackOffPolicy); - - MaxAttemptsRetryPolicy retryPolicy = new MaxAttemptsRetryPolicy(); - retryPolicy.setMaxAttempts(3); - retryTemplate.setRetryPolicy(retryPolicy); - - return retryTemplate; - } -} diff --git a/stats/stat-client/src/main/java/ru/practicum/stat/client/exception/StatsServerUnavailable.java b/stats/stat-client/src/main/java/ru/practicum/stat/client/exception/StatsServerUnavailable.java deleted file mode 100644 index 47d4df3..0000000 --- a/stats/stat-client/src/main/java/ru/practicum/stat/client/exception/StatsServerUnavailable.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.stat.client.exception; - -public class StatsServerUnavailable extends RuntimeException { - public StatsServerUnavailable(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/stats/stat-dto/src/main/java/ru/practicum/stat/dto/EndpointHitDto.java b/stats/stat-dto/src/main/java/ru/practicum/stat/dto/EndpointHitDto.java index c480d43..4883b3a 100644 --- a/stats/stat-dto/src/main/java/ru/practicum/stat/dto/EndpointHitDto.java +++ b/stats/stat-dto/src/main/java/ru/practicum/stat/dto/EndpointHitDto.java @@ -16,6 +16,7 @@ @NoArgsConstructor @AllArgsConstructor public class EndpointHitDto { + @NotBlank(message = "Название приложения не может быть пустым") @Size(max = 255, message = "APP слишком длинный (макс. 255 символов)") private String app; diff --git a/stats/stats-server/pom.xml b/stats/stats-server/pom.xml index 6d5223e..4f59218 100644 --- a/stats/stats-server/pom.xml +++ b/stats/stats-server/pom.xml @@ -21,8 +21,7 @@ org.projectlombok lombok - - + provided org.springframework.boot @@ -87,7 +86,6 @@ io.micrometer micrometer-registry-prometheus - diff --git a/stats/stats-server/src/main/java/ru/practicum/stat/server/util/config/StatServerConfiguration.java b/stats/stats-server/src/main/java/ru/practicum/stat/server/config/JacksonConfiguration.java similarity index 91% rename from stats/stats-server/src/main/java/ru/practicum/stat/server/util/config/StatServerConfiguration.java rename to stats/stats-server/src/main/java/ru/practicum/stat/server/config/JacksonConfiguration.java index 669e26a..dec31a4 100644 --- a/stats/stats-server/src/main/java/ru/practicum/stat/server/util/config/StatServerConfiguration.java +++ b/stats/stats-server/src/main/java/ru/practicum/stat/server/config/JacksonConfiguration.java @@ -1,4 +1,4 @@ -package ru.practicum.stat.server.util.config; +package ru.practicum.stat.server.config; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; @@ -9,7 +9,7 @@ import java.time.format.DateTimeFormatter; @SpringBootConfiguration -public class StatServerConfiguration { +public class JacksonConfiguration { @Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { diff --git a/stats/stats-server/src/main/java/ru/practicum/stat/server/config/WebConfig.java b/stats/stats-server/src/main/java/ru/practicum/stat/server/config/WebConfig.java new file mode 100644 index 0000000..61bd1da --- /dev/null +++ b/stats/stats-server/src/main/java/ru/practicum/stat/server/config/WebConfig.java @@ -0,0 +1,20 @@ +package ru.practicum.stat.server.config; + +import org.jspecify.annotations.NonNull; +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import java.time.format.DateTimeFormatter; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addFormatters(@NonNull FormatterRegistry registry) { + DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); + registrar.setUseIsoFormat(false); + registrar.setDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + registrar.registerFormatters(registry); + } +} \ No newline at end of file diff --git a/stats/stats-server/src/main/java/ru/practicum/stat/server/controller/StatController.java b/stats/stats-server/src/main/java/ru/practicum/stat/server/controller/StatController.java index dc37f5b..8434216 100644 --- a/stats/stats-server/src/main/java/ru/practicum/stat/server/controller/StatController.java +++ b/stats/stats-server/src/main/java/ru/practicum/stat/server/controller/StatController.java @@ -1,6 +1,7 @@ package ru.practicum.stat.server.controller; import jakarta.validation.Valid; +import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.format.annotation.DateTimeFormat; @@ -13,16 +14,16 @@ import java.time.LocalDateTime; import java.util.List; +@Slf4j @RestController @RequiredArgsConstructor -@Slf4j public class StatController { private final StatService statService; @PostMapping("/hit") @ResponseStatus(HttpStatus.CREATED) - public void hit(@Valid @RequestBody EndpointHitDto endpointHitDto) { + public void hit(@NonNull @Valid @RequestBody EndpointHitDto endpointHitDto) { log.info("Сохранение статистики: сервис={}, uri={}, ip={}", endpointHitDto.getApp(), endpointHitDto.getUri(), endpointHitDto.getIp()); statService.saveHit(endpointHitDto); diff --git a/stats/stats-server/src/main/java/ru/practicum/stat/server/model/EndpointHit.java b/stats/stats-server/src/main/java/ru/practicum/stat/server/entity/EndpointHit.java similarity index 68% rename from stats/stats-server/src/main/java/ru/practicum/stat/server/model/EndpointHit.java rename to stats/stats-server/src/main/java/ru/practicum/stat/server/entity/EndpointHit.java index d8181dc..120b5cb 100644 --- a/stats/stats-server/src/main/java/ru/practicum/stat/server/model/EndpointHit.java +++ b/stats/stats-server/src/main/java/ru/practicum/stat/server/entity/EndpointHit.java @@ -1,4 +1,4 @@ -package ru.practicum.stat.server.model; +package ru.practicum.stat.server.entity; import jakarta.persistence.*; import lombok.*; @@ -13,6 +13,7 @@ @AllArgsConstructor @NoArgsConstructor public class EndpointHit { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -28,4 +29,16 @@ public class EndpointHit { @Column(name = "hit_timestamp", nullable = false) private LocalDateTime timestamp; + + @Override + public String toString() { + return """ + { + "id": "%s", + "app": "%s", + "uri": "%s", + "ip": "%s", + "timestamp": "%s" + }""".formatted(id, app, uri, ip, timestamp); + } } diff --git a/stats/stats-server/src/main/java/ru/practicum/stat/server/util/error/ApiError.java b/stats/stats-server/src/main/java/ru/practicum/stat/server/error/ApiError.java similarity index 73% rename from stats/stats-server/src/main/java/ru/practicum/stat/server/util/error/ApiError.java rename to stats/stats-server/src/main/java/ru/practicum/stat/server/error/ApiError.java index d619347..389e94d 100644 --- a/stats/stats-server/src/main/java/ru/practicum/stat/server/util/error/ApiError.java +++ b/stats/stats-server/src/main/java/ru/practicum/stat/server/error/ApiError.java @@ -1,10 +1,13 @@ -package ru.practicum.stat.server.util.error; +package ru.practicum.stat.server.error; +import lombok.AccessLevel; import lombok.Getter; +import lombok.experimental.FieldDefaults; import org.springframework.http.HttpStatus; import java.time.LocalDateTime; @Getter +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class ApiError { private HttpStatus status; private String reason; diff --git a/stats/stats-server/src/main/java/ru/practicum/stat/server/util/error/StatErrorHandler.java b/stats/stats-server/src/main/java/ru/practicum/stat/server/error/StatErrorHandler.java similarity index 66% rename from stats/stats-server/src/main/java/ru/practicum/stat/server/util/error/StatErrorHandler.java rename to stats/stats-server/src/main/java/ru/practicum/stat/server/error/StatErrorHandler.java index 98e75d8..73c0155 100644 --- a/stats/stats-server/src/main/java/ru/practicum/stat/server/util/error/StatErrorHandler.java +++ b/stats/stats-server/src/main/java/ru/practicum/stat/server/error/StatErrorHandler.java @@ -1,11 +1,15 @@ -package ru.practicum.stat.server.util.error; +package ru.practicum.stat.server.error; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; +import org.springframework.lang.NonNull; import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; + import java.io.PrintWriter; import java.io.StringWriter; @@ -13,15 +17,14 @@ @RestControllerAdvice public class StatErrorHandler { - // Обработка 400 Bad Request @ExceptionHandler({ MethodArgumentNotValidException.class, IllegalArgumentException.class, - org.springframework.web.bind.MissingServletRequestParameterException.class, // Отсутствие параметра - org.springframework.web.method.annotation.MethodArgumentTypeMismatchException.class // Неверный формат + MissingServletRequestParameterException.class, + MethodArgumentTypeMismatchException.class }) @ResponseStatus(HttpStatus.BAD_REQUEST) - public ApiError handleBadRequest(final Exception e) { // Здесь Exception + public ApiError handleBadRequest(@NonNull final Exception e) { log.error("400 Bad Request: {}", e.getMessage()); return new ApiError( HttpStatus.BAD_REQUEST, @@ -31,7 +34,7 @@ public ApiError handleBadRequest(final Exception e) { // Здесь Exception ); } - private String getStackTrace(Exception e) { + private String getStackTrace(@NonNull Exception e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); @@ -43,6 +46,7 @@ private String getStackTrace(Exception e) { public ApiError handleException(Exception e) { log.info("500 {}", e.getMessage(), e); String stackTrace = getStackTrace(e); - return new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, "Error ....", e.getMessage(), stackTrace); + return new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, + "Error ....", e.getMessage(), stackTrace); } } diff --git a/stats/stats-server/src/main/java/ru/practicum/stat/server/mapper/EndpointHitMapper.java b/stats/stats-server/src/main/java/ru/practicum/stat/server/mapper/EndpointHitMapper.java index fb45b19..dfe0e33 100644 --- a/stats/stats-server/src/main/java/ru/practicum/stat/server/mapper/EndpointHitMapper.java +++ b/stats/stats-server/src/main/java/ru/practicum/stat/server/mapper/EndpointHitMapper.java @@ -2,7 +2,9 @@ import org.springframework.lang.NonNull; import ru.practicum.stat.dto.EndpointHitDto; -import ru.practicum.stat.server.model.EndpointHit; +import ru.practicum.stat.server.entity.EndpointHit; + +import java.time.temporal.ChronoUnit; public class EndpointHitMapper { @@ -11,7 +13,7 @@ public static EndpointHit toEntity(@NonNull EndpointHitDto dto) { .app(dto.getApp()) .uri(dto.getUri()) .ip(dto.getIp()) - .timestamp(dto.getTimestamp()) + .timestamp(dto.getTimestamp().truncatedTo(ChronoUnit.SECONDS)) .build(); } } diff --git a/stats/stats-server/src/main/java/ru/practicum/stat/server/dao/StatRepository.java b/stats/stats-server/src/main/java/ru/practicum/stat/server/repository/StatRepository.java similarity index 93% rename from stats/stats-server/src/main/java/ru/practicum/stat/server/dao/StatRepository.java rename to stats/stats-server/src/main/java/ru/practicum/stat/server/repository/StatRepository.java index c76d2dc..18b4294 100644 --- a/stats/stats-server/src/main/java/ru/practicum/stat/server/dao/StatRepository.java +++ b/stats/stats-server/src/main/java/ru/practicum/stat/server/repository/StatRepository.java @@ -1,10 +1,10 @@ -package ru.practicum.stat.server.dao; +package ru.practicum.stat.server.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import ru.practicum.stat.dto.ViewStatsDto; -import ru.practicum.stat.server.model.EndpointHit; +import ru.practicum.stat.server.entity.EndpointHit; import java.time.LocalDateTime; import java.util.List; diff --git a/stats/stats-server/src/main/java/ru/practicum/stat/server/service/StatService.java b/stats/stats-server/src/main/java/ru/practicum/stat/server/service/StatService.java index aaed2ce..48bd30f 100644 --- a/stats/stats-server/src/main/java/ru/practicum/stat/server/service/StatService.java +++ b/stats/stats-server/src/main/java/ru/practicum/stat/server/service/StatService.java @@ -7,14 +7,14 @@ import org.springframework.transaction.annotation.Transactional; import ru.practicum.stat.dto.EndpointHitDto; import ru.practicum.stat.dto.ViewStatsDto; -import ru.practicum.stat.server.dao.StatRepository; import ru.practicum.stat.server.mapper.EndpointHitMapper; +import ru.practicum.stat.server.repository.StatRepository; import java.time.LocalDateTime; import java.util.List; -@Service @Slf4j +@Service @RequiredArgsConstructor @Transactional(readOnly = true) public class StatService { @@ -27,7 +27,10 @@ public void saveHit(@NonNull EndpointHitDto endpointHitDto) { statRepository.save(EndpointHitMapper.toEntity(endpointHitDto)); } - public List getStats(LocalDateTime start, LocalDateTime end, List uris, boolean unique) { + public List getStats(LocalDateTime start, + LocalDateTime end, + List uris, + boolean unique) { if (unique) { log.debug("Получение статистики (уникальные IP) для URI: {}", uris); return statRepository.getStatsUnique(start, end, uris); diff --git a/stats/stats-server/src/main/resources/schema-postgres.sql b/stats/stats-server/src/main/resources/schema-postgres.sql index 09ae1f7..37cf885 100644 --- a/stats/stats-server/src/main/resources/schema-postgres.sql +++ b/stats/stats-server/src/main/resources/schema-postgres.sql @@ -4,4 +4,6 @@ CREATE TABLE IF NOT EXISTS stats ( uri VARCHAR(512) NOT NULL, ip VARCHAR(45) NOT NULL, hit_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL - ); \ No newline at end of file + ); + +CREATE INDEX IF NOT EXISTS idx_endpoint_hits_stats ON stats(hit_timestamp, uri, ip); \ No newline at end of file diff --git a/stats/stats-server/src/test/java/ru/practicum/stat/server/mapper/EndpointHitMapperTest.java b/stats/stats-server/src/test/java/ru/practicum/stat/server/mapper/EndpointHitMapperTest.java deleted file mode 100644 index 479ba67..0000000 --- a/stats/stats-server/src/test/java/ru/practicum/stat/server/mapper/EndpointHitMapperTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package ru.practicum.stat.server.mapper; - -import org.junit.jupiter.api.Test; -import ru.practicum.stat.dto.EndpointHitDto; -import ru.practicum.stat.server.model.EndpointHit; - -import java.time.LocalDateTime; - -import static org.junit.jupiter.api.Assertions.*; - -class EndpointHitMapperTest { - - @Test - void toEntity_shouldMapAllFieldsCorrectly() { - LocalDateTime now = LocalDateTime.now(); - EndpointHitDto dto = EndpointHitDto.builder() - .app("ewm-main-service") - .uri("/events/1") - .ip("192.168.1.1") - .timestamp(now) - .build(); - EndpointHit entity = EndpointHitMapper.toEntity(dto); - - assertNotNull(entity); - assertEquals(dto.getApp(), entity.getApp()); - assertEquals(dto.getUri(), entity.getUri()); - assertEquals(dto.getIp(), entity.getIp()); - assertEquals(dto.getTimestamp(), entity.getTimestamp()); - } -} \ No newline at end of file From 0f288a578d55b8be0b6d7f7caedf327948b6832f Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Tue, 1 Sep 2026 22:07:10 +0300 Subject: [PATCH 12/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Рефакторинг EventServiceImpl и StatsFeignRepository Теперь тесты проходят и без статистики --- .../model/data/StatsRequestData.java | 24 ++++ .../repository/StatsFeignRepository.java | 11 +- .../repository/StatsFeignRepositoryImpl.java | 54 +++----- .../compilation/CompilationServiceImpl.java | 15 ++- .../service/event/EventServiceImpl.java | 121 ++++++++++++------ .../ru/practicum/stat/dto/StatsRequest.java | 27 ---- 6 files changed, 143 insertions(+), 109 deletions(-) create mode 100644 core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/StatsRequestData.java delete mode 100644 stats/stat-dto/src/main/java/ru/practicum/stat/dto/StatsRequest.java diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/StatsRequestData.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/StatsRequestData.java new file mode 100644 index 0000000..ac17417 --- /dev/null +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/data/StatsRequestData.java @@ -0,0 +1,24 @@ +package ru.practicum.aggregation.model.data; + +import lombok.Builder; +import lombok.Data; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; + +@Data +@Builder +public class StatsRequestData { + + private List uris; + + @Builder.Default + private LocalDateTime start = LocalDateTime.parse("1970-01-01T00:00:00"); + + @Builder.Default + private LocalDateTime end = LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS); + + @Builder.Default + private Boolean unique = false; +} \ No newline at end of file diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepository.java index d156037..0ff5776 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepository.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepository.java @@ -1,21 +1,16 @@ package ru.practicum.aggregation.repository; import jakarta.servlet.http.HttpServletRequest; +import ru.practicum.aggregation.model.data.StatsRequestData; import ru.practicum.stat.dto.ViewStatsDto; -import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; public interface StatsFeignRepository { - LocalDateTime startUnixEpoch = LocalDateTime.parse("1970-01-01T00:00:00"); - void sendHitRequest(HttpServletRequest request); - List getStat(List statUris, - LocalDateTime rangeStart, - LocalDateTime rangeEnd, - boolean uniqe); + Optional> getStatList(StatsRequestData request); - List getStat(List statUris, boolean uniqe); } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepositoryImpl.java index f0e7558..6f090e7 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/StatsFeignRepositoryImpl.java @@ -12,13 +12,14 @@ import ru.practicum.aggregation.client.RemoteCallResult; import ru.practicum.aggregation.client.stats.StatsClient; import ru.practicum.aggregation.error.exception.stats.StatsResponseException; -import ru.practicum.aggregation.error.exception.unavailable.StatsServerUnavailableException; import ru.practicum.stat.dto.EndpointHitDto; +import ru.practicum.aggregation.model.data.StatsRequestData; import ru.practicum.stat.dto.ViewStatsDto; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.List; +import java.util.Optional; @Slf4j @Component @@ -40,29 +41,14 @@ public void sendHitRequest(@NonNull HttpServletRequest request) { } @Override - public List getStat(List statUris, - LocalDateTime rangeStart, - LocalDateTime rangeEnd, - boolean uniqe) { + public Optional> getStatList(@NonNull StatsRequestData request) { - if (statUris == null || statUris.isEmpty()) { + var statUris = request.getUris(); + if (statUris.isEmpty()) { throw new StatsResponseException(); } - if (rangeStart == null) { - rangeStart = startUnixEpoch.truncatedTo(ChronoUnit.SECONDS); - } - - if (rangeEnd == null) { - rangeEnd = LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS); - } - - return getStatistic(rangeStart, rangeEnd, statUris, uniqe); - } - - @Override - public List getStat(List statUris, boolean uniqe) { - return getStat(statUris, null, null, uniqe); + return getStatistic(request.getStart(), request.getEnd(), statUris, request.getUnique()); } @SuppressWarnings("unused") @@ -77,33 +63,33 @@ private void hit(EndpointHitDto endpointHitDto) { } case RemoteCallResult.Failure(var exeptionNullable) -> { } - case RemoteCallResult.Degraded(var cause) -> { - log.warn(""" - Деградация - Не удалось зарегистрировать статистику - {}""", endpointHitDto); - throw new StatsServerUnavailableException("", cause); - } + case RemoteCallResult.Degraded(var cause) -> log.warn(""" + Деградация + Не удалось зарегистрировать статистику + {}""", endpointHitDto, cause); + } } - private List getStatistic(LocalDateTime start, - LocalDateTime end, - List uris, - boolean unique) { + @NonNull + private Optional> getStatistic(LocalDateTime start, + LocalDateTime end, + List uris, + boolean unique) { log.info(""" PLEASE WAITING Система получает статистику uris: {}""", uris); return switch (RemoteCallExecutor.execute(() -> statsClient.getStats(start, end, uris, unique))) { - case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Success(var response) -> Optional.of(response); case RemoteCallResult.Failure(var exeptionNullable) -> throw exeptionNullable; case RemoteCallResult.Degraded(var cause) -> { log.warn(""" Деградация Не удалось получить статистику - uris: {}""", uris); - throw new StatsServerUnavailableException("", cause); + uris: {}""", uris, cause); + + yield Optional.empty(); } }; } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java index 5d844ee..9ae7429 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/compilation/CompilationServiceImpl.java @@ -24,6 +24,7 @@ import ru.practicum.ewm.events.mapper.CompilationMapper; import ru.practicum.ewm.events.repository.CompilationRepository; import ru.practicum.ewm.events.repository.EventRepository; +import ru.practicum.aggregation.model.data.StatsRequestData; import ru.practicum.stat.dto.ViewStatsDto; import java.util.*; @@ -214,15 +215,21 @@ private Map getViews(@NonNull Collection compilations) // Все URIs List uris = allEvents.stream().map(e -> "/events/" + e.getId()).toList(); - List stats = statsFeignRepository.getStat(uris, false); + var statsOptional = statsFeignRepository.getStatList( + StatsRequestData.builder() + .uris(uris) + .unique(false) + .build() + ); // Map - return stats.stream().collect(Collectors.toMap(statsDto -> + return statsOptional.map(viewStatsDtos -> viewStatsDtos.stream() + .collect(Collectors.toMap(statsDto -> Long.parseLong(statsDto.getUri().replace("/events/", "")), ViewStatsDto::getHits, (a, b) -> a - ) - ); + )) + ).orElse(Collections.emptyMap()); } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java index 07add32..fff81c2 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java @@ -41,6 +41,7 @@ import ru.practicum.ewm.events.repository.EventRepository; import ru.practicum.ewm.events.specification.EventSpecifications; import ru.practicum.ewm.events.specification.SpecBuilder; +import ru.practicum.aggregation.model.data.StatsRequestData; import ru.practicum.stat.dto.ViewStatsDto; import java.time.Instant; @@ -49,6 +50,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; @Slf4j @@ -123,20 +125,30 @@ public List getFreeEvents(@NonNull FreeGetData dto, HttpServletRe Map eventIdToRequestCount = getRequestCountMap(confirmedRequestsCounts); - List uris = getUris(events); - List stats = statsFeignRepository - .getStat(uris, dto.rangeStart(), dto.rangeEnd(), false); + var statsData = StatsRequestData.builder() + .uris(getUris(events)) + .start(dto.rangeStart()) + .end(dto.rangeEnd()) + .unique(false) + .build(); - Map initiators = getUserShortDtoMap(events); + var statsOptional = statsFeignRepository.getStatList(statsData); + + Map userIdToInitiator = getUserShortDtoMap(events); return events.stream() .map(event -> { var userId = event.getInitiatorId(); - var count = eventIdToRequestCount.getOrDefault(userId, 0L); + var confirmets = eventIdToRequestCount.getOrDefault(userId, 0L); + AtomicLong views = new AtomicLong(); + statsOptional.ifPresentOrElse(stats -> + views.set(getViewsFromStatsList(stats, event.getId())), + () -> views.set(1L) + ); var eventData = EventData.builder() - .initiator(initiators.get(userId)) - .confirmedRequests(count) - .views(stats.size()) + .initiator(userIdToInitiator.get(userId)) + .confirmedRequests(confirmets) + .views(views.get()) .build(); return EventMapper.toEventShortDto(event, eventData); }) @@ -152,12 +164,21 @@ public EventFullDto getFreeEventById(Long eventId, HttpServletRequest request) { var event = getEventById(eventId); var initiator = UserMapper.toUserShortDto(getUserById(event.getInitiatorId())); - var uri = List.of(request.getRequestURI()); - var views = statsFeignRepository.getStat(uri, true) - .stream() - .findFirst() - .map(ViewStatsDto::getHits) - .orElse(0L); + var uris = List.of(request.getRequestURI()); + var stats = statsFeignRepository.getStatList( + StatsRequestData.builder() + .uris(uris) + .unique(true) + .build() + ); + + long views = stats.map(viewStatsDtos -> viewStatsDtos.stream() + .findFirst() + .map(ViewStatsDto::getHits) + .orElse(0L) + ) + .orElse(1L); + var confirmedRequestsCount = getConfirmedRequestsCountByEventId(eventId); var eventData = EventData.builder() @@ -236,9 +257,14 @@ public List adminGetEvents(@NonNull AdminGetData dto) { Map eventIdToRequestCount = getRequestCountMap(confirmedRequestsCounts); - List uris = getUris(events); - List stats = statsFeignRepository - .getStat(uris, dto.rangeStart(), dto.rangeEnd(), false); + var statsData = StatsRequestData.builder() + .uris(getUris(events)) + .start(dto.rangeStart()) + .end(dto.rangeEnd()) + .unique(false) + .build(); + + var statsOptional = statsFeignRepository.getStatList(statsData); Map initiators = getUserShortDtoMap(events); @@ -246,10 +272,15 @@ public List adminGetEvents(@NonNull AdminGetData dto) { .map(event -> { var userId = event.getInitiatorId(); var count = getConfirmedRequestsCountFromMap(eventIdToRequestCount, event.getId()); + AtomicLong views = new AtomicLong(); + statsOptional.ifPresentOrElse(stats -> + views.set(getViewsFromStatsList(stats, event.getId())), + () -> views.set(1L) + ); var eventData = EventData.builder() .initiator(initiators.get(userId)) .confirmedRequests(count) - .views(getHits(stats, event.getId())) + .views(views.get()) .build(); return EventMapper.toEventFullDto(event, eventData); }) @@ -333,17 +364,27 @@ public List findByUserId(Long userId, Integer from, Integer size) Map eventIdToRequestCount = getRequestCountMap(confirmedRequestsCounts); List uris = getUris(events); - List stats = statsFeignRepository.getStat(uris, true); + var statsOptional = statsFeignRepository.getStatList( + StatsRequestData.builder() + .uris(uris) + .unique(true) + .build() + ); Map initiators = getUserShortDtoMap(events); return events.stream() .map(event -> { var count = getConfirmedRequestsCountFromMap(eventIdToRequestCount, event.getId()); + AtomicLong views = new AtomicLong(); + statsOptional.ifPresentOrElse(stats -> + views.set(getViewsFromStatsList(stats, event.getId())), + () -> views.set(1L) + ); var eventData = EventData.builder() .initiator(initiators.get(userId)) .confirmedRequests(count) - .views(getHits(stats, event.getId())) + .views(views.get()) .build(); return EventMapper.toEventShortDto(event, eventData); }) @@ -370,7 +411,7 @@ public EventFullDto findEventById(long eventId, long confirmets) { var eventData = EventData.builder() .initiator(UserMapper.toUserShortDto(getUserById(event.getInitiatorId()))) .confirmedRequests(confirmets) - .views(getHits(event.getId())) + .views(getViews(event.getId())) .build(); return EventMapper.toEventFullDto(event, eventData); @@ -473,7 +514,7 @@ private EventData getEventData(long eventId, long initiatorId) { return EventData.builder() .initiator(UserMapper.toUserShortDto(getUserById(initiatorId))) .confirmedRequests(getConfirmedRequestsCountByEventId(eventId)) - .views(getHits(eventId)) + .views(getViews(eventId)) .build(); } @@ -505,25 +546,25 @@ private long getConfirmedRequestsCountByEventId(long eventId) { return counts.getFirst().count(); } - private long getHits(long eventId) { - List stats = statsFeignRepository.getStat( - List.of(EVENTS_PATH + eventId), true); - if (stats.isEmpty()) { - return 0; + private long getViews(long eventId) { + var uris = List.of(EVENTS_PATH + eventId); + var statsOptional = statsFeignRepository.getStatList( + StatsRequestData.builder() + .uris(uris) + .unique(true) + .build() + ); + + if (statsOptional.isEmpty()) { + return 1; } - return stats.getFirst().getHits(); - } - private long getHits(@NonNull List stats, long eventId) { + List stats = statsOptional.get(); + if (stats.isEmpty()) { return 0; } - for (ViewStatsDto stat : stats) { - if (stat.getUri().equals(EVENTS_PATH + eventId)) { - return stat.getHits(); - } - } - return 0; + return stats.getFirst().getHits(); } private long getConfirmedRequestsCountFromMap(@NonNull @@ -538,4 +579,12 @@ private long getConfirmedRequestsCountFromMap(@NonNull return 0; } + private long getViewsFromStatsList(@NonNull List viewStatsMap, long eventId) { + return viewStatsMap.stream() + .filter(statsDto -> statsDto.getUri().equals(EVENTS_PATH + eventId)) + .map(ViewStatsDto::getHits) + .findAny() + .orElse(0L); + } + } diff --git a/stats/stat-dto/src/main/java/ru/practicum/stat/dto/StatsRequest.java b/stats/stat-dto/src/main/java/ru/practicum/stat/dto/StatsRequest.java deleted file mode 100644 index f2fca59..0000000 --- a/stats/stat-dto/src/main/java/ru/practicum/stat/dto/StatsRequest.java +++ /dev/null @@ -1,27 +0,0 @@ -package ru.practicum.stat.dto; - -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class StatsRequest { - - @NotNull - private String start; - - @NotNull - private String end; - - private List uris; - - @Builder.Default - private Boolean unique = false; -} \ No newline at end of file From 4fd03fff6692a2fb0e1224e59b7e766931be406b Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Wed, 2 Sep 2026 03:39:13 +0300 Subject: [PATCH 13/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Рефакторинг rating-service Добавлен новый feign-endpoint Добавлен RatingMapper Теперь тесты пройдут удалении лайка при отсутствии event-service --- .../aggregation/client/event/EventClient.java | 3 + .../client/event/EventFallbackFactory.java | 5 ++ .../repository/EventFeignRepository.java | 2 + .../repository/EventFeignRepositoryImpl.java | 32 ++++++++-- .../system/SystemEventController.java | 9 ++- .../events/repository/EventRepository.java | 7 +++ .../events/service/event/EventService.java | 2 + .../service/event/EventServiceImpl.java | 24 ++++++-- .../ewm/ratings/mapper/RatingMapper.java | 19 ++++++ .../ratings/service/RatingServiceImpl.java | 60 +++++++------------ 10 files changed, 111 insertions(+), 52 deletions(-) create mode 100644 core/rating-service/src/main/java/ru/practicum/ewm/ratings/mapper/RatingMapper.java diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java index 122f44f..cbf7c0a 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventClient.java @@ -19,4 +19,7 @@ public interface EventClient { @GetMapping("/system/events/{eventId}") EventFullDto systemFindEventById(@PathVariable Long eventId, @RequestParam Long confirmets); + @GetMapping("/system/events/{eventId}/initiator") + Long systemGetInitiatorIfPublished(@PathVariable Long eventId); + } \ No newline at end of file diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java index 1cf6a5e..2f64581 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/client/event/EventFallbackFactory.java @@ -23,6 +23,11 @@ public void systemUpdateRating(RatingUpdateRequest request) { public EventFullDto systemFindEventById(Long eventId, Long confirmets) { throw RemoteExceptionMapper.mapEventException(cause); } + + @Override + public Long systemGetInitiatorIfPublished(Long eventId) { + throw RemoteExceptionMapper.mapEventException(cause); + } }; } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java index d09eb9c..c30ce08 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepository.java @@ -8,4 +8,6 @@ public interface EventFeignRepository { void updateRating(RatingUpdateRequest request); EventFullDto systemFindEventById(long eventId, long confirmets); + + Long getInitiatorIfPublished(Long eventId); } diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java index 67ec9bf..3505879 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/EventFeignRepositoryImpl.java @@ -38,12 +38,9 @@ public void updateRating(@NonNull RatingUpdateRequest request) { Не удалось обновить рейтинг события с id={}""", eventId); throw exeption; } - case RemoteCallResult.Degraded(var cause) -> { - log.warn(""" - Деградация - Не удалось обновить рейтинг события с id={}""", eventId); - throw new UserServiceUnavailableException(cause); - } + case RemoteCallResult.Degraded(var cause) -> log.warn(""" + Деградация + Не удалось обновить рейтинг события с id={}""", eventId, cause); } } @@ -70,4 +67,27 @@ public EventFullDto systemFindEventById(long eventId, long confirmets) { }; } + @Override + public Long getInitiatorIfPublished(Long eventId) { + log.info(""" + PLEASE WAITING + Система ищет инициатора события с id={}""", eventId); + return switch (RemoteCallExecutor.execute(() -> + eventClient.systemGetInitiatorIfPublished(eventId))) { + case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Failure(var exeption) -> { + log.warn(""" + Бизнес-исключение + Не удалось получить инициатора события с id={}""", eventId); + throw exeption; + } + case RemoteCallResult.Degraded(var cause) -> { + log.warn(""" + Деградация + Не удалось получить инициатора события с id={}""", eventId); + throw new UserServiceUnavailableException(cause); + } + }; + } + } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java index c3d083c..35987b1 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/controller/system/SystemEventController.java @@ -20,8 +20,13 @@ public class SystemEventController { EventService eventService; @GetMapping("/{eventId}") - EventFullDto systemFindEventById(@PathVariable @Positive Long eventId, - @RequestParam @PositiveOrZero Long confirmets) { + public EventFullDto systemFindEventById(@PathVariable @Positive Long eventId, + @RequestParam @PositiveOrZero Long confirmets) { return eventService.findEventById(eventId, confirmets); } + + @GetMapping("/{eventId}/initiator") + public Long systemGetInitiatorIfPublished(@PathVariable @Positive Long eventId) { + return eventService.getInitiatorIfPublished(eventId); + } } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/repository/EventRepository.java b/core/event-service/src/main/java/ru/practicum/ewm/events/repository/EventRepository.java index 66e6bec..f4c4d98 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/repository/EventRepository.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/repository/EventRepository.java @@ -3,6 +3,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; import ru.practicum.aggregation.enums.EventState; import ru.practicum.ewm.events.entity.Event; @@ -12,6 +13,12 @@ public interface EventRepository extends JpaRepository, JpaSpecific List findByInitiatorId(Long userId, PageRequest pageRequest); + @Query(""" + select e.initiatorId from Event e + where e.id = :eventId + """) + Long getInitiatorIdByEventId(Long eventId); + boolean existsByCategoryId(Long categoryId); boolean existsByIdAndState(Long eventId, EventState state); diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java index c47442a..eac622a 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventService.java @@ -1,6 +1,7 @@ package ru.practicum.ewm.events.service.event; import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.constraints.Positive; import org.springframework.transaction.annotation.Transactional; import ru.practicum.aggregation.dto.event.come.create.NewEventDto; import ru.practicum.aggregation.dto.event.come.update.UpdateEventAdminRequest; @@ -48,4 +49,5 @@ public interface EventService { @Transactional EventFullDto patchEvent(Long userId, Long eventId, UpdateEventUserRequest request); + long getInitiatorIfPublished(@Positive Long eventId); } diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java index fff81c2..e062051 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java @@ -27,6 +27,7 @@ import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; import ru.practicum.aggregation.model.data.AdminGetData; import ru.practicum.aggregation.model.data.FreeGetData; +import ru.practicum.aggregation.model.data.StatsRequestData; import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.repository.RequestFeignRepositoryImpl; import ru.practicum.aggregation.repository.StatsFeignRepository; @@ -41,7 +42,6 @@ import ru.practicum.ewm.events.repository.EventRepository; import ru.practicum.ewm.events.specification.EventSpecifications; import ru.practicum.ewm.events.specification.SpecBuilder; -import ru.practicum.aggregation.model.data.StatsRequestData; import ru.practicum.stat.dto.ViewStatsDto; import java.time.Instant; @@ -156,11 +156,8 @@ public List getFreeEvents(@NonNull FreeGetData dto, HttpServletRe } @Override - public EventFullDto getFreeEventById(Long eventId, HttpServletRequest request) { - if (!eventRepository.existsByIdAndState(eventId, EventState.PUBLISHED)) { - throw new NotFoundException( - "Событие с id=%s не существует или не опубликовано.".formatted(eventId)); - } + public EventFullDto getFreeEventById(Long eventId, @NonNull HttpServletRequest request) { + checkEvent(eventId); var event = getEventById(eventId); var initiator = UserMapper.toUserShortDto(getUserById(event.getInitiatorId())); @@ -428,6 +425,21 @@ public EventFullDto patchEvent(Long userId, Long eventId, @NonNull UpdateEventUs return patchEvent(eventId, request, false); } + @Override + public long getInitiatorIfPublished(Long eventId) { + checkEvent(eventId); + long initiatorId = eventRepository.getInitiatorIdByEventId(eventId); + userFeignRepository.checkUser(initiatorId); + return initiatorId; + } + + private void checkEvent(Long eventId) { + if (!eventRepository.existsByIdAndState(eventId, EventState.PUBLISHED)) { + throw new NotFoundException( + "Событие с id=%s не существует или не опубликовано.".formatted(eventId)); + } + } + @SuppressWarnings("SameParameterValue") private EventFullDto patchEvent(Long eventId, @NonNull UpdateEventUserRequest request, diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/mapper/RatingMapper.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/mapper/RatingMapper.java new file mode 100644 index 0000000..fc293e9 --- /dev/null +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/mapper/RatingMapper.java @@ -0,0 +1,19 @@ +package ru.practicum.ewm.ratings.mapper; + +import lombok.NonNull; +import lombok.experimental.UtilityClass; +import ru.practicum.aggregation.dto.rating.output.RatingResponse; +import ru.practicum.ewm.ratings.entity.Rating; + +@UtilityClass +public class RatingMapper { + + public RatingResponse mapToResponse(@NonNull Rating rating) { + return RatingResponse.builder() + .id(rating.getId()) + .userId(rating.getUserId()) + .eventId(rating.getEventId()) + .reaction(rating.getReaction()) + .build(); + } +} diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java index 56bc5ab..7bd9d23 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/service/RatingServiceImpl.java @@ -7,20 +7,16 @@ import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import ru.practicum.aggregation.dto.rating.come.create.RatingCreateRequest; -import ru.practicum.aggregation.dto.rating.output.RatingResponse; import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest; -import ru.practicum.aggregation.enums.EventState; +import ru.practicum.aggregation.dto.rating.output.RatingResponse; import ru.practicum.aggregation.enums.Reaction; import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException; import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException; -import ru.practicum.aggregation.model.repository.EventRequestCount; import ru.practicum.aggregation.repository.EventFeignRepository; -import ru.practicum.aggregation.repository.RequestFeignRepository; -import ru.practicum.aggregation.repository.UserFeignRepository; import ru.practicum.ewm.ratings.entity.Rating; +import ru.practicum.ewm.ratings.mapper.RatingMapper; import ru.practicum.ewm.ratings.repository.RatingRepository; -import java.util.List; import java.util.Objects; @Service @@ -30,32 +26,22 @@ public class RatingServiceImpl implements RatingService { RatingRepository ratingRepository; - UserFeignRepository userFeignRepository; EventFeignRepository eventFeignRepository; - RequestFeignRepository requestFeignRepository; @Override public RatingResponse addOrUpdateReaction(long userId, long eventId, @NonNull RatingCreateRequest request) { - var user = userFeignRepository.getUserDtoById(userId); - var confirmedRequestsCount = requestFeignRepository.getConfirmedRequestsCount(List.of(eventId)); - long contirmets = confirmedRequestsCount.stream() - .map(EventRequestCount::count).findAny() - .orElse(0L); - var event = eventFeignRepository.systemFindEventById(eventId, contirmets); - var initiator = event.initiator(); + var initiatorId = eventFeignRepository.getInitiatorIfPublished(eventId); - if (Objects.equals(user.id(), initiator.id())) { + if (Objects.equals(userId, initiatorId)) { throw new ValidationException("Нельзя ставить реакции своим событиям"); } - if (!event.state().equals(EventState.PUBLISHED)) { - throw new NotFoundException("Нельзя поставить реакцию не опубликованному событию"); - } - - var rating = ratingRepository.findByUserIdAndEventId(userId, eventId).orElse(null); + var rating = ratingRepository + .findByUserIdAndEventId(userId, eventId) + .orElse(null); var requestReaction = request.reaction(); if (rating != null) { @@ -63,12 +49,13 @@ public RatingResponse addOrUpdateReaction(long userId, ratingRepository.delete(rating); updateEventRate(eventId); throw new ConflictException(""" - Реакция пользователя с id=%s событию с id=%s, удалена""".formatted(userId, eventId)); + Реакция пользователя с id=%s событию с id=%s, удалена""" + .formatted(userId, eventId)); } else { rating.setReaction(requestReaction); ratingRepository.save(rating); updateEventRate(eventId); - return mapToResponse(rating); + return RatingMapper.mapToResponse(rating); } } else { rating = Rating.builder() @@ -78,18 +65,23 @@ public RatingResponse addOrUpdateReaction(long userId, .build(); ratingRepository.save(rating); updateEventRate(eventId); - return mapToResponse(rating); + return RatingMapper.mapToResponse(rating); } } @Override public void removeReaction(long userId, long eventId) { - Rating rating = ratingRepository.findByUserIdAndEventId(userId, eventId).orElseThrow(() -> - new NotFoundException( - "Реакция пользователя с id=%s событию с id=%s, не найдена".formatted(userId, eventId) - ) - ); - ratingRepository.delete(rating); + ratingRepository.findByUserIdAndEventId(userId, eventId) + .ifPresentOrElse(ratingRepository::delete, + () -> { + updateEventRate(eventId); + throw new NotFoundException( + "Реакция пользователя с id=%s событию с id=%s, не найдена" + .formatted(userId, eventId) + ); + } + ); + updateEventRate(eventId); } @@ -106,12 +98,4 @@ private void updateEventRate(long eventId) { eventFeignRepository.updateRating(request); } - private RatingResponse mapToResponse(@NonNull Rating rating) { - return RatingResponse.builder() - .id(rating.getId()) - .userId(rating.getUserId()) - .eventId(rating.getEventId()) - .reaction(rating.getReaction()) - .build(); - } } From c37643556502d6bb118d6fe7125d0beea95e1955 Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Wed, 2 Sep 2026 04:33:56 +0300 Subject: [PATCH 14/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Доработка RequestFeignRepositoryImpl --- .../model/repository/EventRequestCount.java | 3 ++ .../RequestFeignRepositoryImpl.java | 32 +++++++++++++------ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java index 4e2cd41..3a9d103 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/model/repository/EventRequestCount.java @@ -1,10 +1,13 @@ package ru.practicum.aggregation.model.repository; +import lombok.Builder; + /** * @apiNote * {@link Long} eventId
* {@link Integer} count */ +@Builder public record EventRequestCount( Long eventId, Long count diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java index 4835ac2..8b558b9 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/repository/RequestFeignRepositoryImpl.java @@ -11,9 +11,9 @@ import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest; import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult; import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto; -import ru.practicum.aggregation.error.exception.unavailable.RequestServiceUnavailableException; import ru.practicum.aggregation.model.repository.EventRequestCount; +import java.util.Collections; import java.util.List; @Slf4j @@ -32,7 +32,7 @@ public List getConfirmedRequestsCount(List eventIds) { eventIds: {}""", eventIds); return switch (RemoteCallExecutor.execute(() -> requestClient.getConfirmedRequestsCount(eventIds))) { - case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Success(var eventRequestCounts) -> eventRequestCounts; case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение @@ -44,8 +44,15 @@ public List getConfirmedRequestsCount(List eventIds) { log.warn(""" Деградация Не удалось получить количество подтвержденных заявок на участие в событиях - eventIds: {}""", eventIds); - throw new RequestServiceUnavailableException(cause); + eventIds: {}""", eventIds, cause); + + yield eventIds.stream() + .map(id -> EventRequestCount.builder() + .eventId(id) + .count(0L) + .build() + ) + .toList(); } }; } @@ -57,7 +64,7 @@ public List findByUserIdAndEventId(long userId, long ev Система ищет запрос на участие пользователя с id={} в событии с id={}""", userId, eventId); return switch (RemoteCallExecutor.execute(() -> requestClient .findByUserIdAndEventId(userId, eventId))) { - case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Success(var participationRequestDtos) -> participationRequestDtos; case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение @@ -69,8 +76,9 @@ public List findByUserIdAndEventId(long userId, long ev log.warn(""" Деградация Пользователю с id={} - Не удалось получить запросы на участие в событии с id={}""", userId, eventId); - throw new RequestServiceUnavailableException(cause); + Не удалось получить запросы на участие в событии с id={}""", userId, eventId, cause); + + yield Collections.emptyList(); } }; } @@ -86,7 +94,7 @@ public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, {}""", eventId, request); return switch (RemoteCallExecutor.execute(() -> requestClient.updateStatusRequest(eventId, participantLimit, requestModeration, request))) { - case RemoteCallResult.Success(var response) -> response; + case RemoteCallResult.Success(var updateResult) -> updateResult; case RemoteCallResult.Failure(var exeption) -> { log.warn(""" Бизнес-исключение @@ -98,8 +106,12 @@ public EventRequestStatusUpdateResult updateStatusRequest(Long eventId, log.warn(""" Деградация Не удалось обновить статус запросов на участие в событии с id={} - {}""", eventId, request); - throw new RequestServiceUnavailableException(cause); + {}""", eventId, request, cause); + + yield EventRequestStatusUpdateResult.builder() + .confirmedRequests(Collections.emptyList()) + .rejectedRequests(Collections.emptyList()) + .build(); } }; } From 440475d6899e90d210b57919d1ec7736b895ffdc Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Wed, 2 Sep 2026 05:25:08 +0300 Subject: [PATCH 15/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлено логирование во все core-сервисы --- .../dto/user/come/NewUserRequest.java | 1 + .../service/event/EventServiceImpl.java | 4 +- .../controller/UserRatingController.java | 23 ++++++-- .../controller/UserRequestController.java | 44 +++++++-------- .../users/controller/AdminUserController.java | 54 ++++++++++--------- 5 files changed, 76 insertions(+), 50 deletions(-) diff --git a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/come/NewUserRequest.java b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/come/NewUserRequest.java index e70262c..8e959e2 100644 --- a/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/come/NewUserRequest.java +++ b/core/aggregation-api/src/main/java/ru/practicum/aggregation/dto/user/come/NewUserRequest.java @@ -6,6 +6,7 @@ import lombok.Builder; /** + * Данные добавляемого пользователя * @apiNote * String name
* String email diff --git a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java index e062051..0f1a1e5 100644 --- a/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java +++ b/core/event-service/src/main/java/ru/practicum/ewm/events/service/event/EventServiceImpl.java @@ -162,14 +162,14 @@ public EventFullDto getFreeEventById(Long eventId, @NonNull HttpServletRequest r var event = getEventById(eventId); var initiator = UserMapper.toUserShortDto(getUserById(event.getInitiatorId())); var uris = List.of(request.getRequestURI()); - var stats = statsFeignRepository.getStatList( + var statsOptional = statsFeignRepository.getStatList( StatsRequestData.builder() .uris(uris) .unique(true) .build() ); - long views = stats.map(viewStatsDtos -> viewStatsDtos.stream() + long views = statsOptional.map(viewStatsDtos -> viewStatsDtos.stream() .findFirst() .map(ViewStatsDto::getHits) .orElse(0L) diff --git a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/UserRatingController.java b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/UserRatingController.java index f03b759..95a662e 100644 --- a/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/UserRatingController.java +++ b/core/rating-service/src/main/java/ru/practicum/ewm/ratings/controller/UserRatingController.java @@ -1,13 +1,17 @@ package ru.practicum.ewm.ratings.controller; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; +import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import ru.practicum.aggregation.dto.rating.come.create.RatingCreateRequest; import ru.practicum.aggregation.dto.rating.output.RatingResponse; import ru.practicum.ewm.ratings.service.RatingService; +@Slf4j @RestController @RequestMapping("/users/{userId}/events/{eventId}/likes") @RequiredArgsConstructor @@ -19,14 +23,27 @@ public class UserRatingController { @ResponseStatus(HttpStatus.CREATED) public RatingResponse addReaction(@PathVariable Long userId, @PathVariable Long eventId, - @Valid @RequestBody RatingCreateRequest request) { - return ratingService.addOrUpdateReaction(userId, eventId, request); + @Valid @RequestBody RatingCreateRequest createRequest, + @NonNull HttpServletRequest request) { + + log.info(""" + ENDPOINT + Добавление новой реакции + {} {}""", request.getMethod(), request.getRequestURI()); + + return ratingService.addOrUpdateReaction(userId, eventId, createRequest); } @DeleteMapping @ResponseStatus(HttpStatus.NO_CONTENT) public void removeReaction(@PathVariable Long userId, - @PathVariable Long eventId) { + @PathVariable Long eventId, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Удаление реакции + {} {}""", request.getMethod(), request.getRequestURI()); + ratingService.removeReaction(userId, eventId); } diff --git a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java index 10611d1..0e237cf 100644 --- a/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java +++ b/core/request-service/src/main/java/ru/practicum/ewm/requests/controller/UserRequestController.java @@ -1,6 +1,8 @@ package ru.practicum.ewm.requests.controller; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.Positive; +import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -19,21 +21,20 @@ public class UserRequestController { private final RequestService requestService; /** - * Получение информации о заявках текущего пользователя на участие в чужих событиях - *

* В случае, если по заданным фильтрам не найдено ни одной заявки, возвращает пустой список - * - * @param userId id текущего пользователя - * @return List<{@link ParticipationRequestDto}> */ @GetMapping - public List findByRequesterId(@PathVariable @Positive Long userId) { + public List findByRequesterId(@PathVariable @Positive Long userId, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Получение информации о заявках текущего пользователя на участие в чужих событиях + {} {}""", request.getMethod(), request.getRequestURI()); + return requestService.findByRequesterId(userId); } /** - * Добавление запроса от текущего пользователя на участие в событии - *

* Обратите внимание: *

* нельзя добавить повторный запрос (Ожидается код ошибки 409) @@ -46,28 +47,29 @@ public List findByRequesterId(@PathVariable @Positive L *

* если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в * состояние подтвержденного - * - * @param userId id текущего пользователя - * @param eventId id события - * @return {@link ParticipationRequestDto} */ @PostMapping @ResponseStatus(HttpStatus.CREATED) public ParticipationRequestDto addParticipationRequest(@PathVariable @Positive Long userId, - @RequestParam @Positive Long eventId) { + @RequestParam @Positive Long eventId, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Добавление запроса от текущего пользователя на участие в событии + {} {}""", request.getMethod(), request.getRequestURI()); + return requestService.addParticipationRequest(userId, eventId); } - /** - * Отмена своего запроса на участие в событии - * - * @param userId id текущего пользователя - * @param requestId id запроса на участие - * @return {@link ParticipationRequestDto} - */ @PatchMapping("/{requestId}/cancel") public ParticipationRequestDto cancelParticipationRequest(@PathVariable @Positive Long userId, - @PathVariable @Positive Long requestId) { + @PathVariable @Positive Long requestId, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Отмена своего запроса на участие в событии + {} {}""", request.getMethod(), request.getRequestURI()); + return requestService.cancelParticipationRequest(userId, requestId); } diff --git a/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java index c872b55..5cd10ca 100644 --- a/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java +++ b/core/user-service/src/main/java/ru/practicum/ewm/users/controller/AdminUserController.java @@ -1,7 +1,10 @@ package ru.practicum.ewm.users.controller; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; +import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import ru.practicum.aggregation.dto.user.come.NewUserRequest; @@ -10,6 +13,7 @@ import java.util.List; +@Slf4j @RestController @RequestMapping(path = "/admin/users") @RequiredArgsConstructor @@ -17,32 +21,29 @@ public class AdminUserController { private final UserService userService; - /** - * Добавление нового пользователя - * - * @param newUserRequest Данные добавляемого пользователя - * @return {@link UserDto} - */ @PostMapping @ResponseStatus(HttpStatus.CREATED) - public UserDto createUser(@RequestBody @Valid NewUserRequest newUserRequest) { + public UserDto createUser(@RequestBody @Valid NewUserRequest newUserRequest, + @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Добавление нового пользователя + {} {}""", request.getMethod(), request.getRequestURI()); + return userService.createUser(newUserRequest); } - /** - * Получение пользователя по ID - * - * @param userId ID пользователя - * @return {@link UserDto} - */ @GetMapping("/{userId}") - public UserDto getUser(@PathVariable Long userId) { + public UserDto getUser(@PathVariable Long userId, @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Получение пользователя по ID + {} {}""", request.getMethod(), request.getRequestURI()); + return userService.getUser(userId); } /** - * Получение информации о пользователях - *

* Возвращает информацию обо всех пользователях (учитываются параметры ограничения выборки), * либо о конкретных (учитываются указанные идентификаторы) * В случае, если по заданным фильтрам не найдено ни одного пользователя, возвращает пустой список @@ -52,25 +53,30 @@ public UserDto getUser(@PathVariable Long userId) { * Default value : 0 * @param size количество элементов в наборе * Default value : 10 - * @return List<{@link UserDto}> */ @GetMapping public List findUsers( @RequestParam(required = false) List ids, @RequestParam(defaultValue = "0") int from, - @RequestParam(defaultValue = "10") int size + @RequestParam(defaultValue = "10") int size, + @NonNull HttpServletRequest request ) { + log.info(""" + ENDPOINT + Получение информации о пользователях + {} {}""", request.getMethod(), request.getRequestURI()); + return userService.findUsers(ids, from, size); } - /** - * Удаление пользователя - * - * @param userId id пользователя - */ @DeleteMapping("/{userId}") @ResponseStatus(HttpStatus.NO_CONTENT) - public void deleteUser(@PathVariable Long userId) { + public void deleteUser(@PathVariable Long userId, @NonNull HttpServletRequest request) { + log.info(""" + ENDPOINT + Удаление пользователя + {} {}""", request.getMethod(), request.getRequestURI()); + userService.deleteUser(userId); } From ac7a496ab0fe34463fd57bafdf27e9fd28720ddb Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Wed, 2 Sep 2026 06:59:35 +0300 Subject: [PATCH 16/16] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202.=20=D0=A0?= =?UTF-8?q?=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=20=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Обновление README --- .img/ewm-main-event-service.png | Bin 0 -> 98100 bytes .img/ewm-main-rating-service.png | Bin 0 -> 19539 bytes .img/ewm-main-request-service.png | Bin 0 -> 25897 bytes .img/ewm-main-user-service.png | Bin 0 -> 19251 bytes .img/ewm-stats-stats-server.png | Bin 0 -> 25775 bytes .img/external-api-2.png | Bin 0 -> 88111 bytes .img/external-api.png | Bin 0 -> 139674 bytes .img/internal-api.png | Bin 0 -> 79652 bytes .img/microservices.png | Bin 0 -> 61731 bytes image.png | Bin 103532 -> 0 bytes readme.md | 38 +++++++++++++++++++++++------- 11 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 .img/ewm-main-event-service.png create mode 100644 .img/ewm-main-rating-service.png create mode 100644 .img/ewm-main-request-service.png create mode 100644 .img/ewm-main-user-service.png create mode 100644 .img/ewm-stats-stats-server.png create mode 100644 .img/external-api-2.png create mode 100644 .img/external-api.png create mode 100644 .img/internal-api.png create mode 100644 .img/microservices.png delete mode 100644 image.png diff --git a/.img/ewm-main-event-service.png b/.img/ewm-main-event-service.png new file mode 100644 index 0000000000000000000000000000000000000000..a600c6f158f299b2d513fc40cfeca5dbbf49f58c GIT binary patch literal 98100 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�E890eb5h6>Dn3MF8Ef@sslfQ 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 literal 0 HcmV?d00001 diff --git a/.img/ewm-main-rating-service.png b/.img/ewm-main-rating-service.png new file mode 100644 index 0000000000000000000000000000000000000000..f889525165df6d1b670bf2c8d77907bdae1ba0f2 GIT binary patch literal 19539 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 Igmk_CAH&-1P5=M^ literal 0 HcmV?d00001 diff --git a/.img/ewm-main-request-service.png b/.img/ewm-main-request-service.png new file mode 100644 index 0000000000000000000000000000000000000000..d35f401c1bf77ad1375baffe5557d59ed69481f6 GIT binary patch literal 25897 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

DnPb_4X3^oRgbj@6gr~ z?p4V7g&HMAqtXZNUpd=atvh~=aGCCVw1aN`AB(^yyY8qz?k;b;yQ{Q9(DAvYEDReTs?0v3~>V#(;_d literal 0 HcmV?d00001 diff --git a/.img/ewm-main-user-service.png b/.img/ewm-main-user-service.png new file mode 100644 index 0000000000000000000000000000000000000000..2c4e1cd9aadfd7bb13ad20e276f4548746d6e220 GIT binary patch literal 19251 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 literal 0 HcmV?d00001 diff --git a/.img/external-api-2.png b/.img/external-api-2.png new file mode 100644 index 0000000000000000000000000000000000000000..1c13636276bcdd8fbd855b52dbc205b171541732 GIT binary patch literal 88111 zcmb5VbxfSy7cGptySr;~cZZ_I26uONio1Jpr)Z(LTXA-bMM(x3fdByv3=COLR#FWN4Ez-IB*4LdW>VBs=0P9uPO<=3Fff$C ze-C&XBMKpC63tCY+s)3=(%RJ6P8*Ev3lA3yF9-Z;0yt<5<^Qt=0OR0fXW=rs{hbe5 zLHf7iYU*Nc?f_;kslv>Lt)No`21W)ZCn=`xWpMu2(+z8Jy%%^jaapD{hl$~H)g^J< zMJKC)j*TXX4o*m+C|Vl%oq{Y9%NkoEa2+f2@iLgYGkn$H(y z!`IT|^4u)1&D;Wt^(vViAxykbh0Z{}$U9gZ{5x4@IUN>4*lwVxecm+%K+|=&EQ|HW zoYKfDzf0|4a{VnzgSsE()>xNd@z$A4@07-Qr^l8cA;8IUdsqS(xBBP&J|k%Y@lC-D zPypCUxMp>9%riHGJ6djIl$*=8?37gJUa;Q^QBpkH*|CdY;m8{w_GmDF#BbQw;&2)l zhkuDD2+~@i!LdV>#HhCBaYJfqm}YZ42%tI$mMm21-2b3uwyF9-(yHtja~7Ju(#R50 z7R!O7@z)to8?vrc=$FH;ZFgkUP%RX*PE%P!`ET#^BrBP?vJcLxoyizs=+{Ln`4~-P z;KOzAUZUZ~VFQ>b`xTN`~ z`*QjPx0Wx4`)xi8&F>uC>W&N1G#Y*%tJ$PEZqJL`ece=)P@pp_%Rnki zaiP*<(88}+qwz9Gvwe52m?fnr&r`Wc_S%x+FlUEP?%H9S+YHQe>-AT6qJEFxj3ePJ ze+9nHj^{<{(ylh&Lg6Pn3mItW%y+9I6ERDUEm%*lYc|0NHpl!fbmry!%sj*G6Nj1G1L|ntT5$(Y!f;y`mCPa?yVF z4~~}e3#~2f%{(abE}csrl`Xc`+w*leh91?)f?^`?HwEPVcQHrTn#j^#hj*%4$_1&9mp3Xuk2if^4qg5OEwfwfw{_MQNKF#SvzfIqQ;+Q^4y%Z zi+M|NEkIx>v>7DIrTNcq9SoWbP7gn2iO-VF3p++Drry{BHhTtPz^i#ye{R`y)Kg0ivJF!jsmiYn3Y0gD%-6f`apm2rr{txiI9nJ<_jFRH)1E5vK#LNCP4osckpNc5% zrj}&)6+xqjDtJC1D-CU+HZSI6#)WRRpEIQ4hwdFwJ6Y%fn@=?>ccUBn`{@8RQMkthe&=T(OvwXTCl zO-_??S@7qZZ9<=)+H59w5vl2yUphZ=C2gS1W}_{xrnhg!Oy#0mCvQwuy-V}`*U^Z; zoGMom&qd1Eo&H+S^XEGdl1z3v2MYUlaPchp|7odWO=Ju)Q;jCYnKXThB+tSBEIw($ zg~Habr^LH`uZ!r6IZ!Aw&}V`I&@{rIoz62Efir$R;6E{%}bP> z4rc^Z2-|lAH5^zRKWU?2uF16~zw0!x!vq{vv1iRXhpxvl=kMN8k26}HPd;c z64eTt-gr-M-hb{`PF|+DMlA@hG!rQ?zb7LW@^N0Sbg4XB{%wZ6+mQiu?x+_wmj4Zx zIaz9h&Jr8WY#75U0hhGL>#^qxw{2TfU*7+GXke;8OlXo%)TF~={hGvV(`7Jys7R<% zwTMLejE)%iL!s*h*?OsndTTm!>~BZkVT@pUS!&A3mYNL;{<=eoHz!t%yaR87O{j(< zs>(rrGqN1tP|!f2Y$67r=LW7R;$i>jxs=cOP|#CCyw#oK`-(aw*b=oKoh38cxlRnY zqc^UO!2uOHe@02;?~!+?JsAAP)so}F3OY){qG#WG3$@s)fJFJ53%76XbJMkCRHOOQ zH9Gb8hXzm)HjVb`$*;cM6oUHVFmFG&hT`?)mwj`2&T#Z_S8PSE8T@uq@ZA&6A(pus zxZ`5=z)^ios~WX^g!tIC*KH=R%d-BZ*qbWxr%scha?xyYn&o0Ehiz0A?n;@Hnq-rCMNMo%5L&j*J-o^_( z8fgL5PWzL#feLtEV!gj__SpM3n5Ula|O~ z6yEq6AEyYe5JcPVl9wEDKE$ z3LcAku@|_Wsl59+XmO}a2WhHs1;CqAYfYj5R_f=yKqOG&b)b0aVwv;pA=CD%0j3f3*;p(pW{VMMz&!^38j-^pCe`b{DI;M+=wvQqq>gVkX{y(INRTBTAzl z*qz?KUy9!W?fB7#Q1#d6EAMnr!ViCB^tS0y2IM`^M+9b#%$c^pA;(Ex9>*objQnlw zUq3uKlvT1A9Kgk$PWt|~(JY2_S7L5~Nz#y_I{)!y8Yb`4vF=|=Djs1w4JL65`Ai{0 zPV_z|f#*;zc=+XOmIx!O>yE8&)(-la?X6A^q2}M}6-9JX8CB_&r4?Ji{{mB!R~zmb z*vZz~=W4b(+$uB>%%Z9r%}n}hU9Yz_pkgpqR&|Qv61G(&S?&dzhvwy_)8k0t5lE|62Fb8;aAgTdr(Z82g!F0j zt^~Y;dVXLhHYs!d?+M69QLO9C_4*Yu<})NSJ7g-;z>m<{h~pH%+oawp6Vde36|S29 z9w`y8FJhCecl*6B>gQ?g248!${bSsPwOm6IIw)5f3Ijz+aX=gdf7Q+GSJAbIa_uir$eb@ z=Q>`if(<}{L3z7l)1r*UoC>|WJ?C|fA+PiAFF%3dCPJ0I(2SbJq}iS%?$&=rYFWgt zx8E7+D2l!lY%#uSzi)Kp&uUXRyd9W$h8i;4Rxji-jPoXBd&4{X5WY6+=>lP@H^k`p z%ugKfk|(C?UmVk7o+4?frMR;&xI*0eiCCVQ)5E1-yq@6NQSp_hJ>&L@CKsi#}W5se6w&d0b2TXJX3&q zKD-YNlV^B+h9tQWzroinyJT_?wYOGI-E zc06R-)_I#xki%`VpKlgy$36144@q?j8qN0~{iQs__R(R8W`?hd)#`5oc^d#I7DUwp zIZqj$^^Ged_`5l_7864|nfA{l_QqpzwHX*1GQzyKeH$vHy;DUwE!dI`&dUht0_MX5 zban`(OW{@lKuR8()K|4KZ*)r4kd79f2ONgvT?*MLIzj8RK(QI#ZQVIy)eJF7FV2@h zJYFbO33wh{cFesI`du7eLKz;t-18ZWA@uCT=Mj3h;8KnNVd^htRG8?GPchZQ-W$!> z2i^`L#Z^4D@!V!KG8oyZ?<4JuAlMgky$jj&4@zT2(>|<>n*E2XKRom3RMuAoiZn%5 zA=yC48ni=DL>a4sm?&q)Dpj-wv>)`{2gx9ve_uuT^BIm&f&FirR}}q!bI}=@|Bq8$ zD=n5?WH>lDt!-_y&HAu53l&l2<+SO_PkDRgh4Uwo2?=|be>=|CJF>5@of;aVy;mdt zXZiGYl)>1}#kc7F60e5ezbAeh@Hg2k8((jed-=Yi;o*&3thdO?%Zr!KXW_mNFW6u! z#Ve&U=>H(#vPsO#n_BAO`5pLaVD?dmonWoi>US!0C7{qbExyw)3ZlbfZ{Yzp@>kh|gWs+rA9sH=EBawo{J_ftjPY z0K2t2Tv(Lzz#_6|!Gps?m%|C`fER0$Cfnt`5Oh)s!9L$&smCe5x08<4X0H$C58RrH z_9w36flBII{MT8y(-$2ax_6gEvn1tmR+G6AgF^r+I*~k^>&aDYU{@{hGo8!Ja=~&! z{|OP&X6@TX-QVs{r_J-tr!@FE0!Bi+YNm-4Y|xpG1X8FhSL#Q6yu$)tug2lv;n}9S zXRSEBT73&NKYb-`S65f74Z4y+ew;0opuIg8H-oC|T7o{S7|-c){4yO-ilu>z&)AMa z5K4Z`Dve4y)?CGU^wd>Vj(W+0ayW|m4X}N(7gJT-1pGYl#pm#&Bdp!OISj3D#dX$} zYxq%FDg@umEI7uieC6#RnzI}Ww zo57+_XV?wFkcQA@ploDB*5Pr%+0+@)`N~rICZEL&h-=Ap?w5D{pUXU=ZB#Kg|Q z@%2+#UfyClPY5aCNS{TISH{G|Bxvp#Dvt55ZuGEdSZmtoBK*2osd2K=#z-j>Z}RcxZPla$ zMnOpl=n2p-JmBEq0MBZ3Jp$X_IT@ zh)PP3h6eEE!vd4L+u56raQ>XZXwHq}OiSUse?A|-)LVNJig3VZWs+>77G8YAUn!?7 z-8h$x>zo`C(Q>^vV1oH3A=^7Po8}<|JKJs`RWhN?LbCd&7E?=ZoP7y^Q+@K~2GNF! z)Z+f+>n5>q<_&dn=IxH8!wJ-a=EH*3W}L9Tj!Hj5zhjQYVt#hy*Qp{SS@t?aup*c^ zc^WR};ncf^MPPXS$bh86*s`Oo3X^_94df~=NAs39+NeRfkl zLT-ZqiMTJ4aa^URO_nN;OI|AyE4fv6>u)@Ls`RqonY^waT9cmsTpdJUm(%gm?W)#O zlOov-U#8vic~0}P)YdzfD4iMHIjqAW(x>U-Cj9n3GRBy1Q2cwrC4gLJ3mw!p-aK1) zg{HGy^*xh#yjeucX;%kM2i~b-iZ&mqAUXn}Njssg{Hw&=rp4w2SIHOzjtBMi)n>kj zqKygbq08RB(vqWqxZh!wPzxxKSG#WXw}gD}X*f9tu{b9+YBZ7KhJxK~mobQlLYc-l zTAf3BJ~xANbD0C6b!K5gf*Kne#YsWMvX3n5<8#}0j$=;IT59ro>9jFNATVzzF8haN z*3;8MZ?c_(l+-~fuUdx~=9up)EAw(NV zT&AEe_kj-tl%8~*NyWbrk zN|_s4Nzp?zx#PFGUk~)k{1$NOPiZV!HV3-`90Lq)7@PbM9rp&@>8HjuhKojum~)3e z2hZj5Rr<&?zMqMu+I?_u`ST&Ac^juttmPMBb_u9c-;7yPP@8r$t;~A9B`{@jB6zZe z6?D|6+An1Q5OHSfqUsHTiIj7eLJqrR)Yhm!3C#fo7Ue;l4%+Dp3s_)OePKWjT;Tmo z-tq1g22NEe;3`48{mIhi_0aREBdt|-D-#TxY zPk%JoV7SyhIiDb4kEJ9hi+{EIT6hP-9W=^$>U0<0h;Xe+pmZ^r0=}2&aKewAgrSUq z{hG>*;STNN<|umu%<_A9CwHMr{M7y}lD(UC2bGc1>Q*^2gHC(?}x&m)7?7tg)BpXEyH}ta;0~r|GZW(-dsv;Fd=i zhojO(9LQ&Bl;&NmcG)#M(E=O|$ z9sFfde7~>`zL_!&uEEYPSM!a?8W(DSmb)))-;ei)eTn6r&DZ7QVQGJy=HvT^<$Hu} zFIQk6a6PZv%Es)M_psoHaEdx`;zCf?dp<}q6&Zx>zmuRSv8-s@fXTwG{AsjyO(m;Q zc^fx)#&P%YAxPK!v-MCse^y^Vnq3+L3^)SiC9-IWyu7^gza0^wP^O6F{50V;fsSlx zX(g6c@Hj@Mwz}tCM+`mE3wI-C?@PMM{_F7ts3v>)u$kO%`Zz2nN^X*L;2BEw_OHSw z9I+v_m*)P?Sj<+!KIlW)Q8%mUPSU^VPXa|B_ zNBnNaQW$GElmM#aI%lW8q{A>?!j};4Y^Er2VtAcg5><8V`Ibc1yO)5e!Oo$M{OKYY zai+%*N0*yz6qKB{xDR*-33WQG>B}_Kg+9*^XLollW$viR$O7Ym@RgO7#I!UiX=#3i zbf%%v(I{nMdXq_}?qN| zI#u(z+iI4}myX>Lvt@liI&#>~nJGn3fuk-=5UsB|_OJd1W&E-MKW zzvBBooYOZz6QMcfO2-k*R;ZdPN~Mbk_{f?Fpp&&tGM~}L3SenV%jY5Zt<8b}b7{yYeT=;q>mqkB z7HgO>W7|u%M4T<@=f>35Ah{AG^0a5L^K-IDKFn++#x9{vS_QEUDraN-KE)T@iD5ap z6tUab=}I~$uL%*VFtXjM&G&@U)A7W5)q~+QWf#8`+zL`n2?S8`z8qLumZZ>&`Sp^g+w*7ikdakz z6)X$6J1Pp_=F%9En>pER;V4~lJn*O4@&vD%8t$cBGCuja0+lVkfq1(KMiFUZJTJst zC7BwXCXmnen6-32Ke8<*f3TSh$_hUjK~YM_mPo~<+r7U!Vqn}Te=#sHh{a=Tm?;+z zLx5g-*cTKObnjiFq9I98S!ek$ELl&59Of8ZOhx2-{Am__EOI~a zc>Cf=BBXGD+8Z4Xbvay0QOOx8`cOle6Qw?Xd#l=t9U0Xy6qB^YvY+j#Yiu3B!XgSo z^aFLT{F%dQJ3TP7WJoHJEF`QH8zP|sk?d@KbPQVsJGPfql{f~|<%6!m?|7y)nqDy- z`n1KH*d6_EXK(=wA8Z%Pr+B^F0Dr6b(64)?9hanI}8C8EHD{xj)tG zlx4XKIKGb!k-T>1>!%lreNI1{_eplz-}KP#OQW4Md{2v;lrYx+hs^8zVsz5Y<4J@h zt2FK5;9BFAw4Mg(#@V%M4akMb^W@VQj4dsdK^epI5(*t1-GRaL{#2u1d;o#TIyF6A z^Z4`RpA+XEf6y1lff~e+T0Q{3{Q_iPtmoYUj{tbq3iwNZ?QeT?s12ju(Ql%^ zALqrW;i*%dJzcJrRFg!Lv)BrJsoPrB@+a8CttlCr>IxmQRPC>$(O=`CdATFlMGhM` z6!H5WDQ0hN2*YhJsNm4|l|CkpgJ#y~l8R>PIZFREJ~@X{tyvS1pZjiarop#lR9VYA zNH6|MM4~52@yX%_cLTnopS0(SnuQAKvG3~UPsSdfpD9A&W5{@SzY zH$4lkmZ-vYa%w2twpU=)APx#$p>s*OVIA4ekqpRxVv!r@Dbl({QFmlX#s~;|=D?jj ztCHIC=?)$nd~RO`uR1GkgMg!jVtPJ&t7Lx+g)$iM%z>lqXK zw;E<-Y_ng*N7g7%@(&IXdvaTkDLZ10NO9o04(9`N-^2YYoKQzz(ma%~e-))a3@6yC zsL6^(*|c`=qWWzU16K6WiOB5(6E3b+_bW)v0062Ddp7SxBXLK^#-fG&H+$#jQ}E}@ zK>*Lp01Cj=m$|j9_OWXx{G;cy5VJh#`&vJS*j&-Ph~Q=3^vXpAI<$)``z+4#8m7AT zx^^*!nM*^%3@#CzKSXdv|Jo1?Iwarf>hUcJ;zMiuh&H?31Alv)p2hw!MzMvj zEnc=rDrPp}-k3*|tgfz6je%IpVQNg+C|8y)Z5Cz58(BPX%&uteT^qv?TS`ew)3^Ex z$}DdJ>JJYnDU+C!s{*`9DY;<=pEpVu6H`+rpFv%z%)0THe|?P{EfdHPj@^CkVBR?t z;e3fyvFhvV`^Lv(_}x$EYIHs4=G3w{j$p-Oy!j%n_C&c@b(O9JBxS{ckAVs>LUWio z(D(erIyE>0l+)wvKDjtGnvZt0ngHo?f3N4^MX6rVuX;51y7vW!Tbsur&91+D@3u24 zL+bD;Cvit4QiLhnGGpxT_!E(ZyEn*m$0c5s6*!{t_j#0zdbD~JUB z{%KVJg9Zf-3kzFqZ})4)3hm~S&p$6VNJmB~9@sW=xHtPhO7-pAXVj>fRFZkt!h*Wm zx6hb5El|@3V%~74d$NB1BUZz9+f*U`<7Zjfs8v9WEy!GqZ~t#o^`HBY${QPht(U6# z7OR#Ahs&wr<54OW32se3Gy9ClgoJXRp1dGx4G%Wjes3`EHl_bZ_*Q+Y7vN}6xn`tJeVI?OCxZ$ z=)F<-kDfI!_{?gt1ONbR#{E2;)@wP6+1&MkTf!hJS4LVICi(74Fhn<#=9oqmj|kKv-925Die6A3MZ$&rk?I{b{l*#Zbw@&d_<*XeoMi zsZFa-Blx`a3}Qm5vR{x$mxz7+*$df54_9f@-d=AsJ5F;t@UCZG%RZ|>x~}CNY)BN6 zpp1MvkS7Bg(dOl`Wa~^n5=Br(XUf!&<2wJgWH?q_btcdxRtHUTlSB~8F+aIl8z|7% z*LREzz2*OQl|mJDd`ySKl$oGZZn@DqjrM2w%0Mw}`FP(jYU2s%wpuIRTi>ie^Kq6b zmn~h#d_yIQ)i5#HEU7L)Z`3ot+c@tYt*@R*Yb3_leNl;ULzx+{B0z3nW31S z@l$l%Be!DT^ftQFSYfcujBhN}?I7RIJ`3ir_38+ue>!m20Oln=_fJbP?9QHD$dmjz z;xBh#k=@Vau!lb}ycSr|!6c+|2H-X8jfK|phi@-u6c`v76TiVtW7a{*%0{DV=khYB zwA8%NPew*YPfw5Z#fp<)!RB7#1X=_dvO%fbbRgWlwY42MmH0^V?4yW}9p~F51J~7C z`lR)GpCGj)^{ZUs@xe?_ySEjH$%}LML=yf9EdWb&oGQ*w^US3`@IEZ8_5Q8&ux>zMEFAuSOzu859pfzSEr`>jn+VHypU-%93 zmMI1B+af6|Yl~vZ#{GPwEv04CC&mBu0S%qRpukPIZ!YCBPQmH6PGMh=qM!#Mx;kbI z(~~x-peEwE?!g5i`0G=Xz)U@Pvj@dKPWQH&w_E=@Ux@#aBewh}D<-k+x1QkWVnfma ztavW$U-ijwU9QDOv!59xo)dIfhaf<5ac~e96T?`gRl_|E(OVjdc-8et(%0t+=g7gX$QAtUtDy=$(#!k&DXfsenq5;b@FzDoXBF16S6O|5U zdqw(XrKTyQRnf?Q1%l~b$m=N^(sAE&PGR4oPbiY^-G7=x5@zBmxp7?MKu!HHtSGm z1C!TuMLiTbC{Y}{6AowYd(6jctzR4=TUHU-8l3YHv6P~;MurVA{x5fB&Nb01He~b+ znG(8Mzt0wk8=-^iy!FA*)FX>xxoHFSUd*fZns&>ewY_G}YFez;D`=?g76&)GCWbC( zCltVqz8r&b9WLvzL!|k5^fA0391XZ9E(h6sE8_h7id-UuhkD^ugrMjrqd$l~l^9c|BS7HskPlBT?bqqjUzrH4g@JxgXh{S5+OrMI=V_4&^4u=Kl)0zIt-SO*K?tJyTE2J?Dsqm8%)t^W8=(5ba?*$&ET4!pZdi)Z} zb`x0DeoscHdN9p3N-L!yZ%yVrY+k^_%q+i^s6>?F@rQA1=<{|sVg_FEf#MCToc7TQTMTI#XLzMq5lN8nxI=TY1KA&{G^h>;roRY0<{owA zm$SKRQ-)89*xER+^6&q;E>7Tc`4q$L21}p(xoX}gS_vX#HL5U$#sI$^4sqYS@5+E3o>xV3SJRny-8_fylrRz~ zk=!dQ+K^dgZVDh(O#rdF$89Th=_#(T(M93#44o7mEk6v&ZoNJ0BmYx&i^TDE{IGr> z4kF0XgZw^UbnnKzm4q1X`>|ez$Gsrj`aLl^K0AB!T^46bGT`XR?ffyc44+lg^IWhu zBPIhl)yqQk;lkJ6Sw*ejtH2TnWMQnOHBJnwS;yn?AS#O&Mb@!VdufO8N1 z(rmwGrqk?_MZ+8EQ#SEqEX#L@q~f<$@X2bsS->OrP?RRuxG!day-bk0%5&i>W)@uu)eXF z$BqnI;LTM|JU3WB=9Mm(m4H< z=F*&%W^2s0;NBj8P??jW-(`PX+&wvw(zF0RGOg>ZCnqNd5{5Q4aS8c8aQgqdZ#i~* zduw{LPhU3u1kBI6$&=EtK>%S75N}UlNU(UbJM>Xgf<52Bn4TkRQVD(q<2X~HgI}!< zVYGeS9GBB&YB#H+VXZ{zNSuvRO|6eJt8F!-ZS|$Z;9%weuT#hEPgAeCj?yu;QW(B+ zFO3=%fKR2yTB|bsL6kX|obkt^f~GT1ckk$*PL^Qi$t|x4Ys?6;4T0&h@jv&hBK!c}NulK1rYr7jK1oSW@9GzL@ilknBtiA+TVh0Ak?Q5rh zS|dT(@QCzp}v~g%?h^A*7(<|>^*PN==Ek%rp85d^{4AfrbTjk~B z!<9uxlfq_5iv~qMCMKq-&ySV&*GE9NA0VjVGd84{^5n$&a1=~i!$wvk0_*T9x}>%0 zZWk_GR+Cjnu2lJz!`MT?Een35)hl>1(4KtTfT|6~iSZ<=X)VYT=W3Vu78|U=AfcuS z%sWmY#2~{HVS@J5vEE~yE&TDkFURmTDGzNZZ0~jDmpAGTJbf8vH>fr53#a+(qXvjK zo4kn&wau>$xZXW|LIVtrk0bLP2+}`8?tqz_8;mX3+P0DkS5 zNFek`DVTnx0SuOx2o$9e#&BP9GaDvm>wawAcT-y8uiS9?JXZp69SX$XnZ$1c(32>!cMS!NJ#ecc8Qm4-enBwM7cT3u@oKjeKO#qe`q~)Q>5)Zh z52pt7;z0jG88L5n0QXjnaJX5bX#QZsG_6b1$S7-@|LIcw+SXUip&ms- zoo3Rn<5P~m{7&(>kK9jwfHVgTz}@KUxeb5h$sSEH!o|4ZBk8UF=Kwzr4dGQ^%P+rU z5-l-moM+g*Z#9%6N<>43uELhD&jMg0!HEAvz_)8wD(vIu46`g>OYSd@lSyxqm{BE8 zdr=0WG&aNH6tGNXfOQ?BtxV}j7ZNDDmFbY6K)$Q=!jSO*qqDRbSVnqLI0-@l(V+N}mp<#sq-Z;S2j z?KwHSm)fs4AG`5WP{x_ofd$iZV1O-(}1xe5)6=7)5Vnq&jv+3i01YTXDev)+v(Y^-J^0iJ;rz> zSVdBwiai+Nr&0&(03M-L*sI%%HKMPdr00 z2>u3c?F-9Be35s^l9y%sZfW~;gT(UJ=T&<8!4q@63l~zI{Ix0g2DKRxS3CA~>tF;+ zOTuIm3c1Lr{khqT>PjGth`&f`VV44%&!oS4ys%rP-DsMjgYg&UI$CD7^!Y#s+TtVG zPN9Xw0tqxKir@kC(+n!=OA^oIo4Q7*CKz2Y2{d*08IQqsveIg%+fiXhoU_Ul)(Rmbt`n;Vn;k)*GA zf?=wp@%_WY5nCCotP4dC%z$Lp?kA9F;&X5FPc#9M{b9Me#5@qS*_PzSGE#;-mj)t+ zyaQ-*;x^nG*=|FNvp5Qjw(?I)Cc72s+E{SMOI!w~i{T>pn}d*woAceAI215)Lk05` zU~4m!LWksw=lZiWw_<^|sJURnlZvd%JzM7TQSA=i>S~P2-$X01c^*xEgR_1m z^4jgM#-~Hfz$j3e`m0a8nB`_U;=ic%KG0nN6Yz96s%hEWIbY@2--k{NE-EU1x?h$8 zmC7BxA}IRNQL6G1EFb5gE#2mVS#2!sF*@d&YdhdmD{sK_L57Ty(YzI>+vP?WsET5s z8w+n?s(1edx_q@btuHWXjq?kFLwzGnUggR1uIafWXuM%T;&jr|s#@iYEU zREK}g2_Zj6;gvQ{)3Jh=9YU?JdE#y`+ zSOj`Xsv>+s@{Msv~X`w!&l54^Q2d6pRt`=^xB@`Lh{UIINV+A5{5q&tYc~%_*y<5L**~ zuU6W;Z8o%HX{>R00ZFR@CQ0D=2#RQT#*&f*S63|ZeKm4O{F7P=@n7D%rk|VisUC|l zez^jNT1sf;mbF|@4E%!T5(zvaDC^lTgyfx!Z)YP&Mqfa&ET<>rk#j6Onc3;Z7X{Cg z1q7-GN8?#|ll(8dKL-`~#3jUm*LMXnj|L#7GbQ)k32pK0-ptGljg(Z`$A{lXf2Vza ze?KuR>v=||jwj!V0>n*O6OeM2&srU?w~+F!0w!`f2mve`k93r3XEn}^^_^pNXUax) zG5ax{&UxQoqpaN9BtdW3xP^J(!2P(>-dLqjn10!gki&A-oEum{af zlgzm;Ha`*@oU;LK0>5UMb-^08MISeFBRkq-#*MuL^BaPmbcb89@S?zxo4GYh2BBQN z&Ik@ano}Y!Pg5G&{6*4oGDT6Y))no1b|92H9ihSPr3IJk^r^Y70Q*C3NgIuLwfY^P z@U4e=SwNdIXqTu}qM+moDj?L79rOPH{h5Gt!5N-%x3^0Yo&NGkIar2fV158?0D!}| zU(!$hrNR4_2GkaO!JGJ;`xGjo+dVv-Di8~iRZuivYm}p+#RYm0#lfa5RS$VgV+rd9 z^b0;N-ek6qG(>}^Xx(^Ba+djVr4SbGm#)dwD6H%@Iju1R2P?&*6M%9=oiXi7P-!k<$Op|?XLD}~} z?Yw!kD)Nw?#pgM-;d<3}F%Y7`oB&Y2o@yY4TRK08OX#Lx6hM3roW0PEo064%L%%PQ zK~5*R%@gYQLJ)_9&z^dG?7=HgMbErgxP11x5c*4(JcIEX2rP6z)4V(L6w&YSIOq-X zpZIh$xc@zh&F*$GdcGEjq3FCg>gXP=J3;|Rk&%)DpKvj3njjpx^EORYqNS*1>3lW4 zYtFithdv#PpYL2esJ1t@fn}DrK3zD!TpwGu90=7!bWxgOm(U{~1?kyAmdTg>L;1*; z)OgSo)yd`P(G2?ymRX&PpOPFXvye^VZvF-dalBarTcguL&dZC3ii(PbFn+=nNK2Cv z)!#4fusK1)21Eh^Vddn~({*_JK`F2Pmn(!%M5bz4&cPloLpTcF3w}s3MGFDSRY-9N)B3mDOx_Gqhs7Y*Z;qF z0qCBz9&s}M+4AJ5!iwxZKO!rgKoXIkPdYUx!^z?K`529=ZYpx}pqv~cH-7yz zj>1nCNma?Z<>FbZR=;OJv1~HWMEGEFoLR{b5ZD z?aYYO^dAMSS*>D=TvGqMBhr)J=D!g&mcq-P!RvQBA*-y3E@q58F-#KTN+f-E`22ZM z#SzZWE1E7bmGr;(zn5@e{2$WZDLRrj>Kl!d2`08}Pi)&xI<_Y^Cbn&RV%s(+wrwY; z|L^;K>)f52Q@35cs#aI^^X&c8{-*Hj5qS9qO_!Zc!YonNR2>AuDYF*8o}|9erDbGZ z&nmwio-VXXx?flvU;j#AEaF2WZ)%dUwPhGHpCdihhmkn@?75i9yZi4gssP3-d=U5= zsEo(6W;}fM1mDYOU^R2ME4ga<^ zsQjvzHYGlt+>R}DOex!0PAU4yWHDR#>*mq;c25ioJ|Yi=vXZWcw5LPRt)5r4o5^UBI#fC8!_ZMkQ=O|AFM7GP=UPlbYt zDl)JWa&T;Id#B&-9Y3#(KR2fYGI|Kh%f~l1G11Vzh)Bq9!?tL^RqDA?oh_Tj6n}FA zlTgW3sC|SXyX|_FrI+C^gaiVq$FGMK$%dAq?uZO$8pFcK2vi2E<8(cNc(`Il7K4Y@&mrw(kiy29qZ{{zu>Y#uR?}ueKx`E9R=;XE}fL#7& z*Ma-1yYVu5oW813*SEu}_Eu$T2Q6dL#WARlrYJu4Pxo&6H)0H!Q~!Iv9eeTq`Y{}}X>sT-3C;o7MFkCCJegIjq-!NE8rM11w zoH7$M=?j1afq;Z&b)D2y_iYO(=qKqlO1m5z9VO!7$!u1dTf(ub*$D}v7WI{s2k)he zG?@NyM1)-&^D`eG)c9`;l|+5}!$SO>v= za2+D*#bpRl1{MAI{z7PPhSa5; zF_#@gFNw_*ug_0d8EZi#^kGgJpw&0IJ&Nr8JAsjBc>bQwvG0IEuQTu&2#tEQ9|DiM zhARbR_#(4r{dYFvu;By>%Oi2xly@46X^|c=)44u8Q1Dg-Uq+luCU(lB&dUzEE{8gK z$uJa3g)!g>#;>^}t2d;-C&Et!;!8ufqH8)>(-2I^2@TdtSJF6Y2)8@A6Sf)BPgvY( zP&+u89Y1JKXgyxuqWuCviMgV3?oyWU{VLrwrU{pbi_gv`xaKtP-%7hxfN%D8_h(yN zlB$_v<$Q&UwWt!=mm)8Bb}>CbTYJ2kuVajns#cCJ;%792D%Sqpd)m~I)dE;p!RXiL zyTx`l__^=L7C(-E2(oS*_JUNGoh6k&o;jvQR+1}WZQkI}5_)pMB%z!@eYnXE5|86#q2pOpp9-pFpv0dZ-Dygqm zH|Xc2>dBCTe*g?5Yf(H)4A}Vj53t5Z=JytMvPPGf`i@^A7~eNP;k^ye2*6BM%>^M2 z)WjJeT#&%I-yQ8fnWe|!Q)p_zYo^nMYLA#E1XhP#{>Jy3x!nxL-F6yL?LLzCkcNrj zt)=$%-gbq<=!##VN9IJd&K`CXaGs;KW|%S0GsJ_ZF`GhUBxb&xJO`p>WLvtJ3lI?U zq+VQj8x(7EJ99gj8_Iw0Yt`5DlT=bpfKQkLTqty5|BP%(D%xtC0Y>(N` zCk{;N))Ev-MJ~f5WIXN+P=j-Jm?FqD6ABqC&Ry=#@p8JtN+3p&=a&h;#MoxWJJH8~ zIFcgxk1O*hs|sCVOhyvYku9IH2+)0oIazwVu*V$5=f~FXh~O(I=!?pi=UnRB8!eU& zoH%H}jPWdhA}KkJB#OW!=^oGikxAiM%(DSKa3rs;w&T6EaBKeVOj=5f)Ga~WR z?C5=l_2@KqjC#uGbVFc4ZyBl zt;qTbU6V%XZn+`I68q7Ax4lP=3<$q+6(HHNDhF6aanZt0X0|R|D}J z>{^@d($#gpm)FTKQXoM%T}TEMrmEFwSKW9~{o$xJW&ztXrz+?ZGk6mu1C^GnV5(3I z8&%yxD)iqeQ9ohqS7LwqI`aG6`uOrV4WOj`PH6Nd&+UFIvo#Vdoq$e<6Z!)RX!Qtm zoXNE|5GmM%z@Suj$M!_P5ocv!b{)ra9QVX8l_wZF*^Hy+Bkc7Wvp*n?^S*O?cqsCk zfQbRrxksuZbj&v-^OS1Pku;#id9j+UEtJ)K4C2}|7UflqxbGck0d#za0t4Ug2XbPm zX)0Y0VetjJ-%v6e#tCi?lrluc<{C^TkU0E_V+Rv|IMX2`2jj`(3)tZg-ym8=YBSv~ zyj^oS4(&-vf8FS!tb6E;H&mH%L`tvy`muMm^{oX4B2+!tBjN=JCV_>eapt2t)AQtT z*bAh-uHE-;Ccl}auHN`ncQZv99j=d4NgRvORiEsu)B3ZXYm~ zI5JwTVlMej68r*HQ4&`dQ4NuY#|4vnv!~XrVk5hy6|E*xYg`Oys#mXIA*csOk%I_6 z;rw+|4he7ubOzTcJ{B&?Q8n3g1(T`axpaq-tE6q%-*AI?xkL%8$UlKaK;+`B7Ur!J`XQzoMS`_v(T6QK! zBgEM}m-9)~<)q0m56o>K>j-_cxzb#eWiI`GWU80r$NSKQd{b0l7)g*@H)`DpCuR-i zT9TN&Itr8$5%(Y=NVh7`hZ*)+GBhPj9 zuuB-}m-LpWc=olDez)TfH#axnYV!*D3IPR03QC z98*4dd=FRfC8$`m?^NT((B_hp3>e19`DBc5=Qb&TFM?Y9?)l}PG`Exj z>BKbSq9wXW9>Br2&zz0WOz>??_*3cV4Vb$v2 zu%Q>|apDyS3WRT+y6BElmr{wuVw-N0Je~I=fWn$og&u@Z@9Gd_GKi19du})5KYe&n zC-xjY7OF-p^EsP}v<&j-2TTtWMeO}LcKwvi$B`?&#o3npnzWdlX?oaL`dYrJn9}_TG2mKbe)@U`TmLj>5t+W1 zp#1w$m^^SiMXp47cT`}$Y-y91Ylq>ehcjX7&`!Za#ArP@uF(+Vb=f)$3toKMUcY}L zw~7(5D#-NuJGHW#A;v{I|c8^KM9e9GX2^oRs8d!fU3%X8Sc_LKB%8 zOLoHylyg{9^rJm^utq1$e!uzQVl`5)tNs4t{E|5qcRI#~wO}$E)MqWh!xPjJ3iu$~ zM?+^~3k|oq=se#n8SuGBo1IySl$8qxsx~M7#b57OI1j2?1{UuX*t+dEdE@`*6Xf*pVeiu!vD#$%_3YumZ8)ln&huZ-_U zZtqht-O=s$Wr;7B5ww4HW<>}oC@c&H6_`UT5`uzlY01TIi84(Z3t!OK7-YRdGt7P9 z$aQsf#V!zBP~o@&QLHRxV?zf73NH#lELM1v&M_6%+Y!X*#S{Hg71GyXWN3Mqs^OA( z(?B@AUiN6hu;3cFUf!QOKhMZ~`5dBaQ|d-V4GtU!Wj%$ihyXYU&S1&^w>*Z~;uP8y*@-E>2*f^&oBLsFDknFfi4sAx(nH@P2CL(({UUs! zyt$8FmtL&aWeBSHWHKO;MM=baPc38UA@4Ab`Q>!F-u9W?+IwsYuNq5KTO!n;%$YA+ zFLviN)PRLV-PL6aqxn@dJz=&sgvvjw^2bN(nTnqKsS`_*FT5`i3aH`>dyz(2fN`jb zjq3smlGJ)zpmzgL&Q=OkI#C#aFEd~VBf2JavV;zP^fnHgS?$gr2CdHR-YzSHZ%{XO zL&5qulTOEF(hAkS#l{|b7i0J{cQIX{pyW+IA#p;DwDEd=-V7BN+5GB!9n_TkR{9YN zZQ2e(-P5drij;14VLJ>+qI`G=L)z2h<-m+ zUCyI}G$6@}#$ifPBCZC(9l=+dT3X<9q~$PA{eXwhU)YRgK8~kge=MOXV3g_Bp298k z+&v|OhS&(uK_NI1`$K_>%w&eYQ;M$K|n& z>aKaYdl!z_{mW=R7Tz)fm_f6{-x239;nwtV&<^HXeZS2=RZ5gwST2NLSMnfbxW5ih zu?ZWDc01hvsm-ms4i?&N#ACio{5Cu`OQ5B%#E7iET^$#Jc*Yxh0nK(u2oHmS_;o(K zF{57ZzAOo_Qa#iVUQCQv2_VUX)YS>vxjcj=+2HbKm%;F!gCZwVmw6p9Fil4D_eyL|0{Hky!>jK=Hhx)DQ-j2|^ z-QZ34dNq^6!dTemp81DoCApF>H(v+IoU4gty$&6;*(@c0C#v&)Eb27=sI&D4)^JNj zKS7}n>6Uq~{r+UQ^I7qoFMsyl&-9GW6-wB;;?e^BEq7BK+e8|pw@*((iK(G;7Ccam zjzh4S-%Acaa|ck~z6QO%b9gA0%kQJr^(NyuzHFf}Tz(h`!MVLNsI(3wJ}15J_w5^V zK45CPJCKm5r{zpnFYK{y=R~c!b;s4a*U&}=Lrd>_zGm{MS@zD4<)o(7)F<_$sbFAE zUR{Q~L^-l2{;gm#X=@0Emkm#7i7yYi>*+Wn1qNc+m`23VnXQytyzzg6K1Yui_ZAJ0 zsL1)6kxX#l2^V>4(P<7Z9n?p>FR}ys+b++b)MML&>@o24o;%QAYr-2F*;4LYLTj*r*V>9~l6-V$1RMAvJ_8QwO)pPQt)kWdQN3ZEZ<({bYN{6|GM8BHef#;>uL-w3|~2!&)eH# z^KoM<7s<G275esp!@Uw{%roQ7P(5RA%s?gVV&}21(?zph0BqXlms{VeiB$e z#*H3E73OuRL-A^3Jh;d>t5(i?;Qj`QI@gJP7vNl7Qj%C49H&SK+psW;rUMmZ0!!)r z5fC0>8QaqV4DLeB>PV88@3y5UQE5Z@$COo8hT%>VoyFslMQgb-#z;*llCX=97m>-8 z+ZfeJGtQ{dfxO5RmFB9{3L^&KbV`uXSeQ$}vZs!uf-_l8t%TVU2o8^&QEPX-OkgHz zMP?D<=cuQh(LlPfKoY%g#t(z}y|+;(0%*+$*Dzo@`k-TBL*EuICAMev3KB>Ri8^-t z6Q<>SxWKlZ_%;yf{gD{x@GBkc8tO~5If{g?H?#ABl`N}6Slv!2c}x$4z?ay)V{}xu zN4z}n$FBfkxj_GrMqA9_+{ggyu~NzQBxyU}ay500YY=Z;i0WeVf1}q|xM8U{)y3=V zzc+orEYL8dz&$W-M;P*01Lv1QhmohktdL^>xg+I?rIH;vQgvHFRNTzBiPtA7Y_XKb zSHr$ENm>MBch~&KCk1nKTUP63PWN|(O767U9B{>)?<7BdNEMkM?irk*CTlPW|2$p{F`vDz3&D#wkb}}84luE!296Tw@Wa@== zs-#R$%myoxTXA^fc{6Rl_1)E1+0XK*LdR;$tv7i-=EW4YSw!9&q|YnD-rN0=Z~^zY zJ-=IzY*l&D7Hgob2ru*}{=r1@1nu9=>L4d7Lf@`{etSLRWj7aZ#%&<2N>Vs;ppfpE z%P8Q}5|gdtyC+P~%XdCQQqhmiRE7vncoDmBI+OnLL@|+4L>yb^x(iO{VFj_ifS;b} z8Frmbr}YIJs_zqIdR*CUtdw*+PicK}5*Xm`sJ$OW1jVg`JAziD8D!5A4CGk*ObrJ>0HGKs*V*rbK|Of+=gjjQczefuGUKRx(%Nec4?j~FP{gztuUYab#h(S z%a6o_aaN%g@DB6JhsQW z1D)67&+c^Q82VIl5nY|~)DUgq|Bixg#Z4=4pvn_?glqeo{2!*&iaFu`GRH%b|Myf3 zQf#aLb7Iv0_lYWu@-vP>vJ{0L_ZScyHescql(1r#XiFBq3Z_;ZfxA;t(S6#~qt!XX z|6I36^0HCS4-^v<(}>5-a5BvyZHlD0xEL#X`0RP>%TF8$0u2_@stG4u*5LK77>^(A z`b7}&f4=#R_h<#DnDs(oHh)Uw+lA753EUS$E)IJl&taUU&0Z3R-oZZ1mvC$;vA~ zlL)>xw*83a^1Gju)Q~oCo$SBg!=GBbJ?YyOOb+YGJAdse1pq3z*rcwef?l_o%vD=x zN>f+NuLyV|XO4NJ4L-e-MW68cFJdv6zb`TKF#J|p_6S$7!#%~im=^8qu4Fw7X)qbc z8XWdRzFTs7WGUV!jCaY&hio=l|G;4;q~GxxOK3AmH2d?@`tsO$y>JflN7`Z(3J&*hYkEkoiKm;&K^-n{o#r0G@_m`<5Ki zl55Rj#O?)#Cdguakt@5ik)iwaMaTs**y)YO-rrEq7I&C97-0Bo%|&GIG>4Qz6(Z|* zUlYc+v{FYM!1~4SmUHw%KoB^F;D2WX2tao1PhddkY;P)8JgpFNaImlLtGr^sg;t+u$;urUA$Q@hRf z^`gqjh{FkLYI;y228I}R+clEJvH!TN^=4aQR#rF=eSQ59cx<5P6}Q#%v4X8fx!mOA zI1y^H}Bn{Vd*ifsIrl z2#4#WU+(sk=6D0YQj%Br_KL4;VY8C6<<`%C_PIg5J4dZ!@LAnCW`t@o4u!{oq$j`W zQwd%5zf_{(8mfNm_Fdrt(*rwTV6FaWo0+`Ui{W)COH8w2st#@l1PJw;E!*->Zr_G4 zi<*<2VFD522@@7>=ZnD^fzP1TKF}5h28Nf{SBIAyLs<$`t0vUv=WB(5I3#x4H}qPp z$<|gqAe#r&6;_347tR6ue=tEsMI=K*Lp(lDh`_%2O4f8)V9Fx1%|9k!#s-($6&-nT zv1m{ztJNaba<%Tn;Y2FXeGI%nzA~2KffwIIK7i0`Qo1zMsU@fD*Toc`^ z@ZZ2q<@DV1P7XYWOopRhZ>C(F$xisl1W5A=Wi%I<)52P5yIq9cPc39Ec`nq6SbJwV zuD)6G4w=>z8Jhf>8o({UkOlOl^`0J_tI^N{`Mo%g{3P7Dqa&?_C3fseiQYm(CnN2k zT{@09*DtE$i^QaO4aF#y-XVT0WSHo)fHc3Tb8Pe+666~MTOsF23aUwRlcJKiIDp}( zSYPTt9_{$t3_<0LVVs|{c~ZFA5EWe5enI{9R~1yk`ze=}%TyprsR~w(Bx5VI$0|t6 z-Ed#U<#;mPQjux$L2vEK1#NwEA|uo+smY}>#_i4)fw77HleAeKS7#yY?<}PWbTbrG zS>ZOgBAR)K0r_#2=CO&+d_57&Zl^a$U-h(jJn^;&Dm1!6Ksko8Hgz#%vH~f0vu#WV zjw9;=XQF!!O5kBd!H&Fhs%z^JC2DS(p%bI|b%?VC{`BGCe$la3)c4)?V0R zz+}`D)uax3&+AaH zB-Seqb%hRH>bzUwcDM*yDG5!{3JjBj<4(b60?z(3HcNSe_>N}gH$@kJrk9SpilS-+ zpm8!O~|&_c&1B!^j2ZamNlhh%Q1?4By1Z^p>89Ypz!$Sqc`Ow5|%m5 z)V;Oi_dzIP`u(A6ru2dy1_%VG5K{RDRHX}xc5WU@KtS_zl-cxsEJ+GlK5GOg5ADmH zOyu;c@%4|2-sGRz+Ho!h>{=L)kZ8`hcVIJ!*9hdnSn>ONdxM9Uksii{Sn+L_{)AB(;_su8+IDJdC(c z`ct+3z8+jTO>cK&RwH(ry^Nq^(FgcLXN)lFyeKtkX>pyInSy=!nN zy7(k4O_)c@`gFfLbo2Pd9{2GjEm; zk#EVhn@wnPb&l#VkEvwEw106VWY-U6*4-7i2nL=<`Ls*TyTKXGOj_bJpdB+yV+UUo zeLWc!E+RD(n2|&;xX4GE<{g^RrW4`eph(M-|%opCnKj3smVfec9uzqA8)R*1N#X zG`pv`UK(-werOis#UMlciNWV_xI1tFLf;e6jSl-zI2=DkDBi6XKoX{NOEs=LD%lqC zwI$*_Tm;_M@oHEnGMHY+{uEtlj(5m*FDnser*4zH*Aj!|1w#l&Oy4DD5DjHgq2D-Sk(U78xchCkRqKH|Sf3#6o zTXClLJ-yX~=V_4Sb%8m@tMQBp=}bX2jH>P!yzB5qk155nSUtF1Qh8b{#FM^9h6Lxg zw1l?2!LMuFIbLT-Dgs)P9S>@<-uAdqfkSkgwuhh6Rp@1Wk(>D!esFoC;^w%@2MlVbjuOU0w2NIfVpfj5% zTcgAxo%dJa^w@%GtC+QLB*qjq9UTN+^?jNvGoFBJ6j0IwR9OMBYmI1%;I@vA{fi6P zy>!+mC16a#e@S7MmSXE%z>=f2mGivD>-lV?XJTyOV6Iik_}u_xvUR>7ULbWEuPL^j zZCsLf;{YJXEgwUX+aKVafM~n-ib+xLm|DC`3fGy-qvO?JcxWvOHYAEkL8e`WpFfaRl*6Mz=wKkY-U4iH0#Lnb! zj{t2fdkRcZRT^oI87emb*QVX9Eiv!s-l~+(=xR^I>-H5nr9z;3;>2mTwU)E!K=$YR zE@J~$JwDG*gFDOq+UDuy^Vco545VkUC;N5dwj2#Fs7Gs&{%^g#)X(G5C5yR(&4Xw3 z`+pe~1~)~-t?Kp2!hKrm4DaC=S-g!IfS8MaqgvUL=VcTy1u#d-cWkd%Q$#p2vsO)L z7&`5>eSZ*`&`ca&lquE%z+iu2f81?F@;*MCXH_?%%<*t0{Z1auPja?SOVkkIcCioD z^i1wAfEC$vGtkdsiM{)HKbM-A7OR{heINHt`S6YtS8_w9XesLQwnNlz$lAK3Rab6p zx)4hhuQeX}o=vl>u>pnfVZYaV&x+)*D_A;;Pwx!QE>*cjv{hHffVyjXJ(2a7id8QZ zs-&-AIZ&D2E@nk}jMdR8)Q-u_Y{B#|L6*C{xiOg8 z;ww|7VE{-L02i_U3b*~?=<(DtVGsLNi_XVSP+<{~+vn$&FU$%{a*bgdmVDKUi$G8$ z@2ir0w;$`VYPM7xD4#;r8&pR*l-q+R%^{beD5FbOZSGejhdwEJt?xG|tYeFw^YzK4 za!>vJC}6k}U@49eRGpqF!W{*poelwD1A--;+>vqD-R6G%0z%8`(6Y*6J|+r!wxSx9tN^%!|rF-xcG zUbub44AVPW$@WQBBE-%+kX*!y4n=A+DO()9v!ppkYOlbx?oMUZeLhjmtIgtS+N3gc zN$EJNP+5$jG-`Ax5Hqr`42Lo_6b}IgU$hHt>SkqAB#l-mqI#|*(HGB>bQGP1>&pa7sj!SN*m+0KbLN|w&x%6b8d|BL#{?@kOV>X#y*?C`7(p_BF7 z+EJRAsuwq%JMEbNW0Ic-*ZdV|8si2 z(updPIu%%`A#Jpg&t ztL_x2&yhVL^T+rUeXyg44Y|Kl6Agq3GM{7q-Jeq+C~?E(Sk9r_oi2l^rugzNsTy+U zG1b8^Lj7KO@p2X~@KruWc~mUMqSE`Ur zvoQvRAkAf$8&)E>+U-;6 z!xYe;`lqYE)%(WlZrpqW%t-5y_}?Qxy45=NF^E|Du_o`aR!ZB;Y?TLG0nfA93%J7F zVzSAWo>v+Wt7T5bV09Wakymx(&_=Fw+EWw$mEcC6)1ntPvP78%3|U-w-4c2rWDf3Z z2-Wf_S>?P8}(jXt{hX-T+_tWTa6;l$;(2eMuLkw8``7`co*iZu4G4L`aHIU6Wn z05;MBfm%|LM^iVhD(ip+NHa{X%f9pnYbnw$~%rI;-s9p0bmZ5#M7=Ov5Q(-f zVGU+sLxYD(-dD&l> zhhwm1BnOay!AbNIu>^GrzB&(AJ{;!LWCkp1_?NJD|6t7@$V%3$uP5buM4Ock;-yru zT@^6{wL#!{|fjqmsdBLCi>rBciM`NT+SXG1=LmbeNP}>ADoJWyYoa1t{&GEUqE%RU=+2$C0Q`EG z(b})1moZ~Tz&*Xhw>k`_HPr1mTI>2|xH6GGv7H78)B8^L&2`SQ#-Zvo|1M16I0HUv z@i1N=-iYT5B+J&PVI9K9U+HHO2u4ZwKb!<*d!b_ZZ+i`lc? zh}|tDT*db7dBe=n`bTVREG7Te?GZ+BaIkmF6Hk&fIdCuC+cUyV^)NG|XfT_qN*V~z zt8v5nnUXyZ76|5H{Y3IkC7P$?Y(D96TV%dgJ^Jg2jP)~{(ZhD$AjkdIRy^%7Yx5oa zTL);CPo%+N4Jlfx`XCNN2B)}P=rW#5N9P=SB(wV_l<}o?Ys%2%y2h8?r(|&+CJ8x} z5!?*|qz>5YcTcQz;A5-npfuC%k6?5^WIymf$1?VjxrtZzj6Kz0)}M6}D3t-B22s=k zDiI}1ZEM@aIb_IVj=N=E#GG8Fq#5;RQFA6z+OFw-Mqh&yTGETe!ng?=`1pRxGy-Dj zCm%25x#1zk0>18`*p&XV5vco3##j|2B*lp-0Dn{ZYb1wJxP{A zRAY9RrBZUWv}?BA zAHYCKQNjlda62rF-P%8iujq0_M$`j*AWSL9oA+au;x(6x^@Zon5=l2G&n2mUGfylL zG}#R>0NGgC1MKEhPq3yGuJC7uPse-+Lm%@^Gz2po`~_6u9ysSvQj^W9~7nCj%q6$ytmbPGa13({)Eyy%-QwsbYv!ela;^R9?ZO7 z`MoalbOyTg-`Zcig>~jAEP^|C{q^O1y_I-zVFN@MscC5XP2SGC0{y_+0DmegX@P<_ zV5?PUKEuHN*&c80I?-jLSa$2`L?WONSzf)O!WJjCi!D6UN&xHzM&B3(cD40BnHP!w zzTf?BF__NzH*+O&%$yWl3E=&$hBIp+^Huw-R-@RqdkLm3XV}zbSn997v2y+s484s` zt7~gH@|!T9`+t@x9ueIb07I!cz+St>PR>A(h=x+)p5ps^?%dhL#4Ii?ZL~Uj{k&>I z??2jYFE@V+=l_stZ-OqgkpJUPBqOr|{r|)?`7KIRPIdf<2HI;ou9eY2BJ#x>!ZUPl3d0J_Yq-+R^L?e_S$`!gml%Y z=1P&A$Q*|3H41qCoyr5k|LbLJ>sEYkr}PWRA0RjeK71hGw8$Spz~_mCxiaxT0)&t27mqUE(ox!BPS`K z7768>?r_>}4sQ^eOPf{#&3b(@hdpce5dJNRn?-f>@z}0>$~{Nd&Wc|>v@WrT=Ykduu4=>QF^IF6CqyRQH(K&H zvn{QpCaD#JlnTZcRi^aY__}kTkm^g|Ypr8K*ONFp4rqOsvdDkdD<+jImsV1&1Ewlc zMUc3Hf>pr0kg}p8h@g-V@$@vqWENNA zMi!v?t1)=;!UmWZKY21|T-U@WX|R)fT@{1JKRKz@ph%OPJdL8+bRx)luFB7y1P{aM z(PA{&8-us@NIExG>;=1l>te2XU2?JBo%1p|U(cK2Vt%l5wOF4N*5#7(mnXJ>?N${{ zUH$==J!Lr>FeV*!utCVvnxd$Q!uaC}K8=ZBWOSP=X=muAF7T#4{Agh)lgjn((=#g1 zp3?6EP1`POb0i0KWiAsivWNkav|NS;b>}1vt;RMrJnrrN1G4GUi?!Vz8jt?2&C>)o zbbczf#tj>;yp4c#TGez@u$-;?a70uL)HsEk0vrb)kX99 zMNl%`t${h~L!NUeeCUPJa{M0KHSr3IDrltu*U#bk%D;$4S3UvqAARMS&(e320U2bGq*Hn;r*{^;WM~02*7`(6V$WN>W0~_Lzn0U z_oqcp&d6&|kGmSNnR0S$GxJ7!X0~!$kP|e2{z1KMRE^46}Cv5i4Z(pj4N9bD#Vi` z|6eV@oxkTabw#`MJwFNWoiN|;StK9?FC`_%98Dvk>Wmf~JRD+fY%Eiw*Co9Nq^*H( zVHUSfL<~N6fx7jq6&x^(orHGcrO?UE4VnlzjHYvABcXG%!wopNgWYCBP+gq@gva+Y zxjz65>L=c4H13Mbyn1TVA{y`_*qjzH?@wQLI0%A*Is))gbAWLUc5aF1qbTfm&K8AB zDoP^;mvvUN3y^@HHYEHD{$xLR*^j|Q{*lL~c3y zbiYhCzpa}frDH+94)Kk0_eOzUVceh4pT-DJ;@b>~Db`Di`0DPN)>x0o`@lV5b6o%XWl_~yJmEhDp>`d*i|rW_%vZ;< zc{rAPs3TGXB|amTXn3LX-r((*f8mNWbX@`&lg$OHbA9{N8^Z9pRtNjxP|J3HU@GKX z{G5dv!YP~kuUP!`$%5oe>Ldt3k#kVv)9g#(?62-$P1D5m`aTyeCu7Y)eQ>-yn5O;MF-gZX&Jd*)dz`I*zJYAm7 z=9~2^tJr9<28IeExfHFMKVUd_7Zx$=di^249W#-mW~vp0_%dRjio3-qPFLg6WW1Nw z1UTKHZ;rg|7bcY?L`C64xP7#`3)mT|AMZ<`o4Ygwik3#U7Rp` z;@O#_z8;>?lU2N2xVsY?xeM|M&|MdjECbhWx3i7(u$$itJJ1Qq$>Gv!R-OxHQ$X?~ zi`yf?zZul|d@Z?fBqn9dkRVw24=^61R3VoJ`P3)|kE=hKR%6y`>fzTC5eaXWd^V2; z&7y6_5+$>vep9I@S+YPOw9C#oQsHZDu3N~ zy8G_l=)Kmpe%H4JVq&WDW3E?W-INCs6pqT8Q|Ie!(e932;!d3>+P12AcYVaPM~0yIn7?B}go^DR_n%pczeohv6A z_owRi9mzoGJKvv9;vjihPVlQLzC)REIgy7SdNdX^3X#+dI}FZZeFj>uZ{Eu)x?v)u zUsGp%hj;xPFlk1%TK}_RIjxO3l`(w#`}Jv0Ml6JroBcAf`N1mlgAE2`sxeQa;Z9@l z!Mx`_3oHip@%F(Y$<^aM3Znm`*H83Sig!6P@7rvn>Nf^HrnojpFjUO?l-77Gcf`Wy z3uSGF)%0@5yL=x5M74)M@weV|tj@$Z(WB*p?v5$Lw`i@lZo8+R_27N zL$n2mW$Mj-Dh2`pOK66m9;uP`ujA7knfD#FLAXQL-TkR)4A{P|72{Al)7ii~r_1X_ z2CPt_Kh#Y=u+oHS_srgMEA5}X4iJObYm(hZYOb%xg5CZETCmnlEn&5hm$?YdQCdDD%}F}+4aGr+MmmWjMC5OKHVFq7dKh@r6<*(U>A1~dkFo4KDLB0VTX3|K<` z(o5H+;iHfZ0RrCE$MgEecaR4Y-l>R1;ipHV-_WifF`t|kMq^841;=-vN|#_I8}l8u z2+7?*>p9r%{+gHgf)>-AgZ?gWp9Phk50M@BdML2xM|EgaU-n@3>Tx}THwX!4I`rrL z<1-@WfN-`FnLaF#Xiaxw3u=FP^?qrBGGXZq8ztA>X-!jjZyqY9u}byYU=v`Zi7!4I z?gpV{qno@O%j2P1*B~JQ5_^fiyDNOnddlMKE@iNa1}z_Bpq6yp5ssHg2GJfg(JLG` z90EQ#skuE9vAwa*)oIU%6()lFcag|q#p!!iCd;>7g~B98vUHO^8?hLuW-QOhu@=;O zuamFIHD$B_3rgZeNGa4<1=3M_;|_C*?@=y90=#*#B&bbkPe~=EC%TBARNO^yyj9TM zxWw}d!F)vebR6x5`PmUD`l|Zk1@CW%yu~LiY;c2KFiBRJyUQKvYiqkh-;a8y5tF4R zo{>CvCwSt6{+g$`tq2mtXpc1Ir93;wd$H^2R>mRz1>?|}q4d9P1i$xtOIJS%SxvzOTB`*fRe$~e$q=t>DXW;Gf%5+LEiiMIgsU* zm7E3hl_nn|5Fn%uAbT$UZ1&8|#Ps(=0QNx*79PM>FJ{Ljea!vv3kfrG>^!sbom4?= z=68>TZ;bv*CFS!pYHZk&R%wRbEh-&2kRUl%v{=^P7;SGnnqPE?pNwH#gl$kA=@9&h zi9zpM9lN2v%X-KRHh4V0HPAx#uV(J|-ppR)u^h|m3(MPcIB-sNwfS8n(cA4l5cFE> z?2EbTw>TCyN?_7G8_vO?y+Vdaa2Z$2-CDgIpvw_9vW+XGs70JM6J7Ott zx+_je3aW&P{CcvB+{_%ThE=xXy(Y(@<|vG~Aw9o=n%r0d1)JoHu=^3Z+?d?0JTZz9 z)RgBt+ku0W7_D0`n$1WWLfauSf}2-NPWojYmj)uV$Y8?ZMv_<~hwRYeO%sdKNl5}f8f6JATQg9^o_y38V(D3&D04z;1z1@u^H5JjX z@9({e6- z!JEz$iX&9dN|CVFJl6Cw+WWTo^CG?|nfcYMwcb*^F7my)PSxS{2x{}g5#CLoyzyvv zrOt^b3XyPd9%r}3^W6Yd)`L;`L!4bzM4&(^dk%N0dyM1&OZ6xilou?J#mbc@ck<%b zrQ2?5WiZCXNh?ZA*Svp2b+rI!Gl#jHfYsatpJr|z%d{3YxiWDY3$z^b%YY#3owoR& zR@jVmjJKN!$a>KPBLk}tA%(eWE01-WV#{xeOlHAG?G3ealMCd>qjI|GM16P1ai*J{ zUkF|zdKZ;?dKxaT?;{;d$)gqKN`?)c;DA*Jsd#w4?mHB%-bapP;mP*tT6_dv7YCx4LK{syU38f zoFuf@(a+sBU7FM1l(c#SC#M>hOAZwZ_r1I#wrV zP&fD6syb#sHU&+S?+ZOf2Lg}0%h`B%3AZz%Kxo%E4UwnnEESFU{lFYomI0RZ7Ww4V zm?%xpKo+0Tz{d?wOc)hSp^HsAu{^HWqj3bv*-H+(G8Yl925MGpdZ666(PQq)TdHAa zg>kS{ugR}Hf%Z#Og_b(OlF-cloHl_?4@ws;k8mds9KN}SLMh672#a-Od};)kpG+CSD?#57j&S0^E;&R5^F7$fDqaY31 zIts)w-e*`B7T=Fp>cL~l65d!SH&Pgla5K^tsMHFnP^YVJZFOCL!A;_V`ZxNH-=R;w z*@YU8fXUl%Ku|Y9W;4>I3i-n&6yB-n##xq!m*p_cKO75;_M`=y8yAkE+7Xt`R!hxecVfUh=F{>4jUDsRd_**i<3@ z`pVy0RH?{JJarX^=V8Z3wlzwBb=SWdYt-?<@3#S#@Rjvq!u1@H|CIqgJe8M4cB_*X zIT?yS3|s?7;+K(E`Gyo|#f66~ms64^W?G!PaYOw1f=7K|+aq{BO8Z3;QiS5**YUZ0 z+G+uj(9O-I04Z8iHT8esVQY3smTEz&QpAq<>>n!YT~4v0*A9OG@)kKP=TU>UAWTm9wK)nrTxbn3Xiu^t~bt251IMRvXT zmP;(~K<=E?E?Hz(b$!zR!CJ1C6ga_iHHcN_sy|O`uBDouLm395zPe!2{dZ(MgFF3Y zbJKO<-e_}7*=}z0@bMi*Ld7}^W!Hh`)xT7dU3IW0%ZFU5d}7w7Gh2SG&~@qY*K!@v zY*i&2m;OK_%s?V$PWeD^XGCzPeWu|qbp(nRcNyPPYWix2M*EZO8}3qHn>9F;oOevq zw=a2K;1qD9@eILSZ|xT@w5f=NCOUL$OCN$QBMW}E>^AFRUTs|M_irkmc#C~y)})1W z>b>jN!lu?q77ssip-GI92@}Fa!(e6bxuR7&Ghx8yAFQUH%p6j;HC4XOQ>Ta|4(tJqi?L9P^ zt@GELL3g7%M9LyAOZ=j|#%+7i4a;1>T=1jLx=ajo$uFkj#k%K$DcE~Zk&6G4_RQ`kyaamjjbpQbUKsEU*EC$9lnMg zMv`pqWtQHYm97RQmzl^W+%9?&K9|_|%Hna{%-qOhvzfE5Z z5vj6xG@E&3g8t3~jAZx}o#8GJ#709`2X$4~53x}5+~k`)8Rz@{iG7P#P-XCn;zuojUIp{b7mtrGW|N}4AuvykIGCo$ z+|A(F++S$IycvyUh#`w<_rP-V*k{M|6brous%g}y?J}HBY@YdsP7i+8C^wrY}F5#k>eKdS9zLM#r)A4xbb-p9fAbYl`zOsNGQ6ADAUfYEKO$_5a#~W&ralb(-~x?gQCCL*#wos= z-3w!;cTZM$a+znFCk{Bjc8Hc_X(@kO(EV2hx9@7?mwDYj2d5elxNQ0}xsNSghe$xp zA{j%~!RD!dbz@_p&nGDB{r&#?BNmGF0fC*QVvtLxUfAx3iL0AM{Ys2MYNXsj0ZF_?b+3xt>E+_Dp8io`C}tpIezi^MYvFwxKOWG z{l7(W95Y7Kv%!cZWk{{s^ghR)z(}@k1|Joyl5kU<2EGN$Wy>P_O7>$KY!&wb%u4HIBFYXzUMW-t&eC^bHn{a~9uCeP^jgdEh@Ha90m|?#K0k)Gr z7Li<=7lpFoB=pVs2ns#>+t5y~d*k9@WNEBeq#6+AoT+E$SeSV8sebIQ{8$W1*00Pg zOKmE=$+{{A?d|JJd0fZGq;tPOEXw_F^0*#_KqNI)*=5pmq7yM34`f3+yh(&DhSe7wYpH}RD?CKlNMSKCj% zX@ypU5%Y&P3I7?70_w%lk0=k6pG;!Hvapt`^Y%D9>x1`}oMlU{8 zNR1H@Bl5IA{-tJE^&^7X2+TG!@4-JK1B;kH8m5l9uD%#|Tx)PmG11)fX%r^C*8RJk z3dw0b{^YB}{Hw-<0zcNe_!#1rk3zeQlo3|7l}ev@feDcMR9J>dZR*E_mh$tI>-+!! zd!FeOh6XlNNFadF1sh>so~2Y0#?M!&L?n;Nx?R1^83T(!oAR+I8V&}IRt4^kyF>&MyFi{(|u7g>~oPnKYlD`cg#&R6Gjv$ql<14Mki^Oiq%3GM+l#?~j~biry`e zNKUWN2>6zJi4JbFy6uDCbH>W2leuQvv3iI&-RWm3{4f`ixgQ?9N0?cSh>lES{8dD*+zkaaUY5H4f{|cERBaWTm;;Y4fs&ulHSeJX%`j z9BA;9M`t-Z8; z(X?#Ak79+x<3>6Pq2>xLTe6PP8d;V>wLJcw&GA@8L_*ft%?kYFgHNu{5H@pQO=ES1 z=?jc71jLK@{NCk*A*=#2GOR!$Bsoyu3<-~^G*lO978|PW&Um|zb+od)kAh`3zDlyp zy0?H~p>cOphj2a*W4^35Nd|!{uRhp%nK(Opv;DYezE@VR_lSdt523I;A)L(OLXsK!IZ-a}q*y!;#0(8`Xy5zu!^6k8@#XFD zdP@7vf;I0NVs=!9nfb0jx6M3q`A(JX*Nd%p-tDY`JU1WJDh=tj!OB8K;MskGEU2a8 z4j0cDw0M6{&d(>CqXb?#B`-I#h)wL6meEkXvk{~6zRHrNQ9)-g7;dzDwOy{!f$QshzB*H;W;2=2HIc$%qEMoOfX^BH zt1mDnHCDz_8y^!Bb3BzREPs!TPwSa#g$rN?y|Msa?x>d(sOaeZOL{&53VSG%X&g~N zwe@5M4>UG3jM2!{)Zu)|8~`3gIGoB5t*UDFrIG|g*h3vm>JHId`ZFF z^7@!H*v{YmuQ3IR$0L-7_nM@H3f#a)MwDEo0;Vg zn@HXRmBhx*@=|V6H!9~zG8j%?*Yg&K1gY}dd@eYe_}$=!DhclqG5?2Wbibv+D*@v( zt2_J)Us0A5HIpiC9XW%N!AAcD})&;V+Tp_bc2PWwHW=jUfb z6O%fFp{hTDo5nZsB|w4k+1VMO?t#eUmv@#!0$HlkJ1oj-j7|hBO6BB`R#pa2C2|$h z!#>%YPI>9+F{P!YPh8zo#BdZBiloa{*Ey@>EMdF-+&|9E*2)P-YmXtMQyWi%eezbx z`BJQIho0{04aP(ClPRI2n zVxKniOs~`KM|AfOW@O+-$=(s(mYUUdphelw40-_ec8jQ%~-t| z4AQ$2ak-2w1J+TMEb+~}hud2U65d|cr{6%_HJ@aw2pGyRe%;pwL(!v_hNz-37E6AB zDKsSD>X~0u1h{^S0=acXN_3#yBrYvY+SS!nGV>%h#Vq~8$(B*BDNTQ_{kv?%oVpz) zPAM1Ns)qm(zC&L!+pb&e9ay&EX913kX~p1#(qG-LtUi|C474=rF3xezkeJ=S15{q# zCPQ*zipig+BQDbzZuNfI#IK`Ov`-IM%%S(v+v9gD{`SnTcK@pdKo<7d4}dHiW`&Wk zJnUWOa;$#r6?4ltBS~st*1|t8XTp~1*gCnYS~e}*BoBa(nsUc28xL#0+d;i3#NnX- zSp7vZw&@Kg!8DRBYND{Cy@nIafGC#jy@nJ#QWPT3tgC>&p8VXefC}CO*hk9lh5BX} zDpqOGde|}VPNf^~u0YiKeX;xIjJlDPKI(F)8l!P%?wv(=j;utw?8*d4!qc z`TXwTS@+HwfGQD+h>%$mjg{${lt@uz&o#+;g$@DfoQiKO>y?2{mtDQr9sk7*;Ay|S zSpIw@pfmCLjA{08yE{7?`mM)qyQ2!YxQzx41=fFkUWTT9ddk1%PV?CrX_+E5*Y^FK zw)VzJ@k#mjOz-YUczD)Wo57HfkT|)ycTP^E4kt63vkbK<8k(B^d7a<8-rj^)<(aYK z0vS9#uY2;8loSL)-efgBuL!`KQ>n?iL`_vEw93rP%v+~T)|X$&9^v<(cfBUI2yDV& z;Od37x&|dExLr}_ZX5%LLzO9Jr_CdK{yN`?P0}t3BqYE|DTp6ofS&wyx-UG$Jef)s za`xS^G~5?d;s}u>@xi*8d#P6l6pVZF?S`CY>5>7(tk^Vca4`ePzi6(gg=V{{GVxnE z)MsP_h9bwYSd*B!oToDNEC|!@wG|(_2ANS31QZCJmY~aia)irGT=6lIYqj}Y;k7^! z!YxtF8T|Uf^Eg(!$?Zp3{+Ot1Zw$Z8tKBk~4u;uQWeN}orhq(Xu$g%xDSz(42U2}W zimpJBEnac+8IU~5Ay9SP+-0x7_^tY{BZqe}8AljM5Ui9eX{3_tNW7-#W*{}b(){P& z`hH3i*z6*x6u^+=!Z=sn+d0<@Q~3&jrgiB~$_Gh3y8Z76n;ah(FnGRF9s*v%zkvWL zF5nfwd!>^Aq(U`FNy9p};m?lF29&>|9q=U5I6i|D5-=Lf7jj*)_+&D;BeuL>BTkq6 z$BZjbsS|2EJ0{!3S6Vf^P)R>2HHs>b#2an$d=6bS9?rREXO&>OaQb7md=AFX4Mhav z(Oo&$b}x8+@3&m&vNWvxXN7)86~)fENXskEVJrwU`6W-fTvmPiyUSIszL9u0|GJLA z-PgI%Wp}Il+!I$qhqxNK+kqA6;67yH*>Ek`2J*{ za;YLL7~w^M@;xf*bIUh>U8*nsFjDYgH{0J}UfL|z9DNHMC~g;0x5+35ZO?Zk0mgv1 z{)4a>AMm>NWiJNnq;1>9&dKY^f+UUITBK}2shEz8hNf%}dun+Zd)}OlkB{$9U|`~y za+U6PGD(2*0v{nJT%pD7 zmkw;qVaeo`iu@8Hw&CLmlgn70D-R%=NOL}n;3I|MYW!W0W&KTqA{!Zk`69sf$THZ> znauHz@poG$oakZ1wUIY0q2uXBwu$#Q(?u(U@6Zt>wue%DE3ToQGwpOtJzduqvoM7d znG>hxzd1Trl?&HPuP>L?Q3OGWe>oo}x0KkQx!p8;!P!O`zzS2dBkw)pYl1Ni_~DK( zu1p?!TcRR0UF5KR1*$YkRTfj;KPzl+Z*XZ#tSX)4O`z_vNAFb_Arj}B*e8gCJ%*-0 zjV=)Yo6hYR%ggfUaT$3%5u`LuR*H(M_TV{EKI{PT)RZk6hUtjI6!beP!uYkc;LWw3 zI_MSX^2xivoqm6PxH>7bxf=T5XADm85SAQ~#BkOHCnG1fqqsu?>RJ!mE*GQI)5D2j zT-`k8DzrHK=yiZ83lCr`{Tn4b&KG|N?@s}Hmx2OvcJ{`-Jzmd~4SaxP6PzodFLc~$ z#8lcUd{r%NFf>qW@t1`AdoimjB_!oTUjqB$JhM(eGWwANDrch)^w7hl0O~x1jK*Xe zTBhMu$ksCxW7zz-wzj-YAm9GX6*tr+ z7bpB1Vrwq7ft-Ofu{*AY^^7n4D`Fb>fRGNj73){34?ak0kJZ;TTlV6+dOleA6ZSjW zxLO!^o43@(otIV(d|u_&$eGEiuaZo^_b@{0eMRQaVeW*zVENh;3}E+7G*zS^-Gxis zrC%JqmtFlns_t_1wLh*pD1!5qoCo0JV4()(W+>;xs{{S;5JY`~HJwM#tW1`|VsKwX4=D>G#tZuE9*l-l~+eSIDuYU%|Ow6X74NS^O?lePo;X?My=` zPf#Erk<6VWlYiU_de)ra4YE>tCBg|)+m)1L_)VH%~&!3F(Cwa4ZtEH ziJ8=H;^E+gm~vV^eBalvt*d+937Y~SoRENa8|cZv-3E9&9#NdE}bC2quAMS*^jCnH8}=Cn_e)))irBx1q~F&Nx-F~xjohNApChm7?Y8h01@K<( zkjM`ENDZr8X~reKy-pyRa**lGu6f$c*!M>~m~!ju>U@@1r_p&l#Nlpkr%xwr++Xw@ULUMh8*Jnc<#Yf#lrfhgu#y86+lA|8 zY6r-ofRIH5us;kA4+o~TfwMzOK+pjDFsG6D@ zP}@BLXwP9`TZ~d6Xr>BzISTnbnk;vmb0u-OW+23k6`KTwc`e!$qg^Aa`1Sk^;C<>* z1k>A5A^il#8|v048uAQ=B7tg^nQS?_ohrhg#}pRLNm_d=C$%u#9m8vO7Cz!?WaW#Iy7i`eQh!H;$&3@9P5-D1Tmc44JgkK}0w5nBbj7>*2PZ^$ zZl_@x=)t}IR-m|0cNE>idrKWd(%=@c5irY3HW5)?P={YKiA##jjQThBuI^diG@A;X zr1^2@?fwowj&UJdi5j?YI_<%v4A9UuMquB%a$FF+gmgoo!2X6Z(AEF*s-tSfsrd^8 zjmSPTY@4h8WI90ggU(c?iJdFvH?3Z6y2UldBMTX(`i|aGB)k^?fot=K`1gJx>#Q|- z$4+qIF%&g)5p7yKi$GoMM7VU46ihDiEf@QugXD50(Z(C#7$mI&pkk{-iT(HgjD0O{hira=y) z<9^BX3AAYR23NN}Ra^-A!!e(M^fep??VG)C=U*2+Ui|m=kFi z{SX2!N-23(`w-v$B(f5Gj7Es=fb{sByh=z3Y90hD1 zl#WebviI)<4o)@m>N{sUGGUnB2-u9sdU{0cmQO%s`$O9OGU(aqd|$MC$ef9FUPXCU z92r8w9zz%z?)0hSKKs`cVRXc!du{|{D5M{Ptk~1X)CWGvbR{)4>I*hwe~J#KH>rMu z|3<-%f-V?bF~yWDZ#s-0f1s*Le ztpeo-$LqnJ`}fscwO)F?&hf^Iw2`XcS%2U3@~WMN#tMCtUonb?h<=a>%5HqSmyAN9 zD7v~t(fYT1`n*vD;D%fDEH1JGP!}MzFhDf+)Py1m&Yh9j-mYC&lm%ED0gX3U+;Ep>7~J8pM15|Bqag!Z-Z)xeTJ%|2GsKMU2}t=xB9mg8LSgr!+Dl3WT{2whE=% zIzO!;3Ve-tVv$1blrQTEy6A{ z+=%?*A+0k09J7A&LV6yVfvBr@7KzyI;w3efnhJ*$g_yT=8U5bM8lP9O$siNeQ@n0e z>JwJeVA)?_F1vNoy+u<#VJLJJPmVjTNC{A%=Mu4glfp%e<`VLrCks8ZLXv`VxQbr| z&)&??C~OAOjGwh?s-5$HC-J~CZ5yi15`G}$_69L5GHgFIa%eOF^82fwJseqclzYmV z&pU5ji^l(={iVHK^z(39Y>{)C1`7?=Ew(*EA)?^oyIp~D`IWpuTtAd4>1=Hs`!#h& zD&VCo&j@OoX0B{}7{+D?2|e0qy|wG&Xi*?~z_2b09Z%MyL^nLHCRtdTk(CjRFw$Mk zXObZLy(g0a0Wieg-%K6TTsp1<@&xa;=Swj&*{UM`z5Q)jiB^w@a|@pX-NrvI2K?@K zXOn(kw07SnJ)L!1Y*KG+`tr<(`1D>7h&owQ(3((X^FG0-Ezb$67b(L&RP_xGgF6~T zTU7of2iSf~&0l2oJvUSqr^VlM)8vLtveL{(LFnTSJzgASRuNh-kMVbpv%S%6B747a>gdw$ax%*b6b(w63F32RaC2crt z0*)q|K|tRb|8yCLYXJB(l9CA?Qb0`} znb`4(oKfly zI~T6TS8*iW;9S?8hPmdX_|wl#2$z`yN<*)S~FwW&PdiB_h|W&b_z*jHD{-*T#}XF!d{r9X!QL zWM+RgAAw!NTyz}jM9jHG_PvUx#l_751zz;@w80)F`pS|<*R4p21UPXHfKlpu;CDR? zX}oh*wdI`PERBVL4wTEB^qs7*bEyBu&l2+Y7J8-H4htu@##=;G7_-XLzuHSL?-56E zioJ8$j4akb=;^r41~yy*^5?h%5o3J^gJ1T~1HUgR@kyhP4DJ0~89cXq+TpJW3^26> zlLp^4x3gi4H{;34LVmObmENeW_Qmv`=t%!dSuu~p-M=O2k{;<3w>h5|+ft_hmfj3q z4^T^$+6PN|jR|2M$S2<>)t#?c5$SuPGvyx`nuhtYg67sAOf?t2IY?jWJ(QEjKgp>N zzJ)|IJL@x8c(;qvWzmP@@?|7U;*9G$M}xt}BYn`iKhmY!_i^35Vk+G|)L*hiyIp$u z*Z-w@fTjTX2>@&T4`ek~%L%rBn=Cu9CoTuknN!nMEB+QNf)qfvbban9aCBU9IQUCQ zza`t-VTJ`*Lt&Zdpk7+?M#SUv5_!~c^mkSgkskO6 zd1oRmygM3@lpQ$UIUH>E81);b=;1|j*Crl|OMPO_M*?6n#JE)IX8Q+5Xg`CiyHlYo zl&R?!QMv=#b%8)tFxrw`ov2pQtZxjvgyJqX!pA#f)xX|d-<@K(9FpwypquFN2UfF< zX0He#GPwFTo(y;HK+M;9Pj`3kNOYChljJ`HHG96_lsSF*a!6$L<==Q-=q^{N`goAW zq<1F@Q0jTeYD)aW!^2Z&JM+4Z)G~0{=8`yaZCl=&pch9kHo6qkvJdrqsISnvveT&# z0w+#`3{n7g=Toz!+|0OrotO{lHS|i;m?P|4XVQX+FC%=+?k<38iSs$Bf3lap+k%zk zz_7$=)A1Q)K&xJtSt5_HJ>1Y>8dm5^$Lj8#^@+ZAX-q2hvwdaMY}J6$I}wZm%m%k8 zg#7z&iYR-!H;xBgYE6&t&ava%pyL+{cuBHF5|b?xL|sL*-t(;O%GR|eF2}eHTo8hS z;~!3*&F={Auur?h+ZtI{C&AbAF7GLLA?_k$AcJ?-BMaY&ZYNXwutENGobkSKTBuFt zXPcr9y_#TiWO%cQ|oe zg{Ejstk{J6ct!ki`~ZMINR-+rpZ!RgJfq%ldh>I1lO+)#<)j# z*)3ozta5Fuv=5cwO2qcBq-_Hg1krc>8wj8QBCn{Skw6&3OFxX3V)$+Fj!(7m^RN?$_#!wENe;N0#V2vnT;&0zl7q&b{)(>?7 zGu)X{SrEyP5YO9%!$L`-NDwI#i``H15I^bbd@sV_(J`c<6j1fXd&(@rX^Mkns2?e{ z88yU4x4S%-bDqax(9rf8p`hZ}BMT*NL?#ep#7>Wq^2PzDM&-C5~wSF_h>n zNCdmUNf1RH812xK6e^n2Pyh8}uDTKL5v=KI++!CoG7Z1aekA(-YtBiRLQ=y&*+1_?}_S_$$)xP6PqKCTad%2W}Q$d8y2P5!{NIlzd8Qyfl zD{>>KMz|8FX53=J(hg=p1QW1fbPmBCld{&C&Q=8k@^r_;1NC9;?>n@BrSsGQG`fS|PU(5PHAkMyTt+4t@;Cc2Jg1Aa&3tIwo*}avdq>B4Lkpyg z9Hl{-z9vtjL!Lt+47qz1h<_TqSSrb92Mv{V#hQUPQfLJWfZ z6<+z|9*y^tXhU&Wt#Rce2?eYZ0n}>7|m;>{Qbx#iqOdo3#ZK0P-$F}nIo*e>Q|gCT@h;5E4VtOBG7^mNm?z>LhXw=S3}KTEZ}R;qV7HZy0j@EYuX zW-FDUsK7{t@5l%xR*ZsOPp*+Qzmeb;bqB% z9x5HQ;%I&VCtz*No;wSsRhGLCA{ZjQ8!2;ormh>84|dZ)V(vnuZBe6Ia?*5=l5Z`Tj41tk^l2Ge_ z7KBQn2$uQ<7ZZzuLiYRp>_MWOnAR%J0j#vY=0|r*vKy_ve=HluZ9quAiJ*BudS&u{ z86VH^m`Z7JFrG~3X-g+gs;#t*nd`FI7psgNv1u3VI_NP?Cr~WKpK*Pf)$bk)cgyBd zi@H3}tVtq%xud@kl@CNb2}@hGw+;4RjmV6O?2hw%NDr=G)YAwb1cMM13(jJQq~6y# zzvsV4c&z3BXBe)M!1EOK>4w13FsvP4>;biAqQSI2{O$c*IQjOX-5!h4{vZzL>`rTB zj`U#qq6XhFKy-MII$ejYc-+iV^9BiaU~3$A)~5Pm$rO|Kni;Q^cc3C(h8NuL`f`kLQRxo|{_~O12ZF{_ybIskWdtem_B~*L3r@Z zU*lKeT+B8su>2kJciHzcS`8sRcGuV{nGnyVTo{=$SApRk1Z9MNo9&igk# zQ|j%%)XaWUlc3$Tcirh=(q;_RI%O#+mzoMR zCj!#AK|K&s38eJ&ll}N_@#GdRnqwx~{5=*Hk9GPttn7F0iT&}k>+fLKw+eQyeYBCH zMM)(c_Un;tqYb<)!g{*B!Uong2LY1!pJ54Sz%2zE^jIYNhbSoxU0?W7H_Ugf`}IT? z8_fYZ9Y4B_i-dES@$iHyREFX)$ilR%{b$HSCtmLFTk+35AxAJSQ7D-J$b9kvTg%TX z%grk$@jNMLLr}C3lTTt2dc-`+KEbcC$Zm;QjW!2!8(0-HQ+^s#C4E65vXc-k_{zWN z)u!G?meN%1{_5)=dpC_pXbQ2a<=QMmvA}{E`)Br_Cbf9(i`g-zKnlxoFU{Tz5046Z zu;5w=A{9-EWz#WctCWpMwhqd#vEL3Ykk#7g?%y=vp`+_%;S)?Y{9(PZuO}N2cX)Q! zZ!tlhFt%q#3keP;E(-q;se`o|l%*FHiB>a>nNLpZ&*|uLxYk5R!J4$aYO+~*I!Dp` zenkdKcD=iaws%-_zpT2Lxkr^m%!_@AUOYNpgFSmZJcrTWB8ed;{f8lyM*PI@_o0#L zgV?wXInQ~GSa{L!7j6AYZIW7x(by%=20J^je8PsZZ`n~noH(tAn)bpD}H_; z<8?}^30=zRVVd<$j{2{CGR=m*_=Ialw6U1qNwhXpMxmkzBF}6_8a;BPt#rPkiz?Yf z1Fcw9WmEwg0xB*cMNF{gli@}y_vL3Ik0MVuo|iSZ*@{w|)W4Cb_R&ym6LJzT9xt!o zEBk8rpOu4oV7(3>UitrO0mSM4k!ijvM#LaA`~%JW5n&T!p8xM^P#3UA6r}%+aS0Uz zGU5N*7n5HfcmPqHt&7Xh$_h?_QV8*UIadf}>dfi=ks~({zegEp%akVlcP2X`dp+Ef zzzAHXU%lvnrMe<@0wAD($T5DW&7lm%jt~O^PC!oGa=ALPczEeK!2y{4{M8U%o2H@y`b|zVdoZnP~C3jPM_6`mf=`m1@DKh`T?a=x* zJAA5vk5A@d0perS_4RcPgYMVgbc6tzC(3lMsD7%{PK7LNl_j}nSI=O7)rs}4!h7;- ziju6YEsQRG=C&jBj7U#PP8fTwsU^do&zy>w z$tjt`cSz-E4Wq;ySj)jD9GhCl3z6cU)+e=B1ZG|^pUwo&I>akqq|yY-K$pFqETQip5VN9Q$#r8%W`#QXS7kn~Xc( zd@>syf}DrpXBn}^%CD^YQyYk`ev(p5W^qBY31SVyTCas&Se>rP)P79x8DMH-Gy4!+ z$`SeHKH&t0mW4&#EOX6f7)pPtSI?37}XOpyFtL>Io?-egzcgy*)hu#_-klOgaC{RXYVNJAROcp*)?VLSA(yclN649?>Lcf6tUB}~bdE6#?sj>}3e2qJprQa3~ zq+K4^(s)dTEb&B*KtA$m zpA|ELLAhst+JAf+rDjXj{X1{K935ipySZlg)Vo*&$Gji$G@w01BI47YlmP!%1q#@C z#H6N%Ql=_W;vcQlVI@^*9}E$BcU!UpW#~tX6?mEzi^iX7MPuLsISSK2qS<cW%~0PG zmgp}i4R;JazD&+hILY8HSxITGHn-gzJ2Cp?88eoV>66ioA%Fn52g=EvEFmjHNvAep z*vFargSR8pp~Lvw%UcT*b;v&`P1 z`N*{9OAVyBr-fVg)EF60Rj~{=r#bJntx|MmtT`J#2t6TRb6atbA+cIoh^pEHH%=v= zcd=|sz7f{W_IX%X9=KRXWc}JPwiJ4)?ZDL1mjrDA1+%Ni$D(55q`R{nlQ=5d$GgD= zNl0T-Hw`nx<53Sk6-2_K#5t?RfTRpl55Hmx8jd0vNqFW2noivd%6*amNN#49I;K=< z!covF%}vguhJ3@-!7WOa$tPFy;Sm{OH5Km4w+3s$itBz^^61#a-(^7z)|KWEahc)u zDmV87TDJCVot@sCG!V$?#rs6QF(gXL%vLA0`?_~2H%5*)P0fX9maAR7V7LtY=?T#> z?sy#~cNHyZ-o8>z4I)%*n+hCev*-c6zr(P`rU=JsjmC^aLqmZn8RGw;?46=3>Dn;B zBpo{)+qP{x>DWofww-jWPRBO7W81cE+xFD=57u1GT60_HRMk3XSMB}o_YqVW6A*>s zRL_0B*0iL3xZM-1+wPG!u9iee%AW-Y9hVCu^24bj7hUY_?I{$qdz-fWjM<;f>aZKM z5xNPz-B7X7O*=jnfRo|{d|zh^rBy0*TWoR=ge->Co2^k_=zHLy!8vfd|3MUZx-UB8 z#Z6MtFB!4fD_#xt{+)8TNj3$kT(Dz;)5jJMXhU@ZjTYz0M=UqL8G0LLY*fzQ9;}pBnV!Z$(Y8NwJamL^$%1vY3_m0W+p3! zy*ay+a3RwbN0|V(p#eNd9hLg=D7rY5wwqlbhY$UTsf}E)KB?!$0Oid>DT)~DYY^V# zrBBXNCwH(uc-{7}^_ajQa%Tj$wLf(SvWLyCNOrkFcPXYBpyY?8X^r*C8VO$AEmkK< zmq$A+y6uycUG|&b9YBUK&(^NaCa~mvX6ZypfNGLFAR02EBYD_WFk@ViNhUB64%TY9 z)mmS0GTAiNZ1N*hHp;nKh1w#9m3fUb7;gToa8RY`w#jE=%!Fh2+Ut-s1ofXS-oOc(mB)yzZ66bOr6ZpNr$FH421DP zI&DL%eI}(~741z1q1vdJYm#OZP1m%0lX2`z-vn8H4_hxWt#uW-rB0ZgZ9SaLWODgs z0+XWC`SOgD=_iAGYa^K+UG~VD*LM-%XMsMt8Y|jA*C)^)w6Bl z2pT{XBx!Q~JF<_Me7~DAOZUuTysC*7VbtF76Z7O{x_Sn>~IBvt#fb+qvZrIT)Bs63&+ZtF&NC6jEoGOsBGJGPFR< zYxPene+D2>R}HFf1_$r>vEL(9;0v|{F>cQ%Euy)D4nuVqoT?4H(vC$Pt@r5rYRb!9 zNp7cW08$enk(&8nmokzB5@Em`KICBJBnUF!+H7ZbrXVIB!-E2w-yhGpZ1RiyIG@%d zGM+lpm^QxNyD55d@&|Gb!e|i+Gv^x#oVbR|NSXIEk}`$$lV!fdL|GkXXfTp#uW7VH zgtPu0d%*)C4>ZV@(95_uvt?R1saSkj&DsG?o>IIUn^&wjD#aS76`%SL7VUelKQrGX zZ%mp{ZFsOExL=SqwH<R-S3`0?Vu%}z_Fx#O9rWBp>69{Xs%J-S|=Oe_XL34N@BV$pOLuN=>XP?O=`4~KU z$nT9Y0pmE`y#5(wwRi+5aJ~$L{Rypt5uQ5)xXm?jznW3nvYgSzhF|o+T)9-H6E{~f zwM@B{gzM-Cs1xu3o&#*u#zb~@HeeyCJq8eu{=_S{vb@ovAtG`trB9HIj7Xcxtm6Yj zuM8d;MBcyQ_4h73E*ADV@Bvz?v+Z{DyS)N+Q?0zCZr~ne&}*$RDwqkN;T?`qnfrTU znQfNTd$VbQ8#qo5cP^dlt_{znEw%jZT==Sk&gEK9*e9m%mJe3! zqU5PBwo6l)T}CydoIu^sfN`liPOpao)VxF*D9bJUlOhbOnO!I|UyLJ~@p*p75tKKK zt&{!OSATvs4tsF#;>8LR%fZkPRu@c+3>A42ecD;s9ib`hHays`#S;0#+KZ!sf>H5# z8a;xeEp?d#a?@c{dRK#1ypUo~Z%BUj-}Xonf18_i2EzZhoS6} z-+uJ`L`wJvI)&&RzVB;a_gW{qh(0qAuJi0uo1Ib&zyI8BK;;-zhR5=bDisBrN3I9Dd)6@b$vM(E zX=iRdWAiiA>@2oGt8ObLqXxH1i6d1i3l$9w`}{{JpQxTO+nzZCWu4}f27`gnHJp*x zmgLRrGbamjcxQEr$m>j*B~^)C<~Jd&Hr$SME=RjPi62p=f9yc_Z^0U;()tK32c}RR z10gTgW~v-{k^RAflZVGNPIReAV6}8xTwp|NqS3KWy%){@g&WlQm3z74oJb0o&qOko zdzD7|(X1_gZ%oW_TVMj}*%{20%LX;bgb|jK#4*`|57~8vhmmW{!{I<;PKx4zBcFL*M}_oI&lxB=6t#{#4!}~KxZSq7xWy^Ny3cxupP@Oq+{g$h%_+}v>gYlWYzB_?%pr{^fi!W9y>CI(b)epAR~CMf~vEPD|+?>LMrp!M*{8 z)$kWjrr{x)yvVG;#pUpF%WBz)@l8(c+TxSSp3x6|?uDMt2MC`SomWQxP;IOrxA}WR zdkWBSb=lpSH2wcG<~7t}PpJmB$6XzgzP#iiaZCO-IlAPH{v9#aNqoNJY6Zm9)H4Fu z$k1N<%Ii)Hjy$!$|4n9^9;&KWyoVo23Jml^SbGo)X8gj9&wO{r6nyuK*8K(!@j{{j zRb$e2L0}FO!HuzzOcZ&r@zPZFi`n0ns36B9wT1A$h>0==R~ZHG*_N_RL6??6Es!oh z`+VF+?w{_h9LxSX{dE!FS90`0{LX% z_JruSX3v`Ym3b>lyZ}kF^6hx+MHAY5ZiJXNc&7Z1z!-Y`=Z! z&8r7^?h|Ru9tKb=44Gol)zW~HDkGD$)6);wJiZQwgPsD*jR~^rk7+x3)13! z`o=3O*5&LAk*RTt^xP(?$m80btMw^ihYz{#9Wm#vl_ioHp=1)5q%#(OYh+pnW-&+4 zL<;|1<%f7LnbX8|KQoJhK(rv@m^iIvsEZ3GSz1Tj&U=XZ?({+c%1NV~_Y}bJX|=&R zPQ8pAP$F2>TYvx0>7_U1HUU<-*l4{_%hJMEzi_IyC?n71WWgN7VlS}Gd)$L&Ei7xd`L~@T!Wk1bQqT+K^&iBu=5|2eo znWxK+|>ai)P{Cu9dF^7aA&o+t=NpnapO6{vF>-b z>iFHl#ZS&RTZ1-qG?YrTD7{>+9k1Z_Tj3yG#wQzUPWx!vnoN`Ezx*7Th6XY2%fqC9 zkMH+;Pe4y;d-wcjj%o0qT5ww%@55Qx?facEkTzs~mpsAIqXUGg#z+`{)NyVrOipsV zC|FTiXUj1(jm%lFQ6-^Y{Mf{9NU;4#=k1{_G+uk)#p=kwotU^hP-9MHt>wP5g`Pv9`RZYbg*0~t27i7>^#_bU9Zg3r@Z zF~9c;eyFM*4oHxRnDKZ@kx!(Csl@yRHr(zRlGEfnZe6$Zr~KM%*cbt&xsDFN@4}G& z7`ydMbj(9*aAIG?!+YyWPRmVjDKMN4dE{BtiQe7PwEXj{&BF3Sz_ zDS2^-tKF-lH6~Lj0$mdb2nc|zI#*(E@J5GQESEJ(Th%`Ln^<(ls==q>g0|i*Q2QGs zydfYp3eHp5CxQZBa+%-Ij+u}AQ+o1y9H%nxx^6MU1H*k6g=~K!U!+9;r5Fl5SclJBY=G_Sjm4L=Kf>s2) z72cr#wV@1angI(GSsa4iyKyK%q*A{%WH`$dRESLz%y$f4bwPTNx5nHZ3%uTJC2{D+ z)e)p8FjpOkxm|-5-W{uctaU+Lc%mTAh3xGXCLdN7%8vO9gV2soq9WTk_1(lB&y<7& z3{?KD`sali2OVBaYk9<6$@NI1Gq?-x!1f{zqW#KGSMP7Bv#~YKSFYOpO_@C#>Dxq_ z6oBzujNwCxL9N@*l7DS`{d~1f4^*PwvwY1MIGB=RhO;_n&j1Egf8Eqvg>I$j#lHyC zpfy&Jk!&Be)?gc$>Xv|t{_HhzIJ@8o)AiYo6tsG`*gsTSR8!8o&>7=tx($p zl)d|LM+F)i6vzgc693Tp-)cLma^POUbNk`G#q4`X2Z(UTDs+la@<0GG`hNj8g)2bE zRjb}2&$l)yC@83F{LAZY=}{W}Pja@p5x;v`7oD)MFq`G-FQiQ!qZ|mJt(Qgo^*v&w zh2sQ5T0`scf%*{fWHRXKHNb*`^pmZvOK>pU@uU2}We0Z!cQfddk)dF#l+J|${j!pV z1$FY~q^hKPc~E50uR0XMzP-PF!QjX50&%65?LsDpi(!t&tC&H*ZMrBB-Q_oOjaP=l z0ZvX99!j`ik>J6Z{)fG;>Q_xf=w5u&8X}U&poaw7Qhk=q2p!3;pBJ%)#fVv>ho5^R zv}`;1-(T#;?ARC+72aij!+<4YM?kbt{D#UXG%5)4C`NRFxWf(4ivUn{NLn#eCc{8&(+nM+$Fn*I~5mi`XepJpAF%DPBIT7Rz0u1 z0W0^vSfa!08Sk>ccRfE3JGy>ZCLE$9R8%GUftRZ4$R-|jzC4tNy&)Nnr`Fi4J)+mt z)I6>{K|Gu;7(ZX501f1{t*7(GDG4rn<}Blu+P!N5oYRdU$`P8!P(_Ty^qt-flVb@WD#?H-X}FkyLG+ zGgG?wcs^@c*K2IEy@Bb1B6(nF5qJ~s=xfoQ7LzAciSbyb0>!YSqcdf_qwMLc_n#z( zc-i=-;ZgchB}@go&E`@Q6VpN?D(6*on53DCf-)$aBq43D;tBrwN`hu8V*0+thx7f@ zK>sWypMA?;29ZN#Uz~4pFO7Bnej&5iitd>8T9#-@+!u-$rx9hVf$it;w-3pkO4uLk z6&2((hn>vxfSeQ_>l!*Z$0HK(O1%ysyMbl*!^J20e|RPTQ77v>pm}mRS&dOH$nLXX%bhzg zX2SYp{YITK20*74x_S{EC}WrgJ`ygf^Zt)~|3hxsquxf9tk>D-v~CfB+yR@fSL+{k zCAmvy%o!73X-0`7Cc_6#04aZ?)zy3^pFES-KUs~ioic0Ve;BW$#(rgGy7*(nW3$yd zHQn%#MR{jvkx`#S``x6sn7HK41n1W8?Cj7Pi}xvb7nppipBxzgaMs_iOG!Zy3iMFt z3LQ}uas(nqOul5t+=ltn>doNd-Zl@X?l>=;tufGtNr1!-Y>qVSx#DSBstn%}d(}1r zfm^GU9Ld&@Kj`><8-CJrU6zWkPd3uGF&3~8{)e_Nzl?l&_pGGINgf-09YMrgp%#5! zIc|ryB4oa{o`qqQu}6F3mZlZjQuc0=xAZU|kV56NCgx%GERA`F^NvN@%B&E-%yJ^e zr>DPeBpo@6erIZdFt~a;9?}umP&qyN;6WzV`y9jYEV&WURPMf#{o{NRXG#OO-Hcr< zSz{WN)!t8bKvK(jlqyb(E|5Ueq}V7-k+8G99dOMFFZ1&!iS<$?aiJamARp(VKpP&S zK;V7Z(Zs`WBCP*;Cxvo6UFSy5!NShRFwYy@nwvqnn}oJlch%TjH0~2ykIdWp?YyPn zZ^u1}?L9Ig9M;2>soX;+gYR!)6nK=fl(1n=XF+i=p(bOo}`g~7;B#Q?3vV9Nk6{CkstcAc}FFi z66-(C)m}NBw)e&wY0Z=!c4{P7|2SHb)=1<+mUdUD8)y5vVL6t#p`;YoO;w^@tTuH2 z#+lOM$4G-f_;_O@yBLRD^zDFimRAA~*zO6S3`4Qzo5~AEdt0ZuYkG~|Xzk6srmTt< zjOxuL9TJK;R{8EP59|?G(xN$RSL>XQo(`hf0%VGMCnjP5x!F&)bfq%<6G1}|(7?d= z*E2?tdBlGc!^#DKLWPL$9iAmEQ@uj_J04zdv3!n_rsgjqXug~Mgn6WjsS}lw;fb*V zzmL~wEbc$_Ivfdq{)mY6ljGsh%^Uy`w}AI!Jm3!wVz<@k%1us5=?F9r%%=Q;J?-1| zcj}^qF4Xmq8%m^Qs6o(vxt&V~+)zkP>NDyY!C7fPoCg_ukx?>IYFA<6&9m8U#7~1T zg8LHGpis8_$)#^%(ePR*a?U7j{wvP%$KL6)*Yp;ls3@}|=wz8CF{&hP2X8jHF~-XI z?%r>5Qw~a~@-bhnjbr}NQ8FiE@W8pGCC#XfhmJ7{qM_isudo|7sa}T(d{>%sF@}zq zvhWy z-7}d$j+kS(lJF^p4vtbs-c#=pz%T`EkYy>N)MBOxce6MHkK1*_i!-E*(RQt#qFY5? z{gl2^mrF6d(q&B?P4{HI<6GJcWjNz)k!;vuGp~ruxMy$Gdaq{rzv<*Gi@ATG&|nj* zZo-Edqe-HY&9;qv!6K+)C5ZGlj%x8aN#iFfJ0|GMc4-kL^>(UZ`^yGQDSU0)0fW*K zcIxWfr1X(WP2!a4q4Oeivrc=b;*}et^@nY*^CiA_Ji6T8(QB)VXd^?PJ&ddxOPTpR$VP*1H#gybu_qL57Qe6;&Kn`t(MU!Ljnvs)bCIB zdMd_t#ELdOcYT3QAWVKPNxi2I+X^PP*T8M8`Y)M1$rQJVVa3?}`39D&dA)lT5z|#| zAopqz_jFRPH9jY23cy&;>ujLm;0j8IYk3%`s0u~j9{+nJhfP=r@Y$fV6owWnaocV#x=}oOz|3J*j3Z*3O&7_}Q**GrSL|#}mn` z;zX5m$qnoWS0=t)EbzBCxX?4NVCX$BUW{7TX$oyYR+=2`ld5p<9a?P9 zkyqXHMVzIAmb~((ap%#dJ5F6YTb)pp_IBa2Zl=m_x>q5)!3u?1D?x4Q@`NeHgsW@( zS{*=O=8ZIUK6GMOnU%;_%D~k3&1&||nc40!qJq0cNmOWV;i`52&=q*UVWY@3{wnco z7xe`Pf92!ItQ5xv2>-v>0%mV+KVe_>{}xPx&Y- zuCtmFKWhk*#V?-92_8F9_v8>ZPj;^RG4{^S&UEtfl3(2OZEe4KCui4n z5+xO#%_Vr0rv(+EN;om7#8J8DFV(Bbd0OJ6Oe!4eA~v((_6KPh55GI3!wJBUPC>)U z8*h60GqW0un9b(PFTR2cPkxg(X=gV@TdIS{iiAamz|7Wr7NEzgVsh_Y>)$^{NzCT! zIun}POi7XxnH8gb)KRD^b=8pqaRZKLBc8|lK z67enL+sdN`2pDYGZJ(WCK_&F7)SvJ4OR3@>j*omi-o?zm=O*88W1tZ);nN=krlztb zU*{cvk*9~RcBPaa5$vQn@~2(o2pQt=u*F|# zOzqjM4QVwP;^ol!#}q8tsT5)&LkMPPaGuTDMs&&2tSJs^%?Sl+LtyA>A&Va~J8_V9Z{?p)dT3)*(&THoL$Z}9{jojPVa^06Qsina++B6$^7-z-K$sal5FPEe zhrx2wLkz!ClZAg{R(Y;7^i0~O)o~!Y>*o`vCaC+a_$sHy93@jc?jN=(%yZcO46D(Q zoKj!=`d3Cr_MLV9*|2t1*mZcaQacQo)2Lfok`2)b4G3XjVTZ@5T#EpXi;M%~~AG zQYj4V%1?Pql`f2yG=F>hD}(qNFq2L_k9JAunXL)asWhxx048qH_IooymrNLIgJvdA zNVO_{>1Lk4QPN2e$Fp4c%e_+KPu$|+lLEmfaWshIw_2TPZK>_TgYUT#;nU#2!~7y} z@4L*>g9gWN=dH;`peKQHZl)CA*LSTPqu#D>6lfYxzuFeIdMI|$oO>;??sICIbwhq<79MlMbrv2dWCBK z6cYnackzVoNgYn3l7*Kxb+smUW$gN;N5;vBq^FPA1+NO8OK zXCoxexw;W4Me>H>#o?8)+2$h?^^P0xk)>YE79&8DFnU`xtO-gGlYw%aYr4USN!>@!*zWT;?#HmU|bl`;5g8@$YR2GbeGL}Z~o+Zp($FAC{rcWo%_@dt?A zPaJMY|JC}3roSb*xzJtPN<{FW@v>o$-Q6h`7&ubnD;eACp=LeVVm>uUD0JR!w%Q~Z z>)iuQ#I~b+$=f^O(!T~lvmH1HK$O`MlCd^v?Z;h2S>?<$$f-9RD`Pi+|_q-QKAMc-hI=}ujlzY!5 z|Kd8llh353Ut4W7P$WA!;wUB zo=ht}G2rQ8Z4P}OCSS;~{JC;Z zwOK{-HJ~ml0P3>Tk%-0`8ALd%Fr|r-F-`lx+)w$U8=|AJP;bJx z3G1q!`iI8+#=%UxhcQQdop^)6VYEziNbipp-1+-_pGrXg8E5DR-I6+)toki_tiFw8S?k2f zdMg2@f0x-r8QoWCGHU%^2 z#TrVX;$a0Gd0q=xgAf02Zb&A}6Ig)JA|=Q8&`D;{C1irax7@)j7_wE^ldyL~9=h;G z5-B?jG)76CD<{tEn`-nmFw8mEs?fx4t`O>jG>2o|&2#bDLw1mm3hn|>j-z0uV+2Mc zi)h0~8|Tk<38mmDH4P|;m>zqj8I~T%3LV{Xp8dxz&XQpQ+cMx-T6p_XRLkLJ5(9!| z0z^>KQiVMB=?N^>P#f{`)rb7*r%mo=2EPZZ1K@mkk|v01^8avLo_kP&Dm{>(Z&}(4 zrdLU8iGGM$p8tagKDmJcM!;(y8cC2Zpt@S{qRxiP^)+#Vzy-$f#?i#O&hzwd9?&CB za%EgqzK{Y~vQo10>wevh`@L55qFxE47lwz(%4@pMv#ITp8vG~zA zYCDJ2uEz>=XVf9GJY-e4oV+pNR)n_Vj9N&lH)7NUqdv5o{Gv>$EE5A?_o2773eG2p zgFlyg-P9U?DRmKF$a5vFmu4i?!IC)#%d;85}HFrvRBXLY{8plI!UWQqJqJhEow^DgKvV$eAi%)DJa!`}VGt_9fR?52>tm5{U#NQ) z*?I(E9_#ex2K(17#}4$F|C#I3Q-j;LAzD^%J|W$-yGET~L|2AXd_(kfuG?<(fN(W( z(iRu=chYyo=b5b+!;`0D)dmZ!)2M*q1zhRpu*P0JyIvET59g}2%tZui=cXKH6ukLg z99MF8tDE5z->4nq@z%N$Y`3z64z&ph6~_Xx&_{zz9i-&O28Gqk$W-*ao;VP&SYukMri_G86~XCpLfiSS zsC8qgbvtadCf?gWBR3$Uq&)8 z;-dr(LEabmIe^G0gD+vU^Z7Gh(Rqh}Xh4aXJl7M*O&tmsQtA(pL5J58@iZZl-9BAS zC#$rDZ=`62w;ZiJZb*v|=#AH#zwJ_?0-Q1e4?Dy$OvazuU9)rDAmF}q-iW$ImwQo` z$MJnTg0U<68fr~Ry)GH;V5IVGh=s~hwrr=7lmfthRTVu=sXN{D3Y8YgXeFI|eg+MS zR9#&#$pWbFlDo1Y7m+((Z{Yw_f`m%FHmCPz$BZ6MI=XYlE>p;;8Yy{s(~sAOQdOGq zeC*&(pHhiVhMbBV|ImU0QozWvy}g|mvbLknC(ZW{dYateZ(>Ch0>AYAezG?kymqh? z^g<#V9l4fo2riV1w+B_(@Ml|b1B851eiD3wel;P33Fbe!g{FIBAIcp4EooG?wkF{q zUWOB`!n8KrKfIslCP%EVz~aPlIih||9d#A*kam(VP?!8 zs7tw9E!)$oz0tY7i*j&rBI7?~lil)*d*)+B+;mTd=(fR#U%_9|&N3;-Vjb%A&7r@X z!{+f3=#a!>8bi|+yk*bmLfR=}mbZx-L#Hw!8Dy~oLfM0FIK-U~eJm5}AXs$-J>Wi; zBjB^{P!w@tQTAzm^{&0wI>M|WuK4rP)_yIE6WBe1z_fAx{%yTRD8H&{rsx~psI`)fX&?YXKsg;IquA7Q&K`d$A!x}@%Z6AZ{)B>GTL|dSc=uFz5jv36lgUMW%g;0?x-qvYc9{JvkYufA&wZ>oP-8xGz4x=^HCjTDj=q1$QK zwn`IapA|a)=qj4w60=;mPUG7|AIW01U@Im?k8r|S5H3Ahd$=K=%us;I`X;PMq2Ljc z=(F}QdrG#KrPT5^*Q}2$exfY4F|V#!;$3FG+754&}jmw@p({pir& za|k_rd?w6HUgS5rsnfyjLl^g7s)2zrj+j%3_#DUJ<5ZIj)QL|-rzBiZKL)bFt=eoR zGmL{@9F~D`Wzqe|vSel{3Ld;M+WpoYSsrHRw`P#Ml)Rp3Un3K=)`cl@I{A}K058prp zJr7ga7ZhS2pOP`uwB;jerv74I@FfdGZi`%Pc1X)8n2hK|jnIB3uVo|lrQ9B}#zy;l zK}HxL zFL}!P9o=yY>-0NN5d4P_04oaM8$@CG_wPO?njy=_1KpE;o+^!_t1CGZ)9>ZXb&< zpR8+C$#4aRN2+18W=mbF@v)-e=dJs(FEC0>grL758~BsK7=fX--ZPcrr1zLzKaW~? zuQoQDq}nHI^I4j1L0Ah>eqk% zhg5zsJXPc+|0hoU-Aq_|lVdLZKlF5c>YdR0379xql9H&@6@QEF%2OO%%K8CV7=-ZC9iL}n+Dcp2 zM$5;=_#{w!Xp_WCk^LD;MJpx$9wA2J87=+)9Fh-gEtPWKT8kk2@00j_n|+MqR5~0j01mwn@NrqYi;>s zwzc|kHCBYz)jw&@MgHwLE^Z-aE_1e-20}V4*Ni=gSnOAk4?;NYlP0k)Eow z)l$Z_emhd(4&rg`Q-URd6cbB}+}5t}_~;Aq;r3=rv=k{1Jtslh<^rm!WLQO zb5dJAl~UCbmjK>IiXS}^i~kVB-K`=U#OK1PvWAox&LDJ~J}HW?FW;FQ{HwxUT(jwq2_EmvyX5St>5ju5C)D`C7rL8E?tGoA zas~J*?ux4K$|Kt9Pn>wf3{VCLtIp+Z$+96K31|2v5Nf>tBHp(2s=qxtI@pR=&CU1x zGMcC38sqH3lc4+bQFh213deNpe83gFXdUi|>L=O8noY&@YU-Q9&M%m(_8enifgRPS zDxE84#MfCI5HH?&50rneeZu1r1{q?~_xX$KN;4b}$BxbI1@EQDMw@I`)LjS5Dp5~# zaQ(fQQP&)H7KB9L-2?*0=J^JSVZa<4Zo_;-wdcXJ(psglLMza_PwKV4vTtPpPSc-b z_0Y9?A^5S1s(JchAh3DkJp}(oxP7_{iddoP#p9Nx#MM}ue$Tz*$Bl9n+P2)5V-C~R z2MU!KittIefgzEd6{zrmoS_4gFsMFN;Py16s#5rS?EzDWI#_HTRkkVXmib~+7!}PX zQU3P$^$1*=pe?+SK4NK>FYw~R3etc6f)>P_s}6JEC`K_Pr|T*%vRl_0iq| zp0IC;WEBBa%X8+vk}jtUP*bEi3A$t@g8|t0R@*(Jaf7vw$M_(8|LTo;%&(aKDLPZo zzbvpZPi*#uMN)Pe_V?58TLePsBDll+mHQPev|h1x4Dt;z8jl~Zsh$*`;F0>o3sl)m zag3PodJt}}+3ym*OLtNj4eywHm;E1a5%a+UOlG_FIrHKLci@x331nfqZyY_CHo~^Q zFOHMT3fW<$pkzEWq$v?A#<=lyIPv@Astvr5)I_7pzk|Mf_t5Ga7d()j3jN&;Ox)>N z(e0*a=W(Aa$l$}Tan@dDiZ1r4dFRR>+<5)E`3Zkye4SxHrJB8he(e^M=jSfw#vLy# zXXG5UPu}`2fjE@SXHE%ZjQ&E|z~;Yzf{kjb>(F3|nH)DgugBj%0S)^%uEP?>5Z|HY z{tnkae!Db*fDDc;+)5iwSRe2lB-n`fWPJ9?{{7l=Ih1kpg-~aTJk>WASU82a`A-4m zH{hY5Js9JkWwr1xWA^RdH~BHE_4*}KOH48)%h6ZxaUzH=VVe9X{`G|T&(>zJKnF-p z-0I%#Pg`KFuSJjT$6vnXr}{ zU0Hz*B1*wvwZ)DP4u_S<#w$Xqz=7UR<11~3TcOe1KY60QUZC7%3_l!b2-;r#R6WYj zPp!~})Ms^Oic)wYKVgE1lhC6YmA!S{G3ft|ra2!0NlTHel#1Uol)(eC!k~I+nsr(b z3WgThb@T}e#STvutJ|X2s0WF1m#l_a4LeBbkqWPJjj&`#tLvIbFAlGKm@eRAw?;+4 zgEH8=LBF}SMUxc*j@9%JNEJ(`U(q?UpkRgJ6 z*OlRjqX~F7=@iw8;e{{cesx8&x{LkH_amjLgW4;eESh+6)@{KVNV=osa;M+4;kK@S zfgtk8Q-#5*3g61{g+K~QtiIT%zc<5GBRg5jGKuyHo2Kz|7+S7dLO+Zz9 zp{=<5Zhgx(=<7e#S1IaAVD`0{kdr2J!(9kG$%#hnN?Ub~eO_Y0!-Y+oq`bUQA$Lcg zc|-_AXi!FaK8SyF2t@3v`mJTG(Rzxmcp36K;?2^)12or-|0Kbl9-0h8Aj?-BusN7d z=~5IK4-WZe>W8Ol>Dj1Z@_AjDR< z=#wWNM{TBXM4xICa%kB$)eu{Zr6oQ2erY}OO=~Mik6uGJIf`_F&J3BwuRX$^kNuLCOrL)Eh-70 z14mf9D4zs^@qw)Qz;X_K0z0vyVjZ(@hF1VHHA_fa1{Ssj40L!&v&@2m2fuWl{0`h^ z6$$BNXDN@b@yY8TjnAk@teMuEuCcb03=U5b>Q0YS>=xDE7%Er~h}ZN_1QN(^!v}|LD#hwMs;52@ z6vxApv_#BaNLBJ*m1G5pvk!@u-!(g)Q5R_vmseJl%igdky6*X^vR1n9vknB8NgpSr z>wglqvcVlqHv}iIga_e&Z0So)ZI89z&a%w{w&vl;Xi9Fq$-RtVlnvSUiCyj%TwjdZ zP&>qrRlviw-0M|HH(*e)ZPZTsP z<@v?Dh00|uY3px^4IY$zfUZ9%7;Gq6)`N;YUxto&;JQt?{W?jwOpqiQ+%xz0(iBQ) zadUyp>fK_iF72dEOj)ez+IV`5)RU&ygTHJ0){((%dB*f!X?=m5){t;Mvl8J${!4;x z+s%jIbi0-9n$r(TNa>>Z7tOfFdw-G6vxdD4y@s(;=aWW>X6rYS!zL=GmSjJ=Ig+>*B#_mc|O@-tm&rwm#U z8!!L&bcU?2teE~Y-Q5!QrbP8khMbb7|J_phe{lAeL2-R?w<1k9vKw*$}>oM&NU!PJ0M=neB1Gt*?*bZS$FHSaLQxU;1)=;o_Ow z1|A}6J%GZAtX1dv?s;eVxQHOx6cFAK$Y-zD9h19@Qul3?h77vH!!|z4G_x5t(-IxuXBg_7*2vFbGc+?}U z*Wz$q1f{gp$SVxt>@dboK}f%~lcqZiWkk`gYX633zzRy#nSsM+>8%Tf;&OUo?J)cJ zKR&mvk3;?`zO|8yutbOA`==@}5f!5(&SD$&@=hui$N#t6iUnW$TIy#*x!Spv2*DM?5VHEZ_A`-SJhVy;N{&3B4-pu6kb zxY@?)-)nUBN?qfVuC&#%#GNd5o&?5s*hI^KYS|^Fh=1&Mz!} z1`kAX>WcBa)5rrPgAbDX!=fgMZ`plPL`+9>3W90L;b3Zs^hO98d#M_q)Iel`I8I$@ z13#8VuKUhdSR)}|6u(3SX7+_>Mkt`7KT}-fZw#diNE}~Js_32ZP4-Db>VHwBVzZ=8 zk?xYK`WSZ2o~O0!xh0DLr5h}D*X_w0&3(D~V0h13@gyy(u)6e`Td~}V@ybVJOPe#+ zpQ@&ER3IcClP~TcYjcZ+F7TIpTtTE2x*)a8H5)Ej!V|t0GV*!F+z)YSd)NhSvAONw z&kg7lJuvMN(Qr8-;}7Q&vx`a_-DrN9eu@;59Zn=Cb+s9y47_@Y{GLQ%UE4bqmqO8& z$3V_ea=o$!a&8-$hz+b~Fw6FWlx*UIug-hqu6oSl2*3%>ykc?maA- zubZr-CfW01+tFK9-7KC5evEt6d3?$N3P~tj;K%ob2T3C|6-`$Oyxs7^bhFW=qL_)F zwEU14x9ljVP-p91S1w-SjN=q!!tf4!BxK~+Au2N?`E}x z`s6A?XOC0>dv^PK>SX^StTROjdPTOx!lwG+80%pgzywju;UuS~hP33OY6`UNk}{A| zEctVD1=F?;Ow2)2;U}m39j~DPW2egB+*}^JE1{xMEo}S76jz95V1fVoVK`neOZA^j6tz!cHOFPEt&Mtf>o#laK**o^cJBDz6YmqHM0p?! zNe)OzH%^vwe_De?;qXrAu&{J`E#&@_sxZd3B=BF-|L1S8QA6AQr%Zh%Hd8D7|M?KN zP}Z63-u5;iv%%Hm+bRNKqw9wU>C%P|+bI!=^10;n?BTpjfwvYX79J-f8VZVmhRpxo zk}_zymsy3!cKyfBfXw#c;q>$E5ufumX=P<47WVsnf`}$9mUmmHI$t&W-mtQ0`CR=@ zM!`y#4@J?_Z`dB${}i+v(a&s|$X^=tFOMXmz>%C67mMcbdi6E@*UOqM1=EoR)X4UWNuk+37(1tXO}mH}t+^^a`#`hZu}DpTasO(B4A z8P_0%^Vc59gz|w;4lzBO%O9OpF%}KL88>uXphZv>pVMBlKzOFXd<+mqO&J>%frLe1 zParK;&{kxSi3u5lmcQ~nKG(@?W2;3A9{TsyB*Xd`fq^LAZi@%ZAw5IMzWCaidkHTK zDl?gQ^V6XbH_73FH5_FxtBL)FeYr0G_iN`Cjt>DZR7@EZlc8&ad>=E2O|5AOq@oaf zvOq~=euu1k(86&RJmbq*!2I6>lBj)_>qn_-8Cv!RRI8bMDj}f_c0@2#R8-%)V_D~~ ztgcyvc}g?qYpu&oE);z2d|*g;{HclCXfaefuhR^0VCO$U%R(et~3mK^OhS)Bzvzs zP9Lw<-aKf~5KPzM@f(<_$U`&%Lo4N15l4?M zolQcCb_$Pz3pw+QMX0vQHXmOek&D;{M(AKIb-0Q|-B{%)I4Jk?D7QmsrFB{;sDGX$ z(x6|ihab*~IN_+JCKj+gBwX3=_~82gToW3#Y0v$HB-UWK5^a+C>JDfY3-0%)wDs#= zC-4YkcvM! z=*{a8JJE%HbU?Kw8Sdiw{!dIZQGg-y;kq2x-GwoWyTH2p+(DK+DbsH^qQ_6>;b5^Q zXa7i_M5_jmI9{#N{Ct&5Qf5mUOA45w4wuCdIJ>)(^71ZgEEX;SwoAK*@}<++Q{=Ls zBOF}ROu!IR#%agV)vU)fl0Z^yGk2J-OZ^?ZSv6^9;pu4?3#b2-v#=;3MVAjtc)nJ< z0L%*;tcdn%zR+gU{|I~iruf<8(daKBMds6vppmU1JH5)!adpNITe6wf+VPEv^7eG= z!rMZgtlJ%ZUiimFU0Ic>mwBg!0@SkJ@K)xuic>FIthgm7nI3^0S&5EhOpjE<*Z3Wc zVoVMk=AVI5+H;S=%^j&n*T0IniYB9k)p0n?cAX*iPk;{#LMwlg|JB3sYwn639(q#y zY`FxN=?o!R-?vE+no63lq5$jZL_6n$w)6WxP1oq9ftHS7G2N^aw?nojeF}`){B2Wr zW%AA-T%H?ehvNh2tjOTgo(L!I*@ zOp{tjC~tmVqw#KSwl$02BU*RW`26NzLQ-0uJgZ8cOaA~e1+pZ@^XP2qohpU{XDEJ3>ZR>J2eI+EI zY+v?i;rQw-{%nu(i+fjS6|Q12WRgHb{&=69*zU2ZP83$h$;OH_s>jj{ZJfR_L>>Xi zv9QB|K5ickkEZ@!ErD>(8Uclb5r@fSk~tA z1oYoOb8)4{%#svvf(rV;#K=PETF5r&6U^0ZT!gosw=*#{)&Af8FAdvlP8-vg+xvTt zG-MPM%YXAWWdJ{5ll9;o{lvzmt-W2JnqW-O#-NwH^}&I1&t&8sibyXnGA78wUuZ*R zR8(}}-;kSb>m>x(_;`)Ay2R9ByIWgMjTCbRMaG0|%<9ks$I1NhYzD$jasp$taQ|V7 zmplg84{{_JaL{koE(4+@c$G}gysSCl-9Ptg1JCIm=&4ZQJmz&IUH6S0T1G{0@2$=S70V0D40) z@FkFdW8~iIW2t-eYx~4X@vBawC&%^5aN)5b9hz0U&_>z&SDBcDw1;W98~@xh=;Rn} zW&M)AhAjpL{;_lkZM;o0LiyERg_{jK!j;xIlAskC)k?d3;OYTsL#5Bmmv-$Z*9-gU zFFKd)n=`=mqf$-9#NCg`^hs}0xaFwGjpbCU@fWqclikd`##Z0N9s8lmjO2fB1_nW% zO?Fs7CA|42`L}Q>t)1G=&Je-Sz$K+`^OVlg0t(4MD3Dk&p?vZ$i6j%}l|0*lTGZm~q6Wi;&E znEh;#%yy|-O!Azc^O!xi#nAV5B3b##b4kiP1nKVi?c7UC^};-S7!SfEOMV6*RgI5NRco?}@BMxUZoQaFo?`o+`xpb9G{O!Fq| z+2lNdR((tGKdb=$E|UT)lSq#%vM5;YT!kK)P&`ysm+-rV>-UnX@@1EMEP+MPAnD_` zmwqbufA5T2=Ut+51`yD2=6PHFFIB^*d5@!Ma(An{$JfCWopL7IhbsdHB7T{1A|ZA| zUl>1J@mcO{2S2j(MbWC~r6x8Qj@k%nO5`8$I6*&6jmNupE=NG|rSKu&+mdVq|XXtCNH-g$hKTlabhJ^r&UE%dD#ukNul_b+eiinvwKM`E(D8m`4*NBlgKJF1=9LYtFdrJ>n|FXI51|~;W!m$own?!ESMKeEl?EFW8!J37JI#P*~VYfr!H%%6pBM-c*ks) zPLDsHvi5y_yAas%9gI1>z(vjG03!o14)ERen+NlI zJS#IRugw+kN-rY6b&*!ZYRAVON~Vgb)TzW^W@g?uw*o3z9Jm<+?v7P+^Tvhq=vQ`$ zkE72+Yk%Q~OgdinX)hx+bf@+M4d zU14m5hW1B^u~!mDVPmDiN1U61Y*5s9j@~|}8E}k7k0-4flc7%@>+^P9O0 z9q6;gc=2sT{z9(Y1$W_J zlh($U)*w4~#9HAFY_IV^AGJD{NwMMg!cVbeG5`E9+aJ%ZoGTPwddMFxme8ED+Fb8V z!|c|dyUx3W^0LQd~z$k~bY5 zcp)!_roK@ix#-VU>9@`Oe2n=RwIUR)c8IAKAG0?#1#@f>i&jJDLuQAP^C)foS?JbV}qpxCqyiD z?z42qJXd$d$;u2EI}1YeUnN^LKK^FAb%?9+0Iw%alAtNBKr@Joyvl}pZgaNSO7M3X zjCq+&#CZ-F>OgTOV&J zmiBhA)dGblR3+O*EZuI#4wcwMuf0I74hR9%9X>CCf;!uU>+0>%slC_88HE-b-6%=5 z#oohnT>!i=nazdM-S>MujbUuG?t^!>HzhAmh^6O=yq3AP>+?fka5%ji`)jJ^x4+Q^ za3AlO!%XXTSq|hW45IU9tm{FKw5jyw5XT;RcL!t);YcuAE$II4&z!A~WP1eo)lC!?3Pcw;Q`@jhu6N^xCu59Y~Zz-aSOa0`> znfmVD9%Bq7=`tV2zV-H1K9K&<>GePq#X?3Etbo8pdfA0{O6Mz$qB>w~v?n*E4HVZu zIUv!%0f%&Pv6**V$pfExln4so$dKzkya5hW9z}gB7go{CLOLGfq`8^;^)1YK9gaXDT?P(csJs36QD*n#|cJVA(#QC1*udRnecHp(&{c8$H&(#FGfv z>MB5Z!)QoO#*#5zqDs|{B#RwNM<(R9JF^lPCfvG{mtk7j)e?hp#M2>2O2Sv!h@92k_EqGJ+$%C(@jp5eT|;O80#JG zHC^=b;mj7l;Na6IfA*TiPRz6TNM`~xxBb`CkJ2YZzm?bU{QpIHLb0UAjgk{#%de3)IZkw&!3Nrv*v5`GtwA>wts(rUyq9-6A`(r4}@v$`$j4vlCa$A z#pm~c1O#NtoVI$JtT&@WVISA9s=NEMZ!b=Z+x)K4PJEOfB=Tz(q_E~WkM9H}U>^lU`15&t8i-cvdXHgBb5jRFjZ_2Jnl{3nA ziyR#uZGgnskci^n?TcM}$^iby20`^@-fDaugu6^XSk#m>NtGCAm@TTsycga8 zBgHn&|8u^|J%6y}&P#>QSEble(cny;`4iZj?CTph9*&$m;pOZGAm_YLA(khwe+;Pn zkajP69X*-!J=Z;(jLFt<{G`bf(@LAk0GVkl(WmPMb;8PgrxA^a&#TmK1vd`r&Ww#k zY82$*;Sm?7&;Edb1U`l?Yz*ohxiN0C`HeA+D-G#E>{;s?E#hGa8Z}yR9Ui9`xVS&` zYPb(7HRypCnF8>3IY=~f2r@Ek7dJN!`I*tt?NbnrI>-@{-~G%!-AZ;a-&fH#$;TfP zJ}kXm`HP(g`oU&XHO}zQ0^x6~BL(O=&rd{#7Ov3K%*@MG_L;C`>O1>qu>se8Y!^4& zWeSADI|AfPt1VnC`qd#paAIN(vG%dxPq{`43MWOF)1i<=0(;3L5#r=;k1d7~H6 z{&xlgP=Zf85hQJa@7Xp!rQ&12&%Dmtz_#zhRt}byao+jtShd6bWPF-vZSqDgrG2Sb z%;Njj7m`C+ku#l|m?Cx*TOl@{+H~d@5ILq~5#}7+<6Ye(1ZkZsCR6}Y{k2WHZnEo} z_S)ZKaxmG5_!I$Q2q$Acpwh9Zu><%YU&bV<2n`7Ze8vaM^=9e1mWPwiNO97ka-~I9 zGe$sz$}m=ikRbi``JSSh8pikUVr3Re`GRAB!VYe|T!&7?@4kIIbCguzl&eubi-nG^ z8uNuozus!b5rF^1Nf(o`(ngMMCLs~=$2#@C58wHqo&s;f>9Ckuxc%*7cu>a@5^cFc zSoj&{PeqqOh#t+KlL*6?p4x2eh{NNmW^*8KP>1mdDOk-*9A0Kv3OEo zt(NoEO3){&G1i57+=c;cVGzv{ko74xvTYvWpw{(fMwnnDCsDNV2M0-2r8rBE(ayT; zCqi^p=l%ARTbtaLxO=ZR-1$!Ii(fjHU3d66O(BXN;G&kwp|z05OO<@z^Ocpic>;u)Zavv{_B4-UA~vM5ct8Psm@^y`K2EbnbIx7G+L|y( zL<+_K(wx;|%uA_U{Fm^x9Qee?O1EEIVSkEa3O}23Vh3Q+hH*2#lo6Zm=x!1UBsBHb zFx?-FXo}1gxheNc_%FXc>ofA1BOUf|>9cLOfBmO{BFSv~HLiE+BW|ny`B6U=MRD=r zeiyqOPq1EOsRwte^~eKt!O>wO!4^2!q7o}Lu9~#*SNz#8arw98;os`Z!w2K-`6lCT zr-3b&$aZtC+oh8@%wsm}rla11gB08Vk@wmw`B+Y%(kH8E1ZWKUuch~*CbZ&M7Nh1C zA``0*IM6)g5pjh9DR6>@XLO@V2fOu#{@rI*4MV%^MWVO)62}WYc;AgKMx~Hlcv$Kj z4#*#jI2<`b8B-IEdPkaT5XrhwnJDP@N-$(&I;WLmaGalf$GaE6QZ;;j@vz)k%O8L# z@0+5#@J!dc+&>a_oaPghc8SwSwfO=oUa(6vmY0`w)1x0CZD?sY;!d!*^k$Ft$7DP$57W3W5lZX3St&8NnBEbOem zq9qnF=|Gp4>(kZc{?SqK(rG&;(SJqufKLeMDcX3=j?gM$+I?w0y*r*=W1lEd&HHCQ zyg^#u##TAc33VUy*4ixocF${KIq;YrJdWaddjtI`n^}qO`VNt@dJ2kv-hKPH7P)%m zPT-lGH;mJ?4|We;wXE)AEo}1Lme1zFBsQNHKB)A$uFS$PcHg2_UD(m1N%}-JMwUeB zsFIZ?{z7M9b~oudx9wNtNrNtD%`V0}lmDZhRZT z!tea2OEs1o;i3Bk#9j6_s7vV~xWY{$C|Ro2#?2vf^}|VsQeiJ$BF~Edd{0)|F9ft| zH^rkF6EkR7O5}xt=B!d%K{T;v5<|bi1kXq=T5V1Pu~g^G<~ac)@o<>&Qp2}R|CT(t z63x#g?@}iruxXOb>Dth8pwV3B(ZOWnMNu+?Lqrp*BOx$?ZAMtibDm7gzcY5DeE0aO zH;WU_wTaP4fi({{&9eO+I!%Bj@Qq8NlbiIUUpSq~aQG(!9e}tA2~&UBEdfJ^-kAyN z*9YAV2{S~Kq)%I&B;?G#aDMw+&G*A+y))A>A|Z&>!4J7KUmbsK(SN3cXvDc@Jyq7J zDBSX|cAg*iQrwya7!$JHJPZ@rS)#p3o6Qg*w+u&r(_19i z&=<)S4I+cgwV2=Kc0f_QAq_l=aigY9FMgPHM>Hy zOW;7iA=)pq^TBxMmWm^Ies`wHa3XNP<|4npYCziU)hNKrfoi9j59yyU*z3XwU{eht zo+oaCe`|3=CEJjmRf)7%YH_fRX&u>GNQV*|KGD3*?0m-ZouiH6m{HCOcUmNqR?L)2 z9&YRPy4ai{FlIR#0^eBi@nw?=asCW&C~bWvZMl%a;`07w1J)Yk69&@SWKS1xP0S=P z9PM@`2(CzhrDOHJHZ>=hCfb@Ug;oh}@nSVCmZ5Pa=heL(%SkPq&$p>sd0qea|i`*tQIz!%^^P7;SFc3&uz+mz&|l)V{B&Mv$oKvtKLN?MzA-3p$xK-@`t*J6(dgjTVlI=HGh44{wHc@52&{>Ch9$#1vHIH4){|U*NPB-~N;|1RnVA zHpYs`W^hAZX^%8`z0zUXV$(Mxr0GQ^-U{$wy297!E}c=_PM=dQ?sCVjcX>-<0cB&M zw&2)cf2?HcJthJX50Wq8wVIG98AP(f& zut?hC@c3IkJqc^l;MICBm-~r+V#HX^Y`2%oS*CH&|ja=A~8U`%stR(c>X!J z*ug8!Nw(e{JcX#08Nw;5I?e{rAGqGvw2I&qZMCT%#?ehZ*i#>S^r?=v>)l3$Ags?V z4T5skLb177UK1T;v5*OS1DAl|4VICZdzI--l7ru< z`_SbM5;n@@#9tK0C+JNMIz+HxV3C9y-ElGXR1&8xu1-s^PMQd>EpJ+cCE$_>7xtv| zEoW>>wESbB2+$wjV^{@39t`87-GFcH@88E2^(l@ooR24)lV`pl=WcLE^aQ*(A1a|h zz_5dewma{KNtqDnyC5YhRh!vENViRl*(oWb$y=H87*YO$k?Ya~(#V(po|;1hi_l}L zJSw(0U8gD|MOI1mOw}&9oI0I|qe1$qSPVNUYtv~9&DN?^h>bmu$R6MGp7P(+ z$={zaKK=lCBGr1ci?4{bXzrxV^#B1-QlScsHhhoo_1@kguO3lO1>SDp_@$bOCZ$s27c7jOjEsz$S~61#xN}ek4{?Vbz$}sV zu^Kx_I&rG=pYoKxvzdy078+GfIC#5;v|nz@W0<|h!h@pMLKLe+vAl_{Nsoa<%h_>! zaLQ|`*ca72yLcAz?`(xma)4!T9Z_k(aB?~Ot<-Y1JQMfBUA>*%10B#kjZqVFC*Haq zaaUdBu;&`B1*uQcnuf8)=3yaIcaqMe@sa-E-97T~1+QYyEZB_>mZ?s|F(B32SY z$db~G{S}9mU#GaWeUozYSy&)is(B#R09*h z1`#+NXHFrsmetiI0*Huw$Bk@Y)Rg(YY_||y2Unz(c0TNw?=pQfzpJg%ggc$6n?hp!{?hSr$R2oC&S7USDHxlF7|!41_hg)zGWs-f_w zuWSVh`3fHs61=Hz!H_c|nj`E;$H*}A;|)9&2=t{3B!;(vd$@B$1+6fs`_etvwoSB% zMGd_S<=lkLv<`VS9svBFG-L0Zh2Pl1fuUHhNT}HU>c@e9-Mlx1{j{i2I5C>NE$Tso z$x81prj1u7ryP+iO&UkNeTd^RIgWInnKU*3Qu@LyutzwE8Q)N?zle_%52egQb>i&H5cnt;5 zQ$n@nvM>#x3&~?~V%8CNdn5-AhC_c7f+(1a!8(8Q`*V2xvbcu*d*V)-p(-dBJ3M^ajRzL6)Uit0MBcN2lBUXjvb#$#=!~w7;{=8|%uL1%Bd*|m< zz(hr-#lG^y>9%a@N4VDT#zq&91<)G%dVO_OXf~q6+u2sALJyCMTJBb^G&nezn3iVI z9yxV3?+G|6O!i>*&P_|lHXx31%XaVTt1Oo|J;KGI#C{Le&a9mbC{m=~T2= zcb*a{IRkNXuTM8AJh*Hp46eBnxDWPJP~%lG;EH0zI7?1c`dZ*5pk#?nGi@0}c0j|j z{US`pg~k{zTqUJ3GB*AlFi6Io{U9(HAD1ZRYEkX-&04g&eJdy*wnLbVKPZ9`SwX2H9eu0d*~XTs<*gEJTTt_ji7(+C6}5CYLCtpy0xe``CGCW;%Z` zd7ua$@55`SPPkjPvwsN`E;jN{H_T9ncUp(ZI;iVR^Ylsx{(3itXgyCRDVe!NZH-2+ zGCML}Dr!NMd*)%l>lyqOB92saf6f9&?p5SS#=9^uv*h0sZx$BaW%9~^^=t$5*w`>+0hRJ6 znm?AW(k6QloV2trV;IcwN;eaDz9TPhJH}!g3*>4<)_7#_?Oe)J;1GuI(k+AIAcU=z z%sj-tC4rL&L)(*(Hpad?2`83_7c7)$V$~Ju*SC~Bmo>5tYcZ_DT$J|4zS1T%@}>RWp>P`P?Icoh?aTCJ!`t%9H4B4e?j_NfN^6nsl$gZ}Icq2_8 zvyTMES7#{Z5H_^*PLKd+iMxAK=|;%;>ujy1aym$FY}qqSl>tgr^!)gTUTI+k>sA{R zcnvoR6rRaPAn$V3L`~pMd)lYdrSL%UuN&C-pvzQ+@sboJ#BMoIG&wmrf_(k3rZzaV zmJ|;9Y_m36qLF4|8F>Yk4FX1DLkTGq2F72wlPt;hG~1ULL`(KKWhS zi_aT;lb88!Gwl)c;@?$OQ{ZpiWWe?>dXVzoz9fMwHaCreBObyio@sY}WGG!0kN+FM z#FWOYXz4~8SKJ7BEuTC{%P2!Tb{w+jG{tfNRX=E~*5-^0WC~pRf$4^hM>bAs$KR+M zzz5LuPHdxyje?^?6Gg~e51+DdFwQ@GGCkiJ(Ze6k_Qp{b9yX3?+QKCG#RG~*{7#+Yb-9nfI0oy+SIl9{O!J3Qc!p%p>!I zf7pR}Ckztqs8vCYivhH#1#SNGGo=E>|HN?L_bpxv$Rx^fX-)1oGU81vxRQC(4rEh)7v9S*^WjXtntA z63Cx99cr=KDQ#X%IY9d9rrH82pdlC|FKjze#nkgtLG=_pK8J_=7U^iSM>u!Qxutf= z%`G=hsxaAsLBwJ(`qA6%8iq3rI&=#P@Vj;A@LtNJ;`Qod_57OhqbHG=);MJ+@T6j)6Jks?HsPaJVb2I|Rh~ zS5PEDEzlBUs%6T$+w(oWt&E0o!C5I)R<0Er{AJy}=`!*NvwCMNg00Ul;S zpcg|DVKJZ6K93Rivh|SLtzzL58qXnx{U}|AlXo4IYy=K6bo8e-{il~=MUTBTMD~=g zzv|ds4q?{?_%fuF6&zg&n-v&MR;;!ftuKnJUrcGCq4C$>L*P$GZS z)6Bnl+7mg41b=tZo-$kh71q^q>=uoP$lCslPIxTi(og*5T@3;*wIt7caTn5`|CVZT z;}**Y8ovut9R5nXILc7oVAXhSkH9feman0KdGGXe0uohF($^PXQ}cetxmv60Jj(#n zHfJDbcUd8@W-r)bvRSFTyt*1GWlSTZ1l)Eje9>8#DP?TFf&!>IhfASRrs99W@jQR) zb0i9;rN_HZ2>MHXM8Hj?P#<)gU`>6$^iFAoT}U^6upSg&cOT=C`<01YL?COh#N1sP zU99?WK1$vEz@`q)^_QG0ua7s+sWvkeS!<7tqPBAqg^bzgoW{52>{$cZ!#}l7I*r$U z2=`o8U#gGkquabc1Z{jC6o(d8?H^=D)HU?tu^B9&Z3tHz8UpLL5a?OJ1OUd_+kD=49sKLsx$UCrBAYY}Y zurz~~Ek~`h9kXRVv>E%VM7byTE-!BGa6$U7hdcMhQNZS4mkZPz{MBX^^mf#09zB8l zjVlo&C>6vLIE-4)7r9*PwR-(dd6fQ*Q~!6#!SW_(UmpS9Nv8Y8Zc_9_6bzA&ernB z7k(Bhfq~%=$V!T7C~F1C>%CCj^%nC~Roz}87Y8iqX9@YS^0nYC9<^SG|M&C z6?wK83R~cpM>dZ8@jThNulK_e)VrV1NoG3__9Z3@lO|qWS66JlSQQCh5SN8T`OX&- zM#3XyYOPkZ(w%~nGZiqTvbyY2c6gl*567LZH2Cak+P#MKPo_1}JRP(O#wu~`x7Qw; zqC+uQv0$@bT|^1;+aO&>w5sa31W+Z~o%p`GAI=11GK_GuI-MuXYthGro0`TGZU0Sc z=vX~2_-O&^Jhz=+!qjGv4+)~7-dHM>4k*4lo{+KIEEdq6^m7uH9cnRAn=%XxT zuOL8xgDWxa{kS_>OIUPR-M&0ZXo%Xuto?1yuKJ6K>h#89dT<1A2+7MvaXyPvutNua z1ZvpCuyOfw40$O1R4dlPo$U~vQ40=C=mPjw`fQIYbS3GVyh|(71sdt*4$}1xDPy@= zkGKrG**zyc#8Dvxfq}%S8R=ZUcjV7oZDg4L6JMg_iLSbz&-gETB)JPwBKbeSn<&H- zjQ{;xW(B$bO}=SkrBjvvpC|SIzdWgq!1STI3@$_znD$*gJcyO5k)^zIJyKGqzyqUk zSy{HUwB8^`v;Xo<>+6oAk{hLV+5i_`%HQ7~fF=XXc_^B4gg2)~y&9bwEnrJg9HcNfm{--~#U!hORkBFbTBgdWK^I=znWPH@@T|I#gD70;c1Hpud=udl0AE&K=% zpY8N=0`}G`&3-g~p65+D((3=^o07}cSsSgwe*9=~-oY?;<;&rApycDrYG`1Or8Y~s z75&s@{DSD)onKT$4wR+PGB8;6KSg=__`2@&KU<3A(5l}acQW{}HpCu3DLlpK!F&*8 z$4kj}eo$mx&m7@xmL&sjw@!mtVzHzqyOV^U8u~m=mkZv|Ek%Y^q`$`H*v7;&oA1;w zx=WF`#9bODEAttx{!koP)SZ%3r}!d_KlvfAC7 z#j*p#<@%RjrBt4koIF`7p8*EgU(Pla{kikDUn-5lh;dc(dQSnh(%1!_Ak zII-nF!Ml^}Ff!S!z%`lqBWg%T6UU&+h4V;L7Ca2FB#B&JTiwWXXO1Sy*_qX*!`)wf zqNLXGI6UF7qt?E%M>2aA--wX?V0{kbD{;z^FkQqOX~u{e?DHt7fEN3{E2Y^mBBKH8jH97MO$UnfJWW!HHp=J~qElFFX)XhFDRPS%+B!c( zSZ$_(VnB^TA{`tY{DAWL_it=JnZ`@oiaKTq8(dM1`z7=W)q;B(a{I-RsqNmigkdXO|$T_%<7 z)Qa^v2!9FXMN66`=Y$AyLX}L3U(YodQ3M3)BQ<+142;?zceb4#?3d2ck5{{A*@&6Y z;Hu6PMHaX($p-GOLFD3Z^;$NglQjj!UF|xybb|hHi17UbiqaM~;6C_5WHtTh6EU=Z z{{5TQteGlqE9x0D1V~c0*k4Nz5%dh-Z zAO=*%p}O8eY&JTOv9tZ_>$oYQHZqcrWPgajIXe*C)kQNHN7xSjfukN5--%YlVOd=q z#Ywyx8SHGdEXd-3fa962vhafN`?=#@F{%~jFHaff;2~4;^W`s_dUa{8vz~8}j*ks@ z=QPW0?_iiC3B%f@=38Ajy2JtCRoi3omn>G2kIq=Gcts21x(~WWekLdxarey4Ntj^|UG3YLe}nXtTW6JL`{&}Cj#)DKF71A|>wu{|ZoeyUmFQ=(xZ-72F)TNl zFl~HXW4v5$9W=+$*iAPFmrQ+{j$Q0`7)m|v__x{Pi%h)=s3%yg%Y;7U3 z@JLNHGnuAd5Oh*cG>LJ31F;W`9Jsi5>F_6 zKL#h~GLM2cRpnlCMeM91J6;a(zt?gN7y)!k>ynr{MFxA`KxWg@Z6M zGV(ZL&zY*$Z!L2?&e*f65ZYS6!p0_pgZTN=M3LH-tWM;875?pJ1+@GUfjEjSmx|CX z!BHJ7UMWeIvBcDF@pdcd=4Izu>z`n(epA;5%kDc)XgTQ<-*n=XSPT28CE>Hz&DUl{ zw436DqOM{?R_72@rWlaBlwY;GV9{wWgJa&-Ug_;DDHEvk$`-X!nrdz=J(#EJrsbwx zl7C;&HJvp{E~wW;V>_(yg*khA9!FbUZ*Fv!_X|OoLHN5vtliKu7W#J+)y7O;KY|Ab zgXtZPN}Y9o-0R=!3V1{iyo+E`qM~Kix%T9%sA85q3Z8~>x}rZd*si8upM6ft2TSZ| zmsTD5bK{1X>V!y`Uw>$&_152no|j;2?rwGPpo;KkrC&IfOxSgnE!S2WTs*a#)27MU z@?d@s+$*rUC@~^jZb&xXRc9OPZufXo7rg7w*)J6u`bik0en7_OE7X`AWN`rtO7gSj zBS1A2&$)daPnL9TY`?(tFkkZ=9L)7J@K*$60&!5Q<|!bZRB&shWF^`3q9^9%#gPWL zCZO`ItPBnfv3Xyi0pw5#VC>S~-j0QX^SNW9)x1~P-PJ|7xGZ^hD&Iy{(3%pvJ&*a< z*N|Vzlz#f#?Z+N2dN7jKn;)}GKmFJraERF4C(@eOF@>yKy3*$6`h7%NeNWSeQ9pRS zo|Gb(=BCiojN65zmu#E{6-NTB@Q3xIGhzG~Jn)8wr@I<|2*r%^Kplb+^6fH|LPJT? zmtR%$dO6qSgUf>+IP6}fo^n$4qz5}n@A|Y5Ys?nZ^vM3${2_YE z6_jl6^E3g5d<63Z?_osy=}SCph@zHfsMQ}ADjV$1!s!nI!+$nNzI3;?^;nQ`WnnaC zTYzL1NdD+vAJQ^v)IE$HJC0|jKFg3HLrALm zflw-ySS|J)d}`GuV0lgFpgntbvwHO^%F4I^DzlYzuX%Tu94D6u>G%@6N5rPKl@5(+f{8&$_>J z7m^WugBc!Je_l}(Ny)2!P6~jvgi{F>wCEqj>(d@2)Wde2N^+98RA@iu-Z7SrLnpI! z)0YhN)3AB{7Q1bo^{2$x1fQOL>EKz;&b2?)C20V3DI7YT*>DK}g5?SuSA2=v(3jY- zc{5|X)_mV?T^ZEfhtqra*d0eiBsU|a!4l_V5k-N>etp4|NMc%j^FKi5rPJt}c$VE9 z?^_1ZRb9^ao6n$Va9%IB5@Hjw@#=X8K`MS%d1&PSv#&&QgKIiNTu0KX}GIXYCE3Q*6te;MHxNYhm-Z&PRCVFIrsf47RoA#8h1aRd_IAS zk^(YQ&a>;QFFBr40LW2$sOUU=GT#lib)>T+>Ie*xgQmW~R4M_X+PU)36lcqlL~=8_?i2NIzn!61 zuRieWF~?PQcJJ9ub+vsW2FS=r$7C`w>h62^@y9(_t+pQog+f7}K7G+uRZ&`6LO?(O z{rdJJCN>7GR*T7GA|)k-%*+frckbeFROk5m+QyM;wGK_C0Pa0#6QbhqnOM8UqDb8{ z@mhjRM*Wjp8_`(8p|6&3=&L2P?9!hhLvCZl-6NU%ejDDH_B`>$R;rC=k`J%ryJT@EzJZ!(n%HDDV|2z56Df_=l9;HI?uI8{15R#P#H zAAg)*s+!95BBq)*!8lbOuHyf*uS9YqyY8F5cmD7lZQHi3tNs3727`eE2M*A$Uq8l- z8H1OXC%(SEL`6MDyLRpPV*TfsOeU;WE4SWu8~2YLi-(8p%@;j-_M}_4?!?E(IULnF zty{H5uh)~C(|C-u*RTl$SuPR#TZ1Gn8mZ8WxCe$3(BPX>1-GE1!!4JnP!k>+2tfA5 zSiW7qgcn}gLY2BBgZhRe2qKyJ#e}r!g2iaWWHQzLiJrcA3T8wnn%u2!0R(z`U^VGU zj`@Z4%id$j`eP_s45nq6?K^2Q7C;b0(sBw>c{FGdalsfJm0V1v5I^pp*u^fPi9#TQ z`nGGZq=wudA1@0Rj~%sZ;_lVC6}hPibuWLp7H4A-1d%I=X$YRZ36HS9X$TGPfKS8! z@uC*9MZiBWq<#?s;UkBlZm?a+g%k<|Uq+4$!rb7zMxzOpk1r-uebwEW@=(k1X6*Ou zE0NqxuKPq?U0qE|N-}rdHPUgF)6Shc%bq>E>CmAgvuDrYrI-GNhldB>e!C5g#`ero zTl3a4AGHs&XU}2o+}9X4_HUdy6VJheKRFz+d0t*#47+_8OO`CGtHsw61y9C}=}+nT zlcZMH*CGU}Ir38+mf%4=H{(G%whE^WhQNotNKP!e=W!LfxqO)t=0+J!`go!~9n# z6B$sme^3X8-qsaNxoEZetHx5x;_%GW)naUlrSJqh52q73@f{w#y(ZZaQ?4X5GPVkth!_WjB9e0)9+-q8a$8Hl1l&yoLN#b=vX z{_Fr0I9DD3uok1Qwi4NM2;o63@N`o+zpq4c6T0r3z5%vx-^RFqrPd>%kd!C}A z%7~A*H&rEBe7Ej%Dw}DKwP-W=aM@1YedZpPeL0%a(h7V60x*^5vVGp`q?VYG`?kGG zEH@>a$=7BjHdmBy5k90+4}Vl zbb9^~{`uCYAX+KQPhrgm+nBm!T7&IO{Iu~Cy0(6shi5G$YNif@sKQ$%l6C$t@2owI z83ECW@&F*Cf_=Z7WXjlae6e{VKhB@Y(nE<(?JJSojBc3Ov+f#sH&drhW9!x}q^8!t zfWg(s<#HZ+=wTwl!*|^^g$lK&3SCtNW{YES5peVHrhWH5bZY5Oe%e_QQghMiZT$h7i)Hwf z4|!pzk_Vr9*)Ea0QjBtUHC{^4RaV;7NM#BY0WCYwr)xW^3(gUnkd0Ar!_!?(RYfJW zhmo}&&8Ah;IW%(uU!+sy? zvc~{Ag{e4y`V`sbVAgJ0N#VC|F=zepnym!fJbh@@p)>OGEVA=U&{c!l+Z~gxg6akV z3vwl1zG3w4(;a^|GYN4qs8Uo!gOj@iz*}4NmVXk=$%< zm_)5mD7b6n-8}qo6iG=*L?1aqWtD49a?q(`CvF>dJ1TcK7Jf31%*+hOO#tXU{@;A` zbW5g3O((Om*{{c?H5ZF-_X({0Xey=gKl9VUo$Nf3=D5lAOXkg8k37hTTSwAWUBI-b zUZl_}fLCii-SPz`n_lI;9rm3C|Ca>+j(o9Yl|!PINF=WAhG_Z*TaVWR`}dKZolR8K z-}%?9S)`_3A~iLwu9x;-!OP2wpr9an^yrD3o06EA|FCZD8Z4HpH|1SCyoRSwR8!DI zV!Pfn7mN1%_q_G7oF~WMPLB?u>^z>vA83tsO)?L93>tu}=p2hzthXg;07E|WXFN$o zvHdt_i9{l4$_-PaZb&YdxNFwfp2yQ;dnYVE!H zobKIw@3o%i>1Y)tX+$_YI1msJL|GXLH4qTc84wU~au}#@&sduL==TZMSw`0l1O#dL zzXLR#9tr>3iR><^<8JR{{lnbUUI&Dgg_E0!vptY35cwOsD`)EdA_x`#>jf)RWoDHlN3oB3K~q$(`GIW37A@$ zY%_@rdU-4v30YNCaLM@Z)f1Vko^T8j!Lld&ri*T3t~CmLxsqq?8{kSSv1b9Vo0YZI z5qAItvy*YM^!ENal!%qouk3@uSl|p)d7+>yw7f(`N+aTC)1Hm{% z`m(ZEmLnMB8vNBt3?s&yODe87soz(sOiy$4NdnPjJFVLkbqXiARZB2q-M7X4bgOde zPB8Iq`Z?{r<3q;wX^>u4vz}0{dr76-BGqabJOazp9<%m?;Z+OluDc_6ZPdy z%c}MjEq>s=SehIzgX3Zz{3$Q?ToC)~UgJ-YsGcTdsEK%Hf#F1gz-)OHg4A@odKsiU;G z)8=08mQzyga!FHDx#K#i0?{WJz5}F@k01XU-(4kfIAM>JLa{7R?@^GllPfoeJHF{cm|Y7eu9V!l6c!Z+mG4J} z0knE*G>G7EN~pAle?r96-zRf@KU*A{T0;_7){UAh;o z1{_^p;Fn?IsOa#iR5}t8FdSHv52JZ3GX<%j8q&Mw0Xe?IYhe>*3sN%}mE>(xe z*2d(u@Q{)1(eUHdziJ|S~( z*@F|~j#<7mxS9Wt^S8MZB{lgC5^CexB0_a6DP<1-Q?ueHL^$OqC-{wX(|}5v^!o-& zN|QX9hLB>(-8sxJ{NQ}HT(`>09c%;q-|yazKzU~BODs>vMZ{~W8%Lh%&hQ|f+Fxkx z(g&Hfb{@9RP-e8)&i4o;9E&1)3K+1u;oguD+hv`~5@w)32L^a_e{{IKqUnzGY>tf- z(+MzLFutOVr#Z!^lW)-0y@jXZ}XSclhk_@oj|Wo`HgL z&Ye9B4@>0c62+Jl_Xp@&L(rFZPK0IElX^_BBCJVvFdmEC)_fcq$u%OCOX~-rAx)c& z84>$>=1$5J66*MO_>Yh@J0FC%FSE5I^F#FzLE!3g2boCg__$z(<4yZoW2*WImy7nf ztiqC{(vv)4u^Slzig>4Nj5nYlc;Q5c=33Rgy^I+m*Z`aVxbl~TflxqP_A5hwB>;8F zBYjK#L0nX@0qN0by=SO59`fEI)cr9v{I!s%KrF>z%n3z)@en~w{zr&t0!)CF}uySWoYp=!QL!G`TDch z@sUS3EBPi_r$RFP=`-4tu_HjA8JUt&`(6BpW_gDoRiABpz)!EycFE$jon<#L(hbQ# z!xuW@!l=qEyQ_Ip2X1JOTA#LRXkSGP_=28s%utmH9*u~}gM4ihY%K=dA#LH0S@r<_ zVhqG{-4{I7YVMiA)Ff};m%rzV?usni|)(zkj9Ta#^AY*V{E^IT-PM zgw$Bz6C_jL3~|`>F&(em#V*ali%?LA|Gt4%t<=JUiqTE=b;oc~$B^&ropAf2F^z3f z_H^uGq8{Jx<{~)lk8|5$J3sp}K!tPWM<`nQ7lIVMcUK3Cq;lo2{jq`4zQo?fTg_;< zj^E3F%5?cyiC56|M4uas21P$w@H&R z@9Gh$zY5%Gmp~o(m-_e*;pX(ug~cJZ z>4jD%uRJ!3xdJg2(;JpJ(9tN{KRGkAz9MiQTfxvTjjXki)zq*VJ{C(+I^ae=!~QED zp%qjg5HVdKnC-*v=54v81i~PA+VoiJ)gUi086vrrlw}wdFD*E#m>*X!MpD8fR6@fN z_=)Db{P%VsccCLaFLwH0dfncp#jd{m+)VCd?|SD*FC>t^XL9C>i{;K80Smtp5#d$vXlXn)+OH z(X1>rE9p=B*3tX!B(Km!EvFsNMME<)G}Tzm#G%qE`_1(0g~P-(FVeWIRh{`exT!S? ziUQBi%98Bd+^NX}c@&7BKiG{Vi&g$MsG3tLh<|QY-`d2^tbxVJ8B%Y4jiIHFOdKXvmZi-QifDxHEsokC=>Nw92{r!p!@^J?V6R# ztCrHuCo;Z`^}ouOh@;o{?#||_uC~aH4DmLJqlbq6d(j_(v$OnM=uspwmQACzN>#GFrBv=H=r$|$W+Y{EjyJ8RwqQe6E_;moeo^z2(j zvGj%(YD7Q#P1SE&GECQeaekj7v+_+bu#o(1k=6$G3CqWb_Rq~iwS-e(#+YbbU|Lv# zm-ig25gGr~#0nL4y3{}tS0ZxJLsb@bf}ne{QVrYOvtsWMCpX9=P=VD`LpP67ni0SE zF@SNcVbol7)1}pyih@kSNnwv+~r% zn%j-P(EodO&z_h(WZlLh8~=@$e7(@#WFpXCN|0 zLEGVG9FD~8qeONW7>UjsOIr}(qU;VZfmT@A?WRVi*^2RGemuLL*(xPJHB!_xIj-XxjFm6bW*M8BnI zS0p@DQqVKeX{=?td)h7$ibPd@e{t=ZZqxpSYoQlbkF0w|3RZp>SP~|dQp&*3KmaFQ zW^l-0DwiVIWXHQ8j15oAr8*k}I4-*<30$)QmIYkS1CC^BL@|Y>B`KLor2~D&{-Ed1mw!^~{y4ay6;1*N69w#Y z#$`C2E2?wKcD7usTfxHDt(M>2Dcv9c_%YWOF@e}mqVu;Q1G)M}wSx3Xnv^_qVdYAd zzC}>c7$;tRmhv%Qj&1FCj@I@5t`?Cwv3Auy&rNEKxaFOanAo7q!DsQ8iECe<@qtSb(mo`a%7iy9!_6WVwz{l%Wv@d zq&bPT2fq|TqMV)Fx<|I^V(2OOlNq%cx#^(NZShhd!$aO`p;K27xFG=R2rS4v>KUUK z7mI%X!Fxo$fPxa;;S3(H$mn}-WAAU|D86n@&0bS5$*@hJ#tyH#bRiyFblSo559E5U z&Y1Sze)wNImD@v_+3FoD7YC5 z_O|NGHsGKHb=^Fn(eQ^e}MhsjkS()>Nl;v!PAeq!%YQ&?;M!xih_bd z#9I%^9@_$ptH;)KffppZl=3yrA5LjBrSA)=q=K!PKL|lS)0P7Pu88IZPn(?Gd)zUj zkI=gC-{QSnB?)6*Lsd)U=)&Y$Vuj*asp6lBPP;iABG%y}%^og`vE($}-r^ng-$I-{ zhBApgVmj_xBVMpzS+(bBywFV#Q;AVqok`s71$0i$wNiIdOSE#)JFeaey!w_X$?O(8 z*l~!xLs_V?GCr$Vt7de$TGvN=RtXBGlJWmIWqPnhm3GX@^rlnehRna`(XQ~UI(ZJ- z7gE~eW?T+eYOZnp_VmWQ19h)rf$75h#CwY?UT$FhD*ijW4)mkxLYI0JkR@Z}Ev0-w z3Klk6c)BmpTWfbKd~!6qh|lAs&!02$>57_x3H}L>)cxcitjD|0qhXKv=Q#0bu^zb* zF+0rFAH?L50@IDpx2SoQ(y*}M!fmIEACner5~>n4!&LUsmC&IkGuPE_Co6SATy(sz zB?fgwF9lxQ{_=C=5uv|hlx2iBvw*oR*XZ`2?$FVeNH9s?_Dud>TXJ%-r)(ufXZx^s z)sg1?nlz!Qqeu4_3#(*FzS;Ww)&KG@+tH2gLku+%7Y$qsg`xZ&%Fng;v!@?&GXL3o zeUijC-tcWDDKaIrr2mff4`7aMUi6jfBI>l$#VP`OF=;wO_B3`;crWq_*bbb zx$nwbtJhkrWJ2QT=vbwJb!hZCXTu>`R#Ab5N;G@2Ks|T%^=Ih(zu*F{GW4Hx(Uq0h z8-KhitrLYTEUl9Z3KH~cKUbm?44B3yC*iQMu?YwX*1E2=k36(f)<0R7!bmwJIiyz0pKjfDHH3WBu^RTDa*@*ylPwJc zT@RchibWLz0}6s39A|nL*~OJab&~{B)c0)#`S{Ad_sF>AB$9HpkY4q)XRucSt|zE- z3-AUYWB@Wpsmfd~hxfRfeolpruV?g~aOl_6IAZq1e%@}3KOktMIs2V;=A**s+E-4;?McShq9y&6os;Zsv- zcvx&k*@Qmchp=Qft+55)} zx&+4g@z*@Q0qqjz~ed%pT54^_Eud?NeSt< zc3)z8I%cyqVCRgGA0`oo`)TyrbO^Rae+8Ir*wuUR`SGTZ%e4WtF#C8#o&W%>AJ2IY zsbXR{7>s)t?+q~|6NrT)f?93kyiF;tYV~wS+i}^9^*HW#9xprh--k{ESPuxAM-e!^ z>d;EaV1ktG5*!8X9AEEXY!q}Lzsj&cct2qU_>vRTa9JMYlPrYecF#7NuG z&=nj9JnTx8{)|LyL>6|K;5C+so-uJmcMZH=)e9$&s#@(5B7HXG8ZqCO209@3^7^d+ zV~5p4C~}ZRnKIS;0%_LU95}ht_l^n!#VS{Jwzp+s4`*Nz9r~$rg^Fd=`G`F~-+gv> z%@nsdb0(j3nwy(>-H%Op+n=8y;NX}1zdpP`R@c_%YW2O42!P6tN=zV~KAqzbdm@7; z3zf2RavXy7LE{qp#Y2%eoVFaUo$tl)c?_X?Dgdt3);G1uPQRreN|9QKF0Jh(l&5HK zNcj}&=V0in8Y-2N)_fdr5?X1a)#80SCzofPT31QrmNK+wMERLNKUL^uQKXD*OTHEF)5}ES((6%k5 z za5&}(PQHv=r1n5`)?yQOc~boa#1F=KD-mvvT~L^>F^FOm>+NOW%qu5n8N3EHs?g10 zRnx;%(1dt7+x=;`jcOGe+Bmab^Saf83~IYm}*i^zUE-nULo-_A7bQr&ukOSI=H)fXN9l!B= zR3cGZ+Y>r#dU|>gvyv+p{%$kn<-D>oitlTlEOydEBcHQ#ga56P4UmaFaIb?237(#h z-ZbAbv9FKVuN+R!QJ08)6vP#Hrh+#L3LQ7QK($PW^KqjM)z_rv6iN^qh*2`^YB*T0 zx@GQ9b`&4m_0p7H=^t1j2Uw&1h0kcg;mAl>d%HQ)O~f)vQl}VNXcq<`9gm)`V7ctE$YEd`Va-A16`?7TgO6Uicv#W(OYW^t>C+j#-d{eJZ{+JI`w5J^Y{mDvLL` zmIL}p&)@7}i9g&?2PQ)gFaD`*7)2;t7dwn4M1`;LG`!(JmJrmBe)ya{uy(-lQdAuN zEeTLAAbun$WXv7g%&krvZ?=|EKI~=<&*6d`rnD@vUHS=XnB$x(wa8__8C_AX4uXEv ztC-2>NkP3KP2ODm6l{I$XTu-65-8pagHfqm`HsFA#1!_br005f^mR6yV;ZKfTUvRp4;fkYzfF4h8O#NeoSHan^c3*JNEVqB7hs_`^)w z9~+$4q;{KPQGOnR8(AG@1=6^p$xe=sRai<#BoIvedHLo6^w2^aKx)_9hiBwOnCj)b zka{Hwr~IpIw0ePHQD47Lw4?h_WjTn&);%wxV&XWw0x8zkbn^r}QhEd4Os}?0?D4*) zz8lIMx;&AwXM8zj5`=>5Iy|n3|K;BF;JLV-foWavL5Bnn_HdqYGnx%NFLytG(%z55 z__{!f>-pX7L9@Ty7qJ?Cbn_)zKILUTzu1yN4di$UZO*!>Z*IS4 zHfLQQc(jaHvD)CC8Tm#SDSF->tnCtQfII)%0*_?IX0_d<1$oy;Fdtgq^QkjuxjLu2 z$euV4IL5UYS@Pgk6t85OiD0ufBThl?8M7lkFYO0eg=o4=T~f#qNV#)c4lRK)uZ$n# z`~q93U?gE0d%hos%=JFZPWFG?gN$LDZFYWTl&XJAARTnE>g|SdaKY2_jl3R6$(#^s zj&GE2OEE`5!MyD5_9tA>sv~pV=OJT%z)@lKIHV9*TJgCV=`6#CP+7eL4#sz{0z=dA zwLwvm7d(IlzRS(#4>hm^9hL-`FsJPC7SKW56@d=_1sGba_t8IHDOk&|n zNF_9-cd>5Yw=~hDJ?26-NmEuFYSrldy;>Wt0wS@OqCx~qu^5UXE-{$mM|q9u1qz{H=*+Smv9ZL~Mm2i+}sgd2oP{@<6w9%WuNV`ZQQ?nD`}+IfkAncvW7w z`NkmQ#7~!F$Jp&S$3;e$3d{NMRq%eTvF800k58n(f1|K1sU~CR;LP~blpSHh`q{ry zl?Bv{iFhwYkD{nfSS&jt{&%AArYrbSsr=E(>w{w=*Ckke{iYp;Lh!q@7L!k`gK&Wu z2*ieKCV+X1p4-VIZGN6)1rEI9+8Ymvk*_x=&RyZiaAR~agqE^?o2KVjsFj| z_2;)V(bDQO%A*xVVjM_xk<6jlk$RMLBR(u9H>aWg$H1_FH>f^rcq)-qr`%Off#mI- z`aNrHzOw3A;mI-MbeO}?WSy9%EcT)o|FzhEaS zdrkI~wG}RJGK0)#Qx49%lI^FVXDm0f1QObp{VnNYU*92m1=C|3TJ@f_RHuF!IF8uY z0cS`0mO%bN`ex}<7NGsoiTmUqKX45bZh$G?&u(Ho`Uc%nv%vNbwCsz85PFofFLm?l zZtsdI_YV(~(=}hI0RRBAIb$LT408Tm+S|jx6jLfOvqTyno_krjB`VG$gfcfbu&29P zs}@T!^c)qgMg=5emPw;MSP+{`QQR9OBoXp8?tira#Yy6k^(@u8scdypa4F`r45?9i z`9!Lfq$?m5l!<0SzM(Eohov+M$`pp?1WHgS9+G7TqQU8iVC6mshQBF8sB$_a%zg1C zaK8&>*J~B6K(3M?LcK8W?cLL#g@wce@%m(`*rs<;71cCw8pze~ z{}{y9?~Ao?b4|{oL1C_4B_#U$G}&u78x1=1A}CcHm970iv$IQ5iuxD z-2lxcSNf>%_ZDxyr6XpG#~cTDouxv`$gg$3fI=xE!}a&bPPZqd1f+z+*~F2V8SfA^ zQ6r&{>1XIZ{};BEI^4*#Uvl5YLErmrbEjK+hx4aF`5_1Bclj9|%WT-95fT;m7gVrO zrhE8BLXa}Otz6nrt4?2>QGX9L0J8H0h}b3NZGbdm7zg;3CI>-MKW=Y{QZzNrWZal} z4H4N!?`9H8Lszv$hVd{{FEh<}!#pHbT_$5#!-On~f#Ht(gwzbhE%|#+MJG#H4ru7O z6g1XDyk3vpcKm&WyWS`dk6+w%B6R|}y~N(QpbY&RG#TRDiWplKJ@t<`+90uFD~ z(I}^6wMGd*-eeD0eI7q44uMb- zA+l+Xl*)mv<2!+{5P*BsU5m#faLhKunYEF&E8D?=X@w2x`;8-9+ga(m4CCd3v-P5G!<(FsZi$Wn?jFVSHWT7B$>%7>-s5-u@0K;I1act{>ja z{e+okbNhQL`R=Pwu6-vRTS0OxD?25|5NA3X#Vu2#BTOCx1+lFpf$;iG^k33a)Adg0 z71C_)*ho)c=ig<7xTeR`*tU`w<~8?5-ay?-|n>A zt$h9z2Yh~huKxSydMh#V!f3}#jJH0;tkJ(Tgvk2Fr(M56&cD^Mud$;O`?@6W zcapRCmZ!8dS7oOyP%F1Z-~MRf|78-xzaKkRo{fMB$=f(n%A(S}weKQcu#bgTiV)HH>bQktqjZn zBfJp9HFctz?qgsn?!UJ&S($)fKH(4`{t@g{WARppqt1lfUe#CxS3u-nrWQOn5vvP{ zN+IzR6#({J5jC}7wqHA-$$HBYLLQmmyawdwX~3&lrb{Ti>c~`DEGmlZ)TCq`Lpp_` zEtoVqC&%R+_!gV_Mo2k6`ljwoH>)9v#C6q9q8Q;+wK=yeV0MfoWO^`rw)pTa77BrE zoUw*@&92I~Q`5wOHYq)Y3iePv?z`q6Fkx{%nEX~}oxY3woI7!^TC&aIc2b(ksJ(u8 z*xMcmwcmWl{j-}I`i(EvYN?k>fz;}^a|PsZ*;&C!e>=XXp6g>#5eWzf9-?b!cV4Z> z*|N8fPj&$`OBB?itw(1@FY4ag%#hQ@ACK@Y3K+0_K99r6ba@;E%p;B-f4m|wMel6I z;}8>)U0<9Kb$lpy_>tvND7zyNtDc_V?I!Z|9E|0f)#!mf`dDm*k)c^AJ85I7=`7`s zk~f=~(hu&xHL8NQsh6lfxDFrJSGLgVt44n*p4sS5q2udQ=LoXr!U7&dl8F;(U??^b z$T6|0x;c|=9UoU$JOBC~`%AeaF$qsHV7aoh;dUD18~L`OKZM4Mq5A|fW3wYDWVfgN zWCU+)Y#izF#6RqA&P(2--nKXQH=yaBY=2`?X+M+beYe0&HO?77rzF0ft5LG3NT2nC zKZWUiJq1Bhq+)q17wuSIGjX^YX$hY?KCQQ0=f6tU;+zQ!S@(ZgA{F@8H<||<>eAdk z@Ncw$lzr25wv6?@hEg0kYIQ!~9>>V|nTjy9yP>ghpi&fhRB#K*aM>~?n$8Yk1NVz~@Vj`7#>bXwld%vUCu4D1yD%Sc<{NtFSXE8^h zssoywXT^*PUf}Ll93(k{#VQVt>yV%Kb9+E=&=MN_)1ki${^Z0yoUmDtoVUyu|0mQ6 zp@n{ojNqU0g)_U-ne8XsoAkY>kjN3^{Dc$*D%B_aVr*zgI}LX=MA{BHFR* zcclDJjOIi2Gm@Qqz(b+$D@>cMEQSpZW~$+jSsVxzCpmsPU2csFux6f=jy077AP_>p zJZ>4e7!4-@Pt(?WbYt-W_*uVaEKajn{pYN~eD{l#c2)qH%`aVkqmY`AH{I+PoL18X zGc`|rB9%+UDwEMY{=wS9t0EzK5(hit`d+ww1&MV@n;^bDQ3|TPw)4_KfOks03Prq` z$}M+M>dC)H)XjD?XXgUuK=x|*w-7{rgKPnNsIB`z+)xq}zfWnc3ojGL%J~KeQ2-!U z3x&stXm3+T>3pp|`+nKFoM=*|+?77nyxRL&j4!$p{KU3lOof{!%Et}c^=6(jfskL$ z)bwSiEQmiNBcsJ>>nBw_ezW@;KM?76$Ao@bC;L}3qc=hJK%9f>=;Ry%Kem&9Pu@gd zP-aI<2=psxQgzMEQ+r_6W@%MJu~e|IR)oPIxjCzV2#Q=olh=q*hP0fPX=h7QlUHR@cO3h5~Uo zTzQqK{wT?J;qnr(J(wDoe`i_^ijwB3arY!v&P+nZ1v*pM`B{!5Y8vXqNoG1}7NJ%DQh$cNjW38ji>LJ*Je-)VXeb=eH)# zf(8Q)xB>KWJXZN$T-7TT$@tF(-qa2P5n&r-_tuGt7_vY*Pol%hGl`a-SP*i+E+seF zygv0+m#?C0nNnhph?ufKaAjF71a^aIL^GA$C2pMj!c4%b+~!qs`N2-}Fb3TwiB)w% zL^aq>Y7?CkpF_O*ghHh%rITnIT)OhPmH|0T=xmHvkg`~UD^Xo~-dpkbrs$f7I4{xb>6|10AEmtH$LU!x<* zq;F_$F14=Tk0R4x>)e6ucis8*qIadjp8?f?#rba=Tz+IU97Z(NavCd$d{n^GA7 zKXy;N^3db;U7(-{#?Mqr)x0j6O8O*{=pUGYwltatuRs39P`4CXeZb)SI3NFY4!TaZ zeX=JlYAev0)5nInuVJ?e_cZ8d(4B80=M-KJ?g4gI#1M60YjNO6@WOusZy%K_iJEL` zy8p?(K7|&ow>*Py`+B6IyB-{+KW`dnaS$O8g^_ZF%?Y%jI#IID~G96jlq3PD$HoBEGPd%lr) zPT)Z$Ff?g&X)(7;d49il?13#2vu*!Fzc~mxGsI@p9!vo;x9y$)0OM%tM2eh55}q<4 z<~V|x#|!=F7~gxsz#vL2EG!;AKC;x0&hwa<7_+y>+HMwCjsen#nVAC=NnBAze07Yr z$gr9*ZKlL;753wZiKC`t_w$WSB(ga8r*9+BpM zk-;g&1^Iy!D>&-cm8k#Y+0mZCmVVjWvNm!I(&-D%cB+?jcaSX{^_cX_cvdEv`5_cy zT^NvXeGOAmYCf&#e*E{RQa)d9FE-Y%PQN3j$RJ?_J-wWXkL&?}N}nkBH+wc(EFKAB zGgkuHwC%A zn(oz1(&a{gop(H*=Qxqb_*=SS?(PUXJE#J0=Wam%;r9v?Oqn|qXeN8$UH??dmloV* z<6*q)VV$N}gI#$2?{Vc!rrg{D5H*E(*@7i~)gs+VlLdwOu$tr))P6#La{RNb|;?+v@)`)Qc|_%ZU$ zPsX7>gA?$%Cw=$%UQI<+Rg*h$L300EEozmqUg=r!<$;R&5v|bSGi5by&=uf2c5Zf&xPt>B|3}F?f4ipCzfCyDS}L(p zR*}1DufEA+PphPP%D8|Lj7bn`H!UdDt^JnvB_PBg+RvHVhyX}vj17-A2)KLT-a^gI zFyhqeC7<3%9 z3?@K&J&5)u3OJvm^dY@aYsrEYAuxOSma`#Ur(VT5D;{e(Z~b&`Nvi1{MfvV(9s-; z%%+elCwqP~q|>&8lqkvfY7L80Y8HM;egeI>)Te`ghDIkOP%&T4xn=S4@>*D1qhn)_ zHJVG`I6twh`TKW+^7ucq5M2`K=<0$O&Do64&QkI7=MIXMZtw3O<^UyB%furSFfcH# zm$Z$OQ&K`Is-i-nk(}?3QNQPCcu!ByiJ&GWGjrncu?-XgfxRQ!T$3ux>AoLu9+#dY zT%7Z;Ze#kEdvse4irW!5+WdmD9MJu*8+Cj{?kbry;8zt97NA>`tAM_D<;~zrH;u(o@TG} zi9U8}U%UN?$L@NiATdxae2jXvjZN69b2h{J_OB+i4q!E%_?WTMGTd18>(e!$KhyF_ ze{cDb;V<_|ulOoajEZu1?`V}%pfiuZK3I&t$R z;?`n8QX3h5!pd7CNT&xCmMJh^MDz@{%@EJ`ui!|lh8VwDTzX+Jn4 z!xpKzxijyM0CU|wZr_sRLFT@9gTrX(QVqIl&EHs&NJK2rwy@vAjl(cb#2POyNZ}l-`n}^Gkuh|-r zv@TSQvCv_5*zfK>GsJ(pY&!GaV}Vp{7_0+vYeU>ca?wxIWZ&spd zNJi;587owMsBR8$;{Y7D3MMKBUSg0wD{);uCk&^@w}f!v5i+>hZk`8SjmAEf zmj@4vV+e`fqV?ky8~sw&#~7HV7;GE;Z}ErV8^iLIB3=9O>K9lPl(9i}8wgSZ~7hE1DG5I`ChX+ETX&4x)M~UKUYZ+ZT z^Hz1t%T=*zwZwgWeRmsF_q9S^|F!2O&Vq@c za00tiMZAppxrk%MnQ?xXrN#Co6<;R)?!vxyo?9F#+*vP0O+#p}h%i1-E0Ed13xp|& z(qS;V6awX$x;IE*G)$ZCQD=9(Dlp$*UXhQ4k4^FRdW0aTwTKEfkvaIC-u(P)pHwtL zjFg&;57cL6bWv-7S24PgtX2PUa1v#Vy9-&B1`Y-cCc*&e^PO|gkGb*(qHrB$g!z&^ z24`NYy>9m!O%{kRr**Gy?zLkVE$)=cb9<$qxb$poz6F{>a2dVu&B3SRG%gG37qb#d z!4LNpDPr@nt9E*X$PCHLtpuX@^Hp|h&~|nA3Mmudi{ZP!4~TgIe!?9yy{vddxR}Vd ze6F}fq+-PcC6=ed0~dN`5W(Reh>xBCwVMPA#J-%qEysmyCX^5b0i%P&B*91aU;beJ zKBl8c_{IuvE1=*+nkv9t9+Zq^VdVM~SUnmE0s8$NsN?w2eJz4-TU|sYSP4;I>15b+ zb-2kaTqB_?TEaUDsHvgz`uF$y1&U5g8#aD$w9vC7km^zw~Sf7kF3h+Zi(?MtU!albrbbfLVvxj8s zA00r&#wF-8G+Ko~FyIe>RD9w(0D{K2HctY$V@U82tZKdTUAPCy>HI(x9kC>#+8B;z>foQr#iUdAC=K%V#8al|OAiRXU@3xPH1B z92h{t=2o_7V$ev8D)=VyeB1k1Zgf{=_T(Sp{bX)2A&oLyIJrX4sQ_lJd)9u>;xCB*Z;k=DTJ>OF)GA4IOk>_t# zf0lOPqo9r9$iffXTkDJCs4j$A17(IxWH2(wY}&N-nOfH}#Y`d&2Ni5$WbX5ebCFMNAA7?Cjv713HY5kdUM#EcUC;sm47<-(1G_Sd5jC5z*S(8pn@+aaH_YC{y&0d9bsu}?xqlMWQxk_L^!Zu0WlQw^=Nr;G))6&sI z#gvd?NoXu;W5C04sfda~NcvuJioj4NCCR~^c)VSHfA#HwPC^5B`aV0bH`~MR=HQw_t9m{g-nhoith(MIbyWh<0469Yp{V1Of>)lZsM-?}-! zOTNF{jIM~spL19pLLlT!$fw`5nt1)rke#;`_fU*Wf|wjyJ6r?Nzuq`FVg*4yVDXIF-&Bz z5JML1V|q&pwWcFhpnmg82|eVrU+a#}W-qjyT4p>5$@pEiUj|36BB@Hj!V;C=<>|8T zd}7nH+4DAEE^pmtzv()3@{~ZtI~e>Eyjs1I>E^~oCM8U-_Hc9OY4>7p=zDq_YFD#~ zqSye-V2K0nf7+pia0QzZ2Jp1sVpNm_)5?P7UlJ+@vt{G5SD@q@a?q)(u@s-BrFbAh zLgG+V>90diVknChSxt@FI5t6DYoi3{E%f&G7ce8K6WB=%C`I`XZTznmK+A|!@xv~C z3cg6?hYEea4V!xHo{E$wjK#v)-t)804~Ji~+alYqqUtCFWn~s3^v^CQWM~MMv$Ucj z5`(0?PO2rd27`Pp;EJbqojAI_j=XVxvYH_SN9ACTr79`6ECdKVz zV}Q(p+Gr*h0*dglJ~wfvTeQWC>iUj|y-V=1WVX zB-=}(@KrUC1ASU#)TnM zy?-%#36g)AtmaH}eVlkM2?*%)Ux)HCefZhfFv16>UYsrTldchv|0cl18c^9@LW9TX zK>roHy_2mV%*@O=SzW2vZ{R0QkWZ6uY`0*eFHgzUIR!jqus3cJw&!mEns}P~w&KTX zu*YFDL`xWziySR3tJQU6)5tuo+d}EQKB(i#*l^f`26~4Oi@{YB^DV*;G1N68v3P1% zg%>=x4GtOUUXK~b)J0!iN)6jl;cNurpSJvhADeu^nFwFPR&2hBQpyi?c{G-n7QK<$ z&#~pRfn_qBA7~|x^kz)&)-Y})Y|jTJh^`s8x5hr-XYndVA3ZtJiBJ9p#_j2vlORZ+ zOp`iq9kjvn6oF$b0$45jVC6378_D9fN61OuS>R;UWH8Yosw9v^l3X(PMi~}0@_UUW zP-Taa*5>xK{QHE)iGNw{X?h9PT$fMc%69KeO1=?Ep?h^9(`ArRrf^_V@c~$xt#2$( zO*qccsrY?b4c5jd^IAw`pwkg*<^$>9mJ_Wrmx-j&-=40Dbz3g81^v7G^{PN_Q9MCE zypFEjA)%V(2InJWq_FxNW25GDZU-a(HZ#o{gM;Ury_LvU<|u7CERE{-z*Kr&l@>dG zH5yD%$1a=4wf>L)0;s+D{_N=FoC9eU3B-2;X-5hwD!|^OKWa25OI5s}Ijb6B@YPEA z5xgntq>DdzgSNLP0zbHTVvJs+!-2R2C|>@TKLx<5M$an=z*p|HZkFC$-nX1P?;k~$ zD%TVOkEPT6f~mQj5LB<$1cPPXQsqY!A}PA`wkYxRIag{RE727VECh7P}WnQAAqU2>&2Klmp6nL{iMh#2th|M8|}aoEY0 z^V?V^2iKgUR_NvCi)Uw-{qFLUsBJ>%5Ha&5eFqP$2Tk z&vG<0oz2CsZvkVk4I?kinZpv*MpL`Z2HMupH+k6*4cJME6x^TWH*i%#T-VuU1P$#r z<~xmxR#{f5*BrD|BeyfT%nxkjj27w0jD?h#Z3>LdYZ3beLq$a-|9zxOaY`7sHuX-r z&12basB3^X(a-B!T^S5iilC?$)c2-6+}lW4HvRBzD$@hqyOZY~+Rp1K@Avm{b0<76 zo_l4zAg*2eUwR~O_8Bc$PVdK z8Yxeq)lW(w3w*s;`xUb#tfKLf6*!W!#WcMYcQO!mW2#8*W=-6~m?3EwUeeZ+TkvOj zBDXUf=v+17_Xz(_Pyq5Ai^PrY=wR!C&U!Dd*t7u|;m&DUPC*L_E7l{H>+Vq` zmByq%mjL=W4|20kWo4B`4DKV#aw50^|JN+YA!C7KY4`Np`< zPcVAWur&h&u2udrQ4uan)|vK(() z*6iNDIi}v6aahf}-N$)GX=Ppi2z4j_WSn$9u9M1TuR;qu#bfz~EF0>IiFw`B zI92UI8c_-|Vh6X~KgIICeoxAk+erb&v7FQ~zy*a6lcr8@`Gm$8i!<8Qn%284K}An9 z8m;`6zE_Q0sgjtwd`^7H`;68{omoIC3{LBHFF}`S)=ymoNS>^I<$7iVrzdyHEX-Nl z#$3d}5#|a#E&i!J{^Q9iFW$ePd#;6U7&_}LY2L!n^%k{MSC=~-m~{#eLJy=`}z?wtsjyJnd-W@N_XU&Q2<_z4POvM2eR3`ZjE?3~XG* zdc(na-h_67e9Pq`DvXHc=K30{Bk&zUl^px5Cr@-S)n0S@*IN**mTTc{!T^{~7pzVU zgsL9{h3-(IY#{=pA__)gX!d&0nIC7dPLxhEe4 z8V`Nq#gzTqtvjpPRN@>Im~v=%I4;7dgrA?)_Oq%O`Q_zhsaQnNJ6~mrotjXQ`>=dP zzs*R=jGE5&YYxTjXa#Bd$H<4+A2pjX8y*vms~G#Ck_JKx=f-g|k{}39l%sCMyF1#A zmeRT1lap9e2BMbj*CI`J9sU)$blYV@Q?-;kj@m`0)XqDMEbOW6D1L0cB(pCk1Qe9% zWqs__;(6{oWtI=-=~dI^(o$8S(9Ot<~5BG zcM3$22II`wXw~*|}s#9_s4Md8h2y0()VJ9PwpUfGwFc6x^ z9aVFP-2-+5rYt69f0}oNhVQQE&BSmS3ybzfsM!i`DitaWzW?CP$HaE4-tPs@S;&FM z<7rJ$$eGI)W4{Xi@yMcJO+ligH*V2k!imw+M@~2VP+Fg`SZQO$QzbvMAS>Qp?nK<9 zkT1y5WLb+HO11)zug#oX-e}pcHD`AiP8*ZY#2+gq_RHR4DTGYhcxBv5fVu#-JU&&e65DDV@v$GQ@Q6x;q`g)}Rs-2j#CP6A%QbvN~@X~LyWs=s~Iv@u78RVhGM z^)!jWf93wWQYV9z2MtOZEtf*h{BL-YdIDctpM2@zne3YzzU9VWdQdLVzqPgDm%ndo z`L(qH(GzkE*}SqI9$Y&X3dR+t1gigy+Td}Tse}+lJqB4Zk|+g(HtdcT%91K|TVn6; zkt&yP6<0qrK@V&H^(8wl+oJ@0-;y#j``wxl0Lwl@(x|$f_htF3H~S-{quR+K$yxC3}5s*f_~0k$7{CR=m)tL(m5`qbwa}0 z&C7&bP)y!~Md4KtrCNxL6f!%~Q4D=2TyF^H+*argr}TS8^)p*3)H6u(A)mIE5yeld zYCL7B;48Ip4HaXo=kP7&8fuvR`*80a(6JRf=5pN^&T1U{aRi^PA#92i3X zQ7KbO^E~S$lh&$QLJ(F*g=*BH7ueif!Eo7HSgE!We_E7Ujn03SnypS`kc|JLB2;K<}W zto`f;Xzl3EKCC6H@aH}0$C+wo54?>-EkqexCKoN8So9I>o*x6ngOz@Pp%sd46kE!V zm#fl7)v*IFy;b~3EI|K6Jhqx&$1^*4yl#~uy->$<#fV+rPt-3q4ZxA{@n7?Q<;5f= z!KZ|{Oa|dj7c1f<_9{~_u&~gvu>Mu+^U5w&{Y39CO(=43cOM2c6{t{ybep}ms~*Wj zGV=)!gd7FU#GXq14!1h{tFiV7wHc!2P5g3^VuTG7+g-{y?Dpb(bTnCNJ>sq6^Q zhGlZ>7b`3*T&dmRjFupEJzX$9UvYFfTZ;L3d%6RyF?E40FZYFQSP}0Cg!+ML*&awv z=u~MS<8h4~&;DAe_xx_|JQTnvO%lV?)Q8cQu&;O2@sk`X)3q(UAoz8rR0B*VJn;Bh z^!@V=pg@V4TwQtp=T}cb^pEu@A$xpJ>a>;GQ(3(&8k`jyFh&LZF_dvl)Kzb&9MLnQ zaRt-i*Q@6jA@ty3c~hn3CAE2BwH1Z#w@*rwhV0<->@(!Ov#%~W4<82PI^>#e)~Mr2 z*^x5tcwv2=ouVYEScG2(En8>_$+~u1$kM_M?SX@Y@7)tB zd9CN)tOt}Y4;u;pxuk(XXD;%SyoC+zH^$Caf#yPIDfIPY0UI{=r&GDwn%v3?`0VwL zh&)b$L9M@z{rvgNqL*E9mls((1wqk+Qq7fbfg6;2wFVt%q7-kMNgz36o%da!u&9Vh z9o=P&1t%tb(j2r2B;yEny5A%qws&@fm#g)G&Azn8#>V6?Pv@I4;T^oB64Co}#Z>oR z{4^<%BO{1EVy3`pL0x2@hs88FP(v1IN<$w^#tr$)adV7}k`e^|!6PG6P#o47JKtwJ`A?6vD1O#qz|NEP7C~q4Z--qL6t%Q0kYK`^X%^UiLXw1t;qG>aD(x}NUEwz} z4lM2ezEQbggY#0Y|M)yj<6D}_883OdTVrHiu$vFwiR&B;Kwk7bPhrPcDR_QntNKJj zAdV{?H2)FP1}oOR?&#?`bUprAfM{ZU~0oT%h2Nvhzaj-$ZF@wQp04oWXo| z=RpfE=YD#!A8(_VP49Ttqyy{M@v~x0h&zfKP@=GefC~x=nH=|Ue-NNUes(<)$MS#S zA=SYG!Z_qX=XUWzT-|bTeZM znjGo-kmULpCy5m|1Ftn37S8=Lg&v@c*)Gu*bb6Qr*AO=>m>x+W6Fc84DHM>0=e+cO z&O*8h`8ebk6GQIS500rH-HH7nzg5&1;Sd&^xrnj$C`0#o&*{wa! zABNT`Q~kcSu|7N-ix{&6d?UDIZT$`wzqB^M@_0coC!E^6r^SHzsStaks!F zD6mI7zNx{8;E|DzJD34b6^HG4f6fjC17l1@=c2DC41%;v4SNJY-LFFHsg8>kltXgkUq-s&!a+h8>gY*eB@R)2rB=^0v$agxf5#YKs#%px}4(iw)xHtEu(oWA~JPn;b5I}xWHFDn7w zWX0KT0@yDJ3|Z`M6$~cvMuN?S%AWy7Ed}tUF`qpC^fEm<(SW^yx|klcEDO(Kr z_-M^%z@B({)3Ma|oPZFa;h9&xE|273aq#b8-r#2~g#R?yJY*qo))#S$NkcDS6;n9E zWi+PnR;MsKf^zk7Qlc*s$Y5tnko7o?WQ2khe3f)axKZ8Ug-_^;WT+i4?+Ge{Qk$EC zvor9m(kEtWZS8Sj-U={!Q~g701YQ#FsKwc>GtLF2s;vwL7Awk*?^95^sViE!mv$T+ z4rEBImny-t<;!J?(S=cIqD|;?#YZ$22(6lQfX%kFAY1>?#zxL3B#gr}|5N^wC6~_# z(A|?Yvc%S4LL4LepmDz=fC0Pf|Tx;)HIVG%g=)`fmqJSsBMJWijYP zS9fTWkTK=F$V-aIOP64H8iz{Ljw~A53?iVkmoy(5MgGUQepS}aRn@Rv191e+2xblM zv)a{3U>Y1hp?`R8T*u!aQ^g_)c5rHGVRl7+ATwrO@#OCw5d&!lHB>cPFlx%^iS^sz zncO^SvA<$RnZ$5ru{o0(FIV~^=y27T0r^!^iHo{=s^F!{121~bmb~Mc^uy^KE_hMi z@#&lMa77VesaE7zb>mwNPWJr1U_$^6B};heNOLsJf~Ak z8-TV_GSKBRh2e#zUk`3~DAjQ-mPaloCq-NIV$Ps&e3Yz`2!@gQ6B z?wF~pqr{#{g)xQA4N-dv6WsjfJ-%S$g&nj*Crw>^AR8r3qLU3JFGxDGC4nW>O3sRjl2@C zfNpdAj(f}A(|kmWsR~lYdEVb$`AVdK!9ge|XXn<6dHrf>^=98S4p1IKoB#bs=liXm zftM9>Ff0lM$u1Hs3U{8UoV@&j*b2PY!}(AYWA_N_G~alVqJm_ZT0LO#fzZ7ynkv%e zVl_rS%Wv3-O7Ni|B&ho7Q9KfU&(z$CePG978|)klUVQc^F-=qB88=?tMCh!C4fG9A z2PWDVHD?t_Mnkp0hWEk}1F)36-C#fmthykPKMlgDjPdar$81b)O0nwy?U7ExX)n~G z++OzLvhQh$cdS|nXyC?~4Wq*JwGbZUbb<5DY)2|hMB>7OFc4r@qQL-VIndLlULpUP z2RwVUhqk5+s)jNIoDf#+tc5D>l5&kVnmwW2yI$_*l<{9X&KE77INsTh4~X)lTtfcl zX@;&)%2EE{El}q1|Djw~;M?<7Myv(&+E;0NS9d%)Hm@{cdU~)mtd|#7n`=BG^D}5k z^^=E)2fG4iY;amsd0ljF)&bM1qOb|raBE=K%Q)W>1^xaGA5P-_WIY7`FUw_At((L1 z_1#B90#)Z!DKdU~55b^rAw#@3kB9T)u&UM=%)q`ZonUU?A8JOl zb^gRZa-_npxO-*HCXDkI6f#ymHyad()2(U6d12@|@nK6X6!O7zdcPHR{>1?O>FacO zSC_C?+Rl~QEJ&b7d*kvx;}oI{j$*{~zGLD?i66)pQN+}WT^xMGSb=1Q8~QWPKWl88 zTK8zmb<|XRA69z0iU1J?1B2qV@5L_tMM*zcJaD2!ugr$i5MK_|@OTA0GCMZEerrc{ zn>-g&)ytCvHl3+5N|_u);+tYa?>*~}Yimsgb0--ZYhT$QX~1(T@>^@#4%@%?CVrUe z@eEpT5$rF98*I-{J)vb~hq^22<1AHA-CP+S%!sG$jH$=_&9+TyQcNApZ>B}GCB}9b z%Hwd9$|~&RC=3-7g8{wMoE_IG`5qs!T?DbEyG3O|!M1QDaqfP+B+D#jU)r}J7t3h4 z-$kXWbdU&qP|_ImX`VX&0BtBZIiJFUwaBCk^Ya7lK>`*F3)2szlxkHiEh!xx9BFCk z{r!DC)*nX)2kNVMbZ`)pS)3)B(*{0wlWAmRgB|95MH;LGgqvxGn)H;XfQ(Ot@X$N& z=7dmWLPA5Iu7$zu)xMUks-e?b=f`_#^oE>Zhq*#U2fAAtyUlJ7@8c0^zJa-?P4cCD z3QFoo)&xf|s$`sG74RKO#dv+KdwnRhu8zzF*%EHd3mInMKUn9v&V_lgqfe3Vao5ej-}!IYG%$Avv)H&zkTkywcU` zLKWyzaqyHZ+^~HHBL$G*BP>yBcXvbbMMMv`)PA3@{x*C#x~rzNKq)@=5ZiE4DS3I4 z*M4e0-MaJ*X@cm^4oGQlzkP>9NHSh(izigHyx^$~*e$^2KBZ*c&9E8W#|K99F3t^m z97<`EomM?M_fNk~U^3|KxI4mG>$EL5x^AitW7ru(gR?a2uIHQfF`HHp9{FPN1cSvF zwspIByi$T&ox`=epKW9Hx*zUd#!SC3M2wUsBQ>vCe9F1Q?<_1fUXd&Ew^vOqihsQ$ zLflpr&Yz?Ue$o4Uylb^NVNz3%P6d*?qc-WtgDAq2*#_A0Zms^yjSjsAu$ezc-{~?p zqAVz4$yxTMUXfe+M~(!|J%sqan|EDhx=5lW1IpEF6cl7s+HmR=Tf()OEvPE;evG<1 zesyn2QoMmG-b_xT2H;AQHDzJG(ZmWfo9!wRi0vLV{Re1TZw5OBIhZFi*-t=f4kQfH z;M!W0`=*YP@gFeH)iY%QUXU!a*@JEi>c<<(e=_0%$Sm5? z!7Focae~E>AqN8H(FqBMK^aZJ4vS^>yz%4>ZL&~GNCJ;&$Ka0d+eMOzLW43QrauII z8$$HaFeUlNP3w7s@z~7j*G2|7f1l5%@X3IlUws{cO*{U62nz6L2XNG9;OT4JY4e5Y7^ zp-it~Js@Cd1CfytMUB%lBpHVjl+mEP9;7{sR~YLza9N}su*rTx0axm3=i&LbeRLHE zLbB|5Ch_^LvdF050PZM|JRCGXH>>8_cTRQ;7`6!2fsbD*LFio_lmgv;P-;(o6h#V}x9Khp zxs6v4){84);{1(u)8HV}M>LUa&TnOpPoN!}z1*`%nI`^R!--XuyZ(e%K&R<@2rr{g zq``w0)74K1yjj!eoH1!g7w>EGzphKyGo;hTZ4oTIQ1n%+&HeE{vE$UxLI-PK6_%aW zH^;0EC+m$^n*?4M5b5&JdNI7iPf=L4jGv6_+}q#h(qa5$`W13ryF*^F%79Kk2)lR! zL#GvY9n>h0>xdD1Jkk&a`KE_^3|%G}l0K~8t%Lru%s^gI+s?8%cc&DGhzRWziO({p z119#F<$hR7ezzHnzLn^hJHLZ4c4jFDpZS>Y?->~QI#d#2lM3cfAWAA znJ%K}GkXPT=?_Ahi3*n{z!piViIL$V9U@!rb@`rfb7fKcok@EXqj%@JkJKcmU5~Sk zI4dgsLpfzRv=o{~oQKKf_)xIFJ#2aDi;T4n57ha$7hjlSHLrXRuMn>b-+`ZNG_kp{ zWT4!ZKL)I?$lc*-0-xyS{6pQPNXzQ>88hJD(>Ip0683U?&oiGXe;1<1Xhez7eTs`M z;Ly_?$bX5j00qZ-NGuTm(A^Ckg-ERmcR5i1g5lv1z(?J>@~LMU&y2)$5qK3$=U5wQ zb(W0E@15xLXHRBJF{F@rVMNU|jnKNX2s(fPn3!HUFv9h}STIan1Bhs5Ph6?Q!Lx=! z`qXs3WBu_#zFd#pQ0df1t1^hQWHo7oKX&0sMgZ?vjPA4id)|W1*UobAX+VbeoLaK^ zSs*9F1?y%{ej6dq=l!L(dX76>aLmGPB`j7kU!)NUn%O)FT()gb{V$RmT9-nU2voyt z01yBR)#$~!AcGdZ5!q)ns9f)L(hh@r?CNFlVv7|mp_<{tv8#Aard_{&Z3aY0fyvPb zrh;3sJ#<=AE^Rz+@)8p&TiU(6j9oMxwCQ+!gbu~&QV15oa9E-HF_wZT7JA1CihmF; z5Ux=t-R`wv!Sel5aaYV$xN3)x&HFjsK3+_Gc3xI=zUrS$Zs<3-Fe=K$a)e7~HdUjm z0wXL|vRtH0jRpm62Woga7}2qrFgh!&LLGIJWko=UTK%+F3(G24I-M;|*{=P?oTV3= z6rghzHfP*tSy!~&QJ}^}H9=o`y*6U%9@N8bxm2c_*4nDIJVai;Tne5j=H<1xwLwiK zUOs*1yAeRGeX;N3ilC~+vY6W*HGzWMVNOk4hRamz{+$Jdem`sL)<3RX%rUC6~VRuzJXt9lgz-yyJGv!uj_%t?SV&G274VUV`sdc*nv0#yfy}zfF%P zN-ssv+yea5BK3$L&Pgx6H+UL36EqE%5DeA114mg!@lgJ`USFgTaXo#GdEu>Z)&E+p zlG73DxoX|u^Qw6v0zx7MSj~4WVs;0}1ykse!Gee!jRrW8?74IPvO~%Q{U!5Yxxcaa zvDigU;KiOeDdz^jklpf*+k>BCNlD$2CxM`t@3G}?M2M&ybPzCTg`4i1W-3#3?tH+p zIrKsPZ2UdcgC~j}!g4nCEAG)x;t3F%B@tLgly$u!cr%sO6HG(|e;J+eMc#$Tal2A6 zUZ<{Nn+A4;CF*H)Ds>9;oUcK{gl+EpIRmr#zE6o4By z=74O7FJ0~OW$~huz@{%JEXbAy4^j? zcKswl3>=Z;8{b_vASPMX76{)K&vT235V+TG3(%fO#u~+?MP};Igv|7e%ARtR_@k=< zqc3lu`wISTV?((VI@PBOV$>;vlR|J>JNzy?8u-c1?#(CW>uIhS+ujU-|Va%8@NaHMaL&)nWSn&;FczSuN_UkKCH=fLaU}UTl-r`Q>{T?X4l?G)eDG zM|Bhjdv89P3l9~6R1Q^Zhw+ymaIVu9z}qfUO3gi~U8q;Vchs%P_sI7`b2<3H$AP?U zpmGj(^ND0CVMl=cne5gMX~zBqkELWpcp9fZZ1nCz;De6`bIx`uKPWB|&Uwo!{9z1D z!23%Q>S}eJ^;P&^&n;FsrGT(rZjq%v<9+6CA0`4Qa}fTLFDEFS)#vU6<`(}F36DRw z4@G|O_>wex1YB4(I1t}QinlH7I(zJDIYzTTL*QaIaT*m%zk(i^7;;4}XzGEIA zbV!Qn{fUUtbA`s#>NR5oNTjy+#&gKTyK@jE75{eW8w8zddg$wqzzV^GV$V0LuG0g1 zZM%{+y_F;V8QN9(>_Q2jA#vK=l?7i!!Ndy?L~JQWC2Y<`sb{#}KhMzPzb3Ut8BD}U zf%=WvT=Qr@8Fgn^RPX^I=ywNslVKi|xjuC4UBaYE%H%z(Llz0s>+#0qxDFO;V>s|y zis5tWYBZ6)(|PmgLXi6c57a>Yctz8a@0%*>7*B3b4hU||JGv3eNd3~)fpO;9^1mm* ze^oS6v*yI|uZ04Ak;OW|g3wVI3ENQ!WCs(f=ya3B%ro!Io6F5w(cmpNzl;wBd47M_ z)wr9lyGvIWxc|WtCYkFUx^NQb%r^io-s5UC;)|rnJ-uo#Ksp|OKvyE?2K%ZTe26V8 zu(Rli=;81#*8So2lOyXchyYzBO4GnUDQB%^Q>_sZZu5Sxf6{B#SAu233(6W*G$G+LhbT5Pl5(;~l3HmH)XADs80;S_TjWs$ zS!B?2Kj8D{V!f*6`d)h$Mp|gU*fBtd+qG|DSwk+Ieu4hxd)f#E?I!B*V?&a^B7R{U_h^t41U*=|UuiK-L?A z#jR{JQPM_&-ECl`ASEknFsPskTx~kLJ>F9UQvxxP<^pegyv??UX-yUSORdbd^S6-x z=Vj2Qon3j2X>KIPYHI#PDBTV1T#yvoOj^Afo>Y!-#ErIiaxTxGi3wq81MWuHZ1(_Q zEm@wVr8iE;QNw40p)3Ri*bABs@2QO&jcg(w*^nNU{x(xW!clK#u2|xo7fQhBJ|1eJ zioq9JM*r^|J$i(6u;xEM5)q)^Aw;v;DTRfg@M6b$N@(G)&jd41PbQ zp<)1byC^MlFwi+;zNV?v-hP2-9mH{WX1XM{(&_Z*IJPgVE$jgP*|Mns_^HF)hBuCY zWBK6x02b~Vd6uL4y%ICM(RvbigwA*C*@9sroR>1xE}vPoopyr>V&N>1^c~BmfkPFE zw^196Vdg4<{K%+HA&smsRFXOht?kH6R5}B2oq{e52!MQJx0}-YAUA78mUeO=fAwiI zDAD%J2U(XEB&FBUFxf1O}Z;|cge$1VAmRWnT5%Ww^cc9!GQW}Lgd>RM-l zwY72kJdTnO3BOv<$xS0&wPfNfc@VY*JOi6wcc!Rf8*& z(6U38uJoMNYT8hwlrPMvCrp|+Y0n3WLI}HTLUJR)RIm0i_F{uuD0K=1Zm$Nyg=$KV zIUyfX)3TGI*y1ya<$J(|Z9u&=)Z=oXsPX+oo_f>^sa zu$LkmebJ_B2i6ag8x?-{`y@csT$uILZ|O$j+;vDMY`vEpbD;NItSHdX*o(mI+3VzI zP^^!zY=Xep@KAq%QKabD_N-IPMkjd#yV^p32&zswMIQjDB3Q zg#|~b{O28`}>ts-(6T z4nWUStc3G3>X?n@oI^Av%gbKuXh)y8v`h^k)~mH(>Q7n)Bll~nu{(RKC-y0|b1Gtl zR_b?jR!<~+aSVj|elMV*Xi-f?RP}hKtWV9MDywP?_`Nw%`5J6;%`K=%S3e2-+R|Sz ziPyW%b|@`-y6wTN)w87<$-DtNoIU4>Fnc6$P9l^APm4bB>?1?YZTL+$%VM0yuKa^k zzTPZDp+u#X7HgnS%*XK9?IHj^U7=X1m2%3J8BD-6Yi3B&XgbZF$@sn1>Fb|jrnE>m zuKw!-IME$QYYyTJ4qKva`6hk+SO4twAaw&d$aJ?wQB!$)BUO6OtCa$BrgQkm=W zp}716K8csYEf4XjO!}XJhA3k5j{9=CzD7(j*08%fJJL#0qOUQYkwBCu@{4^MWJwB0 zqo|LBd1jbbL9Uz`RKlZ%7%xLFfoC3D5e&T6QE;8*J*9=CPc+EuS(^BO%*i*Fm%wfe zz0Ka@XI#!n5-HO^X2h+bN|ZB*VA2I8^S-Jl+C8Oh-RY#T4dNuN!m>VH{IgWFd55-l zhh1>OwjQ=7hCUB8LW33NRP+C~Vwf*6Di}13DX6inQ)`Hp(E4>{1x=;mnxUS(b}sdE z!U}TIdCtsMH5(dUJDN-}`TyN$CKK2625cXW2h5;$cCFhz!}?c@8OieZ+P@_pUmxot zS*l_egP}vXu6^4JX7t}~IMHwOo9>cxvuLNEY-mZO-v~G^kXWObVTT7BJ?zDEOqp;; zX{Sh0d7gI{@;)!q{jK2OIfHINZvM2xij=jKf<}zsC&yOrckY8O5ht5Yf#*KR1_wM^xQj?%yvlGil!eJ9aexTh3g+gNj7z6R6RV za6-R6@4Gznow>#RBhw;>I}yrNuG>8kjq@mXg5SDCUnA)6Uh-xy$CRVYT-OhdMNAKP zjDkGKZZ&P4&8C>#8QN5Q6-8sb!zYLtE(kP}w21@VwRdvoZ>WOz zKc;K!C$}4XHDy>a+MKR=v8u`^5mCBFB9&Q3sT=q9E&{nJV-*d*0z^(=Ld(UB*ZmGB zU9YEtjxt{7I;f&7rW5ee5m;6lkEJyTBWXv!K=<6|NP{HY^)TX#{s?{m+B_~t;o0?4 z)n5H46y1CCxh)Ud8zC!}Pam5o0gQBbTvlTCVA90vIoJ~J#|#S)UkVPX+;c+%Jr6K7 zTL#otC*<+xEd_Y(a+HF%cK}G*E9xwMGUq4^)zV_g>LIrxWl~8Vh!2Dj!*So}wf_?$ z7Cw^Q^8!zwFZ?aCZFdpq3NhsRcN9bNaogU^3cpN;Cvn0foKK6MM|m)KOjW zESv2{pgFO?pk$cy=Pr5mA%b!@Me{2yh6DmfwJ3Hw=NPvbNX-60FrqI(h# zH;eFt20W5_A|#_fF=638FwGq9;Qtr|Q$TrVaJOHf0%zWyOyOeL4pmOdj^&4Fx2(j%Mk7?)nPl}~T z!b}MMdv0%9!o(Q9-hbv&{-5ZIf8NY74FkPQ!AslK&)4`ruiJ>&&-}k*xgZtzzgPYL z@x8CZJqP<&6pR%e;0WUg<;AJGi3!7=!wBfaClD8`{?SnV z+>)SE_1CuJk?&>@38V1$5MXG>fvBys@=1Xb4x*5%P@}At7tiMrIGl~d8#tuW^h5?4 z3iQvwW2@-h7|2O8^!w)j1?C4jn;Up;w`X8-2cfG_D()D!dASq6;q;{q^kvz}w(FO`+_0*y}^w;Z!iYJ4` zipl_)e5Z0_j7Afusp#0QM~CxNOYHQQ&r{0hKm9Z+wPf~AX_o(FQ9QF1SmfnGd9Mdy zqn`!+eA|zb!v%t0q9Y`TQ_Ytf3-r9u*zJkG1aDRBvQye$4??kTu?IzH>G`U(j76s{qVX4$a%`k*!!{wqCN?{f@5Ha*2(AC~MgNX0NQJ zHPY_dgPIrLTdCbJ3vjL0rYW7TgYDimV`2GvUy~VZ5B7L%E3v}8K2j7H7ou`UIUenu z6}@>Gu%&{y&}AP^%=u0B9x>vL>v{qVzwCOOSsHk0^V*o=6MPfLfyJUPC6S(STFzHS zI@61)m+W_aYssn})pw~Z+aD>w10y4{TsV`%-AIa_&(g+zM6nf4$e%Ag;S)RqFIV4z zDc)MK;^VNZy$e2JIn%V4SZn2UHcStSd!FtD@BIF;BRlTj_YD8N4tNe)eY?n(Sr&(_ zj|LR5hQiPGU)W(neEN1~%;(3e zYK5Ea^3eHeQ&MH6oJ36f7JJ#?v|vr2Da*}_=$?L3txi>?R$b?Q1DP~vrc6nKjDFC( z+RgWOEN$aSA*U_?&9v4W!bI0_^~Q@j-t-4kGQcmKGJmFA-VmJy#78ol59Q@Q{?V9M zU4!N*31SvVFW>o&1rL>1$#I@P93Y1L!CUO!T0r5p1zdA`+yEFTp=%?luS3%?-^u=B z>)I5=m#=R@$-@yB)?B!TF<00Rh5GqH_|onxgy(|EAmCXTL!8I$eDGgPk0ww50?3IT zACKBUFyP3A2U3Z{kbcU5?hL5=Mb44+EmTBUN(v5yd4eKdQYx+4aKMiQK#5>8Cp0x1 zFE20u(S4@-p-$tI=7??`!O=)ZXR9FETRw|p?>+P`U7iB$+|7IRk2$cwnuT7AES(t&{m zr)b=cOzi!Pqd-{-EQmfY9TDwM>U0p9rR5KtNDkgaS#CQv`z1KWCb9P+2b1~R1W=5N zxNMLQKhsgM)1eKSL`4HxVt#Lhj_W#tlhha+m<*uVGNy(*h)VD=%{)%(5}HBN>Un$zoyV~ z^f>C1Q;%$X2;@a|l_%we4^yb7hW-#0kd^weo`yf~Fqt~u*g$2#c!#=951Ojk zsmiMZzszM5X86D91b#6K)pbVx!UW;})1%)hrKG93pY9V_x_zhv;%8<|JjFvoLub3Z zi9h8t9S@vdQZ>5FW0Z=A9NDB{gTPqG4fH8%&o}%^sq&SI1_sr{0msY~zO_Mk7e!%m z!cXp4%w`VGVof;sENmw`6CX<}{nh=~+Om#_S|WNM)X(i&L{xIhSQNExJ0f^ndjGaz zGy;%VSye?obtcbiKJ$ezBIlRj4f5_mzssHQ zu3-~i2rMfD9h*G-gVUGWvrs9K!wgop=#;6c2L%xGyMGXh#g5hDAD^sh~C)i$)O(1|uy)}!vg9GTb&H!x?x zbJ6EA!t^G9Cie>>mF;PR%L64_DIly}C|JZwrZ0GnE>j>&*9ssegaKf(?PIwlSdq%< z{#7Rl=|uilhTFd~bL!tf-w-}?IM0Q4lM}dGP-c(_K7uMHH`;JI=aX}x6Vlm4jhqhhtNH|m`bZ**awPg5+|Ie#sye7-Z7b7|%Oq zTvPtPSpZm$+RG9JDbVC7^S#>*JRG8E27%)FiE#SQ zPQwgYoY}<|_0UCz{71J3;aA}3-gKez>dmQl)K4x8NXNdCbXHF~ltmJPi?G`rUj6hX zE68!hWDj_oQxv1xQ*>2|7;@RFp}95!XRK+9zD{_#oLA`4b{574Bpx$-}KWyrAxGS^tH~kczT`?@*%! z<1``Hs}%gZQ0T11oVhNFvZMR;cd#bu?3ldO(;-M1$(EjY-~{4R3%F{(>$-2Rc_y_c zB*uy6aM0y(CWz;*nkrRj2CsnJe6spxV%e;tbOXtWSj$!%#t z?T^qfQ_v`L=lVYm`BN`eEECbwPinn`q8q-WLV^D;*50zKj;@Q^BuIeZ?(XjH?hqun zyF0<%gS)%C6WrYi?(XgmZ{1IixBo!*`BV%Tqc~?%?7h~UYhE%i5r!JTn6~*!{jVFn z4UbnW+oaz0zI>Hc)5&xk33uX6AcN#!=j6l!w{?}B6?zR4pAW|h|0>`U=wx95m1*+}Q>d=Ix>#4kLU@IIxW|NBr0~BNlnV7>X z0?oH(`5o&>6hs%O-2@$i!b-?DaYDEV9iid$foOW?WZXpI`$2EG8!i6(pfJ3aORVy` zKQw$e1+3TQWwk*^rEW6$T0&z%uWueG*+do>o3NW?#X$RvB1}5J-}kcf1YzBE9mqQa zdD!8io18subZbxw)S~2npxC3_t7-&pC{(Y{@?=O!|NCvmd*x_WP%;&s!si^=?|Y8c zciZiLqhzGY8p=|JKH-ZnHtqCW3P43kZ3{UPr#xQno3?>&q5Bm16grB*w=Y!@6O(5r z?voM+ewq&DZ~b(0&U2+P3rs~LL0vZ(%tz3;U9luKL~(n*CaTNBc)8K0mVdzBDF_Y8BJ#`jvOJ0kW>R3?PDBPi+K&V`DSz6WN{Gw zVdK6I*W%#eQlq4zTgC_f@b*&|21>fJ_@n!xu&b$+Y;lS2eTXeA%1Fg^OkM&kXulq( zcPmBrpP#P&y!#OPk9y3>qVBiMTZR3VCZwtp9fe_!a;wr)-c_jFJ_`Ky-};~u^k3=K z>>iP%)B-`{J#$H7;C&32dy@93Qc~gT+)o@O1jn@#oxKQa0H7>WSZSRv`ojSe^^ubs zb7B__9DkErkdd8eJ zH7yM*;v1rM+qS7*Pgw<7wW(sl@WuF1ZEbDOR4=TAD*`sVwLA}l40sb8IcO?3_uPo| z_VOr=F-}_yE7tk*9d}^K8z$7lzUVsPc#l|8ik=_6K#Ch2s+Gou$PZRx>4Uu#rTxJz zGU}tab~NXgmGx;v3tN*2!EnA^%>pcDn;(idQm<~rtk=$D{MHCkG3ygz+I-I}YTv-W zX%PtUPS!8KZ&2OBjZuW9f#bq$KWOmTmz5KDji6M?{~^}Zx#h)n0Z>f0glYndaDMa| zxJ_YYV;!R3QHLNL$%#YbQ$W&|e2M9e7@Kfjd$>Lt1mK1IQe&-3C+KRM8V1__@I2tK z7b~D>8B06S8uu8j2Zn?ZyS=^%`7jba;V1s^#B>PF^+M_!COw=O)mh!Yk*+ z*@}&IsLC!#ZQcj{L+WUOe>^qqZ(0_ld|Z5zgyB42<9B*BA&sPO7g^A2zTuVmPdBHK zR+w{T=r_HP%O2+*4%!(ZpKfLK%Z)Er|6EiM`)kg+K(f1N3FDCobdaTJNof=ce`ZOi zC+|{I(@ua2(z96Gid}BN_SlCs6NY8B(yMEwlbAb(MZuEr(P5P2KWuk2r*6nr zMKzhXIZ^2C6d5SuYx@i8q>OS~KQ52TG%&P^ER-N&T^yFj3^p?D4l=^Trugkue)$en zE-OvmAHHA>H8w-fFD%E)B{mi>wAYGdTYTDOYRmYH!Qx)Z&U7B4uyuD3cIk8`I5i( zF8xcm&Fi!`umkI@^6GS+))yW24S&h`Wv=Mzo9cvT_EB<_o1>-OUh5N`TeIO6QAa1K z9ohwm`S@TIErij?f)eK(92{%?*G2!qj3<-uDCiW6Erm08SgDCjNRcpJ|40kr`yOs4 z>^Q9|*fSUr<6@K?T2zDv%q$Vp^G8oi$o_Xw%FfOP*X}T<6^~t;?T(xazVKoWB5_vV5JXqOb@#4C+5s}rVZJ$& zZRPtRW~+h?~InatdAk4e4RfKoeNEjNz_~QZ4FTiS%+6t z2`NY(%8qOfOT=!@Qk<|}56@&Xo|w8nU69PaJ|{9Ce>>nVS$3ty_HHc4r%k0i+L<1n z51MOtU@bS>*735Cz-0Box@zsEnDq##B;O*{*3-40_yu;-3FoB;Ic92u5=)Jig;8ta zM)HFqeWQtRh8V-msCa?bKqGmJHoW?ccw{8ylU^(+dj=gpl@?yXw^Iv%PhkvKEF))Ds&U zcrr3Ff%6JOy>-r?KrF?OBFKkRTU(ps@wVQO5M@Q%Y;}3&xEhHJwyI)?#D?ZEezwuk z<(?>6u|FXp0M zU?qoM()3xzeAxc*fH!%4&KnVpyWh@OXK2j-(xQ`25=%_m+3JEYoT7_Y9@AWz4*1sz zZNmqLBk^rKM8v*f0m+?WSx}ds7*v-o!zdYNjA83yUtR?{m>Wr`xUWQYftfuO3hMTD zI7?_NRzj~kJomP@!SrP7j}08dJ1w{^5Gi&L;)NWb6x^<+Q%)_Ekt4PI0CWCg0ViQQ zu&q*ne@m>~2~6W1$4?>UD0yt{2Sln|nnb8$p8F-DdnLRDFWwWZelOZ7NNu@3Ow^D) zJ_$Z2pp+2m3X9E#%#e>p3@OMI3#3)`yKg@Kgqeg z`2fBr5{sqzdD^YtA1FFfeq3!9csitZc6yi9G8qm1%M}v&Zvi>KfG>y>lB!;-b=n=y z7AGBt9d$NN^YZ<+myy*{3C+WQ#E940$>Vfef*dD4!g_y+XtpPrV=*yg|I)*gSUCQ5 zsNe;WL`Y0sdCdOK=do&=NKlbhx%@*_UK#kSGzBd7a{Vy>EBuPRlDPQpo4NN93V zB$e3|Gy5?Y1$+e7`0bLW#W`oX#lMOPMbw$u>ARfvh`t^gB)^`J*JcD&p{mVL1q=H2 znQ;iEFkAO#rN;e_)oYUT_E>A&PFPT4gA}`5_8PI8%}Ef%gJfr~J00B`E91KeEYgw# zBc&RGDjbhpb2T3`y8GK4(f!dhR&Vkus}mlt$Ly+9esAjfcc{+}_12p=iIMVV)6wz; zuh&@~P7O+=xSgNfJXL3{@#&c1s)&dt)s6QR;?Hqeg(*xQqK!lK6N10bTLWaCT4gcU zn@gi zMt6z~5yCOAHT%cAb}U$Ob(DIo2i5OC_1#*042Nq8GbB+K{2>=>z9ygVG3M(nOiMK> zveY1)E~lt~ot>JDg&Y{t6^lT}j))i?ANRC=u37VCLr;UNJ%Eza zFseLN%fGh*%y3#N3dygtCDZbZ1Sq`Wn7({PLp^(_QSck;r43Qjyd&mRHX5uhb7*{s zn9V~F1EFl!GLRZi7j|NG&Y4?H=>&LYjQRth9?`U-BrGB$a=ZHsRvNDS2f^wTATMze zFMP{!Vp@;+lMDm0MT7^JCj4)PGzc`7EKwuS>cg9JmmSQuSXm)vd5WoVI5;Cu!@HPU z&7|3a-yr{bOC+T2r9KUkUuM1dy9&INkqRRPml=3n1EyXDg8p^3wVy|aMy9sn(xQYp zQ`+*!{r|E?DDuMJGHN*7?>5(J>R(d)_sg5qHOj)E`6q|$n!o8EE<}EBPP696)QXm# zp^*#jt2HX!Tc28ZKfdPw2%RcfyB2nKL^G5p4)u9Y7r4amBkZ`YSBaF|zS&~D!5LlD zMVZMPkjO5Q{Rhh(kTpYFjo*895?`n)kAE1~59!X2aL}wDYyUDH$Ag zsx8V>-DqjDAu%xsz;uD}Lisdwi(4<}58~vGZUaa-I|ls;HU9%vrS0QOId40Te|Eep1yQRV3C-R^1X z{d8`yh!eQx`^u0>`*npwwFdPcfVp z^5rHWb6KW$9R!vto!RxC7eN>91G|~h7(>ax9Fa!4!uD+uNHX19s>Ph?_TF3=kf@Om zr`QUO!N9}L-h6+ygkWYy@!$9L1HGN#;#!+>>$LbGleKGpV)2K;9XOvSm>pZ*F+;&p z*3S^kNq+g}m-1kmMTq)9hcUxbi)p(dIux0`rU*^=jzo={@Zd9G+nBM|4rd_rE%7f!iXIq3e7<~&Vk$^L>NtVG z#Zi)2p!$b9-RI|vkx!@6KPHSjd_?Eg`}p`T4bTqYv5*(KwZdX*qQ$=~NTQz?!)L^+$&zjs^4ym5XHo0*L=}3uk>j zpc#$-<w&tOn6d6kPyFTwke2-Kr4s)o)*bvmLdyUP5!L@oZTbIlbpdss z2!KTa$DZJZ$Af+OX%J9~r+G7=KtNc=$0tCwI)$>B$>%L3JHdARcjp0Rq6;V$DeaB* zRev+!QL^>qm|eiE7apvJ;uHu_+GZ2MvHDkVON1u`Ebv&_n!k z{#1y8g|WghhDb9$NO4v*kyfTvF%3JFtzTeRa(#^KN@l0 z=^M=82jy*XHIr>c$QgI4HeX?W8S$@lB-wSnge0V z^~*$`vN16kY<9e4@_VPFP()*lBZ<*#w*Y__3XxFwy#|GplprjU8u5rB`t_C8(210R z8MmNR$KY0@5X^|2ABrn}*i)vNEk2{1$9nynO-{>Jv|i*B<};Oia)#A)r%yP8^j#P! zUlDm_dSJH+B&`KPzS;Hqa&O5Cl0US^FS{AOp73>^Fu|E(O4m|W>OBNDnmdHOIk;vO zr?*CvN-=>>fS)#fi9x@(SGP$7aOD1dwJIiLK$M(}?tJnOjl#^V+3pJsL|Iu`v(Y?U zzNA22=gP1M3msitT$~&Ph-c~rs;f@V$Jm-hg40sf3ZU{j%9_2(rKkK)v@$j^o2#@? z-gMiOgU9C`0gADEglkw_f!~pe1rj7F<20Eg#;V;A4|GZ|yOJl+`qeI4vqd^6<*TW2 zn7`Ay;d6*Lj%lS8ResjUA#ih4h0+S7v4lP$X>9Hto~uz%)1GM3 z=57wXBMLtTcjA5`kR^$sHl|oNc0JNZ z6*Ch))8g%``L@_6nlc{M;OlTY^sy_h#p;&*k%D}}WzWclmnix@+xTlmU$hrB=dtr- zTMb4|6IJ4KS;kn?Ag$4JN^G|4sp8pVIWEmDCD8MeaCRF?Jf>L3LPM@MdINHnPuxJA z%X6;X%uM^7ZbYhW{FBd9%8T`Bs+h^{-YPsJ7&cgjB?R7yl#Du7#4M0zLeD}i5Pzdx zX>B7qvc+B=pGR~*Fg`|*!byMIs#B`v9kRET4Rg7KPad5ZX(`6(SQZLGJd3f&Nay?g zg(#b+6En)y&C(|rsc90v#6tK?sXUuu|KAi!2_gUBa6wzu-&nzAD0vQ{pN%(F!Qt!~ zJ#&zpPAtW;9VJvUsWY)c8yN<=*M56%%_z5mfn_!>uW-kK?OD)IcG)IRbu@!dlv3?6 zR%x~!{Ixj0CYwYzc>D9_gfL;pRl~xQVEA621}n94Eu^FGb9z3+OvpBh>y2?LA+n@cc!w; zTxrP3C;;S(a7y(Cm*aD;10%?%oYzdz`jJvIP}0gC^z_^~VQvaexBT=jKTW!PWxC!e+%J2DPem=UbLE*d{d;7HE$O7moL_bQ(AV}Pa3Hf}L;o3?Fl z7SKoG{TLld*;#Cb1bh2!bQ$@wE>G?L7wYi-Vk5CpOTgi8sW&Qw@+xfh#Drq!h)gs& znT*I?bmz%d36E7u&9+xiSCn(H;J6KsttG%sl=YvX8e^XR30iU`CEdq{Tg%^(379U2QyiNP-GN47!=M zd678&0+Qn7bnDYs1^p8f4xe5FJ4bidL>f~zcCm)&AYWm& zos);_JxQM^yaz(lH-|d84G4K+W_2!>bmE;!lEKWLqjF6iwhkLxxA3>FaB_V zYYr$JqV$P;$fN|BBjG8t4;V>li68U6j$sA%W4$fw6P+FdQ?nr(um=W2B2 z;`8APs7~3`tY(sk@J<~p$3rVOz0ttGi6x;|ZnOls8?7?m-ZhxV?y-1qfu;X4SvpdF z`MmN3X?Z~~+@MHcM5~9j`v%qGj5{>7vd3x9@-0h$D&Iwb*?fYv+9qo>=;AM>Xs&5D zS!EWdU$&etAVPkJD?|DX$7A%`FD!0@0Fvv`u%zB?%v&iG6PzVaSP=6w8miX3}7%bSILhA*KdwK|v>(+lB90e*lsAwx`~M zrE9G=vusZM`t0B6yBj^aV&a_Z>Qcwr9iQa|?`V{|Hw1J*;eheZGU;-Z3G^APx4e{NizN3$b~?ql-uvQ zYl1_LDM9X^`pRf&|3$)5u=~A@_x=4nA}JFncpUbIGWk41fS@7U^%fxj#muOfOu6s} zpu~yisW{f)7G=c_lTrU)FF;O?g0;0ZRnba;1j0}xmexCm<342|>H!e+>ff&QlHl(g z0JnJ=9R#UEpDYG>X?WhkWWDTLwfv!y>J=QZlg7zv9nu5~c!<*Grz27mw4Tc(4L}EU z-y-0?dB{v+x{TCh!pegPk{=@5)BLbG^j5uu5*KhLLw4Vmh`%%DY+s#{&s17S}g#!n38knv6PVb19QXsup`FODq9gs>h@p77~YXG_*bayg*_x$)b z0m^Tp6%K5*&B^nPRvrzg0cJ=tRy?SqJ!-lYTmr-8(_0$6x}T^)0@QmRx)q52a6jVn zDs+{`q&QxX_9BS!p0ZOv7@^(~KRbLh@48k^woLCLbdmRu#pq^HbI2$GnY>&H!?orY z(eYl6dn+zX5D}J5_7wxNav~iW4?{{LM*W9+k_*RoD6AImUG8|T>i;T=d>qF}0u(Oh zX=Fj<$h#|-#kt|#=Bvw7Ms4bvWAl74wa&{E!?C>BU5R13S9KB*MH8KIm;D1~WWY^M z&JMJMH z)mBwHGP0uO4Vc#(bu~4$Kui;Wl!`X>Jz4E4ron{Tvl^4qCM)Fro)`PIxo=?wngRo3 zeVx}rdkb(j#DY=tNR(&v_*cYeYk7bxPHs`uu(XOPgOz1>B&x%vELyN?ipu3}c0Ndc zg|(Wy5GDwGdheHz$E%jQQhKlQW1 zt;B|{8h>@9d5%m34Sv(bKHem;j6^6!?waf)GoXYHCd{}bh0=6JvGiLNx5C#2`mZ?o zlE3sO!cy%(r1Vq29lD;tu7@NziPo!<8nlpjNM4v=6uKM}LnQfe$d0#Au@pHsRg`#9 zA>kZZ!(qi$D!T*c+V7{MkGQ~=1nszK!Cm**L{kV+(arwF#YJ`PkO&NhpXz{N@;Cbp z^o@B0wkSc4_y{=G-mn5~G|eAkpuBO<|2HuJ+Z!y3keB{j3SL) z*wfR)++e2FXs+m`gWufTJTQ>#4UdHN3D#zW?3y$lPDa<&DkH+La|eN6E)0ORMyK2a zjnx_d7Vw#$lqyuHP$T+j2d?zT%oLg>D-{l^%*^?qQ#Tj{h9T#mckM%bNqWUCfHKP_ zN&mPd=w?YOYQ-{#k(^{Pr;4B)nFcqeL?i#H@=uyqJ{a}zNJ;&W9eX|)TXynK;uOlv zsEVvko4+nx1>m6&kypj8i!;bfR93{H(bV~7p+Q`Hm|TSa1PG)* zdp=#9q{8t=wfMAH%-v)reo^0|)ZDG~1#ADArD4TUb3IM%J)J?G7Q9{w%&fbRXwSZy zIo)@M2S3?pPDZB&w;?(SS*?Us&_mC5pZF=NGZk&d)#@JR2(gIfrfgF=DU>A{-E7Tu z3&W;A?>iq(-od{AOWTC%{OtmY>b2J;+!y3OWjcIs7>n7Mau_bfI#C2@SKWvbiU6V> zkZVWC`12|o%zUmG@AK0qlh-|eIh__k5w+Fhff^7|_s5cCNGSvyS)gHY84+tVo1V`b z6l99rHBa3<@6Kuja3YXo%b9btQ1T$FW0m)XQN{e5Yvl5iO}J@HC$~>KQS0yK!}25Z z#<@{xCV^G{A!_eb&px8K<@Eddao=RQNUhdf^3KCFQ^%*nTo?*yTVip!&)YlGqb;Q- zCWe|PCcCwdx8VM2Q>?Gngc|^LbZqc1(W216FQ=~N93fn}X0NAS)=Y~_eC-ib2UI)x z&cnHl;q)zj&zMd%!_kRR5t<*Y>F<0s*c5J{0l8#UWN>LQ7>kForkpin6W9t8SSLi# zNo?8?NR6%F=epBH-Wj#cb^SxL5gIT@EGM1N+UYM`P@tD=8N_pB$areIws3r37&*G# z!FP5UC-Vah>K6Rv1#Py>^kT_1oS$X&iJ^Xe-oMmx3zcEE54F*RvSdWLekco)a&zPB zxMHlI8?VbyQH9DU?&#ZmD}pm-w2|(_Zhn6lG;YqMx5<+3P|rU1eJ42B4R!*0LVG(q z2Dta4*sRuJ(nY(s2Qr0)g{WUyoDP616wT!<8UKwc26)~8j{#Un45rW%P+x3yfdTPh z#U4@E@3-UA2h^uyz^Iu&FlUOvaJ=y=B$cOqc3Ekth){$^rKjr;nBgIv?N&G$PmMW! zQ71=~RkHeCFx5?_9Q8nrr|UYcw8yX`493t?#n%K5Qio(YyM?fe9J8-B-NvalOn6_> zZy2u&nYL@G(r1vEbk$N-qnFz#zR}@iWl&1O|h}HxF zKFu~9NmREdPYksP>wua~##lAkg;U#+JHr+)qRjhqUT5Nw3Ebn$BV$zMP>>Ajyau>|^MaJ}Hn6Oo0W~GEyg5UlXPw};l zRROr2M+Nc`1ergCs42*!o$&7ly(&OK$H~F)M=$JU98eX!bX?2v^=4E`i~GE z9X&KRSD{|4igA3$RL|Q`U%y;yz$YZcVZNM81muf}lq}qSB-5?8yCQ&8smh3pf5(

!)mMOYBH32-l^>ZUWzNemRG(4T=(8xTSg#}E3oHj~@@lMeRwJ)2GZ zfX}fYI!S1^57Mh?7vsk|cl~n#kgL4w*XcwjE}x@tQK{b9ej^||H}o(n6}FunSMxmc zxD_^#WP;OO>>ZfS$=Nn@IQbIkUW8((Nt#bcxW935WD{Cz&HJYnMYqHQ4f4FggVAP( znY-Y~KtUkbqkJ}OMw_21ym*|;iRfT)zV$PCpht86TEBYPX)A_X5DfH7Vv1C^$ZNRfK;3 z5)S!)L2mZ)JzeDgK3$DRCj_kKU8n#}MO2_*C19Ob;pP>lf3tK-eCSl}|5^=jj&y2?b1 zfGwSi_k||mQZ+Q7_VW>VxK*oO{}DE4_XYGh9t051{IwI9 znEdsAuY7U@;=PFfIXO#~MXcLRSf82;hV#7HK;1vYP;|u!2L*cc+pD*0!qu&+5J6CA zgdM$#R{wQi$9jDgrHT^S#R6fjTcyF_!Y@AT>zgw%Yx#k^>~c7>YS8mGGmP1^(Z73y z)TUc4L?QZ8uFr94_79(Y&dn^NEyfDQKrExshPoqj=YY)2XMKRj`zMgWo+3G5S=sAT zf*mH=)|8l-IWIOAW$R{eNC#DAeg-=aW@BhI2iM(hS+w~PGgKZitfMwzI!2=|*GY<3 zXy$g9W4uh~fJ9tYOSD904eLvhYs=o2cf2^#Xt_|MgY8WwB%FIcAbQQr=cwOBOKO5n zqJr-`(*yE2y3bJM%A9)hC1j%}Qjzv>kwEv(S=HOOTI*$8y97vDK1E##p~9bF>qvcc)FXKQggdr%90Qe@V^){85raZDI%K4(>`B%;>L83 z-`gM^$a~i|It?wXM6d5t?&2RN;Xn!&t0-1!y8J9b#7TJ-9tzFI$Xbt>Tw8K9xS;G3 zP-f;gjm%2mfmbc&jf^0rtEpszC#0lvaCH?fnZ$5w0C$$rvN4@d-602FV3ZF`dT>+ zbZB1FAd1LrFXdCiU)E#7Yigr)jWu9rM^76}jx24zp7HfA;d(cVzCW5GV&I8w7MPGG z^-N5maX>mT#~ZoDYE0EePQd1Hq}1NKSfO|GWmg|qxn#QWy%B|o zBVC9Eiu9LyuQ}m&+=*6ieK`LULo9|r0}*0M=}CkvBnb#dlf}RO2gpH=5*M+RhY!Y< zYuyWA0Ao&rEfV%`4Tks;9FXJl>JM`?9hda0t|>o)Z?v(_jFPg1IpO)}co69)o}{!gFj^)ooF6YKBTxFj7j<|cE7|`KSB+7d%DJpe zy}g^+)f*oFN)<`8wY3@9*!XR?ze4XDSuck-kLI^1TCq@p4EtA^c7*Exv{PT0f6;iO9fI|ugpe+Q2gaG6ZAe$9CIGEvZW~XHEUtfH=ygVQ-=PQ-9`0&NnbZbVaYTfW> z{ZBxh4lM)(RHd>)wJUyDT+Yd;0Yby`3`hFKF;0E?u^)@bEBEon8ld1>phIoJAW>nX0f$?8@NkZV~ znH2>Skz`~*zfJH>;fR6gX6p}Y@e4S>!ye19gEs*LB3z)+UZk7~f`s(6+*Ikuq`BM(SH;43G0NTb zQ6*h-|3B?EmdAB_Tu$rxGb4?2LynV#;u%E2&kA48YCD;+ot`t2 zz3p4lu9<=~*tE?UF!epQ04&z%Y(LuSmsa-UnSI$-5*$u$#%dH|#V1rKi!a5GBDFA_Lb#*SMocKkFHNCH3_AdH@mxGLqhXqVwA6rA3#s33rG z8rbX87!5!twHo)8dc+Er$yye!MTm-#rd9~zL}pNDLh^B0kz#W(A5S+oY%*T@m!%mw`;;+;QgTffB=ed zLOQ-EwhFblw4`951MdCyNWn#xcD7X0;Pb&9l;3Ycp^z=BuTO|UuMI+|r}MhKvA(W7 zmC4_~KY9hAuT2^?ZOZIH6j4i8D!I~A69xn*>jr2m+-9A&D;ghf00~d1XLjS9$0vNk zE07l);+N$sqw#XayhAfN*E?}W_nL08T#Zr%9m>{epPhhpp=Coa?D=|XK#@%%1vs$qiV{%wQ1{C@S=8dMS zc9p0w*r)MYb+^>n4M=oVPE&+0_1|xH+1};X;f5 z(8=gdYd<-)%v*5WA}`uw;!?|M6clUC zMjD^&qgJQN`H0ZkX5^}}EY_?Tbuu`5hE&EoX+ob}uAq14?m&)T7u%gJ{y_=FGP%vG za2xX)Pya~*x4;{l)XS^ScX;!Mz1;jEBSy5N7E@<4t@yA0He&Dj9#bf16;#i5Q|9lI z#9V6`M}3Frqs?2k)8Bk5)>`c(^k(N{o*>&Q@4Do&R`=fmJdZht2A963pjkl!cf_q` zH2OU~LIA=lJ}bcnSLM${rR;y@NkqgRK)6z=QbGe^VPOHJwV@=M6fQsx0ni@Crl$2q z!|<1zaAx}>ij;D)K%;qRVj|KB$f`(7g6IKgT+bg*TO*?j3bA8ApNB=+mq4-PuO)#F z1}2r9U+OX@faa{WwH*@jKwYr7IP1IU_Z&feND#J8kK8?j)4NeUY8wcKK*la9ergf( zYWIMjIwX_wepA>zTNpo4ACE7+Zl^47GWqyhQD&s~%4jRIX4z=D5u(_T%-j}1@kCZ^{IM#kI*ku|Qb&4_ zK5Mm%%b&vo3)3rEl=}R0ZY2^RPNPLw^J&&yu2so;6+x^(#|@Z_VX?j#Ez)QCyDNGq zGUmPTjH`D$646k!X4dM%IvaYj&xRvh;0`(&{NDw z5&138{@VJ>%!T2j#_fj-&C>(al+_e9>AkLNzO`?$Pu0 zl?Mp}ElT47eL}N2WOkz?Vvh>Xvd*v{LDszr4(llZ3Mx8puwqW5$@x3E0vA0E(M>&} zMmpN|*_n*7aUmeDh2^VTBYAjuqsqW0iENISC@{?GebKw2@el$ATy!S zY6%`VxHvnby1Tmv1+7!b-!&Q=8-wUu8OgvtMG5zI*s`2z6xSWv$LkW7ny)V>C2oUx zb(oSMS$Jfe&J@K}rtzQ>#9T>sB!$Ue4vj2%#Cmum5YVnlHdFe^27hg@&|?0|h;$Cj z0KzXN+@BDyygL(Q;g;u5hSl`s4>o`dR058ynb4Q8{7mTat^%z&_z`w|Q8jC@+b3IF@ShiYz8uN}@2MOX=__ogPzdJ4b~;YTr&RtEh1!PFw0r|Lg9~CtsQ3tCVp7 zVShz61r2%U;Gnwq@W@0bS&r{3QNKjz=z+;vDJ&b`#z+LrVgE{wJ)7>9`|D#$g4f#x zvd8b6z1QfTFQcoj{({oS@6o=o&FLU zq^ItK@ND&?%)O@;BZQWMBE$PPUTnLS^%Nt=J{m8G2;On^!mE%S%6Bwzo|b0f<1{u= zYxiOKZ%*cTsemq{OKUVcfy>WX$h9Vt=P&;YI)_-QMx8!>hG7H9we@;<@KwiPCDs$N zz!!?N@o+%8pCBua-r*=gj6prvrPcG31cAfcjH1`;n*|85)|T{}KHYdu+R&ogaO_~Q z0CE~zHJ?of*)F{gA2k#Q>FmEs2{$)zlcMN^vqAGF&Hn&&{QUP9Ybozu!%_=NOBe~G zK`YJ%94cbLd;2Fq2<@oxZXm+uuwkA3(}wHadBZdi<$y5T;$W0gggvFQ@_JuthTCX0 zJtYZo#HoJRvYKKH=Go;Ix8IE^1Do2XOJth+$HIOLF+sHp}ThSIT#oIE7 z#Q5#}`{(mC_n(W+APH{GCy@I6-mqD3Dn9ohmhZ!lW%;j8S{MTGKA-U{`k;j$H)wMd zvfBk<(dJv_Pxid~$X##lG-yeD|KfF~tE0A*j9tytyH8aUY1g>i?O|S$_}Yi^Wesnw z)u#x|j!uY&>z79*Bfm?9mn@FA(hADd{sBR_b8-6MQ4zlDZ6>2qoYe2SMuApse z5rA=2FxYr~AkCI7Z8Z;6soJtE<5E?tvT8ToFSH#z{8w(Z=uY?-?o?*nH17G$ZTDa= zR4M)^6{#_4x~px3X3o?J>abMXA7SPMji0iTcu}Ga3q|nt1x-3Ew9CLeV{#0QM)GV* zN*n2E-HPCWDxNJ`7ZR*h1^H_~xLE}oXE_x=JDw*(;?KJXQmNuf${c3FWHeTb74TM| zqw>xvNqH2D zGW4;q3OkzJ{$GurD24bIe z2B8BpM@IW$hRcV+-tet+oRp?v@Q-kwGkMXV-GTk3j;xOlmXV{AqyqOSK|6cH-8EL_ z!#yQbUz9xfqz7pUDH5hSpIA5lmhTgvX#-ZlsHB5KT>}j}%N?&}6AztcBOw=8>msvU zI;&8!iS)R%V#NaBvQ$)25p}4G-ZjJnX3}}@XGGTdE$8)qq-$1D{M^W|8jTjPA;ylo z-^*DDr)Ci6Z4SZ`(Mrh3>=|XL8`wX}YjD4LZj>mGph6p2u3MT#w3OU#cXaWpi|DB} z4JtdYl&Z3&D^vLqYeHS4cv_@H9czP5Z+*N{w`zZ9a z;T`ZJ3Ore_`e9AGcf-Z4J=CDClXDGGQ;6e;PuBI=#$ zN5`h}OG`@^mrdm5;Q=r??{$wGC@5$wDKrqA0$|U;UJ0#!=)VL+nuAkk3fN;x%-fpm)o_g7n`LV%r9ZGhXWIp zm9I4gulq&h?9>}Zr>&2)T>5@@%lje*Nr)YB z=bNzWujR6V1obL(pb2)KzM$Xzc?%<65J#n4% zSl9@Y4hDrD;nF)lPfsv0pL1ax!`?7Qd+Q>q=!;pq#M+Zhh=b}dA-@o|idoS?sIM$E z+NwTQuqLl?>_G2vw&Vz%ronujcM5V>{o3bChcjIHlQe;I=@y`L8+S1>1{Jb3->dOG z*JTSywA}bNKDVdP(t+MW+Y_TO+JZ|XjWDLWGNhx@?#!B^keqN6zrL?`L`({`|ZY6{*FsL^3^7wXwt+J7y%t|!j?!juS5xSgZztiI4ZH(oy z`2k4r5&W0t~#ISH`-k&C^<3Pt&e*A!7^oK?di=LR-PR%Qh@x3q_8mZ(v5|I z!NIuHR6zp+|8eY{>m89y9@iDC7LeQ-AyXU(MuJ8xG$~$Ex?q|;zeu5fFxNvx zc_bcXXN^CSgsY=&-gke{dpS$dem$(~U9O`Cok@XDMm+DlU7f7A(x8yb8egokrA{uC z>?S5Ae&^Gi2F@VQBL+*x7|gS?vjBaz-BeqD=@NiA$#->i{YSut6R4?Zuv#J^jm+jp zwldVV6=6E8d%v+*MyK0fb@o`M|6&1u+0n#LLbD7Ao&GDMB=^e91TJ$`B>^uWR-zpUzHknuhAvWh$BxqG@eS8ccP$>b2e2 z+C7=**NxGEb^_4?Q12bA&yV#CT=}X~)#`P%!CzJFV-ISHTua?21!{^cIi8`LGImM8 z2~|avpCwDTVN*m$iH}7s2XAPQS{jp*u4d=a4 zeo8VIt(dtvP2v7tay#y;Q4ATl%)Q>3z?Rw})z+s$Ok&)+@ zzP&;bQB7I!DoVN`c)g1He@4txD4n+Ai8DDCy>B6ULHn5kB11>tWA?t2DVj{{zd2<7 z86HCpU_n4Y$_=nboB6xknU#CHZ#yh<>@R2V#7AhholN^qqvLb!BOs`;cBZ?LPTz^X z8mNDXR==wxIU3^L3bEDY)Ox8occfj*)lre%?LhX}75;vix!+u&g&v_rTt-tLFrftZ znsezQVJGH&P=+d_UakBZjo1tkclwo@z9SZA#Ewzg^G}Jl0f98Vi z%6H!<+E$m-K&{glsY0g-kB$W!He9uGj3B{D0K?O)HGzbF?VrpVe(nNizgCsLb2s{* zdw|ZPWe4oapBwQ7zu5Ww_P%41Ob7;WFQ8(vYIsU$DtOVYP@@5&;u8`Q{O8`#*1~@x z43>-*`~uSX0*PLnfwN?qV2YhB7baKiJdPR+{oRr8bp~zdqhK?mZmt=SpAL+I3jaj~ zJi;K71{VF>z!C?O0sI;`C9vp9tl+1|#-W@PiW=9iCx58yBB zb^jR%goOZ4g#M=^v43wv$o9S^n>&7gG*l{A)X4?!zy}h|Iq8~D>bb(f#?pVYu(OKr zUKOJZ;)x$;qdg@1id3Ln!;QZ@_d{D}E`aCpO#J4u(%^Jw*@jd3^}~krq1(Ci818Ct zDnX{;2u7#HV&yIZCVfj-R5p!=)v|om@@+!A#q|X@Ee^)n6*WkzQD!6K4Sf)&Q&T-V zDj|+M-eSzN0G!gcC-oq5x21o-F4{$a4|{hj(ugx8#od1AgeW&c|_j*qfbe5fkyw_zX5c?wWt&(;7<^8>AVcgb^y<0n} z^p02KDH5sgD&%5?w34Oo<#qC-?Ww$pmhO=u1Ia9{@cOsgmADUSjeBTbS>CaZ4E9B4 zFbEdQxv?ACKL!*eP6#s}+3sj_zR?}{jtUI$Kf;}oY#3Z(X)})cE59-*HPPe?9Ph?f z7l=YGK{O1&^YbH5?~}m1e|cvXtea+jbvXZWlA#kx*VKzbL60)Ja)T2vqc@ftE?R%g z1`*Q!>Dkrd6Xi)ug~zq(;$CRtC};bnIMU)H=#O<5FCtK+q%+ymcY@IMGM;ItvHO$5 ztSNB)o>Fpl_4fh$;M}dLYP=Wgyw(l5 z5ZYqB!!~7prAD;Zb9BDH2*;RkJjvpZ@=1>{*k8?`v6xV$rB~AvbAWUkai5(RFEU|o z7ZqII&@=rW5;kLXH)x7aBEDm`KcbR_EnaQQhgt0-`gjl+uTptF_AgQ;lL?3>o+K15+j$1bQ8I0;`L8*CVG{n1$J>A8g2ZLzBN?3+n-MirXa(!M3O?Yf$;MzYnucq8T88^dRIX~-Y-oc3XQJDtyQJGfIV zUV;2C?;`6g^61P58Tc=sx6z?_UTAl{*_^0K3oTWoLu0Z>Y~wNSUOLm6`>jHGSz?Q^ zI)^J(xgA_qp&JQ!WOtE*G;Z!jgFRJL6{KfNFh4$fs`#m);VcI(bP$^E986^K)<(N# z-sfAcBo2akrIl~VGN$O!?`TI%2<=*3B`OI6b=8=g{rT$~e~XDDNI46B%vlJYya=(}}c2|yT&W-M}D;~?o-p%z2%F8B!h#O5< zZnI`+@xr$D`v92pfGvb@AUfI_29@H*R?-s6k{5o*No!p7eZ=(9!1(^{H~1@`pDUlP zu<4z><|G4m4CIhU3Y$`Mvd_j@!b8_7U2sA0?NU2tG(Ac?3ID+UGFAGj&B{H3}70^ z)tOBJfob@R-mc*lsFQ6p-u2B}yZj+0JOBKMx?y3nYi_48Jso%bEVFEoXQNB}^omXC zvj*#FOgS>J5%Z%|?= zEpwy9+`aK)u^H0d)QW-~t>Y5%>5>M1c{!hiSy6HbH*)lg)&p!;H7s;;LhFMWp0u-M#I79ZfDYAV_0FQQmPM z^EIRkNnU+EUijO7xs944b2@&59IG&od5?5!vY(}CBkOwe)n>&zD_Ot2|4-)X)##-!mZN&+&ug47dF8!RcoxamgHpF`{Vu$+bNJx zEyrixkT3`cMj&J7q2URm@gs)sIEeJpx>@4jIIY}MGBGKC8KV32$~08?i?4@8+G#EO?s_zV^O*R8enJs_&Iiw@A+{4KemkqpUQM~#PKNcsN}I<6X7lckl+v5y z_TqKfx#dH(*+~y1Uun7)%xJ%L48?GQX)lulkWE}3Eq?@uwU(J`vJ0<~CuENX0M<4L zL4g+~Lo3l1A;`Ys(WYbB4e!wRQBEn+YYo{Z(nPz2N5LQB6ldS3pD(B)O`kgvAb3`8 zi{*op-#gz~3*BJZ8*uHc*l9%>Pk3^Kmn!pND;yt9CpacL=Z>~)hsH^(RLF@6F3J2I z?5N4qtZy52BZ}7PZs5Wy3H`PZ)F1|afArgM!K-F-pJ5pqPxm8ph5CbOez_a|GVQxlT=w5G$-mqW%?s@3DbQoC37mkd%X z8!s*~Y6~ceH?@~D1;HX$##%ddeE5r-Uz2oY9vF}@_~94PE3HJw#noLf&mcitLM!Bu z+v=w}ONJ(-+XReYc38n}ls*c68^`faM3frZ$93~Z2d`f~Zm%?h1ktummS@|$Ug$vl zT|d%x2Q_Z{&8tLl#Gscg1g}(-JNIc#-+99~lg=!j#E3lSIxGkH>vR$!;xPPpcwE2Z zgd_wYcZ~@wzEqJklkHf$Wj7huEeqszbKYnK^|CswZ;?up``wc_+XB6^z@83f6zX*M zyIdz|wYG62HT-gY9T6RTu!SaDf-0-rEPh5!~U_TyJg?bNGhz9D(ZViCp+!KTnZ_S(0!s@UO0_}l@N5ZP6D7iD_Gs%{NOM3Qb3 z6uH7j*iwdf*c0fg`gs!6?DYxBaMJkkDqn)Fi@yEk@Xy@aBc-N3I;Xe&S?d~(-rkCp z>xfT}^ZnEdzBM+6V}Rv&s&>|ZB_X;p%!rD9Vg89`gi}Jt3ewgaQ4BIo`L6c8BEFLu zYnHEX7KMhCO!hNRcgY>%xz@|GWXamPWA(1QCQ0|Q^z5V2vnLk<{-kb`h@;tGiUzG1 z(46iGkL6xtv3`-Q!~WfemF|i5+ADFsZ1(mEwR_QFav4nQrY*`B-no*bm|UaBL)nUT zr-&3|%X;rqkkh6pB3!jY z#zV%>$5-*U3dXwHqV_f76E?Jk2$Km=#&`6h>(w_88#wlM9@ei{lUJ``!a9Zq1mjCj zBjep^Z3xn}wXv-5*l#48HOOQFrAl$`Y%xpB;A}{#1oWjSvaFxcdCk2(+qDV7Ql{Xw zQjMKi>y3op-Lj_NUQH0f!FWwd5>gnTc623XrlBE;>etj9DI*`ePoX$@(@EhQw9afG z2=5)`5}ZjFvs9B>(_=wuTGSl9oRSl0If9Leqe=c?Glk;Xs=v#kB0+Jp8VU;Vbu_ zochmS)1{amZJ)p0sFCSyXnjX_^myjt(|#oL^b&_Zx0;hJRZ>jJoCpt|#p3yPRK2hr z=sLo7C3p7iNrsJ*_u_IvDJ6I0OurL!I_a0U=9vBWQx_ueErz9Hq3vzJSBmr(_yW;N z>=;7x4=i|hyX?Uc1XllFvP7oP|6u~qtyO}|v^=QMMR(&X|KI?Jd|-?6zaeju|2+ov zKO1l)KFa>v*@6g#S;H*)N0SgWl<&gRA0OE_I zL6>imrx!4;b}wC79W0b5P<~5FNg0)O-Ac1-dlP-KNzwc)M(*8MQm74Zu0-wAKe@T9^PNf z5h>*QloS+x44Vk0;Gavjub5~K*sv+<=*UP(!7(yUyPR(XP3I3f?1<3z_j4_Z(i^CY z)jpgqUJfr09_l1o-|Z_}0u`Kq>9ld{OpcMHXq}(ClXF2rz4PPCJnz>9Lk{WGF|x-4 zH#q95dL`gIAUfYZW4?z`qD$rYSkYs6@d8apD}_H#++U-`7OHfGjf{xp`I!j|uV(N+ z67ZhoNiz>|Xx~dWFoY5BsBOHRD3iOj+}!I$>|yo)#v9~ zo)vU-#+D1}04Eo&Yi{?$!0K4}XfnK=o!viL6##}m&N_NHmG@VJHs+T>)9T}C6WyBs zdzZ)ABbLYR?cz~35$W!Pt+8E?hZPho29rdBb^Is^70}Epsp=(5sgO1vrm>&SVlkrD zamE!J2;1mbiEFcG^mHXm!Ic}#LuT^-Nw`ap){oKn{7JAmMr5iYj8qZAi`n*%`)4UYZ&{e~2IdHF#LmYIBU zd59z_vW<-m00t*gZxjRe2O(rYuw_OA-UI)dLBBJahz?J%&GlsACclfh-Gcf?IA~cv z6bbFe#KvOTNs+mg16Unuh3idh84u6p<=pS2ldsfL&3}ybgm!nsR_uPM*!Qn4Vl^oB z+E5nP_rgeccsyWFzDA4SdlJE zX-dKtaZ#5b97;A7u{F{~SIu|OP|bUIdvBi=^%=1#DeA@aKX$s}qynieG=(j0oufPKzK@?_w=F`I&k&f&cMhFI4$T zZ9|gb*{;Y;F`n`DYJ?QQ# zZP+zIF%kM)@ImAm8p@>egs7Hx{c0aWF*vr81nup`0jgl|gzgbyhTrzKNU6y}c4-bv zLG;Pr_F}V{JV}x{JGc`wl1H!+K>k#kahbQx9XJiU-f0(yy}lNsS}vW+2EVfUb6i83 z3`ahvRP2FXavvDmcoW54d;ZD>2)gG~zrUhe(h-1$KZi{ZX-C?yCb)P&O?I?Ew~=zD z;_JM@C+h1&t0pubz7?mh=?=qhn*A{VIRyH$7&OIsTOr%G*JiP#maMnCtG`GyKa#CO zbI`+`Lb03m^mJm7PHC5q*A)^IeIMjEF^W{t%eo_$Ou?x<7yIk+ zoL`FfI_bmrI__AryI7Ak+4VgPB~^*3|BT@oKG_Wk3=hiSb`=wa2ux9FR$Pj-=K zXos+pNsVVx9h{!c+grSlL*c8j0Hk;VxNruC6q}r%Zc0bC-16>DS4l>hfEH%Jw8h76 zgYSR5C+$XPMoPKTp@>FZp#@5iqEt75%KV59K1Nc=ACE(z>5Nz7ev?qQ4id(G11p3s zF{;7`;$%btlh!8(W;{><%X7JWG7Dq zo$Yt_?54du$NCUhJJ26nI=i2JOV$eV7tzL)y)?OABuq#tE_Q4S;PHBwFq7f>>r7}@ zcDX)Pr)&*3Nui+C0v%#b>7432CYyn<@CxRY%bA@n(NrH;=Z}d^N%wpSnWSV%U%p(o zm+`uXgxnL}Fj+g`Hsmq1@2&- zCo>?Ge-j!YUJs0phKZly4&~@CUdT$(cgFL2Tx@%5*562ykNc$(EA#2o`X+DurEv0R zmrv=qgv4AHXqD}iBXZ8o3e}%B+<10DYr&Xm+@vg8zy6TFJM(ZU(P|t7%y@=Be`&^o z6zMqpUec^t8#PWLk&FJDz=iQaM95WQ;i^No&%54lo>#6-X1uj^=6rw`wr3Vzq$ zu-M^qOtE4^V3XNCyZm+WLQ5x0hTO~;XSY_%R`4OIo|e9$2b?qJTMEppbF`GhYrL*7q253YQr3oEZA9Mx0cRh+ z_zX=OMRF>N4Dt~&J9xNv{^Z>{NpLN6Vy=5@F<(kj*97m1TddSTL9O0@cvJ^BSLmuu z*LWJ2mKYxXHy2>$-gjSzt?b&t&ibsUwph>D+$e|SQiP?{6O&JmCBTl(-^^PGsifMJYr1QTKwmtWMDojZv(nNbY>Ulmu# zosCTL9)c6S=TF|TTmwDwxw4|Eoo;u_G7L8@??<)b`5vu=(FvVQXI%)evuiW^oe=DNkD4=@Y#=F{ zn%>k96U(_Xnp3}H0`usU1*<9<)evupX37C7%Gg~n9(qEwFQa+-axulm49)AL&9jhSzE zDwVbVH2w7)_l(VLL{ym}x|Oe}q^Bd0GY_q#O7%$($9=>#hJHQc#`BZP93%67VAV0# zh(O11!#}L!-iC`@Wc~~j36~MN>+0j1%$z!i3I^#)e0x4Y-v6aRY}u+4l3>FBe7P|f z_AkYygStFl#KLUVrowkpG;bg#xwvm}Hb@PMyw7{je@wwcHNOrTVnIHf|nC^s3u|Q)~ z8i*s%uBY8uep1fbc*No+XeitSLG6HJ7d~Y}t)T!vvOlya2D4&DI&y{ek}D2FFN$C~ zyd90uc|vIaZun~y`ANTb`qjZP`rP!pb)D*@-$L%jKyxKo`{`I%qXy?q)8bpiN=^q?4u1RY-*T0{wEzCcz)l@;>9fZ3B+1(t?XZ{VdnzX2#X^>T}P1yaT-sogPx)OsI)3l9# zPUn-|F_`#W%Tz$r#N)^HTy@-u$=)H1?ZRas?nRr+=@0OHIEuQ+JZ0cFSyepVtYDoM z-vrXc__*M`^$;;XRI|F}M{~jOrrwCk0bgQNjFVSehO^4^os>5F;{2i?3~$bJ%Ok21 zZZppCuy!+W<-;o0zHaXFYo;`JBg=KE(q$x5(7_*f>4(!IXC6)mCp`@}K1^r<>N5s{ zV4!>=2FfQzJv})qD>$gnh)&}l@OBdjeQ!`cJwE>9aRE+rV}Fr(WfT>^(Wq58Oq6_r zK}H527U!1UE_V91;aFQYv)QivR-xwbWZGbbZ$lT0{LVkoN8nZ@Ib=dsXnYHkXB;L0 zVG_c9`gv`fWZk-n+JCc^P*j8+Qyxc*q!L$x8m9@j`#~`K&w^ub*Md;5))tLs_;3lY zBxo*Pf2hfmd4Kw|CCbTxlKZvYSazb`&ggs9Zc0i61tZzud6CYQQLk>3VuD*>P+>#R zEJ(IZQS1&>2-}3Rw{KzW&0^J4ft6Z!l*oROOwXT|oTN(R`N_$<$?W5UXMUXEsx?xs z|0Pu{(s9(sAxew2R{U>pL!7zb_V2(dZs1$1IfK!9Yp%ypX)&kQ4NOZ*avhU3B|SX| z=<^GfD(##T3e?Dj&1*eFo|UUtmfg1SX{M*sYSmA%wqWY(>x;$U4X5f?xo$vdC3>%N zTUHF4oldU^lKCHN#Ss0K8#F=U-$j4_OO2AZSQ*0b7@6w_xYeO zfz$^59?fGNBDY?(<%bz3o z<$5PXGXK0nPoqK19lP`KId*kMRsRTcLXW645}GC4&E!>Z`}ogc(IoA4*o#?(9RCOg zHUhgTjX%KhI0YRC({cJe^9@oE;vj-Pi^!?4x0hrZW8e4zmjK$|RRZYSA>42B=TvvS zW!&7bEPhyw3Jd%E&l8o5EWK${>&VAS0RtNYO8+5d@Pqrs&{`~GYdykx2kvx*DR--z zWhCo8OV}HaVBU#zT2u7c4GA{ zCm+9P#7N9hJ>Y36Z+H`7K_X(H$#%-W_GAxci59V*pkOVopD^vf4M91eicg8xq)~YI z7&0qCl9&=7qU&Zp(8m9r$et5TZ}ZeALr!)BGTL&XmiJVftw$kFMrVzRsTTgIeY~Id z7wop5-GS50^x-jOp29RGN7BM6HyZYixFbk%N3JMNS7GO5q;h3rZOnyQW}emQ-bTdZ zCn0CL<$*QNw%#t9goufC#zQ)X*TvUDH3iRyFB|oo>#c4gK{<7>jHJb8zh)O3LIR0& z?1?S(0s)J8fZ~VzS3GLxU^*X<)933eYeh8>{V1JEk3hf^%>T|aR>q+AdvWn{N=+6m z#kVdZ;#|*U`d(be`Tg;O-3~B=UV^W8A9xf$q5}X7G71ZBiFa&{OxA%1q^pn+Pk4AS z;AU5GYqL}?TB@>relC9vQv7Gl7}V1pig^5BJ6Z&2m(Ifygb8yYvDV5t}m!F{|vMjjnv1i8vZS*tgNWH8|}#e z+q~LZ8vp*(e|U#!pUAIK08NpUmHo!b+RrNej{)K@u;xVkKXXU^Ka`MvAkmb-{}~X5 zt3wVjQOg%>Vc-$}u7az(u&&R|8nh^B=tyX|BkRoNjGNSX&2;{~V7bVY4H2NhsNV2R zV>0D}AWwuXpVxY?GpJ^kOi(CQnHYSj)On=xC*X|gF>s*z$7^9pg2_PKI^7-b4XNHb zC+vQ@JszHz=r#EMLK0Xcmj8`~1u!ioXEYnrz+})9(6l_gSNun2K_nSqVaOAWO!0Z> zf%`}8-b;+-1XOZ`g@qclDY$F~ABJk9`i=7H>yu`S$e7mrkM6@zDisuBJbFp*JK*a zn=uxxC%v9nsfS#OK#-`1m)GUonz@9OoEdOaJh)v1+tMbG=BZHYCNe|Of=<$1U|X(ovzSWogP!h9yHABLUPmu=ew%~u zzQQxby+c;xBq~ljK0vK;S5QRIz+6=g&e7AP@quBzLWf`6Z^g3~bsuXwF*987hhB}t z@+)c>Of-#7f(Jde?lP^CDl+%5$MWlPcg`}kA&AuRwg+?HiR$k)Qs|orPov)xzf>i5 z`HVciZ*7Yc0C|;wvac0hE1B2(u0XwdV`6fD-?VR_&x!ap&IKrznqGDK4GuxD(ZL!${-WFJeZFl!pZLtD3aZm1k8m(D;r8ji*eXklXfh)GOj1KVNTDt7X zwAskkE`N!pEgQ7u;pvM-Jof(!%6P=0Jo$9;Y>GjCZ$_KL5jC)}nW*%%$RHQ|u1jpO zAe^T#RcB7fX7|!EkzNesfdCY3hnoXMfICJ&a2D0Mi_}X(yi{w-yal5t42|3`yepVW zubVAc2YfC6Szh9>+sT-ikOH>7e~n7ppPE)LuWt4NE_W}lufcPrB2i*!ii+cDES9L7 zzW2O9B2sfpDGV%Z%T{(y0x1eAYGCnVNTp=k`UF7L#byS{p+{i2+Ts3a^|a?=r8Yh- zVzJ8MdJ)o|>2ziGVX;fvEBb=#j?*E%Dmi5fInyBh*=PNuKw4fn*%}zz%JU=wN;mhZV83T`)^{Z~2^vUO0*Z z%5np{U=czL1;%udG++(N!R^&{(;s~kKKi&nq&RyR-2o-U~ z#F_2^9dzy8aWK~gpHnD_CplKxpwHr$=VHB?Ro`b(>k>l%iw*PBLKwUd58(1n6UA|H zNt(t~FcC)r&iovx*#e1;>z2!;FvB`xJ~xg|()d_%B~XXc`OJn|u#zZ3n||!Zb;lfT zq$tX!WW8E$zg_Db*2rjMdkekrP*M$n z#)cEovCD*#G8Q_#Pz*-4hr;)?Hp%kyw9oC*t|y(C8lHIDu~wTPwB8+B8zV^>LGl;& za^S{sg;6mj`Tm3?)f38sxtSJscCC;{k`~XIrp=gB| zwDgt;@8W0E$4^8J!j&lwc!Gi@vVel(=4kzM;;@L4Qi8P_-D;yPkQNN7pr$5wf3}`2 z$jrg-}0FYTpd3g%n^=T%+mY*R$KHg@t2Fh17#JQts%?Td3kN7+- z7w>`gAYu$207cR@@Kfh@xVR}|v1G3UcZh`-*78d$FQ|0kQr>d(p1>=A4 z+$s$b!Sl?naAUS4Jc-#W>iL^9I9S~-H02wKy{;65Po|fnHSzNHZL+fsDW6uAzU}A2 zW%b1t?(-i>tHG8Xx&zSx?nvD(AoCqS z;B}WrRJNGi-symeQ%;a5I`DTkGH@wZ^I-*UB3T;(o_+n9#hq2wQDH_k}>*l2u(Ru_JunD-mk8Pq7u_A+ejPo7XcH{-v9n$uZF zq9*(av+V-|12WS4pQOcwlQP$puC25W5~vBJ#?=^vq&y$EFOO>b>un2!?CZCRF8eaD zCVM>@;xtTpW3mT=2*Ask5uS(Jnj)#{)!lSQe0mx;5UZOxBf}~dOPwKcm{1xQS0ZZu z&+zUs_%SP1V*A_SnT0%@GaOt42-AAbWzy|H0o zVb{*1DV1@i>6P04;-?@nU=FiR>V%Q}tv;#O*C)aaMb7ai{Qh*zV6$=q57(cJ-wNDw z8G)lz4a)%+JQ2e#DQG0odbY|~^Xb7Nq0y%=$}%yH%@sAa>jBp3THEt9euATFl#E!c zYp9;P^=feO-R!AeQYMov-IPCPr-8z*|25D#UgY8zg~Z06IYqYtJUPtg@2C5PH^aK4 zCx^hXY^kelIbTd_TH9` zNG!)NIfKeIzR5-s&4*qUDdY-+375gESu=*7HJgu+RZF_<8_*y}mrrYmq$HFJI~ANY(xzM zA4+(-d}pZpx#LIbBP}s#$q*Iu93lYtsVTg9fcIXw_CJZ=kPoE z3Y2A}Sv_M^QeSa=NTb##Uxx@E1V$MsTyN(XoTkZNB3*Gra6t@ zc3jypz#(-LA^!k;3zP4|m*Ux$*fqGGPa&xqt!8MPpG1>VQXJ?(Sa(?qXs)xB4-x>1 z?&%}MFBR)M_<8f>TbD#w)%RQy?M|at&X%*|`*z!kO~0tXUShafxjGuIPsxRGi<_%5 zYJw=Oy?A$3I)YW2!gZL6Rnm*Zs-(3~b*_6LtY0+LTSIrzq!}h$E-__5P$NSH)%ZM< z_(YC-QFN;_Izh`|+Vg;X0T}bwEJi|Nd+H=R0cowXKQTtkzOvj|USE}wL^O7Wp6%yp zb|s_xqXzU<0Vn&hlR+^>7YDj?zq-u!USb!~N0IY~82MOC=WG0Ym@*y0A0|`=kJ5w= zY|O~nRdFZE+Dop)L0d((c(;w2RMQy7^^k06{i31=F8fQ6gq!4)HE8ZnEFt}5Ny!IG z@28)~Jgo>;;mf-6tBlKTE($k&o3?Ekh$yOC$u`OcDKosWJ>Fj@Ig``H0l{5+l!cmK zEu5-=dTUbzgaG}DoRAk4{xgCbeK}IpP7XalK3I{`GL>%6dtMMUx?X9(lN}aQuOMN+ z{j&My08!{p5r}Ik(5P58t7uqHG^*zE@82q%WA`mpja3Y8&eXh|rj|41Y$T)D|B~x} zhd;vAsy{kf7f(jV+vd_ItO;gNd+x_X_VkN3od7BMq6&Vph%)_(xsA<)0 zqmVfss#WPG3oUR(DAn8*kS&gH{0Bk#yGbdP4@u_EasLE?Hqs5rr5(DsP<~vO8h-uXnTIO{( zsY6dpm~NO^T3hJ!* z+TJ?Lph3+08G%!>g1Zph(-U;@eo4fYDbf}2I(4L^>G=yLPT@0qQCL?X)G!%~t?oJ; zlmw?Yf$F_l{ayS}HC|C&5(bL8QvN79E^~h! zIE;O=f&x8VhICYLJ|0jFmiA(s*sS^xoINZ^;?Z$;yP&@5kE}ly|dhe;aLZB zo4Z?@xm`N;+nW{2U?M^;!>B@=tMuN>vB9ovQ0T^hEu~F^g=zRe=b25*mmt!^NAL*- z7qU%wtQ5H(jbfc1t`(*0ixV+=dfBNqgyw=X^RkN%D?2L+DOrk|n!rBc#J5p6tIupf zy`v%`nR*a|>#Z`S1g7~*(%z3&-k-@QwZysvKV!2$Z-fa3zmub|&^V9~<57rHT<Xn(ua#^{TS;{j73V{XICy3?_RM=nOLZveY>QYtat;=9ckH>#ej80v5xY6^Zc z1`Ux_ErO+GjUe){16*}U*~wxt$LiALLNe4iO@$e_&PqzEf8mN5lTvT&)OB>4YE#X| zG!b?u#1kCOA|aIxWDVRr{Ub=B^onRseDnm=5_81Ek1wycn>@FN4qogH=CA!9TI)+j zE2m^lMnZi}jZLFb4>(Pccx9VD{-}`e-%u1BCR$tXau=Pk&)R!jhEKoTVIgn7<3p>r zs%2K@Y}x@&_vDN z&i>7YZ~@OFw6b^09}9WR0Ny7e^IWi0-)TBq@@CUOf%hi98EMvuZCqUD?u` z-ppj+5o`p@)han1clz+2x}LSagjzjsJxZE4gzkJ!)( zQHek>(OB4Mv9)zB+|X>hQN(we+2U~atc#BR{t=by3LXxYzdc2r?`^i82ZD)9cleWt z;X`gP1!D2KG4FC(NpWW^nYZ7vIjwGqT7I(OdBozwk%1?L9(H!)nkJ1Zg|%cP$Hb0$ zj4>D_ogW%~xMcq^Rz6J@lsx-_qor|XPkm_oxF6JFWBCflPfsA&nd-5@3Rbq)`1$spx29kO1ra*R(7hMTjW!}LyhU|*e4!u#LJ%3AHuX(Z*n)R0&% z`xibiNxibGF0X8n25h4?1^Wk8)nB>q25A~|qUtqlV{gBIW-QVS;B9l?IXnz$Yisjd z(4D`z9~>SIClN<--tP6>-K@&yc5rqk`K3m8tvbTtbwgQ8%k0Qv9QixwRZxe4PAv=o`vw1*D9Z#=XF4vF zYOa8#-+(7H&ijeak>chezNsS(s(>KhAibCZ#UEVVLH;1&C(XND>XtCU?1ZsGJp4*7 z_I@G#TDoYpSnb`$2TzXmzy3Vfn$VJYOfqX!X2&3Lu$}E6MFBl6Jh0pexE!M^{+C06 zb}E;I%0(uoG%~Mz&IGjf=fCU;ap7=Cg0;4SKdQ5bI~A5%rrT54*~${EZNrJ>&Ut@~ z6f?Z$V$0xj2As34v-ruW-rJfE1B{oQ81+|Frs;3$aTS@0)iH+^lqdd}IAb=^U05I@GM9h=V=<(Ky=1jG%dos7>eK9^T2mu>0pt@sO=wvXxegzN@P z^DCBPioHH6%`PH-nkoRJW47NC$loK1d>cJ5m4=SR4Q2Pt;z13NdF--;)lIi|I=ykT zE1^qcH7C%;_4r_*o6pD$W-vb-QDwPfJeB;}>Xo3f9LV_E8i(!|bIdhsp)&8QjXi9M zEfLe%&hZ-x(vMX^b;{ZK^ag1s5`E0W6)F3u6=G}A>pO!yuHxBi8}wDzAtTn??wL#8 z-TTH0zaRMY6TIbWBc1tV${5#24xfkbVaTbQu5DKH>L5um8ucm`O(ZxtV#<=2;+efY zlWTA*e+`hgJTsH<^5T8f)!Jb8*2lRZC*(T?;cA^Q>WEE(Z&~PWwShLF%?-LHJb#hKgTt~%= z8TSgco4C{g$J@Af)=m28tTX@aVW2?{?lVs6-D@|9oB+Yg5nH%uKVNmlAWs%N)jI#; zRQN?Rf%-)jGVXS$_Cs>n-jik`%0yP`=JveiBqMm1b&Q^}dt@NxB?Y;YeG}(CO$GUmO3&7 z(~NY^iDTEp19V(`j*wu_U(X_I(|YU89CiKdzkrS%1__D2$@PvBkki!LEGGm&eXcR; zAI##^ZET?49kMQ8oOzh>DXN5{zc*SPZ{kBdevEUy{$R)$>Ww;@U+-3+Sto$*sTdsG z|Mfh~C+)`L;?eLd^Q?xy65k}U(T_NP0-v!M-&7^YvG9;qMuk>#6L?aS2X_Eudtoo!EP ze4gCXH0e2-7k^9o`d=yAJM4R{b-m%AgF8PdEPHN)e*=X};aG@*Wi9QtZZ)T?m4@T3 z(f7m0eS`&S+5^s8V-SKGqwkosi2CMi!o`>HU!GWil_NAFE|d8L&&K&(_ZJnREhI|*0nFI_xNTM za_b7-H_zqIFhYd^iz9SP`sKx&?RjlS?vE9J4muH}@lp@Gf&ql{6 zla!DmWg!g?`h3q{yFLQ+8s@|QqqtR&+(|+E$B!)`{V#rO!hi8&rT&XROMRFJEnBG; zq{rhW42<&-;h7seAfxEdq<{sEBh+xKp#8tKyBCxw|6$xxOa6y(`+py?0%UV?^5>!Y zT`Iu^*!ws3^7``aLf4b-?^r6qjZ3RkyczF$IQr=KPJWJ8Hp$UuO*kh~^K1P1_t_Z>20-`{A2BAkdv zK6R3Aw5z?fArYbkybdy(Drm=i&K6H0wVFE~v1X=7blJr#eZTtwG=lS<!k3(&nAZV?)jF!6*!=_~~-|Q$GbzjESsZtxnb;*&B#lUAlp6&9=*C*Ugh!$eS za;JZqXmNp*Bc+4sQ7@OLvaYq zf$x`oc?P;!aYA8=#hNc^Zq-{1ILx)==F>z+T+Q#n`jD|)wy*oXo$^chYRX)V@O{5M zmM2=svs+zy)_X<@vWQ?}i^9BeL9ZtKzX;^ev@$4gB^xn05bQw<=3S+L3cpg@3E^ys=&(%nY4P zQQwsHNZxih8pTxe!u2r{;NC&_heW2oAj`&v}RIr zyL%^5WL;7`8{=uq4>*1yJFPJKtyJyL7i-P*DuIj?moYQ-8h;O7OxN2Vgpo5x$R4w& zV+mF}fJ=+RP_XL5%a!28kWYTvDl&Pqy-8_AU|Od_6(5>UeT`AwpD&^L3+>X=4`)=J zW%QaNJn+jcvv(j}FGy^rHimmyQt{*I@>pSC0)+aL_nG{~eksc9WnCnw4%v@luz^6@ z%#DKXz#P->Eo`_E<4H!1!Iv;rwLJZ5K_!Og9V5NpJ~GAh!hSmPTgQrF^~@K??eCd) zxvXb2KU(pjrq+8jOewMe@mucEkM5oN-c(hsg$R! zjWynn^;9|oq7D{gd^6wsTK0*cT`dRp$2V(uNn2+&^J?G5`Pw_=|6N}M7E?ZMFQhT} zk6|Sx64$g4(8$n5n7UMC*o)GKLbU*!sEsWi{L}lh6qq#$lc) zB=GM9H&$oeA?JQOQ;z&Oc=JJ2#+l!rQ)!a;)H$CT%r<_zTO9o+qo#(o6@W@nmG|tDh$Uif$(&+VhQ~?|_BV0+KHnS8yRRPdI#EX4c#w({w%#A~8wcoLf+$vj zvtNOm;+E5 zO39dJDW@EnTys71p>Dk~Z)+;{S;XBOf@p7M@GUlri;^=5b@&RR)>U7L`SR;urOu~g zQmw7KdxYky-}-<3H1>-V{}*d-85LK!bnD^-2qD28f(8%n?ykW#xVtn?2=0vqcMI0$R1qTUUr9~ ze7Ks*QHFAlCimzY1!Yh+&ysRd|FSB@ux`3D=((6~d4F>#Bin^iC^nWCbFvp(HiLOR zmdZ8R+16-=Z}o86ZzgwKbodH6IFutN>POZ%)nLxNBZPp|u~h;rHE^e}@kwAxT^mgdA27}AShWeg#b_d7pUlxwxU(hO-sYM^A=N*%&l>|}a+3>&zH^>*$vIgB(0J2WzwEjN&6bef~WDlufoVlKR` z@Kg3=4f{pA4L@-6Rr}Uw9FA7Ywr?#@IFKlk5BxRZ;jAF-Ou@WdW9Wyp`@0$wFRhoQ zcCER>Z`T}OChTzlPo8sx-fMe5Cw9+Di#Tp(?70VHqh#--X>OUe}&Q- z>&M|QkXsdqz7Cdk5XeGR64Tr^diEy+VN zN4?jNvi8!GIMHO}$h0|sc)c+zdw&!ixi-W;SE9fQy17x?q`#tAEkChy*IOIR(UdEf z>v|bJO&E}3a~NUM@au?)h`1H8o{i-@%3hN>QGs%OFh#ELJSO{gH!gMMQw!?El&Ay> z#OD%-zTZ~;>R)0dIqZWaxj_ZzQa*I>RhWxSyX*EjgeI@zOf1DC@wbf~ZuuhE?UnyH zX;ZE11<+VnDApuZ9G~|3RyWMDKUtq3ynPCV6#zT^PUC?B!^(Y%%$uW^_ArDF5|T<^ z_P9DH5V0sRU(9kLms`eAnC+MnB4H@VJ?5f0`p8@$zgdto51}weB%RsblV8qHUg{=R zmOH+`@t3_Z*+RBpZ8hQ;3SH+deichb{%&q8GILx&N1-uBB%qbQT8y21bAjQdg?;2t zy8Sk`qfeW!Y0#!qv4+EB?s$ap>DCC9dvtX*x?8i_gvHYfKFLs9GZpk`Lu}`)!hi!y9nr zUz(L9&vfkWMyj(HL*|)8A5G1wurMoc&PMgIrhRzaX89Bu*b$%P6!5Y2*PBjqB|8}; z&PAaXJUi3lu0XbAVrlO9u96wLFkT#tI&@OY$;VjZ(oasJ7A#DGdl;GXcs7#Jx!iig zJLDqS^sk+woI4m&ZcE&8QYE-W7aXSDsE5qnU(RHIHDJ331YgjkqJ#)g;<#uC?B6Y9 zW$ErS${ZOrwZ)Qe?3#iTGSbZo^{6y!amie1Y00QxzDiZPIOf#l^^{F-n#s^3-1M9+ z%I@tn&RB2s`eApA{;nfV%q3YgN@Zkd|C~2eHHC%F37cQ~^4B{kbfx^i&kO%OYdrQ6 z;kjdwWhcqi_4VV`wfq^CK9mG?x1>yBviuR4lz6zI{S_OtHg!}A*UTnAg%XlDC;K@! zq(Ot~kn>R3U5;tg^^DQio&TIv8<}lRHFFxQ!?lGmy{9%)>i$0F=|HY26R1`used3>*Z=a z?YY92!?qJ)!Ow*0L8vT{bykl^X>$E}(w!9BDL?W;2K&0!LuSau6=t2foJ~B)im#f7 zj7rl>ZglCbLr1|(v~_08Qh;IaBy%a zRfCB5{;7UvSWg#+lA5Oe&hAYNyn1Y z0P*Ufosjc?SZ}?iuCKnubQ4wDnXz`gfjV?C2V7n8}O?sRWw5RJm8 zAH}o(_P3Cx%PEq8_kxB{bqUDxshT6+J=m{S-h2&_#aNNUj@CeKL?fn8)+8F+!sC9Y z(Z-7p1UXP3)9xJtQ20^=Wtei|>5LzjM?U`&8hV$dAv>Nt6f`jRcP4+bI^TC~AbM8#{5xnr z3wHFG{9J%cbY96GcOGi+?@M5{b}s4j`0x(Fw{|3TG09=w*Ju_5nDt=BaWTJSw69+t z*sOxer2=0pOSC*bKX+ULkOg$-7thP0<-q#&qyR(J(8nW`fH;qIWRLO9Wp5V#Qtu4G z)n%mu8&da3x5#)J%;^!vu#9yrA)(9gjV>O_CVswz`MnY1j(K?749$-O{P9mx1O@(G z6Vj+VsRqk2G)}m$6y*_TqP`gnkjTs5@2_pK?{_|XS`RyMB;<{6miLyUq8I$^dXd+7 zo0D;OJ(RfjCUvtuyr)fm<0#s9yIjp6q7z#TG>a>(_+r>W~4r8 zqRVf!y=+q~{=8bz@z8440C0BY(bl2QX z&c-~sP==|EGl=5v%Ox{JfEM?YD-dzE1219{$6tJ%;UmY`_%5RBwh{}w3~sEsM%v5S ziwKE*sCq{?y200a{<6O3x_?R)F{dM!43j71LXd&3j-4o*$s78{%N9m&7_(I7( zA@lj%3x9%3S+h|762KP_wLg~mTu~%L$QsU0hzB`f<`}!sLAcqM~%!9!$J3psA3X&K!M*`wZ*a_q@;^YUuJ;8}{`~*Iu~3GDk&{1tk}FhkP5t%tpXG73 zuNyfza!g%4_&@k89$Z3;|Gf|^{(rjp|5_Z${|nfP|1V%m1^hql{{Q_2HyNPeL4SQ? z>xjextXB8iEc0p;KoS3GY;3JtqDe!m5*rsMaYPqy zLgq@fsx9}~*lgeMBE$@JOFnwsYdYDk|DG1V11A@=j(eLKb&M8#O3+Xw+lzmfsvf$q zScu$9uXw*O-w}Ut*4dpE?E{OIE^QercWiF&Z+6;fD&2!uz^sFf}4G{m0 zJ>5bY;{6K$-RePt&1R(on995{L!uG46K4Qbc!m8oTp%)$WcwSmZ*zvgaUId9G_%-N z;kWmI)gM`AOnyJseMHA0@<6KbRIMHrFDbT~GV3(}%NXhJ$Up{$o()$tBvu!gt-|Yd%Zwr$0+$Ta9yFuYk{z zpvG!8R57RTYCCEj4FkhC{WaqY21aibzxQ~dqM`ZX-*`|Rf`;l;t|84#R&(%h+q2}U z4VfiYit}8t*nraWXOAEp|B);! zD^XHLhG~4OvY#-=bR0}`o1HBL1f6i=IDzptHz7DUIWLE3%5jHR64KH}|1Iqr8g{4H zjv1d4`PiL4v0ncSCtywpjKEEztTyO&2S%Kr!#s^W4KY3MM_52VYkAz$zNSS)3VYb_ zC#NtE36cCAsB5w_K#9<%VBZmaf;(MS$y=a$mNmh1{WTWR@bT#sYc#f4WOIdp-<2+9 z3^v7-*I}qQEJqmvQA2p(ws>c6y_M!oF^6h|@^Zt40EDkDoV%lb+9`?76uK;gIJ`wXqcy-?yu1)!G6Qz~HWA z*7iTS80dwQ$IhNtgANcYD=U9E>;ZW96`FaGImFKHu7R;JY{%YcN=R^nfp&X#6u;BK zXNGnjJZ=wRyi%l7LcEaY-7`+K_RcX3UmYNujc)b zj4%?y84}g7AOfbKCNgIeSbB@~2!_>?w&4Zl%m?OuV|&{VavTvj)@6Qi|;K54%fpq_|&8E*aJC)KrG_!Z=1 zxKvvu1{+7%d0V=k%Jx?D4T4vHQ#WH*&+}J1b?kOfq@AF(___h};Y1s9te$(6kfcx_ zIC7fLXe^pyXQgSN=sFR>Y%u^tbi4U-eyve+=3uX2Pf}qmuKCcky}8}B8>SW_&HCbf z7Xe@O^HN4T%%;-z-JWI48T)NYX^HhcP=m-uz=QiBUO6#TrJgIH;Hp20_{A2`&i(dA zp&N1Rr+FSD0X99>FNW_9JFy(x+&QzV8A*BZ7-$5-*L!|@gAfB%B4Kc>AxL_|r*O$R zj>t1bk|nygUzg>?p)=LnqXLRm84ZSr@8}i+HLHvVUQo<_CiYyh?>Ta;Oh7p!9`sy| zuXjr^+0IJMm@WQxHEA1JT(B~t{qxv)|M74lKH^+%yP!JcAm)M74so5{EA%X_rP1Yk zrxhM~>d~CR@^ks+^Pm<)gVk|oJDUf144nY|egfjS)2;LP0wJl7}Qe<$Y$$7m{S6 zl#T%P?zU($Sj;7ke12a4-@m})hm-9n^pBe|89ZTRr7+(Xoo%QMwHCLG>Aq;KnCdF( zSf#EA&+qO-A8=$y5nx#$&!SkKF}`rd8I< zY_T=VK$h&q-5sMmoPg@1Qh$HQwbnTH^c1PT+x>0k7CAy*0Qp>~)lcOsbFF4Zmr!dn z?H8QswKN)lgeLC>kyI~tx%=kEhrMGa(=1C+6|W}nC($$V>^TgFlrK}(nxDQ$ zm(@ys4$Z|=JV~Eqn2DN(8=wXZNDT{>PJVHGz~key(kzqFA@jg>J{kNyNg#=|F6$sR zYbwQC$d#8XK(yF@C5* z+PMiFS@!yTXMd|XD3bA)=QFU=67PIGhm&aBctqKp#$D&g%rw+cqN?VZP*qh&!p%L* zg99<#5#y#Q=J*z_p-_2i*rO~z(WD{l$)jCSRo6Q?_=PF{8-$iPr8b7$gL~DW1**G{ zyiTr}K6|W_Z`X>I)VxXpq=xOB#RSgOITLivclI!nq~(2Q0dc{v-+o|H(0y4qNsGB= zKqb<%XEzYd7P@mFv4=rhdk8i-G#3&7?clVId6(W0stS%a4oP-?jgI@&(z*p7Yoj1( z93Tb0xU6A_M-&w`&X{r1?{-j*3b8lBa;r}4is-~RRJud&iHOOX<+YRfZ>7`$@Pb9HQ;D=<4;Nakn z?$rzZpudI61@wT21~4_z(ji}OR69CI(6KTWQafp$@|JsOxwyFG)o*T|b&jSOH|H7C z9&*;;iO##)a~6<6J=S24h@ZEHi}uO>VhF6bcY-pfl4QZXVTyXjnTsbA)@Q0w85x+( zd6wx*XFJ7>ao7?D)A-05=8lGJwib;~RC=f-vs;U8i^(SOwX>6!6Yiveh)}h11@aV# zu_)}14(zXni=0+&MXqz1W-bLsQW(?d)3_%JC7E&APdl}|#6P=lQ1U%E(H%Bj9g|?B z?Z%=cFdCgWmS`JvBUSU0)`s4CDmb&K&2EeLg%UE9?lg`u`IM-4C0);2r`KP6`&W-} zyV~sEvhP({-qYVag=)D<<)P*&g-GRP>$X1qOXm`P$Is5p?7(KhK!labJCtdgqEo!~ z)JTx!oA%Uc)z!9>bUrPO8NVC) z*%Y10SbFEEOgEdc%EQM$*4pd<+*+G=rgR&!UrKX@3Gt}LB78OtFBIi>S1YTvMI{T zj;2PH$2Nn=ZUR?687#G=d=Ww^Ml8CSBd~Go1^rxaE09oP#(ims8HqdIk;DCPs^Ze< z9JAv@kG~$)m(Q7|dm0%uEv2L!N@@P1o-4WN>N3YJTWfx)hu}T<_Bk@y7_qCJ^p^>2 znO<{fe&Ii&j>DU0?>!yg{R&GrN32vpYb!5syx5%-9{S7xiK7#(9A0_XgHya1QG ziF$F3*#GQ!$=O0>dd%UR@q0Vl!aC9m-mGI}I(bThiXR_A<5KS{? zbvkOyCKK42r#yoJD#9Al_i!zIg_x*&Xq<#COQ%|!>4_b4c$!kT+o*|gpn{wtN&hZqIwtnclivgU$?Fu<8YJj@jIiQwPJ8Q2nv15sG2sGtM? zz(QX<8_?6&_ZdqKRb?i?B9^IsbuB@)n2w#FZuI-&S2D8S55KEom6VjFS1BNOcK7WM zCOVnw`^{>T9qLg6pW;mn4E}8uak|<0B=JD3FP$nLfkQ$WaMZwLyq!TPna3Q-^J|_- znG@J#+zjq_7D1`;DD@dX^T&-(r+S^#88g`V-@;ctqQEqq6Zz6=Kkw6e3>6%=*@zPA z{E#K0kST=|WVU1;Vpd_I4hAz_y}RybaG31#oiiCK2M1?p=}9G{KNCg{S1Tv6`_KoC z>k8*MWh%D|E)?kPZHn?C+xK<tDVQ zi5)XTs#!0wA1E(y;%3sdC^p3cBqw>N>K)rP)Kd^v zk|ATG%Ey8L9fpYXF8oRRz}h1&4hB1W{EU5U;+onf2^o`S*OHgEkRfA~#DO?4hLyE^ zwmY7AViFV^`?#A^zix0xGmfUv{KbWeUE^`d2>*FBtG?dA%U7#_{k!3pmKPa*xF|C} zGE2!2uHonP(yb~3)bD0h4<3tQxhsFh4*I#TMQALYlA*FA-#aO3e8|PyUN-}JRzpc@ zjS8>{bgJXBQ_W>?SK>EI=_V{^dNNjfnv1q|{b z!(ld|Wt*D=`ccc=f`Sw*LDkvqoOLy&n``y8#UF1Ynt1^jbE=pJh)4>SLy?JV6jX8Q zT3Y^XA8Iv~=`^9;hwn->+I@A9#IH^5MEkR$q@~FR6(}|8!)$+>l z(@;?8X!G*8Kq>64FzB{(aS+Q=$ec8rv~a$6LK^7KRrzaYXP2p3s_Aqx5d)Zt91f>M z6KFM5HkJL+ZQ5WK_9~9c(Q+~Q5e8CA=Ci1%g|sGdZrC)$iW$Aam%H;XEPofEcfRpr zon2(Q;-rneBqUZDX+(+T2K<(N%Qw@v}Pgm0Wi-#L1 zaPzaBbE;Z+R=dwuYP)iJr+c0%@5!DBYhTcNFVrNac3ll4bhV<=jZzn?we!VN$o6*U zz}3~U6{=u^DXO6PN1l5)cdvQE{70DE{Q5u6ng16f%zyZG|HoSM|9C-9H}c=izyiwE z=6k?0VdCcTym4?~=9aPJl|OwrFgi*O)QlAqpMU+5LnZWHZTSPNHDCCyzB9l1L@O2T z0Eg1Q>yvYlQ@h0pFvLcJ)6*mKXNGgmu>kK;bu}{(IGFw8xbEiookKkGrJJbzU2`uj z36S%;{7L^^^>cG`1soNx;SAfOQu$=lg| zpD)+jzU+se%#&6Faz6t!y`EoVFs7w#c;1&RIZSnknRegc?>xw%?`oGOM`Xo+_O`=S4AUibdn)?cqlZ9JywQFgzL7i%&upyXdWA+R zIYbQ-FD2$d{3kU5%RV8o6x%?DBG3OSVYDObWK#a~NpJf2mW-VIvKNa6$Ti*o){iq4 zzKw>(8!Xzvcs0*v5mU|c6c3@lc|Gh!t%I^|+t2?faLgL_Vo7UMc|!Wl?KQ@$HLvP( z)!U24YI)IrIEx4A7~zPux25(nMD%svt)E}N!*ai?zO>muGu)W{O#dOaxGf$c-g-v+ z_F^gKdc#UZ^I;ZX0=iB#yApA^ob&+uz!K8Z%;Rt!b8`UqfgdUbe(UtqKO!PRvMsEb z23V>!exs$MQ`At8lVo^Rd#W|WjR?Fwp5H#%8rq>!j*-zNw2~4NEimwkCK-3&yX9+< zS1=FAsb;Rj#PBgOKCCu;e2YpuQLp!-xnFPxS7!eR+B3>nmFrr?TbGilaY;ebHobfN%Y4KO9*@O$%kxSW$vP&BC=qJnF&{JERH)V=W>al_09 z34)P0tM8P@1d0-g)FQfyFszTLIC{2~kI^ zu8vg0WNH4ZiTT>v8X6iLu685=XUjjGjR%Cd352q~cR&I=5f@ii**@=(GAVaFdS17% zK$0RvPfy>~vu%5FiJpoI%m5HL4iDi5@?f^_5@~yIAO|%DR`S(0ow&(6-OvH|x%pBF zC_r#}Z8?!*j;Xl&XSlhljWf>MjOG7KfRC1Q1KgPjBaG20n2YdP5s$0Hn*$%^_C z8)Ao87WtjUXZ{mruf)H!iK!+$U;8l=o(lt!=AsoNA6QZ=2o`DgDHZ-#Ota| zPT7=3%QRY+Yx}g)Iy|yOeSgbW_iJ2ieQ0~VK&BTl#vPuu=1-rebltlSsBTC$J9u)s zvF$uil%Hw?@ml?ejR1Z9IgX$wa~ z{A`W6G7z`h3|uTUG)TU{2Ik$#;^@Vu{KnCd$;ad>T|FC!-{20nXGZ{xKtqcZxU49b z$|3iBN3hUp83ba3XnV6(7{n3jp@hurPf)|6%Zsfa8T49S#83aNwoM<7&m4yPq_XyU z%fXcCWYKdh%}+c*5#irlj~0`k1Dbzpc`JdNmg68VYpX5Pa{RzhGv#1&uD8R@ZX(dG z+wvZRRvFN?!s8R|T!45u<@&9gVwB`@#{MmG7H9r;++G;+0Kx-{!Gito=vFU5FmGjI zyS%Yd`}Fn=HY<_`XWG3tr_I&jsTK{7{fs5Ce6DxcXX+GtfVq7)H%&CLdeB{i|P|w|9$b**?{5{^;z=4hK7q1PjIzCyP ziE|CU!tJh-`ZnU)4;`rE$+hMiPl#`!o|82PdPb5m(lC87KrP|}@ zKYxrq{;4OYpr~@cou@FrUyi_IH(RbN1u;QX(JIz^X*n^%he$WC{+e;TdoZ9Ew=!8i z+)5bBA~gclZZ#6}+CQIS7gdQwF~*u1Cyx9wXZW02CI(k$yFAhZtH&g!c(-G6p0#m( zSnfbu39%q#RudRy!avr#TVCP5PHv<>-g#W|V2sl(RtMPC_Y2L?r-#MnVLvha64TwxiXuXUTU{V);(FnD!uM5ddI*XCTT0xZXQ?KBvn!-zU+Tj zA>jiOM&pE|?rJ25uv*&Q!^U)+nPYcPh2-XA5AXNsq|F3G_wCWFX_AfVK26mDxkoVkONC#K`J2l`c??Yp^pb|*T=vg9qBW4;^Kndzk@0+KHd0m#ztf}aknnIP9#z@}r7a>DeCFsyHO&9K!{P0)!_smyiL03t)6T+(RbIWpzhj;fs4T6d{B zN@3*Q+3o|vQhSiw51lr3rl>3eixg8Np;|J{WDE(3;QqO>IiAHr0Uh74>doJ7DRKqE zc}icfu_Zh`@!4&#r*d*~9xwZeWQN}GPd7o5~$jja_8lI9#o@?J}jk47gtr`=8lu_U69 z_8lJ_}{9GhC4b>2R^;VUtCeLq)=1t;XiueV7YikrRWK}-=7T|wx(}Z zLy|s%Cau^~!^DbbN%n)Pt;YIA5|~-la@bBI8KSh~#>&HVjha(vSn=wz0RGAi?lYnn z9P;LB5%UK{>D@WeDP6iCya(${~V`)0(eXez9r z)nr&8LB@2Ie|Zj!5Xs1UP*W7%UYjV$$jfY8jPfY#4$DcpdT6*R4;rNP( zI!}UAlKPVP>thWSp)+vFv+|c_Cr4WG`E_+|c6#*ot$yM*!n&TlVf;Q40z_*vvL0Y( zOR7LF4Tw>ZkeAk!eVsnlWF1MeY^}JlGRKGw1<&2?(v;+^XdG& zI#m!PWNk%#J?V`@FbLBf-)v)FP-Fln_U0v(=LV6yE}=JSBPOn4*1SJ=Y>Waq-bYTm zr&10+-xaw-Dh~o(og2iQujg@gn?CeBINDD{6hDGfP2Uk7uFk^r5oZF!mC~cc>f{0m zzh@d67iW0D*RZ_t<>|Tpqs@`3wk<+H9wMxQg1*@q2F&}&t&wzKc#3aID=R876|G(I zbX2*|`hc`Bbi^`k6m7TRbi(EHc*fL9|HTKX=;(MR~X8CWu-pKRqDebGNgo=!XhMc0H3cZFmb;K$PBbH$4myg<+OCDu#9_Sb6DU-vMJ zKwK?e&>y(n!sC3;$YF_4*WJ^iYZQC>3Q}_SMS`wQ2hmM#QoD}o#7qH zAum+r(mB44;x!^zcY?zDXypioN&IB<)ph}DP2-Yaa| zDNdDZMCIS7*F?ns%Z@)7zHVI*&;?m7+`c;<*x9TE6Z5y9@Bz?tptFogNS4yoCDbfE zjCV9J_zDXPYy38nVNg7O4773r^ri`4eFFkuf$YGq3=E0me6P7OoH(#_{jG8+#rmr6 zpztoG0&U5}-lS`T9rGLY<;9pp^s%~H{>o=5l16*A4+zbYejg8z5V$?!QU(_#q@-^i zi&7U_Bl+)OfZ+GO+2O>H(U=&B*0$h=;=~VOGPFr#B*QzVz&f=y>KLqAL z+21H*(4GNq(x94z%Xl{-HHog2O8LBBin z_9oFlmIxz+cEeu2-w1DG;$mxiUzL%zxR|u{LJQ!Ugja6;?kyjl8_T*$g)hG*&~Zm4 zPs3~qQi4Fws$L27yC++40w!X!IqoY6wnRQm%t}-Z@Q{x-lNPlj>94qN&PRav*Cab9 z`}>Q(tQ2AZ6o5b{^zkH1!odLuia^M1mAKBxO!|n{Jsq3Ri0Pb~kJjGX@gUo%#@niv zwukM9bD}ayhD`YR(Y7#2)k?El3QxyR6&pF{z%80NVAst*?Snz1h1CMR#O!+J*z;QZ zn_cyNyegQOr$R*bngYm8e12b(R#apJgp*tV?hoj80AEc+QSp*DiBJZh-yIzZ0lLTQ zGfJk{^Buq?irOXekSGBVE zZ(u^<|K|$|uWs|@p*LYIW(qx^SSPJAb zC^$HNLqZd>`n-+)QE}F20FGg_`!DI9LDlJnrx$lF;yS6)ooQpC`4bbF!=IaGexNzi z6-{Y_S$ENpfAn!ndTisbSy=SLt%92Al~Rn2+Ls z>!iwN)isshCo<7wM5L$%1ntM>^iB3vM!Q*iROM&Az~=SmbMGsJZ6TYtM)n-LlJ3+b z`O6vU@y|aEle8S)N{RC89C0-cUEKtM@6rzG?1lD$h|TK3E;UW(@uWKb;P!`FrNw-i ztiC?+-Q6ePzeOYTj`{st#L*jo5!g96P}hru{L^;kkAV|A!Z=|OK;l!;(<2~O(N=ps zy96TRjU7AgRUMQ*&axp7n;r6Ik~W3$JL*z)r>(ihqZiIl$GL8z4#(jPt~AI?z1gg2@1B(Df7kL-UX?l_V+v_9_4r86cKHNnKrF zwaNK#A&WG{`zhw)!p=w||DWs70~DJSuVL_bF}#V6fjL>BND47S%{n$V1}N@ghiimmbOooj@tk_N`*3LJEP_@x!&X&Mq9Hug3&%n22%D*Q6+i(~ z*>&msm>GRAg(X(@&Q+u|r12zPi&#pQ1a|f{6wGvaU7m5cmhjrC1PW9}p-!M&Vb!9@(MQrw)j=#`R>S2QQIN&}v?5Cx zIW;C3?Hy19Q}Pe*)PRbTqqyA%h|^tNU9{B@7nhd?8y(_{&8`gaD1?~}4cI`r>wb6A zd$CEX-gu=9Ox*T}Wo7=QE0tgy z+}zJoH5#>o(~JFBKJg=c!1WC zd*kWBDZo3G%qOOdiJDV6X{3>^`{?C}k*366NyYhF(o1NfkSx>zRiplbGy6#1sBa;i zV@drhURVfUT|na66I3i&QsQY$eE_Adj=vz-jNOVWRye+;iZA_f{H|-Z577wvQ0@MD zZsk6>rZ5Js#ynD44ywKX4+KI6h1aA07ne7(KtgBS-ef zu}ltw;m2F9sn*2TDVpBUx!CT6^7zMg3~Zy#l%_Jzky_aHzG|P8C`vE|fN)-FT^RM= zoX?*25Pn@Sko_`YeY!c6cYDaAhQ<<5Q21O$>$o%U#SzUyQSl1^Unl40E}n8oI4^Hb zw*gDPvnKteLn80I>+owgpRB^*w_Y+!LiUw!b@}$uzK0?gQeWwb$#u=@@Hv>I5;?-RJPkTJRRj9gPfY9{Ty#W?IX1R$ z7#Pb@7*fb{Y;)hF>sQGd8Mk{WCn?VNTT$v`{`z;+dAXU)(88sMZUm7|4lsR8tMpa0 z)_yQP4N47>6Fl7!U$2Y>-Hs-WBryhsX)tAW3@yQ(+W-+Vm1$9R+5)Bn6w+|@9c8qj zHU`u8hd_+x$QbL1iOWh#oU0v{NTNR?>VvE?z&#@L?;ZiBsHq(GT}+E5E~iVtWLi;S zsaPFM2Ef|`0s`&By=fTH9(IKP1G~#+1^oB#rldJe4HV*s+Nn5f&8o*j6H`I-OfaIe+J+1AOvR zzqhtcde>~*b|((WAj-sgqSM@>9SaPlhZgFxdHru`c z&-b%4LUfD-RxQn`!qR~eYD>hL>T1KQoq~~b;3G5q)C9EEGD%FmsfigG<6!dhr1f=b zAQZaRnN>%g71AVkPeE);hZ6v_kFIU?FisIy*vQ zeKPc(^EC;Yj-y-Ws1LuuMpYDYm3i5+T1Eg8@LM^}a%aDy*;z)3cHc z29%Vi6ecc%R-6K+l{J^rGBR66$=k1d&8JwSodG8z))co@(-R&{xfsSoXl@iJVrY8a zpe+hfdpjQ%;A-8)Y?G34_|%5{%daXk$zu}&2a~{!nVFuH*>_MmU9{GPAc($KP!Xy> zl04X#>chJOaDkCBF+q92dkh;@r>@|)w6w&KA!sB}(bE3xk0emnFh5xLflzAPyag=L z>eYM04$|v5^P05-BPCB7vWb{kGqFk<*RF(dYJ_5W^k8{5ymX$)4)u^g?I6ZL6~eICA$VV(h7IZY4#(H67|&;C3?(BQQ2=@Q|$QZJ1M;a-Z(<1I(( zHwvTJqN#i7riw&%{?gF}LEfb6&Te4q9QBKL;QQ+vk`H0&6CV(#{Qe2}o3}@jm|4uH zdYBeZhT?}=%qAd!iXE_SPacPHdfa_%YiolrGG1*qsx-&zW;`kTD5ZCJeVmKQP2 z$>F=icS!@9;CaJ0?*zePF3G$anXRKuJ<#h#xmoJ#!AdP2kcnhEf`nu@BfmXOun!zy z{khEp$A)@aLKsoL$ujXCN@_o6?5&3P@t3bS>AhtSRvKwmn_Raa=aYZBxqsx2rDH$= zX1VnZNb+m1?5|Calt(i~VWrl)#87r;E1Wk^KLLchsJ_1Bv~jCDOjhP02z`Ut+-_uC1TZF-ZwnZ6{$;w2Eg|(f(@kIoNT1RIAeb;hT}S%Tbuk`JPr_ElUbmV>TmLBEg5xttIMsfK$_xqrzi2W9rv z0X1)mUVv!tf_l++Y#YK0qX69YCcZ-7@ITj9Z)a#GW152Cr3eFPXxmC}T3{3`0yAoH z;KW5jTCTdhDONCD`STHRYkM1;amyZ%oyGyuINY&p{ac-WAz#q$2a#7E3gnJPCGv1@c|taMQzG_uHZ`|bMbzWI!Q zdV4YCcVjo5jDu`g{41!mNigNFsN>Jb15w~EUk=A10Vmhr^Fu;jx=k!n(PUs@vc?kC zH^o)QJ<2=ei}fmXT(!+Deuh_NsN}0=cC@h(`U4z$w-)T>JZcO%g4YdPq~2)GdrzXd z@;X5P3?hxk5_0m`{<^i1R5={RoVg?%S9tokX@;l7d4jiuFKzLMvjr&A&;}>Itfk~i zV6%#ElE(uYIE8zpnN1wFdJs@iQE96O#|BNh1ChVQOQoi!4UP?U=@%||-p?kpVyvlA ze&tICU9Z*1ly&s>Catv+830Hyquc^5lN@Asbd+KkIfC+rQgiM^`-<@bp0_?JIODvm zc@F&_sU3T~m*Hk=s`Pi~wvRjgTs-_cux&@r=#8RcC#4(vwLTjhhm%#m?n7)9xW93T zd;aX*#6(1;uW;+O*Ckj<0u8(ST9Gi{!7O|8brgp1j_h7V`HYn*%O3LX>bOS^lZ~4N zROeu5T$(;y)#t>m2+|on!qfa=--q3!*9)SjM3OX&DqZS`_vf$h?l-n^V)y~oh(~yk7iGyB@Z$FhB%uy8|^~oq~OaQ2ED1g>}?V@XbF>l9$mpNxg@U?kxxj$nh zT;luKYyS2T)d1Pmk&{e-P0;;M^`U`quH^XHoyfh6mCPPkke=4$!{o2?9 zPVu)Yj>_Uvf{UW%=!8+c9Qn2$U{hx>!P>r-F(y4#%>M9*EDu-Qls&o5DlCj5F*YbF zHd^PmG#zCqbL;?h93-&k2L!j-94PLetSyff_AKHgrr^#WX!@KR#-Fo(UIG^pj zTuXTDzMT2JKPRE_-Mf~n>M1MXb^U%@}#xf zJd?u1Ki)i1H>t0E1>7OAIivtB@GH;>udLt$TUG&S+;4wXx>=MhE-tT#DOp2Svf*9- z)-LB)_V4dOhx*a}}*25G6@Xy#?4wM8rg|CWO-vK3WZ2 zZi_SG(u{Lx_)R5I5tZ*MMj73uMpllr8jo__<~O#Jeqjtr(5N3N0P*Xlu*LiuVD2$l zTRWGVgZX|xz59FbWMiVXnIA0zIK}xxA_dqtHa0qH%$Xb94oQ~lUgZP@cLxJ4rHWLt z71rEbY0wdY8hOfE#tQ=rtN&`32u-1#Z@Z7*AW0`=X*7`#vbF&?UBzH)e|gq?16z!5 zX_o1+&W*)PR6*fGXIoi<2CK<@E;8MU@g31nG)mqwmXCx*L528_WIzOp2D(PF78Uvl|<=q{mQCD~OVx!~#;_ED<;)=E{4Iw~q zg1ZC@?(Xgo+}+*XEd+P>;O=h0gS)#H?yi0E#=YIYUjJl_Dylg9oXy&6eREFCfdNtU zkw&}C?1jqJT*cDA6BDK{_uapuq9C`9ii>mSRe2$o^MpeI&%ZA~OhZ_X?{nDI?ohqM zuBT&NITDuc212dUAtx@OU7E{gh>dRyh1+U;LXt(AiczH|l#xv5V3^_7LbV{!#VPxD zZUb&ATCJu7K>t;R#sCfoQh-(j9-sG@pq^jAV#{xKxxYMKzFcU5U*6rBjHT8Boh%d# zSDX+bNePKBF2{5Gqx9XmB_&@Z$aV(G?EY#2c)LPlh4o1g)21ynL|zoV;ZL8o>A-v9h&=1qXK89XR-CbuBTk} zq+}r+6npUbu_ccQ9ogjy4U|tU4^v#{S|ob%g9op1k6heg>#xS>x23i@a+U%Dvlv35 zU38fnu|`Hy3j3W$7$#VmrMCHem3lD_3*zfFg$dE(w3PXk_UQsiFpxX}DQz>BMqRON zXtJ>`ub506u8kIJj{QKGl zX*iPK=9yyUBROytoqiKDI1`%17{79H63NLJ44EAY8!uTJA%dv#6>NYl3!PCG*lDB= z4pFl{a78kgf=~+!5}!bwJ10K&T1IYip`QL3|Gld2A)ak`8DoRGN1{7v_l|oN%50&51V4sn&1OU3*YSGt( z>>5LALL)g;*5M0C3wm0DPlHy{YwbT}T3jJlMa6s#pwxbh&Dt_3Ayh*TZ@1yF+jj zEZMgmD&Pm!Vht|0a~f1~o`8r5>Z2nQ!QYYim746NPg^{SQg#oNryXhc2kp1!r$Taa zbmSrEh^|!8e%v{3zC3acN3`197e9cQ$t=}uD#vTsAC?V~NH59dmM|mhVlLH-VQRWQ zfp=Q>QxDs0so{}J++4Aqwz$k{nWo#nLH4R?KP=8_l|VP^tFszzHKd1$G~98b^wE14 zmn_DmZ1R)tSta;Z=2KSi3!;#@O@loP;oYiTz1Mp3i67?1!y(UIblMv}6)Xcu+moGg zTdg5@8Xbf;>*g+C@)N#Uzg>2}yzqU+{=o&s@X_gJah&&!TP<{C1>DBrleBL3VBTjP z3#F!Xx_r~iosz25Yp3Z^sngSnu;m*_CpByG{2F5`Ap(N&di+xdUx~g1B#&Hr8RCza zN)g;Y%mwVyn4OU}4u~F*o)xEA3}kqF*;cRC^!Vv79avSKRO1ft>UqO`#x@a-F(=oXlhF4 zIzQM~GnrHItWMQ6WuMvQ^nU&eE1s?15g+PPr@{F^d9OVol&sv~V@*ykuP#uZPr`|j z6eX#8c}mS}VTe9jK>_-jKbv1uHBiEyJj4Dc`!)idUpzkX7D2VBS0JF>63J1EXfTZ*^?QMhN7Mt;>3p9ox>w^=S2AJM+|di?ci6ih-1PEU zqUjR?g0;ak9t_(9pjzq2EKEH;VatH8Nsa7qeAPIyy( zCaw8NbC-W;9mwj7MTKKb;x#w0b=4?VCDX zU*@EeA|R|;f#+5*Ct|pqEx+F}O%xl)W~4~FTB7Al_(naxj&z2ZXdD1}JRomFa*tNd z;J0^NOh79QgeXI8y=R?pBygdXz~1At1SqLl7jR;vN>(Bw|5Yqg)uM$*G-{z zCcQgog8$PPSnCF8#XT6gLbZ(|-1yTHy`i5S!P3DLroRD}b~MstJP70{3EO8#$ggN~!9ay~y`N4$y$?#!sxz&s z)>BmWkwsf+06%g74O1-&52mJwpvk#EJ?AyL{?_di&}=vgSB$SwDUXf2^eLf6bc7#5 z6icaukA>}RO^+3|4^ak73$Ad?ii-?EJ2DApj;Ew)wt@{0|MA`0c1ce>S+<~Gt%4Ir zDgQfz1TaH|AjA9?KNu8%zus~?%6M(pGDwv=^JHcAIqyxZW>3~@;o}yX(PG)ylr%wG z+cCgk|3DIs0y1v1ZC$2)1l+9IfYv0_-&@4zId#8FTQH* zg`3rA2vQT%glsJ7+S=pINZ_0W^_8XEfprHKF*-VVI0;DG(PzNoa5{jmtz|q|u@DpE z0!|J~ODnmf)sGc!&li3jY+2EY|vT3-H_~Vl!7&xnvoJ~)P>a1sWvoFr0$j}Sx4~( zF5=>;S~A(r_p1@;O!yaqx9j6$Mv%Fh9uTx|JOHnrz5cN2-v?8U(Dt^)HpJ& zMXU-%VVnurqb8E6!Hfa_Jw({QWxiTut^R}?K53^8lMHo><|_}l(h7Oe1(y&sTCaR5 zb~P11{k+J_H~!{@&tvbe+euJ51yf8F8iQA5KY85-fZuiX_1Ij-SSO2qc3o~BKA&?M z=9HP3nJ+Idx3&q8>YWzlfT-MOPrn_-re0jplLNYFaVi5Qe-gP#oM@U+=Rz( zBd65PX1tt%y4}mR;rpOQryEAz{P&knXHx|d0<8vOT5ti2F|OL$88G{9)E=!1PRPQL z(hRwvw(CA9*^~LUn=n#1@|Wp*BD&vs`~ocNkL^QjIF1Gn^iV&opkh7VPdLF0_Ip?x z>Q`SUoD2sZu#_i&DjGOp906hasw6|AVU|P*OSK@C{)#|v<-mXvxBp} z{^^oKBpzpY=Bag;_6_7yp==XilmOTcz=p#{0H>Qz^8CCZK(o8n=OqV^Q<2HM=idOZ zNI3K!K>W3mvZoWtprP(yh&fM*821UF$3`V6mcg>1@A9*qhHr8=#$TA_Fzlu= zvcSOt_Khj2+!Y9I*GWRu@gqF<&^A&K4(H&Ym|)FJfgS>$wL5WW@kcyxBfJEXl^c^suaFlmnh#J(w;Nl{v#k>udB{hGP0H^PV?7jUnuf9=3 zGfZ|cCT6M_c&B2(C}(&~qHv1<9?Gw|!4iEWGR5rQKv*uDC!pt2KYD2gI3(`I#PsJb zGJ~SjC@TyzwJ(a^9#>7;Ye{>rdd1sRKM(2>X*BoC^kr;qfat6Di z3q*8?$j^E>d~Jp%j8xTd#bh$W^3qq*v&z|{%RcQSloS4zPeylNuoB^w7gsc*b71W{ zu8W@u=R&|$7!?!s-h)J;5kqLhPN*pG{h_pWir%lsDV>QmCxA@g4v-S46!O{n6DRd) zH-kLstX6|Xvi$LB8h4VnzJLifKF|{;qvVVeg#)Q!Gk|-iJNy5J>mO1Bs>*ml@%|@N z5p`4b*w|tDj@~QooT@yM*M0+ z9+VANkp?_wJKV!1M&W_|OZ|6|hA^N>xILPUv0e8B3Me@#N7$#f*Hr@lEjfUQFtD?W zsjj96lnAf2XY8)$YYH~?bp9(V1X_&&r=^#)s3+-!=zfvdvuP$DBqpN0dBq66cecu~G9#^;T!gv1&OP{xeMZbK6nn zk7r}7qkDeQQT#^aQTaahKBp`wlGC8aw;{V_H{Dx-sy+UeYMxK+*S#$p6l2)=2gaRFw#(pPPegKFUdxiDl+vli z%(p}?f+qA4m_U2GH#5t0*Ea|Uy!APzWtlovqX!8e=a;I0(r46_QrU8MKd)4iYoFZ( zOO9$j_Qc=P-0lY3j#Vb;pNOQ{Zqqp;FhhCAQ5C*+e4M~UklBr`^aQC^3N0HHH;z3o zwyC`wtF}C{<(eoJe=N+G5PdO~^70DxNDkZA&`QxB#^C!i>$co*HE=bIArXFT737!~ z(D3B-qixxciZ?CUZ2z=x%9r;SUqhDg*Wu)4Q}Pg8k{qm1BVV-V72Z@P4<1?{89ZK0 z1b_IY=TIiwQ}t)QwXc3|T86)Y*^DycT)HMWystN2AHkU{gdQf75y@XF8eHOG(!RtL zy}stlPfA7fq@rdFAj!ip9xoKxL!#%#(t+)9?!SrB-N5M5K1DDRhc9tsom*kG$)9=} z21`n9y=ijy_Gl#<%#R*uumlJO>}B%FiXB~(V+(mj>5u&)Zm^q1mK9`LRCJW$Z;hlW zw^;FEkdO4uoT$WANW_y2tc5`x{u3jf8S4qPNUv)9lM|G z4op|K@P)e1X_r1g^8E!Tv1IH7`0rmz4gBm671LYS#-p=nG^Ty~joY4vwIx-UJ&&E> zFBSu>UXT(j%HA);k%Lh_%w=!FC~^!pvp>Fmb(m*@^A`*XjZcf~+2H-CA1g~98OH#D zR{ZOptzLSS!i;@=KiMcD;feo@UX4N`l8SSYh+V*$11B;{R8p#efSK?>-5yD4LGYh=Gu$@3NBU+NmA}pfkjcFSv z3eOjB0h||pfZw-%zVq%tNW>BLso3ql{k2XylaB&)Qw6u&hj@E@*NfFeF?Jl86#>Bm z(`3w{jOwWg3R8qR|0Aa--%JWUzk3G1+z`ap-o`J%7=#{fqTpPvDpK--T$OT42JAy# z^DpOV6zVNkhj(_-E3Ez?a(FVg7bexw|15XUQtM)#hp;?~VZ1}xdLklz`_#ecqWi~U z1Qr^O_wn37-!hbBX@9pauZ$^ekm&leBQf+10z9WnD^kEztlHW7bWIPsIw_m_O^(}fn*!i7`dKyOQwb>@F z7pN1V!RPaNl`cj3Zm#R?@i+m~qhiR8X4N~}Mvr!1dr6dBpasxX&wyl-k`bra}g(=}sWLR~qBD}f> zv(50(Z8lE=;e*UKCho8py$n$h1tw#8D5~9B|J=^2U+mXBRsb_x;Wz%!Nrss!BKPH9@8qc??giui*v^M^VbSn zXfilDPeNfS`2D*&x#PS%2ru5%Q5e9{1{}PfM)|alS@TQVNNWu;e3uf&j;q-d)K*Bz zv1gV$&ZQ%zrG%@SFqH^u@8H_S%rXUBj~`hHKp!yJBfTdq}QYyw*? z2YC$-P-&?^y!mw7- zl>62qE&si(f4Lmy&l#}6F0=U)vRhNaq=JdUSZ{U^DsOObr&upWDZga24%n2)$;Pa^ z{9Nkvz8bBq1IiK00#m;@x3Fn*j%=1se%hE>Xuz$f_o^&w)GJ5VDZ3na$6GrhiXe+Eg+AqSw=O!x-mhQV3 zu~esmcFRPLHju?>53_VG6?^OqY7bV}{Yh~-8ZJl&bXLIipOo%0=CH zx3y5R?3k|&U2AKr_w`lXDr~BM>rD(XWlY0>U;fm0`;1k{B)|C>MKp0^b#j$h=OeUs zH?_@vHC{5DBor$x`9K`C^Eey_*6>eJpJHnpn}ifgERg3ukG=~ksL>_pDx+|OPvnp; zE}?!vxe#?6zx;`BM76!Aa-5k3*pIV&5_OgnnA6*30F8{#PXF6D%WZ#+W!7p=#>S?J z7LT(zC~IFA$^EQcB~qZTEb$$k3~B2yDmg@ej)n@e4y{B*>NZ40y9Dco^7~KCCd6dU zr0)`-!zU|vixu$3S8m^|HU+(;-d7h8Lvr?=or~TPyqnqT`PlmE&vXKA;IuvF1CtJ> z<@bV~-}5J}-k@K*y%SKw#f%G{fh3++U#32{?7YfY6_lMje5&ganfU3HJnR-C#1xyL zCtx_|s;l0zz=R8?ksL=VAvY|-cI^JLpYp3*yH=>{JyLIj?{`9Wu|_dpjf=;Ckbh)2 zq7uKMSO)w7KV#M`7v$~`ylD9HP=XU5X>~4gXHT%C>XmMDE&JJNTylKCM&jfJT+VK> z@hGz$fE6jM?8Vy!`nS>9=V}w6+-ZK!qvU0$CWTn`fShxAprTfm+CB{}?vxR_Rk`tL z!hLdjP!MxDog^WU;^Af?;bn^4h1?JPLwjD%JlNiB(|lrko&#I}g?hALZY3AzC+I|$ zdE3@}z_S~$(3uPIQdo2u^pGmI1U7*CbS13#8`^}6^j%Q?rR`iYQD{8s+?|^KVxA1n zGNE|s-Yurbt(~z8vQ7H4>UWT0Gs2`ifp~5xFBq)kB>gJV_QIZlF7^hDmdi4>Jk;wd z&qxH)3$m9vCMGNxsda;D!H^Cyvs!4^EjFsv$N3|NTE*LlqvUEPpv-Qg{CE}YovFmW zT|+)qFG2nL2)7hU5JWUXG~IyWxDHBR4!*4u(<6 z{vF@~yXv=Yt_up2&AHa`C)}l&DE4iKc&&9J9QM>oZ#*CC!xx^S$TApc%fbQ+G2Zhw z=IL97*FO}-PB6g|^ygxDi=v&F33O;uWoQC1)z?W`k?#c?t}KNmRts{BW@& zuUbpisE{a&Vn#@J6O6EStUT{qeH;fZFvh&ohM9GDmpP?>0JqV>>$#o!^)Qr7VSeZ+ zq$EuSU)*7_uEFgbJ9bfQ9BEj=&ByLQGapgg+7?`V=4eqNGso2f;HJtHJWTKU`Hy;R z&wGw)jpG|z9o&dqC@!CN@EP?@+U;{FmMl5^6Qy_eqlPA5eOYhMM5}8!=H_#F2z`|4 z^BAJ_FV227HQC^c_jt2Fc=skTOM3K-=GV({-pVHXHe=*FIpQfSG=xG zV9g>~ox`%VD-P65m)|nzZGD?VavM|4Eb727dkk7Ro80N80QCUbB{~Ffpe9V z-fk23otCq`1w1w_&NA-CKaP|Ie4^xpgV;jl?~~`P11Fle=^q3r%Wkz(oi&H1`XVCk zw1(4OOL=wGriqqCN1#0`C#5-o@ZvK9i$GUZwPZm=OBV8}Q#57cgp@40>b!+RJuVGR zK3UFAqgN(xX2-mmjGDQ;mrY8F)|S9lom#elluYr*fDxA}4!Y<7`U(AgCq|8}CJ}Wu z!NC1e_$V-~*)h-m#!VKG`FrG~9s7Yi=5qdpS)JJog&)7$q24#@L8X*?olP1xiZ^hQd&#U9&JFZR{plIHwuHpc_fmU%xUHuLzM{q> zqDi@Q@yx69TP}sw_MP1M8Bkg?Hg(F&IoL+>IBc$Um|0qW|{-L#$3MeH$ZTo6QyH^O-1sBH7*E4iGak(KwC9hW7;0uzp2Hn?9WD zh=_^BW+j3qCgBFj0)R9{sxLv^0!^LFV9RdAuhYEF)-%&koZGW-O`c@%;~SE#R+F}1%cf_l928v#p|m3X6DNuodys;mu$hcG%GLbDWMu9V2&`CeI(RyNl%DGhbk@|w+I7GE92As2T=dH|afM_; zA&$8l;Ce?B-?im=x<0$Kok^HftIl(I(@fStLeoeYyoUgBIz1vg%un{w0y-bPyhjBrZ;Exy}R*XxoGFFgkpD_kG8Z?q%Qf zl?|x!FV4v}OfzXR9!9yokzRm^&Bz#Svgu~_yv2&fPJEK8NoumFrF;nf~KfZ?6#8&d%3BRDEkz##7le0i=9zi_d_{YlIjNrPB@@|jdntfOYLTAeQwjtLMxn&$c< zD#+y3{Ql>FxiR(5P~5f3JAwp;3{|$gA)4iUDwMdBd*{epRPz?w_i5j(mySaJyN$!$ zm($*xUC;Wf)#@*YOqt>27g+X=UX{ds9wp#jrUkJ$-fWs5y%v zOZwPFwV#}>uSQy#=g9G$C-9gF0rVP8{g}76P>s+~Ven@s%|7vL)2Er{quIH%Q8T#& zx<$j@ib5I>4=l~nJtyl=ga*suF=~7)|>X~jfVpmo%l?z#wZ-vNS&xozun7vGW!h|^Q`mtcktmt z+i8Y09Xu^!UOx)Ew74F@mp+B~<8+hfvDoc})-_bna55Y>#%S<3eM1z53C@b>E#DW| z(7dNKYD;YVQnHS7yxD?(uW(QR}QLMk9M7?&r{A0)ftThx6Gu7FL(IvUme06J^V zHrV(jHug-6mv20}ixcg zYR!uuqX6}E!^+cq3GvLw7kB4%phzP*XK{(_doPtN=NG1fISaPrK$^g?3-l^($=Qrw~QK<3j$& zU1cc27{mcl7%DaXpN;khuQ?~jcV^O^Q42ZZxX=YL*x|6O= zv05jv{MFUe(UH0^n|8!R$T3T2zY@q&%!ixRG!bLbVK%Da?PC1U+B*w@)fts^V~E^8 zv#Xfm8S&sWOnq*3#>2(h3t=z$ftMdiB^IhA)YPcdHh1}NXeW0|E6G_6^Ogtz$tNC9 z3M9P)pew(moZKfOKE7-OTsGh{9WA^LZ0Z^t%ddSY{L5^))7$p1&Z<-5C@Vdh%!9=+ z#|z6L6EBeSV`YrZlu!5eb~fV;grSkw7{Jt^wODXyfuk?oM!{^1myRA@;sOuTgI1eH zgRbT>CvmCsl9n_YC&yNSzmA%;rQM?B-(W&+;ptpDuvBC8e~IX!;tH=wczzcfI`PsM zNmJaPX3BpdeEo`yD=J!0ss|~mjE1fp{`>nkqF?%$kZ1~?s0El1!N~E*o}QCrYK|rk z4?`h|(x0{|)IkGIlftH^po9DS`+8>Xc3gr(>H9Ea`_}%VJ#8LPq%BK(E#=I)k;xI; z(7qWgb(xHNF48u!8{mlANxd)0#fy_AHOqzLnx)8r*`B6j@xbi8Lj#3@9`DH&jgRYV zyk3R6WaTgn46;7t0M?f1$(bpSIIn8hgig0ZqS;pG>iW9(-)@L;b5tz-<;xfS-e5CE ztP!tL#88N!?G+tuy>4G00F2N{0B^|?#^t$IT8gW7q!h3;##RZGT5qv*_PF#Q=k?|u z07X04f2h)#8pow%4F}10A89tgY9#j<>};*fXVT}WDT}Ef?5!$ieh5&jA@s(}sSy{H zPaLSZz5h^AP(jxy(;V8D{2MQ_@nlr7>e9=KTH{pq=gAk*h>Cd-F<*Fb0syqr7ueVS z{2eA_IPBVVe=9uFZV3MasDwm;KTN-`t+C^AhT5FX_K$)r5;@&D2cU#46pzV1-ROi% z7!KC0E*1_&{2s2jF#0)`6ej9mMj4GlRG}>E(1{EX)OLy^HCZq3r9%n~;o`)*Y=afe zT$1ED9*8jAgP&O_>d70{If+##;1U}Is7E*svvak)+-IW=zV#laU?$$3qs?b|#g!2b zRU@<4Da|lKcyxdChxdnHfa6pmhOTqkQh_jIjY5-ceovorX|QRjBsn`D&to*taG4AX z$_X-7lALfFnOSgx#c;`%?4A)3Z!Bk2RZ6=+Wz4GA?zBsJ#H)s$)@*+-{~=UeZ*Tm7 zVd!cHp8Gy-lzKA*5lL7RjHzmpL`x0e-{OrgLg4Z7D3p}7Q&l?Li!@*}v$GmkZ?FJc zIW{ZL-{&+byO(A*INNB&u3DuoC@1%HX=H3nI?4Bh<;O*U!+encu^D`Z`9A>+SYEtayI)ej3cIP{_KSXdng~$A0IPZy%l!cbgy2;{)zKTF6=g$ zDY!5vIK|LWrK{LK{Fs&>_}R@w;s5t(IN=e%Sdqabgd$CDt2}1cl4(LFx=htmziMWuZ5E zxvyMv5Z5y_iZN(Z%{KU5Mn!=t2W&$WEY?fNgnD}6@KAu=g0z^J2mQ@fPY~&2W<^aw z!bydeumN!fn+>4ZJy_kaXvDGYykj#o^aYKj%~mb{&GQL^XSg}Z8xfAFBMCCuwAo6U z7FK7dM}*#`-yu-W(K>HQ>lNOtIePeH4C9IyZuYA4z#~s+n2?*Zd<658dMm8XrLh2 zJ6P44&@%1E)u*Q+ad88xmGBgLHR)1s${G@Pq4LVrOZjC!v`UztZZ`$P?rN>q?y$y(0^}s~S*-cDTTD~tW^JU5s8IY?@0B_a zp=ThaQgPjbIf`l&QkGpS#6{+3(|2u;-gsbR!N(ya+_b0)LqEEk(ET;weFj>3d3-iv z1az{9>u>9%(Z$@r5@~$z}tmOTG|ezb$DVayV0hdN*@G8|}(e(>MZ)1#%QGcfjPqxT%tN zNEm;#A)@u>->bxLJkB`RigGpHSD~l1jR!qiCn)s>WRf0KOA)`8o8j`!bXJ-yWb4Th zt`x2{?yh9I)=<T7xhLkq~-RB&%ODIV_x0&fnW*dQ3a+zc*D zgfrMGEv74AdUUB?{51PtMi&g|b?icpU|y4zMMf7YE!NA>qmIS(IlO*BI&JKWOf>q{ z{nH%4D~cFrjS{dxfBp#;o1^wdyZZj?Z;9s~$i%1%5&TK>AMka5`cTV_=Y2E2xCctn zBfm1cy1WF!fkI(`t$$@st-KSdiVB+XXmo#W^#gGiWal(aM`7i#XrX}4t*SL21Qt7W z>}<};joB?IOlA z`0OYNiJ*atouw|gPc^Xfc{jYgX!>}{lJ&bQbrTL%k}-lU7k;jOqh@(l3MsISg&tby z1`HS)+$2Ydh=?$lPw5K<2F0;5v6UlatiOlS6P8BPSOG~1I`dS9y33Gk7sHbSm&;rmY06jyxLrI~QyLghu=Yw&!;->%9yjf>7gh;2|;y-QP zVfzrQq5@gJEF&fcRa-m6;c-n0{D$2F-A+z>eZNZE?P3YyNWXoPPuG4RXVr0}rgwHF z@SLAI7O2(~wAI&6a@CDnV(ac6I~;V`OVZiX9v|8&j>SGXp;(O0$fXqbKlHm!ZL&Wc z!`0Y<9trsCX=|q3xYIbsXK)9(n>tcS)49Xi$LFVd>mx;=$Z}u*WFo7q@(=co;Y%0y z6$Gd6;K0^cXu&A&tsKy|J;#$Y(2TQMAUrrg7q~f~y`28&gr+j?7VG%fcHYQ*!?C%o zYF$(fihets8iA9Jy8#nbF?f`AO1ej+(AW=tslILqvF|9QZUhdyRcX?iF)I8x5e&Pgc%DFslZDl^nwR8ZpW>k~-)6}_XQ z8H`3bEvK_8$_=+z?zowpV{vO%-OY%0u5g57S>i8HJyjZh)ZxS-qIwmkcWRrCGtO_G z9#jMk7mg4ip}yq#&d37u!H!Dpl88XHqaadPa z%g5R;x+134`6|romzP=`AV|b`27e-0wc5XXhs^97$9#EqgW2nMV@%X0Z+Typw1i`E$gOuh{PtJlZvmXzr)bm+jJWz?6a;|M#+2{x`#qUoC1bsnA;dW4hU ztvr=sX<5^g$Q~eq|C0@)&y*zy3;hXWwRtO{3x%Dz8Wvve1|N3 zWh+LD;$8Ln9o%~g72nV5L5OaQrvdF2y6o`7sxrMj9W(PnPwnKnVJZr?|yhzH%h z9(KNhVh3i_&36KEi>+H&i!osmv8h56l8$v1SVv}#77NJDYJUI13SlZ#Un4yNze~FG zdAL4V$5@wlQhvV&?A2(U4&^WYB@<^~m`!TPd!Q1b%p`+IIcrv_AM90zW58pg4x+1U zV6GO8=*A8nyT-krpDe?bL3L$R(~Tcrpf)z54r>Hhq+lqAhtxmDQCu^rzf|Zy$z6EC z$03xyde# zO3Kn(MKnkimC^U6+QVbam9+Ig2D2P zU^!z^7h9fL4-Qw=d*V8$X?x*T&NSvsuJe$~y`A%6p-vuRu$wglT;~|2`vtS>)sb_E zDR=WPWe3agecdwPIW=^z zoK3n?s%v=UOfE?IuA_eMVDd68hv^`jZjbDAr@kTqZUAL*#^iupM&^Uz`;S2BUTe_)`MJjG%v7yRhx z2uqs|e0XHU_-IC+x=_ao@LF&_o?B+cw!c@zaXbID`^u*1l;x!$YCCe-&W+mb!KZXk zJ+S5y;Gb`qVo=O3U>MSjSTOBqZ?%PjePtAvh1CTXjTGQtl_~*N_4VQ^u|f z22G!?5L7l$wobfpbj4#V(gGgUo45a-S7{j&F9DuxjK4K`v`6}^sH(F5?qFj9-v?jJ zyF*P_9<3&3uMKWIu9MR+LO$1@?Vce@>IVIa3MMNo;d?uAtovd9O}0ur ztg2FD`yu&S>l2r*?-i-6+10MuBH&APajE^1GJckLw7;7j4m0B_mSiiQtnoK1Hbf;Y zhS%Gn>-i*>FE-J;%|C2Ku#?w0Zc>6? zY}dywAjlbaOTTCjQR5*p|IK#y@DRGc%W|Rl^1<@2%fiDGC=87Rtmw3td%WHcKF+_h zvk8D9SjwBv?I9y@JO?k)YNnel>QqQJW5MxwSYI~r)2sXz1WrguxM)nF!`s#XL*lxR z6D}PT`a#?G>vqg+{#bS&91?vtM1|*liwmtB63pn>q3eC8g;{L|1sO$5*_ruPv*s!J zA+tQ+ZayJ852GvR9u-3)Vzp3`^gzV5zu-!Fu`Bq{H33&SbVGp)(XXwGM*kB>IDO4! zs5?eX2^@!6j;rnZud#j|ypi&$6IFcc7YpXT3*YR}=g-&AZLc%eu+XTJYLDJ3rBu!q ztEq_z8Lj$|Szb?Q>6?gnM#dL>xAzH4`u>JSTvVFR8y@T-{B~rNv`Ah(lD3J5{sxST~<^G{ZhgiZW`%;ox}!5#Y2 z9l#V(+#HFXT(fK{Qdvbb)ye( z!bp_Q&j5KH6KWir?%TbPK(LYK{R81X=pv?Pi%0Kxq+Zla(|w=WOvwTXIjn8`IFGf^ zfv-<$0KY3Zd|BD|ty1h_GHU(B-tJ@7Vuc8Y9zQio^ss{kYwy^LF(p{!=T6?;?iJaI z`x-0ik26FbUQ!ud-9xgL)bkbJq}$-x#2+{;(mvJ zLhdSi&e(97*lG#{EqD+pC$!x($dZ&x3MxowTfAMm1aNiAlS5%W-eZrpN%4X18aQaMeH z+=oWi+_bkn^P>0hbjx$-{N50bo-p8%z6Gl%tWqYUu8wT^P7EuXw7F==5UVvV__L59 zY2Iq#roZpaVza3g7_0sW_;-D!qyTGzUe?wzz)h~@Zb1v?5(x-;C5#vtd%ipQ0s{k7 zocAUP{#BtxvKc^<#(6rtKs#b3G3ZmMOyYoe`FwQQd|rDmqT5!261Iz$lHA!-NNdhG z`L-dVYDSP6$R}u`u?DWa54=emgXayeTa6uCv@y;^*};k92e^E58LOpF2eox9Z1``Y zld@z8e6Gsy&W2ELRy5K5xvJ{aM(cVjj9kUUU3i)@)|`t?-)I*;J`vWFzxd6Yk{2&I zI}7d4jCWVHc+Aoz@f2_8=OWu-^c*^Y%%&>|saJCn=^N}pN7dcY9;dsf!;0XL?d#y@ z=ku9oG!(6&H#;5jhk%gr;yJ0~`CLx%Mz%$~aeJ1tx*ek$5;9{Qk$^yDLv7}-3Y{8w z$?UP5shoFkwW-Wj%BFm~DO|5#dg>SDAXXrPV|Osh!L9n;uyJ=NX4jtM&~K90q=%J- zCX)N21GRi!%F@yj&^{zjpWG$s;zXU$Rk#ADDnO92`{&)KN&~FHP9f%&*57&cl_k-jKDSG~S>I0qWV9?4q zg=3O@;sJZ25pAZxlzT+C)WTv914lx7afY4sk5cnZ>a7Moir@v z@>B4lRkZf@jt|&}D!bxleXtRY`T)aodCm8X=&0*TACdZKXuS=>6Miy#(e%|= zSYOrY{YXhAsW9dB1g>qR)y?^si!h7F9!B^ zY>bGTJA#1Mv&9q~Fj$|AmrvdTfrgf9cT?HU5VGBOB!Q}3y~&wURCM2Mr&OsJk-n3q zb*3Dc;-XuYcLKNi?$Ob37^(^dCFSFEfgHo|5Oq!jE$0E;-y>_71JLYM8J2n0sABuzV@QW3h2%mS934>7DXwA%R$c<6icj z6S#n}o2s(aUuG~ASg#s;zV*V8?Z9^vpzQk7dtvzHm@D&2*b5b#K+4=5P3M_+Oc-HL zO3;2ZTKMq9?I4Xiefy{goRsIa7n||ab}#aC7mwo|?>H?t;`U+zcfOsavL=Q39XJoi zHh$N$F&lAwc$rW&jn96|8)1LBe2Emc2p+&_A{|3di!{|3M4%}|E(rS%J~Md=t1X! zq+J`rut|OT(8mIEI}|XfAG}QSwr3_Q;}?cr``KN5)9(BLvp@0j^S5eyBK(p9o|==y zw=W+EhVJ)F3srAvKzehX-KOr?K^ga9$N0mtp~ zCN+y%ZytXGf*2du+~PYA+0u95j2Uw>76!SlN=iz z;9w2;a>nIwO}%l#ezPhm_myfDqz~;aUcl_StA4LBR;GQ4w1(L+fP0e=ogN>oI0bx0 zA@fdwL!rKXTzq(lIeQBSkEDJhMenUwwk`$9*8JGHFhVi)c;yH?sOyyu#8Dx>3IN zOw0q@o%X^^1N+J#i4j^NBH9=t!sPJUf#^8no^KR2W@_YU1m4kP-h|)i7DSAk_-F&+ z7j-;V#a&N{?tW#+Xxx(Z`=Pb*KgU~C&WuVfkIhyZx}9uYc};s_9W6mY1}t>h?Nc6{ z-f4%z``KY{2c9L`Z@s!hK)hn}KKcOsqwpzpwk)a;zdnO+AP{nBpI?r0hx)iltv(_s@F}T9k8C`!!h3&`PU98mmOf!9@E0%th93S2+eiS7(#8k4AZFvOeQ9s#D zc)txs=uAZ4jBiBz7Sg@8LF&&am^2gi58pNPRKMCo_2G1@uJd+@akVAps{PWMlcN~2 z*lJFu=iM538|GxNBNwHztDP8;b92h`McT#(MdOHylsqkK5}M3}B=~B8EAhCE27_~u zvm=}&m@26ZZZU+j&YCUL_XSO(nTxcy-~L;xmrUBA7x)ddW%awKO={(<9mzYi_G_Yzf3nE_Uh z?O!kiq=3Hax}G7aum8)E$qxQt%(kQ3dX1Ibw_||q!B+GDS1gngS&lqyK(*zrI3{5~ zmw)^17s068sr|hwNBNDd$eqQ&Yvua(Xjzw+UD0t@M2>~K;}zU-D)QC69|q7Y41JT2 z{NkE_Rd|JnkM;5@g0o*~v|F3`{g%VFhUj%kME(Zu)a75TNxvC0Gq4naiCb7yI^x(>i!$Z-JfY`Fn;t{c(#HKQoBovKLH7$5UKZ$lkcM9 z3ls;;&Z}>%R}5Q=)o!Qfg10!FAymSxv7SH3LOBS;i|@5pHiC8wqP{j}o{3MF+#Z9I zN^Yq;Af>+`m^{9{1uu2@mF0}+q246koDIANOl;X-ap)%1{7mcgjKcig`4f=X1G2J+ zxSfx?!_St{@O3bKeSMwgbWyJc0A(B@PzV_NaygI&CT$=)zQ1xU7<{Og^k=?DM(lO) z^93VJ<`U_%>^pc(Kqq)L_}x5xgq>xp8qtCO0!KeM`0G$bi0rTPmomfB=hTO81zGeLvv&{aV#q9U#rP_ti)15$afylgtU&4Y^H3k#Z z`wp(z32VIOU+^_{##Z52z7Nt@JQE#*!u$RI3`)FuwO>wB{u8#13Sw2IN>Ii+n9I6wlS6_0crd^qRu$ zea7r_#R?dqIJl{FX8REuo@h~GZ}JUQ;!87dK$-8PHk0DU-T;!7ZN&sJL0=_l{7Z5) zG4RZxYc|%y4T|^&-Nfp*-`r=Wq~?Og>TN+I1D$=XtnVPYp@dk!J<7S=^FXJ)g|y`U9FSd3R%60s9X@R8wV z0a8|{2MnU$NkDC*(;<-bTeIaP`)8uZJ_4imcN$6+SDqMBfMCek=0&i9FXb{u4%#cT zoX!+NTTMwcmC5~Hl#C&LVE4Go6&4Z^H5j<+yusMt`GI{rva^Jhc;0fTH}2uaWZUi> zXm(c*g_X^R0FrM3QMRhkQk6?I-|dZx)D^L$c^{lj_}N{qU#~*^NX$){GT~K5 z2FI6J_|-1Sp*d;GOBLp4e^26Nl)!5J4|a^`^z@+i6al&uURYSf{uSZSK4lqQId+po zm=7czu7vw8a(*mO2pwj*@sX)~pCc=MG*>7lV2S*+$Yzex59dE(Y&|)oE(^_E>qx^l zp?I(*tX@UPm3(B@<_8OVD@aELOry|3bwm$<(j5t+a~#UiA)ELI#L#mM-WsP?7V|#fIqpI$cPh z$nXMNSqhi&KJjLjO?)0O4UP=m+T;p|)WiNe+0l93l`_0C#%At69yxgVnYRu#CI|%d ze{~s9lhRavmjCzy!ArzOl9=^}^0nQ`D3i-STL|<0+PbKn#LHWZb&+@nMA z`gdK|v!!+hu(0$+<#m(}x+ z_wI7J8vH`EHQ3ie3%;DojV8l$p*cW$|BGA8Pf}TTKB(%-ba5yCxSPth`teu5k8ka- zK8gK9qfCnHA9F&PgFp!oD{~xr{^ihYu$2_6vZl*;P^R6+POceNmbeneXdp1<@`<>J z{0fmcKj)eDy6?XK7ISD-2QM0r&3~kGeli8=%7GtKqF^v;Jky4$UPFO@3lODkzlHOz zP7X3Y?8qyW7bY6t4T7Tm( z_MYIt)V!X}x^FT!Mh}WlYnqcuX$WhZp7Y+(c{ATn7+Hy>ftI0w9bCR)(t4OBq0GVF zL`YevL3Bv}HJZ$W?(2%^aG5)*_sA&812UXWi+5`N(CBxbz8Xj3VE{RiWZ-1g4oQEu-phys}6Ub&;c63u;p|fmHewMZ)cf?w~F9QuJxnw zr-D5RZc$L0Nc|$L&zp&pVuu6{Y`swB$?@RfD(|7giaS6F^^&9$G(;nsVi`e;c9)GIh?FGRG0pnyXzX!??z3lICo_7)M7D&4K6M#&Lb!r= z%S;xtUkYSZlNfj4l_iA|SerZ#u^l{FR2kCbyb1zvZ3u_|@Czrc?CPR6(r&HnP&Gl# z8jgOv4+xVG8qP=)3I!MsM08|sEYO<_Mb}DX2(I#8G7+S9#El+g{1_<4OFJ1xEH8N8 zQdha&tw`KwX$wM5?MNSJNSax6_E}}MLwM+X{r1w&-+vW`z@K&vq4ONM6D_yi#(l?U zRT6N61e-T}S1RGuA6oOB)L_kSWk~w2|g`Yu|uB)fT@`Kd!O2)@mS>PqjTzrQexqS#sVAc-0@!* za>AYmimH6{oC`eBzlZllF_0yW1N*#MDk>-7kyn~(rjaFKJGAiY?`dt*%><}+=a=%d z+Re>M)hjR{2^}reTs^PI*J%t(l`W)xXN%uRRq=FG(RV8g zqi-g)j_nYz2ae{QPW;IC7o+3I^NI0g(G~_T)2>w)%1aFlC{iaw^$Z6cx|K7;)M-2B zrktM48HeY6MJ{x~Ky@javbJG$2evR_OL%vZ=;((PTfubYqsS_D+kww;Pwe}^{D5hc z%JVm;WvdPkSAuHMaX(m0xaNF44sKVg(HLtA!I;li5B=&`YA=$~z^6HHg(~kiRgi(R&`kbVy+xEU};fadMWQ}QIVbDjhPafZ=uLVKw+Zk(uuk@af$8cc7IZ33>=s9 z5R2DzN9+Qry=%vq>Jn7z93h%H9dEZvDaCB-ShHzVq(qoeCGmM%3Q@G)za4vtb44Xm zN+to)Y^S1Fbkqs6Ea+P(w~7b8)$+9wK<6$YAwc(@%&K_ZAA^$GpSJo(>CZ1i*=?ngR4IGFKCfqCSD~ zQ9>vMr$#nRF%~?RO4jxsq4K*1*};R3)DHph3zmivbwj zeXV@$t49ap2Or1R96DlApFRIA_Sm(we9K<%cCQ+UY!(Kyc>K|)u4$Y$kbwITL- zV)CjnSaCM*nQL*eRy{KUWW?Z$#z&FH!`n+rte<^GnpLJYO>~Ad z!{?M@kdL0Dz3)rU6|M9i)&WTBa((QopJnQwTKfwg5v8|IL_+C zQEfZ0Bz5$m7ehKzuVIwWy2KLlEB$p47F!Eb+AC_sJzY+9Y??Ap*vMg6w z5KB{?3|YZ-avt+uxu%!t)I#OUQJB2jGNN>h>n8D+kxj_IMag51cNf-0CfZ$MPFs6q zU~!}*D2}HxH9gJg4}Pp~hpLnwty5-|jT0vN2w5q9sbtXU&i!PY?<2zK1C*{V)Dp(( zWs0%12TXuIer->WXv~){g|U^K~LlUJIqr5$D}^1g!=4) zuxHaoR#0WiEr98AimC2WL8I2ic1g#Ren8nMOX1giI?tEmJ%m{ByOo>6L2(@^As>CF z79lD?4w zWj3zYi;w}ga%#2zmYN{j!1xDk1O$9dB2r`E+=rV=E#l2P`ZefjjqN05{Kx0Rp;o)F07uaF@*1DoUSWDLg2%geAjnc|`X%do7@p1x!HF;EK(k46zKqnw zyE%wTm_UC?7CFX}vTI`?9IZOY{)=j{)oOztr`t<7LTbyOtprkCf_qXXdP1@8{)Ec) zT<0DRY~~~HKsiQ(Ik#*z*(%XMs+<0^j+xX+l}3mp z&mGOh=yOutbi~S?xqmVp=ygzbg1@&+RL}8vfh^3ob^nC0(ipeGL<{5h2Lu&y&A2f# z*Cz;Ju)&E^?&jSKk$ab(R$sI>4-S!g$AM_;-Z}=TodP4grvw@~g(z0;S8wmC6a*}3 zde;oG(9BL`7e~drJyPuUL=ME%#(aIFFYcHTD2Z`_UCK&(+BvQ3sZ}jw<}+>l{bi&E zkrc6NRwYVE0@E!!dzeBuU&lV*#YlvNlF>DS#pWN&B1akVwtJb)*6`g4-D&^f8!R4$ z7{ypFopRqiISZ~X0xNw}veol%%w*6E3ijK+_UvvxYQJ(wq@rZZfvtUvO5@>|B%hb*0p5%=?MNY~@_+7-|@Au|9QHn?!eg zT|EqXbpD#%a*Yr6v$a6G0IvN&Y!zk29gn1@tCxR{`9=koht@ao2m&&SPAsh@O8?)5 zl^`1nSSV49AS&aw*Vid36S$C)b*_Q6nbe@|hWu0~1waVZB9YIokWhc?!d5r}fyA@n0 zbUzA1fi&r>&TIa`e<@R2nIe#~TO~`@Cf5#*R8+Y07=U%u_E9>>lqB~UpeBYddF>rt z{EMt!s-f}8{_7Ac`tJ>xkpIIIjtD>EVnM02BXu@w2`zWWsGEd8RSO;Iu7kJ$c`qQq z$jAg}6-1FC&$UGPr=Kwp`I*o?JwHw54#*agdaX3LX-SSZ=i8K!iSfs{3Krx1=!>xlB11 zI^BShi_zD7mMKyjQmO;ON3WhNxwh-$+Svt@FP_}+D%V^4;15&uRZ+d@{+36j2TGkp zpM|Xd1Pdf?T@e0#ghk`>;Y|S0YWmgc+M!_My4kI*fF#vyqJ!qrnhqpMs^1pQ5U{z^ zE-xh(h*cohxc{`+VE0m}cqd(hevy}9(50~Dy3ai4Q~9jkZ%PofH1n&qCY-n)j|^t} zPbzax7Ha};*c~6dwKU?kJB5N3DqE)4+0mb241m`UogEog*H)QQ>0X~t*dqu*x(Ohs zMTKJ#Q8o2$^SLfbe=flqnWz6tjX^U84DarR;UteF7`3=eC`I(?o%by8asNGqz z^mrU181~EXE4YVW5jnRzjaCgy?c!dqP$F8aZMHjBUZ3t)<~XHhDvaNNb?W*jOXHJ9 zg84$lCxB^J#{R>iUK1J-M+9)C0!{E_fW8iB#XwDQ`)s*ZBqHv7E+rfRfd`;@Gchs6 zCvc$SR4rNK@w$BhBh(}0b~zkeZ}+66s)kU84JkQBNaI*@LZj1cAiiIml6p!sgUD^F z?}&ISmCsf^e3PhxP@TFuy6! zt2$YwoKEBpmPex|Xd6DPlp{Dza(ZK7STx z^6$*rj~pC1Y>IHhPS1zRU2nSzusGXxz@9Ab?nI&^5KR-W+@iZ=BU=>*+q+~}Uex6?p=1SE} z*vFm@DJ+!9b~UuUz77S(te3v~*d1=3%rq`!n+U<0nzsb~sj&ot?3h35I4`e}jdq)L zMpNFw?UTn{0_HFJZ(_x29K11~Z!Lo6tOghuqh91~46bw33uN*AIoRtmz#l>Hlp`DN z84SKWTINggofd;XyWXaf5F~7LACJXrPeL1$8p)wU@s*2_B0)D0S9%eZOlB6Z$1L{8 zPBu2%;y$|IA^rY8r{wK!Td_yAJCH-Qz}^Ub!U}xAB?y>%#KJ`CGsyND-*%}~P8Qrn z8I7i$2oBNZ?6bjxb^||>?D1ah`D+uc^B@(-_!`5~L@wGg%@t---pfB#Et0)LKwv#w z5@e)u#ySR!e?nQ}31NyYWT~?LveXP!q$w|y;T5QIA-jEZc13Il*9`(10@#}N5>_N+ zWSnH_XKIY4fn^S`LgS|&&Nc?748<*ZKiug~S6UNwIUXEj>Cxuecw*S1XJ;Y6&{g(> zhbkxO@}hkO$oCm>9gOGKf<{PUn7N zJ8o>{56QD^FKJNb^QlQr+nG8Bs15KBi^dAQt@Ja#9KF+~%)~x@e;-)i)m9G2{JyT& zo&JD)Fp_R77+_4;+6j{>gFiB#=cTM6J)vKD{d$;8nu$LU$#gKnFD@9F|yes?l4p%g(+ zO--Sr@3xXB!otGttv26q;*BR4*U+M&ql*X#fDsT798#~|5)l#o^m#j{_gwRN!8dmQ zAjHN#0#fp=zcrVQTx^!fX6V;nB~hcypeAb zqx82BO`KW3o0PrZ*swb82UF@Fo;Fy~>1k3@ECs4+^(rpBENXSyue1p<)4I;a4hp8{ zt-08y3mxOj{zFNlUujK9UOF>Ju7%y5u(^UME3dQF>NhDz_&5Gc8M$;^WXSevD5tDV zf)zV)f=Cw$we0CBm_w`M;uH+%jYiUf!w6nbnSfbZHz)>J&X>2hW%t_qWH=iBCAul9 z5bNvfK#hDtwc)_kD9aA8>Fa1@q^3qpPEOXS+1cHJS2m!F3MQ0(9FuM|I9Zh$FEH>_ zZMB84);w507GrW$YG?RF5`VGNz9iUVn6q52V2FqblRGAj&hhB;HL~@DxQd zWG{xIzQbY97EK+$#pMmNCm|3ck#sw@K=T-Zs%!bxVgo(z>xg9;(3q%Eyg{9)zMvW&Vm>?ujuj{XWmK3(BA)5rA-E<3pTpsO7?g_HsKDAGX2Tc0BSn^ z`K=}R)59_=YhhH=SKG9K{(V2%5y}uDfFS93;PVq0-|h^^|23eZqDf6o?gL2es`^JD za#q$b;K>U{U^(2*L?xvD<^$H8g=12`p5qXay*F_m4qy5cKAqr9~!3rdB5&vnPrt?iKt9eb@O>&22bWsBtf`X4&x24q2 z_WqAT@{aZOAGJ}s+Rpg#N!MVAHZ}x59gVgeV3ZTnmKwr>xiH;qUe)_M77^$4dkmN_ zdsfvRT_(&Iwt?^SPs$rQD2uh89O0>~_P`!R=nIiP9u8RUAjZ>+06PUMEBepX)notV z`pIT#OjNlHCt2vM?<*$g1!S=*%K+j}Qg%$}3W0pIu;3?b%x>1AQ5O$mFB|+2Pa2W! zpj@lM+2sCQ3~8`{{DCaX@R6A^16Dm{HgDh4O5%KsQmE$9d0tF>P zMGU9@1ri0SdQzl%3R0Pt^{9P8;O7@2(!2+u=RKQ$>1*lp7=ftIhqk&CvEsG>aQpsz zaYAkhpM!&=t-XD$77Q>I5cBg#P-J?=jHkDL!NVIR!RKK%nOG~3UE#E&qJA*|;-0QXPDW}GulHgU?h zPkILT)^sDzwtKfA?zq$1GBm`|#&tcC$ch_FNFO3U^34<}Y{*vjnr*Mw`UK5dPxfsa z9h>cQ3cK96oBH4A@)a&`rUb8YeG=r0mUy4p5QrG5aO1lL1Z|LS%ApX?bH^m&2^?Q* zNC3-OYn@=Gw_jXciu}0}*hiEq?`jJZ7+3MtZ^E^qM8XfbU-_7KC+ixR?Lc>R;M0>R zW$wZWP>D_p_y}1hAQ0q_o~{N6m)bo!&Q=-^`Y?m8B!a{jTU`lhRLcY^89+C!t*tk^ zvGU2U09{^nQEQCNdO2)bi4K!Kjb%b;>Nm=+we;>03Hg!Iz1;n8ipi^`A%u%%FnHM0 zw$#fhzho!tW1-i0CouN1r*wp%FJDJ91&a#tL-M{oZe`05Oo#Wfv@T}2oX`)YD4-{E zw~I09+lecn(`p~z9LF_Y%^Ygf9#svs=g$pIjFO7Nq{H0jW|V$=eQrFS(Avr5pn;{r z){uyJZ+ki=nT5X=`>wKJ;R6YJ^ZF7QVSD*pt5Y41ZWPbQ(8daDl9kJPK064l(yyf zJ#B$EozUBFhds^81ngx_i12u^`md{xqU}g2Xs9bhIb}@jkCG5i*l92SZkNfl>04CO zGdV}@R4VyiHBb5bLbqpO<3pK7r=FNI9rg~Kl3shfzku=LvjWZp%d@A(#$ckDub#G$VgnZ%Hrke=@et(yCEHO|| zi3tC=sycEbFSC14xFJwjBa(?f!Vqdw{i?lXEx)eOx(kmt5VkYtZt2!s^f^9&Eq_T( z*3bPHf)YMVq1Ged`uS3)u_rj+E4Ny^f7WgL{UN6w6YFUokP4YJTlD zb5NE_g3}FphG;T<`;?S8cZ84=KqGZVnwhV6Ctuo;%clGdORhCvZ-cT_tt6-68EIPt z^{=mIyuZOHG}`cWJX?UjJe;vHK>QpY%FM`~|2+ZHv;GUI02jmM`aEwKOSOw&Oven9~_KOIhCfXqtAU6V7K!PeonEDOGH%ptuB9sEITUmYtK<# z%B%u#l>zpAV-_V`Fl=z!!#H}&!{N}OnjQc?P6Y^bAt50${q&ZhQu*D>%Q%>rrmJgw zKp2v9!}kGkYD$gT8D%4Dx9_sQ)}NB9$nGK!kR23CfjCR8VG4)sFQD)p;Fj!*o~xUi zQx#KDX}5SKkEPDXrl(u$iu^0lg8BEC0ICPekG}$%^%k;9O2n{NuxZ@RaOoA#Ds1oc z@lxUK>G;{%*|*1YsfUL&WqKblh4SfT_e-_^G!2kE3G_Ox*ZZllPkV`MR;yJVVk5Cs>ZsZbfBfpOn6E8BRUb3xe~0=otab14 z>BdyjeLX&WgW6jObzQ!AmwP=iLa`C>cPv^u_4aRgIH!Ezs9k{J(K_XklYQ5Iu>>eL z)@X()jgtk|TI(sd>{^XB#&^eau53Rb(7$5Tn9qm-_EwQjpHFUE0s8x*C8S&~dp+*8 zW;^B?Hy2q*UNKM`^gHQWd|(Qsu7UFD19hgU?_;99GRYSoK9io0=F(Q9gI9-j{C>n<)o zBRAL>>h^|-tocOlBsoXKGBw)7uScR=Lj$$_Mkaf4`RZus*I?lg={7uO>}NO3wHvsR zq5&6`*Y6i}qKY$l8K7WA({S^OJ1wRPOTPQcC%;CJw%v%czy*Ny^OO5rS$eTcfo&snZ{~BXGM}%j_kpLBoihOBnT*2C?GEMS5wygwtcSY;GCZtm%l z-WP@3sz3$6&^Y0NRsH(3x^0WR@L2!Y-Q!vivVLVkzVxb?_fHt2JJgGZFkSE)DB6cK^+e&!Y1 znPiN>=P}4Y{JGqdDc+`B20}5M=2l#4nKqsLq8apRHRG=b5=kT)Hk8rA(tf#j@FB6{pVh@-M+7 z*WYYJnnkpRY>b*sb9nV2(`M?Obz`P#IMQ8T0B;DYA z`1BW~9N}7M^&>~Fx;;~NLb{d6eB6mJEFpC>Mrti-u}IuV-OTgQRn8lG+s5`7H@OOv%fw`KH z{mJvorimy3Kc0WBi7q3aacFd;Exqr(nw?BEJ2aM@?LsVD_J9zrDR92{m^#>EbUI2@ zZ?xc2JPO>@v0pJ>$vJ!*m9Dcs6qM_R@_Dz1x)Q~(RALjY&T@YcyggZjP|-)#^3f4| zA`|AySC-CD?HZCwtN86H{|Z++bx_?wN=hD+{{8NZ0NBbnIXP=i2MB|M!@)NPqe;-w z(ShoOSV(ASR5U{ghB*sKxe5=|fp8~DV>6iig8Z0Fwe=QOHEnGfH#bh}<%Uvrv*#za z?g-RzGE(drFU{M%Ggt=20rpHs#VbxWuRll9&vrk~c=u^nM5(eQ*l#_Kti_PM{**0B zpw@(#Up-QCJ74tFFo2BSg5AKvOAC;QLI1Eef|OSC|p$ z_{~|XUT~&C(3aoBmb9y(FAYQ}Ssc&Nv#ulXajBQge8N*hiddnm+q9@fQalI9_C$%p z>XJW|+&}L5Ws{k`sb%@Lq-HTfX5ZUx&xM}T)_YrLFk^Bheq!iH@{1jKQM9#&lSMf( z!_Dz7o``ZgpLf5YQ8;jSRhcz83uqOze|(iqgXi+zM+JVfZu%REweS1yWYB3h);6i< z^Mi%l^O9{a8+^-;&oTWUiB^YPUT~F27V`J?@T5j3W-A{mElww0(_DmAcdHv*Uojy( z*m~O;k;-+z{0AnooO4Bcn?QmrjY8Ws&a7vge_*uOcGjW|gK4{)tPAlh@d(_*+2xu= z3Nfdu-UJeEy}~duJ4RAIO<`ErNKLshUFFIjt_}tq4#Mwh&6%!-F)X!F9d9f$5x;_n z0p_M=qq(rmsLG8|ZdBA4KtK#`cNrt)BPPySK3`pSIG&aCcsR1outJ}#!fxgJ=Aa7&oJ4Qo{^-F7qFH-h;~f6ytTaLcSyQZ6oqv0`K9*UNaQu=>DBg3*~d(Y zgS?)>k$Ir!9?wToV5h^BHccuiTgC3TAnq-0LgiNH5g5RtYw~PnTM&jbmb?prF4}{e zNw_SR9e#kTtQ{ z!MQKWXnh@&Ms?C*HSLJtNWm*S!+o;k$Q0jq(J9PWJ%juoFTg;e2~%+?3t<~e0IDQJ zo>cA=96K-jwydVxS7ys?NF<#x2%@H*FZ^DPpIOB5w!>}Z9CKr5x$5W^Fgtr_a1jin z_{W~BcX#X&yja6#N|8QYnsrjZ)=vCbjZ*B5lb|$n+e|hOvw2`dJaa)YfApAadYmfH zARGAj!)*RbbD7gGhXq?2+3nU{4B2cbdG331`|;;K<{xOi;_&>jGJ1LfQKmHXSqnsPaPWQ*00lGD z0G#2#&92oeE{jPS)5Qe<4euG7TjRwB6RkPunh%%Yx}-QVcKk^zlu0r=5i>T@$q0x5 z-rsmSJz}eO3hYSx>=4>p>vIp6?M>D>u|LJor;CoTXI+_hJIc}_1pmw#e7#=z$~>}8 za&{|7zc(0{_7Z<%+@%@glorw!Sndy>&`*cxQt|jJRx*&ESJLlKx#eOeET2cc`1i5j zr6HtTkhPSvZVL)Xa{WB>us$c~@hSWyNa^{jNrVl zepkvvX+?IW!G;3x86L+dyQ{TEdvP!s{Cvyb(LK|!X^?O`NkS@^Ue2mV)tkNstbh`x zA3v2XyAXf{E79X|h2;!dt$MFwsn;^#kunQ^s}eMvsowG%Dbs#VtsYU!%0PsqV!X=#zBvg%gZ%={j`@4WKyKo!l76j&v@h}JU(=&>aqtq2_ z8M1Olli|s!UnDfU2x-czDyb9R8ezo!R0D<*;xDfkipotMeQFYrrc|i>3yY&^wIsG+ zla`2dEe;@WH?=gSP~hh0 zq%y=3CB5NGltSr9B~xkZTT#xaBW^OM52}`2-wx{PZ=Z?w;kJAb%Tcr3V%*!v_o@Po z6>@D*dvsh4mqyGzZ>d^&Ztuyrb?+NI2?~-L6-b-^q%S(}gI z;*((0CzEw2@(BFCOH94ZP(CUrFB{@Wj(R8o2f)`6`1SO|5Sindkd!Q17UbQ>QHr#) zTn6f(54;$A#CKPIkDz`ojL4^E&j43`)+lbbv#%oOs6B?&4hVpXqx`aI_>8L%b!n#E zc`X_{9MB=pN^v(CIr`>7Rv2Tkqel7JH}YvhhtlzRR+1Hm?c29+`x;=q{r&$4_CRw_ z-^~Q_UiAQh>0s2s(OM}7phJ+bvCXN^kBx-`2LM2_gM|Di*)_K|?;IS+yFHrU(4vdV z?r`&Kx7rPpzCCBmkzzJaTMID6I;B2;dOR(y#D2`e1sfpFxiVjjVAm}ukCJ=y+|-Ea zUdFF~Ao`gR>HIglduWQQ3hEP%`WN@z&j9`@+@`;K8Z{hpD*6iDTsj&93aV(Ph|Z$+ z{D+3Bf;p{y+fZfuWw)SQzQ(la=x1%3w0g2258ih-ODTg7;&(BSIU*&TleY&t2@Gxq zXWl}*l6F+zeZ41DT$!B%bKSDHR%34?X*`I8x-lcr|U{{o9$_Vyi(Z zCUkiE(80_A36)ei;mBAKujZxOfd_^XtvQn$aYqB!Z$di4n{tW7jK2|XbaWmMxw-)i zqB~fFO&#A%@F&2iY3Z_v3bT}Gl)s!eEFa}QA;%ae=ZYiERPW>hlp*Z)_1nx++L6=6 zYX8SaH_b*He?XTI6%BZ8vBAe=NQitPqo5!;F^&xu1pE56fY4h~5*C=1_j~e`<|q}L ztyJIxKhO?oG0Q+iI)k-KRQH``HIoWJdF{ACue`+`V@)UX#cT76SEJ1B5AUWo$D|lH zfv9>phh^!>Kp1#S>a6rvJ(i*Hd|+AmS<^J#*|X}@K;%V*#(qAZo2+*&vxkhvgVckL!F zI#I{Gtsv_YyywSo`aIj?JFa^K7NczyBEk%+aWR`cVi7ov{&+u;9-I=yFukRgL|t^} z$g^EISO1Dxdt$(yFy6%rx;LLS-@=3CxE0}DzIdhuxe~!Cactu@H2!xx9C$IzG;Dxe=X{ppbogC$nM{J&<7@Bc>Q7}>(x7NQt4d{&z_om`H zNM<`i6q8LEABhEtkV#79Ls`ALa7%L#a4c=4Yd$^U5xDIm?)$J~iTah5io02E%jDFQ zV*M%gQ;PZvliPpwZd!}Y<4j+Zqmbl~VB4Yjw`u4iDNc5^Gi>QxuI=TzHtZ-#OtBZm zCC(KROK1Isgv60K`D)(I#K{XTYA}MYf8aa+mYvcaCd=GXl^aezneEN;;KPwW1f`K4 z-)Jl0I2rQ-qkV}v9|(2anq5SLrKSvK2*NpFV1jDtAn9oyQaAY(oI#fQCE_;Vwb_Vz z!bmy{8-LOZ zWk}A!_W7R7>yjTv=%`|9;Dgy_G~k-(>W z)lN4O>ziNr1=Zr`i#F#OC;0By6fkX6ruTHn2i3gVJ(YjsaAQC3kIaCH zprWDyEm6RIVBPTiUV02EEId5Cm9_QN)qS>OB0U(8tVtb&SJ`;1ta0scJf!A%&@OUv zD3_SpkB%|&xp6agUpm@Vd1K65COH|Dkz|x_r;E{+Qq@F_7nU=2FbC&i!y5kXB$`*K z%oO@)t2nuD8l9xq9GLPvHWKlbuf6rP2lfkelIo* z09{a)IzA*T>jy6{Uv*<`Y5mI@HL%lncSF=TI{azIk(KNV`usYw)Cx6IputBhhe!1G z?0eAk{!yJJ*c$N|*I)f%bha8o`uPL;KSK7}?l{{&1K$_w|5PeU{2#;KxBpYA7;OB_M}jA}XT!WC>Q^76#k!k;f^fnVFp4+>e6oY-p$)u%dxx)5Y$tTfP^^aUqa2E< zrg2M8a|o(Tx31ZzlhN1<>iPh-@dy_HHJ{7&Vue8p zAw+1h`2l?;Q7g8nVL3P*^q9$Xik1H39(=<(QY^YH z`Em*GSd8w+Ys}8})*jGf4qlz2YlOew&29}Gn zM6NH%7BXrIQ&k(vCXrrPq1!S@HZK8|&2Y{*jl0pmDyKRuZTCCaBHrx#Fz!`bOG;PL zYrEG*QdxOIYZh>{dE2fe>ct>{i@gOp1);I(^w)LWZwcJ&I>h`oDrPT}bcfo=#~*(w3|PDR6zy1BZ?c=0I0nzo*M~m;irvPYSS2aDO zeu8ot6kCa+u_*U)EKG82aV>n|ks2>{Uexf1`g9Wwwh5o|WV9}S&44y2thcF9U~RKxrJnj+mOGe&ymT~D1b6h{DW7`m|J7}}JJ(O<1aG}4Bl zpl(YPe~$i>@DO6ifR!(y&Zj z)48Rh>r&R{@y+gBgP#IGUc4=yKHVgiYBf7aYQy<;wLc!J@rIlL0ZFWf4d1WI*P5w# zDddiI!V-8CveM@<3NskLVXzrtiQH(sD1z=Ax)jm<)^M3H??1Q0r2WR;X% zm=Z|Q95zKmVzQ(>g2n9T-V6RN$?NxJ2(P&CeeQx7d*HtwgVvkbJ=&(-dcxNP^)ol3 zpm;(y+?_nAKOZVI!cx+x%{h-4ZF(Gdr(a->g|%LTm|XDtoQ~IvUZcR|e@1ZhE<`HA zPFb7kO?++VVaGjugBzvdZnTMM6q4=fAG8SpSHiIPIpSxl$YAaj|&$BohYicy;dU}Ps z^1!(j>$9bY>{F!tc!k$@8*+*r2Qc-g8z>X{*US{?hd;l>`Efk)D^KY-Kc3vsFFGZ{ z;8ospKMS@ptS{ucAMe?tGdyN)lZXXE0PLV%U`egd5cO-b59qd@BBvO(3n-&HN$w`E zNix6_rAQdj!U$6=9|oR$xX#Ea(zB1zbRbg@vkrzhbrp>0 z@i$D6zoX?kzL*q>;{VVx;wqgtX10fWG#vi++&!BXdiukbh{bFPk7h&VebpPet40M{|?{aG*xwI2@KZ5C&M$m0;^3zlFhRjuXu^3tMuf9w`2!4 zuC72v{d?V^YOInAa(C5(3MxI`miZS+Fb9}#Cu|AKkJg=UyhKdhv;|B_QLL(eM;UFr zEbrD_h80KmrmbJ7=wCcY+fqrm-%0O+@txdD_1$~h1KT&`y$;m+0n<|JB-eV-D(g?8 zm`A(g(BMqj3E?XGm%;}6dN)TFxMyBz;fkC#`TLjI!(TUd%@UHomMaL?AI>diAip5- zPa(5v0&KyaaZ{?Aws;W+;-qUmG3@Y?s#|rEk+Ip2mOp|#J=_V@j%1*Nm+|LpY z?B&4TnlHqZLEt``K;5`zGfOQfS-HG3ETl(wP=~|Xs)@gLzSP|xZj!sluOc@UB|pDg z+>1~_&R+@F+wIR0EE1{x4Np+EV2KZkNik=)T))zD-%VH`H_y$TOP=QGLhb%Ur*rgG zF-FP$o7pkHD6bq^58dmyyk?xui8mT*IP5qSPE?ra2vaF5`^U=Pn;R;jz>E^8DYd0V z+7e3Y*FbLthyKwKHoBT|5CxA?o{ZgBYEi+1sbd#mkOI)azO+^~FuBadxl;_WM5Ltg z5_!~33TLUDF5Rg^Mo)@kL3rZ4&^!XX1AFFA;g!aLE}ualri*!ZtX{3%{VRZhzvB&{ zK>Fn`kgSwaa^cEN#cVG-mj!y~K$T{ecem8-Nr$_~!(=rF<>%M?H?J9(krnFjjoEhe zPhNwGic1$mNJzv7DWXm?j5o*Y4L+)@iq0j+S2A=NXN4ov*HNiT9i9Ub+85(?q>~8p zZLqb0oA2{y_ghOdKHc8WL2%lW0Mo`2snW$Zx7=LEVu^5-9fp+Z6->=xoR1e&UT>(V zvRR?ow^gw=x$c8kPM*|+IeoU-n^&Ro3N#PXXAF7)Rn&OrpczOGjYSn49300roq9BL z2Gcy37P7<8x#b7*pTZx<6rYcnVcXut-CInp6+s?M4mVe{Q$Kc}`DJ8};)jr%Dn%&* zNvTK&$%NrNqt7=-7EaJsF{xRT<0DmlU%&-t59=LP5IuV4!el37uAJZ~WM69o;k&jW z=|R{0*WF@hB$7w0_xSB1<9x#A=#8O_P7HU_4VSQmg+)l)#ge#A+5HqxlTj z2tC&qRvHfa2jsRCdLc(%Na7n?Tn)Awz!BwwQcW{PH0@A1dk%K3S}ZTZB)s~$X2z#) z4bN`-m3`D7iTJ1o(yp&(ttr#FZq046{{-TD#j8{#&vO16C+#99CHxp+|Fy?;bNds+ zUhQbv=Qm@oh*LBZF2s=cy zYaIC+RE@h4l7F3NJ6t?e50;1YFd$b|`*yjH{kbQ+ynJnPUKP(Njj@t9ORmB{(0p67 z+dtoBXHd?^U^FtWHF5I#pP_3%yNSh83SeV3aahWgwHQIC!%>`67pQ=yvm z`ljUrmo~aQSYht{=lfM!aO*ScPTpcv*k&8RFYu&HU^qQ&;O}5GxebME<`y%u!oJ4` z;;avZ4%hVbjghr>NmH>Gf|Jco|N&d8tbORyf!A&OgUH>WFc0 z2KHiiv?9wkl(?{xim;n15P+KnBFi(o`^yYV#f2$;kRm=?vg!2|WNj=LC}5i}i@zhZ z;+>+>P$Wp=TP=!9`kR(AZ=B+Fgs)!tJSZqgo`K^lw9K~`U%mbShXf9%-;EudZ33dT73n1W z41eS+SIi8ewp!8+6LHNjcLo$Zn)5?@ME}Bn_hUIB4#;)!0y@Zt%uATIHZR!7RQW5<;!BiKo>;v+mS$#n zfFgB4;%1@w6_R4vLMbU)c%`s!0yr(Gh-p2Z@MQMLmX)F5e*XDI5}7(FNPDsyJ|{GY z_9V{s+8s=Sn$@BSR@C=qx_Zb)p{0CY7msuvn-!Vx1tWLd82)PB1Z{>&k>)btjEvtI{ANI3H8?3)T9-7i6T#~ zZxa6|)OQ(RioLyu>0Mo2M zUz70v$4vX5@Z0|#mEV=7v)@Yoh0{774CiaMz+91#PR~x|D=a@q68{bALJkfNtq%Lh zK>gPCYulHb|KJ96T;$X^ukPT8xoNWUU))z11q}1_|MZspU27pID>DZ=y|OE2XN?Z` za_1J_^A|0eu*Jp2As`^!az{TsKD{_z@7b%;eq2RkoB`y-ukU={SZw=-hLTqr4*%?( z0-7QXNJd7mEn=J-6dzPjKr#@5Aij74wE2!2nQVI0nQ2__Rhio&KfTbh<&3gU|K~NJ z7>nIe0IflxCUR(OHN8kGB|bjhOYIZ6Uq0pD!9l&X)aYuB|=su;nZ6(VoZMAwHcA`58)O9o}!B z21xj-{{nUttkX>C+^+PWKR*pE6%}zcTRlbS4}>(B?|J}E>v1@>-1_^2D>nK}J?Hdz zU%_5L40&w%3V5OX1qA`Oi^J`*0-#m!@bb7{4Pu1-2R%1We}BfFLZ>ymaJ58Qu>x=0o%jFQw=+AR4~EGK@~MLrPXPJJiZb zOOOK8hFWr&3WRh-A0L)RMRg~}zSLrgW+&WRh`@H7YRyS9&uqKS<5@ARuOE{a%@gE~Y5Xc9;#Pws$wW8TUhfP%h2zFL~HB4Us-t+t^ z40`#EjmIl>Q3L(Jtj_IXML|J<;|F*}n{Btmw7qce?-7UU!}D_&EM(q|G}u5rSA46# zubEV4$2|}U_(VlPncW+^$z>Na>-1EQicZ6Z-eoJdJWt> zVZ~j#27R7^cHecwHMWn=qz-)szATVlA0A(5H0blQ30Aoo{-$}v!v34N*%3GCDVs~l z1l{s6mELsnlj`$l%jd3V@K5-#f9CZnp1d|qU)?#PUF4z=m3@s03u1J_>@bDe7DvpM9`7&OM1YqG#^&q z?t%?hi#;nw6?f=6a(f^@d)EF7!RCG%e#X=5v_xZF$FjRVsT?mW@-%;OTsiY;gI4GP zDw*h&RwN>6m|v|8&)j@)&8B-;RLKtf%UVN1LLAAH)iq`M(%SHQDLfb{NL2rtihS}J zap3DITeDu1{{H+F&$AyLe-`E5by;BIpFz05_OJG*0xPM-km>SVjaU1}?QvdR&SZ=3 zfuIZ)Z%faV+ZdYMo|Yh$Zq6ygP1<_29(OxXw5J#7}?m@i~h$5A1H$IVitD zL>t7qq9=3;Y8+E){Bp?#B_pcvv(2s^s2-=>%i`&G)^W{T=kc>-XQEC>kdS29&#SJP z<-LYR1X?l$O6;tbRBz&eC!K5Xm-Gc7jMqE0ImNS5np+E|rsfLBzzV2F8sxmZcyMqC z)~k*CV@Wha_4fAmULS9+XwBWsQ}pzEx;4+6QXRF6LCYqLuAbP0d4%8Q3BSNqfNkov zjdpT?!EjCueZ;ic#ag{3IGL~d38%zt&zpSlkN!-%(KakEps;;*;y<>SMb=}*W}_CD zkzpOI23xec16uZCy)<-(%ZUf_F<)p1 z1!@W3seu2N-QPu0kB^Ul2Vn4*c9G?h4F@+jU|E*2xBs@*VmmM}kicfW>d=**tBQlo zW+maE81K#mMyJW^7mq(2Ce+IKy=)I4w)YiO9>eL=^@K2elLW-ZYi zuP**2A290p770xzr((ODx?TXtBE-u7~x8D`Z zU2JwTcA-#wyhfJk*)lWG1Bzr!`%}^ThoSFlcQnvqtmYm=CIFa3xw4`uo*#y%r;)&H zZ3&)lj{;S-0JG`Sgi4ZRa8*^+=+>6ENtIZ^c3$YJ&gS;&e~@1&L#cb2ldH!|k9M*_OE9+VW({mDQ? z^wdzJe2`Kq-J|ag*Eex+^;DGA#7s+xD|k%F&4^}BgW7UkW5p^FZq9#|tT=OI3ske@ zH};`r06`syGe_OtvM4Gk{hi4RbVmmR+%NygSIg>JH@~GAn7yB&BXK#AwY0Rl-|n?` ztx5jzgQe41R`wWZEA4~37PPatoa>sK4_==f4>U$vXL`4yXiQ~>4WyX1RSBm|Da}`+>I_L$Fk7kU zA|gue3S7O-Ks3qFwWz4Ls&2246yQRYu4>7E(bCZ!t+g5gO?*~7`Wib6_XQr&G#@NwL3>Qqq$w>T z;Pv@2-}N{3CUQ8UZ9Nev$XHRJ)fh7IszChDXu~z@?~dV0hV*uPawffx8n;++{hf!p zKYaJt-g($!^=lXk_hV6+t|hOoZw6HhjPEMQHRB6gtxFmFl=Ore><3oXxY^gocHIWJlEfnv4f`D6rb%9s9!;C;Fo< z9Ih?5&c4{ZqM60=_ZJKtw4{>vn*=9p> zelWsJiUvB(H(acW=sV?4z?gwdKrq}Xcc{!eI`TUd$yYPl?0Qn3%Qy|p< z4vKecRKgI8#0<;tEmNWhO05QEaR4s*ZaE}k{Bb9OG!4}5MkUqb}m+Q0*)7l0W7 z#NN67`eXl$5Q@>m#*xt`R|`a_(kP^U2^^Z9_@coQ?!8MNqKm? z?3^6>hgRFi(4@~3D=8CEF_h%x1uQIJi;5O_xa5Gzp%sXybRy(M4YM_t%dmM(nd)J0T<@nzqL>1KQ49I}<)ei8yOiWJBr)2lGy1cW{JoJq=`plxhZ#dDk40?4oiDF82l_)n)ja(0f=ODJ}nvn+f4N~2Qqi}rOAkOG%_+Wph-FU z@^UVcfFC?+sJ~N!Z1F6cbfP3Ewe(sv`@*!@2Hfdv5l@bS=B5(6!E7cP5#f}>`8&#Y z+QhTPL%dDzGb~y~xrP&=Grh+*6r<4)m#lUa^gSzSrPwd$?7^Ilj@MD+AisattKAN=|{jg`LL-%G*9RYfk|)lr^tv+?8}fQDp?bq3&G z#ldh&(`O^=j z!blE8R09r^(QKcXri14rl}>(FPO^^>BnW|PgXPqg*5@0eDLrl-A{`;JG7Bfvcv38i zGLYoA65s?HJWl4-Vn+IQT>eC)$+jl+H7Nbxhyq|`^Mu7+E;fT@)i^oIl&OK8z4#Kn z4oFI_B$qI!w-*dZGED)na^ODUiYl1R)Dr3H>K2k_KtV)_1y-g22O03*-($#n1bREW zyHS1e@zUH7w;pMqpzm*vD{5wQ1%>H#BUwg!1o!_+O@G(wPtg`o)6b7DZ2mR3M1bK* zBN+iVA}vc;()zl0^mjWVBkEUEtBAB*8FnWGM4gI5TXL-Vgp$U}_Q`HwVAW^Qr?$e9 zLit>}EycLXFYwm|-$}A@yLkP5bk!|UuNxF4q{ms$hZnfPd~MkU`sdjF+C^0h2X#q$ zDg_Jc9f*6k;IwEEibUN3-lw9ng{JUlvz1oFvLzQKkKI%<8lJ3qBOQrS)bD7$3*!Vx zrqDAO30cWP&(6Xr^nOWMI6k_vg=B+qdX+R=U(x7&`Zy}!!eXSh#ipwGv}${4zJrYi zkRspA17N)!Gebdr8!i~o(ciJ?g`rV623KEN73BNsO`w+~WqqEM3AX$~RaJib2;*l) z0bPBwUr;OL4KWxh1$jbIL+|)7#QWgJe1IK87;ruyM`95zQnWu`=LAkGuWxThvjr4u z%{KcZvm_(+k(yP-T1z%OSHDnz$6#E#1*WI8lnhYwMF*QV@KK0d*^)f3M=O*Az8Bn=aB4`b5>@F8e!PkeC3pAkyb~o=_nY_5X)~o^yd+7X zo{X1Ri%aJq$E}a4hIh;1Z!-ZF=n4VNdO;=pBu2?^pLTwR` zz1Tmf5JJQDnUgx6H6}W>uXQ-Xz%UO=R7@{(p#ABh&T85iEHfbEWuQio|48J1H;QSF%0=>y}>cfap%J5(O zEZbUN-vQO30h`zJ?qFwdg!&VPz;5k#Ac9*WnaW(Av6O=;i#< z7Ux5vrjTeczG(*-B!LfJ-?XJ#hAH?TwI1Gh`s81Q^(f5{(_Op~?@@G*Hd6qIeqyM9 zHfA^^=5P{eb2|_wfg^zmrOpxwX4>Uo1w+g3vck@$u?EN0JCRU@%~2H~P0h>tOMe)A zi;|qW-jC!IDLLHzi#O&-B3|n@`_bgOHLHN;2{zp0s^!5DMEaEw^UKCe#I0^3BK5Pp zh)B^-Jk)QVpQ(r`3HN7}zd{zm{q~fSC?X~5N|C9*>*i`>0*!7+ZH<>8(^1tz-V0mDWg(?naSF{DWhr4R0*=Y3z7D@BO{NBGF%NfGq%MN zjT*jmoiskFGqc2^oGsm6U1@rqH@Gy~#9fZ98aKHaK8nChl5ND{2q9vSqad?k;jT^} zX+@QBiwlV6)wWJ2lFQm@V8gG-|p4(=f_w?C5+mA!dIQ#pw zCv|)B0k6Y;W+2xtJx{>@{ivU>*!vn0#NDI6QjO`Lgk1H;ASaf;Te^fbx0y33)nhW9 zCY3r*?}Wz|Qs-U%sTrl0))ysMQbb1Z@n4Kp@P)8#bPx=saX4}pPEtLjsTCk0~q zj2V-F^G`)R_TLh;-I<`Ck3CIVyW48C{yn@REbR7lWv&?$42u;(i@kvIl_LH|DinGH zKTqvs_4L_>spmX`${OH8{B%OEN)`RsoaB7j+D)yGuDfxgp1$*f`Y=ua(ZIcpB>W&A(woXoDD4?5)*%T)pCd6B!-A@qH5%`0Suw?i zy~96T;jkWl7o2)}0@eyrblXUS#mn=?Yu(8{>2bB6u?HJ~|B%xy@Ag7%&{1nhH(Y;k z3?Wg*th@0CT@`v4sE2-Gxq&Sye;w7}`fMg6kB}y9SzYSD(p?S>L@$HIm5XNX$-f)w z+(Ct77uBXM5GCpe(}0kEfLni)r@L0&O0WX5x#D+SY$tqG3R`JN`Uj7#X?QF4i4v<4 zEa|w{Ki%2sPo%2RBmhddS{9$4pP4M?DNhW6QEsulC%?57)6)}r%>Yh@vI@K1&T8C5 zvUU%BlNV}dkSrQswtseX_SsIA%#M{er}qyOm_#aP(~T=>sJzgz@+Se+`C@ZBR^pfo zLHfx`ix#UWZMXO!Wu4%q@yYHW-DET5zlLYgv>Bjwr@m+0UOTb?p+E>z_VK%QNvz$U z(F}P91v%e+%rJOZl6&nLcW}#|_TSkg380*-1lLDN_QsOgVr1R}_8qr`9gX?NU9;=h zoXvlS%OCRiyj^h4??0ad4jUsk7nr)D; z9e|OdacTL=M)HCJ^x=8677QPrlp&t^#GRdiLaAh9DcDY49r;$5AKTM-mBUGgr@B|! zKTk0HTAwZ*u|V`j`z05IQQpz?ay_CsADxn_t143j8J+XyNM!%yMK7DcYUofrF z8JLvp7LxREh7V?vy+@Q!SMqRg%~N*28B3SxG_dxLvXbnbl7+~MG>SZMvZAiibP8dm zxPm_sOLhHr!ngiYHe`*r1lgxVwlCSqu=T> z2KlG-MGnuT^}JERs+zwv5JlK#dxZ0@;(SKH3hYy)Cu(?^!FuM|NPR{`Cd$$zDjB?V z%)}=>YMwsJQY~E|u3&it_;5PiPEL+x>R`F0eLrVO>^d$v4f)&IGHJdDEdaH3l2)s& z*#JL8JJa@Z`%_S8j(E+|)m}n@*-S1G7?4JY#C_rr71cyWsoAI~Q!cY}bga0PuV7t# zUIVxeX0!R9#iJ8-NGeM;BoN|vr-)KAh|_L|^&8VyS#0(K`hL{%IeC1dN5qaCvTPBP zz+CxwW4h+i`tD{2$MrUB4=>0VyF-l@b1LJ=0I>M0@8Rw!|#F zZ^G$&Vh`EfnB#XkHS9kwj-&hJ8UKi%tqzKg<}iM;_}y%eN1y$4vr0#|zS-8L)1tY= zK|vkOvVO!*+SqTNKxV7Sjzq7fD0$>xkr4Lx6SPP#YETc*jwlIWhYbw!|1NfQSfl;W zC^DU5cLXYCj7DOKCQ`5X20~*9$;m@(4}BwaeXZj~{d)LoI+=?4Nmv?v+SDrALlAHb z*fM^0e%03s)=-GxTF?pOax4^TV?3gW7CHQFJE6?QdMQrj-@lnvE!Q=kpJeIY?U@C8 zHo7J(;zhIp6(wpex$QQQ1iPRx%pOmWMGhK`a9g#6RmSoqKEocZfs{y!uMwtV5|E8! zy>%2R3~?HyS3)&fSi{8%x_=lgk(;3sjw8!>M(e*LjiXKO`d7r zQiGklAw-RLTO`b?7ikm9eK_*ex$fBU2HI9eP*~S6$f_eF63yIVD=(&4Ca%kVtio-d$TbO*#=C9cSA7>=pRfk4r zH5vl~&UC$O!6*Y8063C#G2q~*)$oTm3()+%T=H`vW7o#6NO{N#s_1&vRTze(L(NC9 ztM>>#?Uj>+0#(VNd04}6XRIx&^W)7va{rnQhI#v?IqxqwxbPm-`%kJuFD|bR;0B|j z)*CMo4e>4J4AMZ=-$j7D8cv=WK*%1=3@_p2O~uUDcN&qY@;^blBHR1?PwsCp?>8c) zli;wZ$f+MWabSXvDGB8-(3`iuv7sf#0u;ME#49YHO!gTBvMTqM*-~n9HD5Jrtr?)yJKSKPhCXl!`vMayf2x7vj(- z<)77sXHtMD9GoZ@memE-AQ;-GY-6UL*Wb60!o|A&6Rt-fjMl$Upva}rK4MYXRmp+u zm9ma17*kTs+=9uAn)&64Y3B&^*J`tYmh1HaH?M`Sz1YtN8+E?UX4970wo9Y)?;A6JE0C>zX_PL)%cJ-+44WV{wJO zo3l~vcY&ZNK5>&g{NPnYZnD2pPK((pFb*by%P}GR1G;2ulMx1G_Fu&^-w>Ff6wporhhRMY}I2I==hMU)cV_TUZTDEOz5XD>~6OAV7o;E;g=S1$VqAo;6YA8;2RK% zUp@@%sau_nkb&ZKS7R;aBE{f}2snRM2pE_$aSsn{9Cn*Oz3==qdUO(HebY-B0jExl z?PE2jyk_fN<$2%t5_D`fmamPl@Ud=jyH?pqyWdJ=wbo_0{ap0uLST=yk%u&BI4pVYsCnKB_(R@ z1!3~6@r45YR(R#FEeeU%VJ{5cF9CMLKXa-6BTSOoaDD?s^PvB+;R7%JU%k1+|81xb z|8Iqo`dKSwR!TO!g%0`O^}e)m=KuT)LTLXjcB+sD6#vsYmE!=+Dewb%VA1wp7ra$9 zm|mbe;~|}9C=2f&MpiQC#Y`4I%0oDysh+r8S4pMN$H=qiVoSCKH3nhcTYY5h^=lD$ z4wr72&DJr~9WV8b3X1B>Qe#A)ZC^f-o-e&g8I6Ya#XJCMXmmtT6GEwKCju_antA?5 zbKg#N3Bcz667YL)&^g3f-yIQA7A$ccI-w4F2x8T=6VD%hx8cz)(3b8BJH?<=D+aQQQNEc4Gn=4TtA=omsfUu3j-?+U`UJhJm@a>${^#Z+aH{h#g&4wLS3*JHdin zl&`Ti#qrtL!Q@3Yegc{9g-3?Ylf-e$C!)$!snWYwdE_eTqITYu&2aVMFweaQ*|Pm! zSEYKve65(3)mw*hMB>k{Sz*#c-8EdT!fvxw5!}w)Y7gS)VaOr?va! zvF~WkGEPMz=(Fr4IVZ}j2mpFVaEv!Lf7Lo@rSz`qSzd)nzRCvkEMr03crpGf#?-81 z(P49ILxP8&H>n{Dq1ZO>FfLpt5Z1}IOv ziBCB=60d8xbFkIlnsRDXz*Kd2dtY{SmQ^~ZDcq@w>`N#gQ|i^(hW*9&>~H$jsOEDL znjPWGFLwbo|J*%eB$GM-adsM-z<0{?q7x)0HYiiY?Q%MiAjdzjzM`i>*@G5Zd(4iI z`uT)GzLpQR1B%bBdlhk1Hmp#U5C`aBFB;0;7ijnQVel4+daii%dY7zsUNWV3`0YU+ z%{|h1;Y&(ukHs+Yh?igmYH9y4g)P7@>ubjn#g`m89HiD%{da7cRB5qm`btv1Ie|$oBCu585^^cehS8u|Ga)GD>~jt&55=7t0NGVTbNN%u}E@~p&eA!sf<9`Bf%WiJhWBu)0eb{6^}?U;5XpWy=wz_L|KK7JwGKq zR7En{AmH2&|M-qs>D;mwIwpbW-^tXBr{+p$v~7FHh#7?$C59|(ZL;VO-|z8I)V`x> zyVvhl+}#!HR0UozD?zr!{BOrx3fMI|atjNC$RaQp@1L$ZAFqwN->;R@d2xpdK81e2 z$f)6PJ+C|?YAGz+)qm5VXOr*b_wURhmiOS^VWLDWi5ARlzu)LhR0|;6(V13`9btgu zH`;Y@O?Uk(lz~oXwp<8j>x$ERe_?ug_Dr&VN}W~7K**{iniUJx+TOma*MQ#VoPm65 zJZ#L(y>@^$0!1Q*y39?L3;QX(o>rj)BuvRSTolv^>+Bwe!C8=GKEr}J7^0L4uECa& zl&+qaMMV*ZToBZiz%0*r)AP;R0atWCf9~vl+vK#<+w3&|PvT-4TNal}X~K=8{PTQq zb%%&#bJ2vl_Q(RVWWx{O0N}Zs4K>LW;j2F4D;BdI7{f1KrhI+2uHj4d10wnKTkIV< zlgar&YHaAky!cGev)|6MoU)<8AkqU4Bl@9((;-y!`72t{YErhX_jnLv%{R|``)1ur z9xJX;r`?;hny-5%p+XSWveb-+acQrTsC|Ns10pRV<60Ujo_=SX~0)9g9{>%>uukHg@CUw znE}IWH2LQ!s*!VJscO)9iD{8&i`eX7`qESGOZQ#Z*sw^$KTrNgcV@l?L}SLXKFi zVt|(B$;#Z7r5Spe8usvSFproMf$*PWb_v>6K92dnw=8pFAy;#22`fpK#3VErqJe~& zXGA5Bn%tvP!RflN*9LRtu?_D8h1^S_@jJxuOsDB@)Q&FMEmcrPCo^@x5*Ki1>(0^E zSe4%29wjuZv=sdnfLOVS@yFuPv(|F&g@k3(79ddK>NYK~303fV9mQybtJB_Uh*ySe zwhC^Llmig!=6R7=@@A)Xdu@aTy-ORxg>a#q_S?v9ns{Iq(_)XSw;(Yn()lsV621H& zes-(=z28A4_-6qP?FK%@4hVma^nnkf1!p1!33!TJHdu!|_Ck$rUX`tcAvSk!Swf0H zxn#EMWYn{28utpcju@tCsx8N&Hy=zsn!!mi zi^P~Nil_}8o zVzI`hr^eDqHe0{Z==QNh-o>}p&nlw*mv4O13NgK))DTmo$y*PA+{aRa8`Z;+y^5=7 zRzWk{m1DLUlh(z2p_S`l&6u^tj#D7UfrSCA+@cXn@utm?G$5w>qZbVpw-*)iiA<^2 z?W9OwU#gERex#ZuJ^i7F3`+W0qO^!7$4Ti4@2%>~4hQx%ulj2@wiY0R<-xVTyxtCKSd3;4`3Gs z$So_tuN)oiM~Eps(fzYl1YF8pxBQo_yEW(kj#~fQfb_pkP!AvU6c4GAMfrL?;xLw* znZ3ONr)P9)ej6ZW+)VRt%=y8{18(PL>mFu6-n%`T%Q4Ntvs~K? zA#FLIcs%9p@3ggLPyl9&JO`kXLPSD>Hyh|*cRpD}#=_D#nkfZ(TIU9Cs?V1d<>h;i z7b?s)iUCt&b3;`#k-|gWJBMlJ?qksy`wUQL*bC?N^ln*7V-`V4bvpq37kVs%7Twb9bzAfBF;%gFq=7j5j+uQ>Dql;*}I1_YZSuaC35ULTNM7w4R^4 z7rd|-y&$EMX~in&AaYA3wBEmh^$$$X_4xV<3F9~b1$Ao{V2Rz}JUbOX2Cl}@#w=OR zMiYcR{}>{rOr7qBCS}Zmkj*-=?sm+uL*@mK3iWp?Y7Ci*>RdJ*;-!;wPjm~g0A@<6 zW_R_T9`AHL0eXVG{BwWynY+7_>2WKy0<`mf5*30(*mHu4Rt$JLH^iS@{+2mf#W-5Nr1K4_8#dXL1EU;o{f->g|Z-}HFHYxZfTmE}rQNT4$pUWsg0zH>%% zvx+J>cpgVC2@fY0m_{-t-#*R3lvkPsYHX&iKtlJfdebY;v6>?!$UmWiCi1q-W{Z$3EK(r15EJODga`M$NaJy$iL(B;>{BpLB8K?pr_8bmO|v~Oc#nxUt|160GmNe?m3Ts2>GJXyOxk(km(DcVfVtY|~NohX}gFP1kQDX8$EO81`!dH$JeuL#T-HlIO4 z^$+_|pT{v-3Xx78ri(bd*>gI5qg_)f3*C22Hhpa?SR+n(etadmFOzn&;JM<0k^jbJ zM7^uYFI~HfXh5q21P59f07qz8uOGHZkoYpXmb;rP z=l#NlNYJe9I59P4veoUq*t{PIdlD}XudOPmD=T?( zN0hX*KQ=qtfb%<(@mPgf(-QfWS(D~e7Pl6#Rg@DDAUwjtY90jRna{^M|2%3X#qufn zCwZRf?2FRRWA>j@Rp~ReGIH&RZkdM(G0cw76hS4r8F`jc%{PX}DGg?71JS>NAb&G6 zwK}~MQZVUY;<=nCGg4UK)W}F0jr;*;Db>n24_y83aO;Lx`4+x1?wa4_o=@}dz5@N1 zWG+@)Lx3RN;63cJMh(_a(r*szpky-G_LmvQX%+l{QcSSlbGEnu#u71xNAYN4B{}LO z-u;f}sR+Cw(rCi)V~Aj-2PBjy{|99?P*ksT=<%DiyZJrh$&95t6Y~3~t$g9ybkQ2# zNm~WY2@@==uwsUXT5H|1fW0q1nHc`0RI$p@PU$QuOCfRxJ5Pa?oK*A{fDHBl*jSKF zr!&Rk30YWfLmte@e+@T0#PI)JX~00hVIyW|#{i%N5T8CRH<%(F9homSZ}M$MVlvjR z&)V&c=mW+unnac0m=+|!yPV1CxY`V>p^B_o!FA^#XuvrF@A`-*V zSwF+V>NXuUhOF2cT(@-N;}hICXyMsi*~1$}|G5rRQ!y<&fUj@Ka4C68>dg(36(bkG zkO)Y-hAgT+T}pZKGtD~OJG+4E0mWJip&GAro33GduM8Gg#XUi?pY+jOn5j2faC5Ch zoA8EwtDJ8*pT0?zdU0SRkKW0nOXWQK?2;K}Fn!P^V!I`xx`p9(erHd+@LrWErF^Tx1vCLH&oY@#XZ`pn;~;Gj0HRDmnx;m2PEgn}gkpUB@S9oH4tgGL8jE4&w8}+)5&?dvv8oc@zJ1l53QogXBOn218kSvdb zW5Ia)F!4|OM10^&)^Fky7Z6(Yj!$5IP+~JZUAGOdWYMCm8q!|KVuqB6`O{`_v{-+m zk$k?=nC8*N<4mIr+;=Tohe+etm1+-tj`cb=&CX~C)2J^Q{y%kHcU0497EJ^Zq$yRY zmH^5SL_xYJMKFL=si6&0B|)kI=^$+YOK2j+Wk6{rlt_qzfG8*ik*Z)6l@20OBp_1e zWp~e<-Lq%^%Sm#QU-^FT-S@qF-@WY%t_mXe*xh8cn27#Q+wp06?VcY6{}ma-VMG6t zQt;#pl#KI4!>8zynwlkyG9k^dq}b?&)U_m4x2+}(fDRw1Ty^6eYiIp@MNZvVboHlp z7og3pE-frE^%CPg%;AiA)QS>IEVM>(a@H31xG2f&LP3`ish4LTfvfAt!U|noz46dY zz0gdx=aiTnLEBkUDMyHyXHCtz372>C?9Aw><}+%I_O4xW2;+G@x8T6gvQpYKc@7g^ z23%T--zwkxhrVz~r<9;yWGdU`rR+udN%qJkqJH=GP6nGc{=uje-Gp6ZiAm* zasP@Y&;38DFQ3S=u(v;FDSTZNVqcj+o|o`Zq1|F?zQ9en?B?z5&6`6w>=S*Fz4nT`7u#XVV%CKo*;PIb7 z!%--d6JgT|aQ0e6$}pt?ljA8MCS6@!o%|}a zk6?-!nzQXzMTv^!_lh}+a-x$`7X86fy<$Q{_IYadk^5pqn zr_5@Y?BYxp+KjF1M8!w8@~N_e+Ou`|Y*-j<=1%=hTo~d$l`thh#oho+3)BNQq(zmG zeSQ5yv_-q_A`NwI;7(Tz5+rEd+(=ZuZ(NX_VZe}DBxb%u!D z=g*(dojiT|LM21`q+0%KmU+d{&_0*QzOr5%njyCG8(e|94ya} za?VYv2w3$Y>4sxAdz(WKR^xY~Djpi>Ssk%-ybHySHjRbklgs3g$heJ-3wS6w6FP$MZBltjqL5Yf$!YenJH%}pA9iD)&FDTt?X=FQ&R;PUJvz< zzMlII;K}G$4O&&ZaOx-Vl$`&nDgoo9!Tat&HpjhG7`Hw8oQqGgj@|i*%jIv}v1Vms zty^r`D+4~vt=g%_R}nBdvLQ4$M3g|xvJUJZUEXABFAi|d{%o|)Dr}wSts0J2Aqtep zr13shs@Y z<1giUhK9e)&836fLRZ}wwqy)h-Pl>f6!om(QH*i$;S9Mby&sHd&)w0g* z-QCxLjm41X4pqY6Sqqw9<@KRwl*<>0E3It43q1C?D^6d|a{c63BEfsv-X8BKeq%P& zN61lBCRXPzZTehanrBzLgTYzn1M;%Sgnjp5k^xfY#B1Y$YS+6%x9u6D`5?10%0VBs z4G2(@%}h;5hQJx0lESTmf6rsi&kyblFFQMnVPOGa6WF`gOjj2P%~fi$jRU+wC@QKI z6^!-KWL8x6N3I!31>o%m5BA(R)!(s9-%tPUviw!b>sa2|?=I-InIRdniBy_mt^bU% zgp`y)xe&chI=b~hlJ8F4u-H^nion(uQb>p```3 zL&wF^RgKPIQe`3reN^`Ga5QgT(Xk^t6+@G|X?@l&egCpVaWOCMdgbG~gAQ5|5&2?i zHIo@i_g@5eOqxmSw;iVzVrq4cbw7R?N0)8d++mT5(SfJzP(Ms8^x|Ohsqtw}WJqoD zxIH>GF*|7R_JIp8EG?r7tDMW1Y*e+VxSi4oPg|3Czgiv>^fgpN>Y;bUJXXoqUnwlOlB_!No^_VSfu+%wJZi~JkFMO%~P7epKPyc=1UCi%B zRf9k|d8+(I$Asd=Uvj~5NyE<04s0{bMS%?gy@4 z^Cb2b9wvM4j$ioP7;dm8+88o(2xwl8RNT`q&x~+YRpYB9Ea&ds?muD-v22wn3~+7h zBHs8TtgNh3XnS<;>O@VyC5bzdLes`@?Sx^L#yXdfuRw9ThU?*pl$&^2#_xgL2MT zrz7_+`JgMiIi%<_tQn)=7R!n6iJdAErvvsQH3T7mppaQqBJ$D>)7)LRoWV+A1rm%nisEP?NgJo!}<(#elwPzR}Eu~#KKjz?TFzn?B; z=CS^9D8gEg=<)pHQMlUWS&E8eWgIs>IH0EMxxypvxYzB2g~ZW6V}3~8!3p+E4DPt_1q_sV~!zGg~e_#0?YS|Mu+J(^{ZG(Fj>MlgNdc7;bF5 z75ekQ3-cFgHW93C#vQkON0RQGOn@YR1jv=N#t6u{ZiS0it7lfOLNVTFZ4KrNDp)E` z&u-}rgKEmk+K1vNT@PT0D~p7qbXiXavI>PYF>zku_}=Mbd*a|B`SJ;);17TXWNQDx z;Rx1vHh@4NPGSsnFTh9t#pe)=BWBM3`11?v{eaaJ(|?Yw_#0;Zxs4na*Q1vw5%kJfPj literal 0 HcmV?d00001 diff --git a/.img/internal-api.png b/.img/internal-api.png new file mode 100644 index 0000000000000000000000000000000000000000..32ab3b5f10805dab3b81956b30707f7a35b7ef76 GIT binary patch literal 79652 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 literal 0 HcmV?d00001 diff --git a/.img/microservices.png b/.img/microservices.png new file mode 100644 index 0000000000000000000000000000000000000000..7db5bf50c39ae7904a1742029f2b8c319f2a5323 GIT binary patch literal 61731 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

^%t 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 M+$C(OxyMic0p-P~U;qFB literal 0 HcmV?d00001 diff --git a/image.png b/image.png deleted file mode 100644 index 318bfe55ad6e7a67837388be4804ae6042226a32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 103532 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 HHT3;|h-4L= diff --git a/readme.md b/readme.md index 5a2a350..11a93aa 100644 --- a/readme.md +++ b/readme.md @@ -4,19 +4,41 @@ ![Static Badge](https://img.shields.io/badge/Spring_Boot-3.5.9-green) ![Static Badge](https://img.shields.io/badge/Lombok-red) ![Static Badge](https://img.shields.io/badge/JPA--specification-8A2BE2) -![Static Badge](https://img.shields.io/badge/RestTemplate-538681) +![Static Badge](https://img.shields.io/badge/SpringCloud-538681) ![Static Badge](https://img.shields.io/badge/PostgreSQL-16.1-blue) ![Static Badge](https://img.shields.io/badge/docker_compose-blue) -![Static Badge](https://img.shields.io/badge/H2_database-blue) -![Static Badge](https://img.shields.io/badge/JUnit-5-orange) -![Static Badge](https://img.shields.io/badge/Mockito-green) -![Static Badge](https://img.shields.io/badge/Jacoco-red) ![Static Badge](https://img.shields.io/badge/Maven-orange) -![Static Badge](https://img.shields.io/badge/Multi--module--project-8A2BE2) +![Static Badge](https://img.shields.io/badge/Microservices-8A2BE2) ## Бэкэнд для сервиса поиска мероприятий -![](image.png) + +### Configuration +configuration files location: infra/config-repo + +### Microservice map +![ms](.img/microservices.png) + +### External API +![external-api.png](.img/external-api.png) +![external-api-2.png](.img/external-api-2.png) + +### Internal API +![internal-api.png](.img/internal-api.png) ### Database map -![db](db-schema.png) \ No newline at end of file + +ewm-main/event-service +![ewm-main-event-service.png](.img/ewm-main-event-service.png) + +ewm-main/rating-service +![ewm-main-rating-service.png](.img/ewm-main-rating-service.png) + +ewm-main/request-service +![ewm-main-request-service.png](.img/ewm-main-request-service.png) + +ewm-main/user-service +![ewm-main-user-service.png](.img/ewm-main-user-service.png) + +ewm-stats/stats-server +![ewm-stats-stats-server.png](.img/ewm-stats-stats-server.png) \ No newline at end of file