Merge pull request #793 from nagisa77/codex/add-participant-info-to-vote-response

feat: expose poll option participants
This commit is contained in:
Tim
2025-08-30 12:03:34 +08:00
committed by GitHub
5 changed files with 52 additions and 9 deletions

View File

@@ -12,5 +12,5 @@ public class PollDto {
private List<String> options; private List<String> options;
private Map<Integer, Integer> votes; private Map<Integer, Integer> votes;
private LocalDateTime endTime; private LocalDateTime endTime;
private List<AuthorDto> participants; private Map<Integer, List<AuthorDto>> participants;
} }

View File

@@ -6,10 +6,12 @@ import com.openisle.dto.PostSummaryDto;
import com.openisle.dto.ReactionDto; import com.openisle.dto.ReactionDto;
import com.openisle.dto.LotteryDto; import com.openisle.dto.LotteryDto;
import com.openisle.dto.PollDto; import com.openisle.dto.PollDto;
import com.openisle.dto.AuthorDto;
import com.openisle.model.CommentSort; import com.openisle.model.CommentSort;
import com.openisle.model.Post; import com.openisle.model.Post;
import com.openisle.model.LotteryPost; import com.openisle.model.LotteryPost;
import com.openisle.model.PollPost; import com.openisle.model.PollPost;
import com.openisle.model.PollParticipant;
import com.openisle.model.User; import com.openisle.model.User;
import com.openisle.service.CommentService; import com.openisle.service.CommentService;
import com.openisle.service.ReactionService; import com.openisle.service.ReactionService;
@@ -19,6 +21,7 @@ import org.springframework.stereotype.Component;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** Mapper responsible for converting posts into DTOs. */ /** Mapper responsible for converting posts into DTOs. */
@@ -102,7 +105,10 @@ public class PostMapper {
p.setOptions(pp.getOptions()); p.setOptions(pp.getOptions());
p.setVotes(pp.getVotes()); p.setVotes(pp.getVotes());
p.setEndTime(pp.getEndTime()); p.setEndTime(pp.getEndTime());
p.setParticipants(pp.getParticipants().stream().map(userMapper::toAuthorDto).collect(Collectors.toList())); Map<Integer, List<AuthorDto>> participants = pp.getParticipants().stream()
.collect(Collectors.groupingBy(PollParticipant::getOptionIndex,
Collectors.mapping(ppart -> userMapper.toAuthorDto(ppart.getUser()), Collectors.toList())));
p.setParticipants(participants);
dto.setPoll(p); dto.setPoll(p);
} }
} }

View File

@@ -0,0 +1,33 @@
package com.openisle.model;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Represents a single vote in a poll, capturing which user selected which option.
*/
@Entity
@Table(name = "poll_participants")
@Getter
@Setter
@NoArgsConstructor
public class PollParticipant {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id", nullable = false)
private PollPost post;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@Column(name = "option_index", nullable = false)
private int optionIndex;
}

View File

@@ -30,11 +30,8 @@ public class PollPost extends Post {
@Column(name = "vote_count") @Column(name = "vote_count")
private Map<Integer, Integer> votes = new HashMap<>(); private Map<Integer, Integer> votes = new HashMap<>();
@ManyToMany @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
@JoinTable(name = "poll_participants", private Set<PollParticipant> participants = new HashSet<>();
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "user_id"))
private Set<User> participants = new HashSet<>();
@Column @Column
private LocalDateTime endTime; private LocalDateTime endTime;

View File

@@ -10,6 +10,7 @@ import com.openisle.model.Comment;
import com.openisle.model.NotificationType; import com.openisle.model.NotificationType;
import com.openisle.model.LotteryPost; import com.openisle.model.LotteryPost;
import com.openisle.model.PollPost; import com.openisle.model.PollPost;
import com.openisle.model.PollParticipant;
import com.openisle.repository.PostRepository; import com.openisle.repository.PostRepository;
import com.openisle.repository.LotteryPostRepository; import com.openisle.repository.LotteryPostRepository;
import com.openisle.repository.PollPostRepository; import com.openisle.repository.PollPostRepository;
@@ -293,13 +294,19 @@ public class PostService {
} }
User user = userRepository.findByUsername(username) User user = userRepository.findByUsername(username)
.orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found")); .orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found"));
if (post.getParticipants().contains(user)) { boolean alreadyVoted = post.getParticipants().stream()
.anyMatch(p -> p.getUser().equals(user));
if (alreadyVoted) {
throw new IllegalArgumentException("User already voted"); throw new IllegalArgumentException("User already voted");
} }
if (optionIndex < 0 || optionIndex >= post.getOptions().size()) { if (optionIndex < 0 || optionIndex >= post.getOptions().size()) {
throw new IllegalArgumentException("Invalid option"); throw new IllegalArgumentException("Invalid option");
} }
post.getParticipants().add(user); PollParticipant participant = new PollParticipant();
participant.setPost(post);
participant.setUser(user);
participant.setOptionIndex(optionIndex);
post.getParticipants().add(participant);
post.getVotes().merge(optionIndex, 1, Integer::sum); post.getVotes().merge(optionIndex, 1, Integer::sum);
return pollPostRepository.save(post); return pollPostRepository.save(post);
} }