Skip to content

Commit 1b2a08c

Browse files
authored
Merge pull request #83 from Decodeat/Refactor/#80
Refactor: 이미지 신고 수락 api를 formdata 방식으로 변경
2 parents d803fa3 + 5d0e548 commit 1b2a08c

File tree

4 files changed

+53
-32
lines changed

4 files changed

+53
-32
lines changed

src/main/java/com/DecodEat/domain/report/controller/AdminReportController.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
package com.DecodEat.domain.report.controller;
22

3-
import com.DecodEat.domain.report.dto.request.ImageUpdateRequestDto;
43
import com.DecodEat.domain.report.dto.response.ReportResponseDto;
54
import com.DecodEat.domain.report.service.ReportService;
65
import com.DecodEat.global.apiPayload.ApiResponse;
76
import io.swagger.v3.oas.annotations.Operation;
87
import io.swagger.v3.oas.annotations.Parameter;
98
import io.swagger.v3.oas.annotations.Parameters;
10-
import io.swagger.v3.oas.annotations.responses.ApiResponses;
119
import io.swagger.v3.oas.annotations.tags.Tag;
1210
import lombok.RequiredArgsConstructor;
11+
import org.springframework.http.MediaType;
1312
import org.springframework.security.access.prepost.PreAuthorize;
1413
import org.springframework.web.bind.annotation.*;
14+
import org.springframework.web.multipart.MultipartFile;
1515

