DiscussionService.java
package qwerty.chaekit.service.group;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import qwerty.chaekit.domain.group.activity.Activity;
import qwerty.chaekit.domain.group.activity.discussion.Discussion;
import qwerty.chaekit.domain.group.activity.discussion.comment.dto.DiscussionCommentCountDto;
import qwerty.chaekit.domain.group.activity.discussion.comment.repository.DiscussionCommentRepository;
import qwerty.chaekit.domain.group.activity.discussion.repository.DiscussionRepository;
import qwerty.chaekit.domain.highlight.Highlight;
import qwerty.chaekit.domain.highlight.repository.HighlightRepository;
import qwerty.chaekit.domain.member.user.UserProfile;
import qwerty.chaekit.dto.group.activity.discussion.DiscussionDetailResponse;
import qwerty.chaekit.dto.group.activity.discussion.DiscussionFetchResponse;
import qwerty.chaekit.dto.group.activity.discussion.DiscussionPatchRequest;
import qwerty.chaekit.dto.group.activity.discussion.DiscussionPostRequest;
import qwerty.chaekit.dto.page.PageResponse;
import qwerty.chaekit.global.enums.ErrorCode;
import qwerty.chaekit.global.exception.BadRequestException;
import qwerty.chaekit.global.exception.NotFoundException;
import qwerty.chaekit.global.security.resolver.UserToken;
import qwerty.chaekit.mapper.DiscussionMapper;
import qwerty.chaekit.service.util.EntityFinder;
import java.util.List;
import java.util.Map;
@Service
@RequiredArgsConstructor
@Transactional
public class DiscussionService {
private final DiscussionRepository discussionRepository;
private final DiscussionMapper discussionMapper;
private final DiscussionCommentRepository discussionCommentRepository;
private final HighlightRepository highlightRepository;
private final ActivityPolicy activityPolicy;
private final EntityFinder entityFinder;
@Transactional(readOnly = true)
public PageResponse<DiscussionFetchResponse> getDiscussions(UserToken userToken, Pageable pageable, Long activityId) {
Long userId = userToken.userId();
activityPolicy.assertJoined(userId, activityId);
Page<Discussion> discussions = discussionRepository.findByActivityId(activityId, pageable);
List<Long> debateIds = discussions.stream()
.filter(Discussion::isDebate)
.map(Discussion::getId)
.toList();
List<Long> nonDebateIds = discussions.stream()
.filter(d -> !d.isDebate())
.map(Discussion::getId)
.toList();
// Get the count of comments for each discussion
// 찬반 토론: stance별로
Map<Long, DiscussionCommentCountDto> debateCounts = discussionCommentRepository.countStanceCommentsByDiscussionIds(debateIds);
// 일반 토론: 전체 count만
Map<Long, DiscussionCommentCountDto> nonDebateCounts = discussionCommentRepository.countCommentsByDiscussionIds(nonDebateIds);
return PageResponse.of(discussions.map(discussion -> {
DiscussionCommentCountDto dto = discussion.isDebate()
? debateCounts.getOrDefault(discussion.getId(), new DiscussionCommentCountDto(0, 0, 0, 0))
: nonDebateCounts.getOrDefault(discussion.getId(), new DiscussionCommentCountDto(0, 0, 0, 0));
return discussionMapper.toFetchResponse(
discussion,
dto.totalCount(),
userId,
dto.agreeCount(),
dto.disagreeCount(),
dto.neutralCount()
);
}));
}
public DiscussionFetchResponse createDiscussion(UserToken userToken, Long activityId, DiscussionPostRequest request) {
UserProfile user = entityFinder.findUser(userToken.userId());
Activity activity = entityFinder.findActivity(activityId);
activityPolicy.assertJoined(user, activity);
Discussion discussion = Discussion.builder()
.activity(activity)
.title(request.title())
.content(request.content())
.author(user)
.isDebate(request.isDebate())
.build();
// 토론에 연결된 하이라이트 추가
List<Long> highlightIds = request.highlightIds();
setDiscussionHighlightLinks(highlightIds, discussion);
discussionRepository.save(discussion);
return discussionMapper.toFetchResponse(discussion, 0L, user.getId(), 0L, 0L, 0L);
}
@Transactional(readOnly = true)
public DiscussionDetailResponse getDiscussionDetail(UserToken userToken, Long discussionId) {
Long userId = userToken.userId();
Discussion discussion = discussionRepository.findByIdWithAuthorAndComments(discussionId)
.orElseThrow(() -> new NotFoundException(ErrorCode.DISCUSSION_NOT_FOUND));
Long commentCount = (long) discussion.getComments().size();
activityPolicy.assertJoined(userId, discussion.getActivity().getId());
return discussionMapper.toDetailResponse(discussion, commentCount, userId);
}
public DiscussionFetchResponse updateDiscussion(UserToken userToken, Long discussionId, DiscussionPatchRequest request) {
Long userId = userToken.userId();
Discussion discussion = getMyDiscussionById(userId, discussionId);
Long commentCount = discussionCommentRepository.countCommentsByDiscussionId(discussion.getId());
discussion.update(request.title(), request.content());
List<Long> highlightIds = request.highlightIds();
setDiscussionHighlightLinks(highlightIds, discussion);
return discussionMapper.toFetchResponse(discussion, commentCount, userId, -1L, -1L, -1L);
}
public void deleteDiscussion(UserToken userToken, Long discussionId) {
Long userId = userToken.userId();
Discussion discussion = getMyDiscussionById(userId, discussionId);
if (!discussion.getComments().isEmpty()) {
throw new BadRequestException(ErrorCode.DISCUSSION_HAS_COMMENTS);
}
discussionRepository.delete(discussion);
}
private Discussion getMyDiscussionById(Long userId, Long discussionId) {
UserProfile user = entityFinder.findUser(userId);
Discussion discussion = entityFinder.findDiscussion(discussionId);
if (!discussion.isAuthor(user)) {
throw new BadRequestException(ErrorCode.DISCUSSION_NOT_YOURS);
}
return discussion;
}
private void setDiscussionHighlightLinks(List<Long> highlightIds, Discussion discussion) {
if (highlightIds != null) {
long count = highlightRepository.countByIdsAndActivity(highlightIds, discussion.getActivity());
if (count != highlightIds.size()) {
throw new BadRequestException(ErrorCode.HIGHLIGHT_NOT_FOUND);
}
discussion.resetHighlights();
highlightIds.forEach(highlightId -> discussion.addHighlight(Highlight.builder().id(highlightId).build()));
}
}
}