Skip to content

Commit 359a315

Browse files
authored
Merge pull request #46 from TP-RENTPLACE/feature/(TP-107)-add-filtrations
Feature/(tp 107) add filtrations
2 parents 50add41 + 1053e35 commit 359a315

8 files changed

Lines changed: 188 additions & 1 deletion

File tree

rentplace/src/main/java/kattsyn/dev/rentplace/configs/SecurityConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
7070
authorize -> authorize
7171
.requestMatchers(PUBLIC_URLS).permitAll()
7272
.requestMatchers(HttpMethod.GET, PUBLIC_URLS_GET).permitAll()
73+
.requestMatchers(HttpMethod.POST, "/api/v1/properties/filtered/").permitAll()
7374
.requestMatchers(HttpMethod.DELETE, ADMIN_URLS).hasAuthority("ROLE_ADMIN")
7475
.requestMatchers(HttpMethod.POST, ADMIN_URLS).hasAuthority("ROLE_ADMIN")
7576
.requestMatchers(HttpMethod.PATCH, ADMIN_URLS).hasAuthority("ROLE_ADMIN")

rentplace/src/main/java/kattsyn/dev/rentplace/controllers/PropertyController.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import kattsyn.dev.rentplace.dtos.ImageDTO;
1313
import kattsyn.dev.rentplace.dtos.PropertyCreateEditDTO;
1414
import kattsyn.dev.rentplace.dtos.PropertyDTO;
15+
import kattsyn.dev.rentplace.dtos.filters.PropertyFilterDTO;
1516
import kattsyn.dev.rentplace.services.PropertyService;
1617
import lombok.RequiredArgsConstructor;
1718
import org.springframework.http.HttpStatus;
@@ -76,6 +77,20 @@ public ResponseEntity<List<PropertyDTO>> getProperties() {
7677
return ResponseEntity.ok(properties);
7778
}
7879

