Skip to content

Commit ab6ef67

Browse files
authored
Merge pull request #1329 from Moadong/develop/be
[release] BE
2 parents 8afd0ef + 9beb856 commit ab6ef67

File tree

15 files changed

+1035
-11
lines changed

15 files changed

+1035
-11
lines changed

backend/src/main/java/moadong/club/controller/PromotionArticleController.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
import io.swagger.v3.oas.annotations.tags.Tag;
66
import lombok.RequiredArgsConstructor;
77
import moadong.club.payload.request.PromotionArticleCreateRequest;
8+
import moadong.club.payload.request.PromotionArticleUpdateRequest;
89
import moadong.club.payload.response.PromotionArticleResponse;
910
import moadong.club.service.PromotionArticleService;
1011
import moadong.global.payload.Response;
1112
import org.springframework.http.ResponseEntity;
1213
import org.springframework.security.access.prepost.PreAuthorize;
1314
import org.springframework.validation.annotation.Validated;
1415
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.PathVariable;
1517
import org.springframework.web.bind.annotation.PostMapping;
18+
import org.springframework.web.bind.annotation.PutMapping;
1619
import org.springframework.web.bind.annotation.RequestBody;
1720
import org.springframework.web.bind.annotation.RequestMapping;
1821
import org.springframework.web.bind.annotation.RestController;
@@ -41,4 +44,15 @@ public ResponseEntity<?> createPromotionArticle(
4144
promotionArticleService.createPromotionArticle(request);
4245
return Response.ok("홍보 게시글이 생성되었습니다.");
4346
}
47+
48+
@PutMapping("/{articleId}")
49+
@Operation(summary = "홍보 게시글 수정", description = "기존 홍보 게시글을 수정합니다.")
50+
@PreAuthorize("hasRole('DEVELOPER')")
51+
@SecurityRequirement(name = "BearerAuth")
52+
public ResponseEntity<?> updatePromotionArticle(
53+
@PathVariable String articleId,
54+
@RequestBody @Validated PromotionArticleUpdateRequest request) {
55+
promotionArticleService.updatePromotionArticle(articleId, request);
56+
return Response.ok("홍보 게시글이 수정되었습니다.");
57+
}
4458
}

backend/src/main/java/moadong/club/entity/PromotionArticle.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import lombok.Builder;
55
import lombok.Getter;
66
import lombok.NoArgsConstructor;
7+
import moadong.club.payload.request.PromotionArticleUpdateRequest;
78
import org.springframework.data.annotation.Id;
89
import org.springframework.data.mongodb.core.mapping.Document;
910

@@ -38,4 +39,15 @@ public class PromotionArticle {
3839

3940
@Builder.Default
4041
private Instant createdAt = Instant.now();
42+
43+
public void update(PromotionArticleUpdateRequest request, String clubName) {
44+
this.clubId = request.clubId();
45+
this.clubName = clubName;
46+
this.title = request.title();
47+
this.location = request.location();
48+
this.eventStartDate = request.eventStartDate();
49+
this.eventEndDate = request.eventEndDate();
50+
this.description = request.description();
51+
this.images = request.images();
52+
}
4153
}

backend/src/main/java/moadong/club/payload/dto/PromotionArticleDto.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package moadong.club.payload.dto;
22

3+
import moadong.club.entity.PromotionArticle;
4+
35
import java.time.Instant;
46
import java.util.List;
57

