Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public BattleUserDetailResponse getBattleDetail(Long battleId) {
List<Tag> allTags = getTagsByBattle(battle);
List<BattleOption> options = battleOptionRepository.findByBattle(battle);

String voteStatus = voteRepository.findByBattleAndUserId(battle, 1L)
String voteStatus = voteRepository.findByBattleIdAndUserId(battleId, 1L)
.map(v -> v.getPostVoteOption() != null ? v.getPostVoteOption().getLabel().name() : "NONE")
.orElse("NONE");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
Expand All @@ -33,33 +34,30 @@ public class PerspectiveCommentController {
@PostMapping("/perspectives/{perspectiveId}/comments")
public ApiResponse<CreateCommentResponse> createComment(
@PathVariable Long perspectiveId,
@AuthenticationPrincipal Long userId,
@RequestBody @Valid CreateCommentRequest request
) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
return ApiResponse.onSuccess(commentService.createComment(perspectiveId, userId, request));
}

@Operation(summary = "댓글 목록 조회", description = "특정 관점의 댓글 목록을 커서 기반 페이지네이션으로 조회합니다.")
@GetMapping("/perspectives/{perspectiveId}/comments")
public ApiResponse<CommentListResponse> getComments(
@PathVariable Long perspectiveId,
@AuthenticationPrincipal Long userId,
@RequestParam(required = false) String cursor,
@RequestParam(required = false) Integer size
) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
return ApiResponse.onSuccess(commentService.getComments(perspectiveId, userId, cursor, size));
}

@Operation(summary = "댓글 삭제", description = "본인이 작성한 댓글을 삭제합니다.")
@DeleteMapping("/perspectives/{perspectiveId}/comments/{commentId}")
public ApiResponse<Void> deleteComment(
@PathVariable Long perspectiveId,
@PathVariable Long commentId
@PathVariable Long commentId,
@AuthenticationPrincipal Long userId
) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
commentService.deleteComment(perspectiveId, commentId, userId);
return ApiResponse.onSuccess(null);
}
Expand All @@ -69,10 +67,9 @@ public ApiResponse<Void> deleteComment(
public ApiResponse<UpdateCommentResponse> updateComment(
@PathVariable Long perspectiveId,
@PathVariable Long commentId,
@AuthenticationPrincipal Long userId,
@RequestBody @Valid UpdateCommentRequest request
) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
return ApiResponse.onSuccess(commentService.updateComment(perspectiveId, commentId, userId, request));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
Expand All @@ -30,53 +31,50 @@ public class PerspectiveController {

private final PerspectiveService perspectiveService;

// TODO: Prevote 의 여부를 Vote 도메인 개발 이후 교체
@Operation(summary = "관점 생성", description = "특정 배틀에 대한 관점을 생성합니다. 사전 투표가 완료된 경우에만 가능합니다.")
@PostMapping("/battles/{battleId}/perspectives")
public ApiResponse<CreatePerspectiveResponse> createPerspective(
@PathVariable Long battleId,
@AuthenticationPrincipal Long userId,
@RequestBody @Valid CreatePerspectiveRequest request
) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
return ApiResponse.onSuccess(perspectiveService.createPerspective(battleId, userId, request));
}

@Operation(summary = "관점 리스트 조회", description = "특정 배틀의 관점 목록을 커서 기반 페이지네이션으로 조회합니다. optionLabel(A/B)로 필터링 가능합니다.")
@GetMapping("/battles/{battleId}/perspectives")
public ApiResponse<PerspectiveListResponse> getPerspectives(
@PathVariable Long battleId,
@AuthenticationPrincipal Long userId,
@RequestParam(required = false) String cursor,
@RequestParam(required = false) Integer size,
@RequestParam(required = false) String optionLabel
) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
return ApiResponse.onSuccess(perspectiveService.getPerspectives(battleId, userId, cursor, size, optionLabel));
}

@Operation(summary = "내 PENDING 관점 조회", description = "특정 배틀에서 내가 작성한 관점이 PENDING 상태인 경우 반환합니다. PENDING 관점이 없으면 404를 반환합니다.")
@GetMapping("/battles/{battleId}/perspectives/me/pending")
public ApiResponse<MyPerspectiveResponse> getMyPendingPerspective(@PathVariable Long battleId) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
public ApiResponse<MyPerspectiveResponse> getMyPendingPerspective(
@PathVariable Long battleId,
@AuthenticationPrincipal Long userId) {
return ApiResponse.onSuccess(perspectiveService.getMyPendingPerspective(battleId, userId));
}

@Operation(summary = "관점 삭제", description = "본인이 작성한 관점을 삭제합니다.")
@DeleteMapping("/perspectives/{perspectiveId}")
public ApiResponse<Void> deletePerspective(@PathVariable Long perspectiveId) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
public ApiResponse<Void> deletePerspective(
@PathVariable Long perspectiveId,
@AuthenticationPrincipal Long userId) {
perspectiveService.deletePerspective(perspectiveId, userId);
return ApiResponse.onSuccess(null);
}

