Skip to content

Commit 414a4dd

Browse files
authored
Merge pull request #127 from GoormOnlyOne/develop
[deploy] 피드 / 정산 / 결제 수정사항 반영
2 parents e984701 + 46d88c3 commit 414a4dd

25 files changed

Lines changed: 390 additions & 151 deletions

File tree

src/main/java/com/example/onlyone/OnlyoneApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
import org.springframework.boot.autoconfigure.SpringBootApplication;
77
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
88
import org.springframework.scheduling.annotation.EnableAsync;
9+
import org.springframework.scheduling.annotation.EnableScheduling;
910

1011
@SpringBootApplication
1112
@EnableJpaAuditing
1213
@EnableAsync
14+
@EnableScheduling
1315
@OpenAPIDefinition(
1416
servers = {
1517
@Server(url = "https://api.buddkit.p-e.kr", description = "Production Server"),

src/main/java/com/example/onlyone/domain/chat/entity/ChatRoom.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class ChatRoom extends BaseTimeEntity {
2424
@Column(name = "chat_room_id", updatable = false, nullable = false)
2525
private Long chatRoomId;
2626

27-
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
27+
@ManyToOne(fetch = FetchType.LAZY)
2828
@JoinColumn(name = "club_id", updatable = false)
2929
@NotNull
3030
@JsonIgnore

src/main/java/com/example/onlyone/domain/chat/entity/Message.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class Message extends BaseTimeEntity {
2626
@NotNull
2727
private ChatRoom chatRoom;
2828

29-
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
29+
@ManyToOne(fetch = FetchType.LAZY)
3030
@JoinColumn(name = "user_id", updatable = false)
3131
@NotNull
3232
private User user;

src/main/java/com/example/onlyone/domain/feed/controller/FeedController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public ResponseEntity<?> updateFeed(@PathVariable("clubId") Long clubId,
4646
@Operation(summary = "피드 삭제", description = "피드를 삭제합니다.")
4747
@DeleteMapping("/{feedId}")
4848
public ResponseEntity<?> deleteFeed(@PathVariable("clubId") Long clubId, @PathVariable("feedId") Long feedId) {
49-
feedService.deleteFeed(clubId, feedId);
49+
feedService.softDeleteFeed(clubId, feedId);
5050
return ResponseEntity.status(HttpStatus.OK).body(CommonResponse.success(null));
5151
}
5252

src/main/java/com/example/onlyone/domain/feed/entity/Feed.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
import jakarta.persistence.*;
77
import jakarta.validation.constraints.NotNull;
88
import lombok.*;
9+
import org.hibernate.annotations.SQLDelete;
10+
import org.hibernate.annotations.SQLRestriction;
11+
import org.hibernate.annotations.SoftDelete;
912

13+
import java.time.LocalDateTime;
1014
import java.util.ArrayList;
1115
import java.util.List;
1216

@@ -23,6 +27,8 @@
2327
@Builder
2428
@AllArgsConstructor
2529
@NoArgsConstructor
30+
@SQLDelete(sql = "UPDATE feed SET deleted = true, deleted_at = now() WHERE feed_id = ?")
31+
@SQLRestriction("deleted = false")
2632
public class Feed extends BaseTimeEntity {
2733

2834
@Id
@@ -49,17 +55,16 @@ public class Feed extends BaseTimeEntity {
4955
@Builder.Default
5056
private FeedType feedType = FeedType.ORIGINAL;
5157

52-
@ManyToOne(fetch = FetchType.LAZY)
53-
@JoinColumn(name = "parent_feed_id")
54-
private Feed parent;
58+
@Column(name = "parent_feed_id")
59+
private Long parentFeedId;
5560

5661
@Column(name = "root_feed_id")
5762
private Long rootFeedId;
5863

59-
@Builder.Default
60-
@Column(name = "depth")
61-
@NotNull
62-
private int depth = 0;
64+
// @Builder.Default
65+
// @Column(name = "depth")
66+
// @NotNull
67+
// private int depth = 0;
6368

6469
@Builder.Default
6570
@OneToMany(mappedBy = "feed", cascade = CascadeType.ALL, orphanRemoval = true)
@@ -76,4 +81,11 @@ public class Feed extends BaseTimeEntity {
7681
public void update(String content) {
7782
this.content = content;
7883
}
84+
85+
@Column(name = "deleted", nullable = false)
86+
private boolean deleted = false;
87+
88+
@Column(name = "deleted_at")
89+
private LocalDateTime deletedAt;
90+
7991
}

src/main/java/com/example/onlyone/domain/feed/repository/FeedRepository.java

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,26 @@
55
import org.springframework.data.domain.Page;
66
import org.springframework.data.domain.Pageable;
77
import org.springframework.data.jpa.repository.JpaRepository;
8+
import org.springframework.data.jpa.repository.Modifying;
89
import org.springframework.data.jpa.repository.Query;
910
import org.springframework.data.repository.query.Param;
1011

1112
import java.util.List;
1213
import java.util.Optional;
1314

1415
public interface FeedRepository extends JpaRepository<Feed,Long> {
15-
long countByParent_FeedId(Long feedId);
16+
long countByParentFeedId(Long feedId);
1617

17-
@Query("""
18-
select f.parent.feedId as parentId, count(f) as cnt
19-
from Feed f
20-
where f.parent.feedId in :feedIds
21-
group by f.parent.feedId
22-
""")
18+
@Query(
19+
value = """
20+
select parent_feed_id as parentId, count(*) as cnt
21+
from feed
22+
where parent_feed_id in (:feedIds)
23+
and deleted = false
24+
group by parent_feed_id
25+
""",
26+
nativeQuery = true
27+
)
2328
List<ParentRepostCount> countDirectRepostsIn(@Param("feedIds") List<Long> feedIds);
2429

2530
interface ParentRepostCount {
@@ -29,7 +34,7 @@ interface ParentRepostCount {
2934

3035
Optional<Feed> findByFeedIdAndClub(Long feedId, Club club);
3136

32-
Page<Feed> findByClubAndParentIsNull(Club club, Pageable pageable);
37+
Page<Feed> findByClubAndParentFeedIdIsNull(Club club, Pageable pageable);
3338

3439
Feed findByFeedId(Long feedId);
3540

@@ -63,5 +68,35 @@ ORDER BY (
6368
""", nativeQuery = true)
6469
List<Feed> findPopularByClubIds(@Param("clubIds") List<Long> clubIds, Pageable pageable);
6570

71+
// 나(= parentId)를 인용하던 '직계 자식'들의 parent/root를 모두 NULL
72+
@Modifying(clearAutomatically = true, flushAutomatically = true)
73+
@Query("""
74+
UPDATE Feed f
75+
SET f.parentFeedId = NULL,
76+
f.rootFeedId = NULL
77+
WHERE f.parentFeedId = :parentId
78+
AND f.deleted = FALSE
79+
""")
80+
int clearParentAndRootForChildren(@Param("parentId") Long parentId);
6681

82+
// 나(= rootId)를 루트로 바라보던 모든 후손들의 root를 NULL
83+
@Modifying(clearAutomatically = true, flushAutomatically = true)
84+
@Query("""
85+
UPDATE Feed f
86+
SET f.rootFeedId = NULL
87+
WHERE f.rootFeedId = :rootId
88+
AND f.deleted = FALSE
89+
""")
90+
int clearRootForDescendants(@Param("rootId") Long rootId);
91+
92+
// 소프트 삭제 (엔티티 @SQLDelete 호출 대신 직접 UPDATE)
93+
@Modifying(clearAutomatically = true, flushAutomatically = true)
94+
@Query("""
95+
UPDATE Feed f
96+
SET f.deleted = TRUE,
97+
f.deletedAt = CURRENT_TIMESTAMP
98+
WHERE f.feedId = :feedId
99+
AND f.deleted = FALSE
100+
""")
101+
int softDeleteById(@Param("feedId") Long feedId);
67102
}

src/main/java/com/example/onlyone/domain/feed/service/FeedMainService.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ private Map<Long, Long> countDirectReposts(List<Feed> feeds) {
122122

123123
private Map<Long, Feed> bulkLoadParents(List<Feed> feeds) {
124124
Set<Long> parentIds = feeds.stream()
125-
.map(f -> f.getParent() != null ? f.getParent().getFeedId() : null)
125+
.map(Feed::getParentFeedId )
126126
.filter(Objects::nonNull)
127127
.collect(Collectors.toSet());
128128
if (parentIds.isEmpty()) return Collections.emptyMap();
@@ -185,11 +185,13 @@ private FeedOverviewDto toOverviewDto(
185185
.created(f.getCreatedAt())
186186
.repostCount(selfRepostCount);
187187

188-
Feed parent = f.getParent();
189-
if (parent != null) {
190-
long parentRepostCount = repostCntMap.getOrDefault(parent.getFeedId(), 0L);
191-
Feed p = parentMap.getOrDefault(parent.getFeedId(), parent); // 영속성 컨텍스트에 이미 있을 수도
192-
b.parentFeed(toShallowDto(p, currentUserId, likedFeedIds, parentRepostCount));
188+
Long parentId = f.getParentFeedId();
189+
if (parentId != null) {
190+
Feed p = parentMap.get(parentId);
191+
if (p != null) {
192+
long parentRepostCount = repostCntMap.getOrDefault(parentId, 0L);
193+
b.parentFeed(toShallowDto(p, currentUserId, likedFeedIds, parentRepostCount));
194+
}
193195
}
194196

195197
Long rootId = f.getRootFeedId();
@@ -246,10 +248,10 @@ public void createRefeed(Long parentFeedId, Long targetClubId, RefeedRequestDto
246248
Feed parent = feedRepository.findById(parentFeedId)
247249
.orElseThrow(() -> new CustomException(ErrorCode.FEED_NOT_FOUND));
248250

249-
int newDepth = parent.getDepth() + 1;
250-
if (newDepth > MAX_REFEED_DEPTH) {
251-
throw new CustomException(ErrorCode.REFEED_DEPTH_LIMIT);
252-
}
251+
// int newDepth = parent.getDepth() + 1;
252+
// if (newDepth > MAX_REFEED_DEPTH) {
253+
// throw new CustomException(ErrorCode.REFEED_DEPTH_LIMIT);
254+
// }
253255

254256
Club club = clubRepository.findById(targetClubId)
255257
.orElseThrow(() -> new CustomException(ErrorCode.CLUB_NOT_FOUND));
@@ -263,9 +265,9 @@ public void createRefeed(Long parentFeedId, Long targetClubId, RefeedRequestDto
263265
Feed reFeed = Feed.builder()
264266
.content(requestDto.getContent())
265267
.feedType(FeedType.REFEED)
266-
.parent(parent)
268+
.parentFeedId(parentFeedId)
267269
.rootFeedId(rootId)
268-
.depth(newDepth)
270+
// .depth(newDepth)
269271
.club(club)
270272
.user(user)
271273
.build();

src/main/java/com/example/onlyone/domain/feed/service/FeedService.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.transaction.annotation.Transactional;
3131

3232
import java.util.List;
33+
import java.util.Objects;
3334
import java.util.Optional;
3435
import java.util.stream.Collectors;
3536

@@ -92,7 +93,7 @@ public Page<FeedSummaryResponseDto> getFeedList(Long clubId, Pageable pageable)
9293
Club club = clubRepository.findById(clubId)
9394
.orElseThrow(() -> new CustomException(ErrorCode.CLUB_NOT_FOUND));
9495

95-
Page<Feed> feeds = feedRepository.findByClubAndParentIsNull(club, pageable);
96+
Page<Feed> feeds = feedRepository.findByClubAndParentFeedIdIsNull(club, pageable);
9697

9798
return feeds.map(feed -> {
9899
String thumbnailUrl = null;
@@ -131,7 +132,7 @@ public FeedDetailResponseDto getFeedDetail(Long clubId, Long feedId) {
131132
List<FeedCommentResponseDto> commentResponseDtos = feed.getFeedComments().stream()
132133
.map(comment -> FeedCommentResponseDto.from(comment, currentUserId))
133134
.collect(Collectors.toList());
134-
long repostCount = feedRepository.countByParent_FeedId(feedId);
135+
long repostCount = feedRepository.countByParentFeedId(feedId);
135136

136137
return FeedDetailResponseDto.from(feed, imageUrls, isLiked, isMine, commentResponseDtos, repostCount);
137138
}
@@ -210,4 +211,29 @@ public void deleteFeed(Long clubId, Long feedId) {
210211
}
211212
feedRepository.delete(feed);
212213
}
214+
215+
public void softDeleteFeed(Long clubId, Long feedId) {
216+
Club club = clubRepository.findById(clubId)
217+
.orElseThrow(() -> new CustomException(ErrorCode.CLUB_NOT_FOUND));
218+
Feed target = feedRepository.findByFeedIdAndClub(feedId, club)
219+
.orElseThrow(() -> new CustomException(ErrorCode.FEED_NOT_FOUND));
220+
221+
// 권한 체크
222+
Long me = userService.getCurrentUser().getUserId();
223+
if (!Objects.equals(target.getUser().getUserId(), me)) {
224+
throw new CustomException(ErrorCode.UNAUTHORIZED_FEED_ACCESS);
225+
}
226+
227+
// 1) 중간 노드 삭제 대비: 나를 parent로 참조하던 '직계 자식'들의 parent/root를 NULL
228+
feedRepository.clearParentAndRootForChildren(target.getFeedId());
229+
230+
// 2) 루트 노드 삭제 대비: 나를 root로 참조하던 모든 후손들의 root를 NULL
231+
feedRepository.clearRootForDescendants(target.getFeedId());
232+
233+
// 3) 내 행 소프트 삭제
234+
int affected = feedRepository.softDeleteById(target.getFeedId());
235+
if (affected == 0) {
236+
throw new CustomException(ErrorCode.FEED_NOT_FOUND); // 동시성 등으로 이미 삭제된 경우
237+
}
238+
}
213239
}

src/main/java/com/example/onlyone/domain/payment/controller/PaymentController.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ public ResponseEntity<?> confirmPayment(@RequestBody ConfirmTossPayRequest req)
4444
return ResponseEntity.ok(CommonResponse.success(response));
4545
}
4646

47-
// @Operation(summary = "결제 실패 기록", description = "토스페이먼츠 결제 요청의 최종 실패를 기록합니다.")
48-
// @PostMapping(value = "/fail")
49-
// public ResponseEntity<?> failPayment(@RequestBody ConfirmTossPayRequest req) {
50-
// paymentService.reportFail(req);
51-
// return ResponseEntity.ok(CommonResponse.success(null));
52-
// }
47+
@Operation(summary = "결제 실패 기록", description = "토스페이먼츠 결제 요청의 최종 실패를 기록합니다.")
48+
@PostMapping(value = "/fail")
49+
public ResponseEntity<?> failPayment(@RequestBody ConfirmTossPayRequest req) {
50+
paymentService.reportFail(req);
51+
return ResponseEntity.ok(CommonResponse.success(null));
52+
}
5353

5454
// // 결제 취소 요청
5555
// @PostMapping("/cancel/{paymentKey}")

src/main/java/com/example/onlyone/domain/payment/entity/Method.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public String getKorean() {
3333
return korean;
3434
}
3535

36-
public static Method from (String value) {
36+
public static Method from(String value) {
3737
if (value == null || value.isBlank()) {
3838
throw new CustomException(ErrorCode.INVALID_PAYMENT_INFO);
3939
}

0 commit comments

Comments
 (0)