Skip to content

Commit 95bfb8f

Browse files
authored
Merge pull request #359 from Team-WSS/feat/#358
[FEAT] 서재 변경 조회 기능 추가
2 parents 6684179 + b4ec471 commit 95bfb8f

File tree

8 files changed

+116
-41
lines changed

8 files changed

+116
-41
lines changed

src/main/java/org/websoso/WSSServer/controller/UserController.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static org.springframework.http.HttpStatus.OK;
66

77
import jakarta.validation.Valid;
8+
import java.time.LocalDateTime;
89
import java.util.List;
910
import lombok.RequiredArgsConstructor;
1011
import org.springframework.http.ResponseEntity;
@@ -156,12 +157,13 @@ public ResponseEntity<UserNovelAndNovelsGetResponse> getUserNovelsAndNovels(@Aut
156157
@RequestParam(value = "query", required = false) String query,
157158
@RequestParam("lastUserNovelId") Long lastUserNovelId,
158159
@RequestParam("size") int size,
159-
@RequestParam("sortType") String sortType) {
160+
@RequestParam("sortType") String sortType,
161+
@RequestParam(value = "updatedSince", required = false) LocalDateTime updatedSince) {
160162
return ResponseEntity
161163
.status(OK)
162164
.body(userNovelService.getUserNovelsAndNovels(
163165
visitor, userId, isInterest, readStatuses, attractivePoints, novelRating, query,
164-
lastUserNovelId, size, sortType));
166+
lastUserNovelId, size, sortType, updatedSince));
165167
}
166168

167169
@GetMapping("/{userId}/feeds")

src/main/java/org/websoso/WSSServer/domain/UserNovelAttractivePoint.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import jakarta.persistence.Column;
66
import jakarta.persistence.Entity;
7+
import jakarta.persistence.EntityListeners;
78
import jakarta.persistence.FetchType;
89
import jakarta.persistence.GeneratedValue;
910
import jakarta.persistence.Id;
@@ -12,10 +13,12 @@
1213
import lombok.AccessLevel;
1314
import lombok.Getter;
1415
import lombok.NoArgsConstructor;
16+
import org.websoso.WSSServer.domain.common.ParentTouchListener;
1517

1618
@Entity
1719
@Getter
1820
@NoArgsConstructor(access = AccessLevel.PROTECTED)
21+
@EntityListeners(ParentTouchListener.class)
1922
public class UserNovelAttractivePoint {
2023

2124
@Id

src/main/java/org/websoso/WSSServer/domain/UserNovelKeyword.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import jakarta.persistence.Column;
66
import jakarta.persistence.Entity;
7+
import jakarta.persistence.EntityListeners;
78
import jakarta.persistence.FetchType;
89
import jakarta.persistence.GeneratedValue;
910
import jakarta.persistence.Id;
@@ -12,10 +13,12 @@
1213
import lombok.AccessLevel;
1314
import lombok.Getter;
1415
import lombok.NoArgsConstructor;
16+
import org.websoso.WSSServer.domain.common.ParentTouchListener;
1517

1618
@Entity
1719
@Getter
1820
@NoArgsConstructor(access = AccessLevel.PROTECTED)
21+
@EntityListeners(ParentTouchListener.class)
1922
public class UserNovelKeyword {
2023

2124
@Id

src/main/java/org/websoso/WSSServer/domain/common/BaseEntity.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,7 @@ public void onPreUpdate() {
3535
this.modifiedDate = LocalDateTime.now();
3636
}
3737

38+
public void touch() {
39+
this.modifiedDate = LocalDateTime.now();
40+
}
3841
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.websoso.WSSServer.domain.common;
2+
3+
import jakarta.persistence.PostPersist;
4+
import jakarta.persistence.PostRemove;
5+
import jakarta.persistence.PostUpdate;
6+
import org.websoso.WSSServer.domain.UserNovelAttractivePoint;
7+
import org.websoso.WSSServer.domain.UserNovelKeyword;
8+
9+
public class ParentTouchListener {
10+
@PostPersist
11+
@PostRemove
12+
@PostUpdate
13+
public void touchParent(Object child) {
14+
if (child instanceof UserNovelKeyword uk) {
15+
uk.getUserNovel().touch();
16+
} else if (child instanceof UserNovelAttractivePoint ap) {
17+
ap.getUserNovel().touch();
18+
}
19+
}
20+
}

src/main/java/org/websoso/WSSServer/repository/UserNovelCustomRepository.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.websoso.WSSServer.repository;
22

3+
import java.time.LocalDateTime;
34
import java.util.List;
45
import org.springframework.data.domain.Pageable;
56
import org.websoso.WSSServer.domain.Genre;
@@ -17,8 +18,9 @@ public interface UserNovelCustomRepository {
1718

1819
List<UserNovel> findFilteredUserNovels(Long userId, Boolean isInterest, List<String> readStatuses,
1920
List<String> attractivePoints, Float novelRating, String query,
20-
Long lastNovelId, int size, boolean isAscending);
21+
Long lastNovelId, int size, boolean isAscending, LocalDateTime updatedSince);
2122

2223
Long countByUserIdAndFilters(Long userId, Boolean isInterest, List<String> readStatuses,
23-
List<String> attractivePoints, Float novelRating, String query);
24+
List<String> attractivePoints, Float novelRating, String query,
25+
LocalDateTime updatedSince);
2426
}