@Operation(summary = "관점 검수 재시도", description = "검수 실패(MODERATION_FAILED) 상태의 관점에 대해 GPT 검수를 다시 요청합니다.")
@PostMapping("/perspectives/{perspectiveId}/moderation/retry")
public ApiResponse<Void> retryModeration(@PathVariable Long perspectiveId) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
public ApiResponse<Void> retryModeration(
@PathVariable Long perspectiveId,
@AuthenticationPrincipal Long userId) {
perspectiveService.retryModeration(perspectiveId, userId);
return ApiResponse.onSuccess(null);
}
Expand All @@ -85,10 +83,9 @@ public ApiResponse<Void> retryModeration(@PathVariable Long perspectiveId) {
@PatchMapping("/perspectives/{perspectiveId}")
public ApiResponse<UpdatePerspectiveResponse> updatePerspective(
@PathVariable Long perspectiveId,
@AuthenticationPrincipal Long userId,
@RequestBody @Valid UpdatePerspectiveRequest request
) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
return ApiResponse.onSuccess(perspectiveService.updatePerspective(perspectiveId, userId, request));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -30,17 +31,17 @@ public ApiResponse<LikeCountResponse> getLikeCount(@PathVariable Long perspectiv

@Operation(summary = "좋아요 등록", description = "특정 관점에 좋아요를 등록합니다.")
@PostMapping("/perspectives/{perspectiveId}/likes")
public ApiResponse<LikeResponse> addLike(@PathVariable Long perspectiveId) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
public ApiResponse<LikeResponse> addLike(
@PathVariable Long perspectiveId,
@AuthenticationPrincipal Long userId) {
return ApiResponse.onSuccess(likeService.addLike(perspectiveId, userId));
}

@Operation(summary = "좋아요 취소", description = "특정 관점에 등록한 좋아요를 취소합니다.")
@DeleteMapping("/perspectives/{perspectiveId}/likes")
public ApiResponse<LikeResponse> removeLike(@PathVariable Long perspectiveId) {
// TODO: Security 적용 후 @AuthenticationPrincipal로 userId 교체
Long userId = 1L;
public ApiResponse<LikeResponse> removeLike(
@PathVariable Long perspectiveId,
@AuthenticationPrincipal Long userId) {
return ApiResponse.onSuccess(likeService.removeLike(perspectiveId, userId));
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.swyp.app.domain.perspective.entity;

import com.swyp.app.domain.battle.entity.Battle;
import com.swyp.app.domain.battle.entity.BattleOption;
import com.swyp.app.domain.perspective.enums.PerspectiveStatus;
import com.swyp.app.domain.user.entity.User;
import com.swyp.app.global.common.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import lombok.AccessLevel;
Expand All @@ -25,17 +28,17 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Perspective extends BaseEntity {

// TODO: Battle 엔티티 병합 후 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "battle_id") 로 교체
@Column(name = "battle_id", nullable = false)
private Long battleId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "battle_id", nullable = false)
private Battle battle;

// TODO: User 엔티티 병합 후 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") 로 교체
@Column(name = "user_id", nullable = false)
private Long userId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;

// TODO: BattleOption 엔티티 병합 후 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "option_id") 로 교체
@Column(name = "option_id", nullable = false)
private Long optionId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "option_id", nullable = false)
private BattleOption option;

@Column(nullable = false, columnDefinition = "TEXT")
private String content;
Expand All @@ -51,10 +54,10 @@ public class Perspective extends BaseEntity {
private PerspectiveStatus status;

@Builder
private Perspective(Long battleId, Long userId, Long optionId, String content) {
this.battleId = battleId;
this.userId = userId;
this.optionId = optionId;
private Perspective(Battle battle, User user, BattleOption option, String content) {
this.battle = battle;
this.user = user;
this.option = option;
this.content = content;
this.likeCount = 0;
this.commentCount = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.swyp.app.domain.perspective.entity;

import com.swyp.app.domain.user.entity.User;
import com.swyp.app.global.common.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
Expand All @@ -22,17 +23,17 @@ public class PerspectiveComment extends BaseEntity {
@JoinColumn(name = "perspective_id", nullable = false)
private Perspective perspective;

// TODO: User 엔티티 병합 후 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") 로 교체
@Column(name = "user_id", nullable = false)
private Long userId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;

@Column(nullable = false, columnDefinition = "TEXT")
private String content;

@Builder
private PerspectiveComment(Perspective perspective, Long userId, String content) {
private PerspectiveComment(Perspective perspective, User user, String content) {
this.perspective = perspective;
this.userId = userId;
this.user = user;
this.content = content;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.swyp.app.domain.perspective.entity;

import com.swyp.app.domain.user.entity.User;
import com.swyp.app.global.common.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
Expand All @@ -26,13 +26,13 @@ public class PerspectiveLike extends BaseEntity {
@JoinColumn(name = "perspective_id", nullable = false)
private Perspective perspective;

// TODO: User 엔티티 병합 후 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") 로 교체
@Column(name = "user_id", nullable = false)
private Long userId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;

@Builder
private PerspectiveLike(Perspective perspective, Long userId) {
private PerspectiveLike(Perspective perspective, User user) {
this.perspective = perspective;
this.userId = userId;
this.user = user;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ public interface PerspectiveCommentRepository extends JpaRepository<PerspectiveC

List<PerspectiveComment> findByPerspectiveAndCreatedAtBeforeOrderByCreatedAtDesc(Perspective perspective, LocalDateTime cursor, Pageable pageable);

// MypageService: 사용자 댓글 활동 조회 (offset 페이지네이션)
@Query("SELECT c FROM PerspectiveComment c JOIN FETCH c.perspective WHERE c.userId = :userId ORDER BY c.createdAt DESC")
@Query("SELECT c FROM PerspectiveComment c JOIN FETCH c.perspective WHERE c.user.id = :userId ORDER BY c.createdAt DESC")
List<PerspectiveComment> findByUserIdOrderByCreatedAtDesc(@Param("userId") Long userId, Pageable pageable);

long countByUserId(Long userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ public interface PerspectiveLikeRepository extends JpaRepository<PerspectiveLike

long countByPerspective(Perspective perspective);

// MypageService: 사용자 좋아요 활동 조회 (offset 페이지네이션)
@Query("SELECT l FROM PerspectiveLike l JOIN FETCH l.perspective WHERE l.userId = :userId ORDER BY l.createdAt DESC")
@Query("SELECT l FROM PerspectiveLike l JOIN FETCH l.perspective WHERE l.user.id = :userId ORDER BY l.createdAt DESC")
List<PerspectiveLike> findByUserIdOrderByCreatedAtDesc(@Param("userId") Long userId, Pageable pageable);

long countByUserId(Long userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import com.swyp.app.domain.perspective.repository.PerspectiveCommentRepository;
import com.swyp.app.domain.perspective.repository.PerspectiveRepository;
import com.swyp.app.domain.user.dto.response.UserSummary;
import com.swyp.app.domain.user.entity.User;
import com.swyp.app.domain.user.repository.UserRepository;
import com.swyp.app.domain.user.service.UserService;
import com.swyp.app.global.common.exception.CustomException;
import com.swyp.app.global.common.exception.ErrorCode;
Expand All @@ -30,25 +32,28 @@ public class PerspectiveCommentService {

private final PerspectiveRepository perspectiveRepository;
private final PerspectiveCommentRepository commentRepository;
private final UserRepository userRepository;
private final UserService userQueryService;

@Transactional
public CreateCommentResponse createComment(Long perspectiveId, Long userId, CreateCommentRequest request) {
Perspective perspective = findPerspectiveById(perspectiveId);
User user = userRepository.findById(userId)
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

PerspectiveComment comment = PerspectiveComment.builder()
.perspective(perspective)
.userId(userId)
.user(user)
.content(request.content())
.build();

commentRepository.save(comment);
perspective.incrementCommentCount();

UserSummary user = userQueryService.findSummaryById(userId);
UserSummary userSummary = userQueryService.findSummaryById(userId);
return new CreateCommentResponse(
comment.getId(),
new CreateCommentResponse.UserSummary(user.userTag(), user.nickname(), user.characterType()),
new CreateCommentResponse.UserSummary(userSummary.userTag(), userSummary.nickname(), userSummary.characterType()),
comment.getContent(),
comment.getCreatedAt()
);
Expand All @@ -67,12 +72,12 @@ public CommentListResponse getComments(Long perspectiveId, Long userId, String c

List<CommentListResponse.Item> items = comments.stream()
.map(c -> {
UserSummary user = userQueryService.findSummaryById(c.getUserId());
UserSummary author = userQueryService.findSummaryById(c.getUser().getId());
return new CommentListResponse.Item(
c.getId(),
new CommentListResponse.UserSummary(user.userTag(), user.nickname(), user.characterType()),
new CommentListResponse.UserSummary(author.userTag(), author.nickname(), author.characterType()),
c.getContent(),
c.getUserId().equals(userId),
c.getUser().getId().equals(userId),
c.getCreatedAt()
);
})
Expand Down Expand Up @@ -116,7 +121,7 @@ private PerspectiveComment findCommentById(Long commentId) {
}

private void validateOwnership(PerspectiveComment comment, Long userId) {
if (!comment.getUserId().equals(userId)) {
if (!comment.getUser().getId().equals(userId)) {
throw new CustomException(ErrorCode.COMMENT_FORBIDDEN);
}
}
Expand Down
Loading