/* * Symphony - A modern community (forum/SNS/blog) platform written in Java. * Copyright (C) 2012-2017, b3log.org & hacpai.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.b3log.symphony.service; import org.b3log.latke.ioc.inject.Inject; import org.b3log.latke.logging.Level; import org.b3log.latke.logging.Logger; import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.annotation.Transactional; import org.b3log.latke.service.ServiceException; import org.b3log.latke.service.annotation.Service; import org.b3log.symphony.model.Article; import org.b3log.symphony.model.Follow; import org.b3log.symphony.model.Tag; import org.b3log.symphony.repository.ArticleRepository; import org.b3log.symphony.repository.FollowRepository; import org.b3log.symphony.repository.TagRepository; import org.json.JSONObject; /** * Follow management service. * * @author <a href="http://88250.b3log.org">Liang Ding</a> * @version 1.3.1.2, Jan 18, 2017 * @since 0.2.5 */ @Service public class FollowMgmtService { /** * Logger. */ private static final Logger LOGGER = Logger.getLogger(FollowMgmtService.class.getName()); /** * Follow repository. */ @Inject private FollowRepository followRepository; /** * Tag repository. */ @Inject private TagRepository tagRepository; /** * Article repository. */ @Inject private ArticleRepository articleRepository; /** * The specified follower follows the specified following tag. * * @param followerId the specified follower id * @param followingTagId the specified following tag id * @throws ServiceException service exception */ @Transactional public void followTag(final String followerId, final String followingTagId) throws ServiceException { try { follow(followerId, followingTagId, Follow.FOLLOWING_TYPE_C_TAG); } catch (final RepositoryException e) { final String msg = "User[id=" + followerId + "] follows a tag[id=" + followingTagId + "] failed"; LOGGER.log(Level.ERROR, msg, e); throw new ServiceException(msg); } } /** * The specified follower follows the specified following user. * * @param followerId the specified follower id * @param followingUserId the specified following user id * @throws ServiceException service exception */ @Transactional public void followUser(final String followerId, final String followingUserId) throws ServiceException { try { follow(followerId, followingUserId, Follow.FOLLOWING_TYPE_C_USER); } catch (final RepositoryException e) { final String msg = "User[id=" + followerId + "] follows a user[id=" + followingUserId + "] failed"; LOGGER.log(Level.ERROR, msg, e); throw new ServiceException(msg); } } /** * The specified follower follows the specified following article. * * @param followerId the specified follower id * @param followingArticleId the specified following article id * @throws ServiceException service exception */ @Transactional public void followArticle(final String followerId, final String followingArticleId) throws ServiceException { try { follow(followerId, followingArticleId, Follow.FOLLOWING_TYPE_C_ARTICLE); } catch (final RepositoryException e) { final String msg = "User[id=" + followerId + "] follows an article[id=" + followingArticleId + "] failed"; LOGGER.log(Level.ERROR, msg, e); throw new ServiceException(msg); } } /** * The specified follower watches the specified following article. * * @param followerId the specified follower id * @param followingArticleId the specified following article id * @throws ServiceException service exception */ @Transactional public void watchArticle(final String followerId, final String followingArticleId) throws ServiceException { try { follow(followerId, followingArticleId, Follow.FOLLOWING_TYPE_C_ARTICLE_WATCH); } catch (final RepositoryException e) { final String msg = "User[id=" + followerId + "] watches an article[id=" + followingArticleId + "] failed"; LOGGER.log(Level.ERROR, msg, e); throw new ServiceException(msg); } } /** * The specified follower unfollows the specified following tag. * * @param followerId the specified follower id * @param followingTagId the specified following tag id * @throws ServiceException service exception */ @Transactional public void unfollowTag(final String followerId, final String followingTagId) throws ServiceException { try { unfollow(followerId, followingTagId, Follow.FOLLOWING_TYPE_C_TAG); } catch (final RepositoryException e) { final String msg = "User[id=" + followerId + "] unfollows a tag[id=" + followingTagId + "] failed"; LOGGER.log(Level.ERROR, msg, e); throw new ServiceException(msg); } } /** * The specified follower unfollows the specified following user. * * @param followerId the specified follower id * @param followingUserId the specified following user id * @throws ServiceException service exception */ @Transactional public void unfollowUser(final String followerId, final String followingUserId) throws ServiceException { try { unfollow(followerId, followingUserId, Follow.FOLLOWING_TYPE_C_USER); } catch (final RepositoryException e) { final String msg = "User[id=" + followerId + "] unfollows a user[id=" + followingUserId + "] failed"; LOGGER.log(Level.ERROR, msg, e); throw new ServiceException(msg); } } /** * The specified follower unfollows the specified following article. * * @param followerId the specified follower id * @param followingArticleId the specified following article id * @throws ServiceException service exception */ @Transactional public void unfollowArticle(final String followerId, final String followingArticleId) throws ServiceException { try { unfollow(followerId, followingArticleId, Follow.FOLLOWING_TYPE_C_ARTICLE); } catch (final RepositoryException e) { final String msg = "User[id=" + followerId + "] unfollows an article[id=" + followingArticleId + "] failed"; LOGGER.log(Level.ERROR, msg, e); throw new ServiceException(msg); } } /** * The specified follower unwatches the specified following article. * * @param followerId the specified follower id * @param followingArticleId the specified following article id * @throws ServiceException service exception */ @Transactional public void unwatchArticle(final String followerId, final String followingArticleId) throws ServiceException { try { unfollow(followerId, followingArticleId, Follow.FOLLOWING_TYPE_C_ARTICLE_WATCH); } catch (final RepositoryException e) { final String msg = "User[id=" + followerId + "] unwatches an article[id=" + followingArticleId + "] failed"; LOGGER.log(Level.ERROR, msg, e); throw new ServiceException(msg); } } /** * The specified follower follows the specified following entity with the specified following type. * * @param followerId the specified follower id * @param followingId the specified following entity id * @param followingType the specified following type * @throws RepositoryException repository exception */ private synchronized void follow(final String followerId, final String followingId, final int followingType) throws RepositoryException { if (followRepository.exists(followerId, followingId, followingType)) { return; } if (Follow.FOLLOWING_TYPE_C_TAG == followingType) { final JSONObject tag = tagRepository.get(followingId); if (null == tag) { LOGGER.log(Level.ERROR, "Not found tag [id={0}] to follow", followingId); return; } tag.put(Tag.TAG_FOLLOWER_CNT, tag.optInt(Tag.TAG_FOLLOWER_CNT) + 1); tag.put(Tag.TAG_RANDOM_DOUBLE, Math.random()); tagRepository.update(followingId, tag); } else if (Follow.FOLLOWING_TYPE_C_ARTICLE == followingType) { final JSONObject article = articleRepository.get(followingId); if (null == article) { LOGGER.log(Level.ERROR, "Not found article [id={0}] to follow", followingId); return; } article.put(Article.ARTICLE_COLLECT_CNT, article.optInt(Article.ARTICLE_COLLECT_CNT) + 1); articleRepository.update(followingId, article); } else if (Follow.FOLLOWING_TYPE_C_ARTICLE_WATCH == followingType) { final JSONObject article = articleRepository.get(followingId); if (null == article) { LOGGER.log(Level.ERROR, "Not found article [id={0}] to watch", followingId); return; } article.put(Article.ARTICLE_WATCH_CNT, article.optInt(Article.ARTICLE_WATCH_CNT) + 1); articleRepository.update(followingId, article); } final JSONObject follow = new JSONObject(); follow.put(Follow.FOLLOWER_ID, followerId); follow.put(Follow.FOLLOWING_ID, followingId); follow.put(Follow.FOLLOWING_TYPE, followingType); followRepository.add(follow); } /** * Removes a follow relationship. * * @param followerId the specified follower id * @param followingId the specified following entity id * @param followingType the specified following type * @throws RepositoryException repository exception */ public synchronized void unfollow(final String followerId, final String followingId, final int followingType) throws RepositoryException { followRepository.removeByFollowerIdAndFollowingId(followerId, followingId, followingType); if (Follow.FOLLOWING_TYPE_C_TAG == followingType) { final JSONObject tag = tagRepository.get(followingId); if (null == tag) { LOGGER.log(Level.ERROR, "Not found tag [id={0}] to unfollow", followingId); return; } tag.put(Tag.TAG_FOLLOWER_CNT, tag.optInt(Tag.TAG_FOLLOWER_CNT) - 1); if (tag.optInt(Tag.TAG_FOLLOWER_CNT) < 0) { tag.put(Tag.TAG_FOLLOWER_CNT, 0); } tag.put(Tag.TAG_RANDOM_DOUBLE, Math.random()); tagRepository.update(followingId, tag); } else if (Follow.FOLLOWING_TYPE_C_ARTICLE == followingType) { final JSONObject article = articleRepository.get(followingId); if (null == article) { LOGGER.log(Level.ERROR, "Not found article [id={0}] to unfollow", followingId); return; } article.put(Article.ARTICLE_COLLECT_CNT, article.optInt(Article.ARTICLE_COLLECT_CNT) - 1); if (article.optInt(Article.ARTICLE_COLLECT_CNT) < 0) { article.put(Article.ARTICLE_COLLECT_CNT, 0); } articleRepository.update(followingId, article); } else if (Follow.FOLLOWING_TYPE_C_ARTICLE_WATCH == followingType) { final JSONObject article = articleRepository.get(followingId); if (null == article) { LOGGER.log(Level.ERROR, "Not found article [id={0}] to unwatch", followingId); return; } article.put(Article.ARTICLE_WATCH_CNT, article.optInt(Article.ARTICLE_WATCH_CNT) - 1); if (article.optInt(Article.ARTICLE_WATCH_CNT) < 0) { article.put(Article.ARTICLE_WATCH_CNT, 0); } articleRepository.update(followingId, article); } } }