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
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import com.ai.lawyer.domain.chatbot.dto.ChatDto.ChatRequest;
import com.ai.lawyer.domain.chatbot.dto.ChatDto.ChatResponse;
import com.ai.lawyer.domain.chatbot.service.ChatBotService;
import com.ai.lawyer.global.util.AuthUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -27,15 +27,19 @@ public class ChatBotController {

@Operation(summary = "01. 새로운 채팅", description = "첫 메시지 전송으로 새로운 채팅방을 생성하고 챗봇과 대화를 시작")
@PostMapping("/message")
public ResponseEntity<Flux<ChatResponse>> postNewMessage(
@AuthenticationPrincipal Long memberId,
@RequestBody ChatRequest chatRequest) {
public ResponseEntity<Flux<ChatResponse>> postNewMessage(@RequestBody ChatRequest chatRequest) {
Long memberId = AuthUtil.getAuthenticatedMemberId();
log.debug("새로운 채팅 요청: memberId={}", memberId);
return ResponseEntity.ok(chatBotService.sendMessage(memberId, chatRequest, null));
}

@Operation(summary = "02. 기존 채팅", description = "기존 채팅방에 메시지를 보내고 챗봇과 대화를 이어감")
@PostMapping("{roomId}/message")
public ResponseEntity<Flux<ChatResponse>> postMessage(@AuthenticationPrincipal Long memberId, @RequestBody ChatRequest chatRequest, @PathVariable(value = "roomId", required = false) Long roomId) {
public ResponseEntity<Flux<ChatResponse>> postMessage(
@RequestBody ChatRequest chatRequest,
@PathVariable(value = "roomId", required = false) Long roomId) {
Long memberId = AuthUtil.getAuthenticatedMemberId();
log.debug("기존 채팅 요청: memberId={}, roomId={}", memberId, roomId);
return ResponseEntity.ok(chatBotService.sendMessage(memberId, chatRequest, roomId));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ public class PollVote {
// Member와 OAuth2Member 모두 지원하기 위해 FK 제약 조건 제거
// 애플리케이션 레벨에서 AuthUtil로 참조 무결성 보장
// foreignKey 제약조건 비활성화 (ConstraintMode.NO_CONSTRAINT)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Member member;
@Column(name = "member_id", nullable = false)
private Long memberId;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "poll_items_id", nullable = false, foreignKey = @ForeignKey(name = "FK_POLLVOTE_POLLOPTIONS"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,20 @@
import java.util.Optional;

public interface PollVoteRepository extends JpaRepository<PollVote, Long>, PollVoteRepositoryCustom {
Optional<PollVote> findByMember_MemberIdAndPoll_PollId(Long memberId, Long pollId);
void deleteByMember_MemberIdAndPoll_PollId(Long memberId, Long pollId);
List<PollVote> findByMember_MemberIdAndPollOptions_PollItemsId(Long memberId, Long pollItemsId);
List<PollVote> findByMember_MemberId(Long memberId);
Optional<PollVote> findByMemberIdAndPoll_PollId(Long memberId, Long pollId);
void deleteByMemberIdAndPoll_PollId(Long memberId, Long pollId);
List<PollVote> findByMemberIdAndPollOptions_PollItemsId(Long memberId, Long pollItemsId);
List<PollVote> findByMemberId(Long memberId);

/**
* member_id로 투표 내역 삭제 (회원 탈퇴 시 사용)
* Member와 OAuth2Member 모두 같은 member_id 공간을 사용하므로 Long 타입으로 삭제
*/
@Modifying
@Query("DELETE FROM PollVote pv WHERE pv.member.memberId = :memberId")
@Query("DELETE FROM PollVote pv WHERE pv.memberId = :memberId")
void deleteByMemberIdValue(@Param("memberId") Long memberId);

boolean existsByPollAndMember(Poll poll, Member member);

@Query("SELECT v.member.memberId FROM PollVote v WHERE v.poll = :poll")
boolean existsByPollAndMemberId(Poll poll, Long memberId);
@Query("SELECT v.memberId FROM PollVote v WHERE v.poll = :poll")
List<Long> findMemberIdsByPoll(@Param("poll") Poll poll);
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public List<PollStaticsDto> countStaticsByPollOptionIds(List<Long> pollOptionIds
pollVote.count())
.from(pollVote)
.join(pollVote.getPollOptions(), pollOptions)
.join(pollVote.getMember(), member)
.join(member).on(pollVote.getMemberId().eq(member.getMemberId()))
.where(pollOptions.getPollItemsId().in(pollOptionIds))
.groupBy(pollOptions.getPollItemsId(), member.getGender(), member.getAge())
.fetch();
Expand Down Expand Up @@ -147,7 +147,7 @@ public List<AgeGroupCountDto> getOptionAgeStatics(Long pollId) {
pollVote.count())
.from(pollVote)
.join(pollVote.getPollOptions(), pollOptions)
.join(pollVote.getMember(), member)
.join(member).on(pollVote.getMemberId().eq(member.getMemberId()))
.where(pollOptions.getPoll().getPollId().eq(pollId))
.groupBy(pollOptions.getOption(),
new com.querydsl.core.types.dsl.CaseBuilder()
Expand Down Expand Up @@ -177,7 +177,7 @@ public List<GenderCountDto> getOptionGenderStatics(Long pollId) {
pollVote.count())
.from(pollVote)
.join(pollVote.getPollOptions(), pollOptions)
.join(pollVote.getMember(), member)
.join(member).on(pollVote.getMemberId().eq(member.getMemberId()))
.where(pollOptions.getPoll().getPollId().eq(pollId))
.groupBy(pollOptions.getOption(), member.getGender())
.fetch();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,17 @@ public PollVoteDto vote(Long pollId, Long pollItemsId, Long memberId) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "투표 권한이 없습니다.");
}
// 기존 투표 내역 조회
var existingVoteOpt = pollVoteRepository.findByMember_MemberIdAndPoll_PollId(memberId, pollId);
var existingVoteOpt = pollVoteRepository.findByMemberIdAndPoll_PollId(memberId, pollId);
if (existingVoteOpt.isPresent()) {
PollVote existingVote = existingVoteOpt.get();
if (existingVote.getPollOptions().getPollItemsId().equals(pollItemsId)) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "이미 투표하셨습니다.");
} else {
pollVoteRepository.deleteByMember_MemberIdAndPoll_PollId(memberId, pollId);
pollVoteRepository.deleteByMemberIdAndPoll_PollId(memberId, pollId);
PollVote pollVote = PollVote.builder()
.poll(poll)
.pollOptions(pollOptions)
.member(member)
.memberId(memberId)
.build();
PollVote savedVote = pollVoteRepository.save(pollVote);
Long voteCount = pollVoteRepository.countByPollOptionId(pollItemsId);
Expand All @@ -140,7 +140,7 @@ public PollVoteDto vote(Long pollId, Long pollItemsId, Long memberId) {
PollVote pollVote = PollVote.builder()
.poll(poll)
.pollOptions(pollOptions)
.member(member)
.memberId(memberId)
.build();
PollVote savedVote = pollVoteRepository.save(pollVote);
Long voteCount = pollVoteRepository.countByPollOptionId(pollItemsId);
Expand Down Expand Up @@ -441,7 +441,7 @@ private PollDto convertToDto(Poll poll, Long memberId, boolean withStatistics) {
Long voteCount = pollVoteRepository.countByPollOptionId(option.getPollItemsId());
boolean voted = false;
if (memberId != null) {
voted = !pollVoteRepository.findByMember_MemberIdAndPollOptions_PollItemsId(memberId, option.getPollItemsId()).isEmpty();
voted = !pollVoteRepository.findByMemberIdAndPollOptions_PollItemsId(memberId, option.getPollItemsId()).isEmpty();
}
List<PollStaticsDto> statics = null;
if (withStatistics && poll.getStatus() == Poll.PollStatus.CLOSED) {
Expand Down Expand Up @@ -539,7 +539,7 @@ public void validatePollCreate(PollCreateDto dto) {

@Override
public void cancelVote(Long pollId, Long memberId) {
pollVoteRepository.findByMember_MemberIdAndPoll_PollId(memberId, pollId)
pollVoteRepository.findByMemberIdAndPoll_PollId(memberId, pollId)
.ifPresent(pollVoteRepository::delete);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,20 @@ public ResponseEntity<String> deleteDummyMembers() {
int deleted = dummyService.deleteDummyMembers();
return ResponseEntity.ok("더미 멤버 " + deleted + "명 삭제 완료");
}

//모든 더미 유저가 1번 옵션에 투표
@Operation(summary = "더미 멤버 1번 투표")
@PostMapping("/vote1")
public ResponseEntity<String> dummyVote1Option(@RequestParam Long postId) {
int voteCount = dummyService.dummyVote1Option(postId);
return ResponseEntity.ok("더미 멤버 " + voteCount + "명 투표 완료");
}

//모든 더미 유저가 2번 옵션에 투표
@Operation(summary = "더미 멤버 2번 투표")
@PostMapping("/vote2")
public ResponseEntity<String> dummyVote2Option(@RequestParam Long postId) {
int voteCount = dummyService.dummyVote2Option(postId);
return ResponseEntity.ok("더미 멤버 " + voteCount + "명 투표 완료");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public int dummyVote(Long postId) {
PollOptions selectedOption = pollOptionsList.get(random.nextInt(pollOptionsList.size()));
PollVote pollVote = PollVote.builder()
.poll(post.getPoll())
.member(member)
.memberId(member.getMemberId())
.pollOptions(selectedOption)
.build();
pollVoteRepository.save(pollVote);
Expand All @@ -114,6 +114,70 @@ public int dummyVote(Long postId) {
return voteCount;
}

/**
* 모든 더미 유저가 1번 옵션에 투표
*/
@Transactional
public int dummyVote1Option(Long postId) {
Optional<Post> postOpt = postRepository.findById(postId);
if (postOpt.isEmpty()) return 0;
Post post = postOpt.get();
if (post.getPoll() == null) return 0;
List<PollOptions> pollOptionsList = pollOptionsRepository.findByPoll_PollId(post.getPoll().getPollId());
if (pollOptionsList.size() < 1) return 0;
PollOptions firstOption = pollOptionsList.get(0);
List<Member> dummyMembers = memberRepository.findAll().stream()
.filter(m -> m.getLoginId().startsWith("dummy") && m.getLoginId().endsWith("@test.com"))
.toList();
List<Long> votedMemberIds = pollVoteRepository.findMemberIdsByPoll(post.getPoll());
Set<Long> votedMemberIdSet = new HashSet<>(votedMemberIds);
int voteCount = 0;
for (Member member : dummyMembers) {
if (!votedMemberIdSet.contains(member.getMemberId())) {
PollVote pollVote = PollVote.builder()
.poll(post.getPoll())
.memberId(member.getMemberId())
.pollOptions(firstOption)
.build();
pollVoteRepository.save(pollVote);
voteCount++;
}
}
return voteCount;
}

/**
* 모든 더미 유저가 2번 옵션에 투표
*/
@Transactional
public int dummyVote2Option(Long postId) {
Optional<Post> postOpt = postRepository.findById(postId);
if (postOpt.isEmpty()) return 0;
Post post = postOpt.get();
if (post.getPoll() == null) return 0;
List<PollOptions> pollOptionsList = pollOptionsRepository.findByPoll_PollId(post.getPoll().getPollId());
if (pollOptionsList.size() < 2) return 0;
PollOptions secondOption = pollOptionsList.get(1);
List<Member> dummyMembers = memberRepository.findAll().stream()
.filter(m -> m.getLoginId().startsWith("dummy") && m.getLoginId().endsWith("@test.com"))
.toList();
List<Long> votedMemberIds = pollVoteRepository.findMemberIdsByPoll(post.getPoll());
Set<Long> votedMemberIdSet = new HashSet<>(votedMemberIds);
int voteCount = 0;
for (Member member : dummyMembers) {
if (!votedMemberIdSet.contains(member.getMemberId())) {
PollVote pollVote = PollVote.builder()
.poll(post.getPoll())
.memberId(member.getMemberId())
.pollOptions(secondOption)
.build();
pollVoteRepository.save(pollVote);
voteCount++;
}
}
return voteCount;
}

@Transactional
public int deleteDummyMembers() {
List<Member> dummyMembers = memberRepository.findAll().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ public Page<PostDto> getClosedPostsPaged(Pageable pageable, Long memberId) {
}

private Page<PostDto> getMyVotedPostsPagedByStatus(Pageable pageable, Long memberId, Poll.PollStatus status) {
List<PollVote> votes = pollVoteRepository.findByMember_MemberId(memberId);
List<PollVote> votes = pollVoteRepository.findByMemberId(memberId);
List<Long> pollIds = votes.stream().map(v -> v.getPoll().getPollId()).distinct().toList();
Page<Post> posts = (status == null)
? postRepository.findByPoll_PollIdIn(pollIds, pageable)
Expand Down