src/main/java/org/websoso/WSSServer/repository/UserNovelCustomRepositoryImpl.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.querydsl.jpa.impl.JPAQuery;
1212
import com.querydsl.jpa.impl.JPAQueryFactory;
1313
import java.time.LocalDate;
14+
import java.time.LocalDateTime;
1415
import java.util.List;
1516
import java.util.Optional;
1617
import java.util.stream.Collectors;
@@ -100,13 +101,14 @@ public List<Novel> findTasteNovels(List<Genre> preferGenres) {
100101
@Override
101102
public List<UserNovel> findFilteredUserNovels(Long userId, Boolean isInterest, List<String> readStatuses,
102103
List<String> attractivePoints, Float novelRating, String query,
103-
Long lastUserNovelId, int size, boolean isAscending) {
104+
Long lastUserNovelId, int size, boolean isAscending,
105+
LocalDateTime updatedSince) {
104106
JPAQuery<UserNovel> queryBuilder = jpaQueryFactory
105107
.selectFrom(userNovel)
106108
.join(userNovel.novel, novel).fetchJoin()
107109
.where(userNovel.user.userId.eq(userId));
108110

109-
applyFilters(queryBuilder, isInterest, readStatuses, attractivePoints, novelRating, query);
111+
applyFilters(queryBuilder, isInterest, readStatuses, attractivePoints, novelRating, query, updatedSince);
110112

111113
queryBuilder.where(isAscending
112114
? userNovel.userNovelId.gt(lastUserNovelId)
@@ -119,20 +121,22 @@ public List<UserNovel> findFilteredUserNovels(Long userId, Boolean isInterest, L
119121

120122
@Override
121123
public Long countByUserIdAndFilters(Long userId, Boolean isInterest, List<String> readStatuses,
122-
List<String> attractivePoints, Float novelRating, String query) {
124+
List<String> attractivePoints, Float novelRating, String query,
125+
LocalDateTime updatedSince) {
123126
JPAQuery<Long> queryBuilder = jpaQueryFactory
124127
.select(userNovel.count())
125128
.from(userNovel)
126129
.join(userNovel.novel, novel)
127130
.where(userNovel.user.userId.eq(userId));
128131

129-
applyFilters(queryBuilder, isInterest, readStatuses, attractivePoints, novelRating, query);
132+
applyFilters(queryBuilder, isInterest, readStatuses, attractivePoints, novelRating, query, updatedSince);
130133

131134
return queryBuilder.fetchOne();
132135
}
133136

134137
private <T> void applyFilters(JPAQuery<T> queryBuilder, Boolean isInterest, List<String> readStatuses,
135-
List<String> attractivePoints, Float novelRating, String query) {
138+
List<String> attractivePoints, Float novelRating, String query,
139+
LocalDateTime updatedSince) {
136140
Optional.ofNullable(isInterest)
137141
.ifPresent(interest -> queryBuilder.where(userNovel.isInterest.eq(interest)));
138142

@@ -154,5 +158,8 @@ private <T> void applyFilters(JPAQuery<T> queryBuilder, Boolean isInterest, List
154158
.filter(q -> !q.isBlank())
155159
.ifPresent(q -> queryBuilder.where(
156160
novel.title.containsIgnoreCase(q).or(novel.author.containsIgnoreCase(q))));
161+
162+
Optional.ofNullable(updatedSince)
163+
.ifPresent(ts -> queryBuilder.where(userNovel.modifiedDate.gt(ts)));
157164
}
158165
}

src/main/java/org/websoso/WSSServer/service/UserNovelService.java

Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import static org.websoso.WSSServer.exception.error.CustomUserNovelError.USER_NOVEL_ALREADY_EXISTS;
99
import static org.websoso.WSSServer.exception.error.CustomUserNovelError.USER_NOVEL_NOT_FOUND;
1010

11+
import java.time.LocalDateTime;
12+
import java.util.ArrayList;
1113
import java.util.Collections;
1214
import java.util.LinkedHashMap;
1315
import java.util.List;
@@ -122,51 +124,84 @@ private void updateUserNovel(UserNovel userNovel, UserNovelUpdateRequest request
122124
}
123125

124126
private void updateAssociations(UserNovel userNovel, UserNovelUpdateRequest request) {
125-
Set<AttractivePoint> previousAttractivePoints = getPreviousAttractivePoints(userNovel);
126-
Set<Keyword> previousKeywords = getPreviousKeywords(userNovel);
127-
128-
manageAttractivePoints(userNovel, request.attractivePoints(), previousAttractivePoints);
129-
manageKeywords(userNovel, request.keywordIds(), previousKeywords);
130-
131-
userNovelAttractivePointRepository.deleteByAttractivePointsAndUserNovel(previousAttractivePoints, userNovel);
132-
userNovelKeywordRepository.deleteByKeywordsAndUserNovel(previousKeywords, userNovel);
127+
updateAttractivePoints(userNovel, request.attractivePoints());
128+
updateKeywords(userNovel, request.keywordIds());
133129
}
134130

135-
private Set<AttractivePoint> getPreviousAttractivePoints(UserNovel userNovel) {
136-
return userNovel.getUserNovelAttractivePoints()
131+
private void updateAttractivePoints(UserNovel userNovel, List<String> attractivePoints) {
132+
Map<AttractivePoint, UserNovelAttractivePoint> currentPointMap = userNovel.getUserNovelAttractivePoints()
137133
.stream()
138-
.map(UserNovelAttractivePoint::getAttractivePoint)
134+
.collect(Collectors.toMap(UserNovelAttractivePoint::getAttractivePoint, it -> it));
135+
136+
Set<AttractivePoint> requestedPoints = attractivePoints.stream()
137+
.map(attractivePointService::getAttractivePointByString)
139138
.collect(Collectors.toSet());
139+
140+
addUserNovelAttractivePoints(userNovel, currentPointMap, requestedPoints);
141+
deleteUserNovelAttractivePoints(userNovel, currentPointMap, requestedPoints);
142+
}
143+
144+
private void addUserNovelAttractivePoints(UserNovel userNovel,
145+
Map<AttractivePoint, UserNovelAttractivePoint> currentPointMap,
146+
Set<AttractivePoint> requestedPoints) {
147+
for (AttractivePoint requested : requestedPoints) {
148+
if (!currentPointMap.containsKey(requested)) {
149+
userNovelAttractivePointRepository.save(UserNovelAttractivePoint.create(userNovel, requested));
150+
}
151+
}
152+
}
153+
154+
private void deleteUserNovelAttractivePoints(UserNovel userNovel,
155+
Map<AttractivePoint, UserNovelAttractivePoint> currentPointMap,
156+
Set<AttractivePoint> requestedPoints) {
157+
List<UserNovelAttractivePoint> toDelete = new ArrayList<>();
158+
for (Map.Entry<AttractivePoint, UserNovelAttractivePoint> entry : currentPointMap.entrySet()) {
159+
if (!requestedPoints.contains(entry.getKey())) {
160+
toDelete.add(entry.getValue());
161+
}
162+
}
163+
if (!toDelete.isEmpty()) {
164+
userNovel.getUserNovelAttractivePoints().removeAll(toDelete);
165+
userNovel.touch();
166+
}
140167
}
141168

142-
private Set<Keyword> getPreviousKeywords(UserNovel userNovel) {
143-
return userNovel.getUserNovelKeywords()
169+
private void updateKeywords(UserNovel userNovel, List<Integer> keywordIds) {
170+
Map<Keyword, UserNovelKeyword> currentKeywordMap = userNovel.getUserNovelKeywords()
144171
.stream()
145-
.map(UserNovelKeyword::getKeyword)
172+
.collect(Collectors.toMap(UserNovelKeyword::getKeyword, it -> it));
173+
174+
Set<Keyword> requestedKeywords = keywordIds.stream()
175+
.map(keywordService::getKeywordOrException)
146176
.collect(Collectors.toSet());
177+
178+
addUserNovelKeywords(userNovel, currentKeywordMap, requestedKeywords);
179+
deleteUserNovelKeywords(userNovel, currentKeywordMap, requestedKeywords);
147180
}
148181

149-
private void manageAttractivePoints(UserNovel userNovel, List<String> attractivePoints,
150-
Set<AttractivePoint> previousAttractivePoints) {
151-
for (String stringAttractivePoint : attractivePoints) {
152-
AttractivePoint attractivePoint = attractivePointService.getAttractivePointByString(stringAttractivePoint);
153-
if (previousAttractivePoints.contains(attractivePoint)) {
154-
previousAttractivePoints.remove(attractivePoint);
155-
} else {
156-
userNovelAttractivePointRepository.save(UserNovelAttractivePoint.create(userNovel, attractivePoint));
182+
private void addUserNovelKeywords(UserNovel userNovel,
183+
Map<Keyword, UserNovelKeyword> currentKeywordMap,
184+
Set<Keyword> requestedKeywords) {
185+
for (Keyword requested : requestedKeywords) {
186+
if (!currentKeywordMap.containsKey(requested)) {
187+
userNovelKeywordRepository.save(UserNovelKeyword.create(userNovel, requested));
157188
}
158189
}
159190
}
160191

161-
private void manageKeywords(UserNovel userNovel, List<Integer> keywordIds, Set<Keyword> previousKeywords) {
162-
for (Integer keywordId : keywordIds) {
163-
Keyword keyword = keywordService.getKeywordOrException(keywordId);
164-
if (previousKeywords.contains(keyword)) {
165-
previousKeywords.remove(keyword);
166-
} else {
167-
userNovelKeywordRepository.save(UserNovelKeyword.create(userNovel, keyword));
192+
private void deleteUserNovelKeywords(UserNovel userNovel,
193+
Map<Keyword, UserNovelKeyword> currentKeywordMap,
194+
Set<Keyword> requestedKeywords) {
195+
List<UserNovelKeyword> toDelete = new ArrayList<>();
196+
for (Map.Entry<Keyword, UserNovelKeyword> entry : currentKeywordMap.entrySet()) {
197+
if (!requestedKeywords.contains(entry.getKey())) {
198+
toDelete.add(entry.getValue());
168199
}
169200
}
201+
if (!toDelete.isEmpty()) {
202+
userNovel.getUserNovelKeywords().removeAll(toDelete);
203+
userNovel.touch();
204+
}
170205
}
171206

172207
private void createUserNovelAttractivePoints(UserNovel userNovel, List<String> request) {
@@ -242,7 +277,7 @@ public UserNovelAndNovelsGetResponse getUserNovelsAndNovels(User visitor, Long o
242277
List<String> readStatuses,
243278
List<String> attractivePoints, Float novelRating,
244279
String query, Long lastUserNovelId, int size,
245-
String sortType) {
280+
String sortType, LocalDateTime updatedSince) {
246281
User owner = userService.getUserOrException(ownerId);
247282

248283
if (isProfileInaccessible(visitor, ownerId, owner)) {
@@ -253,10 +288,10 @@ public UserNovelAndNovelsGetResponse getUserNovelsAndNovels(User visitor, Long o
253288
boolean isAscending = sortType.equalsIgnoreCase(SORT_TYPE_OLDEST);
254289

255290
List<UserNovel> userNovels = userNovelRepository.findFilteredUserNovels(ownerId, isInterest, readStatuses,
256-
attractivePoints, novelRating, query, lastUserNovelId, size, isAscending);
291+
attractivePoints, novelRating, query, lastUserNovelId, size, isAscending, updatedSince);
257292

258293
Long totalCount = userNovelRepository.countByUserIdAndFilters(ownerId, isInterest, readStatuses,
259-
attractivePoints, novelRating, query);
294+
attractivePoints, novelRating, query, updatedSince);
260295

261296
boolean isLoadable = userNovels.size() == size;
262297

0 commit comments

Comments
 (0)