diff --git a/build.gradle b/build.gradle index 1989186d..379579db 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,13 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' implementation 'org.springframework.boot:spring-boot-starter-mail' + + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + + } tasks.named('test') { diff --git a/src/main/java/Gotcha/common/config/JpaConfig.java b/src/main/java/Gotcha/common/config/JpaConfig.java index 48157893..43cb0ff7 100644 --- a/src/main/java/Gotcha/common/config/JpaConfig.java +++ b/src/main/java/Gotcha/common/config/JpaConfig.java @@ -1,9 +1,16 @@ package Gotcha.common.config; +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.data.web.config.EnableSpringDataWebSupport; @Configuration @EnableJpaAuditing +@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO) public class JpaConfig { + } diff --git a/src/main/java/Gotcha/common/constants/SecurityConstants.java b/src/main/java/Gotcha/common/constants/SecurityConstants.java index d3d137fa..bb79524f 100644 --- a/src/main/java/Gotcha/common/constants/SecurityConstants.java +++ b/src/main/java/Gotcha/common/constants/SecurityConstants.java @@ -3,7 +3,8 @@ public class SecurityConstants { public static final String[] PUBLIC_ENDPOINTS = { "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", - "/webjars/**", "/error", "/api/v1/auth/**", "/api/v1/users/nickname-check" + "/webjars/**", "/error", "/api/v1/auth/**", "/api/v1/users/nickname-check", + "/api/v1/notifications/**" }; public static final String[] ADMIN_ENDPOINTS = {"/api/v1/admin/**"}; diff --git a/src/main/java/Gotcha/domain/notification/api/AdminNotificationApi.java b/src/main/java/Gotcha/domain/notification/api/AdminNotificationApi.java new file mode 100644 index 00000000..dfe5b7f8 --- /dev/null +++ b/src/main/java/Gotcha/domain/notification/api/AdminNotificationApi.java @@ -0,0 +1,142 @@ +package Gotcha.domain.notification.api; + +import Gotcha.common.jwt.auth.SecurityUserDetails; +import Gotcha.domain.notification.dto.NotificationReq; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PathVariable; + +@Tag(name = "[관리자 공지사항 API]", description = "관리자용 공지사항 관련 API") +public interface AdminNotificationApi { + + @Operation(summary = "공지사항 생성", description = "공지사항 생성 API") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "공지사항 생성 성공", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "status": "OK", + "message": "공지사항 생성에 성공했습니다." + } + """) + }) + ), + @ApiResponse(responseCode = "400", description = "필드 검증 오류", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "status": "BAD_REQUEST", + "message": "필드 검증 오류입니다.", + "fields": { + "title": "제목은 필수 입력 사항입니다.", + "content": "내용은 필수 입력 사항입니다." + } + } + """) + }) + ), + @ApiResponse(responseCode = "403", description = "권한 없음", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "status": "FORBIDDEN", + "message": "권한이 없습니다." + } + """) + }) + ) + }) + ResponseEntity createNotification( + @Valid @RequestBody NotificationReq notificationReq, + @AuthenticationPrincipal SecurityUserDetails userDetails); + + @Operation(summary = "공지사항 수정", description = "공지사항 수정 API") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "공지사항 수정 성공", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "status": "OK", + "message": "공지사항 수정에 성공했습니다." + } + """) + }) + ), + @ApiResponse(responseCode = "400", description = "필드 검증 오류", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "status": "BAD_REQUEST", + "message": "필드 검증 오류입니다.", + "fields": { + "title": "제목은 필수 입력 사항입니다.", + "content": "내용은 필수 입력 사항입니다." + } + } + """) + }) + ), + @ApiResponse(responseCode = "403", description = "작성자 불일치 또는 수정 권한 없음", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "status": "FORBIDDEN", + "message": "권한이 없습니다." + } + """) + }) + ), + @ApiResponse(responseCode = "404", description = "존재하지 않는 공지사항", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "status": "NOT_FOUND", + "message": "존재하지 않는 공지사항입니다." + } + """) + }) + ) + }) + ResponseEntity updateNotification( + @PathVariable(value = "notificationId") Long notificationId, + @Valid @RequestBody NotificationReq notificationReq, + @AuthenticationPrincipal SecurityUserDetails userDetails); + + @Operation(summary = "공지사항 삭제", description = "공지사항 삭제 API") + @ApiResponses({ + @ApiResponse(responseCode = "204", description = "공지사항 삭제 성공"), + @ApiResponse(responseCode = "403", description = "작성자 불일치 또는 삭제 권한 없음", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "status": "FORBIDDEN", + "message": "권한이 없습니다." + } + """) + }) + ), + @ApiResponse(responseCode = "404", description = "존재하지 않는 공지사항", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "status": "NOT_FOUND", + "message": "존재하지 않는 공지사항입니다." + } + """) + }) + ) + }) + ResponseEntity deleteNotification( + @PathVariable(value = "notificationId") Long notificationId, + @AuthenticationPrincipal SecurityUserDetails userDetails); + + +} diff --git a/src/main/java/Gotcha/domain/notification/api/NotificationApi.java b/src/main/java/Gotcha/domain/notification/api/NotificationApi.java new file mode 100644 index 00000000..1e4e545c --- /dev/null +++ b/src/main/java/Gotcha/domain/notification/api/NotificationApi.java @@ -0,0 +1,117 @@ +package Gotcha.domain.notification.api; + + +import Gotcha.domain.notification.dto.NotificationSortType; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.Min; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +@Tag(name = "[공지사항 API]", description = "공지사항 관련 API") +public interface NotificationApi { + + @Operation(summary = "공지사항 목록", description = "공지사항 목록을 조회하는 API. Keyword로 검색 및 정렬 가능.") + @ApiResponses({ + @ApiResponse( + responseCode = "200", description = "공지사항 목록 조회 성공", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "totalPages": 1, + "totalElements": 3, + "first": true, + "last": true, + "size": 10, + "content": [ + { + "notificationId": 2, + "title" : "걍 공지사항이다", + "createdAt": "2025-03-27T16:13:32", + "writer" : "묘묘" + }, + { + "notificationId": 1, + "title" : "서버 점검 안내", + "createdAt": "2025-03-27T16:05:35", + "writer": "루루" + }, + { + "notificationId": 0, + "title": "이달의 인공지능 선정 결과", + "createdAt" : "2001-11-16T16:02:26", + "writer": "킹형준" + } + ], + "pageable": { + "pageNumber": 0, + "pageSize": 10, + "sort": { + "empty": false, + "unsorted": false, + "sorted": true + }, + "offset": 0, + "unpaged": false, + "paged": true + }, + "numberOfElements": 3, + "sort": { + "empty": false, + "sorted": true, + "unsorted": false + }, + "number": 0, + "empty": false + } + """) + }) + ) + + }) + ResponseEntity getNotifications(@RequestParam(value = "keyword", required = false) String keyword, + @RequestParam(value = "page",defaultValue = "0") @Min(0) Integer page, + @RequestParam(value = "sort", defaultValue = "DATE_DESC") NotificationSortType sort); + + + + @Operation(summary = "공지사항 조회", description = "공지사항 ID를 받아 해당 공지사항을 조회하는 API") + @ApiResponses({ + @ApiResponse( + responseCode = "200", description = "공지사항 조회 성공", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "title": "걍 공지사항이다", + "content": "걍 공지사항이다 인마", + "createdAt": "2025-03-27T16:13:32", + "modifiedAt": "2025-11-16T16:13:32", + "writer": "묘묘" + } + """ + ) + }) + ), + @ApiResponse( + responseCode = "404", description = "존재하지 않는 공지사항", + content = @Content(mediaType = "application/json", examples = { + @ExampleObject(value = """ + { + "status": "NOT_FOUND", + "message": "존재하지 않는 공지사항입니다." + } + """ + ) + }) + ) + + }) + ResponseEntity getNotificationById(@PathVariable(value = "notificationId") Long notificationId); + + +} diff --git a/src/main/java/Gotcha/domain/notification/controller/AdminNotificationController.java b/src/main/java/Gotcha/domain/notification/controller/AdminNotificationController.java new file mode 100644 index 00000000..33b90ca4 --- /dev/null +++ b/src/main/java/Gotcha/domain/notification/controller/AdminNotificationController.java @@ -0,0 +1,51 @@ +package Gotcha.domain.notification.controller; + +import Gotcha.common.api.SuccessRes; +import Gotcha.common.jwt.auth.SecurityUserDetails; +import Gotcha.domain.notification.api.AdminNotificationApi; +import Gotcha.domain.notification.dto.NotificationReq; +import Gotcha.domain.notification.entity.Notification; +import Gotcha.domain.notification.service.AdminNotificationService; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/admin/notification") +public class AdminNotificationController implements AdminNotificationApi { + + private final AdminNotificationService adminNotificationService; + + @Override + @PostMapping + public ResponseEntity createNotification( + @Valid @RequestBody NotificationReq notificationReq, + @AuthenticationPrincipal SecurityUserDetails userDetails){ + adminNotificationService.createNotification(notificationReq, userDetails.getId()); + return ResponseEntity.ok(SuccessRes.from("공지사항 생성에 성공했습니다.")); + } + + @Override + @PutMapping("/{notificationId}") + public ResponseEntity updateNotification( + @PathVariable(value = "notificationId") Long notificationId, + @Valid @RequestBody NotificationReq notificationReq, + @AuthenticationPrincipal SecurityUserDetails userDetails){ + adminNotificationService.updateNotification(notificationReq, notificationId, userDetails.getId()); + return ResponseEntity.ok(SuccessRes.from("공지사항 수정에 성공했습니다.")); + } + + @Override + @DeleteMapping("/{notificationId}") + public ResponseEntity deleteNotification( + @PathVariable(value = "notificationId") Long notificationId, + @AuthenticationPrincipal SecurityUserDetails userDetails){ + adminNotificationService.deleteNotification(notificationId, userDetails.getId()); + return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); + } +} \ No newline at end of file diff --git a/src/main/java/Gotcha/domain/notification/controller/NotificationController.java b/src/main/java/Gotcha/domain/notification/controller/NotificationController.java new file mode 100644 index 00000000..02242214 --- /dev/null +++ b/src/main/java/Gotcha/domain/notification/controller/NotificationController.java @@ -0,0 +1,39 @@ +package Gotcha.domain.notification.controller; + +import Gotcha.domain.notification.api.NotificationApi; +import Gotcha.domain.notification.dto.NotificationRes; +import Gotcha.domain.notification.dto.NotificationSortType; +import Gotcha.domain.notification.dto.NotificationSummaryRes; +import Gotcha.domain.notification.service.NotificationService; +import jakarta.validation.constraints.Min; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/notifications") +public class NotificationController implements NotificationApi { + + private final NotificationService notificationService; + + + @Override + @GetMapping + public ResponseEntity getNotifications(@RequestParam(value = "keyword", required = false) String keyword, + @RequestParam(value = "page", defaultValue = "0") @Min(0) Integer page, + @RequestParam(value = "sort", defaultValue = "DATE_DESC") NotificationSortType sort){ + Page notifications = notificationService.getNotifications(keyword, page, sort); + + return ResponseEntity.status(HttpStatus.OK).body(notifications); + } + + @Override + @GetMapping("/{notificationId}") + public ResponseEntity getNotificationById(@PathVariable(value = "notificationId") Long notificationId) { + NotificationRes notificationRes = notificationService.getNotificationsById(notificationId); + return ResponseEntity.status(HttpStatus.OK).body(notificationRes); + } +} diff --git a/src/main/java/Gotcha/domain/notification/dto/NotificationReq.java b/src/main/java/Gotcha/domain/notification/dto/NotificationReq.java new file mode 100644 index 00000000..ba688ca1 --- /dev/null +++ b/src/main/java/Gotcha/domain/notification/dto/NotificationReq.java @@ -0,0 +1,21 @@ +package Gotcha.domain.notification.dto; + +import Gotcha.domain.notification.entity.Notification; +import Gotcha.domain.user.entity.User; +import jakarta.validation.constraints.NotBlank; + +public record NotificationReq( + @NotBlank(message = "제목은 필수 입력 사항입니다.") + String title, + + @NotBlank(message = "내용은 필수 입력 사항입니다.") + String content +) { + public Notification toEntity(User writer) { + return Notification.builder(). + title(title). + content(content). + writer(writer). + build(); + } +} \ No newline at end of file diff --git a/src/main/java/Gotcha/domain/notification/dto/NotificationRes.java b/src/main/java/Gotcha/domain/notification/dto/NotificationRes.java new file mode 100644 index 00000000..3897e49f --- /dev/null +++ b/src/main/java/Gotcha/domain/notification/dto/NotificationRes.java @@ -0,0 +1,23 @@ +package Gotcha.domain.notification.dto; + +import Gotcha.domain.notification.entity.Notification; + +import java.time.LocalDateTime; + +public record NotificationRes( + String title, + String content, + LocalDateTime createdAt, + LocalDateTime modifiedAt, + String writer +) { + public static NotificationRes fromEntity(Notification noti) { + return new NotificationRes( + noti.getTitle(), + noti.getContent(), + noti.getCreatedAt(), + noti.getModifiedAt(), + noti.getWriter().getNickname() + ); + } +} diff --git a/src/main/java/Gotcha/domain/notification/dto/NotificationSortType.java b/src/main/java/Gotcha/domain/notification/dto/NotificationSortType.java new file mode 100644 index 00000000..d305ff56 --- /dev/null +++ b/src/main/java/Gotcha/domain/notification/dto/NotificationSortType.java @@ -0,0 +1,28 @@ +package Gotcha.domain.notification.dto; + +import lombok.Getter; +import org.springframework.data.domain.Sort; +import com.querydsl.core.types.OrderSpecifier; + +import static Gotcha.domain.notification.entity.QNotification.notification; + +public enum NotificationSortType { + DATE_DESC("createdAt", Sort.Direction.DESC, notification.createdAt.desc()), + DATE_ASC("createdAt", Sort.Direction.ASC, notification.createdAt.asc()); + + private String type; + private Sort.Direction direction; + + @Getter + private OrderSpecifier order; + + NotificationSortType(String type, Sort.Direction direction, OrderSpecifier order){ + this.type = type; + this.direction = direction; + this.order = order; + } + + public Sort getSort(){ + return Sort.by(direction, type); + } +} diff --git a/src/main/java/Gotcha/domain/notification/dto/NotificationSummaryRes.java b/src/main/java/Gotcha/domain/notification/dto/NotificationSummaryRes.java new file mode 100644 index 00000000..1f3c4e22 --- /dev/null +++ b/src/main/java/Gotcha/domain/notification/dto/NotificationSummaryRes.java @@ -0,0 +1,21 @@ +package Gotcha.domain.notification.dto; + +import Gotcha.domain.notification.entity.Notification; + +import java.time.LocalDateTime; + +public record NotificationSummaryRes( + Long notificationId, + String title, + LocalDateTime createdAt, + String writer +) { + public static NotificationSummaryRes fromEntity(Notification noti){ + return new NotificationSummaryRes( + noti.getId(), + noti.getTitle(), + noti.getCreatedAt(), + noti.getWriter().getNickname() + ); + } +} diff --git a/src/main/java/Gotcha/domain/notification/entity/Notification.java b/src/main/java/Gotcha/domain/notification/entity/Notification.java index 1dd3752e..1206a704 100644 --- a/src/main/java/Gotcha/domain/notification/entity/Notification.java +++ b/src/main/java/Gotcha/domain/notification/entity/Notification.java @@ -1,6 +1,7 @@ package Gotcha.domain.notification.entity; import Gotcha.common.entity.BaseTimeEntity; +import Gotcha.domain.notification.dto.NotificationReq; import Gotcha.domain.user.entity.User; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -29,16 +30,19 @@ public class Notification extends BaseTimeEntity { @NotNull private String content; - private Boolean isFixed; - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User writer; @Builder - public Notification(String title, String content, Boolean isFixed){ + public Notification(String title, String content, User writer){ this.title = title; this.content = content; - this.isFixed = isFixed; + this.writer = writer; + } + + public void update(NotificationReq req){ + this.title = req.title(); + this.content = req.content(); } } diff --git a/src/main/java/Gotcha/domain/notification/exception/NotificationExceptionCode.java b/src/main/java/Gotcha/domain/notification/exception/NotificationExceptionCode.java new file mode 100644 index 00000000..0975056b --- /dev/null +++ b/src/main/java/Gotcha/domain/notification/exception/NotificationExceptionCode.java @@ -0,0 +1,27 @@ +package Gotcha.domain.notification.exception; + +import Gotcha.common.exception.exceptionCode.ExceptionCode; +import lombok.AllArgsConstructor; +import lombok.ToString; +import org.springframework.http.HttpStatus; + +@AllArgsConstructor +@ToString +public enum NotificationExceptionCode implements ExceptionCode { + + UNAUTHORIZED_ACTION(HttpStatus.FORBIDDEN, "권한이 없습니다."), + NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 공지사항입니다."); + + private final HttpStatus status; + private final String message; + + @Override + public HttpStatus getStatus() { + return status; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/src/main/java/Gotcha/domain/notification/repository/NotificationRepository.java b/src/main/java/Gotcha/domain/notification/repository/NotificationRepository.java index 8f91ef9c..5b6dc02c 100644 --- a/src/main/java/Gotcha/domain/notification/repository/NotificationRepository.java +++ b/src/main/java/Gotcha/domain/notification/repository/NotificationRepository.java @@ -2,6 +2,9 @@ import Gotcha.domain.notification.entity.Notification; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Page; public interface NotificationRepository extends JpaRepository { + Page findByTitleContainingIgnoreCase(String keyword, Pageable pageable); } diff --git a/src/main/java/Gotcha/domain/notification/service/AdminNotificationService.java b/src/main/java/Gotcha/domain/notification/service/AdminNotificationService.java new file mode 100644 index 00000000..b0b64718 --- /dev/null +++ b/src/main/java/Gotcha/domain/notification/service/AdminNotificationService.java @@ -0,0 +1,74 @@ +package Gotcha.domain.notification.service; + +import Gotcha.common.exception.CustomException; +import Gotcha.domain.notification.dto.NotificationReq; +import Gotcha.domain.notification.entity.Notification; +import Gotcha.domain.notification.exception.NotificationExceptionCode; +import Gotcha.domain.notification.repository.NotificationRepository; +import Gotcha.domain.user.entity.Role; +import Gotcha.domain.user.entity.User; +import Gotcha.domain.user.exceptionCode.UserExceptionCode; +import Gotcha.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +@Service +@RequiredArgsConstructor +public class AdminNotificationService { + + private final NotificationRepository notificationRepository; + private final UserRepository userRepository; + + @Transactional + public void createNotification(NotificationReq notificationReq, Long writerId){ + User writer = validateAdmin(writerId); + + Notification notification = notificationReq.toEntity(writer); + + notificationRepository.save(notification); + } + + + @Transactional + public void updateNotification(NotificationReq notificationReq, Long notificationId, Long userId){ + validateAdmin(userId); + + Notification notification = validateUserNotification(notificationId, userId); + + notification.update(notificationReq); + } + + @Transactional + public void deleteNotification(Long notificationId, Long userId){ + validateAdmin(userId); + + Notification notification = validateUserNotification(notificationId, userId); + + notificationRepository.delete(notification); + } + + private Notification validateUserNotification(Long notificationId, Long userId) { + Notification notification = validateNotification(notificationId); + + if(!notification.getWriter().getId().equals(userId)) + throw new CustomException(NotificationExceptionCode.UNAUTHORIZED_ACTION); + + return notification; + } + + private User validateAdmin(Long userId){ + User user = userRepository.findById(userId) + .orElseThrow(() -> new CustomException(UserExceptionCode.INVALID_USERID)); + if(!user.getRole().equals(Role.ADMIN)) + throw new CustomException(NotificationExceptionCode.UNAUTHORIZED_ACTION); + return user; + } + + private Notification validateNotification(Long notificationId){ + return notificationRepository.findById(notificationId) + .orElseThrow(() -> new CustomException(NotificationExceptionCode.NOT_FOUND)); + } + +} diff --git a/src/main/java/Gotcha/domain/notification/service/NotificationService.java b/src/main/java/Gotcha/domain/notification/service/NotificationService.java new file mode 100644 index 00000000..e805fa78 --- /dev/null +++ b/src/main/java/Gotcha/domain/notification/service/NotificationService.java @@ -0,0 +1,46 @@ +package Gotcha.domain.notification.service; + +import Gotcha.common.exception.CustomException; +import Gotcha.domain.notification.dto.NotificationRes; +import Gotcha.domain.notification.dto.NotificationSortType; +import Gotcha.domain.notification.dto.NotificationSummaryRes; +import Gotcha.domain.notification.entity.Notification; +import Gotcha.domain.notification.exception.NotificationExceptionCode; +import Gotcha.domain.notification.repository.NotificationRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class NotificationService { + + + private final NotificationRepository notificationRepository; + + private final Integer NOTIS_PER_PAGE = 10; + + + @Transactional(readOnly = true) + public Page getNotifications(String keyword, Integer page, NotificationSortType sort){ + Pageable pageable = PageRequest.of(page, NOTIS_PER_PAGE); + + Page notifications = notificationRepository.findByTitleContainingIgnoreCase(keyword, pageable); + + return notifications.map(NotificationSummaryRes::fromEntity); + } + + @Transactional(readOnly = true) + public NotificationRes getNotificationsById(Long notificationId) { + Notification notification = notificationRepository.findById(notificationId). + orElseThrow(() -> new CustomException(NotificationExceptionCode.NOT_FOUND)); + + return NotificationRes.fromEntity(notification); + } + + +}