68
public record PromotionArticleDto(
9+
String id,
710
String clubName,
811
String clubId,
912
String title,
@@ -13,4 +16,17 @@ public record PromotionArticleDto(
1316
String description,
1417
List<String> images
1518
) {
19+
public static PromotionArticleDto from(PromotionArticle article) {
20+
return new PromotionArticleDto(
21+
article.getId(),
22+
article.getClubName(),
23+
article.getClubId(),
24+
article.getTitle(),
25+
article.getLocation(),
26+
article.getEventStartDate(),
27+
article.getEventEndDate(),
28+
article.getDescription(),
29+
article.getImages()
30+
);
31+
}
1632
}

backend/src/main/java/moadong/club/payload/request/PromotionArticleCreateRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
public record PromotionArticleCreateRequest(
1111
@NotBlank String clubId,
1212
@NotBlank String title,
13-
String location,
13+
@NotBlank String location,
1414
@NotNull Instant eventStartDate,
1515
@NotNull Instant eventEndDate,
1616
@NotBlank String description,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package moadong.club.payload.request;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.NotEmpty;
5+
import jakarta.validation.constraints.NotNull;
6+
7+
import java.time.Instant;
8+
import java.util.List;
9+
10+
public record PromotionArticleUpdateRequest(
11+
@NotBlank String clubId,
12+
@NotBlank String title,
13+
@NotBlank String location,
14+
@NotNull Instant eventStartDate,
15+
@NotNull Instant eventEndDate,
16+
@NotBlank String description,
17+
@NotEmpty List<String> images
18+
) {
19+
}
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
package moadong.club.repository;
22

33
import moadong.club.entity.PromotionArticle;
4-
import moadong.club.payload.dto.PromotionArticleDto;
54
import org.springframework.data.mongodb.repository.MongoRepository;
6-
import org.springframework.data.mongodb.repository.Query;
75
import org.springframework.stereotype.Repository;
86

97
import java.util.List;
108

119
@Repository
1210
public interface PromotionArticleRepository extends MongoRepository<PromotionArticle, String> {
1311

14-
@Query(value = "{}", sort = "{ 'createdAt': -1 }")
15-
List<PromotionArticleDto> findAllProjectedBy();
12+
List<PromotionArticle> findAllByOrderByCreatedAtDesc();
1613

1714
List<PromotionArticle> findByClubIdOrderByCreatedAtDesc(String clubId);
1815
}

backend/src/main/java/moadong/club/service/PromotionArticleService.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import moadong.club.entity.PromotionArticle;
66
import moadong.club.payload.dto.PromotionArticleDto;
77
import moadong.club.payload.request.PromotionArticleCreateRequest;
8+
import moadong.club.payload.request.PromotionArticleUpdateRequest;
89
import moadong.club.payload.response.PromotionArticleResponse;
910
import moadong.club.repository.ClubRepository;
1011
import moadong.club.repository.PromotionArticleRepository;
@@ -25,15 +26,16 @@ public class PromotionArticleService {
2526
private final ClubRepository clubRepository;
2627

2728
public PromotionArticleResponse getPromotionArticles() {
28-
List<PromotionArticleDto> articles = promotionArticleRepository.findAllProjectedBy();
29+
List<PromotionArticleDto> articles = promotionArticleRepository.findAllByOrderByCreatedAtDesc()
30+
.stream()
31+
.map(PromotionArticleDto::from)
32+
.toList();
2933
return new PromotionArticleResponse(articles);
3034
}
3135

3236
@Transactional
3337
public void createPromotionArticle(PromotionArticleCreateRequest request) {
34-
ObjectId clubObjectId = ObjectIdConverter.convertString(request.clubId());
35-
Club club = clubRepository.findClubById(clubObjectId)
36-
.orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
38+
Club club = getClub(request.clubId());
3739

3840
PromotionArticle article = PromotionArticle.builder()
3941
.clubId(request.clubId())
@@ -48,4 +50,20 @@ public void createPromotionArticle(PromotionArticleCreateRequest request) {
4850

4951
promotionArticleRepository.save(article);
5052
}
53+
54+
@Transactional
55+
public void updatePromotionArticle(String articleId, PromotionArticleUpdateRequest request) {
56+
PromotionArticle article = promotionArticleRepository.findById(articleId)
57+
.orElseThrow(() -> new RestApiException(ErrorCode.PROMOTION_ARTICLE_NOT_FOUND));
58+
Club club = getClub(request.clubId());
59+
60+
article.update(request, club.getName());
61+
promotionArticleRepository.save(article);
62+
}
63+
64+
private Club getClub(String clubId) {
65+
ObjectId clubObjectId = ObjectIdConverter.convertString(clubId);
66+
return clubRepository.findClubById(clubObjectId)
67+
.orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
68+
}
5169
}

backend/src/main/java/moadong/fcm/controller/StudentFcmController.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
55
import io.swagger.v3.oas.annotations.tags.Tag;
66
import lombok.RequiredArgsConstructor;
7+
import lombok.extern.slf4j.Slf4j;
78
import moadong.fcm.payload.request.StudentFcmTokenRotateRequest;
89
import moadong.fcm.payload.response.ClubSubscribeListResponse;
910
import moadong.fcm.payload.response.StudentFcmTokenRotateResponse;
1011
import moadong.fcm.service.StudentFcmTokenService;
1112
import moadong.global.payload.Response;
1213
import moadong.user.service.StudentJwtService;
1314
import org.springframework.http.ResponseEntity;
14-
import org.springframework.validation.annotation.Validated;
15+
import org.springframework.util.StringUtils;
1516
import org.springframework.web.bind.annotation.GetMapping;
1617
import org.springframework.web.bind.annotation.PutMapping;
1718
import org.springframework.web.bind.annotation.RequestBody;
@@ -22,6 +23,7 @@
2223
@RestController
2324
@RequestMapping("/api/student")
2425
@RequiredArgsConstructor
26+
@Slf4j
2527
@Tag(name = "Student FCM", description = "학생 FCM 토큰 관리 API")
2628
public class StudentFcmController {
2729

@@ -35,8 +37,13 @@ public ResponseEntity<?> rotateFcmToken(
3537
@RequestHeader("Authorization") String authorization,
3638
@RequestBody StudentFcmTokenRotateRequest request
3739
) {
40+
log.info("Student FCM token rotate request received. authorization={}, fcmToken={}",
41+
maskAuthorization(authorization), maskToken(request.fcmToken()));
3842
if (request.fcmToken() == null) return Response.ok("pass");
43+
3944
String studentId = studentJwtService.extractStudentId(authorization);
45+
log.info("Student FCM token rotate request parsed. studentId={}", studentId);
46+
4047
StudentFcmTokenRotateResponse response = studentFcmTokenService.rotateFcmToken(studentId, request.fcmToken());
4148
return Response.ok(response);
4249
}
@@ -47,8 +54,26 @@ public ResponseEntity<?> rotateFcmToken(
4754
public ResponseEntity<?> getSubscribedClubs(
4855
@RequestHeader("Authorization") String authorization
4956
) {
57+
log.info("Student subscribed clubs request received. authorization={}", maskAuthorization(authorization));
58+
5059
String studentId = studentJwtService.extractStudentId(authorization);
60+
log.info("Student subscribed clubs request parsed. studentId={}", studentId);
61+
5162
ClubSubscribeListResponse response = studentFcmTokenService.getSubscribedClubs(studentId);
5263
return Response.ok(response);
5364
}
65+
66+
private String maskAuthorization(String authorization) {
67+
if (!StringUtils.hasText(authorization) || authorization.length() < 10) {
68+
return "***";
69+
}
70+
return authorization.substring(0, 6) + "..." + authorization.substring(authorization.length() - 4);
71+
}
72+
73+
private String maskToken(String token) {
74+
if (!StringUtils.hasText(token) || token.length() < 10) {
75+
return "***";
76+
}
77+
return token.substring(0, 6) + "..." + token.substring(token.length() - 4);
78+
}
5479
}

backend/src/main/java/moadong/global/exception/ErrorCode.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,11 @@ public enum ErrorCode {
6868
// 901xx: FCM 관련 오류
6969
FCMTOKEN_NOT_FOUND(HttpStatus.NOT_FOUND, "901-1", "존재하지 않는 토큰입니다."),
7070
FCMTOKEN_SUBSCRIBE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "901-2", "동아리 구독중에 오류가 발생 하였습니다."),
71-
FCMMESSAGE_SEND_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "901-3", "FCM 메시지 전송 중 오류가 발생했습니다.");
71+
FCMMESSAGE_SEND_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "901-3", "FCM 메시지 전송 중 오류가 발생했습니다."),
72+
73+
// 902xx: 홍보게시판 오류
74+
PROMOTION_ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "902-1", "홍보 게시글이 존재하지 않습니다."),
75+
;
7276

7377
private final HttpStatus httpStatus;
7478
private final String code;

backend/src/main/java/moadong/media/controller/BannerImagesController.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55
import io.swagger.v3.oas.annotations.tags.Tag;
66
import lombok.RequiredArgsConstructor;
77
import moadong.global.payload.Response;
8+
import moadong.media.dto.BannerImageUploadResponse;
89
import moadong.media.dto.BannerImagesRequest;
910
import moadong.media.dto.BannerImagesResponse;
1011
import moadong.media.enums.PlatformType;
12+
import moadong.media.service.BannerImageUploadService;
1113
import moadong.media.service.BannerImagesService;
14+
import org.springframework.http.MediaType;
1215
import org.springframework.http.ResponseEntity;
1316
import org.springframework.security.access.prepost.PreAuthorize;
1417
import org.springframework.validation.annotation.Validated;
1518
import org.springframework.web.bind.annotation.*;
19+
import org.springframework.web.multipart.MultipartFile;
1620

1721
@RestController
1822
@RequestMapping("/api/banner")
@@ -21,6 +25,7 @@
2125
public class BannerImagesController {
2226

2327
private final BannerImagesService bannerImagesService;
28+
private final BannerImageUploadService bannerImageUploadService;
2429

2530
@PutMapping
2631
@PreAuthorize("hasRole('DEVELOPER')")
@@ -37,4 +42,14 @@ public ResponseEntity<?> getBannerImages(@RequestParam(value = "type", required
3742
BannerImagesResponse response = bannerImagesService.getBannerImages(type);
3843
return Response.ok(response);
3944
}
45+
46+
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
47+
@PreAuthorize("hasRole('DEVELOPER')")
48+
@SecurityRequirement(name = "BearerAuth")
49+
@Operation(summary = "배너 이미지 업로드", description = "배너 이미지를 Cloudflare R2 배너 버킷에 업로드하고 최종 URL을 반환합니다.")
50+
public ResponseEntity<?> uploadBannerImage(@RequestPart("file") MultipartFile file,
51+
@RequestParam("type") PlatformType type) {
52+
BannerImageUploadResponse response = bannerImageUploadService.upload(file, type);
53+
return Response.ok("배너 이미지가 업로드되었습니다.", response);
54+
}
4055
}

0 commit comments

Comments
 (0)