Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/main/java/com/threestar/trainus/TrainUsApplication.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.threestar.trainus;

import java.util.TimeZone;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class TrainUsApplication {
Expand All @@ -10,4 +14,10 @@ public static void main(String[] args) {
SpringApplication.run(TrainUsApplication.class, args);
}

@Bean
public CommandLineRunner init() {
return args -> {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
};
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.threestar.trainus.domain.comment.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -14,9 +16,7 @@

import com.threestar.trainus.domain.comment.dto.CommentCreateRequestDto;
import com.threestar.trainus.domain.comment.dto.CommentPageResponseDto;
import com.threestar.trainus.domain.comment.dto.CommentPageWrapperDto;
import com.threestar.trainus.domain.comment.dto.CommentResponseDto;
import com.threestar.trainus.domain.comment.mapper.CommentMapper;
import com.threestar.trainus.domain.comment.service.CommentService;
import com.threestar.trainus.global.annotation.LoginUser;
import com.threestar.trainus.global.unit.BaseResponse;
Expand Down Expand Up @@ -47,14 +47,13 @@ public ResponseEntity<BaseResponse<CommentResponseDto>> createComment(@PathVaria

@GetMapping("/{lessonId}")
@Operation(summary = "댓글 조회", description = "레슨 ID에 해당되는 댓글들을 조회합니다.")
public ResponseEntity<PagedResponse<CommentPageWrapperDto>> readAll(@PathVariable Long lessonId,
public ResponseEntity<PagedResponse<List<CommentResponseDto>>> readAll(@PathVariable Long lessonId,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapper가 없을때 Json Response 형태는 기존과 동일하게 찍히는지 궁금합니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test 코드 실행시 Body = {"status":200,"message":"댓글 조회 성공","data":[{"commentId":1,"userId":1,"content":"댓글1","parentCommentId":1,"deleted":false,"createdAt":"2025-07-29T09:15:16.119127"},{"commentId":2,"userId":2,"content":"대댓글1","parentCommentId":1,"deleted":false,"createdAt":"2025-07-29T09:15:16.218364"},{"commentId":3,"userId":2,"content":"대댓글2","parentCommentId":1,"deleted":false,"createdAt":"2025-07-29T09:15:16.2414"}],"count":16}
와 같이 응답 받아오는 것 확인했습니다!

@RequestParam("page") int page,
@RequestParam("pageSize") int pageSize) {
int correctPage = Math.max(page, 1);
int correctPageSize = Math.max(1, Math.min(pageSize, pageSizeLimit));
CommentPageResponseDto commentsInfo = commentService.readAll(lessonId, correctPage, correctPageSize);
CommentPageWrapperDto comments = CommentMapper.toCommentPageWrapperDto(commentsInfo);
return PagedResponse.ok("댓글 조회 성공", comments, commentsInfo.getCount(), HttpStatus.OK);
return PagedResponse.ok("댓글 조회 성공", commentsInfo.comments(), commentsInfo.count(), HttpStatus.OK);
}

@DeleteMapping("/{commentId}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Getter;

@Getter
public class CommentCreateRequestDto {
public record CommentCreateRequestDto(
@NotBlank(message = "댓글 내용은 필수입니다")
@Size(max = 255, message = "댓글은 255자 이내여야 합니다.")
private String content;
private Long parentCommentId;
String content,
Long parentCommentId
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
import java.util.List;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class CommentPageResponseDto {

private List<CommentResponseDto> comments;
private Integer count;
public record CommentPageResponseDto(
List<CommentResponseDto> comments,
Integer count
) {
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import java.time.LocalDateTime;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class CommentResponseDto {
private Long commentId;
private Long userId;
private String content;
private Long parentCommentId;
private Boolean deleted;
private LocalDateTime createdAt;
public record CommentResponseDto(
Long commentId,
Long userId,
String nickname,
String content,
Long parentCommentId,
Boolean deleted,
LocalDateTime createdAt
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.threestar.trainus.domain.comment.dto;

import java.time.LocalDateTime;

public interface CommentWithUserProjection {
Long getCommentId();

String getContent();

Long getParentCommentId();

Boolean getDeleted();

LocalDateTime getCreatedAt();

Long getUserId();

String getNickname();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import java.util.List;

import com.threestar.trainus.domain.comment.dto.CommentPageResponseDto;
import com.threestar.trainus.domain.comment.dto.CommentPageWrapperDto;
import com.threestar.trainus.domain.comment.dto.CommentResponseDto;
import com.threestar.trainus.domain.comment.dto.CommentWithUserProjection;
import com.threestar.trainus.domain.comment.entity.Comment;

public class CommentMapper {
Expand All @@ -20,6 +20,19 @@ public static CommentResponseDto toCommentResponseDto(Comment comment) {
.parentCommentId(comment.getParentCommentId())
.deleted(comment.getDeleted())
.createdAt(comment.getCreatedAt())
.nickname(comment.getUser().getNickname())
.build();
}

public static CommentResponseDto toCommentResponseDtoWithProjection(CommentWithUserProjection comment) {
return CommentResponseDto.builder()
.commentId(comment.getCommentId())
.userId(comment.getUserId())
.content(comment.getContent())
.parentCommentId(comment.getParentCommentId())
.deleted(comment.getDeleted())
.createdAt(comment.getCreatedAt())
.nickname(comment.getNickname())
.build();
}

Expand All @@ -29,10 +42,4 @@ public static CommentPageResponseDto toCommentPageResponseDto(List<CommentRespon
.count(count)
.build();
}

public static CommentPageWrapperDto toCommentPageWrapperDto(CommentPageResponseDto commentPageResponseDto) {
return CommentPageWrapperDto.builder()
.comments(commentPageResponseDto.getComments())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,34 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.threestar.trainus.domain.comment.dto.CommentWithUserProjection;
import com.threestar.trainus.domain.comment.entity.Comment;

public interface CommentRepository extends JpaRepository<Comment, Long> {

//create index idx_lesson_id_parent_comment_id_comment_id on comments(lesson_id, parent_comment_id asc, comment_id asc); 인덱스 통해 조회 성능 최적화
@Query(value = """
select comments.comment_id, comments.lesson_id, comments.user_id, comments.content,
comments.parent_comment_id, comments.deleted, comments.created_at, comments.updated_at
from (
select comment_id
from comments c
join users u on c.user_id = u.id and u.deleted_at IS NULL
where lesson_id = :lessonId
order by parent_comment_id asc, comment_id asc
limit :limit offset :offset
) t left join comments on t.comment_id = comments.comment_id
select
c.comment_id as commentId,
c.content as content,
c.parent_comment_id as parentCommentId,
c.deleted as deleted,
c.created_at as createdAt,
u.id as userId,
u.nickname as nickname
from (
select comment_id
from comments c
join users u on c.user_id = u.id and u.deleted_at IS NULL
where lesson_id = :lessonId
order by parent_comment_id asc, comment_id asc
limit :limit offset :offset
) t
join comments c on t.comment_id = c.comment_id
join users u on c.user_id = u.id
""", nativeQuery = true)
List<Comment> findAll(@Param("lessonId") Long lessonId, @Param("offset") int offset, @Param("limit") int limit);
List<CommentWithUserProjection> findAll(@Param("lessonId") Long lessonId, @Param("offset") int offset,
@Param("limit") int limit);

@Query(value = """
select count(*) from (
Expand All @@ -46,7 +56,8 @@ select count(*) from (
limit :limit
) t
""", nativeQuery = true)
Long countBy(@Param("lessonId") Long lessonId, @Param("parentCommentId") Long parentCommentId, @Param("limit") int limit);
Long countBy(@Param("lessonId") Long lessonId, @Param("parentCommentId") Long parentCommentId,
@Param("limit") int limit);

Optional<Comment> findByCommentIdAndUserId(Long commentId, Long userId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public CommentResponseDto createComment(CommentCreateRequestDto request, Long le
.lesson(findLesson)
.user(findUser)
.deleted(false)
.content(request.getContent())
.content(request.content())
.build();

commentRepository.saveAndFlush(newComment); //즉시 저장을 통해 commentId
Expand All @@ -51,7 +51,7 @@ public CommentResponseDto createComment(CommentCreateRequestDto request, Long le
}

private Comment findParent(CommentCreateRequestDto request) {
Long parentCommentId = request.getParentCommentId();
Long parentCommentId = request.parentCommentId();
if (parentCommentId == null) {
return null;
}
Expand All @@ -65,7 +65,7 @@ private Comment findParent(CommentCreateRequestDto request) {
public CommentPageResponseDto readAll(Long lessonId, int page, int pageSize) {
return CommentMapper.toCommentPageResponseDto(
commentRepository.findAll(lessonId, (page - 1) * pageSize, pageSize)
.stream().map(CommentMapper::toCommentResponseDto)
.stream().map(CommentMapper::toCommentResponseDtoWithProjection)
.toList(),
commentRepository.count(lessonId, PageLimitCalculator.calculatePageLimit(page, pageSize, 5))
//한번에 보일 수 있는 페이지 이동 갯수 5개(프론트와 협의)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,17 @@ public class Payment extends BaseDateEntity {
@Enumerated(EnumType.STRING)
private PaymentMethod paymentMethod;

public void processPayment(int totalPrice, LocalDateTime paidAt, String method) {
this.payPrice = totalPrice;
this.payDate = paidAt;
this.status = PaymentStatus.DONE;
this.paymentMethod = PaymentMethod.fromTossMethod(method);
}

public void cancelPayment(LocalDateTime cancelAt, int refundPrice) {
this.status = PaymentStatus.CANCELED;
this.cancelledAt = cancelAt;
this.refundPrice = refundPrice;
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.threestar.trainus.domain.payment.mapper;

import java.time.LocalDateTime;
import java.util.List;

import com.threestar.trainus.domain.coupon.user.entity.UserCoupon;
import com.threestar.trainus.domain.lesson.teacher.entity.Lesson;
import com.threestar.trainus.domain.payment.dto.PaymentResponseDto;
import com.threestar.trainus.domain.payment.dto.TossPaymentResponseDto;
import com.threestar.trainus.domain.payment.dto.cancel.CancelPaymentResponseDto;
import com.threestar.trainus.domain.payment.dto.cancel.PaymentCancelHistoryPageDto;
import com.threestar.trainus.domain.payment.dto.cancel.PaymentCancelHistoryResponseDto;
Expand All @@ -11,14 +16,47 @@
import com.threestar.trainus.domain.payment.dto.success.PaymentSuccessPageWrapperDto;
import com.threestar.trainus.domain.payment.dto.success.SuccessfulPaymentResponseDto;
import com.threestar.trainus.domain.payment.entity.Payment;
import com.threestar.trainus.domain.payment.entity.PaymentMethod;
import com.threestar.trainus.domain.payment.entity.PaymentStatus;
import com.threestar.trainus.domain.payment.entity.TossPayment;
import com.threestar.trainus.domain.user.entity.User;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PaymentMapper {

public static Payment toPayment(User user, Lesson lesson, String orderId, int finalPrice, UserCoupon coupon,
PaymentStatus status, PaymentMethod payMethod) {
return Payment.builder()
.user(user)
.lesson(lesson)
.orderId(orderId)
.payPrice(finalPrice)
.originPrice(lesson.getPrice())
.payDate(LocalDateTime.now())
.userCoupon(coupon)
.status(status)
.paymentMethod(payMethod)
.build();
}

public static TossPayment toTossPayment(Payment payment, TossPaymentResponseDto tossDto, LocalDateTime requestAt,
LocalDateTime paidAt, PaymentStatus status) {
return TossPayment.builder()
.payment(payment)
.paymentKey(tossDto.paymentKey())
.orderId(tossDto.orderId())
.amount(tossDto.totalAmount())
.orderName(tossDto.orderName())
.paymentStatus(status)
.paymentMethod(PaymentMethod.fromTossMethod(tossDto.method()))
.requestedAt(requestAt)
.approvedAt(paidAt)
.build();
}

public static SuccessfulPaymentResponseDto toSuccessfulPaymentResponseDto(Payment payment) {
return SuccessfulPaymentResponseDto.builder()
.payPrice(payment.getPayPrice())
Expand Down Expand Up @@ -110,4 +148,14 @@ public static PaymentCancelPageWrapperDto toPaymentFailurePageWrapperDto(Payment
.failureHistory(paymentDto.failureHistory())
.build();
}

public static PaymentResponseDto toPaymentResponseDto(int originPrice, String lessonTitle, PaymentMethod payMethod ,int finalPrice ,String orderId) {
return PaymentResponseDto.builder()
.originPrice(originPrice)
.lessonTitle(lessonTitle)
.paymentMethod(payMethod)
.payPrice(finalPrice)
.orderId(orderId)
.build();
}
}
Loading