deleted likes

This commit is contained in:
SlimusMinus
2026-09-24 00:18:13 +03:00
parent ca06c2a5c3
commit 0afb2397cf
4 changed files with 11 additions and 102 deletions

View File

@@ -22,9 +22,7 @@ public class PostResponse {
private String userEmail; private String userEmail;
private String phone; private String phone;
private List<String> images; private List<String> images;
private boolean isFavorite;
private Long userId; private Long userId;
private Long likeCount;
private Role userRole; private Role userRole;
private String rulesAccepted; private String rulesAccepted;
private Boolean reward; private Boolean reward;

View File

@@ -22,15 +22,6 @@ public interface FavoriteRepository extends JpaRepository<Favorite, Long> {
""") """)
List<Long> findFavoritePostIds(Long userId, List<Long> postIds); List<Long> findFavoritePostIds(Long userId, List<Long> postIds);
// ADD THIS METHOD:
@Query("""
SELECT f.post.id, COUNT(f.id)\s
FROM Favorite f\s
WHERE f.post.id IN :postIds\s
GROUP BY f.post.id
\s""")
List<Object[]> countLikesByPostIds(List<Long> postIds);
@Query("select f.post.id from Favorite f where f.user.id = :userId") @Query("select f.post.id from Favorite f where f.user.id = :userId")
List<Long> findPostIdsByUserId(@Param("userId") Long userId); List<Long> findPostIdsByUserId(@Param("userId") Long userId);
} }

View File

@@ -10,7 +10,6 @@ import com.krylov.refound.mapper.PostMapper;
import com.krylov.refound.repository.FavoriteRepository; import com.krylov.refound.repository.FavoriteRepository;
import com.krylov.refound.repository.PostRepository; import com.krylov.refound.repository.PostRepository;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -75,11 +74,9 @@ public class FavoriteService {
.map(Post::getId) .map(Post::getId)
.toList(); .toList();
Map<Long, Long> likeCounts = postService.countLikes(postIds);
Set<Long> userFavoriteIds = postService.currentUserFavoriteIds(postIds); // тут будет == postIds, но ок для единообразия
return posts.stream() return posts.stream()
.map(post -> postService.toResponse(post, likeCounts, userFavoriteIds, true)) .map(post -> postService.toResponse(post, true))
.toList(); .toList();
} }
} }

View File

