Skip to content

Commit 1c05a8a

Browse files
committed
Merge remote-tracking branch 'origin/dev' into fix/287
2 parents ab6baa8 + 08ea0b1 commit 1c05a8a

File tree

21 files changed

+500
-865
lines changed

21 files changed

+500
-865
lines changed

src/main/java/com/back/domain/board/post/service/PostService.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.back.domain.file.entity.FileAttachment;
1818
import com.back.domain.file.repository.AttachmentMappingRepository;
1919
import com.back.domain.file.repository.FileAttachmentRepository;
20+
import com.back.domain.file.service.FileService;
2021
import com.back.domain.user.common.entity.User;
2122
import com.back.domain.user.common.repository.UserRepository;
2223
import com.back.global.exception.CustomException;
@@ -40,6 +41,7 @@ public class PostService {
4041
private final PostCategoryRepository postCategoryRepository;
4142
private final FileAttachmentRepository fileAttachmentRepository;
4243
private final AttachmentMappingRepository attachmentMappingRepository;
44+
private final FileService fileService;
4345

4446
/**
4547
* 게시글 생성 서비스
@@ -183,7 +185,15 @@ public void deletePost(Long postId, Long userId) {
183185
throw new CustomException(ErrorCode.POST_NO_PERMISSION);
184186
}
185187

186-
// AttachmentMapping 매핑 제거
188+
// 첨부 파일 삭제
189+
List<AttachmentMapping> mappings =
190+
attachmentMappingRepository.findAllByEntityTypeAndEntityId(EntityType.POST, post.getId());
191+
for (AttachmentMapping mapping : mappings) {
192+
FileAttachment fileAttachment = mapping.getFileAttachment();
193+
if (fileAttachment != null) {
194+
fileService.deleteFile(fileAttachment.getId(), userId);
195+
}
196+
}
187197
attachmentMappingRepository.deleteAllByEntityTypeAndEntityId(EntityType.POST, post.getId());
188198

189199
// Post 삭제

src/main/java/com/back/domain/file/entity/AttachmentMapping.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
@Getter
1010
@NoArgsConstructor
1111
public class AttachmentMapping extends BaseEntity {
12-
@ManyToOne(fetch = FetchType.LAZY)
13-
@JoinColumn(name = "attachment_id")
12+
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
13+
@JoinColumn(name = "attachment_id", nullable = false)
1414
private FileAttachment fileAttachment;
1515

1616
@Enumerated(EnumType.STRING)

src/main/java/com/back/domain/file/entity/FileAttachment.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
import lombok.NoArgsConstructor;
88
import org.springframework.web.multipart.MultipartFile;
99

10-
import java.util.ArrayList;
11-
import java.util.List;
12-
1310
@Entity
1411
@Getter
1512
@NoArgsConstructor
@@ -29,8 +26,8 @@ public class FileAttachment extends BaseEntity {
2926
@JoinColumn(name = "uploaded_by")
3027
private User user;
3128

32-
@OneToMany(mappedBy = "fileAttachment", cascade = CascadeType.ALL, orphanRemoval = true)
33-
private List<AttachmentMapping> attachmentMappings = new ArrayList<>();
29+
@OneToOne(mappedBy = "fileAttachment", fetch = FetchType.LAZY)
30+
private AttachmentMapping attachmentMapping;
3431

3532
public FileAttachment(
3633
String storedName,

src/main/java/com/back/domain/file/repository/AttachmentMappingRepository.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
public interface AttachmentMappingRepository extends JpaRepository<AttachmentMapping, Long> {
1111
List<AttachmentMapping> findAllByEntityTypeAndEntityId(EntityType entityType, Long entityId);
12+
Optional<AttachmentMapping> findByEntityTypeAndEntityId(EntityType entityType, Long entityId);
1213

1314
void deleteAllByEntityTypeAndEntityId(EntityType entityType, Long entityId);
1415
}

src/main/java/com/back/domain/file/repository/FileAttachmentRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,8 @@
33
import com.back.domain.file.entity.FileAttachment;
44
import org.springframework.data.jpa.repository.JpaRepository;
55

6+
import java.util.Optional;
7+
68
public interface FileAttachmentRepository extends JpaRepository<FileAttachment, Long> {
9+
Optional<FileAttachment> findByPublicURL(String publicUrl);
710
}

src/main/java/com/back/domain/user/account/controller/AccountController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import com.back.domain.board.post.dto.PostListResponse;
66
import com.back.domain.user.account.controller.docs.AccountControllerDocs;
77
import com.back.domain.user.account.dto.ChangePasswordRequest;
8-
import com.back.domain.user.account.dto.UpdateUserProfileRequest;
8+
import com.back.domain.user.account.dto.UserProfileRequest;
99
import com.back.domain.user.account.dto.UserDetailResponse;
1010
import com.back.domain.user.account.service.AccountService;
1111
import com.back.global.common.dto.RsData;
@@ -42,7 +42,7 @@ public ResponseEntity<RsData<UserDetailResponse>> getMyInfo(
4242
@PatchMapping
4343
public ResponseEntity<RsData<UserDetailResponse>> updateMyProfile(
4444
@AuthenticationPrincipal CustomUserDetails user,
45-
@Valid @RequestBody UpdateUserProfileRequest request
45+
@Valid @RequestBody UserProfileRequest request
4646
) {
4747
UserDetailResponse updated = accountService.updateUserProfile(user.getUserId(), request);
4848
return ResponseEntity

src/main/java/com/back/domain/user/account/controller/docs/AccountControllerDocs.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import com.back.domain.board.common.dto.PageResponse;
55
import com.back.domain.board.post.dto.PostListResponse;
66
import com.back.domain.user.account.dto.ChangePasswordRequest;
7-
import com.back.domain.user.account.dto.UpdateUserProfileRequest;
7+
import com.back.domain.user.account.dto.UserProfileRequest;
88
import com.back.domain.user.account.dto.UserDetailResponse;
99
import com.back.global.common.dto.RsData;
1010
import com.back.global.security.user.CustomUserDetails;
@@ -307,7 +307,7 @@ ResponseEntity<RsData<UserDetailResponse>> getMyInfo(
307307
})
308308
ResponseEntity<RsData<UserDetailResponse>> updateMyProfile(
309309
@AuthenticationPrincipal CustomUserDetails user,
310-
@Valid @RequestBody UpdateUserProfileRequest request
310+
@Valid @RequestBody UserProfileRequest request
311311
);
312312

313313
@Operation(

src/main/java/com/back/domain/user/account/dto/UpdateUserProfileRequest.java renamed to src/main/java/com/back/domain/user/account/dto/UserProfileRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* @param bio 사용자의 자기소개
1414
* @param birthDate 사용자의 생년월일
1515
*/
16-
public record UpdateUserProfileRequest(
16+
public record UserProfileRequest(
1717
@NotBlank @Size(max = 20) String nickname,
1818
String profileImageUrl,
1919
@Size(max = 255) String bio,

src/main/java/com/back/domain/user/account/service/AccountService.java

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@
55
import com.back.domain.board.common.dto.PageResponse;
66
import com.back.domain.board.post.dto.PostListResponse;
77
import com.back.domain.board.post.repository.PostRepository;
8+
import com.back.domain.file.entity.AttachmentMapping;
9+
import com.back.domain.file.entity.EntityType;
10+
import com.back.domain.file.entity.FileAttachment;
11+
import com.back.domain.file.repository.AttachmentMappingRepository;
12+
import com.back.domain.file.repository.FileAttachmentRepository;
13+
import com.back.domain.file.service.FileService;
814
import com.back.domain.user.account.dto.ChangePasswordRequest;
9-
import com.back.domain.user.account.dto.UpdateUserProfileRequest;
15+
import com.back.domain.user.account.dto.UserProfileRequest;
1016
import com.back.domain.user.account.dto.UserDetailResponse;
1117
import com.back.domain.user.common.entity.User;
1218
import com.back.domain.user.common.entity.UserProfile;
@@ -23,6 +29,8 @@
2329
import org.springframework.stereotype.Service;
2430
import org.springframework.transaction.annotation.Transactional;
2531

32+
import java.util.Optional;
33+
2634
@Service
2735
@RequiredArgsConstructor
2836
@Transactional
@@ -31,6 +39,9 @@ public class AccountService {
3139
private final UserProfileRepository userProfileRepository;
3240
private final CommentRepository commentRepository;
3341
private final PostRepository postRepository;
42+
private final FileAttachmentRepository fileAttachmentRepository;
43+
private final AttachmentMappingRepository attachmentMappingRepository;
44+
private final FileService fileService;
3445
private final PasswordEncoder passwordEncoder;
3546

3647
/**
@@ -54,7 +65,7 @@ public UserDetailResponse getUserInfo(Long userId) {
5465
* 3. UserProfile 업데이트
5566
* 4. UserDetailResponse 변환 및 반환
5667
*/
57-
public UserDetailResponse updateUserProfile(Long userId, UpdateUserProfileRequest request) {
68+
public UserDetailResponse updateUserProfile(Long userId, UserProfileRequest request) {
5869

5970
// 사용자 조회 및 상태 검증
6071
User user = getValidUser(userId);
@@ -71,10 +82,48 @@ public UserDetailResponse updateUserProfile(Long userId, UpdateUserProfileReques
7182
profile.setBio(request.bio());
7283
profile.setBirthDate(request.birthDate());
7384

85+
// 프로필 이미지 및 매핑 업데이트
86+
updateProfileImage(userId, request.profileImageUrl());
87+
7488
// UserDetailResponse로 변환하여 반환
7589
return UserDetailResponse.from(user);
7690
}
7791

92+
/**
93+
* 프로필 이미지 및 매핑 교체 로직
94+
* - 기존 이미지(S3 + FileAttachment + Mapping) 삭제 후 새 매핑 생성
95+
*/
96+
private void updateProfileImage(Long userId, String newImageUrl) {
97+
98+
// 기존 매핑 및 파일 삭제
99+
attachmentMappingRepository.findByEntityTypeAndEntityId(EntityType.PROFILE, userId)
100+
.ifPresent(existingMapping -> {
101+
FileAttachment oldAttachment = existingMapping.getFileAttachment();
102+
if (oldAttachment != null) {
103+
fileService.deleteFile(oldAttachment.getId(), userId);
104+
}
105+
attachmentMappingRepository.delete(existingMapping);
106+
});
107+
108+
// 새 이미지가 없는 경우
109+
if (newImageUrl == null || newImageUrl.isBlank()) {
110+
return;
111+
}
112+
113+
// 새 파일 조회 및 검증
114+
FileAttachment newAttachment = fileAttachmentRepository
115+
.findByPublicURL(newImageUrl)
116+
.orElseThrow(() -> new CustomException(ErrorCode.FILE_NOT_FOUND));
117+
118+
if (!newAttachment.getUser().getId().equals(userId)) {
119+
throw new CustomException(ErrorCode.FILE_ACCESS_DENIED);
120+
}
121+
122+
// 새 매핑 생성 및 저장
123+
AttachmentMapping newMapping = new AttachmentMapping(newAttachment, EntityType.PROFILE, userId);
124+
attachmentMappingRepository.save(newMapping);
125+
}
126+
78127
/**
79128
* 비밀번호 변경 서비스
80129
* 1. 사용자 조회 및 상태 검증
@@ -114,6 +163,9 @@ public void deleteUser(Long userId) {
114163
// 사용자 조회 및 상태 검증
115164
User user = getValidUser(userId);
116165

166+
// 프로필 이미지 및 매핑 삭제
167+
deleteProfileImage(userId);
168+
117169
// 상태 변경 (soft delete)
118170
user.setUserStatus(UserStatus.DELETED);
119171

@@ -133,6 +185,20 @@ public void deleteUser(Long userId) {
133185
}
134186
}
135187

188+
/**
189+
* 프로필 이미지 및 매핑 삭제
190+
*/
191+
private void deleteProfileImage(Long userId) {
192+
attachmentMappingRepository.findByEntityTypeAndEntityId(EntityType.PROFILE, userId)
193+
.ifPresent(mapping -> {
194+
FileAttachment attachment = mapping.getFileAttachment();
195+
if (attachment != null) {
196+
fileService.deleteFile(attachment.getId(), userId);
197+
}
198+
attachmentMappingRepository.delete(mapping);
199+
});
200+
}
201+
136202
/**
137203
* 내 게시글 목록 조회 서비스
138204
* 1. 사용자 조회 및 상태 검증
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.back.global.websocket.event;
2+
3+
import lombok.Getter;
4+
import org.springframework.context.ApplicationEvent;
5+
6+
/**
7+
* WebSocket 세션 연결이 종료되었을 때 발생하는 내부 이벤트.
8+
* 이 이벤트는 SessionManager가 발행하고, ParticipantService가 구독하여 처리합니다.
9+
*/
10+
@Getter
11+
public class SessionDisconnectedEvent extends ApplicationEvent {
12+
13+
private final Long userId;
14+
15+
public SessionDisconnectedEvent(Object source, Long userId) {
16+
super(source);
17+
this.userId = userId;
18+
}
19+
}
20+

0 commit comments

Comments
 (0)