80+
@Operation(
81+
summary = "Получение всех объявлений, с фильтрацией",
82+
description = "Позволяет получить все объявления, с фильтрацией"
83+
)
84+
@ApiResponses(value = {
85+
@ApiResponse(responseCode = "200", description = "Успешно", content = @Content(mediaType = "application/json", schema = @Schema(implementation = PropertyDTO[].class))),
86+
@ApiResponse(responseCode = "500", description = "Непредвиденная ошибка со стороны сервера", content = @Content)
87+
})
88+
@PostMapping(path = "/filtered/", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
89+
public ResponseEntity<List<PropertyDTO>> getPropertiesByFilter(@Valid @ModelAttribute PropertyFilterDTO propertyFilter) {
90+
List<PropertyDTO> properties = propertyService.findAllByFilter(propertyFilter);
91+
return ResponseEntity.ok(properties);
92+
}
93+
7994
@Operation(
8095
summary = "Получение объявлений пользователя",
8196
description = "Позволяет получить все объявления пользователя по его токену. Только для авторизованных пользователей."
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package kattsyn.dev.rentplace.dtos.filters;
2+
3+
import jakarta.validation.constraints.Min;
4+
import kattsyn.dev.rentplace.enums.SortType;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
import lombok.Setter;
9+
10+
import java.util.List;
11+
12+
@Getter
13+
@Setter
14+
@NoArgsConstructor
15+
@AllArgsConstructor
16+
public class PropertyFilterDTO {
17+
18+
private SortType sortType;
19+
private List<Long> categoryIds;
20+
private List<Long> facilityIds;
21+
private Boolean isLongTermRent;
22+
@Min(0)
23+
private Integer minPrice;
24+
@Min(0)
25+
private Integer maxPrice;
26+
@Min(0)
27+
private Integer guestsAmount;
28+
@Min(0)
29+
private Integer bedsAmount;
30+
@Min(0)
31+
private Integer bedrooms;
32+
@Min(0)
33+
private Integer rooms;
34+
35+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package kattsyn.dev.rentplace.enums;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
5+
@Schema(
6+
description = "Тип сортировки объявлений"
7+
)
8+
public enum SortType {
9+
10+
@Schema(name = "Сначала новые")
11+
MOST_RECENT,
12+
@Schema(name = "Сначала старые")
13+
MOST_OLD,
14+
@Schema(name = "Сначала дорогие, убывание по цене")
15+
MOST_EXPENSIVE,
16+
@Schema(name = "Сначала дешевые, возрастание по цене")
17+
MOST_CHEAP
18+
19+
}

rentplace/src/main/java/kattsyn/dev/rentplace/repositories/PropertyRepository.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33

44
import kattsyn.dev.rentplace.entities.Property;
55
import org.springframework.data.jpa.repository.JpaRepository;
6+
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
67
import org.springframework.data.jpa.repository.Query;
78
import org.springframework.data.repository.query.Param;
89

910
import java.util.List;
1011
import java.util.Optional;
1112

12-
public interface PropertyRepository extends JpaRepository<Property, Long> {
13+
public interface PropertyRepository extends JpaRepository<Property, Long>, JpaSpecificationExecutor<Property> {
1314

1415
@Query("""
1516
SELECT DISTINCT p

rentplace/src/main/java/kattsyn/dev/rentplace/services/PropertyService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import kattsyn.dev.rentplace.dtos.ImageDTO;
44
import kattsyn.dev.rentplace.dtos.PropertyCreateEditDTO;
55
import kattsyn.dev.rentplace.dtos.PropertyDTO;
6+
import kattsyn.dev.rentplace.dtos.filters.PropertyFilterDTO;
67
import kattsyn.dev.rentplace.entities.Property;
78
import org.springframework.web.multipart.MultipartFile;
89

@@ -18,6 +19,8 @@ public interface PropertyService {
1819

1920
List<PropertyDTO> findAllByOwnerEmail(String email);
2021

22+
List<PropertyDTO> findAllByFilter(PropertyFilterDTO filter);
23+
2124
PropertyDTO findById(long id);
2225

2326
Property getPropertyById(long id);

rentplace/src/main/java/kattsyn/dev/rentplace/services/impl/PropertyServiceImpl.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
import kattsyn.dev.rentplace.dtos.ImageDTO;
55
import kattsyn.dev.rentplace.dtos.PropertyCreateEditDTO;
66
import kattsyn.dev.rentplace.dtos.PropertyDTO;
7+
import kattsyn.dev.rentplace.dtos.filters.PropertyFilterDTO;
78
import kattsyn.dev.rentplace.entities.Image;
89
import kattsyn.dev.rentplace.entities.Property;
910
import kattsyn.dev.rentplace.entities.User;
1011
import kattsyn.dev.rentplace.enums.ImageType;
1112
import kattsyn.dev.rentplace.enums.Role;
13+
import kattsyn.dev.rentplace.enums.SortType;
1214
import kattsyn.dev.rentplace.exceptions.ForbiddenException;
1315
import kattsyn.dev.rentplace.exceptions.NotFoundException;
1416
import kattsyn.dev.rentplace.mappers.ImageMapper;
@@ -17,8 +19,10 @@
1719
import kattsyn.dev.rentplace.services.ImageService;
1820
import kattsyn.dev.rentplace.services.PropertyService;
1921
import kattsyn.dev.rentplace.services.UserService;
22+
import kattsyn.dev.rentplace.specifications.PropertySpecification;
2023
import kattsyn.dev.rentplace.utils.PathResolver;
2124
import lombok.RequiredArgsConstructor;
25+
import org.springframework.data.domain.Sort;
2226
import org.springframework.stereotype.Service;
2327
import org.springframework.web.multipart.MultipartFile;
2428

@@ -69,6 +73,24 @@ public List<PropertyDTO> findAllByOwnerEmail(String email) {
6973
return propertyMapper.fromProperties(propertyRepository.findAllByOwnerEmail(email));
7074
}
7175

76+
@Override
77+
public List<PropertyDTO> findAllByFilter(PropertyFilterDTO filter) {
78+
return propertyMapper.fromProperties(
79+
propertyRepository.findAll(new PropertySpecification(filter), buildSort(filter.getSortType()))
80+
);
81+
}
82+
83+
private Sort buildSort(SortType sortType) {
84+
if (sortType == null) return Sort.unsorted();
85+
86+
return switch (sortType) {
87+
case MOST_OLD -> Sort.by(Sort.Order.asc("propertyId"));
88+
case MOST_RECENT -> Sort.by(Sort.Order.desc("propertyId"));
89+
case MOST_EXPENSIVE -> Sort.by(Sort.Order.desc("cost"));
90+
case MOST_CHEAP -> Sort.by(Sort.Order.asc("cost"));
91+
};
92+
}
93+
7294
@Override
7395
@Transactional
7496
public Property getPropertyById(long id) {
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package kattsyn.dev.rentplace.specifications;
2+
3+
import jakarta.persistence.criteria.*;
4+
import kattsyn.dev.rentplace.dtos.filters.PropertyFilterDTO;
5+
import kattsyn.dev.rentplace.entities.Category;
6+
import kattsyn.dev.rentplace.entities.Facility;
7+
import kattsyn.dev.rentplace.entities.Property;
8+
import lombok.AllArgsConstructor;
9+
import org.springframework.data.jpa.domain.Specification;
10+
11+
@AllArgsConstructor
12+
public class PropertySpecification implements Specification<Property> {
13+
14+
private final PropertyFilterDTO filter;
15+
16+
@Override
17+
public Predicate toPredicate(Root<Property> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
18+
19+
Predicate predicate = criteriaBuilder.conjunction();
20+
21+
root.fetch("images", JoinType.LEFT);
22+
root.fetch("facilities", JoinType.LEFT);
23+
root.fetch("categories", JoinType.LEFT);
24+
25+
assert query != null;
26+
query.distinct(true);
27+
28+
Join<Property, Category> categoryJoin = null;
29+
if (filter.getCategoryIds() != null && !filter.getCategoryIds().isEmpty()) {
30+
categoryJoin = root.join("categories", JoinType.INNER);
31+
}
32+
33+
Join<Property, Facility> facilityJoin = null;
34+
if (filter.getFacilityIds() != null && !filter.getFacilityIds().isEmpty()) {
35+
facilityJoin = root.join("facilities", JoinType.INNER);
36+
}
37+
38+
if (filter.getMinPrice() != null && filter.getMinPrice() >= 0) {
39+
predicate = criteriaBuilder.and(predicate, criteriaBuilder.greaterThanOrEqualTo(root.get("cost"), filter.getMinPrice()));
40+
}
41+
42+
if (filter.getMaxPrice() != null && filter.getMaxPrice() >= 0) {
43+
predicate = criteriaBuilder.and(predicate, criteriaBuilder.lessThanOrEqualTo(root.get("cost"), filter.getMaxPrice()));
44+
}
45+
46+
if (filter.getGuestsAmount() != null && !(filter.getGuestsAmount() == 0)) {
47+
predicate = filter.getGuestsAmount() >= 5
48+
? criteriaBuilder.and(predicate, criteriaBuilder.greaterThanOrEqualTo(root.get("maxGuests"), filter.getGuestsAmount()))
49+
: criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("maxGuests"), filter.getGuestsAmount()));
50+
}
51+
52+
if (filter.getBedsAmount() != null && !(filter.getBedsAmount() == 0)) {
53+
predicate = filter.getBedsAmount() >= 5
54+
? criteriaBuilder.and(predicate, criteriaBuilder.greaterThanOrEqualTo(root.get("sleepingPlaces"), filter.getBedsAmount()))
55+
: criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("sleepingPlaces"), filter.getBedsAmount()));
56+
}
57+
58+
if (filter.getBedrooms() != null && !(filter.getBedrooms() == 0)) {
59+
predicate = filter.getBedrooms() >= 5
60+
? criteriaBuilder.and(predicate, criteriaBuilder.greaterThanOrEqualTo(root.get("bedrooms"), filter.getBedrooms()))
61+
: criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("bedrooms"), filter.getBedrooms()));
62+
}
63+
64+
if (filter.getRooms() != null && !(filter.getRooms() == 0)) {
65+
predicate = filter.getRooms() >= 5
66+
? criteriaBuilder.and(predicate, criteriaBuilder.greaterThanOrEqualTo(root.get("rooms"), filter.getRooms()))
67+
: criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("rooms"), filter.getRooms()));
68+
}
69+
70+
if (filter.getIsLongTermRent() != null) {
71+
predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("isLongTermRent"), filter.getIsLongTermRent()));
72+
}
73+
74+
if (filter.getCategoryIds() != null && !filter.getCategoryIds().isEmpty() && categoryJoin != null) {
75+
predicate = criteriaBuilder.and(
76+
predicate,
77+
categoryJoin.get("categoryId").in(filter.getCategoryIds())
78+
);
79+
}
80+
81+
// Фильтр по удобствам
82+
if (filter.getFacilityIds() != null && !filter.getFacilityIds().isEmpty() && facilityJoin != null) {
83+
predicate = criteriaBuilder.and(
84+
predicate,
85+
facilityJoin.get("facilityId").in(filter.getFacilityIds())
86+
);
87+
}
88+
89+
return predicate;
90+
}
91+
}

0 commit comments

Comments
 (0)