Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] 경제 톡톡 저장 기능 구현 및 기타 버그 수정 #60

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-devtools'

//Lombok
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ public ResponseEntity<Object> handleAllException(Exception e, WebRequest request
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

e.printStackTrace();

return handleExceptionInternal(GlobalErrorCode.INTERNAL_SERVER_ERROR);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public class NotificationService {

@Transactional
public void createCommentNotification(final Post post, final Comment comment) {
if (post.getAuthor() == null) {
return;
}

User postAuthor = post.getAuthor();

Expand All @@ -50,26 +53,34 @@ public void createReplyNotification(final Post post, final Comment comment) {
String content = comment.getContent();
String title = post.getTitle();

Notification notificationForPostAuthor =
Notification.toNotificationEntity(postAuthor, content, title, NotificationType.REPLY, post);
if (post.getAuthor() != null) {

Notification notificationForCommentAuthor =
Notification.toNotificationEntity(
commentAuthor, content, title, NotificationType.REPLY, post);
Notification notificationForPostAuthor =
Notification.toNotificationEntity(
postAuthor, content, title, NotificationType.REPLY, post);
notificationRepository.save(notificationForPostAuthor);
sendNotification(postAuthor, NotificationDTO.toNotificationDTO(notificationForPostAuthor));
}
if (comment.getCommenter() != null) {

notificationRepository.save(notificationForPostAuthor);
notificationRepository.save(notificationForCommentAuthor);
Notification notificationForCommentAuthor =
Notification.toNotificationEntity(
commentAuthor, content, title, NotificationType.REPLY, post);

// 게시글 작성자와 댓글 작성자에게 알림 전송
sendNotification(postAuthor, NotificationDTO.toNotificationDTO(notificationForPostAuthor));
sendNotification(
commentAuthor, NotificationDTO.toNotificationDTO(notificationForCommentAuthor));
notificationRepository.save(notificationForCommentAuthor);

sendNotification(
commentAuthor, NotificationDTO.toNotificationDTO(notificationForCommentAuthor));
}
}

@Transactional
public void createPopularNotification(final Post post) {

User receiver = post.getAuthor();
if (receiver == null) {
return;
}

Notification notification =
Notification.toNotificationEntity(
Expand Down
27 changes: 19 additions & 8 deletions src/main/java/com/ripple/BE/post/controller/ToktokController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

import com.ripple.BE.global.dto.response.ApiResponse;
import com.ripple.BE.post.domain.type.PostSort;
import com.ripple.BE.post.dto.PostDTO;
import com.ripple.BE.post.dto.PostListDTO;
import com.ripple.BE.post.dto.ToktokDTO;
import com.ripple.BE.post.dto.ToktokListDTO;
import com.ripple.BE.post.dto.response.ToktokPreviewListResponse;
import com.ripple.BE.post.dto.response.ToktokPreviewResponse;
import com.ripple.BE.post.dto.response.ToktokResponse;
import com.ripple.BE.post.service.ToktokAdminService;
import com.ripple.BE.post.service.ToktokService;
import com.ripple.BE.user.domain.CustomUserDetails;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -18,6 +19,7 @@
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -29,14 +31,15 @@
public class ToktokController {

private final ToktokService toktokService;
private final ToktokAdminService toktokAdminService;

@Operation(summary = "오늘의 경제 톡톡 주제 조회", description = "오늘의 경제 톡톡 주제를 조회합니다.")
@GetMapping("/toktok-today")
public ResponseEntity<ApiResponse<Object>> getTodayToktok() {
PostDTO postDTO = toktokService.getTodayToktok();
ToktokDTO toktokDTO = toktokService.getTodayToktok();

return ResponseEntity.status(HttpStatus.OK)
.body(ApiResponse.from(ToktokPreviewResponse.toToktokPreviewResponse(postDTO)));
.body(ApiResponse.from(ToktokPreviewResponse.toToktokPreviewResponse(toktokDTO)));
}

@Operation(summary = "경제 톡톡 목록 조회", description = "경제 톡톡 목록을 조회합니다.")
Expand All @@ -45,10 +48,11 @@ public ResponseEntity<ApiResponse<Object>> getToktoks(
final @AuthenticationPrincipal CustomUserDetails currentUser,
final @RequestParam(required = false, defaultValue = "0") @PositiveOrZero int page,
final @RequestParam(required = false, defaultValue = "RECENT") PostSort sort) {
PostListDTO postListDTO = toktokService.getToktoks(page, sort, currentUser.getId());
ToktokListDTO toktokListDTO = toktokService.getToktoks(page, sort, currentUser.getId());

return ResponseEntity.status(HttpStatus.OK)
.body(ApiResponse.from(ToktokPreviewListResponse.toToktokPreviewListResponse(postListDTO)));
.body(
ApiResponse.from(ToktokPreviewListResponse.toToktokPreviewListResponse(toktokListDTO)));
}

@Operation(summary = "경제 톡톡 게시물 상세 조회", description = "경제 톡톡 게시물을 상세 조회합니다.")
Expand All @@ -57,9 +61,16 @@ public ResponseEntity<ApiResponse<Object>> getToktok(
final @AuthenticationPrincipal CustomUserDetails currentUser,
final @PathVariable("id") long id) {

PostDTO postDTO = toktokService.getToktok(id, currentUser.getId());
ToktokDTO toktokDTO = toktokService.getToktok(id, currentUser.getId());

return ResponseEntity.status(HttpStatus.OK)
.body(ApiResponse.from(ToktokResponse.toToktokResponse(postDTO)));
.body(ApiResponse.from(ToktokResponse.toToktokResponse(toktokDTO)));
}

@Operation(summary = "경제 톡톡 게시물 생성 (관리자)", description = "경제 톡톡 게시물을 생성합니다. (관리자 전용)")
@PostMapping("/toktok/excel")
public ResponseEntity<ApiResponse<Object>> saveToktokByExcel() {
toktokAdminService.createToktokByExcel();
return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.from(ApiResponse.EMPTY_RESPONSE));
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/ripple/BE/post/domain/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.ripple.BE.image.domain.Image;
import com.ripple.BE.post.domain.type.PostType;
import com.ripple.BE.post.dto.PostDTO;
import com.ripple.BE.post.dto.ToktokDTO;
import com.ripple.BE.user.domain.User;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
Expand Down Expand Up @@ -126,6 +127,15 @@ public static Post toPostEntity(PostDTO postDTO) {
.build();
}

public static Post toPostEntity(ToktokDTO toktokDTO) {
return Post.builder()
.title(toktokDTO.title())
.content(toktokDTO.content())
.type(toktokDTO.type())
.imageList(new ArrayList<>())
.build();
}

public void update(PostDTO postDTO) {
this.title = postDTO.title();
this.content = postDTO.content();
Expand Down
94 changes: 94 additions & 0 deletions src/main/java/com/ripple/BE/post/dto/ToktokDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.ripple.BE.post.dto;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.ripple.BE.image.dto.ImageListDTO;
import com.ripple.BE.post.domain.Post;
import com.ripple.BE.post.domain.type.PostType;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Map;

public record ToktokDTO(
Long id,
String title,
String content,
PostType type,
Long likeCount,
Long commentCount,
Long scrapCount,
ImageListDTO imageList,
Boolean isScraped,
Boolean isLiked,
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
LocalDateTime createdDate,
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
LocalDateTime modifiedDate,
@JsonSerialize(using = LocalDateSerializer.class)
@JsonDeserialize(using = LocalDateDeserializer.class)
LocalDate usedDate,
CommentListDTO commentListDTO) {

private static final String TITLE = "title";
private static final String CONTENT = "content";

public static ToktokDTO toToktokDTO(final Post post) {
return new ToktokDTO(
post.getId(),
post.getTitle(),
post.getContent(),
post.getType(),
post.getLikeCount(),
post.getCommentCount(),
post.getScrapCount(),
ImageListDTO.toImageListDTO(post.getImageList()),
post.getIsScrapped(),
post.getIsLiked(),
post.getCreatedDate(),
post.getModifiedDate(),
post.getUsedDate(),
null);
}

public static ToktokDTO toToktokDTO(final Post post, final CommentListDTO commentListDTO) {
return new ToktokDTO(
post.getId(),
post.getTitle(),
post.getContent(),
post.getType(),
post.getLikeCount(),
post.getCommentCount(),
post.getScrapCount(),
ImageListDTO.toImageListDTO(post.getImageList()),
post.getIsScrapped(),
post.getIsLiked(),
post.getCreatedDate(),
post.getModifiedDate(),
post.getUsedDate(),
commentListDTO);
}

public static ToktokDTO toToktokDTO(final Map<String, String> excelData) {
return new ToktokDTO(
null,
excelData.get(TITLE),
excelData.get(CONTENT),
PostType.ECONOMY_TALK,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null);
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/ripple/BE/post/dto/ToktokListDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.ripple.BE.post.dto;

import com.ripple.BE.post.domain.Post;
import java.util.List;
import org.springframework.data.domain.Page;

public record ToktokListDTO(List<ToktokDTO> toktokDTOList, int totalPage, int currentPage) {

public static ToktokListDTO toToktokListDTO(Page<Post> postPage) {
return new ToktokListDTO(
postPage.getContent().stream().map(ToktokDTO::toToktokDTO).toList(),
postPage.getTotalPages(),
postPage.getNumber());
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.ripple.BE.post.dto.response;

import com.ripple.BE.global.utils.RelativeTimeFormatter;
import com.ripple.BE.image.domain.Image;
import com.ripple.BE.image.domain.S3Info;
import com.ripple.BE.post.dto.CommentDTO;
import java.util.List;
import java.util.Optional;

public record CommentResponse(
Long id,
Expand All @@ -25,7 +28,10 @@ public static CommentResponse toCommentResponse(CommentDTO commentDTO) {
commentDTO.likeCount(),
commentDTO.commenter().id(),
commentDTO.commenter().nickname(),
commentDTO.commenter().profileImage().getS3Info().getUrl(),
Optional.ofNullable(commentDTO.commenter().profileImage())
.map(Image::getS3Info)
.map(S3Info::getUrl)
.orElse(null),
commentDTO.isDeleted(),
commentDTO.isAuthor(),
commentDTO.isLiked(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.ripple.BE.post.dto.response;

import com.ripple.BE.global.utils.RelativeTimeFormatter;
import com.ripple.BE.image.domain.Image;
import com.ripple.BE.image.domain.S3Info;
import com.ripple.BE.image.dto.response.ImageResponse;
import com.ripple.BE.post.domain.type.PostType;
import com.ripple.BE.post.dto.PostDTO;
import java.util.List;
import java.util.Optional;

public record PostResponse(
Long id,
Expand All @@ -28,7 +31,10 @@ public static PostResponse toPostResponse(PostDTO postDTO) {
postDTO.id(),
postDTO.title(),
postDTO.author().nickname(),
postDTO.author().profileImage().getS3Info().getUrl(),
Optional.ofNullable(postDTO.author().profileImage())
.map(Image::getS3Info)
.map(S3Info::getUrl)
.orElse(null),
postDTO.content(),
postDTO.type(),
postDTO.likeCount(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.ripple.BE.post.dto.response;

import com.ripple.BE.post.dto.PostListDTO;
import com.ripple.BE.post.dto.ToktokListDTO;
import java.util.List;

public record ToktokPreviewListResponse(List<ToktokPreviewResponse> toktokPreviewResponseList) {

public static ToktokPreviewListResponse toToktokPreviewListResponse(PostListDTO postListDTO) {
public static ToktokPreviewListResponse toToktokPreviewListResponse(ToktokListDTO toktokListDTO) {
return new ToktokPreviewListResponse(
postListDTO.postDTOList().stream()
toktokListDTO.toktokDTOList().stream()
.map(ToktokPreviewResponse::toToktokPreviewResponse)
.toList());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.ripple.BE.post.dto.response;

import com.ripple.BE.global.utils.RelativeTimeFormatter;
import com.ripple.BE.post.dto.PostDTO;
import com.ripple.BE.post.dto.ToktokDTO;

public record ToktokPreviewResponse(
Long id,
Expand All @@ -12,17 +12,17 @@ public record ToktokPreviewResponse(
Boolean isScraped,
String createdDate) {

public static ToktokPreviewResponse toToktokPreviewResponse(PostDTO postDTO) {
public static ToktokPreviewResponse toToktokPreviewResponse(ToktokDTO toktokDTO) {

return new ToktokPreviewResponse(
postDTO.id(),
postDTO.title(),
postDTO.commentCount(),
postDTO.likeCount(),
postDTO.imageList().imageDTOList().isEmpty()
toktokDTO.id(),
toktokDTO.title(),
toktokDTO.commentCount(),
toktokDTO.likeCount(),
toktokDTO.imageList().imageDTOList().isEmpty()
? null
: postDTO.imageList().imageDTOList().get(0).url(),
postDTO.isScraped(),
RelativeTimeFormatter.formatRelativeTime(postDTO.usedDate().atStartOfDay()));
: toktokDTO.imageList().imageDTOList().get(0).url(),
toktokDTO.isScraped(),
RelativeTimeFormatter.formatRelativeTime(toktokDTO.usedDate().atStartOfDay()));
}
}
Loading