├── .github
└── workflows
│ ├── api-tests.yml
│ └── wait-for-it.sh
├── .gitignore
├── README.md
├── checkstyle.xml
├── docker-compose.yml
├── ewm-main-service-spec.json
├── ewm-service
├── Dockerfile
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── ru
│ │ └── practicum
│ │ ├── ExploreWithMe.java
│ │ ├── category
│ │ ├── controller
│ │ │ ├── CategoryAdminController.java
│ │ │ └── CategoryPublicController.java
│ │ ├── data
│ │ │ ├── Category.java
│ │ │ ├── CategoryRepository.java
│ │ │ └── dto
│ │ │ │ ├── CategoryDto.java
│ │ │ │ ├── CategoryMapper.java
│ │ │ │ └── NewCategoryDto.java
│ │ └── service
│ │ │ ├── CategoryService.java
│ │ │ └── CategoryServiceImpl.java
│ │ ├── comment
│ │ ├── controller
│ │ │ ├── CommentAdminController.java
│ │ │ ├── CommentPrivateController.java
│ │ │ └── CommentPublicController.java
│ │ ├── data
│ │ │ ├── Comment.java
│ │ │ ├── CommentMapper.java
│ │ │ ├── CommentRepository.java
│ │ │ └── dto
│ │ │ │ ├── CommentDto.java
│ │ │ │ ├── CommentPatchDto.java
│ │ │ │ └── NewCommentDto.java
│ │ └── service
│ │ │ ├── CommentService.java
│ │ │ └── CommentServiceImpl.java
│ │ ├── compilation
│ │ ├── controller
│ │ │ ├── CompilationAdminController.java
│ │ │ └── CompilationPublicController.java
│ │ ├── data
│ │ │ ├── Compilation.java
│ │ │ ├── CompilationDto.java
│ │ │ ├── CompilationMapper.java
│ │ │ ├── CompilationRepository.java
│ │ │ └── dto
│ │ │ │ ├── NewCompilationDto.java
│ │ │ │ └── UpdateCompilationRequest.java
│ │ └── service
│ │ │ ├── CompilationService.java
│ │ │ └── CompilationServiceImpl.java
│ │ ├── configuration
│ │ └── BeanConfiguration.java
│ │ ├── event
│ │ ├── controller
│ │ │ ├── AdminEventController.java
│ │ │ ├── PrivateEventController.java
│ │ │ └── PublicEventController.java
│ │ ├── data
│ │ │ ├── AdminEventStateAction.java
│ │ │ ├── Event.java
│ │ │ ├── EventRepository.java
│ │ │ ├── EventState.java
│ │ │ ├── Location.java
│ │ │ ├── UserEventStateAction.java
│ │ │ └── dto
│ │ │ │ ├── EventFullDto.java
│ │ │ │ ├── EventMapper.java
│ │ │ │ ├── EventRequestStatusUpdateRequest.java
│ │ │ │ ├── EventRequestStatusUpdateResult.java
│ │ │ │ ├── EventShortDto.java
│ │ │ │ ├── NewEventDto.java
│ │ │ │ ├── SearchParameters.java
│ │ │ │ ├── SortProperty.java
│ │ │ │ ├── UpdateEventAdminRequest.java
│ │ │ │ ├── UpdateEventRequest.java
│ │ │ │ └── UpdateEventUserRequest.java
│ │ └── service
│ │ │ ├── EventService.java
│ │ │ └── EventServiceImpl.java
│ │ ├── exception
│ │ ├── ApiError.java
│ │ ├── ChangeEventStatusForbiddenException.java
│ │ ├── EventParticipationForbiddenException.java
│ │ ├── RequestNotValidException.java
│ │ ├── RequestStatusNotPendingException.java
│ │ └── controller
│ │ │ └── ExceptionController.java
│ │ ├── request
│ │ ├── controller
│ │ │ └── PrivateRequestController.java
│ │ ├── data
│ │ │ ├── Request.java
│ │ │ ├── RequestRepository.java
│ │ │ ├── RequestStatus.java
│ │ │ └── dto
│ │ │ │ ├── ParticipationRequestDto.java
│ │ │ │ └── RequestMapper.java
│ │ └── service
│ │ │ ├── RequestService.java
│ │ │ └── RequestServiceImpl.java
│ │ ├── user
│ │ ├── controller
│ │ │ └── UserAdminController.java
│ │ ├── data
│ │ │ ├── User.java
│ │ │ ├── UserRepository.java
│ │ │ └── dto
│ │ │ │ ├── UserDto.java
│ │ │ │ ├── UserMapper.java
│ │ │ │ └── UserShortDto.java
│ │ └── service
│ │ │ ├── UserService.java
│ │ │ └── UserServiceImpl.java
│ │ └── validation
│ │ ├── DateTimeConsistency.java
│ │ ├── DateTimeConsistencyValidator.java
│ │ ├── InFuture.java
│ │ └── InFutureValidator.java
│ └── resources
│ ├── application.properties
│ └── schema.sql
├── ewm-stats-service-spec.json
├── lombok.config
├── pom.xml
├── postman
└── feature.json
├── stat
├── pom.xml
├── stat-client
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── ru
│ │ └── practicum
│ │ └── StatClient.java
├── stat-dto
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── ru
│ │ └── practicum
│ │ └── dto
│ │ ├── EndpointHitDto.java
│ │ └── ViewStats.java
└── stat-server
│ ├── Dockerfile
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ └── ru
│ │ └── practicum
│ │ ├── StatApplication.java
│ │ ├── controller
│ │ ├── ExceptionController.java
│ │ └── StatController.java
│ │ ├── entity
│ │ └── EndpointHit.java
│ │ ├── exception
│ │ └── DatesNotConsistentException.java
│ │ ├── mapper
│ │ └── EndpointHitMapper.java
│ │ ├── repository
│ │ └── StatRepository.java
│ │ └── service
│ │ ├── StatService.java
│ │ └── StatServiceImpl.java
│ └── resources
│ ├── application.properties
│ └── schema.sql
└── suppressions.xml
/.github/workflows/api-tests.yml:
--------------------------------------------------------------------------------
1 | name: Explore With Me API Tests
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | build:
8 | uses: yandex-praktikum/java-explore-with-me/.github/workflows/api-tests.yml@ci
--------------------------------------------------------------------------------
/.github/workflows/wait-for-it.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Use this script to test if a given TCP host/port are available
3 |
4 | WAITFORIT_cmdname=${0##*/}
5 |
6 | echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
7 |
8 | usage()
9 | {
10 | cat << USAGE >&2
11 | Usage:
12 | $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
13 | -h HOST | --host=HOST Host or IP under test
14 | -p PORT | --port=PORT TCP port under test
15 | Alternatively, you specify the host and port as host:port
16 | -s | --strict Only execute subcommand if the test succeeds
17 | -q | --quiet Don't output any status messages
18 | -t TIMEOUT | --timeout=TIMEOUT
19 | Timeout in seconds, zero for no timeout
20 | -- COMMAND ARGS Execute command with args after the test finishes
21 | USAGE
22 | exit 1
23 | }
24 |
25 | wait_for()
26 | {
27 | if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
28 | echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
29 | else
30 | echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
31 | fi
32 | WAITFORIT_start_ts=$(date +%s)
33 | while :
34 | do
35 | if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
36 | nc -z $WAITFORIT_HOST $WAITFORIT_PORT
37 | WAITFORIT_result=$?
38 | else
39 | # (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
40 | (curl --fail --silent $WAITFORIT_HOST:$WAITFORIT_PORT/actuator/health | grep UP) >/dev/null 2>&1
41 | WAITFORIT_result=$?
42 | fi
43 | if [[ $WAITFORIT_result -eq 0 ]]; then
44 | WAITFORIT_end_ts=$(date +%s)
45 | echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
46 | break
47 | fi
48 | sleep 1
49 | done
50 | return $WAITFORIT_result
51 | }
52 |
53 | wait_for_wrapper()
54 | {
55 | # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
56 | if [[ $WAITFORIT_QUIET -eq 1 ]]; then
57 | timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
58 | else
59 | timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
60 | fi
61 | WAITFORIT_PID=$!
62 | trap "kill -INT -$WAITFORIT_PID" INT
63 | wait $WAITFORIT_PID
64 | WAITFORIT_RESULT=$?
65 | if [[ $WAITFORIT_RESULT -ne 0 ]]; then
66 | echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
67 | fi
68 | return $WAITFORIT_RESULT
69 | }
70 |
71 | # process arguments
72 | while [[ $# -gt 0 ]]
73 | do
74 | case "$1" in
75 | *:* )
76 | WAITFORIT_hostport=(${1//:/ })
77 | WAITFORIT_HOST=${WAITFORIT_hostport[0]}
78 | WAITFORIT_PORT=${WAITFORIT_hostport[1]}
79 | shift 1
80 | ;;
81 | --child)
82 | WAITFORIT_CHILD=1
83 | shift 1
84 | ;;
85 | -q | --quiet)
86 | WAITFORIT_QUIET=1
87 | shift 1
88 | ;;
89 | -s | --strict)
90 | WAITFORIT_STRICT=1
91 | shift 1
92 | ;;
93 | -h)
94 | WAITFORIT_HOST="$2"
95 | if [[ $WAITFORIT_HOST == "" ]]; then break; fi
96 | shift 2
97 | ;;
98 | --host=*)
99 | WAITFORIT_HOST="${1#*=}"
100 | shift 1
101 | ;;
102 | -p)
103 | WAITFORIT_PORT="$2"
104 | if [[ $WAITFORIT_PORT == "" ]]; then break; fi
105 | shift 2
106 | ;;
107 | --port=*)
108 | WAITFORIT_PORT="${1#*=}"
109 | shift 1
110 | ;;
111 | -t)
112 | WAITFORIT_TIMEOUT="$2"
113 | if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
114 | shift 2
115 | ;;
116 | --timeout=*)
117 | WAITFORIT_TIMEOUT="${1#*=}"
118 | shift 1
119 | ;;
120 | --)
121 | shift
122 | WAITFORIT_CLI=("$@")
123 | break
124 | ;;
125 | --help)
126 | usage
127 | ;;
128 | *)
129 | echoerr "Unknown argument: $1"
130 | usage
131 | ;;
132 | esac
133 | done
134 |
135 | if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
136 | echoerr "Error: you need to provide a host and port to test."
137 | usage
138 | fi
139 |
140 | WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
141 | WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
142 | WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
143 | WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
144 |
145 | # Check to see if timeout is from busybox?
146 | WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
147 | WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
148 |
149 | WAITFORIT_BUSYTIMEFLAG=""
150 | if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
151 | WAITFORIT_ISBUSY=1
152 | # Check if busybox timeout uses -t flag
153 | # (recent Alpine versions don't support -t anymore)
154 | if timeout &>/dev/stdout | grep -q -e '-t '; then
155 | WAITFORIT_BUSYTIMEFLAG="-t"
156 | fi
157 | else
158 | WAITFORIT_ISBUSY=0
159 | fi
160 |
161 | if [[ $WAITFORIT_CHILD -gt 0 ]]; then
162 | wait_for
163 | WAITFORIT_RESULT=$?
164 | exit $WAITFORIT_RESULT
165 | else
166 | if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
167 | wait_for_wrapper
168 | WAITFORIT_RESULT=$?
169 | else
170 | wait_for
171 | WAITFORIT_RESULT=$?
172 | fi
173 | fi
174 |
175 | if [[ $WAITFORIT_CLI != "" ]]; then
176 | if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
177 | echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
178 | exit $WAITFORIT_RESULT
179 | fi
180 | exec "${WAITFORIT_CLI[@]}"
181 | else
182 | exit $WAITFORIT_RESULT
183 | fi
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | checkstyle.xml
3 | target/
4 | !.mvn/wrapper/maven-wrapper.jar
5 | !**/src/main/**/target/
6 | !**/src/test/**/target/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 |
17 | ### IntelliJ IDEA ###
18 | .idea
19 | *.iws
20 | *.iml
21 | *.ipr
22 |
23 | ### NetBeans ###
24 | /nbproject/private/
25 | /nbbuild/
26 | /dist/
27 | /nbdist/
28 | /.nb-gradle/
29 | build/
30 | !**/src/main/**/build/
31 | !**/src/test/**/build/
32 |
33 | ### VS Code ###
34 | .vscode/
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## ExploreWithMe - сервис для участия в мероприятиях
2 |
3 | - Основная идея проекта - помогать пользователям находить интересные мероприятия и делиться ими с друзьями
4 | - Приложение содержит два сервиса:
5 | * Основной сервис - предназначен для работы с событиями и их участниками.
6 | * Сервис статистики - хранит информацию об обращениях к основному сервису. Позволяет формировать статистику для основного сервиса.
7 |
8 | ## Дополнительная функциональность
9 |
10 | Комментарии к событиям
11 | - позволяет оставлять и модерировать комментарии к событиям,
12 | - позволяет ставить лайки и дизлайки событию,
13 | - позволяет получать количество комментариев у событий для понимания их популярности.
14 |
15 | - https://github.com/Iregor/java-explore-with-me/pull/9
16 |
17 | ## Технологический стек
18 |
19 | Проект ExploreWithMe разработан с использованием следующих технологий и инструментов:
20 |
21 | - Java 11
22 | - Spring Boot
23 | - Hibernate ORM, Query DSL - маппинг данных
24 | - Apache Maven - управление зависимостями и сборка приложения.
25 | - PostgresSQL, H2 - реляционные базы данных
26 | - Docker, Docker Compose - контейнеризация сервисов приложения.
27 |
28 | ## Микросервисная архитектура
29 |
30 | Приложение состоит из 2 основных модулей:
31 | - Stats - сервер хранения обращений к приложению и формирования статистики
32 | - Service - сервер обработки запросов к приложению
33 |
34 | ## Установка и запуск проекта
35 |
36 | ### 1 вариант:
37 | Необходимо настроенная система виртуализации, установленный Docker Desktop(скачать и установить можно с официального сайта https://www.docker.com/products/docker-desktop/)
38 |
39 | 1. Клонируйте репозиторий проекта на свою локальную машину.
40 | 2. Запустите командную строку и перейдите в корень директории с проектом.
41 | 3. Введите следующую команду, которая подготовит и запустит приложение на вашей локальной машине
42 | ```
43 | $ docker-compose up
44 | ```
45 | 4. Приложение будет запущено на порту 8080 и готово принимать http-запросы.
46 | 5. Список доступных эндпоинтов предоставлен ниже
47 |
48 |
49 | ### 2 вариант:
50 |
51 | 1. Установите Java Development Kit (JDK) версии 11 или выше, если у вас его еще нет.
52 | 2. Установите PostgreSQL и создайте базу данных для проекта.
53 | 3. Клонируйте репозиторий проекта на свою локальную машину.
54 | 4. Настройте файл `application.properties`, расположенный в директории `src/main/resources`, чтобы указать данные для подключения к вашей базе данных PostgreSQL.
55 | 5. Запустите приложение, выполнив следующую команду в корневой директории проекта:
56 | ```
57 | mvn spring-boot:run
58 | ```
59 | 6. Приложение будет запущено на порту 8080 и готово принимать http-запросы.
60 |
61 | Эндпоинты
62 | ---
63 | - GET /compilations - Получение подборок событий
64 | - GET /compilations/{compId} - Получение подборки событий по его id
65 | ---
66 | - POST /admin/categories Добавление новой категории
67 | - DELETE /admin/categories/{catId} Удаление категории
68 | - GET /admin/categories/{catId} Получение списка бронирований для всех вещей текущего пользователя.
69 | ---
70 | - GET /users/{userId}/events - Получение событий, добавленных текущим пользователем
71 | - POST /users/{userId}/events - Добавление нового события
72 | - GET /users/{userId}/events/{eventId} - Получение полной информации о событии добавленном текущим пользователем
73 | - PATCH /users/{userId}/events/{eventId} - Изменение события добавленного текущим пользователем
74 | - GET /users/{userId}/events/{eventId}/requests - Получение информации о запросах на участие в событии текущего пользователя
75 | - PATCH /users/{userId}/events/{eventId}/requests - Изменение статуса (подтверждена, отменена) заявок на участие в событии текущего пользователя
76 | ---
77 | - GET /categories - Получение категорий
78 | - GET /categories/{catId} - Получение информации о категории по её идентификатору
79 | ---
80 | - GET /admin/events - Поиск событий
81 | - PATCH /admin/events/{eventId} - Редактирование данных события и его статуса (отклонение/публикация).
82 | ---
83 | - GET /events/{id} - Получение подробной информации об опубликованном событии по его идентификатору
84 | - GET /events/{id} - Получение подробной информации об опубликованном событии по его идентификатору
85 | ---
86 | - GET /users/{userId}/requests - Получение информации о заявках текущего пользователя на участие в чужих событиях
87 | - POST /users/{userId}/requests - Добавление запроса от текущего пользователя на участие в событии
88 | - DELETE /users/{userId}/requests/{requestId}/cancel - Отмена своего запроса на участие в событии
89 | ---
90 | - GET /admin/users - Получение информации о пользователях
91 | - POST /admin/users - Добавление нового пользователя
92 | - DELETE /admin/users/{userId} - Удаление пользователя
93 | ---
94 | - POST /admin/compilations - Добавление новой подборки (подборка может не содержать событий)
95 | - DELETE /admin/compilations/{compId} - Удаление подборки
96 | - PATCH /admin/compilations/{compId} - Обновить информацию о подборке
97 | ---
98 | - GET /stats - Получение статистики по посещениям
99 | - POST /hit - Сохранение информации о том, что на uri конкретного сервиса был отправлен запрос пользователем.
100 | ---
101 | - DELETE /comments/{commentId} - удаление комменатрия администратором
102 | - POST /users/{userId}/events/{eventId}/comments - добавление комментария пользователем событию
103 | - PATCH /users/{userId}/events/{eventId}/comments - редактирование пользователем собственного комментария
104 | - DELETE /users/{userId}/events/{eventId}/comments/{commentId} - удаление комментария пользователем
105 | - GET /comments - получение пользователем списка собственных комментариев
106 | - GET /events/{eventId}/comments/{commentId} - получение комментария по его id
107 | - GET /events/{eventId}/comments - получение списка комментариев события
108 | ---
109 |
--------------------------------------------------------------------------------
/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.1'
2 | services:
3 | stats-server:
4 | build: stat/stat-server
5 | container_name: stats-server-container
6 | ports:
7 | - "9090:9090"
8 | depends_on:
9 | - stat-db
10 | environment:
11 | - SPRING_DATASOURCE_URL=jdbc:postgresql://stat-db:5432/stat-db
12 | - SPRING_DATASOURCE_USERNAME=user
13 | - SPRING-DATASOURCE_PASSWORD=password
14 |
15 | stat-db:
16 | image: postgres:15-alpine
17 | container_name: stat-db-container
18 | ports:
19 | - "6542:5432"
20 | environment:
21 | - POSTGRES_DB=stat-db
22 | - POSTGRES_USER=user
23 | - POSTGRES_PASSWORD=password
24 |
25 | ewm-service:
26 | build: ewm-service
27 | container_name: ewm-service-container
28 | ports:
29 | - "8080:8080"
30 | depends_on:
31 | - ewm-db
32 | - stats-server
33 | environment:
34 | - STAT_URL=http://stats-server:9090
35 | - SPRING_DATASOURCE_URL=jdbc:postgresql://ewm-db:5432/ewm-db
36 | - SPRING_DATASOURCE_USERNAME=user
37 | - SPRING-DATASOURCE_PASSWORD=password
38 |
39 | ewm-db:
40 | image: postgres:15-alpine
41 | container_name: emw-db-container
42 | ports:
43 | - "6541:5432"
44 | environment:
45 | - POSTGRES_DB=ewm-db
46 | - POSTGRES_USER=user
47 | - POSTGRES_PASSWORD=password
--------------------------------------------------------------------------------
/ewm-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM amazoncorretto:11-alpine-jdk
2 | COPY target/*.jar ewm-service.jar
3 | ENTRYPOINT ["java","-jar","/ewm-service.jar"]
--------------------------------------------------------------------------------
/ewm-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | explore-with-me
7 | ru.practicum
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | service
13 |
14 |
15 | 11
16 | 11
17 | UTF-8
18 |
19 |
20 |
21 |
22 | org.springframework.boot
23 | spring-boot-starter-web
24 |
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-data-jpa
29 |
30 |
31 |
32 | com.querydsl
33 | querydsl-apt
34 |
35 |
36 | com.querydsl
37 | querydsl-jpa
38 |
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-validation
43 |
44 |
45 |
46 | org.springframework.boot
47 | spring-boot-starter-actuator
48 |
49 |
50 |
51 | com.h2database
52 | h2
53 | runtime
54 |
55 |
56 |
57 | org.postgresql
58 | postgresql
59 |
60 |
61 |
62 | ru.practicum
63 | stat-client
64 | 0.0.1-SNAPSHOT
65 | compile
66 |
67 |
68 |
69 |
70 |
71 |
72 | org.springframework.boot
73 | spring-boot-maven-plugin
74 |
75 |
76 | com.mysema.maven
77 | apt-maven-plugin
78 | 1.1.3
79 |
80 |
81 |
82 | process
83 |
84 |
85 | target/generated-sources
86 | com.querydsl.apt.jpa.JPAAnnotationProcessor
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/ExploreWithMe.java:
--------------------------------------------------------------------------------
1 | package ru.practicum;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class ExploreWithMe {
8 | public static void main(String[] args) {
9 | SpringApplication.run(ExploreWithMe.class, args);
10 | }
11 | }
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/category/controller/CategoryAdminController.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.category.controller;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.web.bind.annotation.*;
7 | import ru.practicum.category.data.dto.CategoryDto;
8 | import ru.practicum.category.data.dto.NewCategoryDto;
9 | import ru.practicum.category.service.CategoryService;
10 |
11 | import javax.validation.Valid;
12 | import javax.validation.constraints.Positive;
13 | import java.time.LocalDateTime;
14 |
15 | @RestController
16 | @RequestMapping("/admin/categories")
17 | @Slf4j
18 | @RequiredArgsConstructor
19 | public class CategoryAdminController {
20 | private final CategoryService categoryService;
21 |
22 | @PostMapping
23 | @ResponseStatus(HttpStatus.CREATED)
24 | public CategoryDto createCategory(@RequestBody @Valid NewCategoryDto newCategoryDto) {
25 | CategoryDto dto = categoryService.createCategory(newCategoryDto);
26 | log.info(String.format("%s: category created: %s", LocalDateTime.now(), dto));
27 | return dto;
28 | }
29 |
30 | @DeleteMapping("/{catId}")
31 | @ResponseStatus(HttpStatus.NO_CONTENT)
32 | public void deleteCategory(@PathVariable @Positive long catId) {
33 | categoryService.deleteCategory(catId);
34 | log.info(String.format("%s: category was deleted by id: %d", LocalDateTime.now(), catId));
35 | }
36 |
37 | @PatchMapping("/{catId}")
38 | public CategoryDto patchCategory(@RequestBody @Valid CategoryDto categoryDto, @PathVariable long catId) {
39 | CategoryDto dto = categoryService.patchCategory(categoryDto, catId);
40 | log.info(String.format("%s: category was patched: %s", LocalDateTime.now(), dto));
41 | return dto;
42 | }
43 | }
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/category/controller/CategoryPublicController.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.category.controller;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.web.bind.annotation.*;
6 | import ru.practicum.category.data.dto.CategoryDto;
7 | import ru.practicum.category.service.CategoryService;
8 |
9 | import javax.validation.constraints.Positive;
10 | import java.time.LocalDateTime;
11 | import java.util.List;
12 |
13 | @RestController
14 | @RequestMapping("/categories")
15 | @Slf4j
16 | @RequiredArgsConstructor
17 | public class CategoryPublicController {
18 | private final CategoryService categoryService;
19 |
20 | @GetMapping
21 | List getAllCategories(@RequestParam(defaultValue = "0") int from,
22 | @RequestParam(defaultValue = "10") int size) {
23 | List categories = categoryService.getAllCategories(from, size);
24 | log.info(String.format("%s: categories are returned: %s", LocalDateTime.now(), categories.toString()));
25 | return categories;
26 | }
27 |
28 | @GetMapping("/{catId}")
29 | CategoryDto findCategoryById(@PathVariable @Positive long catId) {
30 | CategoryDto category = categoryService.findCategoryById(catId);
31 | log.info(String.format("%s: category is returned: %s", LocalDateTime.now(), category.toString()));
32 | return category;
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/category/data/Category.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.category.data;
2 |
3 | import lombok.*;
4 |
5 | import javax.persistence.*;
6 | import javax.validation.constraints.NotBlank;
7 | import javax.validation.constraints.Size;
8 |
9 | @Entity
10 | @Table(name = "categories")
11 | @AllArgsConstructor
12 | @NoArgsConstructor
13 | @Getter
14 | @Setter
15 | @ToString
16 | public class Category {
17 | @Id
18 | @GeneratedValue(strategy = GenerationType.IDENTITY)
19 | @Column(name = "id")
20 | private Long id;
21 | @NotBlank
22 | @Size(min = 1, max = 50)
23 | @Column(name = "name")
24 | private String name;
25 | }
26 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/category/data/CategoryRepository.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.category.data;
2 |
3 | import org.springframework.data.jpa.repository.JpaRepository;
4 | import org.springframework.stereotype.Repository;
5 |
6 | @Repository
7 | public interface CategoryRepository extends JpaRepository {
8 | }
9 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/category/data/dto/CategoryDto.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.category.data.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | import javax.validation.constraints.NotBlank;
8 | import javax.validation.constraints.Size;
9 |
10 | @Data
11 | @AllArgsConstructor
12 | @NoArgsConstructor
13 | public class CategoryDto {
14 | private Long id;
15 | @NotBlank
16 | @Size(min = 1, max = 50)
17 | private String name;
18 | }
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/category/data/dto/CategoryMapper.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.category.data.dto;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.NoArgsConstructor;
5 | import ru.practicum.category.data.Category;
6 |
7 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
8 | public class CategoryMapper {
9 | public static Category toCategory(CategoryDto categoryDto) {
10 | return new Category(categoryDto.getId(), categoryDto.getName());
11 | }
12 |
13 | public static CategoryDto toCategoryDto(Category category) {
14 | return new CategoryDto(category.getId(), category.getName());
15 | }
16 |
17 | public static Category toCategory(NewCategoryDto newCategoryDto) {
18 | return new Category(null, newCategoryDto.getName());
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/category/data/dto/NewCategoryDto.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.category.data.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | import javax.validation.constraints.NotBlank;
8 | import javax.validation.constraints.Size;
9 |
10 | @Data
11 | @AllArgsConstructor
12 | @NoArgsConstructor
13 | public class NewCategoryDto {
14 | @NotBlank
15 | @Size(min = 1, max = 50)
16 | private String name;
17 | }
18 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/category/service/CategoryService.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.category.service;
2 |
3 | import ru.practicum.category.data.dto.CategoryDto;
4 | import ru.practicum.category.data.dto.NewCategoryDto;
5 |
6 | import java.util.List;
7 |
8 | public interface CategoryService {
9 | CategoryDto createCategory(NewCategoryDto newCategoryDto);
10 |
11 | void deleteCategory(long catId);
12 |
13 | CategoryDto patchCategory(CategoryDto categoryDto, long catId);
14 |
15 | List getAllCategories(int from, int size);
16 |
17 | CategoryDto findCategoryById(long catId);
18 | }
19 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/category/service/CategoryServiceImpl.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.category.service;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.data.domain.PageRequest;
5 | import org.springframework.data.domain.Pageable;
6 | import org.springframework.data.domain.Sort;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.transaction.annotation.Transactional;
9 | import ru.practicum.category.data.Category;
10 | import ru.practicum.category.data.CategoryRepository;
11 | import ru.practicum.category.data.dto.CategoryDto;
12 | import ru.practicum.category.data.dto.CategoryMapper;
13 | import ru.practicum.category.data.dto.NewCategoryDto;
14 |
15 | import java.util.List;
16 | import java.util.stream.Collectors;
17 |
18 | @Service
19 | @RequiredArgsConstructor
20 | public class CategoryServiceImpl implements CategoryService {
21 | private final CategoryRepository categoryRepository;
22 |
23 | @Override
24 | @Transactional
25 | public CategoryDto createCategory(NewCategoryDto newCategoryDto) {
26 | Category category = CategoryMapper.toCategory(newCategoryDto);
27 | return CategoryMapper.toCategoryDto(categoryRepository.save(category));
28 | }
29 |
30 | @Override
31 | @Transactional
32 | public void deleteCategory(long catId) {
33 | categoryRepository.deleteById(catId);
34 | }
35 |
36 | @Override
37 | @Transactional
38 | public CategoryDto patchCategory(CategoryDto categoryDto, long catId) {
39 | Category category = CategoryMapper.toCategory(categoryDto);
40 | category.setId(catId);
41 | return CategoryMapper.toCategoryDto(categoryRepository.save(category));
42 | }
43 |
44 | @Override
45 | @Transactional
46 | public List getAllCategories(int from, int size) {
47 | Pageable page = PageRequest.of(from / size, size, Sort.by("id").ascending());
48 | return categoryRepository.findAll(page)
49 | .stream()
50 | .map(CategoryMapper::toCategoryDto)
51 | .collect(Collectors.toList());
52 | }
53 |
54 | @Override
55 | @Transactional
56 | public CategoryDto findCategoryById(long catId) {
57 | return categoryRepository.findById(catId).map(CategoryMapper::toCategoryDto).orElseThrow();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/comment/controller/CommentAdminController.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.comment.controller;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.web.bind.annotation.*;
7 | import ru.practicum.comment.service.CommentService;
8 |
9 | import javax.validation.constraints.Positive;
10 | import java.time.LocalDateTime;
11 |
12 | @RestController
13 | @RequestMapping("/comments")
14 | @RequiredArgsConstructor
15 | @Slf4j
16 | public class CommentAdminController {
17 | private final CommentService commentService;
18 |
19 | @DeleteMapping("/{commentId}")
20 | @ResponseStatus(HttpStatus.NO_CONTENT)
21 | public void deleteCommentById(@PathVariable @Positive long commentId) {
22 | commentService.deleteCommentById(commentId);
23 | log.info("{}: comment with id={} was deleted", LocalDateTime.now(), commentId);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/comment/controller/CommentPrivateController.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.comment.controller;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.validation.annotation.Validated;
7 | import org.springframework.web.bind.annotation.*;
8 | import ru.practicum.comment.data.dto.CommentDto;
9 | import ru.practicum.comment.data.dto.CommentPatchDto;
10 | import ru.practicum.comment.data.dto.NewCommentDto;
11 | import ru.practicum.comment.service.CommentService;
12 |
13 | import javax.validation.Valid;
14 | import javax.validation.constraints.Positive;
15 | import javax.validation.constraints.PositiveOrZero;
16 | import java.time.LocalDateTime;
17 | import java.util.List;
18 |
19 | @RestController
20 | @RequestMapping("/users/{userId}")
21 | @Validated
22 | @RequiredArgsConstructor
23 | @Slf4j
24 | public class CommentPrivateController {
25 | private final CommentService commentService;
26 |
27 | @PostMapping("/events/{eventId}/comments")
28 | @ResponseStatus(HttpStatus.CREATED)
29 | public CommentDto createComment(@PathVariable @Positive long userId, @PathVariable @Positive long eventId, @RequestBody @Valid NewCommentDto newCommentDto) {
30 | CommentDto comment = commentService.createComment(userId, eventId, newCommentDto);
31 | log.info("{}: comment created: {}", LocalDateTime.now(), comment);
32 | return comment;
33 | }
34 |
35 | @PatchMapping("/events/{eventId}/comments")
36 | public CommentDto updateComment(@PathVariable @Positive long userId, @PathVariable @Positive long eventId, @RequestBody @Valid CommentPatchDto commentPatchDto) {
37 | CommentDto comment = commentService.updateComment(userId, eventId, commentPatchDto);
38 | log.info("{}: comment updated: {}", LocalDateTime.now(), comment);
39 | return comment;
40 | }
41 |
42 | @DeleteMapping("/events/{eventId}/comments/{commentId}")
43 | @ResponseStatus(HttpStatus.NO_CONTENT)
44 | public void deleteCommentById(@PathVariable @Positive long userId, @PathVariable @Positive long eventId, @PathVariable @Positive long commentId) {
45 | commentService.deleteComment(userId, eventId, commentId);
46 | log.info("{}: comment deleted: userId = {}, eventId = {}, commentId = {}", LocalDateTime.now(), userId, eventId, commentId);
47 | }
48 |
49 | @GetMapping("/comments")
50 | public List getUserComments(@PathVariable @Positive long userId,
51 | @RequestParam(defaultValue = "0") @PositiveOrZero int from,
52 | @RequestParam(defaultValue = "10") @Positive int size) {
53 | List comments = commentService.getUserComments(userId, from, size);
54 | log.info("{}: comments returned: {}", LocalDateTime.now(), comments);
55 | return comments;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/comment/controller/CommentPublicController.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.comment.controller;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.web.bind.annotation.*;
6 | import ru.practicum.comment.data.dto.CommentDto;
7 | import ru.practicum.comment.service.CommentService;
8 |
9 | import javax.validation.constraints.Positive;
10 | import javax.validation.constraints.PositiveOrZero;
11 | import java.time.LocalDateTime;
12 | import java.util.List;
13 |
14 | @RestController
15 | @RequestMapping("/events/{eventId}/comments")
16 | @RequiredArgsConstructor
17 | @Slf4j
18 | public class CommentPublicController {
19 | private final CommentService commentService;
20 |
21 | @GetMapping("/{commentId}")
22 | //позволяет получить комментарий по id
23 | public CommentDto getCommentById(@PathVariable @Positive long eventId, @PathVariable @Positive long commentId) {
24 | CommentDto comment = commentService.getCommentById(eventId, commentId);
25 | log.info("{}: comment returned: {}", LocalDateTime.now(), comment);
26 | return comment;
27 | }
28 |
29 | @GetMapping
30 | //Позволяет получить комментарии к событию
31 | public List getEventComments(@PathVariable @Positive long eventId,
32 | @RequestParam(defaultValue = "0") @PositiveOrZero int from,
33 | @RequestParam(defaultValue = "10") @Positive int size) {
34 | List comments = commentService.getEventComments(eventId, from, size);
35 | log.info("{}: comments returned: {}", LocalDateTime.now(), comments);
36 | return comments;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/comment/data/Comment.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.comment.data;
2 |
3 | import lombok.*;
4 | import ru.practicum.event.data.Event;
5 | import ru.practicum.user.data.User;
6 |
7 | import javax.persistence.*;
8 | import javax.validation.constraints.NotBlank;
9 | import javax.validation.constraints.NotNull;
10 | import javax.validation.constraints.Size;
11 | import java.time.LocalDateTime;
12 |
13 | @Entity
14 | @Table(name = "comments")
15 | @Getter
16 | @Setter
17 | @NoArgsConstructor
18 | @AllArgsConstructor
19 | @Builder
20 | @ToString
21 | public class Comment {
22 | @Id
23 | @GeneratedValue(strategy = GenerationType.IDENTITY)
24 | private Long id;
25 | @NotBlank
26 | @Size(min = 5, max = 5000)
27 | private String text;
28 | @NotNull
29 | private LocalDateTime created;
30 | private LocalDateTime editedOn;
31 | @NotNull
32 | @ManyToOne
33 | private User commentator;
34 | @NotNull
35 | @ManyToOne
36 | private Event event;
37 | }
38 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/comment/data/CommentMapper.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.comment.data;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.NoArgsConstructor;
5 | import ru.practicum.comment.data.dto.CommentDto;
6 | import ru.practicum.comment.data.dto.NewCommentDto;
7 | import ru.practicum.event.data.Event;
8 | import ru.practicum.event.data.dto.EventMapper;
9 | import ru.practicum.user.data.User;
10 | import ru.practicum.user.data.dto.UserMapper;
11 |
12 | import java.time.LocalDateTime;
13 |
14 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
15 | public class CommentMapper {
16 | public static Comment toComment(User commentator, Event event, NewCommentDto newCommentDto, LocalDateTime created) {
17 | return Comment.builder()
18 | .text(newCommentDto.getText())
19 | .commentator(commentator)
20 | .event(event)
21 | .created(created)
22 | .build();
23 | }
24 |
25 | public static CommentDto toCommentDto(Comment comment) {
26 | return CommentDto.builder()
27 | .id(comment.getId())
28 | .text(comment.getText())
29 | .created(comment.getCreated())
30 | .editedOn(comment.getEditedOn())
31 | .commentator(UserMapper.toUserShortDto(comment.getCommentator()))
32 | .event(EventMapper.toEventShortDto(comment.getEvent()))
33 | .build();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/comment/data/CommentRepository.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.comment.data;
2 |
3 | import org.springframework.data.domain.Pageable;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | import java.util.List;
8 | import java.util.Optional;
9 |
10 | @Repository
11 | public interface CommentRepository extends JpaRepository {
12 | Optional findByCommentatorIdAndEventId(long userId, long eventId);
13 |
14 | Optional findByIdAndCommentatorIdAndEventId(long commentId, long userId, long eventId);
15 |
16 | Optional findByIdAndEventId(long commentId, long eventId);
17 |
18 | List findAllByCommentatorId(long userId, Pageable page);
19 |
20 | List findAllByEventId(long eventId, Pageable page);
21 |
22 | long countByEventId(long eventId);
23 | }
24 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/comment/data/dto/CommentDto.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.comment.data.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import ru.practicum.event.data.dto.EventShortDto;
8 | import ru.practicum.user.data.dto.UserShortDto;
9 |
10 | import javax.validation.constraints.NotBlank;
11 | import javax.validation.constraints.NotNull;
12 | import javax.validation.constraints.Positive;
13 | import java.time.LocalDateTime;
14 |
15 | @Data
16 | @NoArgsConstructor
17 | @AllArgsConstructor
18 | @Builder
19 | public class CommentDto {
20 | @Positive
21 | @NotNull
22 | private Long id;
23 | @NotBlank
24 | private String text;
25 | @NotNull
26 | private LocalDateTime created;
27 | private LocalDateTime editedOn;
28 | @NotNull
29 | private UserShortDto commentator;
30 | @NotNull
31 | private EventShortDto event;
32 | }
33 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/comment/data/dto/CommentPatchDto.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.comment.data.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | import javax.validation.constraints.Size;
9 |
10 | @Data
11 | @NoArgsConstructor
12 | @AllArgsConstructor
13 | @Builder
14 | public class CommentPatchDto {
15 | @Size(min = 5, max = 300)
16 | private String text;
17 | }
18 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/comment/data/dto/NewCommentDto.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.comment.data.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | import javax.validation.constraints.NotBlank;
9 | import javax.validation.constraints.Size;
10 |
11 | @Data
12 | @NoArgsConstructor
13 | @AllArgsConstructor
14 | @Builder
15 | public class NewCommentDto {
16 | @NotBlank
17 | @Size(min = 5, max = 500)
18 | private String text;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/comment/service/CommentService.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.comment.service;
2 |
3 | import ru.practicum.comment.data.dto.CommentDto;
4 | import ru.practicum.comment.data.dto.CommentPatchDto;
5 | import ru.practicum.comment.data.dto.NewCommentDto;
6 |
7 | import java.util.List;
8 |
9 | public interface CommentService {
10 | CommentDto createComment(long userId, long eventId, NewCommentDto newCommentDto);
11 |
12 | CommentDto updateComment(long userId, long eventId, CommentPatchDto commentPatchDto);
13 |
14 | void deleteComment(long userId, long eventId, long commentId);
15 |
16 | CommentDto getCommentById(long eventId, long commentId);
17 |
18 | List getUserComments(long userId, int from, int size);
19 |
20 | List getEventComments(long eventId, int from, int size);
21 |
22 | void deleteCommentById(long commentId);
23 | }
24 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/comment/service/CommentServiceImpl.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.comment.service;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.data.domain.PageRequest;
5 | import org.springframework.data.domain.Pageable;
6 | import org.springframework.data.domain.Sort;
7 | import org.springframework.stereotype.Service;
8 | import ru.practicum.comment.data.Comment;
9 | import ru.practicum.comment.data.CommentMapper;
10 | import ru.practicum.comment.data.CommentRepository;
11 | import ru.practicum.comment.data.dto.CommentDto;
12 | import ru.practicum.comment.data.dto.CommentPatchDto;
13 | import ru.practicum.comment.data.dto.NewCommentDto;
14 | import ru.practicum.event.data.Event;
15 | import ru.practicum.event.data.EventRepository;
16 | import ru.practicum.user.data.User;
17 | import ru.practicum.user.data.UserRepository;
18 |
19 | import java.time.LocalDateTime;
20 | import java.util.List;
21 | import java.util.stream.Collectors;
22 |
23 | @Service
24 | @RequiredArgsConstructor
25 | public class CommentServiceImpl implements CommentService {
26 | private final CommentRepository commentRepository;
27 | private final UserRepository userRepository;
28 | private final EventRepository eventRepository;
29 |
30 | @Override
31 | public CommentDto createComment(long userId, long eventId, NewCommentDto newCommentDto) {
32 | User commentator = userRepository.findById(userId).orElseThrow();
33 | Event event = eventRepository.findById(eventId).orElseThrow();
34 | Comment comment = CommentMapper.toComment(commentator, event, newCommentDto, LocalDateTime.now());
35 | return CommentMapper.toCommentDto(commentRepository.save(comment));
36 | }
37 |
38 | @Override
39 | public CommentDto updateComment(long userId, long eventId, CommentPatchDto commentPatchDto) {
40 | Comment comment = commentRepository.findByCommentatorIdAndEventId(userId, eventId).orElseThrow();
41 | patchComment(comment, commentPatchDto);
42 | return CommentMapper.toCommentDto(commentRepository.save(comment));
43 | }
44 |
45 | @Override
46 | public void deleteComment(long userId, long eventId, long commentId) {
47 | Comment comment = commentRepository.findByIdAndCommentatorIdAndEventId(commentId, userId, eventId).orElseThrow();
48 | commentRepository.delete(comment);
49 | }
50 |
51 | @Override
52 | public CommentDto getCommentById(long eventId, long commentId) {
53 | return CommentMapper.toCommentDto(commentRepository.findByIdAndEventId(commentId, eventId).orElseThrow());
54 | }
55 |
56 | @Override
57 | public List getUserComments(long userId, int from, int size) {
58 | Pageable page = PageRequest.of(from / size, size, Sort.by("id").descending());
59 | return commentRepository.findAllByCommentatorId(userId, page)
60 | .stream()
61 | .map(CommentMapper::toCommentDto)
62 | .collect(Collectors.toList());
63 | }
64 |
65 | @Override
66 | public List getEventComments(long eventId, int from, int size) {
67 | Pageable page = getPageable(from, size);
68 | return commentRepository.findAllByEventId(eventId, page)
69 | .stream()
70 | .map(CommentMapper::toCommentDto)
71 | .collect(Collectors.toList());
72 | }
73 |
74 | @Override
75 | public void deleteCommentById(long commentId) {
76 | commentRepository.deleteById(commentId);
77 | }
78 |
79 | private Pageable getPageable(int from, int size) {
80 | return PageRequest.of(from / size, size, Sort.by("id").ascending());
81 | }
82 |
83 | private void patchComment(Comment comment, CommentPatchDto commentPatchDto) {
84 | if (commentPatchDto != null) {
85 | comment.setText(commentPatchDto.getText());
86 | }
87 | comment.setEditedOn(LocalDateTime.now());
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/compilation/controller/CompilationAdminController.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.compilation.controller;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.validation.annotation.Validated;
7 | import org.springframework.web.bind.annotation.*;
8 | import ru.practicum.compilation.data.CompilationDto;
9 | import ru.practicum.compilation.data.dto.NewCompilationDto;
10 | import ru.practicum.compilation.data.dto.UpdateCompilationRequest;
11 | import ru.practicum.compilation.service.CompilationService;
12 |
13 | import javax.validation.Valid;
14 | import javax.validation.constraints.Positive;
15 | import java.time.LocalDateTime;
16 |
17 | @RestController
18 | @RequestMapping("/admin/compilations")
19 | @RequiredArgsConstructor
20 | @Slf4j
21 | @Validated
22 | public class CompilationAdminController {
23 | private final CompilationService compilationService;
24 |
25 | @PostMapping
26 | @ResponseStatus(HttpStatus.CREATED)
27 | public CompilationDto createCompilation(@RequestBody @Valid NewCompilationDto newCompilationDto) {
28 | CompilationDto compilationDto = compilationService.createCompilation(newCompilationDto);
29 | log.info("{}: compilation created: {}", LocalDateTime.now(), compilationDto.toString());
30 | return compilationDto;
31 | }
32 |
33 | @DeleteMapping("/{compId}")
34 | @ResponseStatus(HttpStatus.NO_CONTENT)
35 | public void deleteCompilation(@PathVariable @Positive long compId) {
36 | compilationService.deleteCompilation(compId);
37 | log.info("{}: compilation with id={} was deleted.", LocalDateTime.now(), compId);
38 | }
39 |
40 | @PatchMapping("/{compId}")
41 | public CompilationDto patchCompilation(@PathVariable @Positive long compId, @RequestBody @Valid UpdateCompilationRequest updateCompilationRequest) {
42 | CompilationDto compilationDto = compilationService.patchCompilation(compId, updateCompilationRequest);
43 | log.info("{}: compilation updated {}", LocalDateTime.now(), compilationDto);
44 | return compilationDto;
45 | }
46 |
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/compilation/controller/CompilationPublicController.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.compilation.controller;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.validation.annotation.Validated;
6 | import org.springframework.web.bind.annotation.*;
7 | import ru.practicum.compilation.data.CompilationDto;
8 | import ru.practicum.compilation.service.CompilationService;
9 |
10 | import javax.validation.constraints.Positive;
11 | import java.time.LocalDateTime;
12 | import java.util.List;
13 |
14 | @RestController
15 | @RequestMapping("/compilations")
16 | @RequiredArgsConstructor
17 | @Slf4j
18 | @Validated
19 | public class CompilationPublicController {
20 | private final CompilationService compilationService;
21 |
22 | @GetMapping
23 | public List getCompilationsByParams(@RequestParam(required = false) Boolean pinned, @RequestParam(defaultValue = "0") int from, @RequestParam(defaultValue = "10") int size) {
24 | List compilations = compilationService.getCompilationsByParams(pinned, from, size);
25 | log.info("{}: compilations were returned: {}", LocalDateTime.now(), compilations.toString());
26 | return compilations;
27 | }
28 |
29 | @GetMapping("/{compId}")
30 | public CompilationDto getCompilationById(@PathVariable @Positive long compId) {
31 | CompilationDto compilationDto = compilationService.getCompilationById(compId);
32 | log.info("{}: compilation returned: {}", LocalDateTime.now(), compilationDto);
33 | return compilationDto;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/compilation/data/Compilation.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.compilation.data;
2 |
3 | import lombok.*;
4 | import ru.practicum.event.data.Event;
5 |
6 | import javax.persistence.*;
7 | import java.util.List;
8 |
9 | @Entity
10 | @Table(name = "compilations")
11 | @AllArgsConstructor
12 | @NoArgsConstructor
13 | @Builder
14 | @Getter
15 | @Setter
16 | @ToString
17 | public class Compilation {
18 | @Id
19 | @GeneratedValue(strategy = GenerationType.IDENTITY)
20 | private long id;
21 | @Column(name = "title")
22 | private String title;
23 | @Column(name = "pinned")
24 | private boolean pinned;
25 | @ManyToMany
26 | @JoinTable(name = "compilation_event",
27 | joinColumns = @JoinColumn(name = "compilation_id"),
28 | inverseJoinColumns = @JoinColumn(name = "event_id"))
29 | private List events;
30 | }
31 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/compilation/data/CompilationDto.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.compilation.data;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import ru.practicum.event.data.dto.EventShortDto;
8 |
9 | import java.util.List;
10 |
11 | @Data
12 | @NoArgsConstructor
13 | @AllArgsConstructor
14 | @Builder
15 | public class CompilationDto {
16 | private long id;
17 | private String title;
18 | private boolean pinned;
19 | private List events;
20 | }
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/compilation/data/CompilationMapper.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.compilation.data;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.NoArgsConstructor;
5 | import ru.practicum.compilation.data.dto.NewCompilationDto;
6 | import ru.practicum.compilation.data.dto.UpdateCompilationRequest;
7 | import ru.practicum.event.data.Event;
8 | import ru.practicum.event.data.dto.EventMapper;
9 |
10 | import java.util.List;
11 | import java.util.stream.Collectors;
12 |
13 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
14 | public class CompilationMapper {
15 | public static Compilation toCompilation(NewCompilationDto newCompilationDto, List events) {
16 | return Compilation.builder()
17 | .title(newCompilationDto.getTitle())
18 | .pinned(newCompilationDto.isPinned())
19 | .events(events)
20 | .build();
21 | }
22 |
23 | public static Compilation toCompilation(UpdateCompilationRequest updateCompilationRequest, Compilation compilation, List events) {
24 | return Compilation.builder()
25 | .id(compilation.getId())
26 | .title(updateCompilationRequest.getTitle() != null ? updateCompilationRequest.getTitle() : compilation.getTitle())
27 | .pinned(updateCompilationRequest.getPinned() != null ? updateCompilationRequest.getPinned() : compilation.isPinned())
28 | .events(events)
29 | .build();
30 | }
31 |
32 | public static CompilationDto toCompilationDto(Compilation compilation) {
33 | return CompilationDto.builder()
34 | .id(compilation.getId())
35 | .title(compilation.getTitle())
36 | .pinned(compilation.isPinned())
37 | .events(compilation.getEvents() != null ? compilation.getEvents().stream().map(EventMapper::toEventShortDto).collect(Collectors.toList()) : null)
38 | .build();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/compilation/data/CompilationRepository.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.compilation.data;
2 |
3 | import org.springframework.data.domain.Pageable;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | import java.util.List;
8 |
9 | @Repository
10 | public interface CompilationRepository extends JpaRepository {
11 | List findAllByPinned(boolean pinned, Pageable page);
12 | }
13 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/compilation/data/dto/NewCompilationDto.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.compilation.data.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | import javax.validation.constraints.NotBlank;
9 | import javax.validation.constraints.Size;
10 | import java.util.List;
11 |
12 | @Data
13 | @NoArgsConstructor
14 | @AllArgsConstructor
15 | @Builder
16 | public class NewCompilationDto {
17 | @NotBlank
18 | @Size(min = 1, max = 50)
19 | private String title;
20 | private boolean pinned;
21 | private List events;
22 | }
23 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/compilation/data/dto/UpdateCompilationRequest.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.compilation.data.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | import javax.validation.constraints.Size;
8 | import java.util.List;
9 |
10 | @Data
11 | @NoArgsConstructor
12 | @AllArgsConstructor
13 | public class UpdateCompilationRequest {
14 | @Size(min = 1, max = 50)
15 | private String title;
16 | private Boolean pinned;
17 | private List events;
18 | }
19 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/compilation/service/CompilationService.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.compilation.service;
2 |
3 | import ru.practicum.compilation.data.CompilationDto;
4 | import ru.practicum.compilation.data.dto.NewCompilationDto;
5 | import ru.practicum.compilation.data.dto.UpdateCompilationRequest;
6 |
7 | import java.util.List;
8 |
9 | public interface CompilationService {
10 | CompilationDto createCompilation(NewCompilationDto newCompilationDto);
11 |
12 | void deleteCompilation(long compId);
13 |
14 | CompilationDto patchCompilation(long compId, UpdateCompilationRequest updateCompilationRequest);
15 |
16 | List getCompilationsByParams(Boolean pinned, int from, int size);
17 |
18 | CompilationDto getCompilationById(long compId);
19 | }
20 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/compilation/service/CompilationServiceImpl.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.compilation.service;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.data.domain.PageRequest;
6 | import org.springframework.data.domain.Pageable;
7 | import org.springframework.data.domain.Sort;
8 | import org.springframework.stereotype.Service;
9 | import org.springframework.transaction.annotation.Transactional;
10 | import ru.practicum.compilation.data.Compilation;
11 | import ru.practicum.compilation.data.CompilationDto;
12 | import ru.practicum.compilation.data.CompilationMapper;
13 | import ru.practicum.compilation.data.CompilationRepository;
14 | import ru.practicum.compilation.data.dto.NewCompilationDto;
15 | import ru.practicum.compilation.data.dto.UpdateCompilationRequest;
16 | import ru.practicum.event.data.Event;
17 | import ru.practicum.event.data.EventRepository;
18 |
19 | import java.util.List;
20 | import java.util.stream.Collectors;
21 |
22 | @Service
23 | @RequiredArgsConstructor
24 | @Slf4j
25 | public class CompilationServiceImpl implements CompilationService {
26 | private final CompilationRepository compilationRepository;
27 | private final EventRepository eventRepository;
28 |
29 | @Override
30 | @Transactional
31 | public CompilationDto createCompilation(NewCompilationDto newCompilationDto) {
32 | List events = newCompilationDto.getEvents() != null ?
33 | eventRepository.findAllByIdIn(newCompilationDto.getEvents()) : null;
34 | Compilation compilation = CompilationMapper.toCompilation(newCompilationDto, events);
35 | return CompilationMapper.toCompilationDto(compilationRepository.save(compilation));
36 | }
37 |
38 | @Override
39 | public void deleteCompilation(long compId) {
40 | compilationRepository.deleteById(compId);
41 | }
42 |
43 | @Override
44 | public CompilationDto patchCompilation(long compId, UpdateCompilationRequest updateCompilationRequest) {
45 | Compilation compilation = compilationRepository.findById(compId).orElseThrow();
46 | List events;
47 | if (updateCompilationRequest.getEvents() != null && updateCompilationRequest.getEvents().size() > 0) {
48 | events = eventRepository.findAllByIdIn(updateCompilationRequest.getEvents());
49 | } else {
50 | events = compilation.getEvents();
51 | }
52 | compilation = CompilationMapper.toCompilation(updateCompilationRequest, compilation, events);
53 | return CompilationMapper.toCompilationDto(compilationRepository.save(compilation));
54 | }
55 |
56 | @Override
57 | public List getCompilationsByParams(Boolean pinned, int from, int size) {
58 | List compilations = pinned != null ?
59 | compilationRepository.findAllByPinned(pinned, getPageable(from, size)) :
60 | compilationRepository.findAll(getPageable(from, size)).stream().collect(Collectors.toList());
61 | return compilations.stream().map(CompilationMapper::toCompilationDto).collect(Collectors.toList());
62 | }
63 |
64 | @Override
65 | public CompilationDto getCompilationById(long compId) {
66 | return CompilationMapper.toCompilationDto(compilationRepository.findById(compId).orElseThrow());
67 | }
68 |
69 | private Pageable getPageable(int from, int size) {
70 | return PageRequest.of(from / size, size, Sort.by("id").ascending());
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/configuration/BeanConfiguration.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.configuration;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import ru.practicum.StatClient;
7 |
8 | @Configuration
9 | @RequiredArgsConstructor
10 | public class BeanConfiguration {
11 |
12 | @Bean
13 | StatClient createStatClient() {
14 | return new StatClient();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/controller/AdminEventController.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.controller;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.format.annotation.DateTimeFormat;
6 | import org.springframework.validation.annotation.Validated;
7 | import org.springframework.web.bind.annotation.*;
8 | import ru.practicum.event.data.EventState;
9 | import ru.practicum.event.data.dto.EventFullDto;
10 | import ru.practicum.event.data.dto.SearchParameters;
11 | import ru.practicum.event.data.dto.UpdateEventAdminRequest;
12 | import ru.practicum.event.service.EventService;
13 |
14 | import javax.validation.Valid;
15 | import javax.validation.constraints.Positive;
16 | import javax.validation.constraints.PositiveOrZero;
17 | import java.time.LocalDateTime;
18 | import java.util.List;
19 |
20 | @RestController
21 | @RequestMapping("admin/events")
22 | @Slf4j
23 | @RequiredArgsConstructor
24 | @Validated
25 | public class AdminEventController {
26 | private final EventService eventService;
27 |
28 | @GetMapping
29 | List getAllEventWithParams(@RequestParam(required = false) List users,
30 | @RequestParam(required = false) List states,
31 | @RequestParam(required = false) List categories,
32 | @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeStart,
33 | @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeEnd,
34 | @RequestParam(defaultValue = "0") @PositiveOrZero int from,
35 | @RequestParam(defaultValue = "10") @Positive int size) {
36 |
37 | SearchParameters parameters = new SearchParameters()
38 | .setUsers(users)
39 | .setStates(states)
40 | .setCategories(categories)
41 | .setRangeStart(rangeStart)
42 | .setRangeEnd(rangeEnd)
43 | .setFrom(from)
44 | .setSize(size);
45 |
46 | List events = eventService.getAllEventWithParams(parameters);
47 | log.info(String.format("%s: events were returned: %s", LocalDateTime.now(), events.toString()));
48 | return events;
49 | }
50 |
51 | @PatchMapping("/{eventId}")
52 | public EventFullDto moderateEvent(@PathVariable @Positive long eventId,
53 | @RequestBody @Valid UpdateEventAdminRequest eventAdminRequest) {
54 | EventFullDto eventDto = eventService.moderateEvent(eventId, eventAdminRequest);
55 | log.info(String.format("%s: event was successfully moderated: %s", LocalDateTime.now(), eventDto.toString()));
56 | return eventDto;
57 | }
58 | }
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/controller/PrivateEventController.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.controller;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.web.bind.annotation.*;
7 | import ru.practicum.event.data.dto.*;
8 | import ru.practicum.event.service.EventService;
9 | import ru.practicum.request.data.dto.ParticipationRequestDto;
10 |
11 | import javax.validation.Valid;
12 | import javax.validation.constraints.Positive;
13 | import java.time.LocalDateTime;
14 | import java.util.List;
15 |
16 | @RestController
17 | @RequestMapping("/users/{userId}/events")
18 | @Slf4j
19 | @RequiredArgsConstructor
20 | public class PrivateEventController {
21 | private final EventService eventService;
22 |
23 | @GetMapping
24 | public List getUserEvents(@PathVariable @Positive long userId,
25 | @RequestParam(defaultValue = "0") int from,
26 | @RequestParam(defaultValue = "10") int size) {
27 | List userEvents = eventService.getUserEvents(userId, from, size);
28 | log.info(String.format("%s: events by user id=%d were returned: %s", LocalDateTime.now(), userId, userEvents.toString()));
29 | return userEvents;
30 | }
31 |
32 | @PostMapping
33 | @ResponseStatus(HttpStatus.CREATED)
34 | public EventFullDto createEvent(@RequestBody @Valid NewEventDto newEventDto, @PathVariable @Positive long userId) {
35 | EventFullDto eventDto = eventService.createEvent(newEventDto, userId);
36 | log.info(String.format("%s: new event is created: %s", LocalDateTime.now(), eventDto.toString()));
37 | return eventDto;
38 | }
39 |
40 | @GetMapping("/{eventId}")
41 | public EventFullDto findUserEventById(@PathVariable @Positive long userId, @PathVariable @Positive long eventId) {
42 | EventFullDto eventDto = eventService.findUserEventById(userId, eventId);
43 | log.info(String.format("%s: event by id returned: %s", LocalDateTime.now(), eventDto.toString()));
44 | return eventDto;
45 | }
46 |
47 | @PatchMapping("/{eventId}")
48 | public EventFullDto patchEvent(@PathVariable @Positive long userId,
49 | @PathVariable @Positive long eventId,
50 | @RequestBody @Valid UpdateEventUserRequest updateRequest) {
51 | EventFullDto eventDto = eventService.patchEvent(userId, eventId, updateRequest);
52 | log.info(String.format("%s: event was successfully patched: %s", LocalDateTime.now(), eventDto.toString()));
53 | return eventDto;
54 | }
55 |
56 | @GetMapping("/{eventId}/requests")
57 | public List getAllEventRequests(@PathVariable @Positive long userId, @PathVariable @Positive long eventId) {
58 | List eventRequests = eventService.getAllEventRequests(userId, eventId);
59 | log.info(String.format("%s: all event requests returned: %s", LocalDateTime.now(), eventRequests.toString()));
60 | return eventRequests;
61 | }
62 |
63 |
64 | @PatchMapping("/{eventId}/requests")
65 | public EventRequestStatusUpdateResult updateRequestsStatus(@PathVariable @Positive long userId,
66 | @PathVariable @Positive long eventId,
67 | @RequestBody @Valid EventRequestStatusUpdateRequest updateRequest) {
68 | EventRequestStatusUpdateResult result = eventService.updateRequestsStatus(userId, eventId, updateRequest);
69 | log.info(String.format("%s: Requests status updated: %s", LocalDateTime.now(), result.toString()));
70 | return result;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/controller/PublicEventController.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.controller;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.format.annotation.DateTimeFormat;
6 | import org.springframework.validation.annotation.Validated;
7 | import org.springframework.web.bind.annotation.*;
8 | import ru.practicum.event.data.dto.EventFullDto;
9 | import ru.practicum.event.data.dto.EventShortDto;
10 | import ru.practicum.event.data.dto.SearchParameters;
11 | import ru.practicum.event.data.dto.SortProperty;
12 | import ru.practicum.event.service.EventService;
13 | import ru.practicum.validation.DateTimeConsistency;
14 |
15 | import javax.servlet.http.HttpServletRequest;
16 | import javax.validation.constraints.Positive;
17 | import javax.validation.constraints.PositiveOrZero;
18 | import java.time.LocalDateTime;
19 | import java.util.List;
20 |
21 | @RestController
22 | @RequestMapping("/events")
23 | @RequiredArgsConstructor
24 | @Slf4j
25 | @Validated
26 | public class PublicEventController {
27 |
28 | private final EventService eventService;
29 |
30 | @GetMapping
31 | @DateTimeConsistency
32 | List getAllPublicEvents(@RequestParam(required = false) String text,
33 | @RequestParam(required = false) List categories,
34 | @RequestParam(required = false) Boolean paid,
35 | @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeStart,
36 | @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeEnd,
37 | @RequestParam(defaultValue = "false") boolean onlyAvailable,
38 | @RequestParam(required = false) SortProperty sortProperty,
39 | @RequestParam(defaultValue = "0") @PositiveOrZero int from,
40 | @RequestParam(defaultValue = "10") @Positive int size,
41 | HttpServletRequest request) {
42 | SearchParameters parameters = new SearchParameters()
43 | .setText(text)
44 | .setCategories(categories)
45 | .setPaid(paid)
46 | .setRangeStart(rangeStart)
47 | .setRangeEnd(rangeEnd)
48 | .setOnlyAvailable(onlyAvailable)
49 | .setSortProperty(sortProperty)
50 | .setFrom(from)
51 | .setSize(size);
52 | List events = eventService.getAllPublicEventsWithParams(parameters, request);
53 | log.info(String.format("%s: events are returned: %s", LocalDateTime.now(), events.toString()));
54 | return events;
55 | }
56 |
57 | @GetMapping("/{eventId}")
58 | EventFullDto getEventById(@PathVariable @Positive long eventId, HttpServletRequest request) {
59 | EventFullDto event = eventService.getEventById(eventId, request);
60 | log.info(String.format("%s: event is returned: %s", LocalDateTime.now(), event.toString()));
61 | return event;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/AdminEventStateAction.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data;
2 |
3 | public enum AdminEventStateAction {
4 | PUBLISH_EVENT,
5 | REJECT_EVENT
6 | }
7 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/Event.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data;
2 |
3 | import lombok.*;
4 | import ru.practicum.category.data.Category;
5 | import ru.practicum.user.data.User;
6 |
7 | import javax.persistence.*;
8 | import javax.validation.constraints.NotBlank;
9 | import javax.validation.constraints.NotNull;
10 | import javax.validation.constraints.PositiveOrZero;
11 | import java.time.LocalDateTime;
12 |
13 | @Entity
14 | @Table(name = "events")
15 | @Getter
16 | @Setter
17 | @NoArgsConstructor
18 | @AllArgsConstructor
19 | @Builder
20 | @ToString
21 | public class Event {
22 | @NotBlank
23 | private String annotation;
24 | @NotNull
25 | @ManyToOne
26 | private Category category;
27 | @NotNull
28 | private LocalDateTime createdOn;
29 | @NotBlank
30 | private String description;
31 | @NotNull
32 | private LocalDateTime eventDate;
33 | @Id
34 | @GeneratedValue(strategy = GenerationType.IDENTITY)
35 | private Long id;
36 | @NotNull
37 | @ManyToOne
38 | private User initiator;
39 | private float lat;
40 | private float lon;
41 | @NotNull
42 | private Boolean paid;
43 | @PositiveOrZero
44 | private int participantLimit;
45 | private LocalDateTime publishedOn;
46 | @NotNull
47 | private Boolean requestModeration;
48 | @NotNull
49 | @Enumerated(EnumType.STRING)
50 | private EventState state;
51 | @NotBlank
52 | private String title;
53 | }
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/EventRepository.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data;
2 |
3 | import org.springframework.data.domain.Pageable;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 | import org.springframework.data.querydsl.QuerydslPredicateExecutor;
6 | import org.springframework.stereotype.Repository;
7 |
8 | import java.util.List;
9 | import java.util.Optional;
10 |
11 | @Repository
12 | public interface EventRepository extends JpaRepository, QuerydslPredicateExecutor {
13 | List findAllByInitiatorId(long initiatorId, Pageable page);
14 |
15 | Optional findByInitiatorIdAndId(long initiatorId, long eventId);
16 |
17 | Optional findByIdAndState(long eventId, EventState eventState);
18 |
19 | List findAllByIdIn(List eventIds);
20 | }
21 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/EventState.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data;
2 |
3 | public enum EventState {
4 | PENDING,
5 | PUBLISHED,
6 | CANCELED
7 | }
8 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/Location.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 |
6 | @Data
7 | @AllArgsConstructor
8 | public class Location {
9 | private float lat;
10 | private float lon;
11 | }
12 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/UserEventStateAction.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data;
2 |
3 | public enum UserEventStateAction {
4 | SEND_TO_REVIEW,
5 | CANCEL_REVIEW
6 | }
7 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/dto/EventFullDto.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data.dto;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import lombok.*;
5 | import ru.practicum.category.data.dto.CategoryDto;
6 | import ru.practicum.event.data.EventState;
7 | import ru.practicum.event.data.Location;
8 | import ru.practicum.user.data.dto.UserShortDto;
9 |
10 | import javax.validation.constraints.NotBlank;
11 | import javax.validation.constraints.NotNull;
12 | import javax.validation.constraints.PositiveOrZero;
13 | import java.time.LocalDateTime;
14 |
15 | @Data
16 | @EqualsAndHashCode(callSuper = true)
17 | @NoArgsConstructor
18 | @AllArgsConstructor
19 | public class EventFullDto extends EventShortDto {
20 | @NotNull
21 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
22 | private LocalDateTime createdOn;
23 | @NotBlank
24 | private String description;
25 | @NotNull
26 | private Location location;
27 | @PositiveOrZero
28 | private int participantLimit;
29 | @NotNull
30 | private LocalDateTime publishedOn;
31 | @NotNull
32 | private Boolean requestModeration;
33 | @NotNull
34 | private EventState state;
35 |
36 | @Builder(builderMethodName = "fullBuilder")
37 | public EventFullDto(String annotation,
38 | CategoryDto category,
39 | long confirmedRequests,
40 | LocalDateTime eventDate, long id,
41 | UserShortDto initiator,
42 | Boolean paid,
43 | String title,
44 | long views,
45 | long comments,
46 | LocalDateTime createdOn,
47 | String description,
48 | Location location,
49 | int participantLimit,
50 | LocalDateTime publishedOn,
51 | Boolean requestModeration,
52 | EventState state) {
53 | super(annotation, category, confirmedRequests, eventDate, id, initiator, paid, title, views, comments);
54 | this.createdOn = createdOn;
55 | this.description = description;
56 | this.location = location;
57 | this.participantLimit = participantLimit;
58 | this.publishedOn = publishedOn;
59 | this.requestModeration = requestModeration;
60 | this.state = state;
61 | }
62 | }
63 |
64 |
65 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/dto/EventMapper.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data.dto;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.NoArgsConstructor;
5 | import ru.practicum.category.data.Category;
6 | import ru.practicum.category.data.dto.CategoryMapper;
7 | import ru.practicum.event.data.Event;
8 | import ru.practicum.event.data.Location;
9 | import ru.practicum.user.data.User;
10 | import ru.practicum.user.data.dto.UserMapper;
11 |
12 | import java.time.format.DateTimeFormatter;
13 |
14 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
15 | public class EventMapper {
16 | public static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
17 |
18 | public static EventShortDto toEventShortDto(Event event) {
19 | return EventShortDto.builder()
20 | .annotation(event.getAnnotation())
21 | .category(CategoryMapper.toCategoryDto(event.getCategory()))
22 | .confirmedRequests(0)
23 | .eventDate(event.getEventDate())
24 | .id(event.getId())
25 | .initiator(UserMapper.toUserShortDto(event.getInitiator()))
26 | .paid(event.getPaid())
27 | .title(event.getTitle())
28 | .views(0)
29 | .build();
30 | }
31 |
32 | public static EventShortDto toEventShortDto(Event event, long confirmedRequests, long views) {
33 | return EventShortDto.builder()
34 | .annotation(event.getAnnotation())
35 | .category(CategoryMapper.toCategoryDto(event.getCategory()))
36 | .confirmedRequests(confirmedRequests)
37 | .eventDate(event.getEventDate())
38 | .id(event.getId())
39 | .initiator(UserMapper.toUserShortDto(event.getInitiator()))
40 | .paid(event.getPaid())
41 | .title(event.getTitle())
42 | .views(views)
43 | .build();
44 | }
45 |
46 | public static EventFullDto toEventFullDto(Event event) {
47 | return EventFullDto
48 | .fullBuilder()
49 | .annotation(event.getAnnotation())
50 | .category(CategoryMapper.toCategoryDto(event.getCategory()))
51 | .createdOn(event.getCreatedOn())
52 | .description(event.getDescription())
53 | .eventDate(event.getEventDate())
54 | .id(event.getId())
55 | .initiator(UserMapper.toUserShortDto(event.getInitiator()))
56 | .location(new Location(event.getLat(), event.getLon()))
57 | .paid(event.getPaid())
58 | .participantLimit(event.getParticipantLimit())
59 | .publishedOn(event.getPublishedOn())
60 | .requestModeration(event.getRequestModeration())
61 | .state(event.getState())
62 | .title(event.getTitle())
63 | .build();
64 | }
65 |
66 | public static EventFullDto toEventFullDto(Event event, long confirmedRequests, long views, long comments) {
67 | return EventFullDto
68 | .fullBuilder()
69 | .annotation(event.getAnnotation())
70 | .category(CategoryMapper.toCategoryDto(event.getCategory()))
71 | .confirmedRequests(confirmedRequests)
72 | .createdOn(event.getCreatedOn())
73 | .description(event.getDescription())
74 | .eventDate(event.getEventDate())
75 | .id(event.getId())
76 | .initiator(UserMapper.toUserShortDto(event.getInitiator()))
77 | .location(new Location(event.getLat(), event.getLon()))
78 | .paid(event.getPaid())
79 | .participantLimit(event.getParticipantLimit())
80 | .requestModeration(event.getRequestModeration())
81 | .state(event.getState())
82 | .title(event.getTitle())
83 | .views(views)
84 | .comments(comments)
85 | .build();
86 | }
87 |
88 | public static Event toEvent(NewEventDto newEventDto, User initiator, Category category) {
89 | return Event.builder()
90 | .annotation(newEventDto.getAnnotation())
91 | .category(category)
92 | .createdOn(null)
93 | .description(newEventDto.getDescription())
94 | .eventDate(newEventDto.getEventDate())
95 | .initiator(initiator)
96 | .lat(newEventDto.getLocation().getLat())
97 | .lon(newEventDto.getLocation().getLon())
98 | .paid(newEventDto.isPaid())
99 | .participantLimit(newEventDto.getParticipantLimit())
100 | .requestModeration(newEventDto.isRequestModeration())
101 | .state(null)
102 | .title(newEventDto.getTitle())
103 | .build();
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/dto/EventRequestStatusUpdateRequest.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data.dto;
2 |
3 | import lombok.Data;
4 | import ru.practicum.request.data.RequestStatus;
5 |
6 | import javax.validation.constraints.NotNull;
7 | import java.util.List;
8 |
9 | @Data
10 | public class EventRequestStatusUpdateRequest {
11 | @NotNull
12 | private List requestIds;
13 | @NotNull
14 | private RequestStatus status;
15 | }
16 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/dto/EventRequestStatusUpdateResult.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import ru.practicum.request.data.dto.ParticipationRequestDto;
6 |
7 | import java.util.List;
8 |
9 | @Data
10 | @AllArgsConstructor
11 | public class EventRequestStatusUpdateResult {
12 | List confirmedRequests;
13 | List rejectedRequests;
14 | }
15 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/dto/EventShortDto.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data.dto;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Builder;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 | import ru.practicum.category.data.dto.CategoryDto;
9 | import ru.practicum.user.data.dto.UserShortDto;
10 |
11 | import javax.validation.constraints.NotBlank;
12 | import javax.validation.constraints.NotNull;
13 | import javax.validation.constraints.PositiveOrZero;
14 | import java.time.LocalDateTime;
15 |
16 | @Data
17 | @NoArgsConstructor
18 | @Builder
19 | @AllArgsConstructor
20 | public class EventShortDto {
21 | @NotBlank
22 | private String annotation;
23 | @NotNull
24 | private CategoryDto category;
25 | @PositiveOrZero
26 | private long confirmedRequests;
27 | @NotNull
28 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
29 | private LocalDateTime eventDate;
30 | private long id;
31 | @NotNull
32 | private UserShortDto initiator;
33 | @NotNull
34 | private Boolean paid;
35 | @NotBlank
36 | private String title;
37 | @PositiveOrZero
38 | private long views;
39 | @PositiveOrZero
40 | private long comments;
41 | }
42 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/dto/NewEventDto.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data.dto;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 | import ru.practicum.event.data.Location;
7 | import ru.practicum.validation.InFuture;
8 |
9 | import javax.validation.constraints.*;
10 | import java.time.LocalDateTime;
11 |
12 | @Data
13 | @NoArgsConstructor
14 | public class NewEventDto {
15 | @NotBlank
16 | @Size(min = 20, max = 2000)
17 | private String annotation;
18 | @NotNull
19 | @Positive
20 | private Long category;
21 | @NotBlank
22 | @Size(min = 20, max = 7000)
23 | private String description;
24 | @NotNull
25 | @InFuture(2)
26 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
27 | private LocalDateTime eventDate;
28 | @NotNull
29 | private Location location;
30 | private boolean paid = false;
31 | @PositiveOrZero
32 | private int participantLimit;
33 | private boolean requestModeration = true;
34 | @NotBlank
35 | @Size(min = 3, max = 120)
36 | private String title;
37 | }
38 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/dto/SearchParameters.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data.dto;
2 |
3 | import lombok.Getter;
4 | import lombok.ToString;
5 | import ru.practicum.event.data.EventState;
6 |
7 | import java.time.LocalDateTime;
8 | import java.util.List;
9 |
10 | @Getter
11 | @ToString
12 | public class SearchParameters {
13 | private String text;
14 | private List users;
15 | private List states;
16 | private List categories;
17 | private Boolean paid;
18 | private LocalDateTime rangeStart;
19 | private LocalDateTime rangeEnd;
20 | private Boolean onlyAvailable;
21 | private Integer from;
22 | private Integer size;
23 | private SortProperty sortProperty;
24 |
25 |
26 | public SearchParameters setSortProperty(SortProperty sortProperty) {
27 | this.sortProperty = sortProperty;
28 | return this;
29 | }
30 |
31 | public SearchParameters setUsers(List users) {
32 | this.users = users;
33 | return this;
34 | }
35 |
36 | public SearchParameters setText(String text) {
37 | this.text = text;
38 | return this;
39 | }
40 |
41 | public SearchParameters setPaid(Boolean paid) {
42 | this.paid = paid;
43 | return this;
44 | }
45 |
46 | public SearchParameters setOnlyAvailable(Boolean onlyAvailable) {
47 | this.onlyAvailable = onlyAvailable;
48 | return this;
49 | }
50 |
51 | public SearchParameters setStates(List states) {
52 | this.states = states;
53 | return this;
54 | }
55 |
56 | public SearchParameters setCategories(List categories) {
57 | this.categories = categories;
58 | return this;
59 | }
60 |
61 | public SearchParameters setRangeStart(LocalDateTime rangeStart) {
62 | this.rangeStart = rangeStart;
63 | return this;
64 | }
65 |
66 | public SearchParameters setRangeEnd(LocalDateTime rangeEnd) {
67 | this.rangeEnd = rangeEnd;
68 | return this;
69 | }
70 |
71 | public SearchParameters setFrom(int from) {
72 | this.from = from;
73 | return this;
74 | }
75 |
76 | public SearchParameters setSize(int size) {
77 | this.size = size;
78 | return this;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/dto/SortProperty.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data.dto;
2 |
3 | public enum SortProperty {
4 | EVENT_DATE,
5 | VIEWS
6 | }
7 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/dto/UpdateEventAdminRequest.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data.dto;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import lombok.Data;
5 | import lombok.EqualsAndHashCode;
6 | import lombok.NoArgsConstructor;
7 | import ru.practicum.event.data.AdminEventStateAction;
8 | import ru.practicum.validation.InFuture;
9 |
10 | import java.time.LocalDateTime;
11 |
12 | @Data
13 | @EqualsAndHashCode(callSuper = true)
14 | @NoArgsConstructor
15 | public class UpdateEventAdminRequest extends UpdateEventRequest {
16 | private AdminEventStateAction stateAction;
17 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
18 | @InFuture(1)
19 | private LocalDateTime eventDate;
20 | }
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/dto/UpdateEventRequest.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data.dto;
2 |
3 | import lombok.Data;
4 | import lombok.NoArgsConstructor;
5 | import ru.practicum.event.data.Location;
6 |
7 | import javax.validation.constraints.Size;
8 |
9 | @Data
10 | @NoArgsConstructor
11 | public class UpdateEventRequest {
12 | @Size(min = 20, max = 2000)
13 | private String annotation;
14 | private Long category;
15 | @Size(min = 20, max = 7000)
16 | private String description;
17 | private Location location;
18 | private Boolean paid;
19 | private Integer participantLimit;
20 | private Boolean requestModeration;
21 | @Size(min = 3, max = 120)
22 | private String title;
23 | }
24 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/data/dto/UpdateEventUserRequest.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.data.dto;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import lombok.Data;
5 | import lombok.EqualsAndHashCode;
6 | import lombok.NoArgsConstructor;
7 | import ru.practicum.event.data.UserEventStateAction;
8 | import ru.practicum.validation.InFuture;
9 |
10 | import java.time.LocalDateTime;
11 |
12 | @Data
13 | @EqualsAndHashCode(callSuper = true)
14 | @NoArgsConstructor
15 | public class UpdateEventUserRequest extends UpdateEventRequest {
16 | private UserEventStateAction stateAction;
17 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
18 | @InFuture(2)
19 | private LocalDateTime eventDate;
20 | }
21 |
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/service/EventService.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.service;
2 |
3 | import ru.practicum.event.data.dto.*;
4 | import ru.practicum.request.data.dto.ParticipationRequestDto;
5 |
6 | import javax.servlet.http.HttpServletRequest;
7 | import java.util.List;
8 |
9 | public interface EventService {
10 | List getUserEvents(long userId, int from, int size);
11 |
12 | EventFullDto createEvent(NewEventDto newEventDto, long userId);
13 |
14 | EventFullDto findUserEventById(long userId, long eventId);
15 |
16 | EventFullDto patchEvent(long userId, long eventId, UpdateEventUserRequest updateRequest);
17 |
18 | EventFullDto moderateEvent(long eventId, UpdateEventAdminRequest eventAdminRequest);
19 |
20 | List getAllEventRequests(long userId, long eventId);
21 |
22 | EventRequestStatusUpdateResult updateRequestsStatus(long userId, long eventId, EventRequestStatusUpdateRequest updateRequest);
23 |
24 | List getAllEventWithParams(SearchParameters parameters);
25 |
26 | List getAllPublicEventsWithParams(SearchParameters parameters, HttpServletRequest request);
27 |
28 | EventFullDto getEventById(long eventId, HttpServletRequest request);
29 | }
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/event/service/EventServiceImpl.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.event.service;
2 |
3 | import com.querydsl.core.BooleanBuilder;
4 | import com.querydsl.core.types.Predicate;
5 | import com.querydsl.core.types.dsl.NumberExpression;
6 | import com.querydsl.jpa.impl.JPAQuery;
7 | import lombok.RequiredArgsConstructor;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.springframework.data.domain.PageRequest;
10 | import org.springframework.data.domain.Pageable;
11 | import org.springframework.data.domain.Sort;
12 | import org.springframework.stereotype.Service;
13 | import org.springframework.transaction.annotation.Transactional;
14 | import ru.practicum.StatClient;
15 | import ru.practicum.category.data.Category;
16 | import ru.practicum.category.data.CategoryRepository;
17 | import ru.practicum.comment.data.CommentRepository;
18 | import ru.practicum.comment.data.QComment;
19 | import ru.practicum.dto.ViewStats;
20 | import ru.practicum.event.data.*;
21 | import ru.practicum.event.data.dto.*;
22 | import ru.practicum.exception.ChangeEventStatusForbiddenException;
23 | import ru.practicum.exception.EventParticipationForbiddenException;
24 | import ru.practicum.exception.RequestNotValidException;
25 | import ru.practicum.exception.RequestStatusNotPendingException;
26 | import ru.practicum.request.data.QRequest;
27 | import ru.practicum.request.data.Request;
28 | import ru.practicum.request.data.RequestRepository;
29 | import ru.practicum.request.data.RequestStatus;
30 | import ru.practicum.request.data.dto.ParticipationRequestDto;
31 | import ru.practicum.request.data.dto.RequestMapper;
32 | import ru.practicum.user.data.User;
33 | import ru.practicum.user.data.UserRepository;
34 |
35 | import javax.persistence.EntityManager;
36 | import javax.servlet.http.HttpServletRequest;
37 | import java.time.LocalDateTime;
38 | import java.util.*;
39 | import java.util.stream.Collectors;
40 |
41 | @Service
42 | @RequiredArgsConstructor
43 | @Slf4j
44 | public class EventServiceImpl implements EventService {
45 | private final EventRepository eventRepository;
46 | private final UserRepository userRepository;
47 | private final CategoryRepository categoryRepository;
48 | private final RequestRepository requestRepository;
49 | private final CommentRepository commentRepository;
50 | private final EntityManager entityManager;
51 | private final StatClient statClient;
52 |
53 |
54 | @Override
55 | @Transactional
56 | public List getUserEvents(long userId, int from, int size) {
57 | Pageable page = PageRequest.of(from / size, size, Sort.by("id").ascending());
58 | List events = eventRepository.findAllByInitiatorId(userId, page)
59 | .stream()
60 | .map(EventMapper::toEventShortDto)
61 | .collect(Collectors.toList());
62 | fillEventListWithViews(events);
63 | fillEventListWithConfirmedRequests(events);
64 | fillEventListWithComments(events);
65 | return events;
66 | }
67 |
68 | @Override
69 | @Transactional
70 | public EventFullDto createEvent(NewEventDto newEventDto, long userId) {
71 | User initiator = userRepository.findById(userId).orElseThrow();
72 | Category category = categoryRepository.findById(newEventDto.getCategory()).orElseThrow();
73 | Event event = EventMapper.toEvent(newEventDto, initiator, category);
74 | event.setCreatedOn(LocalDateTime.now());
75 | event.setState(EventState.PENDING);
76 | return EventMapper.toEventFullDto(eventRepository.save(event));
77 | }
78 |
79 | @Override
80 | @Transactional
81 | public EventFullDto findUserEventById(long userId, long eventId) {
82 | return EventMapper.toEventFullDto(
83 | eventRepository.findByInitiatorIdAndId(userId, eventId).orElseThrow(),
84 | getConfirmedRequests(eventId),
85 | getSingleEventViews(eventId),
86 | getSingleEventComments(eventId));
87 | }
88 |
89 | @Override
90 | @Transactional
91 | public EventFullDto patchEvent(long userId, long eventId, UpdateEventUserRequest updateRequest) {
92 | Event event = eventRepository.findByInitiatorIdAndId(userId, eventId).orElseThrow();
93 | patchEventState(event, updateRequest);
94 | patchEventDate(event, updateRequest);
95 | patchEventFields(event, updateRequest);
96 | return EventMapper.toEventFullDto(
97 | eventRepository.save(event),
98 | getConfirmedRequests(eventId),
99 | getSingleEventViews(eventId),
100 | getSingleEventComments(eventId));
101 | }
102 |
103 | @Override
104 | @Transactional
105 | public EventFullDto moderateEvent(long eventId, UpdateEventAdminRequest updateRequest) {
106 | Event event = eventRepository.findById(eventId).orElseThrow();
107 | patchEventState(event, updateRequest);
108 | patchEventDate(event, updateRequest);
109 | patchEventFields(event, updateRequest);
110 |
111 | return EventMapper.toEventFullDto(
112 | eventRepository.save(event),
113 | getConfirmedRequests(eventId),
114 | getSingleEventViews(eventId),
115 | getSingleEventComments(eventId));
116 | }
117 |
118 | @Override
119 | @Transactional
120 | public List getAllEventRequests(long userId, long eventId) {
121 | eventRepository.findByInitiatorIdAndId(userId, eventId).orElseThrow(); //just to validate event exists
122 | return requestRepository.findAllByEventId(eventId)
123 | .stream()
124 | .map(RequestMapper::toParticipationRequestDto)
125 | .collect(Collectors.toList());
126 | }
127 |
128 | @Override
129 | @Transactional
130 | public EventRequestStatusUpdateResult updateRequestsStatus(long userId, long eventId, EventRequestStatusUpdateRequest updateRequest) {
131 | Event event = eventRepository.findByInitiatorIdAndId(userId, eventId).orElseThrow();
132 | List requestsToConfirm = requestRepository.findAllByIdIn(updateRequest.getRequestIds());
133 |
134 | if (updateRequest.getStatus() == RequestStatus.REJECTED) {
135 | return rejectAll(requestsToConfirm);
136 | }
137 | validateParticipationLimitNotReached(event);
138 | if (!event.getRequestModeration() || updateRequest.getStatus() == RequestStatus.CONFIRMED) { //if no pre-moderation required - return all as confirmed
139 | return confirmAllUntilLimitReached(event, requestsToConfirm);
140 | }
141 | throw new RequestNotValidException("Request status not valid.");
142 | }
143 |
144 | @Override
145 | @Transactional
146 | public List getAllEventWithParams(SearchParameters parameters) {
147 | Predicate predicate = getCommonPredicate(parameters);
148 | Pageable page = getPageable(parameters.getFrom(), parameters.getSize());
149 | List events = eventRepository.findAll(predicate, page)
150 | .stream()
151 | .map(EventMapper::toEventFullDto)
152 | .collect(Collectors.toList());
153 | fillEventListWithConfirmedRequests(events);
154 | fillEventListWithViews(events);
155 | fillEventListWithComments(events);
156 | return events;
157 | }
158 |
159 | @Override
160 | @Transactional
161 | public List getAllPublicEventsWithParams(SearchParameters parameters, HttpServletRequest request) {
162 | Pageable page = getPageable(parameters.getFrom(), parameters.getSize(), parameters.getSortProperty());
163 | JPAQuery> query = new JPAQuery(entityManager);
164 | statClient.saveEndpointHit("ewm-main-service", "/events", request.getRemoteAddr(), LocalDateTime.now().format(EventMapper.dateTimeFormatter));
165 |
166 | List events = eventRepository.findAll(getPublicPredicate(query, parameters), page)
167 | .stream()
168 | .map(EventMapper::toEventShortDto)
169 | .collect(Collectors.toList());
170 | fillEventListWithConfirmedRequests(events);
171 | fillEventListWithViews(events);
172 | fillEventListWithComments(events);
173 | if (parameters.getSortProperty() == SortProperty.VIEWS) {
174 | events.sort(Comparator.comparingLong(EventShortDto::getViews));
175 | }
176 | return events;
177 | }
178 |
179 | @Override
180 | @Transactional
181 | public EventFullDto getEventById(long eventId, HttpServletRequest request) {
182 | Event event = eventRepository.findByIdAndState(eventId, EventState.PUBLISHED).orElseThrow();
183 | statClient.saveEndpointHit("ewm-main-service", String.format("/events/%d", eventId), request.getRemoteAddr(), LocalDateTime.now().format(EventMapper.dateTimeFormatter));
184 | return EventMapper.toEventFullDto(
185 | event,
186 | getConfirmedRequests(eventId),
187 | getSingleEventViews(eventId),
188 | getSingleEventComments(eventId));
189 | }
190 |
191 | private Event patchEventFields(Event event, UpdateEventRequest updateRequest) {
192 | if (updateRequest.getAnnotation() != null) {
193 | event.setAnnotation(updateRequest.getAnnotation());
194 | }
195 | if (updateRequest.getCategory() != null) {
196 | Category category = categoryRepository.findById(updateRequest.getCategory()).orElseThrow();
197 | event.setCategory(category);
198 | }
199 | if (updateRequest.getDescription() != null) {
200 | event.setDescription(updateRequest.getDescription());
201 | }
202 | if (updateRequest.getLocation() != null) {
203 | event.setLat(updateRequest.getLocation().getLat());
204 | event.setLon(updateRequest.getLocation().getLon());
205 | }
206 | if (updateRequest.getPaid() != null) {
207 | event.setPaid(updateRequest.getPaid());
208 | }
209 | if (updateRequest.getParticipantLimit() != null) {
210 | event.setParticipantLimit(updateRequest.getParticipantLimit());
211 | }
212 | if (updateRequest.getRequestModeration() != null) {
213 | event.setRequestModeration(updateRequest.getRequestModeration());
214 | }
215 | if (updateRequest.getTitle() != null) {
216 | event.setTitle(updateRequest.getTitle());
217 | }
218 | return event;
219 | }
220 |
221 | private void patchEventDate(Event event, UpdateEventUserRequest updateRequest) {
222 | if (updateRequest.getEventDate() != null) {
223 | event.setEventDate(updateRequest.getEventDate());
224 | }
225 | }
226 |
227 | private void patchEventDate(Event event, UpdateEventAdminRequest updateRequest) {
228 | if (event.getEventDate().isBefore(LocalDateTime.now().plusHours(1))) {
229 | throw new ChangeEventStatusForbiddenException("Event will start within 1 hour.");
230 | }
231 | if (updateRequest.getEventDate() != null) {
232 | event.setEventDate(updateRequest.getEventDate());
233 | }
234 | }
235 |
236 | private void patchEventState(Event event, UpdateEventUserRequest updateRequest) {
237 | EventState eventState = event.getState();
238 | if (eventState == EventState.PUBLISHED) {
239 | throw new ChangeEventStatusForbiddenException("Event is already published.");
240 | }
241 | UserEventStateAction stateAction;
242 | if ((stateAction = updateRequest.getStateAction()) != null) {
243 | event.setState(determineEventState(eventState, stateAction));
244 | }
245 | }
246 |
247 | private void patchEventState(Event event, UpdateEventAdminRequest updateRequest) {
248 | EventState eventState = event.getState();
249 | AdminEventStateAction stateAction;
250 | if ((stateAction = updateRequest.getStateAction()) != null) {
251 | event.setState(determineEventState(eventState, stateAction));
252 | }
253 | if (stateAction == AdminEventStateAction.PUBLISH_EVENT) {
254 | event.setPublishedOn(LocalDateTime.now()); //unreachable if event was published earlier (exception in snippet above)
255 | }
256 | }
257 |
258 | private EventState determineEventState(EventState eventState, UserEventStateAction stateAction) {
259 | switch (stateAction) {
260 | case SEND_TO_REVIEW:
261 | if (eventState == EventState.CANCELED || eventState == EventState.PENDING) {
262 | return EventState.PENDING;
263 | }
264 | break;
265 | case CANCEL_REVIEW:
266 | if (eventState == EventState.PENDING) {
267 | return EventState.CANCELED;
268 | }
269 | break;
270 | }
271 | return eventState;
272 | }
273 |
274 | private EventState determineEventState(EventState eventState, AdminEventStateAction stateAction) {
275 | switch (stateAction) {
276 | case PUBLISH_EVENT:
277 | if (eventState == EventState.PENDING) {
278 | return EventState.PUBLISHED;
279 | } else {
280 | throw new ChangeEventStatusForbiddenException("Event is already published or canceled.");
281 | }
282 | case REJECT_EVENT:
283 | if (eventState != EventState.PUBLISHED) {
284 | return EventState.CANCELED;
285 | } else {
286 | throw new ChangeEventStatusForbiddenException("Event is already published.");
287 | }
288 | default:
289 | return null; //unreachable
290 | }
291 | }
292 |
293 | private void validateParticipationLimitNotReached(Event event) {
294 | long confirmedRequestsCount = requestRepository.countByStatusAndEventId(RequestStatus.CONFIRMED, event.getId());
295 | if (confirmedRequestsCount >= event.getParticipantLimit() && event.getParticipantLimit() != 0) {
296 | throw new EventParticipationForbiddenException("Event participation limit reached.");
297 | }
298 | }
299 |
300 | private EventRequestStatusUpdateResult confirmAllUntilLimitReached(Event event, List requestsToConfirm) {
301 | long confirmedRequestsCount = getConfirmedRequests(event.getId());
302 | long participationLimit = event.getParticipantLimit();
303 | int requestPointer = 0;
304 | List confirmedRequests = new ArrayList<>();
305 | List rejectedRequests = new ArrayList<>();
306 |
307 | while ((confirmedRequestsCount < participationLimit || participationLimit == 0) && requestPointer < requestsToConfirm.size()) {
308 | Request request = requestsToConfirm.get(requestPointer);
309 | validateRequestStatusIsPendingOrDefined(request, RequestStatus.CONFIRMED);
310 | request.setStatus(RequestStatus.CONFIRMED);
311 | confirmedRequests.add(request);
312 | requestPointer++;
313 | confirmedRequestsCount++;
314 | }
315 | while (requestPointer < requestsToConfirm.size()) {
316 | Request request = requestsToConfirm.get(requestPointer);
317 | validateRequestStatusIsPendingOrDefined(request, RequestStatus.REJECTED);
318 | request.setStatus(RequestStatus.REJECTED);
319 | rejectedRequests.add(request);
320 | }
321 | List requestsToSave = new ArrayList<>();
322 | requestsToSave.addAll(confirmedRequests);
323 | requestsToSave.addAll(rejectedRequests);
324 | requestRepository.saveAll(requestsToSave);
325 | return new EventRequestStatusUpdateResult(
326 | confirmedRequests.stream().map(RequestMapper::toParticipationRequestDto).collect(Collectors.toList()),
327 | rejectedRequests.stream().map(RequestMapper::toParticipationRequestDto).collect(Collectors.toList()));
328 | }
329 |
330 | private EventRequestStatusUpdateResult rejectAll(List requestsToReject) {
331 | List rejectedRequests = new ArrayList<>();
332 |
333 | for (Request request : requestsToReject) {
334 | validateRequestStatusIsPendingOrDefined(request, RequestStatus.REJECTED);
335 | request.setStatus(RequestStatus.REJECTED);
336 | rejectedRequests.add(request);
337 | }
338 | requestRepository.saveAll(rejectedRequests);
339 | return new EventRequestStatusUpdateResult(
340 | new ArrayList<>(),
341 | rejectedRequests.stream().map(RequestMapper::toParticipationRequestDto).collect(Collectors.toList()));
342 | }
343 |
344 | private void validateRequestStatusIsPendingOrDefined(Request request, RequestStatus status) {
345 | if (request.getStatus() != RequestStatus.PENDING && request.getStatus() != status) {
346 | throw new RequestStatusNotPendingException(String.format("Request status is %s", request.getStatus().name()));
347 | }
348 | }
349 |
350 | private BooleanBuilder getCommonPredicate(SearchParameters parameters) {
351 | QEvent event = QEvent.event;
352 | BooleanBuilder predicate = new BooleanBuilder();
353 |
354 | if (parameters.getUsers() != null) {
355 | predicate.and(event.initiator.id.in(parameters.getUsers()));
356 | }
357 | if (parameters.getText() != null) {
358 | predicate.and(event.annotation.containsIgnoreCase(parameters.getText()).or(event.description.containsIgnoreCase(parameters.getText())));
359 | }
360 | if (parameters.getPaid() != null) {
361 | predicate.and(event.paid.eq(parameters.getPaid()));
362 | }
363 | if (parameters.getStates() != null) {
364 | predicate.and(event.state.in(parameters.getStates()));
365 | }
366 | if (parameters.getCategories() != null) {
367 | predicate.and(event.category.id.in(parameters.getCategories()));
368 | }
369 | if (parameters.getRangeStart() != null) {
370 | predicate.and(event.eventDate.after(parameters.getRangeStart()));
371 | }
372 | if (parameters.getRangeEnd() != null) {
373 | predicate.and(event.eventDate.before(parameters.getRangeEnd()));
374 | }
375 | return predicate;
376 | }
377 |
378 | private BooleanBuilder getPublicPredicate(JPAQuery> query, SearchParameters parameters) {
379 | BooleanBuilder predicate = getCommonPredicate(parameters);
380 | QEvent event = QEvent.event;
381 |
382 | if (parameters.getOnlyAvailable()) {
383 | predicate.and(event.id.in(getAvailableEventsIds(query)));
384 | }
385 |
386 | if (parameters.getRangeStart() == null) {
387 | predicate.and(event.eventDate.after(LocalDateTime.now()));
388 | }
389 |
390 | predicate.and(event.state.eq(EventState.PUBLISHED)); //to return only published events
391 | return predicate;
392 | }
393 |
394 | private Pageable getPageable(int from, int size) {
395 | return PageRequest.of(from / size, size, Sort.by("id").ascending());
396 | }
397 |
398 | private Pageable getPageable(int from, int size, SortProperty sortProperty) {
399 | if (sortProperty == null || sortProperty == SortProperty.VIEWS) {
400 | return getPageable(from, size);
401 | }
402 | return PageRequest.of(from / size, size, Sort.by("event_date").ascending());
403 | }
404 |
405 | List getAvailableEventsIds(JPAQuery> query) {
406 | QRequest request = QRequest.request;
407 | NumberExpression id = request.event.id.as("id");
408 | NumberExpression limit = request.event.participantLimit.as("limit");
409 | NumberExpression confirmed = request.status.count().as("confirmed");
410 |
411 | return query.select(id, limit, confirmed) //get all event ids with participation available
412 | .from(request)
413 | .where(request.status.eq(RequestStatus.CONFIRMED))
414 | .groupBy(id)
415 | .having(limit.gt(confirmed))
416 | .fetch()
417 | .stream()
418 | .map(tuple -> tuple.get(id))
419 | .collect(Collectors.toList());
420 | }
421 |
422 | private void fillEventListWithConfirmedRequests(List extends EventShortDto> events) {
423 | HashMap eventsMap = new HashMap<>();
424 | for (EventShortDto dto : events) {
425 | eventsMap.put(dto.getId(), dto);
426 | }
427 |
428 | List ids = events.stream().map(EventShortDto::getId).collect(Collectors.toList());
429 | JPAQuery> query = new JPAQuery(entityManager);
430 | QRequest request = QRequest.request;
431 | query
432 | .select(request.event.id, request.status.count())
433 | .from(request)
434 | .where(request.event.id.in(ids).and(request.status.eq(RequestStatus.CONFIRMED)))
435 | .groupBy(request.event.id)
436 | .fetch()
437 | .forEach(tuple -> {
438 | long eventId = tuple.get(request.event.id);
439 | eventsMap.get(eventId).setConfirmedRequests(tuple.get(request.status.count()));
440 | });
441 | }
442 |
443 | private long getConfirmedRequests(long eventId) {
444 | return requestRepository.countByStatusAndEventId(RequestStatus.CONFIRMED, eventId);
445 | }
446 |
447 | private void fillEventListWithComments(List extends EventShortDto> events) {
448 | HashMap eventsMap = new HashMap<>();
449 | for (EventShortDto dto : events) {
450 | eventsMap.put(dto.getId(), dto);
451 | }
452 |
453 | List ids = events.stream().map(EventShortDto::getId).collect(Collectors.toList());
454 |
455 | JPAQuery> query = new JPAQuery(entityManager);
456 | QComment comment = QComment.comment;
457 | query
458 | .select(comment.event.id, comment.id.count())
459 | .from(comment)
460 | .where(comment.event.id.in(ids))
461 | .groupBy(comment.event.id)
462 | .fetch()
463 | .forEach(tuple -> {
464 | long eventId = tuple.get(comment.event.id);
465 | eventsMap.get(eventId).setComments(tuple.get(comment.id.count()));
466 | });
467 | }
468 |
469 | private long getSingleEventComments(long eventId) {
470 | return commentRepository.countByEventId(eventId);
471 | }
472 |
473 | private void fillEventListWithViews(List extends EventShortDto> events) {
474 | List eventIds = events.stream().map(EventShortDto::getId).collect(Collectors.toList());
475 | Map eventViewsMap = getEventViewsMap(eventIds);
476 |
477 | for (EventShortDto event : events) {
478 | Long views = eventViewsMap.get(event.getId());
479 | event.setViews(views != null ? views : 0);
480 | }
481 | }
482 |
483 | private Map getEventViewsMap(List eventIds) {
484 | List uris = eventIds.stream().map(id -> String.format("/events/%d", id)).collect(Collectors.toList());
485 | ViewStats[] viewStats = statClient.getStat(LocalDateTime.of(2020, 1, 1, 1, 1), LocalDateTime.now(), uris, true).getBody();
486 | HashMap eventsViews = new HashMap<>();
487 | for (ViewStats viewStat : viewStats) {
488 | long eventId = Long.parseLong(viewStat.getUri().split("/")[2]);
489 | eventsViews.put(eventId, viewStat.getHits());
490 | }
491 | return eventsViews;
492 | }
493 |
494 | private long getSingleEventViews(long eventId) {
495 | return getSingleEventViews(eventId, null, null);
496 | }
497 |
498 | private long getSingleEventViews(long eventId, LocalDateTime start, LocalDateTime end) {
499 | start = start != null ? start : LocalDateTime.of(2020, 1, 1, 1, 1);
500 | end = end != null ? end : LocalDateTime.now();
501 | ViewStats[] viewStats = statClient.getStat(start, end, List.of(String.format("/events/%d", eventId)), true).getBody();
502 | return viewStats.length > 0 ? viewStats[0].getHits() : 0;
503 | }
504 | }
--------------------------------------------------------------------------------
/ewm-service/src/main/java/ru/practicum/exception/ApiError.java:
--------------------------------------------------------------------------------
1 | package ru.practicum.exception;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | import java.time.LocalDateTime;
9 | import java.util.List;
10 |
11 | @AllArgsConstructor
12 | @NoArgsConstructor
13 | @Data
14 | public class ApiError {
15 | private List