From 17f8c0dcb22b0c70a42f2f7c7dec02b878885034 Mon Sep 17 00:00:00 2001 From: YONGWOOK CHOI <60510921+CYY1007@users.noreply.github.com> Date: Fri, 8 Sep 2023 21:42:10 +0900 Subject: [PATCH] =?UTF-8?q?Feature/112=20=E2=9C=A8=20[FEAT]=20=EA=B3=B5?= =?UTF-8?q?=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20API=20=EC=B6=94=EA=B0=80=20(#1?= =?UTF-8?q?14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :hammer: Feat : 공지 관련 API 시그니처 작성 Dto, Controller 시그니처 작성 * :sparkles: Feat : 공지 목록 조회 API 추가 * :memo: 공지 목록조회 API swagger 추가 * :sparkles: Feat : 공지 단건 조회API 추가 annotation validation 포함 --- .../server/auth/config/SecurityConfig.java | 2 +- src/main/java/zipdabang/server/base/Code.java | 1 + .../server/converter/RootConverter.java | 38 +++++++++++++++++++ .../repository/NotificationRepository.java | 14 +++++++ .../zipdabang/server/service/RootService.java | 5 +++ .../service/serviceImpl/RootServiceImpl.java | 14 +++++++ .../server/utils/converter/TimeConverter.java | 37 ++++++++++++++++++ .../annotation/ExistNotification.java | 18 +++++++++ .../validator/ExistNotificationValidator.java | 35 +++++++++++++++++ .../server/web/controller/RootController.java | 20 ++++++++++ .../web/dto/responseDto/RootResponseDto.java | 28 ++++++++++++++ 11 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 src/main/java/zipdabang/server/repository/NotificationRepository.java create mode 100644 src/main/java/zipdabang/server/utils/converter/TimeConverter.java create mode 100644 src/main/java/zipdabang/server/validation/annotation/ExistNotification.java create mode 100644 src/main/java/zipdabang/server/validation/validator/ExistNotificationValidator.java diff --git a/src/main/java/zipdabang/server/auth/config/SecurityConfig.java b/src/main/java/zipdabang/server/auth/config/SecurityConfig.java index 749450d..fae2b6d 100644 --- a/src/main/java/zipdabang/server/auth/config/SecurityConfig.java +++ b/src/main/java/zipdabang/server/auth/config/SecurityConfig.java @@ -40,7 +40,7 @@ public WebSecurityCustomizer webSecurityCustomizer(){ "/v3/api-docs/**", "/swagger-ui/**", "/docs/**","/members/oauth", "/members/oauth/info","/members/new-token","/members/terms","/categories","/members/exist-nickname", - "/members/phone/sms","/members/phone/auth","/members/temp-login","/auto-login" + "/members/phone/sms","/members/phone/auth","/members/temp-login","/auto-login","/notices/**" ); } diff --git a/src/main/java/zipdabang/server/base/Code.java b/src/main/java/zipdabang/server/base/Code.java index 92ebcc6..4fc702d 100644 --- a/src/main/java/zipdabang/server/base/Code.java +++ b/src/main/java/zipdabang/server/base/Code.java @@ -44,6 +44,7 @@ public enum Code { JWT_UNSUPPORTED_TOKEN(HttpStatus.UNAUTHORIZED, 4007,"지원하지 않는 JWT 토큰입니다."), JWT_TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, 4008,"유효한 JWT 토큰이 없습니다."), FEIGN_CLIENT_ERROR_400(HttpStatus.BAD_REQUEST, 4009, "feign에서 400번대 에러가 발생했습니다."), + NOTIFICATION_NOT_FOUND(HttpStatus.NOT_FOUND, 4010, "공지를 찾을 수 없습니다."), REFRESH_TOKEN_NOT_FOUND(HttpStatus.BAD_REQUEST, 4050,"refresh token이 필요합니다."), diff --git a/src/main/java/zipdabang/server/converter/RootConverter.java b/src/main/java/zipdabang/server/converter/RootConverter.java index 4e4dbe3..fec5ae0 100644 --- a/src/main/java/zipdabang/server/converter/RootConverter.java +++ b/src/main/java/zipdabang/server/converter/RootConverter.java @@ -3,8 +3,11 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import zipdabang.server.domain.Category; +import zipdabang.server.domain.inform.Notification; +import zipdabang.server.utils.converter.TimeConverter; import zipdabang.server.web.dto.responseDto.RootResponseDto; +import javax.annotation.PostConstruct; import java.util.List; import java.util.stream.Collectors; @@ -12,6 +15,15 @@ @RequiredArgsConstructor public class RootConverter { + private final TimeConverter timeConverter; + + private static TimeConverter staticTimeConverter; + + @PostConstruct + public void init() { + this.staticTimeConverter = this.timeConverter; + } + public static RootResponseDto.BeverageCategoryDto toBeverageCategoryDto (Category category){ return RootResponseDto.BeverageCategoryDto.builder() .categoryName(category.getName()) @@ -30,4 +42,30 @@ public static RootResponseDto.BeverageCategoryListDto toBeverageCategoryListDto .size(beverageCategoryDtoList.size()) .build(); } + + public static RootResponseDto.NoticeSummaryDto toNoticeSummaryDto(Notification notification){ + return RootResponseDto.NoticeSummaryDto.builder() + .noticeId(notification.getId()) + .title(notification.getName()) + .createdAt(staticTimeConverter.ConvertTime(notification.getCreatedAt())) + .build(); + } + + public static RootResponseDto.NoticeListDto toNoticeListDto(List notificationList){ + List noticeSummaryDtoList = notificationList.stream() + .map(notification -> toNoticeSummaryDto(notification)).collect(Collectors.toList()); + + return RootResponseDto.NoticeListDto.builder() + .noticeList(noticeSummaryDtoList) + .size(noticeSummaryDtoList.size()) + .build(); + } + + public static RootResponseDto.NoticeSpecDto toNoticeSpecDto(Notification notification){ + return RootResponseDto.NoticeSpecDto.builder() + .description(notification.getDescription()) + .title(notification.getName()) + .createdAt(staticTimeConverter.ConvertTime(notification.getCreatedAt())) + .build(); + } } diff --git a/src/main/java/zipdabang/server/repository/NotificationRepository.java b/src/main/java/zipdabang/server/repository/NotificationRepository.java new file mode 100644 index 0000000..caac49f --- /dev/null +++ b/src/main/java/zipdabang/server/repository/NotificationRepository.java @@ -0,0 +1,14 @@ +package zipdabang.server.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import zipdabang.server.domain.inform.Notification; + +import java.util.List; +import java.util.Optional; + +public interface NotificationRepository extends JpaRepository { + + List findAllByOrderByCreatedAtDesc(); + + Optional findById(Long id); +} diff --git a/src/main/java/zipdabang/server/service/RootService.java b/src/main/java/zipdabang/server/service/RootService.java index 8198a9c..976543b 100644 --- a/src/main/java/zipdabang/server/service/RootService.java +++ b/src/main/java/zipdabang/server/service/RootService.java @@ -1,6 +1,7 @@ package zipdabang.server.service; import zipdabang.server.domain.Category; +import zipdabang.server.domain.inform.Notification; import java.util.List; @@ -9,4 +10,8 @@ public interface RootService { List getAllCategories(); Boolean autoLoginService(String authorizationHeader); + + List notificationList(); + + Notification findNotification(Long notificationId); } diff --git a/src/main/java/zipdabang/server/service/serviceImpl/RootServiceImpl.java b/src/main/java/zipdabang/server/service/serviceImpl/RootServiceImpl.java index 62e3089..00e089a 100644 --- a/src/main/java/zipdabang/server/service/serviceImpl/RootServiceImpl.java +++ b/src/main/java/zipdabang/server/service/serviceImpl/RootServiceImpl.java @@ -8,8 +8,10 @@ import zipdabang.server.base.Code; import zipdabang.server.base.exception.handler.RootException; import zipdabang.server.domain.Category; +import zipdabang.server.domain.inform.Notification; import zipdabang.server.domain.member.Member; import zipdabang.server.repository.CategoryRepository; +import zipdabang.server.repository.NotificationRepository; import zipdabang.server.repository.memberRepositories.MemberRepository; import zipdabang.server.service.RootService; @@ -25,6 +27,8 @@ public class RootServiceImpl implements RootService { private final MemberRepository memberRepository; + private final NotificationRepository notificationRepository; + private final TokenProvider tokenProvider; @Override @@ -54,4 +58,14 @@ public Boolean autoLoginService(String authorizationHeader) { return result; } + + @Override + public List notificationList() { + return notificationRepository.findAllByOrderByCreatedAtDesc(); + } + + @Override + public Notification findNotification(Long notificationId) { + return notificationRepository.findById(notificationId).get(); + } } diff --git a/src/main/java/zipdabang/server/utils/converter/TimeConverter.java b/src/main/java/zipdabang/server/utils/converter/TimeConverter.java new file mode 100644 index 0000000..fa6571d --- /dev/null +++ b/src/main/java/zipdabang/server/utils/converter/TimeConverter.java @@ -0,0 +1,37 @@ +package zipdabang.server.utils.converter; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@Component +@RequiredArgsConstructor +public class TimeConverter { + + public String ConvertTime(LocalDateTime time) { + LocalDateTime currentTime = LocalDateTime.now(); + + String result = null; + Duration duration = Duration.between(time,currentTime); + Long minutes = duration.toMinutes(); + Long hours = duration.toHours(); + Long days = duration.toDays(); + + if (minutes < 1) { + result = "방금"; + } else if (hours < 1) { + result = minutes.toString() + "분 전"; + } else if (days < 1) { + result = hours.toString() + "시간 전"; + } else { + DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy.MM.dd"); + String newFormattedTime = time.format(newFormatter); + result = newFormattedTime; + } + return result; + } +} diff --git a/src/main/java/zipdabang/server/validation/annotation/ExistNotification.java b/src/main/java/zipdabang/server/validation/annotation/ExistNotification.java new file mode 100644 index 0000000..2db001c --- /dev/null +++ b/src/main/java/zipdabang/server/validation/annotation/ExistNotification.java @@ -0,0 +1,18 @@ +package zipdabang.server.validation.annotation; + +import zipdabang.server.validation.validator.CheckTempMemberValidator; +import zipdabang.server.validation.validator.ExistNotificationValidator; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = ExistNotificationValidator.class) +@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExistNotification { + String message() default "해당하는 게시글이 존재하지 않습니다."; + Class[] groups() default {}; + Class[] payload() default {}; +} diff --git a/src/main/java/zipdabang/server/validation/validator/ExistNotificationValidator.java b/src/main/java/zipdabang/server/validation/validator/ExistNotificationValidator.java new file mode 100644 index 0000000..0b264ad --- /dev/null +++ b/src/main/java/zipdabang/server/validation/validator/ExistNotificationValidator.java @@ -0,0 +1,35 @@ +package zipdabang.server.validation.validator; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import zipdabang.server.base.Code; +import zipdabang.server.domain.inform.Notification; +import zipdabang.server.repository.NotificationRepository; +import zipdabang.server.validation.annotation.ExistNotification; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.Optional; + +@Component +@RequiredArgsConstructor +public class ExistNotificationValidator implements ConstraintValidator { + + private final NotificationRepository notificationRepository; + + @Override + public void initialize(ExistNotification constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(Long value, ConstraintValidatorContext context) { + Optional foundNotification = notificationRepository.findById(value); + if(foundNotification.isEmpty()){ + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(Code.NOTIFICATION_NOT_FOUND.toString()).addConstraintViolation(); + return false; + } + return true; + } +} diff --git a/src/main/java/zipdabang/server/web/controller/RootController.java b/src/main/java/zipdabang/server/web/controller/RootController.java index 1f25d71..009e331 100644 --- a/src/main/java/zipdabang/server/web/controller/RootController.java +++ b/src/main/java/zipdabang/server/web/controller/RootController.java @@ -11,13 +11,16 @@ import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; import zipdabang.server.base.Code; import zipdabang.server.base.ResponseDto; import zipdabang.server.converter.RootConverter; import zipdabang.server.domain.Category; +import zipdabang.server.domain.inform.Notification; import zipdabang.server.service.RootService; +import zipdabang.server.validation.annotation.ExistNotification; import zipdabang.server.web.dto.common.BaseDto; import zipdabang.server.web.dto.responseDto.RootResponseDto; @@ -74,4 +77,21 @@ public ResponseDto autoLogin(@RequestHeader(value = "Au else return ResponseDto.of(Code.AUTO_LOGIN_NOT_MAIN,null); } + + @GetMapping("/notices/{noticeId}") + public ResponseDto showNotification(@PathVariable(name = "noticeId") @ExistNotification Long noticeId){ + Notification notification = rootService.findNotification(noticeId); + return ResponseDto.of(RootConverter.toNoticeSpecDto(notification)); + } + + @Operation(summary = "[🎪figma 더보기-공지사항1] 공지 목록 조회 API",description = "공지사항 목록 조회 API 입니다.") + @ApiResponses({ + @ApiResponse(responseCode = "2000", description = "OK 성공 공지를 최신순으로 보여줌"), + @ApiResponse(responseCode = "5000",description = "SERVER ERROR, 백앤드 개발자에게 알려주세요",content = @Content(schema = @Schema(implementation = ResponseDto.class))), + }) + @GetMapping("/notices") + public ResponseDto getNoticeList(){ + List notificationList = rootService.notificationList(); + return ResponseDto.of(RootConverter.toNoticeListDto(notificationList)); + } } diff --git a/src/main/java/zipdabang/server/web/dto/responseDto/RootResponseDto.java b/src/main/java/zipdabang/server/web/dto/responseDto/RootResponseDto.java index 9e968f8..84faa11 100644 --- a/src/main/java/zipdabang/server/web/dto/responseDto/RootResponseDto.java +++ b/src/main/java/zipdabang/server/web/dto/responseDto/RootResponseDto.java @@ -42,4 +42,32 @@ public static class BannerImageDto { List bannerList; Integer size; } + + @Builder + @Getter + @AllArgsConstructor(access = AccessLevel.PROTECTED) + @NoArgsConstructor(access = AccessLevel.PROTECTED) + public static class NoticeSpecDto{ + String title; + String description; + String createdAt; + } + @Builder + @Getter + @AllArgsConstructor(access = AccessLevel.PROTECTED) + @NoArgsConstructor(access = AccessLevel.PROTECTED) + public static class NoticeSummaryDto{ + Long noticeId; + String title; + String createdAt; + } + + @Builder + @Getter + @AllArgsConstructor(access = AccessLevel.PROTECTED) + @NoArgsConstructor(access = AccessLevel.PROTECTED) + public static class NoticeListDto{ + List noticeList; + Integer size; + } }