From 915ffbd4af62976fabc2ae09fa956fc529b58f49 Mon Sep 17 00:00:00 2001 From: Sojung Lee <90453158+xxoznge@users.noreply.github.com> Date: Sat, 10 Feb 2024 19:52:41 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20feat:=20=EB=B6=81=EB=A7=88?= =?UTF-8?q?=ED=81=AC=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EC=B6=94=EA=B0=80=20(#154)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../announcement/entity/Announcement.java | 5 ++++ .../controller/BookmarkController.java | 2 +- .../bookmark/dto/BookmarkGetResponse.java | 29 ++++++++++++++----- .../domain/bookmark/entity/Bookmark.java | 4 +-- .../repository/BookmarkRepository.java | 5 ++-- .../bookmark/service/BookmarkService.java | 13 ++++++--- 6 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/sponus/sponusbe/domain/announcement/entity/Announcement.java b/src/main/java/com/sponus/sponusbe/domain/announcement/entity/Announcement.java index c3c907fa..6252eba9 100644 --- a/src/main/java/com/sponus/sponusbe/domain/announcement/entity/Announcement.java +++ b/src/main/java/com/sponus/sponusbe/domain/announcement/entity/Announcement.java @@ -6,6 +6,7 @@ import com.sponus.sponusbe.domain.announcement.entity.enums.AnnouncementCategory; import com.sponus.sponusbe.domain.announcement.entity.enums.AnnouncementStatus; import com.sponus.sponusbe.domain.announcement.entity.enums.AnnouncementType; +import com.sponus.sponusbe.domain.bookmark.entity.Bookmark; import com.sponus.sponusbe.domain.organization.entity.Organization; import com.sponus.sponusbe.global.common.BaseEntity; @@ -72,6 +73,10 @@ public class Announcement extends BaseEntity { @OneToMany(mappedBy = "announcement", cascade = CascadeType.ALL, orphanRemoval = true) private List announcementImages = new ArrayList<>(); + @OneToMany(mappedBy = "announcement", cascade = CascadeType.ALL, orphanRemoval = true) + private List bookmarks = new ArrayList<>(); + + public void increaseViewCount() { this.viewCount++; } diff --git a/src/main/java/com/sponus/sponusbe/domain/bookmark/controller/BookmarkController.java b/src/main/java/com/sponus/sponusbe/domain/bookmark/controller/BookmarkController.java index d18ef0df..3d07f5df 100644 --- a/src/main/java/com/sponus/sponusbe/domain/bookmark/controller/BookmarkController.java +++ b/src/main/java/com/sponus/sponusbe/domain/bookmark/controller/BookmarkController.java @@ -37,7 +37,7 @@ public ApiResponse bookmarkToggle( } @GetMapping("/bookmarked") - public ApiResponse> getRecentBookmark( + public ApiResponse> getBookmark( @AuthOrganization Organization authOrganization, @RequestParam(name = "sort") BookmarkStatus sortStatus ) { diff --git a/src/main/java/com/sponus/sponusbe/domain/bookmark/dto/BookmarkGetResponse.java b/src/main/java/com/sponus/sponusbe/domain/bookmark/dto/BookmarkGetResponse.java index 64ac0a3e..e12425ab 100644 --- a/src/main/java/com/sponus/sponusbe/domain/bookmark/dto/BookmarkGetResponse.java +++ b/src/main/java/com/sponus/sponusbe/domain/bookmark/dto/BookmarkGetResponse.java @@ -2,25 +2,40 @@ import java.time.LocalDateTime; +import com.sponus.sponusbe.domain.announcement.dto.response.AnnouncementImageResponse; +import com.sponus.sponusbe.domain.announcement.entity.Announcement; +import com.sponus.sponusbe.domain.announcement.entity.AnnouncementImage; +import com.sponus.sponusbe.domain.announcement.entity.enums.AnnouncementCategory; +import com.sponus.sponusbe.domain.announcement.entity.enums.AnnouncementType; import com.sponus.sponusbe.domain.bookmark.entity.Bookmark; import lombok.Builder; -// TODO: 이미지 등 추가 @Builder public record BookmarkGetResponse( Long id, - Long announcementId, - String announcementTitle, + Long writerId, + String title, + AnnouncementType type, + AnnouncementCategory category, + AnnouncementImageResponse mainImage, LocalDateTime createdAt, Long saveCount ) { - public static BookmarkGetResponse from(Bookmark bookmark) { + public static BookmarkGetResponse from(Announcement announcement, Bookmark bookmark) { + AnnouncementImage mainImage = announcement.getAnnouncementImages() + .stream() + .findFirst() + .orElseThrow(); + return BookmarkGetResponse.builder() - .id(bookmark.getId()) - .announcementId(bookmark.getAnnouncement().getId()) - .announcementTitle(bookmark.getAnnouncement().getTitle()) + .id(announcement.getId()) + .writerId(announcement.getWriter().getId()) + .title(announcement.getTitle()) + .type(announcement.getType()) + .category(announcement.getCategory()) + .mainImage(AnnouncementImageResponse.from(mainImage)) .createdAt(bookmark.getCreatedAt()) .saveCount(bookmark.getSaveCount()) .build(); diff --git a/src/main/java/com/sponus/sponusbe/domain/bookmark/entity/Bookmark.java b/src/main/java/com/sponus/sponusbe/domain/bookmark/entity/Bookmark.java index 6324d136..8bb85e04 100644 --- a/src/main/java/com/sponus/sponusbe/domain/bookmark/entity/Bookmark.java +++ b/src/main/java/com/sponus/sponusbe/domain/bookmark/entity/Bookmark.java @@ -1,7 +1,5 @@ package com.sponus.sponusbe.domain.bookmark.entity; -import java.time.LocalDateTime; - import com.sponus.sponusbe.domain.announcement.entity.Announcement; import com.sponus.sponusbe.domain.organization.entity.Organization; import com.sponus.sponusbe.global.common.BaseEntity; @@ -44,7 +42,7 @@ public class Bookmark extends BaseEntity { @JoinColumn(name = "organization_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) private Organization organization; - @ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.REMOVE) + @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "announcement_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) private Announcement announcement; diff --git a/src/main/java/com/sponus/sponusbe/domain/bookmark/repository/BookmarkRepository.java b/src/main/java/com/sponus/sponusbe/domain/bookmark/repository/BookmarkRepository.java index 66de16ff..cb8398be 100644 --- a/src/main/java/com/sponus/sponusbe/domain/bookmark/repository/BookmarkRepository.java +++ b/src/main/java/com/sponus/sponusbe/domain/bookmark/repository/BookmarkRepository.java @@ -13,7 +13,8 @@ public interface BookmarkRepository extends JpaRepository { Optional findByOrganizationAndAnnouncement(Organization organization, Announcement announcement); - List findByOrganizationOrderByCreatedAtDesc(Organization organization); - List findByOrganizationOrderBySaveCountDesc(Organization organization); + List findByOrganizationOrderByCreatedAtDesc(Organization organization); + List findByOrganizationOrderBySaveCountDesc(Organization organization); + } diff --git a/src/main/java/com/sponus/sponusbe/domain/bookmark/service/BookmarkService.java b/src/main/java/com/sponus/sponusbe/domain/bookmark/service/BookmarkService.java index b169a9d1..3d2e46d2 100644 --- a/src/main/java/com/sponus/sponusbe/domain/bookmark/service/BookmarkService.java +++ b/src/main/java/com/sponus/sponusbe/domain/bookmark/service/BookmarkService.java @@ -3,6 +3,7 @@ import static com.sponus.sponusbe.domain.announcement.exception.AnnouncementErrorCode.*; import java.util.List; +import java.util.stream.Collectors; import org.springframework.stereotype.Service; @@ -47,12 +48,16 @@ public BookmarkToggleResponse bookmarkToggle(Organization organization, Bookmark } public List getRecentBookmark(Organization organization) { - return bookmarkRepository.findByOrganizationOrderByCreatedAtDesc(organization); + return bookmarkRepository.findByOrganizationOrderByCreatedAtDesc(organization) + .stream() + .map(bookmark -> BookmarkGetResponse.from(bookmark.getAnnouncement(), bookmark)) + .collect(Collectors.toList()); } public List getSavedBookmark(Organization organization) { - return bookmarkRepository.findByOrganizationOrderBySaveCountDesc(organization); + List bookmarks = bookmarkRepository.findByOrganizationOrderBySaveCountDesc(organization); + return bookmarks.stream() + .map(bookmark -> BookmarkGetResponse.from(bookmark.getAnnouncement(), bookmark)) + .collect(Collectors.toList()); } - - } From 869cfcc7b44a77c8f852a99243a208c22da30311 Mon Sep 17 00:00:00 2001 From: Kim Dae Hwi <121790935+marooo326@users.noreply.github.com> Date: Sat, 10 Feb 2024 22:09:11 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=90=9B=20fix:=20=EA=B3=B5=EA=B3=A0,?= =?UTF-8?q?=20=EC=A0=9C=EC=95=88=20=EA=B4=80=EB=A0=A8=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20API=20=EC=88=98=EC=A0=95=20(#155)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 공고 수정 시 이미지가 없는 경우 기존 이미지 그대로 유지 * fix: 보낸제안, 받은제안 엔드포인트 분리 * feat: 제안 데이터 반환 시 공고 요약, 세부정보 포함 * fix: 첨부파일 반환 정보 추가 * fix: 공고 수정 시 이미지가 반드시 존재해야하도록 변경 * feat: 제안 수정 시 이미지 업데이트 기능 추가 * fix: 제안한 그룹과 제안받는 그룹이 반대로 조회되는 문제 수정 * fix: 제안 이미지 주석처리, 업데이트 시 기존 사진 삭제 안되던 문제 수정 --- .../controller/AnnouncementController.java | 4 +-- .../request/AnnouncementCreateRequest.java | 9 +++-- .../response/AnnouncementSummaryResponse.java | 2 ++ .../announcement/entity/Announcement.java | 8 ----- .../service/AnnouncementService.java | 16 ++++++--- .../propose/controller/ProposeController.java | 30 ++++++++++------ .../dto/request/ProposeGetCondition.java | 20 ----------- .../dto/request/ProposeSearchType.java | 6 ---- .../response/ProposeAttachmentResponse.java | 20 +++++++++++ .../response/ProposeDetailGetResponse.java | 17 ++++----- .../response/ProposeSummaryGetResponse.java | 8 ++--- .../domain/propose/entity/Propose.java | 8 ++--- .../domain/propose/entity/ProposeImage.java | 14 ++++---- .../propose/service/ProposeQueryService.java | 36 ++++++++----------- .../propose/service/ProposeService.java | 25 +++++++++++-- 15 files changed, 120 insertions(+), 103 deletions(-) delete mode 100644 src/main/java/com/sponus/sponusbe/domain/propose/dto/request/ProposeGetCondition.java delete mode 100644 src/main/java/com/sponus/sponusbe/domain/propose/dto/request/ProposeSearchType.java create mode 100644 src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeAttachmentResponse.java diff --git a/src/main/java/com/sponus/sponusbe/domain/announcement/controller/AnnouncementController.java b/src/main/java/com/sponus/sponusbe/domain/announcement/controller/AnnouncementController.java index 6d5054d7..72438d9f 100644 --- a/src/main/java/com/sponus/sponusbe/domain/announcement/controller/AnnouncementController.java +++ b/src/main/java/com/sponus/sponusbe/domain/announcement/controller/AnnouncementController.java @@ -70,7 +70,7 @@ public ApiResponse> searchAnnouncement(@Reques @PostMapping(consumes = "multipart/form-data") public ApiResponse createAnnouncement( @AuthOrganization Organization authOrganization, - @RequestPart("request") @Valid AnnouncementCreateRequest request, + @Valid @RequestPart("request") AnnouncementCreateRequest request, @RequestPart(value = "images") List images ) { return ApiResponse.onSuccess( @@ -95,7 +95,7 @@ public ApiResponse updateAnnouncement( @AuthOrganization Organization authOrganization, @PathVariable Long announcementId, @RequestPart("request") @Valid AnnouncementUpdateRequest request, - @RequestPart(value = "images", required = false) List images + @RequestPart(value = "images") @Valid List images ) { return ApiResponse.onSuccess(announcementService.updateAnnouncement( authOrganization, diff --git a/src/main/java/com/sponus/sponusbe/domain/announcement/dto/request/AnnouncementCreateRequest.java b/src/main/java/com/sponus/sponusbe/domain/announcement/dto/request/AnnouncementCreateRequest.java index 87189c17..04198079 100644 --- a/src/main/java/com/sponus/sponusbe/domain/announcement/dto/request/AnnouncementCreateRequest.java +++ b/src/main/java/com/sponus/sponusbe/domain/announcement/dto/request/AnnouncementCreateRequest.java @@ -4,22 +4,21 @@ import com.sponus.sponusbe.domain.announcement.entity.Announcement; import com.sponus.sponusbe.domain.announcement.entity.enums.AnnouncementCategory; -import com.sponus.sponusbe.domain.announcement.entity.enums.AnnouncementStatus; import com.sponus.sponusbe.domain.announcement.entity.enums.AnnouncementType; import com.sponus.sponusbe.domain.organization.entity.Organization; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; public record AnnouncementCreateRequest( - @NotNull(message = "[ERROR] 타이틀 입력은 필수 입니다.") + @NotBlank(message = "[ERROR] 타이틀 입력은 필수 입니다.") String title, @NotNull(message = "[ERROR] 유형 입력은 필수 입니다.") AnnouncementType type, @NotNull(message = "[ERROR] 카테코리 입력은 필수 입니다.") AnnouncementCategory category, - @NotNull(message = "[ERROR] 내용 입력은 필수 입니다.") - String content, - AnnouncementStatus status + @NotBlank(message = "[ERROR] 내용 입력은 필수 입니다.") + String content ) { public Announcement toEntity(Organization writer) { diff --git a/src/main/java/com/sponus/sponusbe/domain/announcement/dto/response/AnnouncementSummaryResponse.java b/src/main/java/com/sponus/sponusbe/domain/announcement/dto/response/AnnouncementSummaryResponse.java index 6d5bcf6b..ce3a8518 100644 --- a/src/main/java/com/sponus/sponusbe/domain/announcement/dto/response/AnnouncementSummaryResponse.java +++ b/src/main/java/com/sponus/sponusbe/domain/announcement/dto/response/AnnouncementSummaryResponse.java @@ -12,6 +12,7 @@ public record AnnouncementSummaryResponse( Long id, Long writerId, + String writerName, String title, AnnouncementType type, AnnouncementCategory category, @@ -27,6 +28,7 @@ public static AnnouncementSummaryResponse from(Announcement announcement) { return AnnouncementSummaryResponse.builder() .id(announcement.getId()) .writerId(announcement.getWriter().getId()) + .writerName(announcement.getWriter().getName()) .title(announcement.getTitle()) .type(announcement.getType()) .category(announcement.getCategory()) diff --git a/src/main/java/com/sponus/sponusbe/domain/announcement/entity/Announcement.java b/src/main/java/com/sponus/sponusbe/domain/announcement/entity/Announcement.java index 6252eba9..ebd5cc76 100644 --- a/src/main/java/com/sponus/sponusbe/domain/announcement/entity/Announcement.java +++ b/src/main/java/com/sponus/sponusbe/domain/announcement/entity/Announcement.java @@ -76,7 +76,6 @@ public class Announcement extends BaseEntity { @OneToMany(mappedBy = "announcement", cascade = CascadeType.ALL, orphanRemoval = true) private List bookmarks = new ArrayList<>(); - public void increaseViewCount() { this.viewCount++; } @@ -90,13 +89,6 @@ public void updateInfo(String title, AnnouncementType type, AnnouncementCategory this.status = (status != null) ? status : this.status; } - public void updateImages(List images) { - if (images != null) { - this.announcementImages.clear(); - this.announcementImages.addAll(images); - } - } - public boolean isAvailable() { return this.status == AnnouncementStatus.OPENED; } diff --git a/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementService.java b/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementService.java index f06e6ca1..9b6607af 100644 --- a/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementService.java +++ b/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementService.java @@ -39,7 +39,7 @@ public AnnouncementCreateResponse createAnnouncement( List images ) { final Announcement announcement = request.toEntity(authOrganization); - setAnnouncementImages(images, announcement); + updateAnnouncementImages(announcement, images); return AnnouncementCreateResponse.from(announcementRepository.save(announcement)); } @@ -80,9 +80,14 @@ public AnnouncementUpdateResponse updateAnnouncement( if (!isOrganizationsAnnouncement(authOrganization.getId(), announcement)) throw new AnnouncementException(AnnouncementErrorCode.INVALID_ORGANIZATION); - announcement.updateInfo(request.title(), request.type(), request.category(), request.content(), - request.status()); - setAnnouncementImages(images, announcement); + announcement.updateInfo( + request.title(), + request.type(), + request.category(), + request.content(), + request.status() + ); + updateAnnouncementImages(announcement, images); announcementRepository.save(announcement); return AnnouncementUpdateResponse.from(announcement); } @@ -91,7 +96,8 @@ private boolean isOrganizationsAnnouncement(Long organizationId, Announcement an return announcement.getWriter().getId().equals(organizationId); } - private void setAnnouncementImages(List images, Announcement announcement) { + private void updateAnnouncementImages(Announcement announcement, List images) { + // 공고의 이미지는 반드시 존재해야함 announcement.getAnnouncementImages().clear(); images.forEach(image -> { final String url = s3Service.uploadFile(image); diff --git a/src/main/java/com/sponus/sponusbe/domain/propose/controller/ProposeController.java b/src/main/java/com/sponus/sponusbe/domain/propose/controller/ProposeController.java index 6e9bbe90..5759357d 100644 --- a/src/main/java/com/sponus/sponusbe/domain/propose/controller/ProposeController.java +++ b/src/main/java/com/sponus/sponusbe/domain/propose/controller/ProposeController.java @@ -4,12 +4,11 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @@ -17,7 +16,6 @@ import com.sponus.sponusbe.auth.annotation.AuthOrganization; import com.sponus.sponusbe.domain.organization.entity.Organization; import com.sponus.sponusbe.domain.propose.dto.request.ProposeCreateRequest; -import com.sponus.sponusbe.domain.propose.dto.request.ProposeGetCondition; import com.sponus.sponusbe.domain.propose.dto.request.ProposeUpdateRequest; import com.sponus.sponusbe.domain.propose.dto.response.ProposeCreateResponse; import com.sponus.sponusbe.domain.propose.dto.response.ProposeDetailGetResponse; @@ -54,12 +52,19 @@ public ApiResponse createPropose( ); } - @GetMapping("/me") - public ApiResponse> getMyProposes( + @GetMapping("/sent") + public ApiResponse> getSentProposes( + @AuthOrganization Organization authOrganization + ) { + return ApiResponse.onSuccess(proposeQueryService.getSentProposes(authOrganization)); + } + + @GetMapping("/received") + public ApiResponse> getReceivedProposes( @AuthOrganization Organization authOrganization, - @ModelAttribute @Valid ProposeGetCondition condition + @RequestParam Long announcementId ) { - return ApiResponse.onSuccess(proposeQueryService.getProposes(authOrganization, condition)); + return ApiResponse.onSuccess(proposeQueryService.getReceivedProposes(authOrganization, announcementId)); } @GetMapping("/{proposeId}") @@ -67,13 +72,18 @@ public ApiResponse getProposeDetail(@PathVariable Long return ApiResponse.onSuccess(proposeQueryService.getProposeDetail(proposeId)); } - @PatchMapping("/{proposeId}") + @PatchMapping(value = "/{proposeId}", consumes = "multipart/form-data") public ApiResponse updatePropose( @AuthOrganization Organization authOrganization, @PathVariable Long proposeId, - @RequestBody @Valid ProposeUpdateRequest request + @RequestPart @Valid ProposeUpdateRequest request, + @RequestPart(value = "attachments", required = false) List attachments ) { - proposeService.updatePropose(authOrganization, proposeId, request); + proposeService.updatePropose( + authOrganization, + proposeId, + request, + attachments == null ? List.of() : attachments); return ApiResponse.onSuccess(null); } diff --git a/src/main/java/com/sponus/sponusbe/domain/propose/dto/request/ProposeGetCondition.java b/src/main/java/com/sponus/sponusbe/domain/propose/dto/request/ProposeGetCondition.java deleted file mode 100644 index b7402f90..00000000 --- a/src/main/java/com/sponus/sponusbe/domain/propose/dto/request/ProposeGetCondition.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.sponus.sponusbe.domain.propose.dto.request; - -import io.swagger.v3.oas.annotations.Hidden; -import jakarta.validation.constraints.NotNull; - -public record ProposeGetCondition( - @NotNull - ProposeSearchType proposeSearchType, - Long announcementId -) { - @Hidden - public boolean isSentPropose() { - return proposeSearchType == ProposeSearchType.SEND; - } - - @Hidden - public boolean isReceivedPropose() { - return proposeSearchType == ProposeSearchType.RECEIVED; - } -} diff --git a/src/main/java/com/sponus/sponusbe/domain/propose/dto/request/ProposeSearchType.java b/src/main/java/com/sponus/sponusbe/domain/propose/dto/request/ProposeSearchType.java deleted file mode 100644 index 51519494..00000000 --- a/src/main/java/com/sponus/sponusbe/domain/propose/dto/request/ProposeSearchType.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.sponus.sponusbe.domain.propose.dto.request; - -public enum ProposeSearchType { - SEND, RECEIVED; - -} diff --git a/src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeAttachmentResponse.java b/src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeAttachmentResponse.java new file mode 100644 index 00000000..3bbe9d6b --- /dev/null +++ b/src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeAttachmentResponse.java @@ -0,0 +1,20 @@ +package com.sponus.sponusbe.domain.propose.dto.response; + +import com.sponus.sponusbe.domain.propose.entity.ProposeAttachment; + +import lombok.Builder; + +@Builder +public record ProposeAttachmentResponse( + Long id, + String name, + String url +) { + public static ProposeAttachmentResponse from(ProposeAttachment attachment) { + return ProposeAttachmentResponse.builder() + .id(attachment.getId()) + .name(attachment.getName()) + .url(attachment.getUrl()) + .build(); + } +} diff --git a/src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeDetailGetResponse.java b/src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeDetailGetResponse.java index 41978f40..54f8fc98 100644 --- a/src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeDetailGetResponse.java +++ b/src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeDetailGetResponse.java @@ -4,8 +4,8 @@ import java.util.List; import java.util.Locale; +import com.sponus.sponusbe.domain.announcement.dto.response.AnnouncementDetailResponse; import com.sponus.sponusbe.domain.propose.entity.Propose; -import com.sponus.sponusbe.domain.propose.entity.ProposeAttachment; public record ProposeDetailGetResponse( Long proposeId, @@ -16,18 +16,19 @@ public record ProposeDetailGetResponse( String proposedOrganizationName, Long proposingOrganizationId, String proposingOrganizationName, - List proposeAttachmentUrl, - - // TODO : 공고 상세 정보 (공고에서 묶기!) - Long announcementId, + List proposeAttachmentUrl, + AnnouncementDetailResponse announcementDetails, String createdDate, String createdDay ) { public static ProposeDetailGetResponse from(Propose propose) { DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM.dd"); DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("EEE", Locale.ENGLISH); - List attachmentUrls = propose.getProposeAttachments().stream().map(ProposeAttachment::getUrl).toList(); - + List attachmentUrls = propose.getProposeAttachments() + .stream() + .map(ProposeAttachmentResponse::from) + .toList(); + AnnouncementDetailResponse announcementDetails = AnnouncementDetailResponse.from(propose.getAnnouncement()); return new ProposeDetailGetResponse( propose.getId(), propose.getTitle(), @@ -38,7 +39,7 @@ public static ProposeDetailGetResponse from(Propose propose) { propose.getProposingOrganization().getId(), propose.getProposingOrganization().getName(), attachmentUrls, - propose.getAnnouncement().getId(), + announcementDetails, propose.getCreatedAt().format(dateFormatter), propose.getUpdatedAt().format(dayFormatter) ); diff --git a/src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeSummaryGetResponse.java b/src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeSummaryGetResponse.java index a8eed12e..296dec47 100644 --- a/src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeSummaryGetResponse.java +++ b/src/main/java/com/sponus/sponusbe/domain/propose/dto/response/ProposeSummaryGetResponse.java @@ -3,6 +3,7 @@ import java.time.format.DateTimeFormatter; import java.util.Locale; +import com.sponus.sponusbe.domain.announcement.dto.response.AnnouncementSummaryResponse; import com.sponus.sponusbe.domain.propose.entity.Propose; public record ProposeSummaryGetResponse( @@ -13,15 +14,14 @@ public record ProposeSummaryGetResponse( String proposedOrganizationName, Long proposingOrganizationId, String proposingOrganizationName, - // TODO : 공고 간략 정보 (공고에서 묶기!) - Long announcementId, + AnnouncementSummaryResponse announcementSummary, String createdDate, String createdDay ) { public static ProposeSummaryGetResponse from(Propose propose) { DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM.dd"); DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("EEE", Locale.ENGLISH); - + AnnouncementSummaryResponse announcementSummary = AnnouncementSummaryResponse.from(propose.getAnnouncement()); return new ProposeSummaryGetResponse( propose.getId(), propose.getTitle(), @@ -30,7 +30,7 @@ public static ProposeSummaryGetResponse from(Propose propose) { propose.getProposedOrganization().getName(), propose.getProposingOrganization().getId(), propose.getProposingOrganization().getName(), - propose.getAnnouncement().getId(), + announcementSummary, propose.getCreatedAt().format(dateFormatter), propose.getUpdatedAt().format(dayFormatter) ); diff --git a/src/main/java/com/sponus/sponusbe/domain/propose/entity/Propose.java b/src/main/java/com/sponus/sponusbe/domain/propose/entity/Propose.java index 7fd4b8b7..08380aaf 100644 --- a/src/main/java/com/sponus/sponusbe/domain/propose/entity/Propose.java +++ b/src/main/java/com/sponus/sponusbe/domain/propose/entity/Propose.java @@ -66,12 +66,12 @@ public class Propose extends BaseEntity { @JoinColumn(name = "report_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) private Report report; - @Builder.Default - @OneToMany(mappedBy = "propose", cascade = CascadeType.ALL) - private List proposeImages = new ArrayList<>(); + // @Builder.Default + // @OneToMany(mappedBy = "propose", cascade = CascadeType.ALL, orphanRemoval = true) + // private List proposeImages = new ArrayList<>(); @Builder.Default - @OneToMany(mappedBy = "propose", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "propose", cascade = CascadeType.ALL, orphanRemoval = true) private List proposeAttachments = new ArrayList<>(); public void update(String title, String content, ProposeStatus status) { diff --git a/src/main/java/com/sponus/sponusbe/domain/propose/entity/ProposeImage.java b/src/main/java/com/sponus/sponusbe/domain/propose/entity/ProposeImage.java index 58f68c11..c1a9ee67 100644 --- a/src/main/java/com/sponus/sponusbe/domain/propose/entity/ProposeImage.java +++ b/src/main/java/com/sponus/sponusbe/domain/propose/entity/ProposeImage.java @@ -39,11 +39,11 @@ public class ProposeImage { @JoinColumn(name = "propose_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) private Propose propose; - public void setPropose(Propose propose) { - if (this.propose != null) { - this.propose.getProposeImages().remove(this); - } - this.propose = propose; - propose.getProposeImages().add(this); - } + // public void setPropose(Propose propose) { + // if (this.propose != null) { + // this.propose.getProposeImages().remove(this); + // } + // this.propose = propose; + // propose.getProposeImages().add(this); + // } } diff --git a/src/main/java/com/sponus/sponusbe/domain/propose/service/ProposeQueryService.java b/src/main/java/com/sponus/sponusbe/domain/propose/service/ProposeQueryService.java index 667b3fa3..9d2670f6 100644 --- a/src/main/java/com/sponus/sponusbe/domain/propose/service/ProposeQueryService.java +++ b/src/main/java/com/sponus/sponusbe/domain/propose/service/ProposeQueryService.java @@ -6,7 +6,6 @@ import org.springframework.transaction.annotation.Transactional; import com.sponus.sponusbe.domain.organization.entity.Organization; -import com.sponus.sponusbe.domain.propose.dto.request.ProposeGetCondition; import com.sponus.sponusbe.domain.propose.dto.response.ProposeDetailGetResponse; import com.sponus.sponusbe.domain.propose.dto.response.ProposeSummaryGetResponse; import com.sponus.sponusbe.domain.propose.entity.Propose; @@ -25,26 +24,21 @@ public class ProposeQueryService { private final ProposeRepository proposeRepository; private final ProposeCustomRepository proposeCustomRepository; - public List getProposes(Organization organization, ProposeGetCondition condition) { - List response; - if (condition.isSentPropose()) { - // 내가 보낸 제안은 그냥 반환 - response = proposeCustomRepository.findSentPropose(organization.getId()).stream() - .map(ProposeSummaryGetResponse::from) - .toList(); - } else { - // 내가 받은 제안은 공고 id 별로 보여줘야하고, 공고 id가 없으면 안됨 - if (condition.announcementId() == null) { - throw new ProposeException(ProposeErrorCode.ANNOUNCEMENT_ID_IS_REQUIRED); - } - response = proposeCustomRepository.findReceivedProposeWithAnnouncementId( - organization.getId(), - condition.announcementId()) - .stream() - .map(ProposeSummaryGetResponse::from) - .toList(); - } - return response; + public List getSentProposes(Organization organization) { + return proposeCustomRepository.findSentPropose(organization.getId()).stream() + .map(ProposeSummaryGetResponse::from) + .toList(); + } + + public List getReceivedProposes( + Organization organization, + Long announcementId) { + return proposeCustomRepository.findReceivedProposeWithAnnouncementId( + organization.getId(), + announcementId) + .stream() + .map(ProposeSummaryGetResponse::from) + .toList(); } public ProposeDetailGetResponse getProposeDetail(Long proposeId) { diff --git a/src/main/java/com/sponus/sponusbe/domain/propose/service/ProposeService.java b/src/main/java/com/sponus/sponusbe/domain/propose/service/ProposeService.java index cae8bc3c..d0a64059 100644 --- a/src/main/java/com/sponus/sponusbe/domain/propose/service/ProposeService.java +++ b/src/main/java/com/sponus/sponusbe/domain/propose/service/ProposeService.java @@ -22,7 +22,9 @@ import com.sponus.sponusbe.domain.s3.S3Service; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @RequiredArgsConstructor @Transactional @Service @@ -43,8 +45,8 @@ public ProposeCreateResponse createPropose( // 제안 생성 final Propose propose = request.toEntity( announcement, - authOrganization, - announcement.getWriter() + announcement.getWriter(), + authOrganization ); // 제안의 첨부파일 업로드 @@ -62,9 +64,14 @@ public ProposeCreateResponse createPropose( ); } - public void updatePropose(Organization authOrganization, Long proposeId, ProposeUpdateRequest request) { + public void updatePropose( + Organization authOrganization, + Long proposeId, + ProposeUpdateRequest request, + List attachments) { final Propose propose = getAccessablePropose(authOrganization, proposeId); propose.update(request.title(), request.content(), request.status()); + updateProposeAttachments(propose, attachments); } public void deletePropose(Organization authOrganization, Long proposeId) { @@ -93,4 +100,16 @@ private Propose getAccessablePropose(Organization organization, Long proposeId) return propose; } + + private void updateProposeAttachments(Propose propose, List attachments) { + propose.getProposeAttachments().clear(); + attachments.forEach(attachment -> { + final String url = s3Service.uploadFile(attachment); + ProposeAttachment proposeAttachment = ProposeAttachment.builder() + .name(attachment.getOriginalFilename()) + .url(url) + .build(); + proposeAttachment.setPropose(propose); + }); + } } From 7cda365a4ef8e812b4e028f0d8b3155b75049c41 Mon Sep 17 00:00:00 2001 From: seheonnn Date: Sat, 10 Feb 2024 22:45:21 +0900 Subject: [PATCH 3/4] =?UTF-8?q?:sparkles:=20feat:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20(#153)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/NotificationCreateResponse.java | 24 -------------- .../response/NotificationSummaryResponse.java | 33 +++++++++++++++++++ .../repository/NotificationRepository.java | 4 +++ .../controller/OrganizationController.java | 7 ++++ .../service/OrganizationQueryService.java | 12 +++++++ 5 files changed, 56 insertions(+), 24 deletions(-) delete mode 100644 src/main/java/com/sponus/sponusbe/domain/notification/dto/response/NotificationCreateResponse.java create mode 100644 src/main/java/com/sponus/sponusbe/domain/notification/dto/response/NotificationSummaryResponse.java diff --git a/src/main/java/com/sponus/sponusbe/domain/notification/dto/response/NotificationCreateResponse.java b/src/main/java/com/sponus/sponusbe/domain/notification/dto/response/NotificationCreateResponse.java deleted file mode 100644 index 77959ba9..00000000 --- a/src/main/java/com/sponus/sponusbe/domain/notification/dto/response/NotificationCreateResponse.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.sponus.sponusbe.domain.notification.dto.response; - -import com.sponus.sponusbe.domain.notification.entity.Notification; - -import lombok.Builder; - -@Builder -public record NotificationCreateResponse( - String title, - String body, - Long organizationId, - Long announcementId, - Long proposeId -) { - public static NotificationCreateResponse from(Notification notification) { - return NotificationCreateResponse.builder() - .title(notification.getTitle()) - .body(notification.getBody()) - .organizationId(notification.getOrganization().getId()) - .announcementId(notification.getAnnouncement().getId()) - .proposeId(notification.getPropose().getId()) - .build(); - } -} diff --git a/src/main/java/com/sponus/sponusbe/domain/notification/dto/response/NotificationSummaryResponse.java b/src/main/java/com/sponus/sponusbe/domain/notification/dto/response/NotificationSummaryResponse.java new file mode 100644 index 00000000..440f0416 --- /dev/null +++ b/src/main/java/com/sponus/sponusbe/domain/notification/dto/response/NotificationSummaryResponse.java @@ -0,0 +1,33 @@ +package com.sponus.sponusbe.domain.notification.dto.response; + +import com.sponus.sponusbe.domain.announcement.entity.Announcement; +import com.sponus.sponusbe.domain.notification.entity.Notification; +import com.sponus.sponusbe.domain.propose.entity.Propose; + +import lombok.Builder; + +@Builder +public record NotificationSummaryResponse( + Long id, + String title, + String body, + Long organizationId, + Long announcementId, + Long proposeId, + boolean isRead +) { + public static NotificationSummaryResponse from(Notification notification) { + Announcement announcement = notification.getAnnouncement(); + Propose propose = notification.getPropose(); + + return NotificationSummaryResponse.builder() + .id(notification.getId()) + .title(notification.getTitle()) + .body(notification.getBody()) + .organizationId(notification.getOrganization().getId()) + .announcementId(announcement != null ? announcement.getId() : null) + .proposeId(propose != null ? propose.getId() : null) + .isRead(notification.isRead()) + .build(); + } +} diff --git a/src/main/java/com/sponus/sponusbe/domain/notification/repository/NotificationRepository.java b/src/main/java/com/sponus/sponusbe/domain/notification/repository/NotificationRepository.java index 78ec81fb..7715b75c 100644 --- a/src/main/java/com/sponus/sponusbe/domain/notification/repository/NotificationRepository.java +++ b/src/main/java/com/sponus/sponusbe/domain/notification/repository/NotificationRepository.java @@ -1,8 +1,12 @@ package com.sponus.sponusbe.domain.notification.repository; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; import com.sponus.sponusbe.domain.notification.entity.Notification; +import com.sponus.sponusbe.domain.organization.entity.Organization; public interface NotificationRepository extends JpaRepository { + List findByOrganization(Organization organization); } diff --git a/src/main/java/com/sponus/sponusbe/domain/organization/controller/OrganizationController.java b/src/main/java/com/sponus/sponusbe/domain/organization/controller/OrganizationController.java index 8b35aad3..8df192d0 100644 --- a/src/main/java/com/sponus/sponusbe/domain/organization/controller/OrganizationController.java +++ b/src/main/java/com/sponus/sponusbe/domain/organization/controller/OrganizationController.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RestController; import com.sponus.sponusbe.auth.annotation.AuthOrganization; +import com.sponus.sponusbe.domain.notification.dto.response.NotificationSummaryResponse; import com.sponus.sponusbe.domain.organization.dto.OrganizationDetailGetResponse; import com.sponus.sponusbe.domain.organization.dto.OrganizationJoinRequest; import com.sponus.sponusbe.domain.organization.dto.OrganizationJoinResponse; @@ -81,4 +82,10 @@ public ApiResponse sendEmail(@RequestParam("email") String email) throws public ApiResponse> searchOrganization(@RequestParam("search") String keyword) { return ApiResponse.onSuccess(organizationService.searchOrganization(keyword)); } + + @GetMapping("/notifications") + public ApiResponse> getNotifications( + @AuthOrganization Organization organization) { + return ApiResponse.onSuccess(organizationQueryService.getNotifications(organization)); + } } diff --git a/src/main/java/com/sponus/sponusbe/domain/organization/service/OrganizationQueryService.java b/src/main/java/com/sponus/sponusbe/domain/organization/service/OrganizationQueryService.java index f4d9cee8..690ae7fb 100644 --- a/src/main/java/com/sponus/sponusbe/domain/organization/service/OrganizationQueryService.java +++ b/src/main/java/com/sponus/sponusbe/domain/organization/service/OrganizationQueryService.java @@ -2,9 +2,13 @@ import static com.sponus.sponusbe.domain.organization.exception.OrganizationErrorCode.*; +import java.util.List; + import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.sponus.sponusbe.domain.notification.dto.response.NotificationSummaryResponse; +import com.sponus.sponusbe.domain.notification.repository.NotificationRepository; import com.sponus.sponusbe.domain.organization.dto.OrganizationDetailGetResponse; import com.sponus.sponusbe.domain.organization.entity.Organization; import com.sponus.sponusbe.domain.organization.exception.OrganizationException; @@ -17,6 +21,7 @@ @Service public class OrganizationQueryService { private final OrganizationRepository organizationRepository; + private final NotificationRepository notificationRepository; public OrganizationDetailGetResponse getOrganization(Long organizationId) { Organization organization = organizationRepository.findById(organizationId) @@ -24,4 +29,11 @@ public OrganizationDetailGetResponse getOrganization(Long organizationId) { return OrganizationDetailGetResponse.from(organization); } + + public List getNotifications(Organization organization) { + return notificationRepository.findByOrganization(organization) + .stream() + .map(NotificationSummaryResponse::from) + .toList(); + } } From 69b1ee10f1de1cbc2f35e6cd35439050279f61c0 Mon Sep 17 00:00:00 2001 From: JungTae Kwon Date: Sun, 11 Feb 2024 00:51:26 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=B5=9C=EA=B7=BC=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=9C=20=EA=B3=B5=EA=B3=A0=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84=20=20(#152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ♻️ refactor : request dto 및 엔티티에 null 허용 * ♻️ refactor : 미삭제된 location 변수도 삭제 * ♻️ refactor : redis에 저장되는 refresh token의 key값 구체화 * style: 코드 포맷팅 * style: redis에 함수명 수정 * ✨ feat: 최근 조회한 공고 기능 구현 * style: 코드 포맷팅 --- .../auth/jwt/filter/CustomLoginFilter.java | 2 +- .../auth/jwt/filter/CustomLogoutHandler.java | 4 +-- .../sponusbe/auth/jwt/util/JwtUtil.java | 8 +++--- .../sponusbe/auth/jwt/util/RedisUtil.java | 27 ++++++++++++++++++- .../controller/AnnouncementController.java | 16 +++++++++-- .../service/AnnouncementQueryService.java | 7 +++++ .../service/AnnouncementService.java | 8 +++++- .../domain/bookmark/entity/Bookmark.java | 9 ++++--- .../repository/BookmarkRepository.java | 3 +-- .../bookmark/service/BookmarkService.java | 2 +- .../OrganizationLinkController.java | 6 +++-- .../OrganizationLinkUpdateRequest.java | 4 +-- .../entity/OrganizationLink.java | 1 + .../service/OrganizationLinkQueryService.java | 14 +++++----- .../report/controller/ReportController.java | 10 +++---- .../domain/report/service/ReportService.java | 1 - .../domain/tag/controller/TagController.java | 10 +++---- 17 files changed, 94 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/sponus/sponusbe/auth/jwt/filter/CustomLoginFilter.java b/src/main/java/com/sponus/sponusbe/auth/jwt/filter/CustomLoginFilter.java index 5b34b38e..506f6583 100644 --- a/src/main/java/com/sponus/sponusbe/auth/jwt/filter/CustomLoginFilter.java +++ b/src/main/java/com/sponus/sponusbe/auth/jwt/filter/CustomLoginFilter.java @@ -58,7 +58,7 @@ public Authentication attemptAuthentication( String password = (String)requestBody.get("password"); String fcmToken = (String)requestBody.get("fcmToken"); - redisUtil.save( + redisUtil.saveAsValue( email + "_fcm_token", fcmToken, 999999999L, diff --git a/src/main/java/com/sponus/sponusbe/auth/jwt/filter/CustomLogoutHandler.java b/src/main/java/com/sponus/sponusbe/auth/jwt/filter/CustomLogoutHandler.java index e3582009..3a6d3177 100644 --- a/src/main/java/com/sponus/sponusbe/auth/jwt/filter/CustomLogoutHandler.java +++ b/src/main/java/com/sponus/sponusbe/auth/jwt/filter/CustomLogoutHandler.java @@ -30,7 +30,7 @@ public void logout(HttpServletRequest request, HttpServletResponse response, Aut String accessToken = jwtUtil.resolveAccessToken(request); - redisUtil.save( + redisUtil.saveAsValue( accessToken, "logout", jwtUtil.getExpTime(accessToken), @@ -40,7 +40,7 @@ public void logout(HttpServletRequest request, HttpServletResponse response, Aut String email = jwtUtil.getEmail(accessToken); redisUtil.delete( - email + email + "_refresh_token" ); redisUtil.delete( diff --git a/src/main/java/com/sponus/sponusbe/auth/jwt/util/JwtUtil.java b/src/main/java/com/sponus/sponusbe/auth/jwt/util/JwtUtil.java index 351c7c47..1620f86b 100644 --- a/src/main/java/com/sponus/sponusbe/auth/jwt/util/JwtUtil.java +++ b/src/main/java/com/sponus/sponusbe/auth/jwt/util/JwtUtil.java @@ -83,8 +83,8 @@ public String createJwtRefreshToken(CustomUserDetails customUserDetails) { .signWith(secretKey) .compact(); - redisUtil.save( - customUserDetails.getEmail(), + redisUtil.saveAsValue( + customUserDetails.getEmail() + "_refresh_token", refreshToken, refreshExpMs, TimeUnit.MILLISECONDS @@ -94,7 +94,7 @@ public String createJwtRefreshToken(CustomUserDetails customUserDetails) { } public JwtPair reissueToken(String refreshToken) { - // TODO: 임시메서드(작동안함). Repository에 대한 의존성을 가져야해서 Service로 빼야함 + CustomUserDetails tempCustomUserDetails = new CustomUserDetails( getId(refreshToken), getEmail(refreshToken), @@ -125,7 +125,7 @@ public void validateRefreshToken(String refreshToken) { String email = getEmail(refreshToken); //redis에 refreshToken 있는지 검증 - if (!redisUtil.hasKey(email)) { + if (!redisUtil.hasKey(email + "_refresh_token")) { log.warn("[*] case : Invalid refreshToken"); throw new SecurityCustomException(SecurityErrorCode.INVALID_TOKEN); } diff --git a/src/main/java/com/sponus/sponusbe/auth/jwt/util/RedisUtil.java b/src/main/java/com/sponus/sponusbe/auth/jwt/util/RedisUtil.java index 19ae9fc1..6ec4899e 100644 --- a/src/main/java/com/sponus/sponusbe/auth/jwt/util/RedisUtil.java +++ b/src/main/java/com/sponus/sponusbe/auth/jwt/util/RedisUtil.java @@ -1,22 +1,43 @@ package com.sponus.sponusbe.auth.jwt.util; +import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Component +@Slf4j @RequiredArgsConstructor public class RedisUtil { private final RedisTemplate redisTemplate; - public void save(String key, Object val, Long time, TimeUnit timeUnit) { + public void saveAsValue(String key, Object val, Long time, TimeUnit timeUnit) { redisTemplate.opsForValue().set(key, val, time, timeUnit); } + public void appendToRecentlyViewedAnnouncement(String key, String newValue) { + long RECENT_VIEWED_ANNOUNCEMENT_LIMIT = 20; + + log.info("[*] Newly Viewed Announcement: " + newValue); + Object mostRecentlyViewedValue = redisTemplate.opsForList().index(key, 0); + if (Objects.equals(mostRecentlyViewedValue, newValue)) { + log.info("[*] Skip saving viewed history..."); + return; + } + if (Objects.equals(redisTemplate.opsForList().size(key), RECENT_VIEWED_ANNOUNCEMENT_LIMIT)) { + log.info("[*] Recent Announcement Deque Full Capacity.."); + log.info("[*] Del Top()"); + redisTemplate.opsForList().rightPop(key); + } + redisTemplate.opsForList().leftPush(key, newValue); + } + public boolean hasKey(String key) { return Boolean.TRUE.equals(redisTemplate.hasKey(key)); } @@ -25,6 +46,10 @@ public Object get(String key) { return redisTemplate.opsForValue().get(key); } + public List getList(String key) { + return redisTemplate.opsForList().range(key, 0, -1); + } + public boolean delete(String key) { return Boolean.TRUE.equals(redisTemplate.delete(key)); } diff --git a/src/main/java/com/sponus/sponusbe/domain/announcement/controller/AnnouncementController.java b/src/main/java/com/sponus/sponusbe/domain/announcement/controller/AnnouncementController.java index 72438d9f..a9a601cb 100644 --- a/src/main/java/com/sponus/sponusbe/domain/announcement/controller/AnnouncementController.java +++ b/src/main/java/com/sponus/sponusbe/domain/announcement/controller/AnnouncementController.java @@ -52,8 +52,11 @@ public ApiResponse getAnnouncement() { } @GetMapping("/{announcementId}") - public ApiResponse getAnnouncement(@PathVariable Long announcementId) { - return ApiResponse.onSuccess(announcementService.getAnnouncement(announcementId)); + public ApiResponse getAnnouncement( + @PathVariable Long announcementId, + @AuthOrganization Organization authOrganization + ) { + return ApiResponse.onSuccess(announcementService.getAnnouncement(authOrganization, announcementId)); } @GetMapping("/status") @@ -114,4 +117,13 @@ public ApiResponse> getAnnouncementByCategory( return ApiResponse.onSuccess( announcementQueryService.getAnnouncementByCategory(category, type)); } + + @GetMapping("/recently_viewed_announcements") + public ApiResponse> getRecentAnnouncement( + @AuthOrganization Organization authOrganization + ) { + return ApiResponse.onSuccess( + announcementQueryService.getRecentlyViewedAnnouncement(authOrganization) + ); + } } diff --git a/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementQueryService.java b/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementQueryService.java index 1177fc58..8c94aebc 100644 --- a/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementQueryService.java +++ b/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementQueryService.java @@ -5,10 +5,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.sponus.sponusbe.auth.jwt.util.RedisUtil; import com.sponus.sponusbe.domain.announcement.dto.response.AnnouncementSummaryResponse; import com.sponus.sponusbe.domain.announcement.entity.enums.AnnouncementCategory; import com.sponus.sponusbe.domain.announcement.entity.enums.AnnouncementType; import com.sponus.sponusbe.domain.announcement.repository.AnnouncementRepository; +import com.sponus.sponusbe.domain.organization.entity.Organization; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -20,6 +22,7 @@ public class AnnouncementQueryService { private final AnnouncementRepository announcementRepository; + private final RedisUtil redisUtil; public List searchAnnouncement(String keyword) { log.info("search announcement by keyword: {}", keyword); @@ -57,4 +60,8 @@ else if (type != null) { return announcementRepository.findAll().stream().map(AnnouncementSummaryResponse::from).toList(); } } + + public List getRecentlyViewedAnnouncement(Organization authOrganization) { + return redisUtil.getList(authOrganization.getEmail() + "_recently_viewed_list"); + } } diff --git a/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementService.java b/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementService.java index 9b6607af..802c7e6d 100644 --- a/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementService.java +++ b/src/main/java/com/sponus/sponusbe/domain/announcement/service/AnnouncementService.java @@ -6,6 +6,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import com.sponus.sponusbe.auth.jwt.util.RedisUtil; import com.sponus.sponusbe.domain.announcement.dto.request.AnnouncementCreateRequest; import com.sponus.sponusbe.domain.announcement.dto.request.AnnouncementUpdateRequest; import com.sponus.sponusbe.domain.announcement.dto.response.AnnouncementCreateResponse; @@ -32,6 +33,7 @@ public class AnnouncementService { private final AnnouncementRepository announcementRepository; private final S3Service s3Service; + private final RedisUtil redisUtil; public AnnouncementCreateResponse createAnnouncement( Organization authOrganization, @@ -43,10 +45,14 @@ public AnnouncementCreateResponse createAnnouncement( return AnnouncementCreateResponse.from(announcementRepository.save(announcement)); } - public AnnouncementDetailResponse getAnnouncement(Long announcementId) { + public AnnouncementDetailResponse getAnnouncement(Organization organization, Long announcementId) { Announcement announcement = announcementRepository.findById(announcementId) .orElseThrow(() -> new AnnouncementException(AnnouncementErrorCode.ANNOUNCEMENT_NOT_FOUND)); announcement.increaseViewCount(); + + redisUtil.appendToRecentlyViewedAnnouncement(organization.getEmail() + "_recently_viewed_list", + String.valueOf(announcementId)); + return AnnouncementDetailResponse.from(announcement); } diff --git a/src/main/java/com/sponus/sponusbe/domain/bookmark/entity/Bookmark.java b/src/main/java/com/sponus/sponusbe/domain/bookmark/entity/Bookmark.java index 8bb85e04..2ec054be 100644 --- a/src/main/java/com/sponus/sponusbe/domain/bookmark/entity/Bookmark.java +++ b/src/main/java/com/sponus/sponusbe/domain/bookmark/entity/Bookmark.java @@ -46,10 +46,13 @@ public class Bookmark extends BaseEntity { @JoinColumn(name = "announcement_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) private Announcement announcement; - public void increaseSaveCount() { this.saveCount++; } + public void increaseSaveCount() { + this.saveCount++; + } public void decreaseSaveCount() { if (this.saveCount > 0) { - this.saveCount--; - } } + this.saveCount--; + } + } } diff --git a/src/main/java/com/sponus/sponusbe/domain/bookmark/repository/BookmarkRepository.java b/src/main/java/com/sponus/sponusbe/domain/bookmark/repository/BookmarkRepository.java index cb8398be..0af80db1 100644 --- a/src/main/java/com/sponus/sponusbe/domain/bookmark/repository/BookmarkRepository.java +++ b/src/main/java/com/sponus/sponusbe/domain/bookmark/repository/BookmarkRepository.java @@ -16,5 +16,4 @@ public interface BookmarkRepository extends JpaRepository { List findByOrganizationOrderByCreatedAtDesc(Organization organization); List findByOrganizationOrderBySaveCountDesc(Organization organization); -} - +} \ No newline at end of file diff --git a/src/main/java/com/sponus/sponusbe/domain/bookmark/service/BookmarkService.java b/src/main/java/com/sponus/sponusbe/domain/bookmark/service/BookmarkService.java index 3d2e46d2..54b34b8e 100644 --- a/src/main/java/com/sponus/sponusbe/domain/bookmark/service/BookmarkService.java +++ b/src/main/java/com/sponus/sponusbe/domain/bookmark/service/BookmarkService.java @@ -60,4 +60,4 @@ public List getSavedBookmark(Organization organization) { .map(bookmark -> BookmarkGetResponse.from(bookmark.getAnnouncement(), bookmark)) .collect(Collectors.toList()); } -} +} \ No newline at end of file diff --git a/src/main/java/com/sponus/sponusbe/domain/organizationLink/controller/OrganizationLinkController.java b/src/main/java/com/sponus/sponusbe/domain/organizationLink/controller/OrganizationLinkController.java index dd92afaf..faed4f2e 100644 --- a/src/main/java/com/sponus/sponusbe/domain/organizationLink/controller/OrganizationLinkController.java +++ b/src/main/java/com/sponus/sponusbe/domain/organizationLink/controller/OrganizationLinkController.java @@ -2,6 +2,7 @@ import com.sponus.sponusbe.domain.organizationLink.dto.request.OrganizationLinkUpdateRequest; import com.sponus.sponusbe.domain.organizationLink.dto.response.OrganizationLinkGetResponse; + import org.springframework.web.bind.annotation.*; import com.sponus.sponusbe.auth.annotation.AuthOrganization; @@ -30,13 +31,14 @@ public ApiResponse createOrganizationLink( } @GetMapping("/{organizationLinkId}") - public ApiResponse getOrganizationLink(@PathVariable("organizationLinkId") Long organizationLinkId) { + public ApiResponse getOrganizationLink( + @PathVariable("organizationLinkId") Long organizationLinkId) { return ApiResponse.onSuccess(organizationLinkQueryService.getOrganizationLink(organizationLinkId)); } @PatchMapping("/{organizationLinkId}") public ApiResponse updateOrganizationLink(@PathVariable("organizationLinkId") Long organizationLinkId, - @RequestBody OrganizationLinkUpdateRequest request) { + @RequestBody OrganizationLinkUpdateRequest request) { organizationLinkService.updateOrganizationLink(organizationLinkId, request); return ApiResponse.onSuccess(null); } diff --git a/src/main/java/com/sponus/sponusbe/domain/organizationLink/dto/request/OrganizationLinkUpdateRequest.java b/src/main/java/com/sponus/sponusbe/domain/organizationLink/dto/request/OrganizationLinkUpdateRequest.java index 34fc66b8..b2569313 100644 --- a/src/main/java/com/sponus/sponusbe/domain/organizationLink/dto/request/OrganizationLinkUpdateRequest.java +++ b/src/main/java/com/sponus/sponusbe/domain/organizationLink/dto/request/OrganizationLinkUpdateRequest.java @@ -1,7 +1,7 @@ package com.sponus.sponusbe.domain.organizationLink.dto.request; public record OrganizationLinkUpdateRequest( - String name, - String url + String name, + String url ) { } diff --git a/src/main/java/com/sponus/sponusbe/domain/organizationLink/entity/OrganizationLink.java b/src/main/java/com/sponus/sponusbe/domain/organizationLink/entity/OrganizationLink.java index d683cb50..0e98f744 100644 --- a/src/main/java/com/sponus/sponusbe/domain/organizationLink/entity/OrganizationLink.java +++ b/src/main/java/com/sponus/sponusbe/domain/organizationLink/entity/OrganizationLink.java @@ -3,6 +3,7 @@ import com.sponus.sponusbe.domain.organization.entity.Organization; import com.sponus.sponusbe.domain.organizationLink.dto.request.OrganizationLinkUpdateRequest; + import jakarta.persistence.Column; import jakarta.persistence.ConstraintMode; import jakarta.persistence.Entity; diff --git a/src/main/java/com/sponus/sponusbe/domain/organizationLink/service/OrganizationLinkQueryService.java b/src/main/java/com/sponus/sponusbe/domain/organizationLink/service/OrganizationLinkQueryService.java index 0f15ce51..9027fabd 100644 --- a/src/main/java/com/sponus/sponusbe/domain/organizationLink/service/OrganizationLinkQueryService.java +++ b/src/main/java/com/sponus/sponusbe/domain/organizationLink/service/OrganizationLinkQueryService.java @@ -4,6 +4,7 @@ import com.sponus.sponusbe.domain.organizationLink.dto.response.OrganizationLinkGetResponse; import com.sponus.sponusbe.domain.organizationLink.entity.OrganizationLink; import com.sponus.sponusbe.domain.organizationLink.repository.OrganizationLinkRepository; + import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -15,12 +16,13 @@ @Transactional(readOnly = true) @Service public class OrganizationLinkQueryService { - private final OrganizationLinkRepository organizationLinkRepository; - public OrganizationLinkGetResponse getOrganizationLink(Long organizationLinkId) { - OrganizationLink organizationLink = organizationLinkRepository.findById(organizationLinkId) - .orElseThrow(() -> new OrganizationException(ORGANIZATION_LINK_NOT_FOUND)); + private final OrganizationLinkRepository organizationLinkRepository; + + public OrganizationLinkGetResponse getOrganizationLink(Long organizationLinkId) { + OrganizationLink organizationLink = organizationLinkRepository.findById(organizationLinkId) + .orElseThrow(() -> new OrganizationException(ORGANIZATION_LINK_NOT_FOUND)); - return OrganizationLinkGetResponse.from(organizationLink); + return OrganizationLinkGetResponse.from(organizationLink); - } + } } diff --git a/src/main/java/com/sponus/sponusbe/domain/report/controller/ReportController.java b/src/main/java/com/sponus/sponusbe/domain/report/controller/ReportController.java index 54d066c0..8c60030e 100644 --- a/src/main/java/com/sponus/sponusbe/domain/report/controller/ReportController.java +++ b/src/main/java/com/sponus/sponusbe/domain/report/controller/ReportController.java @@ -56,11 +56,11 @@ public ApiResponse updateReport( @RequestPart(value = "attachments") List attachments) { return ApiResponse.onSuccess( reportService.updateReport( - authOrganization, - reportId, - request, - images, - attachments)); + authOrganization, + reportId, + request, + images, + attachments)); } @GetMapping("/{reportId}") diff --git a/src/main/java/com/sponus/sponusbe/domain/report/service/ReportService.java b/src/main/java/com/sponus/sponusbe/domain/report/service/ReportService.java index f4a30d7b..efbb9f78 100644 --- a/src/main/java/com/sponus/sponusbe/domain/report/service/ReportService.java +++ b/src/main/java/com/sponus/sponusbe/domain/report/service/ReportService.java @@ -52,7 +52,6 @@ public ReportUpdateResponse updateReport( if (!isOrganizationsReport(authOrganization.getId(), report)) throw new ReportException(ReportErrorCode.INVALID_ORGANIZATION); - report.update(request.title(), request.content()); setReportImages(images, report); setReportAttachments(attachments, report); diff --git a/src/main/java/com/sponus/sponusbe/domain/tag/controller/TagController.java b/src/main/java/com/sponus/sponusbe/domain/tag/controller/TagController.java index c8c3c003..6f2f310e 100644 --- a/src/main/java/com/sponus/sponusbe/domain/tag/controller/TagController.java +++ b/src/main/java/com/sponus/sponusbe/domain/tag/controller/TagController.java @@ -36,21 +36,21 @@ public ApiResponse createTag(@AuthOrganization Organization o return ApiResponse.onSuccess(tagService.createTag(organization.getId(), request)); } - @DeleteMapping("/{tagId}") + @DeleteMapping("/{tagId}") public ApiResponse deleteTag(@PathVariable Long tagId) { tagService.deleteTag(tagId); return ApiResponse.onSuccess(null); } - @PatchMapping("/{tagId}") + @PatchMapping("/{tagId}") public ApiResponse updateTag(@PathVariable Long tagId, @RequestBody TagUpdateRequest request) { tagService.updateTag(tagId, request); return ApiResponse.onSuccess(null); } - - @GetMapping("/{tagId}") + + @GetMapping("/{tagId}") public ApiResponse getTag(@PathVariable Long tagId) { return ApiResponse.onSuccess(tagQueryService.getTag(tagId)); } - + }