1616
@RestController
1717
@RequiredArgsConstructor
@@ -26,7 +26,6 @@ public class AdminReportController {
2626
summary = "상품 수정 요청 조회 (관리자)",
2727
description = "관리자가 모든 상품 정보 수정 요청을 페이지별로 조회합니다. 영양 정보 수정과 이미지 확인 요청을 모두 포함합니다.")
2828
@Parameters({
29-
// @PreAuthorize("hasRole('ADMIN')") // Spring Security 사용 시 권한 설정
3029
@Parameter(name = "page", description = "페이지 번호, 0부터 시작합니다.", example = "0"),
3130
@Parameter(name = "size", description = "한 페이지에 보여줄 항목 수", example = "10")
3231
})
@@ -42,7 +41,6 @@ public ApiResponse<ReportResponseDto.ReportListResponseDTO> getReports(
4241
summary = "상품 수정 요청 거절 (관리자)",
4342
description = "관리자가 상품 정보 수정 요청을 거절합니다. 해당 신고 내역의 상태를 REJECTED로 변경합니다.")
4443
@Parameter(name = "reportId", description = "거절할 신고의 ID", example = "1")
45-
// @PreAuthorize("hasRole('ADMIN')") // Spring Security 사용 시 권한 설정
4644
@PatchMapping("/{reportId}/reject")
4745
public ApiResponse<ReportResponseDto> rejectReport(@PathVariable Long reportId) {
4846
return ApiResponse.onSuccess(reportService.rejectReport(reportId));
@@ -57,13 +55,12 @@ public ApiResponse<ReportResponseDto> rejectReport(@PathVariable Long reportId)
5755
@Parameters({
5856
@Parameter(name = "reportId", description = "수락할 신고의 ID", example = "1", required = true)
5957
})
60-
// @PreAuthorize("hasRole('ADMIN')") // Spring Security 사용 시 권한 설정
61-
@PatchMapping("/{reportId}/accept")
58+
@PatchMapping(value = "/{reportId}/accept", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
6259
public ApiResponse<ReportResponseDto> acceptReport(
6360
@PathVariable Long reportId,
64-
@Parameter(name = "imageUpdateRequestDto", description = "이미지 신고 시에만 사용. 새 이미지 URL을 제공하여 교체하거나, 본문을 비워 기존 이미지를 삭제합니다.")
65-
@RequestBody(required = false) ImageUpdateRequestDto requestDto) {
66-
return ApiResponse.onSuccess(reportService.acceptReport(reportId, requestDto));
61+
@Parameter(name = "newImageUrl", description = "이미지 신고 시에만 사용. 교체할 새 이미지 파일을 첨부하거나, 보내지 않으면 기존 이미지가 삭제됩니다.")
62+
@RequestPart(value = "newImageUrl", required = false) MultipartFile newImageUrl) {
63+
return ApiResponse.onSuccess(reportService.acceptReport(reportId, newImageUrl));
6764
}
6865

6966
@Operation(summary = "신고 상세 조회 API", description = "관리자가 특정 신고 내역의 상세 정보를 조회합니다.")

src/main/java/com/DecodEat/domain/report/dto/request/ImageUpdateRequestDto.java

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/main/java/com/DecodEat/domain/report/service/ReportService.java

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import com.DecodEat.domain.products.entity.ProductNutrition;
55
import com.DecodEat.domain.products.repository.ProductRepository;
66
import com.DecodEat.domain.report.converter.ReportConverter;
7-
import com.DecodEat.domain.report.dto.request.ImageUpdateRequestDto;
87
import com.DecodEat.domain.report.dto.request.ProductNutritionUpdateRequestDto;
98
import com.DecodEat.domain.report.dto.response.ReportResponseDto;
109
import com.DecodEat.domain.report.entity.*;
@@ -14,6 +13,7 @@
1413
import com.DecodEat.domain.report.repository.ReportRecordRepository;
1514
import com.DecodEat.domain.users.entity.User;
1615
import com.DecodEat.global.apiPayload.code.status.ErrorStatus;
16+
import com.DecodEat.global.aws.s3.AmazonS3Manager;
1717
import com.DecodEat.global.exception.GeneralException;
1818
import lombok.RequiredArgsConstructor;
1919
import org.springframework.data.domain.Page;
@@ -22,6 +22,9 @@
2222
import org.springframework.data.domain.Sort;
2323
import org.springframework.stereotype.Service;
2424
import org.springframework.transaction.annotation.Transactional;
25+
import org.springframework.web.multipart.MultipartFile;
26+
27+
import java.util.UUID;
2528

2629
import static com.DecodEat.global.apiPayload.code.status.ErrorStatus.*;
2730

@@ -33,6 +36,7 @@ public class ReportService {
3336
private final NutritionReportRepository nutritionReportRepository;
3437
private final ImageReportRepository imageReportRepository;
3538
private final ReportRecordRepository reportRecordRepository;
39+
private final AmazonS3Manager amazonS3Manager;
3640

3741
public ReportResponseDto requestUpdateNutrition(User user, Long productId, ProductNutritionUpdateRequestDto requestDto){
3842

@@ -103,7 +107,7 @@ public ReportResponseDto rejectReport(Long reportId){
103107
* @param reportId 수락할 신고의 ID
104108
* @return 처리 결과를 담은 DTO
105109
*/
106-
public ReportResponseDto acceptReport(Long reportId, ImageUpdateRequestDto requestDto){
110+
public ReportResponseDto acceptReport(Long reportId, MultipartFile newImageUrl){
107111
// 1. ID로 신고 내역 조회
108112
ReportRecord reportRecord = reportRecordRepository.findById(reportId)
109113
.orElseThrow(() -> new GeneralException(REPORT_NOT_FOUND));
@@ -115,16 +119,29 @@ public ReportResponseDto acceptReport(Long reportId, ImageUpdateRequestDto reque
115119

116120
Product product = reportRecord.getProduct();
117121

118-
// 3. 신고 유횽에 따른 로직 분기
122+
// 3. 신고 유형에 따른 로직 분기
119123
if (reportRecord instanceof NutritionReport) {
120124
ProductNutrition productNutrition = product.getProductNutrition();
121125
productNutrition.updateFromReport((NutritionReport) reportRecord);
122126

123127
} else if (reportRecord instanceof ImageReport) {
124-
// 새로운 이미지가 없는 경우 이미지 삭제 -> null로 처리
125-
// 새로운 이미지가 있는 경우 해당 이미지로 변경
126-
String newImageUrl = (requestDto != null) ? requestDto.getNewImageUrl() : null;
127-
product.updateProductImage(newImageUrl);
128+
129+
String oldImageUrl = product.getProductImage();
130+
131+
// 새로운 이미지가 있는 경우 새로운 이미지로 변경
132+
if(newImageUrl != null && !newImageUrl.isEmpty()) {
133+
String imageKey = "products/" + UUID.randomUUID() + "_" + newImageUrl.getOriginalFilename();
134+
String uploadedImageUrl = amazonS3Manager.uploadFile(imageKey, newImageUrl);
135+
product.updateProductImage(uploadedImageUrl);
136+
} else {
137+
// 새로운 이미지가 없는 경우 이미지 삭제 -> null로 처리
138+
product.updateProductImage(null);
139+
}
140+
141+
if(oldImageUrl != null && !oldImageUrl.isEmpty()) {
142+
String oldImageKey = amazonS3Manager.getKeyFromUrl(oldImageUrl);
143+
amazonS3Manager.deleteFile(oldImageKey);
144+
}
128145
}
129146

130147
// 4. reportstatus 상태를 accepted 변경

src/main/java/com/DecodEat/global/aws/s3/AmazonS3Manager.java

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

33
import com.DecodEat.global.config.AmazonConfig;
44
import com.amazonaws.services.s3.AmazonS3;
5+
import com.amazonaws.services.s3.model.DeleteObjectRequest;
56
import com.amazonaws.services.s3.model.ObjectMetadata;
67
import com.amazonaws.services.s3.model.PutObjectRequest;
78
import lombok.RequiredArgsConstructor;
@@ -10,6 +11,7 @@
1011
import org.springframework.web.multipart.MultipartFile;
1112

1213
import java.io.IOException;
14+
import java.net.URL;
1315

1416
@Slf4j
1517
@Component
@@ -31,4 +33,25 @@ public String uploadFile(String keyName, MultipartFile file){
3133

3234
return amazonS3.getUrl(amazonConfig.getBucket(), keyName).toString();
3335
}
36+
37+
public void deleteFile(String keyName){
38+
try {
39+
amazonS3.deleteObject(new DeleteObjectRequest(amazonConfig.getBucket(), keyName));
40+
log.info("S3 파일 삭제 성공: key={}", keyName);
41+
} catch (Exception e) {
42+
log.error("S3 파일 삭제 중 에러 발생: key={}", keyName, e);
43+
}
44+
45+
}
46+
47+
public String getKeyFromUrl(String fileUrl) {
48+
try {
49+
URL url = new URL(fileUrl);
50+
// URL 경로에서 맨 앞의 '/'를 제외한 부분이 key가 됨
51+
return url.getPath().substring(1);
52+
} catch (Exception e) {
53+
log.error("URL로부터 S3 key 추출 중 에러 발생: url={}", fileUrl, e);
54+
throw new RuntimeException("URL parsing error", e);
55+
}
56+
}
3457
}

0 commit comments

Comments
 (0)