@@ -14,18 +14,14 @@ import com.krylov.refound.enums.PostStatus;
import com.krylov.refound.enums.PostType; import com.krylov.refound.enums.PostType;
import com.krylov.refound.exception.ApiException; import com.krylov.refound.exception.ApiException;
import com.krylov.refound.mapper.PostMapper; import com.krylov.refound.mapper.PostMapper;
import com.krylov.refound.repository.FavoriteRepository;
import com.krylov.refound.repository.ImageRepository; import com.krylov.refound.repository.ImageRepository;
import com.krylov.refound.repository.PostRepository; import com.krylov.refound.repository.PostRepository;
import com.krylov.refound.util.PostSpecification; import com.krylov.refound.util.PostSpecification;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -50,7 +46,6 @@ public class PostService {
private final ImageRepository imageRepository; private final ImageRepository imageRepository;
private final GeocodingService geocodingService; private final GeocodingService geocodingService;
private final UserService userService; private final UserService userService;
private final FavoriteRepository favoriteRepository;
private final FileStorageService fileStorageService; private final FileStorageService fileStorageService;
private final StatisticsService statisticsService; private final StatisticsService statisticsService;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
@@ -84,21 +79,13 @@ public class PostService {
} }
public Page<PostResponse> getFeed(String search, Pageable pageable) { public Page<PostResponse> getFeed(String search, Pageable pageable) {
Specification<Post> spec = PostSpecification.hasSearch(search).and((root, query, cb) -> root.get("status").in(FEED_VISIBLE_STATUSES)); Specification<Post> spec = PostSpecification.hasSearch(search)
.and((root, query, cb) ->
root.get("status").in(FEED_VISIBLE_STATUSES));
Page<Post> postsPage = postRepository.findAll(spec, pageable); Page<Post> postsPage = postRepository.findAll(spec, pageable);
List<Long> postIds = postsPage.getContent().stream() return postsPage.map(post -> toResponse(post, true));
.map(Post::getId)
.toList();
if (postIds.isEmpty()) {
return Page.empty(pageable);
}
Map<Long, Long> likeCounts = countLikes(postIds);
Set<Long> userFavoriteIds = currentUserFavoriteIds(postIds);
return postsPage.map(post -> toResponse(post, likeCounts, userFavoriteIds, true));
} }
/** /**
@@ -115,15 +102,8 @@ public class PostService {
return List.of(); return List.of();
} }
List<Long> postIds = posts.stream()
.map(Post::getId)
.toList();
Map<Long, Long> likeCounts = countLikes(postIds);
Set<Long> userFavoriteIds = currentUserFavoriteIds(postIds);
return posts.stream() return posts.stream()
.map(post -> toResponse(post, likeCounts, userFavoriteIds, false)) .map(post -> toResponse(post, false))
.toList(); .toList();
} }
@@ -203,9 +183,6 @@ public class PostService {
Post post = postRepository.findById(id) Post post = postRepository.findById(id)
.orElseThrow(() -> new ApiException(ErrorCode.NOT_FOUND, "Post not found", HttpStatus.NOT_FOUND)); .orElseThrow(() -> new ApiException(ErrorCode.NOT_FOUND, "Post not found", HttpStatus.NOT_FOUND));
// Сначала избранное — иначе DataIntegrityViolationException (FK) при удалении поста
favoriteRepository.deleteByPostIn(List.of(post));
// Без этого файлы останутся в MinIO мусором после удаления объявления // Без этого файлы останутся в MinIO мусором после удаления объявления
post.getImages().forEach(img -> deleteFileQuietly(img.getUrl())); post.getImages().forEach(img -> deleteFileQuietly(img.getUrl()));
@@ -234,13 +211,7 @@ public class PostService {
post.setStatus(newStatus); post.setStatus(newStatus);
Post updated = postRepository.save(post); Post updated = postRepository.save(post);
log.info("Статус поста id={} изменён: {} -> {}", id, previousStatus, newStatus); log.info("Статус поста id={} изменён: {} -> {}", id, previousStatus, newStatus);
return mapper.toResponse(updated);
List<Long> postIds = List.of(id);
PostResponse response = mapper.toResponse(updated);
response.setLikeCount(countLikes(postIds).getOrDefault(id, 0L));
response.setFavorite(currentUserFavoriteIds(postIds).contains(id));
return response;
} }
public List<PostResponse> searchByCity(String city) { public List<PostResponse> searchByCity(String city) {
@@ -256,15 +227,8 @@ public class PostService {
return List.of(); return List.of();
} }
List<Long> postIds = posts.stream()
.map(Post::getId)
.toList();
Map<Long, Long> likeCounts = countLikes(postIds);
Set<Long> userFavoriteIds = currentUserFavoriteIds(postIds);
return posts.stream() return posts.stream()
.map(post -> toResponse(post, likeCounts, userFavoriteIds, true)) .map(post -> toResponse(post, true))
.toList(); .toList();
} }
@@ -328,45 +292,8 @@ public class PostService {
} }
} }
public Map<Long, Long> countLikes(List<Long> postIds) { public PostResponse toResponse(Post post, boolean includeUserRole) {
if (postIds.isEmpty()) {
return Map.of();
}
return favoriteRepository.countLikesByPostIds(postIds).stream()
.collect(Collectors.toMap(
row -> (Long) row[0],
row -> (Long) row[1]
));
}
/**
* Возвращает id постов, лайкнутых текущим пользователем, либо пустой набор,
* если пользователь не авторизован.
*/
public Set<Long> currentUserFavoriteIds(List<Long> postIds) {
if (postIds.isEmpty()) {
return Set.of();
}
try {
User currentUser = userService.getCurrentUser();
return new HashSet<>(favoriteRepository.findFavoritePostIds(currentUser.getId(), postIds));
} catch (ApiException e) {
if (e.getErrorCode() == ErrorCode.UNAUTHORIZED) {
// Ожидаемо для анонимных пользователей — не считаем ошибкой
log.debug("Избранное не подгружено: пользователь не авторизован");
} else {
// Подозрительный случай: в SecurityContext есть аутентификация с логином,
// но пользователя с таким логином нет в БД (например, удалён после выдачи токена)
log.warn("Избранное не подгружено: {}", e.getMessage());
}
return Set.of();
}
}
public PostResponse toResponse(Post post, Map<Long, Long> likeCounts, Set<Long> userFavoriteIds, boolean includeUserRole) {
PostResponse response = mapper.toResponse(post); PostResponse response = mapper.toResponse(post);
response.setLikeCount(likeCounts.getOrDefault(post.getId(), 0L));
response.setFavorite(userFavoriteIds.contains(post.getId()));
response.setReward(post.getIsReward()); response.setReward(post.getIsReward());
if (includeUserRole) { if (includeUserRole) {
response.setUserRole(post.getUser().getRole()); response.setUserRole(post.getUser().getRole());
@@ -389,10 +316,6 @@ public class PostService {
Post post = postRepository.findById(postId) Post post = postRepository.findById(postId)
.orElseThrow(() -> new ApiException(ErrorCode.NOT_FOUND, "Post not found", HttpStatus.NOT_FOUND)); .orElseThrow(() -> new ApiException(ErrorCode.NOT_FOUND, "Post not found", HttpStatus.NOT_FOUND));
List<Long> postIds = List.of(postId); return toResponse(post, true);
Map<Long, Long> likeCounts = countLikes(postIds);
Set<Long> userFavoriteIds = currentUserFavoriteIds(postIds);
return toResponse(post, likeCounts, userFavoriteIds, true);
} }
} }