using Microsoft.EntityFrameworkCore; using RMuseum.Models.Divan; using RMuseum.Models.Divan.ViewModels; using RMuseum.Utils; using RSecurityBackend.Models.Generic; using System; using System.Linq; using System.Threading.Tasks; namespace RMuseum.Services.Implementation { /// /// IDivanService implementation /// public partial class DivanService : IDivanService { /// /// like / dislike / clear rating for a comment /// /// /// /// +1: like, -1: dislike, 0: remove previous rating /// public async Task> RateCommentAsync(Guid userId, int commentId, short value) { if (value != 1 && value != -1 && value != 0) { return new RServiceResult(null, "value != 1 && value != -1 && value != 0"); } try { var comment = await _context.DivanComments.Where(c => c.Id == commentId).SingleOrDefaultAsync(); if (comment == null) { return new RServiceResult(null, "comment == null"); } if (comment.UserId != null && comment.UserId == userId) { return new RServiceResult(null, "امکان رأی دادن به حاشیهٔ خودتان وجود ندارد."); } var oldRating = await _context.DivanCommentReactions.Where(c => c.DivanCommentId == commentId && c.UserId == userId).SingleOrDefaultAsync(); if (value == 0 && oldRating == null) { return await _CurrentCommentRatingResultAsync(comment, 0); } if (oldRating != null) { if (oldRating.Value == value) { return await _CurrentCommentRatingResultAsync(comment, oldRating.Value); } if (value == 0) { _context.Remove(oldRating); } else { oldRating.Value = value; oldRating.ReactionDate = DateTime.Now; _context.Update(oldRating); } await _context.SaveChangesAsync(); return await ReCalculateCommentSortKeyAsync(comment, value); } else { var rating = new DivanCommentReaction() { DivanCommentId = commentId, PoemId = comment.PoemId, UserId = userId, Value = value, ReactionDate = DateTime.Now, }; _context.Add(rating); await _context.SaveChangesAsync(); return await ReCalculateCommentSortKeyAsync(comment, value); } } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// get the requesting user's own rating values for all comments of a poem /// (meant to be merged client-side into an already fetched, anonymously cacheable comment list) /// /// /// /// public async Task> GetUserCommentRatings(int poemId, Guid userId) { try { var ratings = await _context.DivanCommentReactions.AsNoTracking() .Where(r => r.PoemId == poemId && r.UserId == userId) .Select(r => new DivanCommentUserRatingViewModel() { CommentId = r.DivanCommentId, Value = r.Value, }) .ToArrayAsync(); return new RServiceResult(ratings); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } private Task> _CurrentCommentRatingResultAsync(DivanComment comment, short currentUserRatingValue) { return Task.FromResult(new RServiceResult( new DivanCommentRatingResultViewModel() { CommentId = comment.Id, LikeCount = comment.LikeCount, DislikeCount = comment.DislikeCount, CurrentUserRatingValue = currentUserRatingValue, })); } private async Task> ReCalculateCommentSortKeyAsync(DivanComment comment, short currentUserRatingValue) { try { var likes = await _context.DivanCommentReactions.AsNoTracking().CountAsync(r => r.DivanCommentId == comment.Id && r.Value == 1); var dislikes = await _context.DivanCommentReactions.AsNoTracking().CountAsync(r => r.DivanCommentId == comment.Id && r.Value == -1); comment.LikeCount = likes; comment.DislikeCount = dislikes; comment.SortKey = DivanCommentRankingScoreCalculator.ComputeRankingScore(likes, dislikes); _context.Update(comment); await _context.SaveChangesAsync(); await CacheCleanForComment(comment.Id); return new RServiceResult( new DivanCommentRatingResultViewModel() { CommentId = comment.Id, LikeCount = comment.LikeCount, DislikeCount = comment.DislikeCount, CurrentUserRatingValue = currentUserRatingValue, }); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } } }