Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/main/java/com/team/buddyya/feed/domain/Feed.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.BatchSize;

@Entity
@Table(name = "feed")
Expand Down Expand Up @@ -53,6 +54,7 @@ public class Feed extends BaseTime {
@Column(name = "pinned", nullable = false)
private boolean pinned;

@BatchSize(size = 100)
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "student_id", nullable = false)
private Student student;
Expand All @@ -68,6 +70,7 @@ public class Feed extends BaseTime {
@OneToMany(mappedBy = "feed", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();

@BatchSize(size = 100)
@OneToMany(mappedBy = "feed", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<FeedImage> images = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.team.buddyya.feed.dto.projection;

import com.team.buddyya.student.domain.Role;

public record FeedAuthorInfo(
Long id,
String name,
String country,
Role role,
String characterProfileImage,
boolean isCertificated,
boolean isDeleted,
String universityName
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.team.buddyya.feed.domain.Feed;
import com.team.buddyya.feed.domain.FeedImage;
import com.team.buddyya.feed.domain.FeedUserAction;
import com.team.buddyya.feed.dto.projection.FeedAuthorInfo;
import com.team.buddyya.student.domain.Role;
import java.time.LocalDateTime;
import java.util.List;
Expand Down Expand Up @@ -35,19 +36,19 @@ public record FeedResponse(
private static final String BUDDYYA_PROFILE_IMAGE =
"https://buddyya.s3.ap-northeast-2.amazonaws.com/default-profile-image/buddyya_icon.png";

public static FeedResponse from(Feed feed, FeedUserAction userAction) {
String profileImageUrl = feed.getStudent().getRole() == Role.OWNER ? BUDDYYA_PROFILE_IMAGE
: feed.getStudent().getCharacterProfileImage();
public static FeedResponse from(Feed feed, FeedUserAction userAction, FeedAuthorInfo authorInfo) {
String profileImageUrl = authorInfo.role() == Role.OWNER ? BUDDYYA_PROFILE_IMAGE
: authorInfo.characterProfileImage();
return new FeedResponse(
feed.getId(),
feed.getStudent().getId(),
authorInfo.id(),
feed.getUniversity().getUniversityName(),
feed.getCategory().getName(),
feed.getStudent().getName(),
feed.getStudent().getCountry(),
authorInfo.name(),
authorInfo.country(),
feed.getTitle(),
feed.getContent(),
feed.getStudent().getUniversity().getUniversityName(),
authorInfo.universityName(),
profileImageUrl,
feed.getImages().stream()
.map(FeedImage::getUrl)
Expand All @@ -59,9 +60,9 @@ public static FeedResponse from(Feed feed, FeedUserAction userAction) {
userAction.isLiked(),
userAction.isBookmarked(),
feed.isPinned(),
feed.getStudent().getIsCertificated(),
authorInfo.isCertificated(),
feed.isProfileVisible(),
feed.getStudent().getIsDeleted(),
authorInfo.isDeleted(),
feed.getCreatedDate()
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
import com.team.buddyya.feed.domain.Bookmark;
import com.team.buddyya.feed.domain.Feed;
import com.team.buddyya.student.domain.Student;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
Expand All @@ -17,4 +21,8 @@ public interface BookmarkRepository extends JpaRepository<Bookmark, Long> {
boolean existsByStudentAndFeed(Student student, Feed feed);

Page<Bookmark> findAllByStudent(Student student, Pageable pageable);

@Query("SELECT b.feed.id FROM Bookmark b WHERE b.student.id = :studentId AND b.feed.id IN :feedIds")
Set<Long> findFeedIdsByStudentIdAndFeedIdsIn(@Param("studentId") Long studentId,
@Param("feedIds") List<Long> feedIds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
import com.team.buddyya.feed.domain.Feed;
import com.team.buddyya.feed.domain.FeedLike;
import com.team.buddyya.student.domain.Student;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
Expand All @@ -13,4 +17,8 @@ public interface FeedLikeRepository extends JpaRepository<FeedLike, Long> {
Optional<FeedLike> findByStudentAndFeed(Student student, Feed feed);

boolean existsByStudentAndFeed(Student student, Feed feed);

@Query("SELECT fl.feed.id FROM FeedLike fl WHERE fl.student.id = :studentId AND fl.feed.id IN :feedIds")
Set<Long> findFeedIdsByStudentIdAndFeedIdsIn(@Param("studentId") Long studentId,
@Param("feedIds") List<Long> feedIds);
}
14 changes: 14 additions & 0 deletions src/main/java/com/team/buddyya/feed/repository/FeedRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,28 @@
import com.team.buddyya.student.domain.Student;
import com.team.buddyya.student.domain.University;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface FeedRepository extends JpaRepository<Feed, Long> {

@Override
@EntityGraph(attributePaths = {
"category",
"university",
"images"
})
Optional<Feed> findById(Long id);

@EntityGraph(attributePaths = {
"category",
"university",
})
Page<Feed> findAllByUniversityAndCategory(University university, Category category, Pageable pageable);

Page<Feed> findAllByStudent(Student student, Pageable pageable);
Expand Down
102 changes: 81 additions & 21 deletions src/main/java/com/team/buddyya/feed/service/FeedService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.team.buddyya.feed.domain.Feed;
import com.team.buddyya.feed.domain.FeedImage;
import com.team.buddyya.feed.domain.FeedUserAction;
import com.team.buddyya.feed.dto.projection.FeedAuthorInfo;
import com.team.buddyya.feed.dto.request.feed.FeedCreateRequest;
import com.team.buddyya.feed.dto.request.feed.FeedListRequest;
import com.team.buddyya.feed.dto.request.feed.FeedUpdateRequest;
Expand All @@ -24,11 +25,16 @@
import com.team.buddyya.student.exception.StudentException;
import com.team.buddyya.student.exception.StudentExceptionType;
import com.team.buddyya.student.repository.BlockRepository;
import com.team.buddyya.student.repository.StudentRepository;
import com.team.buddyya.student.repository.UniversityRepository;
import com.team.buddyya.student.service.FindStudentService;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
Expand All @@ -37,6 +43,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
Expand All @@ -53,6 +60,7 @@ public class FeedService {
private final BlockRepository blockRepository;
private final UniversityRepository universityRepository;
private final ReportRepository reportRepository;
private final StudentRepository studentRepository;

@Transactional(readOnly = true)
protected Feed findFeedByFeedId(Long feedId) {
Expand All @@ -73,13 +81,20 @@ protected University findUniversityByUniversityName(String universityName) {

@Transactional(readOnly = true)
public FeedListResponse getFeeds(StudentInfo studentInfo, Pageable pageable, FeedListRequest request) {
String keyword = request.keyword();
Student student = findStudentByStudentId(studentInfo.id());
Page<Feed> feeds = (keyword == null || keyword.isBlank())
Page<Feed> feeds = (request.keyword() == null || request.keyword().isBlank())
? getFeedsByUniversityAndCategory(request, pageable)
: getFeedsByKeyword(student, keyword, pageable);
: getFeedsByKeyword(student, request.keyword(), pageable);

Set<Long> blockedStudentIds = blockRepository.findBlockedStudentIdByBlockerId(studentInfo.id());
List<FeedResponse> response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, studentInfo.id());
List<Long> feedIds = feeds.getContent().stream()
.map(Feed::getId)
.toList();
Set<Long> likedFeedIds = feedLikeRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds);
Set<Long> bookmarkedFeedIds = bookmarkRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds);
Map<Long, FeedAuthorInfo> authorInfoMap = getFeedAuthorInfoMap(feeds.getContent());
List<FeedResponse> response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, student, likedFeedIds,
bookmarkedFeedIds, authorInfoMap);
return FeedListResponse.from(response, feeds);
}

Expand All @@ -89,15 +104,19 @@ public FeedListResponse getPopularFeeds(
Pageable pageable,
FeedListRequest request
) {
Student student = findStudentByStudentId(studentInfo.id());
University university = findUniversityByUniversityName(request.university());
Page<Feed> feeds = feedRepository
.findByLikeCountGreaterThanEqualAndUniversity(
LIKE_COUNT_THRESHOLD,
university,
pageable
);
Set<Long> blocked = blockRepository.findBlockedStudentIdByBlockerId(studentInfo.id());
List<FeedResponse> response = filterBlockedFeeds(feeds.getContent(), blocked, studentInfo.id());
Page<Feed> feeds = feedRepository.findByLikeCountGreaterThanEqualAndUniversity(LIKE_COUNT_THRESHOLD, university,
pageable);
Set<Long> blockedStudentIds = blockRepository.findBlockedStudentIdByBlockerId(studentInfo.id());
List<Long> feedIds = feeds.getContent().stream()
.map(Feed::getId)
.toList();
Set<Long> likedFeedIds = feedLikeRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds);
Set<Long> bookmarkedFeedIds = bookmarkRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds);
Map<Long, FeedAuthorInfo> authorInfoMap = getFeedAuthorInfoMap(feeds.getContent());
List<FeedResponse> response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, student, likedFeedIds,
bookmarkedFeedIds, authorInfoMap);
return FeedListResponse.from(response, feeds);
}

Expand All @@ -123,7 +142,7 @@ public FeedListResponse getMyFeed(StudentInfo studentInfo, Pageable pageable) {
Student student = findStudentByStudentId(studentInfo.id());
Page<Feed> feeds = feedRepository.findAllByStudent(student, customPageable);
List<FeedResponse> response = feeds.getContent().stream()
.map(feed -> createFeedResponse(feed, studentInfo.id()))
.map(feed -> createFeedResponse(feed, student))
.toList();
return FeedListResponse.from(response, feeds);
}
Expand All @@ -134,7 +153,15 @@ public FeedListResponse getBookmarkFeed(StudentInfo studentInfo, Pageable pageab
Page<Bookmark> bookmarks = bookmarkRepository.findAllByStudent(student, pageable);
Page<Feed> feeds = bookmarks.map(Bookmark::getFeed);
Set<Long> blockedStudentIds = blockRepository.findBlockedStudentIdByBlockerId(studentInfo.id());
List<FeedResponse> response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, studentInfo.id());
List<Long> feedIds = feeds.getContent().stream()
.map(Feed::getId)
.toList();

Set<Long> likedFeedIds = feedLikeRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds);
Set<Long> bookmarkedFeedIds = bookmarkRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds);
Map<Long, FeedAuthorInfo> authorInfoMap = getFeedAuthorInfoMap(feeds.getContent());
List<FeedResponse> response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, student, likedFeedIds,
bookmarkedFeedIds, authorInfoMap);
return FeedListResponse.from(response, feeds);
}

Expand All @@ -155,9 +182,11 @@ FeedUserAction getUserAction(Student student, Feed feed) {
}

public FeedResponse getFeed(StudentInfo studentInfo, Long feedId) {
Student student = findStudentByStudentId(studentInfo.id());
Feed feed = findFeedByFeedId(feedId);
feed.increaseViewCount();
return createFeedResponse(feed, studentInfo.id());
FeedResponse response = createFeedResponse(feed, student);
return response;
}

public void createFeed(StudentInfo studentInfo, FeedCreateRequest request) {
Expand Down Expand Up @@ -204,23 +233,54 @@ public void togglePin(StudentInfo studentInfo, Long feedId) {
}

private List<FeedResponse> filterBlockedFeeds(List<Feed> feeds, Set<Long> blockedStudentIds,
Long currentStudentId) {
Student currentStudent, Set<Long> likedFeedIds,
Set<Long> bookmarkedFeedIds,
Map<Long, FeedAuthorInfo> authorInfoMap) {
return feeds.stream()
.filter(feed -> !blockedStudentIds.contains(feed.getStudent().getId()))
.map(feed -> createFeedResponse(feed, currentStudentId))
.map(feed -> {
boolean isFeedOwner = feed.isFeedOwner(currentStudent.getId());
boolean isLiked = likedFeedIds.contains(feed.getId());
boolean isBookmarked = bookmarkedFeedIds.contains(feed.getId());
FeedUserAction userAction = FeedUserAction.from(isFeedOwner, isLiked, isBookmarked);
FeedAuthorInfo authorInfo = authorInfoMap.get(feed.getStudent().getId());
return FeedResponse.from(feed, userAction, authorInfo);
})
.toList();
}

private Map<Long, FeedAuthorInfo> getFeedAuthorInfoMap(List<Feed> feeds) {
Set<Long> studentIds = feeds.stream()
.map(feed -> feed.getStudent().getId())
.collect(Collectors.toSet());
if (studentIds.isEmpty()) {
return Map.of();
}
return studentRepository.findAuthorInfoByIdsIn(studentIds).stream()
.collect(Collectors.toMap(FeedAuthorInfo::id, Function.identity()));
}


private void validateFeedOwner(StudentInfo studentInfo, Feed feed) {
if (!studentInfo.id().equals(feed.getStudent().getId()) && !(studentInfo.role() == Role.OWNER)) {
throw new FeedException(FeedExceptionType.NOT_FEED_OWNER);
}
}

private FeedResponse createFeedResponse(Feed feed, Long studentId) {
Student student = findStudentByStudentId(studentId);
FeedUserAction userAction = getUserAction(student, feed);
return FeedResponse.from(feed, userAction);
private FeedResponse createFeedResponse(Feed feed, Student currentStudent) {
FeedUserAction userAction = getUserAction(currentStudent, feed);
Student authorStudent = feed.getStudent();
FeedAuthorInfo authorInfo = new FeedAuthorInfo(
authorStudent.getId(),
authorStudent.getName(),
authorStudent.getCountry(),
authorStudent.getRole(),
authorStudent.getCharacterProfileImage(),
authorStudent.getIsCertificated(),
authorStudent.getIsDeleted(),
authorStudent.getUniversity().getUniversityName()
);
return FeedResponse.from(feed, userAction, authorInfo);
}

private void updateImages(Feed feed, List<MultipartFile> images) {
Expand Down
Loading