Skip to content

Commit 79e74b6

Browse files
authored
[feat] 펀딩 새소식 이미지 기능 구현 (#379)
1 parent eda0e91 commit 79e74b6

File tree

6 files changed

+61
-1
lines changed

6 files changed

+61
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ db_dev.trace.db
4242
.DS_Store
4343
.env
4444
/src/main/generated/
45+
/k6
4546

4647
### GA4 Service Account Key (민감 정보!) ###
4748
src/main/resources/ga4-service-account.json

src/main/java/com/back/domain/funding/controller/FundingNewsController.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,32 @@
33
import com.back.domain.funding.dto.request.FundingNewsCreateRequest;
44
import com.back.domain.funding.service.FundingNewsService;
55
import com.back.global.rsData.RsData;
6+
import com.back.global.s3.FileType;
7+
import com.back.global.s3.S3Service;
8+
import com.back.global.s3.UploadResultResponse;
69
import io.swagger.v3.oas.annotations.Operation;
10+
import io.swagger.v3.oas.annotations.Parameter;
711
import io.swagger.v3.oas.annotations.tags.Tag;
812
import jakarta.validation.Valid;
913
import jakarta.validation.constraints.Positive;
1014
import lombok.RequiredArgsConstructor;
1115
import org.springframework.http.HttpStatus;
16+
import org.springframework.http.MediaType;
1217
import org.springframework.http.ResponseEntity;
1318
import org.springframework.security.access.prepost.PreAuthorize;
1419
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1520
import org.springframework.web.bind.annotation.*;
21+
import org.springframework.web.multipart.MultipartFile;
22+
23+
import java.util.List;
1624

1725
@RestController
1826
@RequestMapping("/api/fundings")
1927
@RequiredArgsConstructor
2028
@Tag(name = "펀딩 새소식", description = "펀딩 새소식 API 컨트롤러")
2129
public class FundingNewsController{
2230
private final FundingNewsService fundingNewsService;
31+
private final S3Service s3Service;
2332

2433
@PostMapping("/{id}/news")
2534
@PreAuthorize("isAuthenticated()")
@@ -34,6 +43,34 @@ public ResponseEntity<RsData<?>> addNews(
3443
.body(new RsData<>("201", "새소식이 등록되었습니다.", newsId));
3544
}
3645

46+
@PostMapping(value = "/{id}/news/images", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
47+
@PreAuthorize("hasAuthority('ROLE_ARTIST') or hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_ROOT')")
48+
@Operation(
49+
summary = "펀딩 새소식 이미지 업로드",
50+
description = "펀딩 새소식에 사용할 이미지 업로드. 한 장만 업로드 가능"
51+
)
52+
public ResponseEntity<RsData<List<UploadResultResponse>>> uploadNewsImage(
53+
@PathVariable @Positive Long id,
54+
@Parameter(description = "업로드 할 이미지 파일", required = true)
55+
@RequestPart("file")MultipartFile file) {
56+
List<UploadResultResponse> result = s3Service.uploadFile(file, "funding-news-images", FileType.ADDITIONAL);
57+
return ResponseEntity.ok(RsData.of("200", "이미지 업로드 성공", result));
58+
}
59+
60+
@DeleteMapping("/{id}/news/images")
61+
@PreAuthorize("hasAuthority('ROLE_ARTIST') or hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_ROOT')")
62+
@Operation(
63+
summary = "펀딩 새소식 이미지 삭제",
64+
description = "S3에 업로드된 이미지 삭제"
65+
)
66+
public ResponseEntity<RsData<String>> deleteNewsImages(
67+
@PathVariable @Positive Long id,
68+
@Parameter(description = "삭제할 파일의 s3Key", required = true)
69+
@RequestParam String s3Key) {
70+
s3Service.deleteFile(s3Key);
71+
return ResponseEntity.ok(RsData.of("200", "이미지가 성공적으로 삭제되었습니다.", s3Key));
72+
}
73+
3774
@DeleteMapping("/{fundingId}/news/{newsId}")
3875
@PreAuthorize("isAuthenticated()")
3976
@Operation(summary = "펀딩 새소식 삭제", description = "펀딩 새소식을 삭제")

src/main/java/com/back/domain/funding/dto/request/FundingNewsCreateRequest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
public record FundingNewsCreateRequest(
66
@NotBlank String title,
77
@NotBlank String content,
8-
String imageUrl
8+
String imageUrl,
9+
String s3Key
910
) {}

src/main/java/com/back/domain/funding/dto/response/FundingNewsItemDto.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public record FundingNewsItemDto(
1111
String title,
1212
String content,
1313
String imageUrl,
14+
String s3Key,
1415
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
1516
LocalDateTime createDate
1617
) {
@@ -21,6 +22,7 @@ public FundingNewsItemDto(FundingNews u) {
2122
u.getTitle(),
2223
u.getContent(),
2324
u.getImageUrl(),
25+
u.getS3Key(),
2426
u.getCreateDate()
2527
);
2628
}

src/main/java/com/back/domain/funding/entity/FundingNews.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,18 @@ public class FundingNews extends BaseEntity {
3131

3232
private String imageUrl;
3333

34+
private String s3Key;
35+
3436
@Column(nullable = false)
3537
@Builder.Default
3638
private boolean deleted = false;
3739

3840
public void delete() {
3941
this.deleted = true;
4042
}
43+
44+
public void removeImage() {
45+
this.imageUrl = null;
46+
this.s3Key = null;
47+
}
4148
}

src/main/java/com/back/domain/funding/service/FundingNewsService.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import com.back.domain.user.entity.User;
99
import com.back.domain.user.repository.UserRepository;
1010
import com.back.global.exception.ServiceException;
11+
import com.back.global.s3.S3Service;
12+
import com.back.global.s3.S3ValidationService;
1113
import lombok.RequiredArgsConstructor;
1214
import org.springframework.stereotype.Service;
1315
import org.springframework.transaction.annotation.Transactional;
@@ -18,6 +20,8 @@ public class FundingNewsService {
1820
private final FundingNewsRepository fundingNewsRepository;
1921
private final FundingRepository fundingRepository;
2022
private final UserRepository userRepository;
23+
private final S3Service s3Service;
24+
private final S3ValidationService s3ValidationService;
2125

2226
@Transactional
2327
public Long addFundingNews(Long fundingId, FundingNewsCreateRequest req, String userEmail) {
@@ -31,12 +35,17 @@ public Long addFundingNews(Long fundingId, FundingNewsCreateRequest req, String
3135
throw new ServiceException("403", "권한이 없습니다.");
3236
}
3337

38+
if (req.s3Key() != null && !req.s3Key().isBlank()) {
39+
s3ValidationService.validateFileExists(req.s3Key());
40+
}
41+
3442
FundingNews news = FundingNews.builder()
3543
.funding(funding)
3644
.artist(artist)
3745
.title(req.title())
3846
.content(req.content())
3947
.imageUrl(req.imageUrl())
48+
.s3Key(req.s3Key())
4049
.build();
4150

4251
fundingNewsRepository.save(news);
@@ -54,6 +63,9 @@ public void deleteFundingNews(Long fundingId ,Long newsId, String userEmail) {
5463
if (!funding.getUser().getId().equals(artist.getId())) {
5564
throw new ServiceException("403", "권한이 없습니다.");
5665
}
66+
if (news.getS3Key() != null && !news.getS3Key().isBlank()){
67+
s3Service.deleteFile(news.getS3Key());
68+
}
5769
news.delete();
5870
}
5971
}

0 commit comments

Comments
 (0)