Skip to content

Commit dd8f8e8

Browse files
authored
Merge pull request #62 from Block-Guard/fix/#57/update-guardian
[Fix] 보호자 프로필 사진 업데이트 오류 수정
2 parents f1e896b + 3341818 commit dd8f8e8

File tree

5 files changed

+81
-14
lines changed

5 files changed

+81
-14
lines changed

src/main/java/com/blockguard/server/domain/guardian/api/GuardianApi.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.blockguard.server.domain.guardian.dto.request.CreateGuardianRequest;
44
import com.blockguard.server.domain.guardian.dto.request.UpdateGuardianPrimaryRequest;
5+
import com.blockguard.server.domain.guardian.dto.request.UpdateGuardianRequest;
56
import com.blockguard.server.domain.guardian.dto.response.GuardianResponse;
67
import com.blockguard.server.domain.guardian.dto.response.GuardiansListResponse;
78
import com.blockguard.server.domain.guardian.application.GuardianService;
@@ -47,8 +48,8 @@ public ResponseEntity<BaseResponse<GuardianResponse>> createGuardian(@Parameter(
4748
@PutMapping(value = "/{id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
4849
public ResponseEntity<BaseResponse<GuardianResponse>> updateGuardian(@Parameter(hidden = true) @CurrentUser User user,
4950
@PathVariable("id") Long guardianId,
50-
@Valid CreateGuardianRequest createGuardianRequest) {
51-
GuardianResponse guardianResponse = guardianService.updateGuardian(user, guardianId, createGuardianRequest);
51+
@Valid UpdateGuardianRequest updateGuardianRequest) {
52+
GuardianResponse guardianResponse = guardianService.updateGuardian(user, guardianId, updateGuardianRequest);
5253
return ResponseEntity.ok(BaseResponse.of(SuccessCode.GUARDIAN_UPDATED, guardianResponse));
5354
}
5455

src/main/java/com/blockguard/server/domain/guardian/application/GuardianService.java

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.blockguard.server.domain.guardian.domain.Guardian;
55
import com.blockguard.server.domain.guardian.dto.request.CreateGuardianRequest;
66
import com.blockguard.server.domain.guardian.dto.request.UpdateGuardianPrimaryRequest;
7+
import com.blockguard.server.domain.guardian.dto.request.UpdateGuardianRequest;
78
import com.blockguard.server.domain.guardian.dto.response.GuardianResponse;
89
import com.blockguard.server.domain.guardian.dto.response.GuardiansListResponse;
910
import com.blockguard.server.domain.user.domain.User;
@@ -54,31 +55,63 @@ public GuardianResponse createGuardian(User user, CreateGuardianRequest request)
5455
}
5556

5657
@Transactional
57-
public GuardianResponse updateGuardian(User user, Long guardianId, CreateGuardianRequest request) {
58+
public GuardianResponse updateGuardian(User user, Long guardianId, UpdateGuardianRequest request) {
5859
Guardian guardian = guardianRepository.findByIdAndUser(guardianId, user)
5960
.orElseThrow(() -> new BusinessExceptionHandler(ErrorCode.GUARDIAN_NOT_FOUND));
6061

6162
if (!guardian.getName().equals(request.getName()) &&
6263
guardianRepository.existsByUserAndNameAndDeletedAtIsNull(user, request.getName())) {
6364
throw new BusinessExceptionHandler(ErrorCode.DUPLICATE_GUARDIAN_NAME);
6465
}
66+
// 입력 플래그/파일 상태
67+
final boolean wantDefault = Boolean.TRUE.equals(request.getIsDefaultImage());
68+
final boolean hasNewFile = request.getProfileImage() != null && !request.getProfileImage().isEmpty();
6569

66-
String key = null;
67-
if (request.getProfileImage() != null && !request.getProfileImage().isEmpty()){
68-
// 새 이미지 업로드
69-
key = s3Service.upload(request.getProfileImage(), "guardians");
70-
// 예전 이미지 삭제
71-
if (guardian.getProfileImageKey() != null){
72-
s3Service.delete(guardian.getProfileImageKey());
73-
}
70+
// 기본이미지로 변경 + 새 파일 업로드 동시 요청 불가
71+
if (wantDefault && hasNewFile) {
72+
throw new BusinessExceptionHandler(ErrorCode.UPDATE_PROFILE_CONFLICT);
7473
}
75-
guardian.updateGuardianInfo(request.getName(), request.getPhoneNumber(), key);
7674

77-
Guardian updated = guardianRepository.save(guardian);
75+
String currentKey = guardian.getProfileImageKey();
76+
String nextKey = currentKey;
7877

78+
// 기본 이미지로 변경하는 경우
79+
if (wantDefault) {
80+
nextKey = null;
81+
// 커밋 후 기존 파일 삭제, 롤백 시 아무것도 안 함
82+
registerAfterCommitDelete(currentKey, null);
83+
}
84+
// 새 이미지로 변경하는 경우
85+
else if (hasNewFile) {
86+
// 새 이미지 업로드
87+
String uploadedKey = s3Service.upload(request.getProfileImage(), "guardians");
88+
nextKey = uploadedKey;
89+
// 커밋 후 기존 파일 삭제, 롤백 시 방금 올린 파일 삭제
90+
registerAfterCommitDelete(currentKey, uploadedKey);
91+
}
92+
// 기존 이미지 유지 (nextKey = currentKey)
93+
guardian.updateGuardianInfo(request.getName(), request.getPhoneNumber(), nextKey);
94+
Guardian updated = guardianRepository.save(guardian);
7995
return GuardianResponse.from(updated, s3Service);
8096
}
8197

98+
// 트랜잭션 커밋 후 기존 키 삭제, 롤백 시 새로 업로드한 키 삭제
99+
private void registerAfterCommitDelete(String oldKey, String uploadedKey) {
100+
org.springframework.transaction.support.TransactionSynchronizationManager
101+
.registerSynchronization(new org.springframework.transaction.support.TransactionSynchronization() {
102+
@Override public void afterCommit() {
103+
if (oldKey != null && !oldKey.equals(uploadedKey)) {
104+
s3Service.delete(oldKey);
105+
}
106+
}
107+
@Override public void afterCompletion(int status) {
108+
if (status == STATUS_ROLLED_BACK && uploadedKey != null) {
109+
s3Service.delete(uploadedKey);
110+
}
111+
}
112+
});
113+
}
114+
82115
@Transactional
83116
public GuardianResponse updatePrimary(User user, Long guardianId, UpdateGuardianPrimaryRequest request) {
84117
Guardian guardian = guardianRepository.findByIdAndUser(guardianId, user)

src/main/java/com/blockguard/server/domain/guardian/dto/request/CreateGuardianRequest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@
33
import io.swagger.v3.oas.annotations.media.Schema;
44
import jakarta.validation.constraints.NotBlank;
55
import jakarta.validation.constraints.Pattern;
6+
import jakarta.validation.constraints.Size;
67
import lombok.AllArgsConstructor;
78
import lombok.Builder;
89
import lombok.Getter;
10+
import lombok.NoArgsConstructor;
911
import org.springframework.web.multipart.MultipartFile;
1012

1113
@Getter
1214
@AllArgsConstructor
1315
@Builder
1416
public class CreateGuardianRequest {
1517
@NotBlank
18+
@Size(max = 50, message = "name length must be <= 50")
1619
private String name;
1720

1821
@NotBlank
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.blockguard.server.domain.guardian.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.NotBlank;
5+
import jakarta.validation.constraints.Pattern;
6+
import jakarta.validation.constraints.Size;
7+
import lombok.AllArgsConstructor;
8+
import lombok.Builder;
9+
import lombok.Getter;
10+
import lombok.NoArgsConstructor;
11+
import org.springframework.web.multipart.MultipartFile;
12+
13+
@Getter
14+
@AllArgsConstructor
15+
@Builder
16+
public class UpdateGuardianRequest {
17+
@NotBlank
18+
@Size(max = 50, message = "name length must be <= 50")
19+
private String name;
20+
21+
@NotBlank
22+
@Pattern(regexp = "^\\d{3}-\\d{4}-\\d{4}$", message = "phoneNumber must match 010-1234-5678 format")
23+
private String phoneNumber;
24+
25+
@Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED)
26+
private MultipartFile profileImage;
27+
28+
private Boolean isDefaultImage;
29+
}

src/main/java/com/blockguard/server/global/config/swagger/SwaggerResponseDescription.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ public enum SwaggerResponseDescription {
7070
ErrorCode.FILE_NAME_NOT_FOUND,
7171
ErrorCode.INVALID_DIRECTORY_ROUTE,
7272
ErrorCode.FILE_SIZE_EXCEEDED,
73-
ErrorCode.DUPLICATE_GUARDIAN_NAME
73+
ErrorCode.DUPLICATE_GUARDIAN_NAME,
74+
ErrorCode.UPDATE_PROFILE_CONFLICT
7475
))),
7576

7677
UPDATE_GUARDIAN_PRIMARY_FAIL(new LinkedHashSet<>(Set.of(

0 commit comments

Comments
 (0)