/* * Copyright 1998-2016 Linux.org.ru * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ru.org.linux.poll; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowCallbackHandler; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import ru.org.linux.topic.Topic; import ru.org.linux.topic.TopicDao; import ru.org.linux.user.User; import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; @Repository public class PollDao { private static final String queryPoolIdByTopicId = "SELECT polls.id FROM polls,topics WHERE topics.id=? AND polls.topic=topics.id"; private static final String queryCurrentPollId = "SELECT polls.id FROM polls,topics WHERE topics.id=polls.topic AND topics.moderate = 't' AND topics.deleted = 'f' AND topics.commitdate = (select max(commitdate) from topics where groupid=19387 AND moderate AND NOT deleted)"; private static final String queryPool = "SELECT topic, multiselect FROM polls WHERE id=?"; private static final String queryPollVariantsOrderById = "SELECT id, label, votes FROM polls_variants WHERE vote=? ORDER BY id"; private static final String queryPollVariantsOrderByVotes = "SELECT id, label, votes FROM polls_variants WHERE vote=? ORDER BY votes DESC, id"; private static final String queryPollUserVote = "select count(vote) from vote_users where userid=? and variant_id=?"; private static final String queryCountVotesUser = "SELECT count(vote) FROM vote_users WHERE vote=? AND userid=?"; private static final String queryCountVotesPool = "SELECT count(DISTINCT userid) FROM vote_users WHERE vote=?"; private static final String queryCountVotes = "SELECT sum(votes) as s FROM polls_variants WHERE vote=?"; private static final String updateVote = "UPDATE polls_variants SET votes=votes+1 WHERE id=? AND vote=?"; private static final String insertVoteUser = "INSERT INTO vote_users VALUES(?, ?, ?)"; private static final String insertPoll = "INSERT INTO polls (id, multiselect, topic) values (?,?,?)"; private static final String queryNextPollId = "select nextval('vote_id') as voteid"; private static final String insertNewVariant = "INSERT INTO polls_variants (id, vote, label) values (nextval('votes_id'), ?, ?)"; private static final String updateVariant = "UPDATE polls_variants SET label=? WHERE id=?"; private static final String deleteVariant = "DELETE FROM polls_variants WHERE id=?"; private static final String updateMultiselect = "UPDATE polls SET multiselect=? WHERE id=?"; private JdbcTemplate jdbcTemplate; @Autowired public void setDataSource(DataSource dataSource) { jdbcTemplate = new JdbcTemplate(dataSource); } /** * Получить список вариантов голосования по идентификатору голосования. * Список отсортирован по id варианта * * @param pollId идентификатор голосования * @return список вариантов голосования */ private List<PollVariant> getVoteDTO(int pollId) { return jdbcTemplate.query(queryPollVariantsOrderById, new RowMapper<PollVariant>() { @Override public PollVariant mapRow(ResultSet rs, int rowNum) throws SQLException { return new PollVariant(rs.getInt("id"), rs.getString("label")); } }, pollId); } /** * Возвращает кол-во проголосовавших пользователей в голосовании. * * @param poll объект голосования * @return кол-во проголосвавших пользователей */ public int getCountUsers(Poll poll) { return jdbcTemplate.queryForObject(queryCountVotesPool, Integer.class, poll.getId()); } /** * Возвращает кол-во голосов в голосовании. * * @param pollId идентификатор голосвания * @return кол-во голосов всего (несколько вариантов от одного пользователя суммируется" */ public int getVotersCount(int pollId) { return jdbcTemplate.queryForObject(queryCountVotes, Integer.class, pollId); } /** * Учет голосования, если user не голосовал в этом голосании, то * добавить его варианты в голосование и пометить, что он проголосовал. * * @param pollId идентификатор голосования * @param votes пункты за которые голосует пользователь * @param user голосующий пользователь * @throws BadVoteException неправильное голосование */ @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED) public void updateVotes(int pollId, int[] votes, User user) throws BadVoteException { if(jdbcTemplate.queryForObject(queryCountVotesUser, Integer.class, pollId, user.getId()) == 0){ for(int vote : votes) { if(jdbcTemplate.update(updateVote, vote, pollId) == 0) { throw new BadVoteException(); } jdbcTemplate.update(insertVoteUser, pollId, user.getId(), vote); } } } /** * Возвращает текщее авктивное голосование * @return id текущего голосования */ public int getCurrentPollId() { try { return jdbcTemplate.queryForObject(queryCurrentPollId, Integer.class); } catch (EmptyResultDataAccessException exception) { return 0; } } /** * Получить текщее голосование. * * @return текушие голование * @throws PollNotFoundException если голосование не существует */ public Poll getCurrentPoll() throws PollNotFoundException{ return getPoll(getCurrentPollId()); } /** * Получить голосование по идентификатору. * * @param pollId идентификатор голосования * @return объект голосование * @throws PollNotFoundException если голосование не существует */ public Poll getPoll(final int pollId) throws PollNotFoundException { int currentPollId = getCurrentPollId(); SqlRowSet rs = jdbcTemplate.queryForRowSet(queryPool, pollId); if (!rs.next()) { throw new PollNotFoundException(); } return new Poll( pollId, rs.getInt("topic"), rs.getBoolean("multiselect"), pollId == currentPollId, getVoteDTO(pollId) ); } /** * Получить голосование по идентификатору темы. * * @param topicId идентификатор темы голосования * @return объект голосования * @throws PollNotFoundException если голосование не существует */ public Poll getPollByTopicId(int topicId) throws PollNotFoundException { try { return getPoll(jdbcTemplate.queryForObject(queryPoolIdByTopicId, Integer.class, topicId)); } catch (EmptyResultDataAccessException exception) { throw new PollNotFoundException(); } } /** * Варианты опроса для ананимного пользователя * * @param poll опрос * @return неизменяемый список вариантов опроса */ public ImmutableList<PollVariantResult> getPollVariants(Poll poll) { return getPollVariants(poll, Poll.ORDER_ID, null); } /** * Варианты опроса для кокретного пользователя * * @param poll объект голосования * @param order порядок сортировки вариантов Poll.ORDER_ID и Poll.ORDER_VOTES * @param user для какого пользователя отдаем * @return неизменяемый список вариантов опроса */ public ImmutableList<PollVariantResult> getPollVariants(Poll poll, int order, final User user) { final List<PollVariantResult> variants = new ArrayList<>(); String query; switch (order) { case Poll.ORDER_ID: query = queryPollVariantsOrderById; break; case Poll.ORDER_VOTES: query = queryPollVariantsOrderByVotes; break; default: throw new RuntimeException("Oops!? order="+order); } jdbcTemplate.query(query, new RowCallbackHandler() { @Override public void processRow(ResultSet resultSet) throws SQLException { int id = resultSet.getInt("id"); String label = resultSet.getString("label"); int votes = resultSet.getInt("votes"); boolean voted = false; if(user != null && jdbcTemplate.queryForObject(queryPollUserVote, Integer.class, user.getId(), resultSet.getInt("id")) !=0) { voted = true; } variants.add(new PollVariantResult(id, label, votes, voted)); } }, poll.getId()); return ImmutableList.copyOf(variants); } /** * Создать голосование. * * @param pollList - Список вариантов ответов * @param multiSelect - true если голосование с мультивыбором * @param msgid - идентификатор темы. */ // call in @Transactional public void createPoll(List<String> pollList, boolean multiSelect, int msgid) { final int voteid = getNextPollId(); jdbcTemplate.update(insertPoll, voteid, multiSelect, msgid); try { final Poll poll = getPoll(voteid); for (String variant : pollList) { if (variant.trim().isEmpty()) { continue; } addNewVariant(poll, variant); } } catch (PollNotFoundException e) { throw new RuntimeException(e); } } /** * Получить идентификатор будущего голосования * * @return идентификатор будущего голосования */ private int getNextPollId() { return jdbcTemplate.queryForObject(queryNextPollId, Integer.class); } /** * Добавить новый вариант ответа в голосование. * * @param poll объект голосования * @param label - новый вариант ответа */ public void addNewVariant(Poll poll, String label) { jdbcTemplate.update( insertNewVariant, poll.getId(), label ); } /** * Изменить вариант голосования. * * @param var объект варианта голосования * @param label новое содержимое */ private void updateVariant(PollVariant var, String label) { if (var.getLabel().equals(label)) { return; } jdbcTemplate.update(updateVariant, label, var.getId()); } /** * Удалить вариант голосования * * @param variant объект варианта голосования */ void removeVariant(PollVariant variant) { jdbcTemplate.update(deleteVariant, variant.getId()); } /** * Обновить признак мультивыбора для опроса * @param poll опрос * @param multiselect признак мультивыбора */ private void updateMultiselect(Poll poll, boolean multiselect) { jdbcTemplate.update(updateMultiselect, multiselect, poll.getId()); } @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED) public boolean updatePoll(Topic message, List<PollVariant> newVariants, boolean multiselect) throws PollNotFoundException { boolean modified = false; final Poll poll = getPollByTopicId(message.getId()); ImmutableList<PollVariant> oldVariants = poll.getVariants(); Map<Integer, String> newMap = PollVariant.toMap(newVariants); for (final PollVariant var : oldVariants) { final String label = newMap.get(var.getId()); if (!TopicDao.equalStrings(var.getLabel(), label)) { modified = true; } if (Strings.isNullOrEmpty(label)) { removeVariant(var); } else { updateVariant(var, label); } } for (final PollVariant var : newVariants) { if (var.getId()==0 && !Strings.isNullOrEmpty(var.getLabel())) { modified = true; addNewVariant(poll, var.getLabel()); } } if (poll.isMultiSelect()!=multiselect) { modified = true; updateMultiselect(poll, multiselect); } return modified; } }