From d628f24fe210ddb7836df4f4584cf0fb99b7d83e Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Mon, 25 Aug 2025 03:31:50 +0900 Subject: [PATCH 01/18] =?UTF-8?q?feat:=20notice=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=97=94=ED=84=B0=ED=8B=B0=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B8=80=20=EC=9E=91=EC=84=B1=20api=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/WriteNoticeRequest.java | 9 ++++ .../dto/response/WriteNoticeResponse.java | 27 ++++++++++++ .../usecase/WriteNoticeUseCase.java | 43 +++++++++++++++++++ .../domain/notice/domain/entity/Notice.java | 31 +++++++++++++ .../domain/repository/NoticeRepository.java | 9 ++++ .../domain/notice/service/NoticeService.java | 30 +++++++++++++ .../notice/ui/WriteNoticeController.java | 28 ++++++++++++ .../code/status/GlobalErrorStatus.java | 6 ++- 8 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/WriteNoticeResponse.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java new file mode 100644 index 0000000..292226d --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java @@ -0,0 +1,9 @@ +package com.project.dorumdorum.domain.notice.application.dto.request; + +public record WriteNoticeRequest( + String title, + String content, + Long roomNo +) { + +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/WriteNoticeResponse.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/WriteNoticeResponse.java new file mode 100644 index 0000000..ca26bb5 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/WriteNoticeResponse.java @@ -0,0 +1,27 @@ +package com.project.dorumdorum.domain.notice.application.dto.response; + +import com.project.dorumdorum.domain.notice.domain.entity.Notice; + +import lombok.Builder; +import java.time.LocalDateTime; + +@Builder +public record WriteNoticeResponse( + Long noticeNo, + Long userNo, + LocalDateTime createdAt, + LocalDateTime updatedAt, + String title, + String content +) { + public static WriteNoticeResponse create(Notice notice) { + return WriteNoticeResponse.builder() + .noticeNo(notice.getNoticeNo()) + .userNo(notice.getUserNo()) + .createdAt(notice.getCreatedAt()) + .updatedAt(notice.getUpdatedAt()) + .title(notice.getTitle()) + .content(notice.getContent()) + .build(); + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java new file mode 100644 index 0000000..c747a42 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java @@ -0,0 +1,43 @@ +package com.project.dorumdorum.domain.notice.application.usecase; + +import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.WriteNoticeResponse; +import com.project.dorumdorum.domain.notice.service.NoticeService; +import com.project.dorumdorum.domain.room.domain.entity.Room; +import com.project.dorumdorum.domain.room.domain.service.RoomService; +import com.project.dorumdorum.domain.room.domain.service.RoommateService; +import com.project.dorumdorum.domain.user.domain.service.UserService; +import com.project.dorumdorum.global.exception.RestApiException; +import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class WriteNoticeUseCase { + + private final UserService userService; + private final NoticeService noticeService; + private final RoomService roomService; + private final RoommateService roommateService; + + @Transactional + public WriteNoticeResponse execute(Long userNo, WriteNoticeRequest request) { + userService.validateExistsById(userNo); + + if(request.title().isEmpty()) + throw new RestApiException(GlobalErrorStatus.TITLE_IS_EMPTY); + if(request.content().isEmpty()) + throw new RestApiException(GlobalErrorStatus.CONTENT_IS_EMPTY); + + Room room = roomService.findById(request.roomNo()); + if(!room.isHost(userNo)) + throw new RestApiException(GlobalErrorStatus.ROOM_NOT_FOUND); + + + return WriteNoticeResponse.create(noticeService.writeNotice(userNo, room, request)); + + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java b/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java new file mode 100644 index 0000000..3c856c1 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java @@ -0,0 +1,31 @@ +package com.project.dorumdorum.domain.notice.domain.entity; + +import com.project.dorumdorum.domain.room.domain.entity.Room; +import com.project.dorumdorum.global.common.BaseEntity; + +import io.hypersistence.utils.hibernate.id.Tsid; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@Builder +public class Notice extends BaseEntity { + @Id @Tsid + private Long noticeNo; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "room_no", nullable = false) + private Room room; + + @Column(nullable = false) + private Long userNo; + + @Column(nullable = false) + private String title; + + @Column(nullable = false) + private String content; +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java b/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java new file mode 100644 index 0000000..9bcde32 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java @@ -0,0 +1,9 @@ +package com.project.dorumdorum.domain.notice.domain.repository; + + +import com.project.dorumdorum.domain.notice.domain.entity.Notice; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NoticeRepository extends JpaRepository { + +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java new file mode 100644 index 0000000..7e57be4 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java @@ -0,0 +1,30 @@ +package com.project.dorumdorum.domain.notice.service; + +import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; +import com.project.dorumdorum.domain.notice.domain.entity.Notice; +import com.project.dorumdorum.domain.notice.domain.repository.NoticeRepository; +import com.project.dorumdorum.domain.room.domain.entity.Room; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class NoticeService { + + private final NoticeRepository noticeRepository; + + + public Notice writeNotice(Long userNo, Room room, WriteNoticeRequest request) { + Notice entity = Notice.builder() + .room(room) + .userNo(userNo) + .title(request.title()) + .content(request.content()) + .build(); + + return noticeRepository.save(entity); + } + + + +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java new file mode 100644 index 0000000..37a00b7 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java @@ -0,0 +1,28 @@ +package com.project.dorumdorum.domain.notice.ui; + + +import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.WriteNoticeResponse; +import com.project.dorumdorum.domain.notice.application.usecase.WriteNoticeUseCase; +import com.project.dorumdorum.global.annotation.CurrentUser; +import com.project.dorumdorum.global.common.BaseResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class WriteNoticeController { + + private final WriteNoticeUseCase writeNoticeUseCase; + + @PostMapping("/api/notice") + public BaseResponse writeNotice( + @CurrentUser Long userNo, + @RequestBody @Valid WriteNoticeRequest request + ) { + return BaseResponse.onSuccess(writeNoticeUseCase.execute(userNo, request)); + } +} diff --git a/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java b/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java index 81f4699..6649213 100644 --- a/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java +++ b/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java @@ -52,9 +52,11 @@ public enum GlobalErrorStatus implements BaseCodeInterface { FRIENDSHIP_NOT_FOUND(HttpStatus.NOT_FOUND, "FRIEND005", "친구가 아닙니다."), REQUEST_NOT_RECEIVER(HttpStatus.FORBIDDEN, "FRIEND006", "해당 친구추가 요청의 수신자가 아닙니다."), REQUEST_NOT_SENDER(HttpStatus.FORBIDDEN, "FRIEND007", "해당 친구추가 요청의 발신자가 아닙니다."), - REQUEST_NOT_PENDING(HttpStatus.BAD_REQUEST, "FRIEND008", "해당 친구추가 요청이 대기 상태가 아닙니다.") + REQUEST_NOT_PENDING(HttpStatus.BAD_REQUEST, "FRIEND008", "해당 친구추가 요청이 대기 상태가 아닙니다."), - ; + // Notice + TITLE_IS_EMPTY(HttpStatus.BAD_REQUEST, "NOTICE001", "제목이 비어있습니다."), + CONTENT_IS_EMPTY(HttpStatus.BAD_REQUEST, "NOTICE002" , "본문이 비어있습니다."); private final HttpStatus httpStatus; private final boolean isSuccess = false; From 737e0b13d1df557b1a770ad673288fffae13ffd1 Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Mon, 25 Aug 2025 04:48:35 +0900 Subject: [PATCH 02/18] =?UTF-8?q?feat:=20=EA=B8=80=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20api=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/WriteNoticeRequest.java | 4 +-- .../dto/response/LoadNoticesResponse.java | 27 +++++++++++++++ .../usecase/LoadNoticesUseCase.java | 34 +++++++++++++++++++ .../domain/repository/NoticeRepository.java | 5 ++- .../domain/notice/service/NoticeService.java | 9 +++++ .../notice/ui/LoadNoticeController.java | 29 ++++++++++++++++ .../notice/ui/WriteNoticeController.java | 2 +- .../code/status/GlobalErrorStatus.java | 1 + 8 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/LoadNoticesResponse.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticeController.java diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java index 292226d..384304d 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java @@ -1,9 +1,9 @@ package com.project.dorumdorum.domain.notice.application.dto.request; public record WriteNoticeRequest( + Long roomNo, String title, - String content, - Long roomNo + String content ) { } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/LoadNoticesResponse.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/LoadNoticesResponse.java new file mode 100644 index 0000000..7c6d695 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/LoadNoticesResponse.java @@ -0,0 +1,27 @@ +package com.project.dorumdorum.domain.notice.application.dto.response; + +import com.project.dorumdorum.domain.notice.domain.entity.Notice; + +import lombok.Builder; +import java.time.LocalDateTime; + +@Builder +public record LoadNoticesResponse( + Long noticeNo, + Long userNo, + LocalDateTime createdAt, + LocalDateTime updatedAt, + String title, + String content +) { + public static LoadNoticesResponse create(Notice notice) { + return LoadNoticesResponse.builder() + .noticeNo(notice.getNoticeNo()) + .userNo(notice.getUserNo()) + .createdAt(notice.getCreatedAt()) + .updatedAt(notice.getUpdatedAt()) + .title(notice.getTitle()) + .content(notice.getContent()) + .build(); + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java new file mode 100644 index 0000000..74b2622 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java @@ -0,0 +1,34 @@ +package com.project.dorumdorum.domain.notice.application.usecase; + +import com.project.dorumdorum.domain.notice.application.dto.response.LoadNoticesResponse; +import com.project.dorumdorum.domain.notice.service.NoticeService; +import com.project.dorumdorum.domain.room.domain.entity.Room; +import com.project.dorumdorum.domain.room.domain.service.RoomService; +import com.project.dorumdorum.domain.room.domain.service.RoommateService; +import com.project.dorumdorum.domain.user.domain.service.UserService; +import com.project.dorumdorum.global.exception.RestApiException; +import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class LoadNoticesUseCase { + + private final UserService userService; + private final RoommateService roommateService; + private final RoomService roomService; + private final NoticeService noticeService; + + public List execute(Long userNo, Long roomNo) { + userService.validateExistsById(userNo); + + Room room = roomService.findById(roomNo); + if(!roommateService.isUserInRoom(userNo, room)) + throw new RestApiException(GlobalErrorStatus.USER_NOT_IN_ROOM); + + return noticeService.loadNoticeList(room); + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java b/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java index 9bcde32..084f898 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java @@ -1,9 +1,12 @@ package com.project.dorumdorum.domain.notice.domain.repository; - import com.project.dorumdorum.domain.notice.domain.entity.Notice; +import com.project.dorumdorum.domain.room.domain.entity.Room; + import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; public interface NoticeRepository extends JpaRepository { + List findByRoom(Room room); } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java index 7e57be4..64a233a 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java @@ -1,11 +1,14 @@ package com.project.dorumdorum.domain.notice.service; import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.LoadNoticesResponse; import com.project.dorumdorum.domain.notice.domain.entity.Notice; import com.project.dorumdorum.domain.notice.domain.repository.NoticeRepository; import com.project.dorumdorum.domain.room.domain.entity.Room; + import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.List; @Service @RequiredArgsConstructor @@ -26,5 +29,11 @@ public Notice writeNotice(Long userNo, Room room, WriteNoticeRequest request) { } + public List loadNoticeList(Room room) { + List notices = noticeRepository.findByRoom(room); + return notices.stream() + .map(LoadNoticesResponse::create) + .toList(); + } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticeController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticeController.java new file mode 100644 index 0000000..1167ad4 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticeController.java @@ -0,0 +1,29 @@ +package com.project.dorumdorum.domain.notice.ui; + + +import com.project.dorumdorum.domain.notice.application.dto.response.LoadNoticesResponse; +import com.project.dorumdorum.domain.notice.application.usecase.LoadNoticesUseCase; +import com.project.dorumdorum.global.annotation.CurrentUser; +import com.project.dorumdorum.global.common.BaseResponse; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import java.util.List; + +@RestController +@RequiredArgsConstructor +public class LoadNoticeController { + + private final LoadNoticesUseCase loadNoticesUseCase; + + @GetMapping("/api/notices") + public BaseResponse> loadNotices( + @CurrentUser Long userNo, + @RequestParam Long roomNo + ) { + return BaseResponse.onSuccess(loadNoticesUseCase.execute(userNo, roomNo)); + } + +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java index 37a00b7..77e0910 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java @@ -1,11 +1,11 @@ package com.project.dorumdorum.domain.notice.ui; - import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; import com.project.dorumdorum.domain.notice.application.dto.response.WriteNoticeResponse; import com.project.dorumdorum.domain.notice.application.usecase.WriteNoticeUseCase; import com.project.dorumdorum.global.annotation.CurrentUser; import com.project.dorumdorum.global.common.BaseResponse; + import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; diff --git a/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java b/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java index 6649213..0871004 100644 --- a/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java +++ b/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java @@ -42,6 +42,7 @@ public enum GlobalErrorStatus implements BaseCodeInterface { ROOM_REQUEST_NOT_FOUND(HttpStatus.NOT_FOUND, "ROOM006", "존재하지 않는 요청입니다."), ROOM_IS_NOT_FULL(HttpStatus.BAD_REQUEST, "ROOM007", "방이 가득차지 않았습니다."), ALREADY_CONFIRM_REQUEST(HttpStatus.BAD_REQUEST, "ROOM008", "이미 처리된 요청입니다."), + USER_NOT_IN_ROOM(HttpStatus.BAD_REQUEST, "ROOM009", "멤버가 아닙니다."), // 이거 추가해도 될까요? // Friend From 7516614f79182221a567c43dbfd489ae3c6fa545 Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Mon, 25 Aug 2025 20:26:46 +0900 Subject: [PATCH 03/18] =?UTF-8?q?feat:=20=EA=B8=80=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?api=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/UpdateNoticeRequest.java | 12 ++++++ .../dto/request/WriteNoticeRequest.java | 9 ++-- ...ticesResponse.java => NoticeResponse.java} | 6 +-- .../dto/response/WriteNoticeResponse.java | 27 ------------ .../usecase/LoadNoticesUseCase.java | 4 +- .../usecase/UpdateNoticeUseCase.java | 42 +++++++++++++++++++ .../usecase/WriteNoticeUseCase.java | 8 ++-- .../domain/notice/domain/entity/Notice.java | 10 +++++ .../domain/notice/service/NoticeService.java | 20 +++++++-- .../notice/ui/LoadNoticeController.java | 4 +- .../notice/ui/UpdateNoticeController.java | 27 ++++++++++++ .../notice/ui/WriteNoticeController.java | 4 +- .../code/status/GlobalErrorStatus.java | 7 +++- 13 files changed, 133 insertions(+), 47 deletions(-) create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateNoticeRequest.java rename src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/{LoadNoticesResponse.java => NoticeResponse.java} (82%) delete mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/WriteNoticeResponse.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateNoticeController.java diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateNoticeRequest.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateNoticeRequest.java new file mode 100644 index 0000000..dc5ef29 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateNoticeRequest.java @@ -0,0 +1,12 @@ +package com.project.dorumdorum.domain.notice.application.dto.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public record UpdateNoticeRequest( + @NotNull Long roomNo, + @NotNull Long noticeNo, + @NotBlank String title, + @NotBlank String content +) { +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java index 384304d..48399fd 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java @@ -1,9 +1,12 @@ package com.project.dorumdorum.domain.notice.application.dto.request; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + public record WriteNoticeRequest( - Long roomNo, - String title, - String content + @NotNull Long roomNo, + @NotBlank String title, + @NotBlank String content ) { } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/LoadNoticesResponse.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticeResponse.java similarity index 82% rename from src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/LoadNoticesResponse.java rename to src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticeResponse.java index 7c6d695..4054b95 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/LoadNoticesResponse.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticeResponse.java @@ -6,7 +6,7 @@ import java.time.LocalDateTime; @Builder -public record LoadNoticesResponse( +public record NoticeResponse( Long noticeNo, Long userNo, LocalDateTime createdAt, @@ -14,8 +14,8 @@ public record LoadNoticesResponse( String title, String content ) { - public static LoadNoticesResponse create(Notice notice) { - return LoadNoticesResponse.builder() + public static NoticeResponse create(Notice notice) { + return NoticeResponse.builder() .noticeNo(notice.getNoticeNo()) .userNo(notice.getUserNo()) .createdAt(notice.getCreatedAt()) diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/WriteNoticeResponse.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/WriteNoticeResponse.java deleted file mode 100644 index ca26bb5..0000000 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/WriteNoticeResponse.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.project.dorumdorum.domain.notice.application.dto.response; - -import com.project.dorumdorum.domain.notice.domain.entity.Notice; - -import lombok.Builder; -import java.time.LocalDateTime; - -@Builder -public record WriteNoticeResponse( - Long noticeNo, - Long userNo, - LocalDateTime createdAt, - LocalDateTime updatedAt, - String title, - String content -) { - public static WriteNoticeResponse create(Notice notice) { - return WriteNoticeResponse.builder() - .noticeNo(notice.getNoticeNo()) - .userNo(notice.getUserNo()) - .createdAt(notice.getCreatedAt()) - .updatedAt(notice.getUpdatedAt()) - .title(notice.getTitle()) - .content(notice.getContent()) - .build(); - } -} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java index 74b2622..7c88a8d 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java @@ -1,6 +1,6 @@ package com.project.dorumdorum.domain.notice.application.usecase; -import com.project.dorumdorum.domain.notice.application.dto.response.LoadNoticesResponse; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.service.NoticeService; import com.project.dorumdorum.domain.room.domain.entity.Room; import com.project.dorumdorum.domain.room.domain.service.RoomService; @@ -22,7 +22,7 @@ public class LoadNoticesUseCase { private final RoomService roomService; private final NoticeService noticeService; - public List execute(Long userNo, Long roomNo) { + public List execute(Long userNo, Long roomNo) { userService.validateExistsById(userNo); Room room = roomService.findById(roomNo); diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java new file mode 100644 index 0000000..314d582 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java @@ -0,0 +1,42 @@ +package com.project.dorumdorum.domain.notice.application.usecase; + +import com.project.dorumdorum.domain.notice.application.dto.request.UpdateNoticeRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; +import com.project.dorumdorum.domain.notice.domain.entity.Notice; +import com.project.dorumdorum.domain.notice.service.NoticeService; +import com.project.dorumdorum.domain.room.domain.entity.Room; +import com.project.dorumdorum.domain.room.domain.service.RoomService; +import com.project.dorumdorum.domain.user.domain.service.UserService; +import com.project.dorumdorum.global.exception.RestApiException; +import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class UpdateNoticeUseCase { + + private final UserService userService; + private final RoomService roomService; + private final NoticeService noticeService; + + @Transactional + public NoticeResponse execute(Long userNo, UpdateNoticeRequest request) { + userService.validateExistsById(userNo); + + Room room = roomService.findById(request.roomNo()); + + // 방장이 아닌 구성원도 작성가능 할 경우 삭제 + if(!room.isHost(userNo)) + throw new RestApiException(GlobalErrorStatus.NO_PERMISSION_ON_NOTICE); + + Notice notice = noticeService.findById(request.noticeNo()); + + if(!notice.isWriter(userNo)) + throw new RestApiException(GlobalErrorStatus.NO_PERMISSION_ON_NOTICE); + + return noticeService.updateNotice(notice, request); + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java index c747a42..46f0fb3 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java @@ -1,7 +1,7 @@ package com.project.dorumdorum.domain.notice.application.usecase; import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; -import com.project.dorumdorum.domain.notice.application.dto.response.WriteNoticeResponse; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.service.NoticeService; import com.project.dorumdorum.domain.room.domain.entity.Room; import com.project.dorumdorum.domain.room.domain.service.RoomService; @@ -24,7 +24,7 @@ public class WriteNoticeUseCase { private final RoommateService roommateService; @Transactional - public WriteNoticeResponse execute(Long userNo, WriteNoticeRequest request) { + public NoticeResponse execute(Long userNo, WriteNoticeRequest request) { userService.validateExistsById(userNo); if(request.title().isEmpty()) @@ -34,10 +34,10 @@ public WriteNoticeResponse execute(Long userNo, WriteNoticeRequest request) { Room room = roomService.findById(request.roomNo()); if(!room.isHost(userNo)) - throw new RestApiException(GlobalErrorStatus.ROOM_NOT_FOUND); + throw new RestApiException(GlobalErrorStatus.NO_PERMISSION_ON_NOTICE); - return WriteNoticeResponse.create(noticeService.writeNotice(userNo, room, request)); + return NoticeResponse.create(noticeService.writeNotice(userNo, room, request)); } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java b/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java index 3c856c1..4fa59d1 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java @@ -5,6 +5,7 @@ import io.hypersistence.utils.hibernate.id.Tsid; import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; import lombok.*; @Entity @@ -28,4 +29,13 @@ public class Notice extends BaseEntity { @Column(nullable = false) private String content; + + public void update(@NotBlank String title, @NotBlank String content) { + this.title = title; + this.content = content; + } + + public boolean isWriter(Long userNo) { + return this.userNo.equals(userNo); + } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java index 64a233a..905d7e1 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java @@ -1,11 +1,15 @@ package com.project.dorumdorum.domain.notice.service; +import com.project.dorumdorum.domain.notice.application.dto.request.UpdateNoticeRequest; import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; -import com.project.dorumdorum.domain.notice.application.dto.response.LoadNoticesResponse; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.domain.entity.Notice; import com.project.dorumdorum.domain.notice.domain.repository.NoticeRepository; import com.project.dorumdorum.domain.room.domain.entity.Room; +import com.project.dorumdorum.global.exception.RestApiException; +import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; @@ -29,11 +33,21 @@ public Notice writeNotice(Long userNo, Room room, WriteNoticeRequest request) { } - public List loadNoticeList(Room room) { + public List loadNoticeList(Room room) { List notices = noticeRepository.findByRoom(room); return notices.stream() - .map(LoadNoticesResponse::create) + .map(NoticeResponse::create) .toList(); } + + public Notice findById(@NotNull Long noticeNo) { + return noticeRepository.findById(noticeNo) + .orElseThrow(() -> new RestApiException(GlobalErrorStatus.NOTICE_NOT_FOUND)); + } + + public NoticeResponse updateNotice(Notice notice, UpdateNoticeRequest request) { + notice.update(request.title(), request.content()); + return NoticeResponse.create(notice); + } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticeController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticeController.java index 1167ad4..4a3bc10 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticeController.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticeController.java @@ -1,7 +1,7 @@ package com.project.dorumdorum.domain.notice.ui; -import com.project.dorumdorum.domain.notice.application.dto.response.LoadNoticesResponse; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.application.usecase.LoadNoticesUseCase; import com.project.dorumdorum.global.annotation.CurrentUser; import com.project.dorumdorum.global.common.BaseResponse; @@ -19,7 +19,7 @@ public class LoadNoticeController { private final LoadNoticesUseCase loadNoticesUseCase; @GetMapping("/api/notices") - public BaseResponse> loadNotices( + public BaseResponse> loadNotices( @CurrentUser Long userNo, @RequestParam Long roomNo ) { diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateNoticeController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateNoticeController.java new file mode 100644 index 0000000..b83a787 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateNoticeController.java @@ -0,0 +1,27 @@ +package com.project.dorumdorum.domain.notice.ui; + +import com.project.dorumdorum.domain.notice.application.dto.request.UpdateNoticeRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; +import com.project.dorumdorum.domain.notice.application.usecase.UpdateNoticeUseCase; +import com.project.dorumdorum.global.annotation.CurrentUser; +import com.project.dorumdorum.global.common.BaseResponse; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class UpdateNoticeController { + + private final UpdateNoticeUseCase updateNoticeUseCase; + + @PatchMapping("/api/notice") + public BaseResponse updateNotice( + @CurrentUser Long userNo, + @RequestBody UpdateNoticeRequest request + ) { + return BaseResponse.onSuccess(updateNoticeUseCase.execute(userNo, request)); + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java index 77e0910..5821a8f 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java @@ -1,7 +1,7 @@ package com.project.dorumdorum.domain.notice.ui; import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; -import com.project.dorumdorum.domain.notice.application.dto.response.WriteNoticeResponse; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.application.usecase.WriteNoticeUseCase; import com.project.dorumdorum.global.annotation.CurrentUser; import com.project.dorumdorum.global.common.BaseResponse; @@ -19,7 +19,7 @@ public class WriteNoticeController { private final WriteNoticeUseCase writeNoticeUseCase; @PostMapping("/api/notice") - public BaseResponse writeNotice( + public BaseResponse writeNotice( @CurrentUser Long userNo, @RequestBody @Valid WriteNoticeRequest request ) { diff --git a/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java b/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java index 0871004..7f3a820 100644 --- a/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java +++ b/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java @@ -57,7 +57,12 @@ public enum GlobalErrorStatus implements BaseCodeInterface { // Notice TITLE_IS_EMPTY(HttpStatus.BAD_REQUEST, "NOTICE001", "제목이 비어있습니다."), - CONTENT_IS_EMPTY(HttpStatus.BAD_REQUEST, "NOTICE002" , "본문이 비어있습니다."); + CONTENT_IS_EMPTY(HttpStatus.BAD_REQUEST, "NOTICE002" , "본문이 비어있습니다."), + NO_PERMISSION_ON_NOTICE(HttpStatus.BAD_REQUEST, "NOTICE003", "공지사항 편집 권한이 없습니다."), + NOTICE_NOT_FOUND(HttpStatus.BAD_REQUEST, "NOTICE004", "공지사항을 찾을 수 없습니다.") + + + ; private final HttpStatus httpStatus; private final boolean isSuccess = false; From 96dff5316fbbd3b4c06aef2ff793ddc43bc9606d Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Mon, 25 Aug 2025 23:02:03 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat:=20=EA=B8=80=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?api=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/DeleteNoticeUseCase.java | 30 +++++++++++++++++++ .../domain/notice/service/NoticeService.java | 4 +++ .../notice/ui/DeleteNoticeController.java | 26 ++++++++++++++++ ...roller.java => LoadNoticesController.java} | 2 +- .../code/status/GlobalErrorStatus.java | 4 +-- 5 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteNoticeUseCase.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteNoticeController.java rename src/main/java/com/project/dorumdorum/domain/notice/ui/{LoadNoticeController.java => LoadNoticesController.java} (96%) diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteNoticeUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteNoticeUseCase.java new file mode 100644 index 0000000..cc54680 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteNoticeUseCase.java @@ -0,0 +1,30 @@ +package com.project.dorumdorum.domain.notice.application.usecase; + +import com.project.dorumdorum.domain.notice.domain.entity.Notice; +import com.project.dorumdorum.domain.notice.service.NoticeService; +import com.project.dorumdorum.domain.user.domain.service.UserService; +import com.project.dorumdorum.global.exception.RestApiException; +import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class DeleteNoticeUseCase { + + private final UserService userService; + private final NoticeService noticeService; + + public void execute(Long userNo, Long noticeNo) { + userService.validateExistsById(userNo); + + Notice notice = noticeService.findById(noticeNo); + + if(notice.isDeleted()) + throw new RestApiException(GlobalErrorStatus.NOTICE_ALREADY_DELETED); + if(!notice.isWriter(userNo)) + throw new RestApiException(GlobalErrorStatus.NO_PERMISSION_ON_NOTICE); + + noticeService.deleteNotice(noticeNo); + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java index 905d7e1..a665bb4 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java @@ -50,4 +50,8 @@ public NoticeResponse updateNotice(Notice notice, UpdateNoticeRequest request) { notice.update(request.title(), request.content()); return NoticeResponse.create(notice); } + + public void deleteNotice(Long noticeNo) { + noticeRepository.deleteById(noticeNo); + } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteNoticeController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteNoticeController.java new file mode 100644 index 0000000..4a76bc5 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteNoticeController.java @@ -0,0 +1,26 @@ +package com.project.dorumdorum.domain.notice.ui; + + +import com.project.dorumdorum.domain.notice.application.usecase.DeleteNoticeUseCase; +import com.project.dorumdorum.global.annotation.CurrentUser; +import com.project.dorumdorum.global.common.BaseResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class DeleteNoticeController { + + private final DeleteNoticeUseCase deleteNoticeUseCase; + + @DeleteMapping("/api/notice") + public BaseResponse deleteNotice( + @CurrentUser Long userNo, + @RequestParam Long noticeNo + ) { + deleteNoticeUseCase.execute(userNo, noticeNo); + return BaseResponse.onSuccess(); + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticeController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticesController.java similarity index 96% rename from src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticeController.java rename to src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticesController.java index 4a3bc10..0447294 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticeController.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticesController.java @@ -14,7 +14,7 @@ @RestController @RequiredArgsConstructor -public class LoadNoticeController { +public class LoadNoticesController { private final LoadNoticesUseCase loadNoticesUseCase; diff --git a/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java b/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java index 7f3a820..ebdebec 100644 --- a/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java +++ b/src/main/java/com/project/dorumdorum/global/exception/code/status/GlobalErrorStatus.java @@ -59,8 +59,8 @@ public enum GlobalErrorStatus implements BaseCodeInterface { TITLE_IS_EMPTY(HttpStatus.BAD_REQUEST, "NOTICE001", "제목이 비어있습니다."), CONTENT_IS_EMPTY(HttpStatus.BAD_REQUEST, "NOTICE002" , "본문이 비어있습니다."), NO_PERMISSION_ON_NOTICE(HttpStatus.BAD_REQUEST, "NOTICE003", "공지사항 편집 권한이 없습니다."), - NOTICE_NOT_FOUND(HttpStatus.BAD_REQUEST, "NOTICE004", "공지사항을 찾을 수 없습니다.") - + NOTICE_NOT_FOUND(HttpStatus.BAD_REQUEST, "NOTICE004", "공지사항을 찾을 수 없습니다."), + NOTICE_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "NOTICE005", "이미 삭제된 글입니다.") ; From d1e12b6cb7605760a8ae2f6e7e9fb4a5b95c0692 Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Mon, 25 Aug 2025 23:20:00 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat:=20=EA=B8=80=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=EB=B3=B4=EA=B8=B0=20api=20=EA=B0=9C=EB=B0=9C,=20=EA=B8=80=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20dto=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/NoticesResponse.java | 23 +++++++++++++++++++ .../usecase/LoadNoticesUseCase.java | 9 +++++++- .../domain/notice/service/NoticeService.java | 11 +++++++-- .../notice/ui/LoadNoticesController.java | 13 +++++++++-- 4 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticesResponse.java diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticesResponse.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticesResponse.java new file mode 100644 index 0000000..2e2e004 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticesResponse.java @@ -0,0 +1,23 @@ +package com.project.dorumdorum.domain.notice.application.dto.response; + +import com.project.dorumdorum.domain.notice.domain.entity.Notice; +import lombok.Builder; + +import java.time.LocalDateTime; + +@Builder +public record NoticesResponse( + Long noticeNo, + Long userNo, + LocalDateTime updatedAt, + String title +) { + public static NoticesResponse create(Notice notice) { + return NoticesResponse.builder() + .noticeNo(notice.getNoticeNo()) + .userNo(notice.getUserNo()) + .updatedAt(notice.getUpdatedAt()) + .title(notice.getTitle()) + .build(); + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java index 7c88a8d..b63ac44 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java @@ -1,6 +1,7 @@ package com.project.dorumdorum.domain.notice.application.usecase; import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticesResponse; import com.project.dorumdorum.domain.notice.service.NoticeService; import com.project.dorumdorum.domain.room.domain.entity.Room; import com.project.dorumdorum.domain.room.domain.service.RoomService; @@ -22,7 +23,7 @@ public class LoadNoticesUseCase { private final RoomService roomService; private final NoticeService noticeService; - public List execute(Long userNo, Long roomNo) { + public List loadNotices(Long userNo, Long roomNo) { userService.validateExistsById(userNo); Room room = roomService.findById(roomNo); @@ -31,4 +32,10 @@ public List execute(Long userNo, Long roomNo) { return noticeService.loadNoticeList(room); } + + public NoticeResponse loadNotice(Long userNo, Long noticeNo) { + userService.validateExistsById(userNo); + + return noticeService.loadNotice(noticeNo); + } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java index a665bb4..3c7cdf8 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java @@ -3,6 +3,7 @@ import com.project.dorumdorum.domain.notice.application.dto.request.UpdateNoticeRequest; import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticesResponse; import com.project.dorumdorum.domain.notice.domain.entity.Notice; import com.project.dorumdorum.domain.notice.domain.repository.NoticeRepository; import com.project.dorumdorum.domain.room.domain.entity.Room; @@ -33,11 +34,17 @@ public Notice writeNotice(Long userNo, Room room, WriteNoticeRequest request) { } - public List loadNoticeList(Room room) { + public NoticeResponse loadNotice(Long noticeNo) { + Notice notice = noticeRepository.findById(noticeNo) + .orElseThrow(() -> new RestApiException(GlobalErrorStatus.NOTICE_NOT_FOUND)); + return NoticeResponse.create(notice); + } + + public List loadNoticeList(Room room) { List notices = noticeRepository.findByRoom(room); return notices.stream() - .map(NoticeResponse::create) + .map(NoticesResponse::create) .toList(); } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticesController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticesController.java index 0447294..b699cf2 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticesController.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticesController.java @@ -2,6 +2,7 @@ import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticesResponse; import com.project.dorumdorum.domain.notice.application.usecase.LoadNoticesUseCase; import com.project.dorumdorum.global.annotation.CurrentUser; import com.project.dorumdorum.global.common.BaseResponse; @@ -19,11 +20,19 @@ public class LoadNoticesController { private final LoadNoticesUseCase loadNoticesUseCase; @GetMapping("/api/notices") - public BaseResponse> loadNotices( + public BaseResponse> loadNotices( @CurrentUser Long userNo, @RequestParam Long roomNo ) { - return BaseResponse.onSuccess(loadNoticesUseCase.execute(userNo, roomNo)); + return BaseResponse.onSuccess(loadNoticesUseCase.loadNotices(userNo, roomNo)); + } + + @GetMapping("/api/notice") + public BaseResponse loadNotice( + @CurrentUser Long userNo, + @RequestParam Long noticeNo + ) { + return BaseResponse.onSuccess(loadNoticesUseCase.loadNotice(userNo, noticeNo)); } } From b9ba9dad9ed00cfb54f51a8bd2735bf0ce8300a7 Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Mon, 25 Aug 2025 23:42:21 +0900 Subject: [PATCH 06/18] =?UTF-8?q?fix:=20room=20=EC=97=94=ED=84=B0=ED=8B=B0?= =?UTF-8?q?=EC=97=90=20hostUserNo=20=EC=A0=80=EC=9E=A5=EB=90=98=EC=A7=80?= =?UTF-8?q?=20=EC=95=8A=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/room/application/dto/usecase/CreateRoomUseCase.java | 2 +- .../dorumdorum/domain/room/domain/service/RoomService.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/project/dorumdorum/domain/room/application/dto/usecase/CreateRoomUseCase.java b/src/main/java/com/project/dorumdorum/domain/room/application/dto/usecase/CreateRoomUseCase.java index da2d49b..dba8b6e 100644 --- a/src/main/java/com/project/dorumdorum/domain/room/application/dto/usecase/CreateRoomUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/room/application/dto/usecase/CreateRoomUseCase.java @@ -22,7 +22,7 @@ public class CreateRoomUseCase { public void execute(Long userNo, RoomCreateRequest request) { userService.validateExistsById(userNo); - Room room = roomService.create(request); + Room room = roomService.create(request, userNo); roommateService.create(userNo, room, RoomRole.HOST); } } diff --git a/src/main/java/com/project/dorumdorum/domain/room/domain/service/RoomService.java b/src/main/java/com/project/dorumdorum/domain/room/domain/service/RoomService.java index 7e92065..de63a54 100644 --- a/src/main/java/com/project/dorumdorum/domain/room/domain/service/RoomService.java +++ b/src/main/java/com/project/dorumdorum/domain/room/domain/service/RoomService.java @@ -23,12 +23,13 @@ public class RoomService { private final RoomRepository roomRepository; - public Room create(RoomCreateRequest request) { + public Room create(RoomCreateRequest request, Long hostUserNo) { Room entity = Room.builder() .capacity(request.capacity()) .roomType(request.roomType()) .tags(request.tags()) .title(request.title()) + .hostUserNo(hostUserNo) .build(); return roomRepository.save(entity); From e9d16c95b28aa21bdbf9f9fff30648ccd47fe201 Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Fri, 29 Aug 2025 22:58:37 +0900 Subject: [PATCH 07/18] =?UTF-8?q?refactor:=20user=20=EC=97=94=ED=84=B0?= =?UTF-8?q?=ED=8B=B0=20room=20=ED=95=84=EB=93=9C=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notice/application/usecase/WriteNoticeUseCase.java | 4 +--- .../dorumdorum/domain/notice/domain/entity/Notice.java | 6 ++---- .../dorumdorum/domain/notice/service/NoticeService.java | 4 ++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java index 46f0fb3..48f3676 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java @@ -5,7 +5,6 @@ import com.project.dorumdorum.domain.notice.service.NoticeService; import com.project.dorumdorum.domain.room.domain.entity.Room; import com.project.dorumdorum.domain.room.domain.service.RoomService; -import com.project.dorumdorum.domain.room.domain.service.RoommateService; import com.project.dorumdorum.domain.user.domain.service.UserService; import com.project.dorumdorum.global.exception.RestApiException; import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; @@ -21,7 +20,6 @@ public class WriteNoticeUseCase { private final UserService userService; private final NoticeService noticeService; private final RoomService roomService; - private final RoommateService roommateService; @Transactional public NoticeResponse execute(Long userNo, WriteNoticeRequest request) { @@ -37,7 +35,7 @@ public NoticeResponse execute(Long userNo, WriteNoticeRequest request) { throw new RestApiException(GlobalErrorStatus.NO_PERMISSION_ON_NOTICE); - return NoticeResponse.create(noticeService.writeNotice(userNo, room, request)); + return NoticeResponse.create(noticeService.writeNotice(userNo, room.getRoomNo(), request)); } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java b/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java index 4fa59d1..586831d 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Notice.java @@ -1,6 +1,5 @@ package com.project.dorumdorum.domain.notice.domain.entity; -import com.project.dorumdorum.domain.room.domain.entity.Room; import com.project.dorumdorum.global.common.BaseEntity; import io.hypersistence.utils.hibernate.id.Tsid; @@ -17,9 +16,8 @@ public class Notice extends BaseEntity { @Id @Tsid private Long noticeNo; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "room_no", nullable = false) - private Room room; + @Column(nullable = false) + private Long roomNo; @Column(nullable = false) private Long userNo; diff --git a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java index 3c7cdf8..7bec782 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java @@ -22,9 +22,9 @@ public class NoticeService { private final NoticeRepository noticeRepository; - public Notice writeNotice(Long userNo, Room room, WriteNoticeRequest request) { + public Notice writeNotice(Long userNo, Long roomNo, WriteNoticeRequest request) { Notice entity = Notice.builder() - .room(room) + .roomNo(roomNo) .userNo(userNo) .title(request.title()) .content(request.content()) From 0ed0bba9061c5de0fbe8c86ced6601a0f62d65ce Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Sat, 6 Sep 2025 16:43:04 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat:=20=EB=8C=93=EA=B8=80=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20api=20=EA=B8=B0=EB=8A=A5=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/WriteCommentRequest.java | 7 ++++ .../dto/response/CommentResponse.java | 23 ++++++++++++ .../usecase/WriteCommentUseCase.java | 35 +++++++++++++++++++ .../domain/notice/domain/entity/Comment.java | 30 ++++++++++++++++ .../domain/repository/CommentRepository.java | 8 +++++ .../domain/notice/service/CommentService.java | 22 ++++++++++++ .../notice/ui/WriteCommentController.java | 29 +++++++++++++++ 7 files changed, 154 insertions(+) create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteCommentRequest.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/CommentResponse.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteCommentUseCase.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Comment.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/domain/repository/CommentRepository.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/service/CommentService.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/WriteCommentController.java diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteCommentRequest.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteCommentRequest.java new file mode 100644 index 0000000..939cb68 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteCommentRequest.java @@ -0,0 +1,7 @@ +package com.project.dorumdorum.domain.notice.application.dto.request; + +public record WriteCommentRequest( + Long noticeNo, + String content +) { +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/CommentResponse.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/CommentResponse.java new file mode 100644 index 0000000..195b665 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/CommentResponse.java @@ -0,0 +1,23 @@ +package com.project.dorumdorum.domain.notice.application.dto.response; + +import com.project.dorumdorum.domain.notice.domain.entity.Comment; +import lombok.Builder; + +import java.time.LocalDateTime; + +@Builder +public record CommentResponse( + Long noticeNo, + Long userNo, + LocalDateTime updated_at, + String content +) { + public static CommentResponse create(Comment comment) { + return CommentResponse.builder() + .noticeNo(comment.getNoticeNo()) + .userNo(comment.getUserNo()) + .updated_at(comment.getUpdatedAt()) + .content(comment.getContent()) + .build(); + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteCommentUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteCommentUseCase.java new file mode 100644 index 0000000..b7f7264 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteCommentUseCase.java @@ -0,0 +1,35 @@ +package com.project.dorumdorum.domain.notice.application.usecase; + + +import com.project.dorumdorum.domain.notice.application.dto.request.WriteCommentRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.CommentResponse; +import com.project.dorumdorum.domain.notice.domain.entity.Comment; +import com.project.dorumdorum.domain.notice.domain.entity.Notice; +import com.project.dorumdorum.domain.notice.service.CommentService; +import com.project.dorumdorum.domain.notice.service.NoticeService; +import com.project.dorumdorum.domain.user.domain.service.UserService; +import com.project.dorumdorum.global.exception.RestApiException; +import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class WriteCommentUseCase { + + private final UserService userService; + private final NoticeService noticeService; + private final CommentService commentService; + + public CommentResponse execute(Long userNo, WriteCommentRequest request) { + userService.validateExistsById(userNo); + + Notice notice = noticeService.findById(request.noticeNo()); + + if(request.content().isEmpty()) + throw new RestApiException(GlobalErrorStatus.CONTENT_IS_EMPTY); + Comment comment = commentService.saveComment(userNo, notice.getNoticeNo(), request.content()); + + return CommentResponse.create(comment); + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Comment.java b/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Comment.java new file mode 100644 index 0000000..0a09cf4 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Comment.java @@ -0,0 +1,30 @@ +package com.project.dorumdorum.domain.notice.domain.entity; + +import com.project.dorumdorum.global.common.BaseEntity; +import io.hypersistence.utils.hibernate.id.Tsid; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import lombok.*; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +@Builder +public class Comment extends BaseEntity { + + @Id @Tsid + private Long commentNo; + + @Column(nullable = false) + private Long userNo; + + @Column(nullable = false) + private Long noticeNo; + + @Column(nullable = false) + private String content; + + +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/CommentRepository.java b/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/CommentRepository.java new file mode 100644 index 0000000..6f01525 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/CommentRepository.java @@ -0,0 +1,8 @@ +package com.project.dorumdorum.domain.notice.domain.repository; + +import com.project.dorumdorum.domain.notice.domain.entity.Comment; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CommentRepository extends JpaRepository { + +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/service/CommentService.java b/src/main/java/com/project/dorumdorum/domain/notice/service/CommentService.java new file mode 100644 index 0000000..1aea13b --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/service/CommentService.java @@ -0,0 +1,22 @@ +package com.project.dorumdorum.domain.notice.service; + +import com.project.dorumdorum.domain.notice.domain.entity.Comment; +import com.project.dorumdorum.domain.notice.domain.repository.CommentRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CommentService { + + private final CommentRepository commentRepository; + + public Comment saveComment(Long userNo, Long noticeNo, String content) { + Comment entity = Comment.builder() + .userNo(userNo) + .noticeNo(noticeNo) + .content(content) + .build(); + return commentRepository.save(entity); + } +} diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteCommentController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteCommentController.java new file mode 100644 index 0000000..4c87f3e --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteCommentController.java @@ -0,0 +1,29 @@ +package com.project.dorumdorum.domain.notice.ui; + + +import com.project.dorumdorum.domain.notice.application.dto.request.WriteCommentRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.CommentResponse; +import com.project.dorumdorum.domain.notice.application.usecase.WriteCommentUseCase; +import com.project.dorumdorum.global.annotation.CurrentUser; +import com.project.dorumdorum.global.common.BaseResponse; +import jakarta.validation.Valid; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +public class WriteCommentController { + + private final WriteCommentUseCase writeCommentUseCase; + + @PostMapping("/api/comment") + public BaseResponse writeComment( + @CurrentUser Long userNo, + @Valid @RequestBody WriteCommentRequest request + ) { + return BaseResponse.onSuccess(writeCommentUseCase.execute(userNo, request)); + } +} From 25cff7eee09381d4bccc666accb476c0ba81d8b3 Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Sat, 6 Sep 2025 17:24:27 +0900 Subject: [PATCH 09/18] =?UTF-8?q?refactor:=20Notice=20=EC=97=94=ED=84=B0?= =?UTF-8?q?=ED=8B=B0=20=ED=95=84=EB=93=9C=20=ED=83=80=EC=9E=85=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notice/application/usecase/LoadNoticesUseCase.java | 2 +- .../domain/notice/domain/repository/NoticeRepository.java | 3 +-- .../dorumdorum/domain/notice/service/NoticeService.java | 5 ++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java index b63ac44..96fe1df 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java @@ -30,7 +30,7 @@ public List loadNotices(Long userNo, Long roomNo) { if(!roommateService.isUserInRoom(userNo, room)) throw new RestApiException(GlobalErrorStatus.USER_NOT_IN_ROOM); - return noticeService.loadNoticeList(room); + return noticeService.loadNoticeList(room.getRoomNo()); } public NoticeResponse loadNotice(Long userNo, Long noticeNo) { diff --git a/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java b/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java index 084f898..111d5f5 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/NoticeRepository.java @@ -1,12 +1,11 @@ package com.project.dorumdorum.domain.notice.domain.repository; import com.project.dorumdorum.domain.notice.domain.entity.Notice; -import com.project.dorumdorum.domain.room.domain.entity.Room; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; public interface NoticeRepository extends JpaRepository { - List findByRoom(Room room); + List findByRoomNo(Long roomNo); } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java index 7bec782..39f446a 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java @@ -6,7 +6,6 @@ import com.project.dorumdorum.domain.notice.application.dto.response.NoticesResponse; import com.project.dorumdorum.domain.notice.domain.entity.Notice; import com.project.dorumdorum.domain.notice.domain.repository.NoticeRepository; -import com.project.dorumdorum.domain.room.domain.entity.Room; import com.project.dorumdorum.global.exception.RestApiException; import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; @@ -40,8 +39,8 @@ public NoticeResponse loadNotice(Long noticeNo) { return NoticeResponse.create(notice); } - public List loadNoticeList(Room room) { - List notices = noticeRepository.findByRoom(room); + public List loadNoticeList(Long roomNo) { + List notices = noticeRepository.findByRoomNo(roomNo); return notices.stream() .map(NoticesResponse::create) From 37f38f1fa37b4e5cbf6f2a14ddebf9ea087d2dc3 Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Sun, 28 Dec 2025 02:08:25 +0900 Subject: [PATCH 10/18] build: add s3 dependency --- build.gradle | 3 +++ src/main/resources/application.yml | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3241b91..6b6df67 100644 --- a/build.gradle +++ b/build.gradle @@ -68,6 +68,9 @@ dependencies { // Firebase implementation 'com.google.firebase:firebase-admin:9.2.0' + // AWS (v2 SDK BOM + S3) + implementation 'software.amazon.awssdk:s3:2.25.69' + // Test testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.projectreactor:reactor-test' diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 41ef263..bd50f7c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -123,4 +123,9 @@ include-email-domains: firebase: service-account: - path: "/firebase/dorumdorum-9ce75-firebase-adminsdk-fbsvc-8d66c0dccb.json" \ No newline at end of file + path: "/firebase/dorumdorum-9ce75-firebase-adminsdk-fbsvc-8d66c0dccb.json" + +aws: + s3: + bucket: ${AWS_S3_BUCKET} + region: ${AWS_REGION:ap-northeast-2} \ No newline at end of file From 70862de6a7931d1452113f2f4142b180678c3f6b Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Sun, 28 Dec 2025 02:55:30 +0900 Subject: [PATCH 11/18] feat: add s3 config --- .../dorumdorum/global/config/S3Config.java | 34 +++++++++++++++++++ .../global/properties/S3Properties.java | 22 ++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/main/java/com/project/dorumdorum/global/config/S3Config.java create mode 100644 src/main/java/com/project/dorumdorum/global/properties/S3Properties.java diff --git a/src/main/java/com/project/dorumdorum/global/config/S3Config.java b/src/main/java/com/project/dorumdorum/global/config/S3Config.java new file mode 100644 index 0000000..ca2ca0f --- /dev/null +++ b/src/main/java/com/project/dorumdorum/global/config/S3Config.java @@ -0,0 +1,34 @@ +package com.project.dorumdorum.global.config; + +import com.project.dorumdorum.global.properties.S3Properties; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; + +@Configuration +@RequiredArgsConstructor +public class S3Config { + + private final S3Properties s3Properties; + + @Bean + public S3Client s3Client() { + return S3Client.builder() + .region(Region.of(s3Properties.getRegion())) + .credentialsProvider(DefaultCredentialsProvider.create()) + .build(); + } + + @Bean + public S3Presigner s3Presigner() { + return S3Presigner.builder() + .region(Region.of(s3Properties.getRegion())) + .credentialsProvider(DefaultCredentialsProvider.create()) + .build(); + } +} + diff --git a/src/main/java/com/project/dorumdorum/global/properties/S3Properties.java b/src/main/java/com/project/dorumdorum/global/properties/S3Properties.java new file mode 100644 index 0000000..edd5333 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/global/properties/S3Properties.java @@ -0,0 +1,22 @@ +package com.project.dorumdorum.global.properties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Getter +@Setter +@ConfigurationProperties(prefix = "aws.s3") +public class S3Properties { + + /** + * 업로드/다운로드에 사용할 S3 버킷 이름 + */ + private String bucket; + + /** + * 버킷이 위치한 리전(e.g. ap-northeast-2) + */ + private String region; +} + From 1ed806819b92d6af9d24ef9e857f4fe40689b27a Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Sun, 28 Dec 2025 04:47:17 +0900 Subject: [PATCH 12/18] feat: add generating s3 presigned url --- .../notice/infra/S3PresignedUrlService.java | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/infra/S3PresignedUrlService.java diff --git a/src/main/java/com/project/dorumdorum/domain/notice/infra/S3PresignedUrlService.java b/src/main/java/com/project/dorumdorum/domain/notice/infra/S3PresignedUrlService.java new file mode 100644 index 0000000..28de4a8 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/infra/S3PresignedUrlService.java @@ -0,0 +1,118 @@ +package com.project.dorumdorum.domain.notice.infra; + +import com.project.dorumdorum.global.properties.S3Properties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; +import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; + +import java.time.Duration; +import java.util.UUID; + +@Slf4j +@Service +@RequiredArgsConstructor +public class S3PresignedUrlService { + + private final S3Presigner s3Presigner; + private final S3Client s3Client; + private final S3Properties s3Properties; + + // Presigned URL 유효 시간 + private static final Duration UPLOAD_URL_EXPIRATION = Duration.ofMinutes(5); + private static final Duration DOWNLOAD_URL_EXPIRATION = Duration.ofHours(1); + + // Notice 이미지 저장 경로 prefix + private static final String NOTICE_IMAGE_PREFIX = "notice/images/"; + + public UploadUrlInfo generateUploadPresignedUrl(Long roomNo, String fileName) { + String key = generateNoticeImageKey(roomNo, fileName); + + PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .bucket(s3Properties.getBucket()) + .key(key) + .build(); + + PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder() + .signatureDuration(UPLOAD_URL_EXPIRATION) + .putObjectRequest(putObjectRequest) + .build(); + + PresignedPutObjectRequest presignedRequest = s3Presigner.presignPutObject(presignRequest); + String url = presignedRequest.url().toString(); + + log.info("Generated upload presigned URL for roomNo: {}, key: {}", roomNo, key); + + return new UploadUrlInfo(url, key); + } + + public String generateDownloadPresignedUrl(String key) { + GetObjectRequest getObjectRequest = GetObjectRequest.builder() + .bucket(s3Properties.getBucket()) + .key(key) + .build(); + + GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder() + .signatureDuration(DOWNLOAD_URL_EXPIRATION) + .getObjectRequest(getObjectRequest) + .build(); + + PresignedGetObjectRequest presignedRequest = s3Presigner.presignGetObject(presignRequest); + String url = presignedRequest.url().toString(); + + log.info("Generated download presigned URL for key: {}", key); + + return url; + } + + + public void deleteObject(String key) { + if (key == null || key.isEmpty()) { + log.warn("S3 key is null or empty, skipping deletion"); + return; + } + + try { + DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder() + .bucket(s3Properties.getBucket()) + .key(key) + .build(); + + s3Client.deleteObject(deleteObjectRequest); + log.info("Successfully deleted S3 object with key: {}", key); + } catch (Exception e) { + log.error("Failed to delete S3 object with key: {}", key, e); + throw new RuntimeException("S3 객체 삭제에 실패했습니다.", e); + } + } + + private String generateNoticeImageKey(Long roomNo, String fileName) { + String uuid = UUID.randomUUID().toString(); + String sanitizedFileName = sanitizeFileName(fileName); + return String.format("%s%d/%s_%s", NOTICE_IMAGE_PREFIX, roomNo, uuid, sanitizedFileName); + } + + private String sanitizeFileName(String fileName) { + if (fileName == null || fileName.isEmpty()) { + return "image.jpg"; + } + // 특수문자를 언더스코어로 변경, 공백 제거 + return fileName.replaceAll("[^a-zA-Z0-9.\\-_]", "_") + .replaceAll("\\s+", "") + .toLowerCase(); + } + + public record UploadUrlInfo( + String uploadUrl, + String s3Key + ) {} +} + From 9f90f9194ab0a28b2b2c1e9a9c00e3a68d7e9a67 Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Sun, 28 Dec 2025 04:49:34 +0900 Subject: [PATCH 13/18] feat: add image entity, repository, service --- .../domain/image/domain/entity/Image.java | 32 ++++++++++++++ .../domain/repository/ImageRepository.java | 14 ++++++ .../image/domain/service/ImageService.java | 44 +++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 src/main/java/com/project/dorumdorum/domain/image/domain/entity/Image.java create mode 100644 src/main/java/com/project/dorumdorum/domain/image/domain/repository/ImageRepository.java create mode 100644 src/main/java/com/project/dorumdorum/domain/image/domain/service/ImageService.java diff --git a/src/main/java/com/project/dorumdorum/domain/image/domain/entity/Image.java b/src/main/java/com/project/dorumdorum/domain/image/domain/entity/Image.java new file mode 100644 index 0000000..e570c80 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/image/domain/entity/Image.java @@ -0,0 +1,32 @@ +package com.project.dorumdorum.domain.image.domain.entity; + +import com.project.dorumdorum.global.common.BaseEntity; +import io.hypersistence.utils.hibernate.id.Tsid; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Table(name = "image") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@Builder +public class Image extends BaseEntity { + + @Id + @Tsid + private Long imageNo; + + @Column(nullable = false) + private Long noticeNo; + + @Column(nullable = false) + private String s3Key; + + @Column(nullable = false) + private String fileName; + + @Column(nullable = false) + private Long fileSize; +} + diff --git a/src/main/java/com/project/dorumdorum/domain/image/domain/repository/ImageRepository.java b/src/main/java/com/project/dorumdorum/domain/image/domain/repository/ImageRepository.java new file mode 100644 index 0000000..54a95e2 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/image/domain/repository/ImageRepository.java @@ -0,0 +1,14 @@ +package com.project.dorumdorum.domain.image.domain.repository; + +import com.project.dorumdorum.domain.image.domain.entity.Image; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface ImageRepository extends JpaRepository { + + Optional findByNoticeNo(Long noticeNo); + + long deleteByNoticeNo(Long noticeNo); +} + diff --git a/src/main/java/com/project/dorumdorum/domain/image/domain/service/ImageService.java b/src/main/java/com/project/dorumdorum/domain/image/domain/service/ImageService.java new file mode 100644 index 0000000..fbce78a --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/image/domain/service/ImageService.java @@ -0,0 +1,44 @@ +package com.project.dorumdorum.domain.image.domain.service; + +import com.project.dorumdorum.domain.image.domain.entity.Image; +import com.project.dorumdorum.domain.image.domain.repository.ImageRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class ImageService { + + private final ImageRepository imageRepository; + + @Transactional + public Image saveNoticeImage(Long noticeNo, String s3Key, String fileName, Long fileSize) { + Image image = Image.builder() + .noticeNo(noticeNo) + .s3Key(s3Key) + .fileName(fileName) + .fileSize(fileSize) + .build(); + return imageRepository.save(image); + } + + @Transactional(readOnly = true) + public Optional findByNoticeNo(Long noticeNo) { + return imageRepository.findByNoticeNo(noticeNo); + } + + @Transactional + public void softDelete(Image image) { + image.delete(); + imageRepository.save(image); + } + + @Transactional + public void deleteByNoticeNo(Long noticeNo) { + imageRepository.deleteByNoticeNo(noticeNo); + } +} + From b997b81846f7479b142a8612bbfa82887e04d1f3 Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Sun, 28 Dec 2025 04:53:34 +0900 Subject: [PATCH 14/18] feat: add image to notice create, update, delete, load --- .../dto/request/UpdateNoticeRequest.java | 6 ++- .../dto/request/WriteNoticeRequest.java | 5 +- .../dto/response/NoticeResponse.java | 18 ++++++- .../usecase/DeleteNoticeUseCase.java | 19 +++++-- .../usecase/LoadNoticesUseCase.java | 23 +++++++- .../usecase/UpdateNoticeUseCase.java | 52 ++++++++++++++++++- .../usecase/WriteNoticeUseCase.java | 34 +++++++++++- .../domain/notice/service/NoticeService.java | 21 ++++---- 8 files changed, 155 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateNoticeRequest.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateNoticeRequest.java index dc5ef29..fa108cc 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateNoticeRequest.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateNoticeRequest.java @@ -2,11 +2,15 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import org.springframework.lang.Nullable; public record UpdateNoticeRequest( @NotNull Long roomNo, @NotNull Long noticeNo, @NotBlank String title, - @NotBlank String content + @NotBlank String content, + @Nullable String imageFileName, + @Nullable Long imageFileSize, + @Nullable Boolean deleteImage ) { } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java index 48399fd..aab4c3e 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/WriteNoticeRequest.java @@ -2,11 +2,14 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import org.springframework.lang.Nullable; public record WriteNoticeRequest( @NotNull Long roomNo, @NotBlank String title, - @NotBlank String content + @NotBlank String content, + @Nullable String imageFileName, + @Nullable Long imageFileSize ) { } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticeResponse.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticeResponse.java index 4054b95..e5a3289 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticeResponse.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticeResponse.java @@ -12,9 +12,21 @@ public record NoticeResponse( LocalDateTime createdAt, LocalDateTime updatedAt, String title, - String content + String content, + String imageUploadUrl, + String imageDownloadUrl, + String imageFileName, + Long imageFileSize ) { public static NoticeResponse create(Notice notice) { + return create(notice, null, null, null, null); + } + + public static NoticeResponse create(Notice notice, + String imageUploadUrl, + String imageDownloadUrl, + String imageFileName, + Long imageFileSize) { return NoticeResponse.builder() .noticeNo(notice.getNoticeNo()) .userNo(notice.getUserNo()) @@ -22,6 +34,10 @@ public static NoticeResponse create(Notice notice) { .updatedAt(notice.getUpdatedAt()) .title(notice.getTitle()) .content(notice.getContent()) + .imageUploadUrl(imageUploadUrl) + .imageDownloadUrl(imageDownloadUrl) + .imageFileName(imageFileName) + .imageFileSize(imageFileSize) .build(); } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteNoticeUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteNoticeUseCase.java index cc54680..bc16071 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteNoticeUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteNoticeUseCase.java @@ -1,12 +1,16 @@ package com.project.dorumdorum.domain.notice.application.usecase; +import com.project.dorumdorum.domain.image.domain.entity.Image; +import com.project.dorumdorum.domain.image.domain.service.ImageService; import com.project.dorumdorum.domain.notice.domain.entity.Notice; +import com.project.dorumdorum.domain.notice.infra.S3PresignedUrlService; import com.project.dorumdorum.domain.notice.service.NoticeService; import com.project.dorumdorum.domain.user.domain.service.UserService; import com.project.dorumdorum.global.exception.RestApiException; import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor @@ -14,17 +18,26 @@ public class DeleteNoticeUseCase { private final UserService userService; private final NoticeService noticeService; + private final ImageService imageService; + private final S3PresignedUrlService s3PresignedUrlService; + @Transactional public void execute(Long userNo, Long noticeNo) { userService.validateExistsById(userNo); Notice notice = noticeService.findById(noticeNo); - if(notice.isDeleted()) + if (notice.isDeleted()) throw new RestApiException(GlobalErrorStatus.NOTICE_ALREADY_DELETED); - if(!notice.isWriter(userNo)) + if (!notice.isWriter(userNo)) throw new RestApiException(GlobalErrorStatus.NO_PERMISSION_ON_NOTICE); - noticeService.deleteNotice(noticeNo); + Image image = imageService.findByNoticeNo(noticeNo).orElse(null); + if (image != null) { + s3PresignedUrlService.deleteObject(image.getS3Key()); // 버킷에는 하드 삭제 + imageService.softDelete(image); // image는 소프트 삭제 + } + + noticeService.softDelete(notice); // notice 소프트 삭제 } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java index 96fe1df..d69eed4 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java @@ -1,7 +1,11 @@ package com.project.dorumdorum.domain.notice.application.usecase; +import com.project.dorumdorum.domain.image.domain.entity.Image; +import com.project.dorumdorum.domain.image.domain.service.ImageService; import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.application.dto.response.NoticesResponse; +import com.project.dorumdorum.domain.notice.domain.entity.Notice; +import com.project.dorumdorum.domain.notice.infra.S3PresignedUrlService; import com.project.dorumdorum.domain.notice.service.NoticeService; import com.project.dorumdorum.domain.room.domain.entity.Room; import com.project.dorumdorum.domain.room.domain.service.RoomService; @@ -9,9 +13,9 @@ import com.project.dorumdorum.domain.user.domain.service.UserService; import com.project.dorumdorum.global.exception.RestApiException; import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; - import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; + import java.util.List; @Service @@ -22,6 +26,8 @@ public class LoadNoticesUseCase { private final RoommateService roommateService; private final RoomService roomService; private final NoticeService noticeService; + private final ImageService imageService; + private final S3PresignedUrlService s3PresignedUrlService; public List loadNotices(Long userNo, Long roomNo) { userService.validateExistsById(userNo); @@ -36,6 +42,19 @@ public List loadNotices(Long userNo, Long roomNo) { public NoticeResponse loadNotice(Long userNo, Long noticeNo) { userService.validateExistsById(userNo); - return noticeService.loadNotice(noticeNo); + Notice notice = noticeService.findById(noticeNo); + Image image = imageService.findByNoticeNo(noticeNo).orElse(null); + + String downloadUrl = null; + String fileName = null; + Long fileSize = null; + + if (image != null) { + downloadUrl = s3PresignedUrlService.generateDownloadPresignedUrl(image.getS3Key()); + fileName = image.getFileName(); + fileSize = image.getFileSize(); + } + + return NoticeResponse.create(notice, null, downloadUrl, fileName, fileSize); } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java index 314d582..a39b707 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java @@ -1,15 +1,17 @@ package com.project.dorumdorum.domain.notice.application.usecase; +import com.project.dorumdorum.domain.image.domain.entity.Image; +import com.project.dorumdorum.domain.image.domain.service.ImageService; import com.project.dorumdorum.domain.notice.application.dto.request.UpdateNoticeRequest; import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.domain.entity.Notice; +import com.project.dorumdorum.domain.notice.infra.S3PresignedUrlService; import com.project.dorumdorum.domain.notice.service.NoticeService; import com.project.dorumdorum.domain.room.domain.entity.Room; import com.project.dorumdorum.domain.room.domain.service.RoomService; import com.project.dorumdorum.domain.user.domain.service.UserService; import com.project.dorumdorum.global.exception.RestApiException; import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; - import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -21,6 +23,8 @@ public class UpdateNoticeUseCase { private final UserService userService; private final RoomService roomService; private final NoticeService noticeService; + private final ImageService imageService; + private final S3PresignedUrlService s3PresignedUrlService; @Transactional public NoticeResponse execute(Long userNo, UpdateNoticeRequest request) { @@ -37,6 +41,50 @@ public NoticeResponse execute(Long userNo, UpdateNoticeRequest request) { if(!notice.isWriter(userNo)) throw new RestApiException(GlobalErrorStatus.NO_PERMISSION_ON_NOTICE); - return noticeService.updateNotice(notice, request); + Image existingImage = imageService.findByNoticeNo(notice.getNoticeNo()).orElse(null); + + boolean deleteImage = Boolean.TRUE.equals(request.deleteImage()); + boolean hasNewImage = hasImageInfo(request); + + if (deleteImage || hasNewImage) { + if (existingImage != null) { + s3PresignedUrlService.deleteObject(existingImage.getS3Key()); + imageService.deleteByNoticeNo(notice.getNoticeNo()); + existingImage = null; + } + } + + String imageUploadUrl = null; + String imageDownloadUrl = null; + String imageFileName = null; + Long imageFileSize = null; + + if (hasNewImage) { + if (request.imageFileSize() == null) + throw new RestApiException(GlobalErrorStatus._BAD_REQUEST); + + imageFileName = request.imageFileName(); + imageFileSize = request.imageFileSize(); + + S3PresignedUrlService.UploadUrlInfo uploadInfo = + s3PresignedUrlService.generateUploadPresignedUrl(request.roomNo(), imageFileName); + + imageService.saveNoticeImage(notice.getNoticeNo(), uploadInfo.s3Key(), imageFileName, imageFileSize); + + imageUploadUrl = uploadInfo.uploadUrl(); + imageDownloadUrl = s3PresignedUrlService.generateDownloadPresignedUrl(uploadInfo.s3Key()); + } else if (!deleteImage && existingImage != null) { + imageFileName = existingImage.getFileName(); + imageFileSize = existingImage.getFileSize(); + imageDownloadUrl = s3PresignedUrlService.generateDownloadPresignedUrl(existingImage.getS3Key()); + } + + Notice updatedNotice = noticeService.updateNotice(notice, request); + + return NoticeResponse.create(updatedNotice, imageUploadUrl, imageDownloadUrl, imageFileName, imageFileSize); + } + + private boolean hasImageInfo(UpdateNoticeRequest request) { + return request.imageFileName() != null && !request.imageFileName().isBlank(); } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java index 48f3676..0750c6a 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java @@ -1,14 +1,16 @@ package com.project.dorumdorum.domain.notice.application.usecase; +import com.project.dorumdorum.domain.image.domain.service.ImageService; import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; +import com.project.dorumdorum.domain.notice.domain.entity.Notice; +import com.project.dorumdorum.domain.notice.infra.S3PresignedUrlService; import com.project.dorumdorum.domain.notice.service.NoticeService; import com.project.dorumdorum.domain.room.domain.entity.Room; import com.project.dorumdorum.domain.room.domain.service.RoomService; import com.project.dorumdorum.domain.user.domain.service.UserService; import com.project.dorumdorum.global.exception.RestApiException; import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; - import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -20,6 +22,8 @@ public class WriteNoticeUseCase { private final UserService userService; private final NoticeService noticeService; private final RoomService roomService; + private final ImageService imageService; + private final S3PresignedUrlService s3PresignedUrlService; @Transactional public NoticeResponse execute(Long userNo, WriteNoticeRequest request) { @@ -34,8 +38,34 @@ public NoticeResponse execute(Long userNo, WriteNoticeRequest request) { if(!room.isHost(userNo)) throw new RestApiException(GlobalErrorStatus.NO_PERMISSION_ON_NOTICE); + Notice notice = noticeService.writeNotice(userNo, room.getRoomNo(), request); + + String imageUploadUrl = null; + String imageDownloadUrl = null; + String imageFileName = null; + Long imageFileSize = null; + + if (hasImageInfo(request)) { + if (request.imageFileSize() == null) + throw new RestApiException(GlobalErrorStatus._BAD_REQUEST); + + imageFileName = request.imageFileName(); + imageFileSize = request.imageFileSize(); + + S3PresignedUrlService.UploadUrlInfo uploadInfo = + s3PresignedUrlService.generateUploadPresignedUrl(room.getRoomNo(), imageFileName); - return NoticeResponse.create(noticeService.writeNotice(userNo, room.getRoomNo(), request)); + imageService.saveNoticeImage(notice.getNoticeNo(), uploadInfo.s3Key(), imageFileName, imageFileSize); + + imageUploadUrl = uploadInfo.uploadUrl(); + imageDownloadUrl = s3PresignedUrlService.generateDownloadPresignedUrl(uploadInfo.s3Key()); + } + + return NoticeResponse.create(notice, imageUploadUrl, imageDownloadUrl, imageFileName, imageFileSize); + + } + private boolean hasImageInfo(WriteNoticeRequest request) { + return request.imageFileName() != null && !request.imageFileName().isBlank(); } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java index 39f446a..5c5af51 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/service/NoticeService.java @@ -2,16 +2,15 @@ import com.project.dorumdorum.domain.notice.application.dto.request.UpdateNoticeRequest; import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; -import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.application.dto.response.NoticesResponse; import com.project.dorumdorum.domain.notice.domain.entity.Notice; import com.project.dorumdorum.domain.notice.domain.repository.NoticeRepository; - import com.project.dorumdorum.global.exception.RestApiException; import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; + import java.util.List; @Service @@ -33,12 +32,6 @@ public Notice writeNotice(Long userNo, Long roomNo, WriteNoticeRequest request) } - public NoticeResponse loadNotice(Long noticeNo) { - Notice notice = noticeRepository.findById(noticeNo) - .orElseThrow(() -> new RestApiException(GlobalErrorStatus.NOTICE_NOT_FOUND)); - return NoticeResponse.create(notice); - } - public List loadNoticeList(Long roomNo) { List notices = noticeRepository.findByRoomNo(roomNo); @@ -52,12 +45,18 @@ public Notice findById(@NotNull Long noticeNo) { .orElseThrow(() -> new RestApiException(GlobalErrorStatus.NOTICE_NOT_FOUND)); } - public NoticeResponse updateNotice(Notice notice, UpdateNoticeRequest request) { + public Notice updateNotice(Notice notice, UpdateNoticeRequest request) { notice.update(request.title(), request.content()); - return NoticeResponse.create(notice); + return notice; + } + + public void softDelete(Notice notice) { + notice.delete(); + noticeRepository.save(notice); } public void deleteNotice(Long noticeNo) { - noticeRepository.deleteById(noticeNo); + Notice notice = findById(noticeNo); + softDelete(notice); } } From 752077fdc337e20b26b5db686702cb7e9cc7ad6c Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Sun, 28 Dec 2025 04:54:12 +0900 Subject: [PATCH 15/18] docs: add api spec --- .../notice/ui/DeleteNoticeController.java | 6 +-- .../notice/ui/LoadNoticesController.java | 10 ++--- .../notice/ui/UpdateNoticeController.java | 7 ++-- .../notice/ui/WriteCommentController.java | 6 +-- .../notice/ui/WriteNoticeController.java | 7 ++-- .../notice/ui/spec/DeleteNoticeApiSpec.java | 25 +++++++++++ .../notice/ui/spec/LoadNoticesApiSpec.java | 41 +++++++++++++++++++ .../notice/ui/spec/UpdateNoticeApiSpec.java | 40 ++++++++++++++++++ .../notice/ui/spec/WriteCommentApiSpec.java | 34 +++++++++++++++ .../notice/ui/spec/WriteNoticeApiSpec.java | 37 +++++++++++++++++ 10 files changed, 194 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/spec/DeleteNoticeApiSpec.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/spec/LoadNoticesApiSpec.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/spec/UpdateNoticeApiSpec.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/spec/WriteCommentApiSpec.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/spec/WriteNoticeApiSpec.java diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteNoticeController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteNoticeController.java index 4a76bc5..c6f3683 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteNoticeController.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteNoticeController.java @@ -2,20 +2,20 @@ import com.project.dorumdorum.domain.notice.application.usecase.DeleteNoticeUseCase; +import com.project.dorumdorum.domain.notice.ui.spec.DeleteNoticeApiSpec; import com.project.dorumdorum.global.annotation.CurrentUser; import com.project.dorumdorum.global.common.BaseResponse; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor -public class DeleteNoticeController { +public class DeleteNoticeController implements DeleteNoticeApiSpec { private final DeleteNoticeUseCase deleteNoticeUseCase; - @DeleteMapping("/api/notice") + @Override public BaseResponse deleteNotice( @CurrentUser Long userNo, @RequestParam Long noticeNo diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticesController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticesController.java index b699cf2..b2152a4 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticesController.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/LoadNoticesController.java @@ -4,22 +4,22 @@ import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.application.dto.response.NoticesResponse; import com.project.dorumdorum.domain.notice.application.usecase.LoadNoticesUseCase; +import com.project.dorumdorum.domain.notice.ui.spec.LoadNoticesApiSpec; import com.project.dorumdorum.global.annotation.CurrentUser; import com.project.dorumdorum.global.common.BaseResponse; - import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; + import java.util.List; @RestController @RequiredArgsConstructor -public class LoadNoticesController { +public class LoadNoticesController implements LoadNoticesApiSpec { private final LoadNoticesUseCase loadNoticesUseCase; - @GetMapping("/api/notices") + @Override public BaseResponse> loadNotices( @CurrentUser Long userNo, @RequestParam Long roomNo @@ -27,7 +27,7 @@ public BaseResponse> loadNotices( return BaseResponse.onSuccess(loadNoticesUseCase.loadNotices(userNo, roomNo)); } - @GetMapping("/api/notice") + @Override public BaseResponse loadNotice( @CurrentUser Long userNo, @RequestParam Long noticeNo diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateNoticeController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateNoticeController.java index b83a787..8359834 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateNoticeController.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateNoticeController.java @@ -3,21 +3,20 @@ import com.project.dorumdorum.domain.notice.application.dto.request.UpdateNoticeRequest; import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.application.usecase.UpdateNoticeUseCase; +import com.project.dorumdorum.domain.notice.ui.spec.UpdateNoticeApiSpec; import com.project.dorumdorum.global.annotation.CurrentUser; import com.project.dorumdorum.global.common.BaseResponse; - import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor -public class UpdateNoticeController { +public class UpdateNoticeController implements UpdateNoticeApiSpec { private final UpdateNoticeUseCase updateNoticeUseCase; - @PatchMapping("/api/notice") + @Override public BaseResponse updateNotice( @CurrentUser Long userNo, @RequestBody UpdateNoticeRequest request diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteCommentController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteCommentController.java index 4c87f3e..ec30a8c 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteCommentController.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteCommentController.java @@ -4,22 +4,22 @@ import com.project.dorumdorum.domain.notice.application.dto.request.WriteCommentRequest; import com.project.dorumdorum.domain.notice.application.dto.response.CommentResponse; import com.project.dorumdorum.domain.notice.application.usecase.WriteCommentUseCase; +import com.project.dorumdorum.domain.notice.ui.spec.WriteCommentApiSpec; import com.project.dorumdorum.global.annotation.CurrentUser; import com.project.dorumdorum.global.common.BaseResponse; import jakarta.validation.Valid; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor(access = AccessLevel.PROTECTED) -public class WriteCommentController { +public class WriteCommentController implements WriteCommentApiSpec { private final WriteCommentUseCase writeCommentUseCase; - @PostMapping("/api/comment") + @Override public BaseResponse writeComment( @CurrentUser Long userNo, @Valid @RequestBody WriteCommentRequest request diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java index 5821a8f..8a07c19 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/WriteNoticeController.java @@ -3,22 +3,21 @@ import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.application.usecase.WriteNoticeUseCase; +import com.project.dorumdorum.domain.notice.ui.spec.WriteNoticeApiSpec; import com.project.dorumdorum.global.annotation.CurrentUser; import com.project.dorumdorum.global.common.BaseResponse; - import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor -public class WriteNoticeController { +public class WriteNoticeController implements WriteNoticeApiSpec { private final WriteNoticeUseCase writeNoticeUseCase; - @PostMapping("/api/notice") + @Override public BaseResponse writeNotice( @CurrentUser Long userNo, @RequestBody @Valid WriteNoticeRequest request diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/DeleteNoticeApiSpec.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/DeleteNoticeApiSpec.java new file mode 100644 index 0000000..45a103e --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/DeleteNoticeApiSpec.java @@ -0,0 +1,25 @@ +package com.project.dorumdorum.domain.notice.ui.spec; + +import com.project.dorumdorum.global.common.BaseResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@Tag(name = "Notice") +public interface DeleteNoticeApiSpec { + + @Operation( + summary = "공지 삭제", + description = "공지 소프트 삭제 + 연결 이미지 소프트 삭제, S3 오브젝트는 즉시 삭제" + ) + @DeleteMapping("/api/notice") + BaseResponse deleteNotice( + @Parameter(hidden = true) + Long userNo, + @Parameter(description = "공지 번호", required = true) + @RequestParam Long noticeNo + ); +} + diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/LoadNoticesApiSpec.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/LoadNoticesApiSpec.java new file mode 100644 index 0000000..86b317b --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/LoadNoticesApiSpec.java @@ -0,0 +1,41 @@ +package com.project.dorumdorum.domain.notice.ui.spec; + +import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticesResponse; +import com.project.dorumdorum.global.common.BaseResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +@Tag(name = "Notice") +public interface LoadNoticesApiSpec { + + @Operation( + summary = "공지 목록 조회", + description = "방 단위 공지 리스트 조회" + ) + @GetMapping("/api/notices") + BaseResponse> loadNotices( + @Parameter(hidden = true) + Long userNo, + @Parameter(description = "방 번호", required = true) + @RequestParam Long roomNo + ); + + @Operation( + summary = "공지 상세 조회", + description = "공지 상세 + 이미지 조회 presigned URL 반환" + ) + @GetMapping("/api/notice") + BaseResponse loadNotice( + @Parameter(hidden = true) + Long userNo, + @Parameter(description = "공지 번호", required = true) + @RequestParam Long noticeNo + ); +} + diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/UpdateNoticeApiSpec.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/UpdateNoticeApiSpec.java new file mode 100644 index 0000000..6453e2f --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/UpdateNoticeApiSpec.java @@ -0,0 +1,40 @@ +package com.project.dorumdorum.domain.notice.ui.spec; + +import com.project.dorumdorum.domain.notice.application.dto.request.UpdateNoticeRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; +import com.project.dorumdorum.global.common.BaseResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.PatchMapping; + +@Tag(name = "Notice") +public interface UpdateNoticeApiSpec { + + @Operation( + summary = "공지 수정", + description = """ + 공지 내용 수정 및 이미지 교체/삭제 + - 새 이미지 업로드: imageFileName + imageFileSize 제공 → 새 presigned URL 발급 + - 기존 이미지 삭제: deleteImage=true + """ + ) + @PatchMapping("/api/notice") + BaseResponse updateNotice( + @Parameter(hidden = true) + Long userNo, + @RequestBody( + description = """ + 공지 수정 요청 + - roomNo, noticeNo: 식별자 + - title, content: 수정 내용 + - imageFileName/imageFileSize: 새 이미지 교체 시 + - deleteImage: 기존 이미지만 삭제 시 true + """, + required = true + ) + UpdateNoticeRequest request + ); +} + diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/WriteCommentApiSpec.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/WriteCommentApiSpec.java new file mode 100644 index 0000000..fe5a1c4 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/WriteCommentApiSpec.java @@ -0,0 +1,34 @@ +package com.project.dorumdorum.domain.notice.ui.spec; + +import com.project.dorumdorum.domain.notice.application.dto.request.WriteCommentRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.CommentResponse; +import com.project.dorumdorum.global.common.BaseResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.PostMapping; + +@Tag(name = "Notice Comment") +public interface WriteCommentApiSpec { + + @Operation( + summary = "댓글 작성", + description = "공지에 대한 댓글 등록" + ) + @PostMapping("/api/comment") + BaseResponse writeComment( + @Parameter(hidden = true) + Long userNo, + @RequestBody( + description = """ + 댓글 작성 요청 + - noticeNo: 공지 번호 + - content: 댓글 내용 + """, + required = true + ) + WriteCommentRequest request + ); +} + diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/WriteNoticeApiSpec.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/WriteNoticeApiSpec.java new file mode 100644 index 0000000..29c5fd0 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/WriteNoticeApiSpec.java @@ -0,0 +1,37 @@ +package com.project.dorumdorum.domain.notice.ui.spec; + +import com.project.dorumdorum.domain.notice.application.dto.request.WriteNoticeRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; +import com.project.dorumdorum.global.common.BaseResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.PostMapping; + +@Tag(name = "Notice") +public interface WriteNoticeApiSpec { + + @Operation( + summary = "공지 작성", + description = "공지 생성 및 이미지 업로드 presigned URL 발급 (이미지 1개). " + + "요청에 imageFileName, imageFileSize를 주면 업로드 URL이 응답에 포함됩니다." + ) + @PostMapping("/api/notice") + BaseResponse writeNotice( + @Parameter(hidden = true) + Long userNo, + @RequestBody( + description = """ + 공지 작성 요청 + - roomNo: 방 번호 + - title, content: 제목/내용 + - imageFileName: 업로드할 이미지 파일명 + - imageFileSize: 업로드할 이미지 크기(byte) + """, + required = true + ) + WriteNoticeRequest request + ); +} + From 3999856e5d6d261602ae7f8e257c02f6b355fb90 Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Sun, 28 Dec 2025 05:16:34 +0900 Subject: [PATCH 16/18] feat: add load comment --- .../application/dto/response/CommentResponse.java | 4 ++-- .../application/dto/response/NoticeResponse.java | 14 ++++++++++++-- .../application/usecase/LoadNoticesUseCase.java | 8 +++++++- .../domain/notice/ui/spec/LoadNoticesApiSpec.java | 2 +- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/CommentResponse.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/CommentResponse.java index 195b665..cdbb349 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/CommentResponse.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/CommentResponse.java @@ -7,14 +7,14 @@ @Builder public record CommentResponse( - Long noticeNo, + Long commentNo, Long userNo, LocalDateTime updated_at, String content ) { public static CommentResponse create(Comment comment) { return CommentResponse.builder() - .noticeNo(comment.getNoticeNo()) + .commentNo(comment.getCommentNo()) .userNo(comment.getUserNo()) .updated_at(comment.getUpdatedAt()) .content(comment.getContent()) diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticeResponse.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticeResponse.java index e5a3289..b0a344f 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticeResponse.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/response/NoticeResponse.java @@ -3,7 +3,10 @@ import com.project.dorumdorum.domain.notice.domain.entity.Notice; import lombok.Builder; + import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; @Builder public record NoticeResponse( @@ -16,13 +19,19 @@ public record NoticeResponse( String imageUploadUrl, String imageDownloadUrl, String imageFileName, - Long imageFileSize + Long imageFileSize, + List comments ) { public static NoticeResponse create(Notice notice) { - return create(notice, null, null, null, null); + return create(notice, Collections.emptyList(), null, null, null, null); + } + + public static NoticeResponse create(Notice notice, List comments) { + return create(notice, comments, null, null, null, null); } public static NoticeResponse create(Notice notice, + List comments, String imageUploadUrl, String imageDownloadUrl, String imageFileName, @@ -38,6 +47,7 @@ public static NoticeResponse create(Notice notice, .imageDownloadUrl(imageDownloadUrl) .imageFileName(imageFileName) .imageFileSize(imageFileSize) + .comments(comments == null ? Collections.emptyList() : comments) .build(); } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java index d69eed4..be55fca 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/LoadNoticesUseCase.java @@ -4,9 +4,11 @@ import com.project.dorumdorum.domain.image.domain.service.ImageService; import com.project.dorumdorum.domain.notice.application.dto.response.NoticeResponse; import com.project.dorumdorum.domain.notice.application.dto.response.NoticesResponse; +import com.project.dorumdorum.domain.notice.application.dto.response.CommentResponse; import com.project.dorumdorum.domain.notice.domain.entity.Notice; import com.project.dorumdorum.domain.notice.infra.S3PresignedUrlService; import com.project.dorumdorum.domain.notice.service.NoticeService; +import com.project.dorumdorum.domain.notice.service.CommentService; import com.project.dorumdorum.domain.room.domain.entity.Room; import com.project.dorumdorum.domain.room.domain.service.RoomService; import com.project.dorumdorum.domain.room.domain.service.RoommateService; @@ -28,6 +30,7 @@ public class LoadNoticesUseCase { private final NoticeService noticeService; private final ImageService imageService; private final S3PresignedUrlService s3PresignedUrlService; + private final CommentService commentService; public List loadNotices(Long userNo, Long roomNo) { userService.validateExistsById(userNo); @@ -44,6 +47,9 @@ public NoticeResponse loadNotice(Long userNo, Long noticeNo) { Notice notice = noticeService.findById(noticeNo); Image image = imageService.findByNoticeNo(noticeNo).orElse(null); + List comments = commentService.findByNoticeNo(noticeNo).stream() + .map(CommentResponse::create) + .toList(); String downloadUrl = null; String fileName = null; @@ -55,6 +61,6 @@ public NoticeResponse loadNotice(Long userNo, Long noticeNo) { fileSize = image.getFileSize(); } - return NoticeResponse.create(notice, null, downloadUrl, fileName, fileSize); + return NoticeResponse.create(notice, comments, null, downloadUrl, fileName, fileSize); } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/LoadNoticesApiSpec.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/LoadNoticesApiSpec.java index 86b317b..6a5e2a2 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/LoadNoticesApiSpec.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/LoadNoticesApiSpec.java @@ -28,7 +28,7 @@ BaseResponse> loadNotices( @Operation( summary = "공지 상세 조회", - description = "공지 상세 + 이미지 조회 presigned URL 반환" + description = "공지 상세 + 댓글 목록 + 이미지 조회 presigned URL 반환" ) @GetMapping("/api/notice") BaseResponse loadNotice( From 709336c58cd7e83479ac9f3ee38e77c603ceb64d Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Sun, 28 Dec 2025 05:17:09 +0900 Subject: [PATCH 17/18] feat: develop update comment api --- .../dto/request/UpdateCommentRequest.java | 8 ++++ .../usecase/UpdateCommentUseCase.java | 37 +++++++++++++++++++ .../usecase/UpdateNoticeUseCase.java | 4 +- .../usecase/WriteNoticeUseCase.java | 4 +- .../domain/notice/domain/entity/Comment.java | 6 +++ .../domain/repository/CommentRepository.java | 4 +- .../domain/notice/service/CommentService.java | 28 ++++++++++++++ .../notice/ui/UpdateCommentController.java | 29 +++++++++++++++ .../notice/ui/spec/UpdateCommentApiSpec.java | 34 +++++++++++++++++ 9 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateCommentRequest.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateCommentUseCase.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateCommentController.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/spec/UpdateCommentApiSpec.java diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateCommentRequest.java b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateCommentRequest.java new file mode 100644 index 0000000..12f84cc --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/dto/request/UpdateCommentRequest.java @@ -0,0 +1,8 @@ +package com.project.dorumdorum.domain.notice.application.dto.request; + +public record UpdateCommentRequest( + Long commentNo, + String content +) { +} + diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateCommentUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateCommentUseCase.java new file mode 100644 index 0000000..3d47b3e --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateCommentUseCase.java @@ -0,0 +1,37 @@ +package com.project.dorumdorum.domain.notice.application.usecase; + +import com.project.dorumdorum.domain.notice.application.dto.request.UpdateCommentRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.CommentResponse; +import com.project.dorumdorum.domain.notice.domain.entity.Comment; +import com.project.dorumdorum.domain.notice.service.CommentService; +import com.project.dorumdorum.domain.user.domain.service.UserService; +import com.project.dorumdorum.global.exception.RestApiException; +import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class UpdateCommentUseCase { + + private final UserService userService; + private final CommentService commentService; + + @Transactional + public CommentResponse execute(Long userNo, UpdateCommentRequest request) { + userService.validateExistsById(userNo); + + Comment comment = commentService.findActiveById(request.commentNo()); + + if (!comment.isWriter(userNo)) + throw new RestApiException(GlobalErrorStatus.NO_PERMISSION_ON_NOTICE); + if (request.content() == null || request.content().isEmpty()) + throw new RestApiException(GlobalErrorStatus.CONTENT_IS_EMPTY); + + Comment updated = commentService.updateComment(comment, request.content()); + + return CommentResponse.create(updated); + } +} + diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java index a39b707..9032040 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/UpdateNoticeUseCase.java @@ -16,6 +16,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; + @Service @RequiredArgsConstructor public class UpdateNoticeUseCase { @@ -81,7 +83,7 @@ public NoticeResponse execute(Long userNo, UpdateNoticeRequest request) { Notice updatedNotice = noticeService.updateNotice(notice, request); - return NoticeResponse.create(updatedNotice, imageUploadUrl, imageDownloadUrl, imageFileName, imageFileSize); + return NoticeResponse.create(updatedNotice, Collections.emptyList(), imageUploadUrl, imageDownloadUrl, imageFileName, imageFileSize); } private boolean hasImageInfo(UpdateNoticeRequest request) { diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java index 0750c6a..b0a5465 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/WriteNoticeUseCase.java @@ -15,6 +15,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; + @Service @RequiredArgsConstructor public class WriteNoticeUseCase { @@ -61,7 +63,7 @@ public NoticeResponse execute(Long userNo, WriteNoticeRequest request) { imageDownloadUrl = s3PresignedUrlService.generateDownloadPresignedUrl(uploadInfo.s3Key()); } - return NoticeResponse.create(notice, imageUploadUrl, imageDownloadUrl, imageFileName, imageFileSize); + return NoticeResponse.create(notice, Collections.emptyList(), imageUploadUrl, imageDownloadUrl, imageFileName, imageFileSize); } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Comment.java b/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Comment.java index 0a09cf4..a054695 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Comment.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/domain/entity/Comment.java @@ -26,5 +26,11 @@ public class Comment extends BaseEntity { @Column(nullable = false) private String content; + public boolean isWriter(Long userNo) { + return this.userNo.equals(userNo); + } + public void updateContent(String content) { + this.content = content; + } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/CommentRepository.java b/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/CommentRepository.java index 6f01525..5f76550 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/CommentRepository.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/domain/repository/CommentRepository.java @@ -3,6 +3,8 @@ import com.project.dorumdorum.domain.notice.domain.entity.Comment; import org.springframework.data.jpa.repository.JpaRepository; -public interface CommentRepository extends JpaRepository { +import java.util.List; +public interface CommentRepository extends JpaRepository { + List findByNoticeNoAndDeletedAtIsNull(Long noticeNo); } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/service/CommentService.java b/src/main/java/com/project/dorumdorum/domain/notice/service/CommentService.java index 1aea13b..d7e4a2f 100644 --- a/src/main/java/com/project/dorumdorum/domain/notice/service/CommentService.java +++ b/src/main/java/com/project/dorumdorum/domain/notice/service/CommentService.java @@ -2,9 +2,13 @@ import com.project.dorumdorum.domain.notice.domain.entity.Comment; import com.project.dorumdorum.domain.notice.domain.repository.CommentRepository; +import com.project.dorumdorum.global.exception.RestApiException; +import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.List; + @Service @RequiredArgsConstructor public class CommentService { @@ -19,4 +23,28 @@ public Comment saveComment(Long userNo, Long noticeNo, String content) { .build(); return commentRepository.save(entity); } + + public List findByNoticeNo(Long noticeNo) { + return commentRepository.findByNoticeNoAndDeletedAtIsNull(noticeNo); + } + + public Comment findActiveById(Long commentNo) { + Comment comment = commentRepository.findById(commentNo) + .orElseThrow(() -> new RestApiException(GlobalErrorStatus._NOT_FOUND)); + + if (comment.isDeleted()) + throw new RestApiException(GlobalErrorStatus._BAD_REQUEST); + + return comment; + } + + public Comment updateComment(Comment comment, String content) { + comment.updateContent(content); + return comment; + } + + public void softDelete(Comment comment) { + comment.delete(); + commentRepository.save(comment); + } } diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateCommentController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateCommentController.java new file mode 100644 index 0000000..46f8914 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/UpdateCommentController.java @@ -0,0 +1,29 @@ +package com.project.dorumdorum.domain.notice.ui; + +import com.project.dorumdorum.domain.notice.application.dto.request.UpdateCommentRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.CommentResponse; +import com.project.dorumdorum.domain.notice.application.usecase.UpdateCommentUseCase; +import com.project.dorumdorum.domain.notice.ui.spec.UpdateCommentApiSpec; +import com.project.dorumdorum.global.annotation.CurrentUser; +import com.project.dorumdorum.global.common.BaseResponse; +import jakarta.validation.Valid; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +public class UpdateCommentController implements UpdateCommentApiSpec { + + private final UpdateCommentUseCase updateCommentUseCase; + + @Override + public BaseResponse updateComment( + @CurrentUser Long userNo, + @Valid @RequestBody UpdateCommentRequest request + ) { + return BaseResponse.onSuccess(updateCommentUseCase.execute(userNo, request)); + } +} + diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/UpdateCommentApiSpec.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/UpdateCommentApiSpec.java new file mode 100644 index 0000000..2e1ec73 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/UpdateCommentApiSpec.java @@ -0,0 +1,34 @@ +package com.project.dorumdorum.domain.notice.ui.spec; + +import com.project.dorumdorum.domain.notice.application.dto.request.UpdateCommentRequest; +import com.project.dorumdorum.domain.notice.application.dto.response.CommentResponse; +import com.project.dorumdorum.global.common.BaseResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.PatchMapping; + +@Tag(name = "Notice Comment") +public interface UpdateCommentApiSpec { + + @Operation( + summary = "댓글 수정", + description = "댓글 내용 수정" + ) + @PatchMapping("/api/comment") + BaseResponse updateComment( + @Parameter(hidden = true) + Long userNo, + @RequestBody( + description = """ + 댓글 수정 요청 + - commentNo: 댓글 번호 + - content: 수정 내용 + """, + required = true + ) + UpdateCommentRequest request + ); +} + From 845306821d809bb69d2d1b129ece6890ef8a3685 Mon Sep 17 00:00:00 2001 From: sbmax002 Date: Sun, 28 Dec 2025 05:17:24 +0900 Subject: [PATCH 18/18] feat: develop delete comment api --- .../usecase/DeleteCommentUseCase.java | 31 +++++++++++++++++++ .../notice/ui/DeleteCommentController.java | 27 ++++++++++++++++ .../notice/ui/spec/DeleteCommentApiSpec.java | 25 +++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteCommentUseCase.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteCommentController.java create mode 100644 src/main/java/com/project/dorumdorum/domain/notice/ui/spec/DeleteCommentApiSpec.java diff --git a/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteCommentUseCase.java b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteCommentUseCase.java new file mode 100644 index 0000000..85cc3aa --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/application/usecase/DeleteCommentUseCase.java @@ -0,0 +1,31 @@ +package com.project.dorumdorum.domain.notice.application.usecase; + +import com.project.dorumdorum.domain.notice.domain.entity.Comment; +import com.project.dorumdorum.domain.notice.service.CommentService; +import com.project.dorumdorum.domain.user.domain.service.UserService; +import com.project.dorumdorum.global.exception.RestApiException; +import com.project.dorumdorum.global.exception.code.status.GlobalErrorStatus; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class DeleteCommentUseCase { + + private final UserService userService; + private final CommentService commentService; + + @Transactional + public void execute(Long userNo, Long commentNo) { + userService.validateExistsById(userNo); + + Comment comment = commentService.findActiveById(commentNo); + + if (!comment.isWriter(userNo)) + throw new RestApiException(GlobalErrorStatus.NO_PERMISSION_ON_NOTICE); + + commentService.softDelete(comment); + } +} + diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteCommentController.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteCommentController.java new file mode 100644 index 0000000..b639f60 --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/DeleteCommentController.java @@ -0,0 +1,27 @@ +package com.project.dorumdorum.domain.notice.ui; + +import com.project.dorumdorum.domain.notice.application.usecase.DeleteCommentUseCase; +import com.project.dorumdorum.domain.notice.ui.spec.DeleteCommentApiSpec; +import com.project.dorumdorum.global.annotation.CurrentUser; +import com.project.dorumdorum.global.common.BaseResponse; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +public class DeleteCommentController implements DeleteCommentApiSpec { + + private final DeleteCommentUseCase deleteCommentUseCase; + + @Override + public BaseResponse deleteComment( + @CurrentUser Long userNo, + @RequestParam Long commentNo + ) { + deleteCommentUseCase.execute(userNo, commentNo); + return BaseResponse.onSuccess(); + } +} + diff --git a/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/DeleteCommentApiSpec.java b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/DeleteCommentApiSpec.java new file mode 100644 index 0000000..59dbdfc --- /dev/null +++ b/src/main/java/com/project/dorumdorum/domain/notice/ui/spec/DeleteCommentApiSpec.java @@ -0,0 +1,25 @@ +package com.project.dorumdorum.domain.notice.ui.spec; + +import com.project.dorumdorum.global.common.BaseResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@Tag(name = "Notice Comment") +public interface DeleteCommentApiSpec { + + @Operation( + summary = "댓글 삭제", + description = "댓글 소프트 삭제" + ) + @DeleteMapping("/api/comment") + BaseResponse deleteComment( + @Parameter(hidden = true) + Long userNo, + @Parameter(description = "댓글 번호", required = true) + @RequestParam Long commentNo + ); +} +