Skip to content

Commit 32ecb79

Browse files
authored
Fix: [Community] 게시물 작성시 사진은 필수, 제목과 내용은 선택으로 수정 및 알람 발송 (#65)
* Fix: [Community] 게시물 작성시 사진은 필수, 제목과 내용은 선택으로 수정 * Fix: [Community] 게시물 작성, 수정, 삭제 시 알람 발송
1 parent 7e4207c commit 32ecb79

11 files changed

Lines changed: 138 additions & 4 deletions

File tree

runtracker/src/main/java/com/runtracker/domain/community/entity/Post.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ public class Post extends BaseEntity {
2525
@Column(name = "crew_id", nullable = false)
2626
private Long crewId;
2727

28-
@Column(name = "title", length = 100, nullable = false)
28+
@Column(name = "title", length = 100)
2929
private String title;
3030

31-
@Column(name = "content", columnDefinition = "TEXT", nullable = false)
31+
@Column(name = "content", columnDefinition = "TEXT")
3232
private String content;
3333

3434
@Column(name = "photos", columnDefinition = "JSON")

runtracker/src/main/java/com/runtracker/domain/community/enums/CommunityErrorCode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ public enum CommunityErrorCode implements ResponseCode {
1717
COMMENT_CREATION_FAILED("CM007", "Failed to create comment"),
1818
UNAUTHORIZED_COMMENT_ACCESS("CM008", "Unauthorized access to comment"),
1919
NO_POSTS_FOUND("CM009", "No posts found in this crew"),
20-
NO_SEARCH_RESULTS("CM010", "No posts found for the given keyword");
20+
NO_SEARCH_RESULTS("CM010", "No posts found for the given keyword"),
21+
PHOTOS_REQUIRED("CM011", "Photos are required for post creation");
2122

2223
private final String statusCode;
2324
private final String message;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.runtracker.domain.community.event;
2+
3+
public record PostCreateEvent(Long authorMemberId, Long postId) {
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.runtracker.domain.community.event;
2+
3+
public record PostDeleteEvent(Long authorMemberId, Long postId) {
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.runtracker.domain.community.event;
2+
3+
public record PostUpdateEvent(Long authorMemberId, Long postId) {
4+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.runtracker.domain.community.exception;
2+
3+
import com.runtracker.domain.community.enums.CommunityErrorCode;
4+
import com.runtracker.global.exception.CustomException;
5+
6+
public class PhotosRequiredException extends CustomException {
7+
public PhotosRequiredException() {
8+
super(CommunityErrorCode.PHOTOS_REQUIRED);
9+
}
10+
11+
public PhotosRequiredException(String message) {
12+
super(CommunityErrorCode.PHOTOS_REQUIRED, message);
13+
}
14+
}

runtracker/src/main/java/com/runtracker/domain/community/service/CommunityService.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import com.runtracker.domain.community.dto.PostDetailDTO;
77
import com.runtracker.domain.community.dto.PostListDTO;
88
import com.runtracker.domain.community.dto.RunningMetaDTO;
9+
import com.runtracker.domain.community.event.PostCreateEvent;
10+
import com.runtracker.domain.community.event.PostUpdateEvent;
11+
import com.runtracker.domain.community.event.PostDeleteEvent;
912
import com.runtracker.domain.community.event.PostLikeEvent;
1013
import com.runtracker.domain.community.event.PostCommentEvent;
1114
import com.runtracker.domain.community.entity.Post;
@@ -17,6 +20,7 @@
1720
import com.runtracker.domain.community.exception.NoPostsFoundException;
1821
import com.runtracker.domain.community.exception.NoSearchResultsException;
1922
import com.runtracker.domain.community.exception.NotLikedPostException;
23+
import com.runtracker.domain.community.exception.PhotosRequiredException;
2024
import com.runtracker.domain.community.exception.PostCreationFailedException;
2125
import com.runtracker.domain.community.exception.PostNotFoundException;
2226
import com.runtracker.domain.community.exception.UnauthorizedCommentAccessException;
@@ -53,6 +57,10 @@ public void createPost(PostDTO postDTO, UserDetailsImpl userDetails) {
5357
Long crewId = userDetails.getCrewMembership().getCrewId();
5458
crewAuthorizationUtil.validateCrewMemberAccess(userDetails, crewId);
5559

60+
if (postDTO.getPhotos() == null || postDTO.getPhotos().isEmpty()) {
61+
throw new PhotosRequiredException();
62+
}
63+
5664
try {
5765
Post.PostBuilder postBuilder = Post.builder()
5866
.memberId(userDetails.getMemberId())
@@ -71,6 +79,8 @@ public void createPost(PostDTO postDTO, UserDetailsImpl userDetails) {
7179
Post post = postBuilder.build();
7280

7381
postRepository.save(post);
82+
83+
eventPublisher.publishEvent(new PostCreateEvent(userDetails.getMemberId(), post.getId()));
7484
} catch (Exception e) {
7585
throw new PostCreationFailedException();
7686
}
@@ -104,6 +114,8 @@ public void updatePost(Long postId, PostDTO postDTO, UserDetailsImpl userDetails
104114
postDTO.getMeta().getAvgSpeed()
105115
);
106116
}
117+
118+
eventPublisher.publishEvent(new PostUpdateEvent(userDetails.getMemberId(), postId));
107119
}
108120

109121
@Transactional
@@ -118,6 +130,8 @@ public void deletePost(Long postId, UserDetailsImpl userDetails) {
118130
}
119131

120132
postRepository.deleteById(postId);
133+
134+
eventPublisher.publishEvent(new PostDeleteEvent(userDetails.getMemberId(), postId));
121135
}
122136

123137
@Transactional

runtracker/src/main/java/com/runtracker/domain/notification/eventHandler/NotificationEventHandler.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import com.runtracker.domain.crew.event.CrewDeleteEvent;
88
import com.runtracker.domain.crew.event.CrewBanEvent;
99
import com.runtracker.domain.crew.event.CrewLeaveEvent;
10+
import com.runtracker.domain.community.event.PostCreateEvent;
11+
import com.runtracker.domain.community.event.PostUpdateEvent;
12+
import com.runtracker.domain.community.event.PostDeleteEvent;
1013
import com.runtracker.domain.community.event.PostLikeEvent;
1114
import com.runtracker.domain.community.event.PostCommentEvent;
1215
import com.runtracker.domain.schedule.event.ScheduleCreateEvent;
@@ -165,6 +168,48 @@ public void sendPostCommentNotification(PostCommentEvent event) {
165168
}
166169
}
167170

171+
@Async
172+
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
173+
public void sendPostCreateNotification(PostCreateEvent event) {
174+
try {
175+
notificationService.notifyPostCreate(event.authorMemberId());
176+
} catch (Exception e) {
177+
log.error("Failed to send post create notification - authorMemberId: {}, postId: {}, error: {}",
178+
event.authorMemberId(),
179+
event.postId(),
180+
e.getMessage());
181+
throw e;
182+
}
183+
}
184+
185+
@Async
186+
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
187+
public void sendPostUpdateNotification(PostUpdateEvent event) {
188+
try {
189+
notificationService.notifyPostUpdate(event.authorMemberId());
190+
} catch (Exception e) {
191+
log.error("Failed to send post update notification - authorMemberId: {}, postId: {}, error: {}",
192+
event.authorMemberId(),
193+
event.postId(),
194+
e.getMessage());
195+
throw e;
196+
}
197+
}
198+
199+
@Async
200+
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
201+
public void sendPostDeleteNotification(PostDeleteEvent event) {
202+
try {
203+
notificationService.notifyPostDelete(event.authorMemberId());
204+
} catch (Exception e) {
205+
log.error("Failed to send post delete notification - authorMemberId: {}, postId: {}, error: {}",
206+
event.authorMemberId(),
207+
event.postId(),
208+
e.getMessage());
209+
throw e;
210+
}
211+
}
212+
168213
@Async
169214
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
170215
public void sendScheduleCreateNotification(ScheduleCreateEvent event) {

runtracker/src/main/java/com/runtracker/domain/notification/service/NotificationService.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,48 @@ public void notifyPostComment(Long commenterMemberId, Long postAuthorMemberId) {
200200
fcmClient.send(title, content, fcmToken);
201201
}
202202

203+
@Transactional
204+
public void notifyPostCreate(Long authorMemberId) {
205+
String title = messages.get("notify.post.create.title");
206+
String content = messages.get("notify.post.create.content");
207+
208+
String fcmToken = memberService.getFcmToken(authorMemberId).orElse(null);
209+
if (fcmToken == null || fcmToken.trim().isEmpty()) {
210+
log.info("FCM token not found for post author - skipping post create notification: authorMemberId={}", authorMemberId);
211+
return;
212+
}
213+
214+
fcmClient.send(title, content, fcmToken);
215+
}
216+
217+
@Transactional
218+
public void notifyPostUpdate(Long authorMemberId) {
219+
String title = messages.get("notify.post.update.title");
220+
String content = messages.get("notify.post.update.content");
221+
222+
String fcmToken = memberService.getFcmToken(authorMemberId).orElse(null);
223+
if (fcmToken == null || fcmToken.trim().isEmpty()) {
224+
log.info("FCM token not found for post author - skipping post update notification: authorMemberId={}", authorMemberId);
225+
return;
226+
}
227+
228+
fcmClient.send(title, content, fcmToken);
229+
}
230+
231+
@Transactional
232+
public void notifyPostDelete(Long authorMemberId) {
233+
String title = messages.get("notify.post.delete.title");
234+
String content = messages.get("notify.post.delete.content");
235+
236+
String fcmToken = memberService.getFcmToken(authorMemberId).orElse(null);
237+
if (fcmToken == null || fcmToken.trim().isEmpty()) {
238+
log.info("FCM token not found for post author - skipping post delete notification: authorMemberId={}", authorMemberId);
239+
return;
240+
}
241+
242+
fcmClient.send(title, content, fcmToken);
243+
}
244+
203245
@Transactional
204246
public void notifyScheduleCreation(Long creatorId, Long crewId, String scheduleTitle) {
205247
Member creator = memberRepository.findById(creatorId).orElseThrow();

runtracker/src/main/resources/messages.properties

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ notify.crew.ban.title=크루 추방 알림
1414
notify.crew.ban.content={0}크루에서 추방되었습니다.
1515
notify.crew.leave.title=크루 탈퇴 알림
1616
notify.crew.leave.content={0}님이 크루를 떠났습니다.
17+
notify.post.create.title=게시물 작성 완료
18+
notify.post.create.content=게시물이 성공적으로 작성되었습니다.
19+
notify.post.update.title=게시물 수정 완료
20+
notify.post.update.content=게시물이 성공적으로 수정되었습니다.
21+
notify.post.delete.title=게시물 삭제 완료
22+
notify.post.delete.content=게시물이 성공적으로 삭제되었습니다.
1723
notify.post.like.title= 게시물 좋아요 알림
1824
notify.post.like.content={0}님이 회원님의 게시물에 좋아요를 눌렀습니다.
1925
notify.post.comment.title=게시물 댓글 알림

0 commit comments

Comments
 (0)