diff --git a/README.md b/README.md index 18a246e..648d936 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # java-explore-with-me -Template repository for ExploreWithMe project. +[Ссылка на пул-реквест](https://github.com/MrGriv/java-explore-with-me/pull/3) diff --git a/ewm-service/src/main/java/ru/practicum/controller/ErrorHandler.java b/ewm-service/src/main/java/ru/practicum/controller/ErrorHandler.java index a35eb50..127657a 100644 --- a/ewm-service/src/main/java/ru/practicum/controller/ErrorHandler.java +++ b/ewm-service/src/main/java/ru/practicum/controller/ErrorHandler.java @@ -1,5 +1,6 @@ package ru.practicum.controller; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; @@ -14,11 +15,13 @@ import java.time.format.DateTimeFormatter; import java.util.Arrays; +@Slf4j @RestControllerAdvice public class ErrorHandler { @ExceptionHandler @ResponseStatus(HttpStatus.CONFLICT) public ApiError handleConflictException(final ConflictException e) { + log.debug("Получен статус 409 CONFLICT {}", e.getMessage(), e); return new ApiError(e.getMessage(), HttpStatus.CONFLICT.toString(), Arrays.toString(e.getStackTrace()), @@ -28,6 +31,7 @@ public ApiError handleConflictException(final ConflictException e) { @ExceptionHandler @ResponseStatus(HttpStatus.NOT_FOUND) public ApiError handleNotFoundException(final NotFoundException e) { + log.debug("Получен статус 404 NOT FOUND {}", e.getMessage(), e); return new ApiError(e.getMessage(), HttpStatus.NOT_FOUND.toString(), Arrays.toString(e.getStackTrace()), @@ -37,6 +41,7 @@ public ApiError handleNotFoundException(final NotFoundException e) { @ExceptionHandler @ResponseStatus(HttpStatus.CONFLICT) public ApiError handleSqlException(final SQLException e) { + log.debug("Получен статус 409 CONFLICT {}", e.getMessage(), e); return new ApiError(e.getMessage(), HttpStatus.CONFLICT.toString(), Arrays.toString(e.getStackTrace()), @@ -46,6 +51,7 @@ public ApiError handleSqlException(final SQLException e) { @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ApiError handleBadRequestException(final BadRequestException e) { + log.debug("Получен статус 400 BAD REQUEST {}", e.getMessage(), e); return new ApiError(e.getMessage(), HttpStatus.BAD_REQUEST.toString(), Arrays.toString(e.getStackTrace()), diff --git a/ewm-service/src/main/java/ru/practicum/controller/admin/AdminCategoryController.java b/ewm-service/src/main/java/ru/practicum/controller/admin/AdminCategoryController.java index 763e1ea..4ed8271 100644 --- a/ewm-service/src/main/java/ru/practicum/controller/admin/AdminCategoryController.java +++ b/ewm-service/src/main/java/ru/practicum/controller/admin/AdminCategoryController.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import ru.practicum.dto.category.CategoryDto; import ru.practicum.dto.category.NewCategoryDto; @@ -10,7 +11,7 @@ import javax.validation.Valid; -@RestController +@Controller @RequiredArgsConstructor @RequestMapping(ApiPathConstants.ADMIN_CATEGORY_PATH) public class AdminCategoryController { @@ -27,7 +28,7 @@ public ResponseEntity delete(@PathVariable Long id) { } @PatchMapping(ApiPathConstants.BY_ID_PATH) - public CategoryDto update(@PathVariable Long id, + public ResponseEntity update(@PathVariable Long id, @Valid @RequestBody NewCategoryDto newCategoryDto) { return categoryService.update(id, newCategoryDto); } diff --git a/ewm-service/src/main/java/ru/practicum/controller/admin/AdminCompilationsController.java b/ewm-service/src/main/java/ru/practicum/controller/admin/AdminCompilationsController.java index 0e10cfd..516bfb4 100644 --- a/ewm-service/src/main/java/ru/practicum/controller/admin/AdminCompilationsController.java +++ b/ewm-service/src/main/java/ru/practicum/controller/admin/AdminCompilationsController.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import ru.practicum.dto.compilation.CompilationDto; import ru.practicum.dto.compilation.NewCompilationDto; @@ -11,7 +12,7 @@ import javax.validation.Valid; -@RestController +@Controller @RequiredArgsConstructor @RequestMapping(ApiPathConstants.ADMIN_COMPILATIONS_PATH) public class AdminCompilationsController { @@ -28,7 +29,7 @@ public ResponseEntity delete(@PathVariable Long id) { } @PatchMapping(ApiPathConstants.BY_ID_PATH) - public CompilationDto update(@Valid @RequestBody UpdateCompilationRequest updateCompilation, + public ResponseEntity update(@Valid @RequestBody UpdateCompilationRequest updateCompilation, @PathVariable Long id) { return compilationService.update(updateCompilation, id); } diff --git a/ewm-service/src/main/java/ru/practicum/controller/admin/AdminUserController.java b/ewm-service/src/main/java/ru/practicum/controller/admin/AdminUserController.java index cb8b28d..3a7883a 100644 --- a/ewm-service/src/main/java/ru/practicum/controller/admin/AdminUserController.java +++ b/ewm-service/src/main/java/ru/practicum/controller/admin/AdminUserController.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import ru.practicum.dto.user.UserDto; import ru.practicum.service.UserService; @@ -10,7 +11,7 @@ import javax.validation.Valid; import java.util.List; -@RestController +@Controller @RequiredArgsConstructor @RequestMapping(ApiPathConstants.ADMIN_USERS_PATH) public class AdminUserController { @@ -22,7 +23,7 @@ public ResponseEntity add(@Valid @RequestBody UserDto userDto) { } @GetMapping - public List get(@RequestParam(defaultValue = "") List ids, + public ResponseEntity> get(@RequestParam(defaultValue = "") List ids, @RequestParam(defaultValue = "0") int from, @RequestParam(defaultValue = "10") int size) { return userService.get(ids, from, size); diff --git a/ewm-service/src/main/java/ru/practicum/controller/pvt/PrivateFriendController.java b/ewm-service/src/main/java/ru/practicum/controller/pvt/PrivateFriendController.java new file mode 100644 index 0000000..81cd64b --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/controller/pvt/PrivateFriendController.java @@ -0,0 +1,37 @@ +package ru.practicum.controller.pvt; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import ru.practicum.dto.event.EventFullDto; +import ru.practicum.model.user.ShowEventsState; +import ru.practicum.service.FriendService; +import ru.practicum.util.ApiPathConstants; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping(ApiPathConstants.USER_ID_FRIEND_PATH) +public class PrivateFriendController { + private final FriendService friendService; + + @PostMapping(ApiPathConstants.FRIEND_ID) + public void add(@PathVariable Long id, @PathVariable Long friendId) { + friendService.add(id, friendId); + } + + @GetMapping(ApiPathConstants.FRIEND_ID) + public List getFriendParticipations(@PathVariable Long id, + @PathVariable Long friendId, + @RequestParam(defaultValue = "0") int from, + @RequestParam(defaultValue = "10") int size) { + return friendService.getFriendParticipation(id, friendId, from, size); + } + + @PatchMapping + public void changeEventsVisibility(@PathVariable Long id, + @RequestParam ShowEventsState state, + @RequestParam(required = false) List events) { + friendService.changeEventsVisibility(id, state, events); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/dto/user/UserDto.java b/ewm-service/src/main/java/ru/practicum/dto/user/UserDto.java index 0cda114..fa830ff 100644 --- a/ewm-service/src/main/java/ru/practicum/dto/user/UserDto.java +++ b/ewm-service/src/main/java/ru/practicum/dto/user/UserDto.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import ru.practicum.model.user.ShowEventsState; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; @@ -24,4 +25,5 @@ public class UserDto { @NotEmpty @Size(min = 6, max = 254) private String email; + private ShowEventsState showEventsState; } diff --git a/ewm-service/src/main/java/ru/practicum/mapper/UserMapper.java b/ewm-service/src/main/java/ru/practicum/mapper/UserMapper.java index 3b81527..4608ce0 100644 --- a/ewm-service/src/main/java/ru/practicum/mapper/UserMapper.java +++ b/ewm-service/src/main/java/ru/practicum/mapper/UserMapper.java @@ -2,7 +2,7 @@ import org.mapstruct.Mapper; import ru.practicum.dto.user.UserDto; -import ru.practicum.model.User; +import ru.practicum.model.user.User; @Mapper(componentModel = "spring") public interface UserMapper { diff --git a/ewm-service/src/main/java/ru/practicum/model/User.java b/ewm-service/src/main/java/ru/practicum/model/User.java deleted file mode 100644 index 65cc085..0000000 --- a/ewm-service/src/main/java/ru/practicum/model/User.java +++ /dev/null @@ -1,23 +0,0 @@ -package ru.practicum.model; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import javax.persistence.*; - -@Entity -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@Table(name = "users") -public class User { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "user_id") - private Long id; - private String name; - private String email; -} diff --git a/ewm-service/src/main/java/ru/practicum/model/event/Event.java b/ewm-service/src/main/java/ru/practicum/model/event/Event.java index 3accca3..c8fbde8 100644 --- a/ewm-service/src/main/java/ru/practicum/model/event/Event.java +++ b/ewm-service/src/main/java/ru/practicum/model/event/Event.java @@ -5,7 +5,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import ru.practicum.model.LocationDb; -import ru.practicum.model.User; +import ru.practicum.model.user.User; import javax.persistence.*; import java.time.LocalDateTime; diff --git a/ewm-service/src/main/java/ru/practicum/model/request/Request.java b/ewm-service/src/main/java/ru/practicum/model/request/Request.java index e481ae9..768cae9 100644 --- a/ewm-service/src/main/java/ru/practicum/model/request/Request.java +++ b/ewm-service/src/main/java/ru/practicum/model/request/Request.java @@ -4,7 +4,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import ru.practicum.model.User; +import ru.practicum.model.user.User; import ru.practicum.model.event.Event; import javax.persistence.*; diff --git a/ewm-service/src/main/java/ru/practicum/model/user/ShowEventsState.java b/ewm-service/src/main/java/ru/practicum/model/user/ShowEventsState.java new file mode 100644 index 0000000..5b14c00 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/model/user/ShowEventsState.java @@ -0,0 +1,7 @@ +package ru.practicum.model.user; + +public enum ShowEventsState { + ALL, + CHOSEN, + HIDE +} diff --git a/ewm-service/src/main/java/ru/practicum/model/user/User.java b/ewm-service/src/main/java/ru/practicum/model/user/User.java new file mode 100644 index 0000000..432c6c4 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/model/user/User.java @@ -0,0 +1,35 @@ +package ru.practicum.model.user; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; +import java.util.List; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "user_id") + private Long id; + private String name; + private String email; + @ElementCollection + @CollectionTable(name = "friends", joinColumns = @JoinColumn(name = "user_id")) + @Column(name = "friend_id") + private List friends; + @Enumerated(EnumType.STRING) + @Column(name = "show_event_state") + private ShowEventsState showEventsState; + @ElementCollection + @CollectionTable(name = "users_events", joinColumns = @JoinColumn(name = "user_id")) + @Column(name = "event_id") + private List userEvents; +} diff --git a/ewm-service/src/main/java/ru/practicum/service/CategoryService.java b/ewm-service/src/main/java/ru/practicum/service/CategoryService.java index 606989d..232e6f9 100644 --- a/ewm-service/src/main/java/ru/practicum/service/CategoryService.java +++ b/ewm-service/src/main/java/ru/practicum/service/CategoryService.java @@ -11,7 +11,7 @@ public interface CategoryService { ResponseEntity delete(Long categoryId); - CategoryDto update(Long id, NewCategoryDto newCategoryDto); + ResponseEntity update(Long id, NewCategoryDto newCategoryDto); List get(int from, int size); diff --git a/ewm-service/src/main/java/ru/practicum/service/CompilationService.java b/ewm-service/src/main/java/ru/practicum/service/CompilationService.java index d2a6bb9..b2c1881 100644 --- a/ewm-service/src/main/java/ru/practicum/service/CompilationService.java +++ b/ewm-service/src/main/java/ru/practicum/service/CompilationService.java @@ -12,7 +12,7 @@ public interface CompilationService { ResponseEntity delete(Long compilationId); - CompilationDto update(UpdateCompilationRequest updateCompilation, Long compilationId); + ResponseEntity update(UpdateCompilationRequest updateCompilation, Long compilationId); List get(Boolean pinned, int from, int size); diff --git a/ewm-service/src/main/java/ru/practicum/service/FriendService.java b/ewm-service/src/main/java/ru/practicum/service/FriendService.java new file mode 100644 index 0000000..b2fa57e --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/service/FriendService.java @@ -0,0 +1,14 @@ +package ru.practicum.service; + +import ru.practicum.dto.event.EventFullDto; +import ru.practicum.model.user.ShowEventsState; + +import java.util.List; + +public interface FriendService { + void add(Long userId, Long friendId); + + List getFriendParticipation(Long userId, Long friendId, int from, int size); + + void changeEventsVisibility(Long userId, ShowEventsState state, List events); +} diff --git a/ewm-service/src/main/java/ru/practicum/service/UserService.java b/ewm-service/src/main/java/ru/practicum/service/UserService.java index 65a5c59..617887b 100644 --- a/ewm-service/src/main/java/ru/practicum/service/UserService.java +++ b/ewm-service/src/main/java/ru/practicum/service/UserService.java @@ -8,7 +8,7 @@ public interface UserService { ResponseEntity add(UserDto userDto); - List get(List ids, int from, int size); + ResponseEntity> get(List ids, int from, int size); ResponseEntity delete(Long userId); } diff --git a/ewm-service/src/main/java/ru/practicum/service/impl/CategoryServiceImpl.java b/ewm-service/src/main/java/ru/practicum/service/impl/CategoryServiceImpl.java index 3fa85e6..e073f5b 100644 --- a/ewm-service/src/main/java/ru/practicum/service/impl/CategoryServiceImpl.java +++ b/ewm-service/src/main/java/ru/practicum/service/impl/CategoryServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ru.practicum.dto.category.CategoryDto; import ru.practicum.dto.category.NewCategoryDto; import ru.practicum.exception.NotFoundException; @@ -18,6 +19,7 @@ import java.util.List; @Service +@Transactional @RequiredArgsConstructor public class CategoryServiceImpl implements CategoryService { private final CategoryStorage categoryStorage; @@ -37,17 +39,18 @@ public ResponseEntity delete(Long id) { } @Override - public CategoryDto update(Long id, NewCategoryDto newCategoryDto) { + public ResponseEntity update(Long id, NewCategoryDto newCategoryDto) { Category updatedCategory = categoryStorage.findById(id) .orElseThrow(() -> new NotFoundException("Category: Категория с id=" + id + " не найдена")); if (!updatedCategory.getName().equals(newCategoryDto.getName())) { updatedCategory.setName(newCategoryDto.getName()); - return categoryMapper.toDto(categoryStorage.save(updatedCategory)); + return new ResponseEntity<>(categoryMapper.toDto(categoryStorage.save(updatedCategory)), HttpStatus.OK); } - return categoryMapper.toDto(updatedCategory); + return new ResponseEntity<>(categoryMapper.toDto(updatedCategory), HttpStatus.OK); } @Override + @Transactional(readOnly = true) public List get(int from, int size) { PageRequest page = PageRequest.of(from > 0 ? from / size : 0, size); Page categories = categoryStorage.findAll(page); diff --git a/ewm-service/src/main/java/ru/practicum/service/impl/CompilationServiceImpl.java b/ewm-service/src/main/java/ru/practicum/service/impl/CompilationServiceImpl.java index 4a37c9a..299f9b3 100644 --- a/ewm-service/src/main/java/ru/practicum/service/impl/CompilationServiceImpl.java +++ b/ewm-service/src/main/java/ru/practicum/service/impl/CompilationServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ru.practicum.dto.compilation.CompilationDto; import ru.practicum.dto.compilation.NewCompilationDto; import ru.practicum.dto.compilation.UpdateCompilationRequest; @@ -26,6 +27,7 @@ import java.util.stream.Collectors; @Service +@Transactional @RequiredArgsConstructor public class CompilationServiceImpl implements CompilationService { private final CompilationStorage compilationStorage; @@ -50,7 +52,7 @@ public ResponseEntity delete(Long compilationId) { } @Override - public CompilationDto update(UpdateCompilationRequest updateCompilation, + public ResponseEntity update(UpdateCompilationRequest updateCompilation, Long compilationId) { Compilation compilation = compilationStorage.findById(compilationId) .orElseThrow(() -> new NotFoundException("Compilation: Список событий с id=" + compilationId + @@ -65,10 +67,11 @@ public CompilationDto update(UpdateCompilationRequest updateCompilation, List events = eventStorage.findAllByIdIn(savedCompilation.getEvents()); CompilationDto compilationDto = compilationMapper.toDto(savedCompilation); compilationDto.setEvents(events.stream().map(eventMapper::toShortDto).collect(Collectors.toList())); - return compilationDto; + return new ResponseEntity<>(compilationDto, HttpStatus.OK); } @Override + @Transactional(readOnly = true) public List get(Boolean pinned, int from, int size) { PageRequest page = PageRequest.of(from > 0 ? from / size : 0, size); Page compilations; diff --git a/ewm-service/src/main/java/ru/practicum/service/impl/EventServiceImpl.java b/ewm-service/src/main/java/ru/practicum/service/impl/EventServiceImpl.java index a0edf8c..c2adcfa 100644 --- a/ewm-service/src/main/java/ru/practicum/service/impl/EventServiceImpl.java +++ b/ewm-service/src/main/java/ru/practicum/service/impl/EventServiceImpl.java @@ -9,6 +9,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ru.practicum.StatsClient; import ru.practicum.ViewStats; import ru.practicum.dto.Location; @@ -26,7 +27,8 @@ import ru.practicum.mapper.RequestMapper; import ru.practicum.model.Category; import ru.practicum.model.LocationDb; -import ru.practicum.model.User; +import ru.practicum.model.user.ShowEventsState; +import ru.practicum.model.user.User; import ru.practicum.model.event.*; import ru.practicum.model.request.Request; import ru.practicum.model.request.RequestStatus; @@ -42,6 +44,7 @@ @Slf4j @Service +@Transactional @RequiredArgsConstructor public class EventServiceImpl implements EventService { private final EventStorage eventStorage; @@ -83,6 +86,7 @@ public ResponseEntity add(NewEventDto newEventDto, Long userId) { } @Override + @Transactional(readOnly = true) public List get(Long userId, int from, int size) { User initiator = userStorage.findById(userId) .orElseThrow(() -> new NotFoundException("User: Пользователь с id=" + userId + " не найден")); @@ -133,6 +137,7 @@ public EventFullDto userUpdate(UpdateEventUserRequestDto updateEventDto, Long us } @Override + @Transactional(readOnly = true) public List getInitiatorRequests(Long userId, Long eventId) { User initiator = userStorage.findById(userId) .orElseThrow(() -> new NotFoundException("User: Пользователь с id=" + userId + " не найден")); @@ -161,6 +166,7 @@ public EventRequestStatusUpdateResult changeRequestStatus(Long userId, int limit = event.getParticipantLimit(); List confirmedRequests = new ArrayList<>(); List rejectedRequests = new ArrayList<>(); + List userIds = new ArrayList<>(); if (event.getParticipantLimit() == 0 || !event.getRequestModeration()) { for (Request request : requests) { if (!request.getStatus().equals(RequestStatus.PENDING)) { @@ -171,7 +177,8 @@ public EventRequestStatusUpdateResult changeRequestStatus(Long userId, amountConfirmedRequests, confirmedRequests, rejectedRequests, - request); + request, + userIds); } } for (Request request : requests) { @@ -184,11 +191,25 @@ public EventRequestStatusUpdateResult changeRequestStatus(Long userId, amountConfirmedRequests, confirmedRequests, rejectedRequests, - request); + request, + userIds); } else { rejectedRequests.add(request); } } + List users = userStorage.findAllById(userIds); + if (!users.isEmpty()) { + List usersForSave = new ArrayList<>(); + for (User user : users) { + if (user.getShowEventsState().equals(ShowEventsState.ALL)) { + List events = user.getUserEvents().isEmpty() ? new ArrayList<>() : user.getUserEvents(); + events.add(event.getId()); + user.setUserEvents(events); + usersForSave.add(user); + } + } + userStorage.saveAll(usersForSave); + } event.setConfirmedRequests(amountConfirmedRequests); eventStorage.save(event); requestStorage.saveAll(rejectedRequests); @@ -203,11 +224,13 @@ private int setRequestsStatus(EventRequestStatusUpdateRequest incomingRequests, int amountConfirmedRequests, List confirmedRequests, List rejectedRequests, - Request request) { + Request request, + List users) { if (incomingRequests.getStatus().equals(RequestStatus.CONFIRMED)) { request.setStatus(RequestStatus.CONFIRMED); confirmedRequests.add(request); ++amountConfirmedRequests; + users.add(request.getRequester().getId()); } else { request.setStatus(RequestStatus.REJECTED); rejectedRequests.add(request); @@ -217,6 +240,7 @@ private int setRequestsStatus(EventRequestStatusUpdateRequest incomingRequests, //Admin @Override + @Transactional(readOnly = true) public List getWithFilters(List users, List states, List categories, @@ -228,10 +252,16 @@ public List getWithFilters(List users, GetAdminEvent getAdminEvent = new GetAdminEvent(users, states, categories, rangeStart, rangeEnd); BooleanExpression conditions = makeAdminEventQueryFilters(getAdminEvent); Page events = eventStorage.findAll(conditions, page); - return events.isEmpty() ? new ArrayList<>() : setCategoriesAndReturnList(events.toList()); + return events.isEmpty() ? new ArrayList<>() : setCategoriesAndReturnList(events.toList(), + categoryStorage, + categoryMapper, + eventMapper); } - private List setCategoriesAndReturnList(List events) { + protected static List setCategoriesAndReturnList(List events, + CategoryStorage categoryStorage, + CategoryMapper categoryMapper, + EventMapper eventMapper) { List categoriesId = new ArrayList<>(); for (Event event : events) { categoriesId.add(event.getCategory()); @@ -244,6 +274,7 @@ private List setCategoriesAndReturnList(List events) { .collect(Collectors.toList()); } + @Override public EventFullDto adminUpdate(UpdateEventAdminRequestDto updateEventDto, Long eventId) { if (updateEventDto.getEventDate() != null) { @@ -318,6 +349,7 @@ private void setEventFields(Event event, //Public @Override + @Transactional(readOnly = true) public List userGetByFilters(String text, List categories, Boolean paid, @@ -406,7 +438,7 @@ public static BooleanExpression makeUserEventQueryFilters(GetUserEvent getUserEv List conditions = new ArrayList<>(); if (getUserEvent.getText() != null) { - String textToSearch = getUserEvent.getText();; + String textToSearch = getUserEvent.getText(); conditions.add(qEvent.title.containsIgnoreCase(textToSearch) .or(qEvent.annotation.containsIgnoreCase(textToSearch)) .or(qEvent.description.containsIgnoreCase(textToSearch))); diff --git a/ewm-service/src/main/java/ru/practicum/service/impl/FriendServiceImpl.java b/ewm-service/src/main/java/ru/practicum/service/impl/FriendServiceImpl.java new file mode 100644 index 0000000..0407a95 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/service/impl/FriendServiceImpl.java @@ -0,0 +1,116 @@ +package ru.practicum.service.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.dto.event.EventFullDto; +import ru.practicum.exception.ConflictException; +import ru.practicum.exception.NotFoundException; +import ru.practicum.mapper.CategoryMapper; +import ru.practicum.mapper.EventMapper; +import ru.practicum.model.event.Event; +import ru.practicum.model.request.Request; +import ru.practicum.model.user.ShowEventsState; +import ru.practicum.model.user.User; +import ru.practicum.service.FriendService; +import ru.practicum.storage.CategoryStorage; +import ru.practicum.storage.EventStorage; +import ru.practicum.storage.RequestStorage; +import ru.practicum.storage.UserStorage; + +import java.util.ArrayList; +import java.util.List; + +import static ru.practicum.service.impl.EventServiceImpl.setCategoriesAndReturnList; + +@Service +@Transactional +@RequiredArgsConstructor +public class FriendServiceImpl implements FriendService { + private final UserStorage userStorage; + private final EventStorage eventStorage; + private final CategoryStorage categoryStorage; + private final CategoryMapper categoryMapper; + private final EventMapper eventMapper; + private final RequestStorage requestStorage; + + @Override + public void add(Long userId, Long friendId) { + User user = userStorage.findById(userId) + .orElseThrow(() -> new NotFoundException("User: Пользователь с id=" + userId + " не найден")); + User friend = userStorage.findById(friendId) + .orElseThrow(() -> new NotFoundException("User: Пользователь с id=" + friendId + " не найден")); + List userFriends = user.getFriends().isEmpty() ? new ArrayList<>() : user.getFriends(); + userFriends.add(friend.getId()); + user.setFriends(userFriends); + userStorage.save(user); + List friendFriends = friend.getFriends().isEmpty() ? new ArrayList<>() : friend.getFriends(); + friendFriends.add(user.getId()); + friend.setFriends(friendFriends); + userStorage.save(friend); + } + + @Override + @Transactional(readOnly = true) + public List getFriendParticipation(Long userId, Long friendId, int from, int size) { + User user = userStorage.findById(userId) + .orElseThrow(() -> new NotFoundException("User: Пользователь с id=" + userId + " не найден")); + User friend = userStorage.findById(friendId) + .orElseThrow(() -> new NotFoundException("User: Пользователь с id=" + friendId + " не найден")); + if (!friend.getFriends().contains(user.getId())) { + throw new ConflictException("Friends: Пользователь с id=" + userId + + "нет в друзьях у пользователя c id=" + friend); + } + PageRequest page = PageRequest.of(from > 0 ? from / size : 0, size); + if (friend.getShowEventsState().equals(ShowEventsState.ALL) || + friend.getShowEventsState().equals(ShowEventsState.CHOSEN)) { + Page events = eventStorage.findAllByIdIn(friend.getUserEvents(), page); + return events.isEmpty() ? new ArrayList<>() : setCategoriesAndReturnList(events.toList(), + categoryStorage, + categoryMapper, + eventMapper); + } else { + throw new ConflictException("Friend: пользователь скрыл посещаемые события"); + } + } + + @Override + public void changeEventsVisibility(Long userId, ShowEventsState state, List events) { + User user = userStorage.findById(userId) + .orElseThrow(() -> new NotFoundException("User: Пользователь с id=" + userId + " не найден")); + if (state.equals(ShowEventsState.ALL)) { + List requests = requestStorage.findAllByRequesterAnsStatusConfirmed(user.getId()); + if (!requests.isEmpty()) { + List userEvents = new ArrayList<>(); + for (Request request : requests) { + userEvents.add(request.getEvent().getId()); + } + user.setShowEventsState(ShowEventsState.ALL); + user.setUserEvents(userEvents); + userStorage.save(user); + } + } else if (state.equals(ShowEventsState.CHOSEN)) { + if (events == null || events.isEmpty()) { + throw new ConflictException("User: При смене статуса на CHOSEN нужно выбрать события" + + " для отображения друзьям"); + } + List requests = requestStorage.findAllByEventId(events, user.getId()); + + if (requests.isEmpty()) { + throw new ConflictException("User: в списке событий не обнаружены указанные события: " + events); + } + if (requests.size() != events.size()) { + throw new ConflictException("User: Не все события из списка будут или были посещены пользователем"); + } + user.setUserEvents(events); + user.setShowEventsState(ShowEventsState.CHOSEN); + userStorage.save(user); + } else { + user.setShowEventsState(ShowEventsState.HIDE); + user.setUserEvents(new ArrayList<>()); + userStorage.save(user); + } + } +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/service/impl/RequestServiceImpl.java b/ewm-service/src/main/java/ru/practicum/service/impl/RequestServiceImpl.java index dfd2f2c..0918c23 100644 --- a/ewm-service/src/main/java/ru/practicum/service/impl/RequestServiceImpl.java +++ b/ewm-service/src/main/java/ru/practicum/service/impl/RequestServiceImpl.java @@ -1,15 +1,16 @@ package ru.practicum.service.impl; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ru.practicum.dto.request.ParticipationRequestDto; import ru.practicum.exception.ConflictException; import ru.practicum.exception.NotFoundException; import ru.practicum.mapper.RequestMapper; -import ru.practicum.model.User; +import ru.practicum.model.user.ShowEventsState; +import ru.practicum.model.user.User; import ru.practicum.model.event.Event; import ru.practicum.model.event.EventState; import ru.practicum.model.request.Request; @@ -25,7 +26,7 @@ import java.util.stream.Collectors; @Service -@Slf4j +@Transactional @RequiredArgsConstructor public class RequestServiceImpl implements RequestService { private final RequestStorage requestStorage; @@ -62,6 +63,12 @@ public ResponseEntity add(Long userId, Long eventId) { request.setRequester(requester); if (event.getParticipantLimit() == 0 || !event.getRequestModeration()) { request.setStatus(RequestStatus.CONFIRMED); + if (requester.getShowEventsState().equals(ShowEventsState.ALL)) { + List events = requester.getUserEvents().isEmpty() ? new ArrayList<>() : requester.getUserEvents(); + events.add(event.getId()); + requester.setUserEvents(events); + userStorage.save(requester); + } } else { request.setStatus(RequestStatus.PENDING); } @@ -73,6 +80,7 @@ public ResponseEntity add(Long userId, Long eventId) { } @Override + @Transactional(readOnly = true) public List get(Long userId) { User requester = userStorage.findById(userId) .orElseThrow(() -> new NotFoundException("User: Пользователь с id=" + userId + " не найден")); diff --git a/ewm-service/src/main/java/ru/practicum/service/impl/UserServiceImpl.java b/ewm-service/src/main/java/ru/practicum/service/impl/UserServiceImpl.java index 18bf9a2..3639f57 100644 --- a/ewm-service/src/main/java/ru/practicum/service/impl/UserServiceImpl.java +++ b/ewm-service/src/main/java/ru/practicum/service/impl/UserServiceImpl.java @@ -6,9 +6,11 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ru.practicum.dto.user.UserDto; import ru.practicum.mapper.UserMapper; -import ru.practicum.model.User; +import ru.practicum.model.user.ShowEventsState; +import ru.practicum.model.user.User; import ru.practicum.service.UserService; import ru.practicum.storage.UserStorage; @@ -16,6 +18,7 @@ import java.util.List; @Service +@Transactional @RequiredArgsConstructor public class UserServiceImpl implements UserService { private final UserStorage userStorage; @@ -23,17 +26,21 @@ public class UserServiceImpl implements UserService { public ResponseEntity add(UserDto userDto) { User user = userStorage.save(userMapper.toEntity(userDto)); + user.setShowEventsState(ShowEventsState.ALL); return new ResponseEntity<>(userMapper.toDto(user), HttpStatus.CREATED); } @Override - public List get(List ids, int from, int size) { + @Transactional(readOnly = true) + public ResponseEntity> get(List ids, int from, int size) { PageRequest page = PageRequest.of(from > 0 ? from / size : 0, size); if (ids.isEmpty()) { - return userStorage.findAll(page).map(userMapper::toDto).getContent(); + List users = userStorage.findAll(page).map(userMapper::toDto).getContent(); + return new ResponseEntity<>(users, HttpStatus.OK); } else { Page users = userStorage.findAllByIdIn(ids, page); - return users.isEmpty() ? new ArrayList<>() : users.map(userMapper::toDto).getContent(); + return users.isEmpty() ? new ResponseEntity<>(new ArrayList<>(), HttpStatus.OK) : + new ResponseEntity<>(users.map(userMapper::toDto).getContent(), HttpStatus.OK); } } diff --git a/ewm-service/src/main/java/ru/practicum/storage/EventStorage.java b/ewm-service/src/main/java/ru/practicum/storage/EventStorage.java index 29cb589..8170742 100644 --- a/ewm-service/src/main/java/ru/practicum/storage/EventStorage.java +++ b/ewm-service/src/main/java/ru/practicum/storage/EventStorage.java @@ -5,7 +5,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import ru.practicum.model.User; +import ru.practicum.model.user.User; import ru.practicum.model.event.Event; import java.util.List; @@ -21,4 +21,6 @@ public interface EventStorage extends JpaRepository, QuerydslPredic Optional findPublishedEventById(Long eventId); List findAllByIdIn(List id); + + Page findAllByIdIn(List id, Pageable pageable); } diff --git a/ewm-service/src/main/java/ru/practicum/storage/RequestStorage.java b/ewm-service/src/main/java/ru/practicum/storage/RequestStorage.java index 079b443..fbaa7f8 100644 --- a/ewm-service/src/main/java/ru/practicum/storage/RequestStorage.java +++ b/ewm-service/src/main/java/ru/practicum/storage/RequestStorage.java @@ -2,7 +2,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; -import ru.practicum.model.User; +import ru.practicum.model.user.User; import ru.practicum.model.event.Event; import ru.practicum.model.request.Request; @@ -22,4 +22,14 @@ public interface RequestStorage extends JpaRepository { "join r.event as e " + "where r.id in ?1 and e.id = ?2 and e.initiator.id = ?3") List findAllByEventAndRequesterIn(List requestIds, Long eventId, Long initiatorId); + + @Query("select r from Request r " + + "join r.requester as u " + + "where u.id = ?1 and r.status = 'CONFIRMED'") + List findAllByRequesterAnsStatusConfirmed(Long requesterId); + + @Query("select r from Request r " + + "join r.event as e " + + "where e.id in ?1 and r.requester.id = ?2") + List findAllByEventId(List eventIds, Long requesterId); } diff --git a/ewm-service/src/main/java/ru/practicum/storage/UserStorage.java b/ewm-service/src/main/java/ru/practicum/storage/UserStorage.java index 9567b6f..1814b10 100644 --- a/ewm-service/src/main/java/ru/practicum/storage/UserStorage.java +++ b/ewm-service/src/main/java/ru/practicum/storage/UserStorage.java @@ -3,7 +3,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; -import ru.practicum.model.User; +import ru.practicum.model.user.User; import java.util.List; diff --git a/ewm-service/src/main/java/ru/practicum/util/ApiPathConstants.java b/ewm-service/src/main/java/ru/practicum/util/ApiPathConstants.java index e5e1290..4a5fdbe 100644 --- a/ewm-service/src/main/java/ru/practicum/util/ApiPathConstants.java +++ b/ewm-service/src/main/java/ru/practicum/util/ApiPathConstants.java @@ -14,6 +14,8 @@ public class ApiPathConstants { public static final String REQUEST_ID_PATH = "{requestId}"; public static final String CANCEL_PATH = "cancel"; public static final String COMPILATIONS_PATH = "compilations"; + public static final String FRIENDS_PATH = "friends"; + public static final String FRIEND_ID = "{friendId}"; public static final String ID_CANCEL_PATH = REQUEST_ID_PATH + "/" + CANCEL_PATH; public static final String ADMIN_USERS_PATH = ADMIN_PATH + "/" + USERS_PATH; public static final String PRIVATE_USERS_EVENTS = USERS_PATH + "/" + BY_ID_PATH + "/" + EVENTS_PATH; @@ -22,4 +24,5 @@ public class ApiPathConstants { public static final String ADMIN_EVENTS_PATH = ADMIN_PATH + "/" + EVENTS_PATH; public static final String USER_ID_REQUEST_PATH = USERS_PATH + "/" + BY_ID_PATH + "/" + REQUEST_PATH; public static final String ADMIN_COMPILATIONS_PATH = ADMIN_PATH + "/" + COMPILATIONS_PATH; + public static final String USER_ID_FRIEND_PATH = USERS_PATH + "/" + BY_ID_PATH + "/" + FRIENDS_PATH; } diff --git a/ewm-service/src/main/resources/schema.sql b/ewm-service/src/main/resources/schema.sql index 180381e..4723042 100644 --- a/ewm-service/src/main/resources/schema.sql +++ b/ewm-service/src/main/resources/schema.sql @@ -1,12 +1,23 @@ CREATE TABLE IF NOT EXISTS users ( - user_id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, - name VARCHAR(250) NOT NULL, - email VARCHAR(254) NOT NULL, + user_id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, + name VARCHAR(250) NOT NULL, + email VARCHAR(254) NOT NULL, + show_event_state VARCHAR(20), CONSTRAINT pk_user PRIMARY KEY (user_id), CONSTRAINT UQ_USER_EMAIL UNIQUE (email) ); +CREATE TABLE IF NOT EXISTS friends +( + user_id BIGINT, + friend_id BIGINT, + CONSTRAINT fk_user_friend + FOREIGN KEY (user_id) REFERENCES USERS (user_id), + CONSTRAINT fk_friend_user + FOREIGN KEY (friend_id) REFERENCES USERS (user_id) +); + CREATE TABLE IF NOT EXISTS categories ( category_id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, @@ -50,6 +61,16 @@ CREATE TABLE IF NOT EXISTS events FOREIGN KEY (category) REFERENCES categories (category_id) ); +CREATE TABLE IF NOT EXISTS users_events +( + user_id BIGINT, + event_id BIGINT, + CONSTRAINT fk_user_event_user + FOREIGN KEY (user_id) REFERENCES USERS (user_id), + CONSTRAINT fk_user_event_event + FOREIGN KEY (event_id) REFERENCES events (event_id) +); + CREATE TABLE IF NOT EXISTS requests ( request_id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, diff --git a/postman/feature.json b/postman/feature.json new file mode 100644 index 0000000..476c3ce --- /dev/null +++ b/postman/feature.json @@ -0,0 +1,994 @@ +{ + "info": { + "_postman_id": "4954e083-8ee3-4aef-8230-1e91c0e31521", + "name": "feature", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Редактирование статуса евента с id 1", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(200);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"annotation\": \"Facere molestiae facere. Non omnis occaecati eos omnis voluptas non dicta temporibus. Tenetur aut amet excepturi amet molestias voluptas repudiandae et. Officia qui tenetur inventore iure suscipit facilis voluptatem.\",\r\n \"category\": 1,\r\n \"description\": \"Et velit est iste quis sunt eveniet est. Enim aut eius beatae. Nihil provident omnis et reiciendis voluptas consequuntur. Quae quia maiores nihil delectus tenetur nam minus quibusdam facere.\\n \\rVoluptates eum sit odio corporis repellat. In occaecati inventore labore sed nam voluptatem commodi provident quis. Quidem expedita neque. Et quia odio ea quas minima officia eveniet.\\n \\rVelit doloremque eveniet tempore cum consequatur ut consequatur et ut. Aut ut nostrum aut rerum voluptatum aliquam quo voluptas. Magnam sint minima et molestias sint minima placeat dolorem vel.\",\r\n \"eventDate\": \"2025-07-19 04:41:06\",\r\n \"location\": {\r\n \"lat\": -88.362,\r\n \"lon\": -123.946\r\n },\r\n \"paid\": \"true\",\r\n \"participantLimit\": \"596\",\r\n \"requestModeration\": \"true\",\r\n \"title\": \"Blanditiis tenetur esse tempore voluptatibus quidem quis itaque.\",\r\n \"stateAction\": \"PUBLISH_EVENT\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8080/admin/events/1", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "admin", + "events", + "1" + ] + } + }, + "response": [] + }, + { + "name": "Редактирование статуса евента с id 4", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(200);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"annotation\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\r\n \"category\": 1,\r\n \"description\": \"aa aa aaa aaaaa aaaaa aaaa a\",\r\n \"eventDate\": \"2025-07-19 04:41:06\",\r\n \"location\": {\r\n \"lat\": -99.362,\r\n \"lon\": -111.946\r\n },\r\n \"paid\": \"true\",\r\n \"participantLimit\": \"0\",\r\n \"requestModeration\": \"true\",\r\n \"title\": \"a.a.a.a.a.a.aa.a.a.a.a.\",\r\n \"stateAction\": \"PUBLISH_EVENT\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8080/admin/events/4", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "admin", + "events", + "4" + ] + } + }, + "response": [] + }, + { + "name": "Редактирование статуса евента с id 3", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(200);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"annotation\": \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\r\n \"category\": 1,\r\n \"description\": \"bbbbbbbbbbb bbbbbbbbbb bbbbbbbb bbbbb bbb bb\",\r\n \"eventDate\": \"2025-07-19 04:41:06\",\r\n \"location\": {\r\n \"lat\": -33.362,\r\n \"lon\": -22.946\r\n },\r\n \"paid\": \"true\",\r\n \"participantLimit\": \"596\",\r\n \"requestModeration\": \"false\",\r\n \"title\": \"b!