package org.kalipo.service; import org.joda.time.DateTime; import org.kalipo.aop.KalipoExceptionHandler; import org.kalipo.aop.RateLimit; import org.kalipo.config.Constants; import org.kalipo.config.ErrorCode; import org.kalipo.domain.Comment; import org.kalipo.domain.Vote; import org.kalipo.repository.CommentRepository; import org.kalipo.repository.VoteRepository; import org.kalipo.security.Privileges; import org.kalipo.security.SecurityUtils; import org.kalipo.service.util.Asserts; import org.kalipo.service.util.NumUtils; import org.kalipo.web.rest.KalipoException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Service; import javax.inject.Inject; import javax.validation.Valid; import java.util.List; import java.util.concurrent.Future; @Service @KalipoExceptionHandler public class VoteService { private final Logger log = LoggerFactory.getLogger(VoteService.class); @Inject private VoteRepository voteRepository; @Inject private ReputationModifierService reputationModifierService; @Inject private UserService userService; @Inject private NotificationService notificationService; @Inject private CommentRepository commentRepository; @Inject private WebSocketService webSocketService; @RateLimit public Vote create(@Valid Vote vote) throws KalipoException { Asserts.isNull(vote.getId(), "id"); if(vote.isLike()) { Asserts.hasPrivilege(Privileges.VOTE_UP); } else { Asserts.hasPrivilege(Privileges.VOTE_DOWN); } final String currentLogin = SecurityUtils.getCurrentLogin(); final boolean isSuperMod = userService.isSuperMod(currentLogin); // -- Quota int count = voteRepository.countWithinDateRange(currentLogin, DateTime.now().minusDays(1), DateTime.now()); int dailyLimit = 40; // todo senseful quota if (count >= dailyLimit && !isSuperMod) { throw new KalipoException(ErrorCode.METHOD_REQUEST_LIMIT_REACHED, "daily vote quota is " + dailyLimit); } // -- final Comment comment = commentRepository.findOne(vote.getCommentId()); Asserts.isNotNull(comment, "commentId"); // todo enable // if(currentLogin.equals(comment.getAuthorId())) { // throw new KalipoException(ErrorCode.CONSTRAINT_VIOLATED, "You can't vote on your comment"); // } reputationModifierService.onCommentVoting(vote, currentLogin); vote.setAuthorId(SecurityUtils.getCurrentLogin()); vote.setThreadId(comment.getThreadId()); // todo replace by scheduled job, with delay to prevent bandwaggon effect if (vote.isLike()) { log.info(String.format("User '%s' likes comment %s", SecurityUtils.getCurrentLogin(), comment.getId())); comment.setLikes(NumUtils.nullToZero(comment.getLikes()) + 1); notificationService.announceCommentLiked(comment); } else { log.info(String.format("User '%s' dislikes comment %s", SecurityUtils.getCurrentLogin(), comment.getId())); comment.setDislikes(NumUtils.nullToZero(comment.getDislikes()) + 1); // don't notify about dislikes, it's bad for the motivation and feeds a troll } commentRepository.save(comment); webSocketService.broadcast(comment.getThreadId(), WebSocketService.Type.VOTE, vote.anonymized()); return voteRepository.save(vote); } @Async public Future<List<Vote>> getVotesWithPages(String userId, int pageNumber) { PageRequest pageable = new PageRequest(pageNumber, 10, Sort.Direction.DESC, Constants.PARAM_CREATED_DATE); // todo this does not work return new AsyncResult<>(voteRepository.findByAuthorId(userId, pageable)); } }