/// <summary>
        /// 创建 <see cref="LatestArticleList"/>
        /// </summary>
        /// <param name="pointId">据点 ID</param>
        /// <param name="page">分页页码</param>
        /// <param name="returnPageCount">是否返回总页数</param>
        /// <param name="returnFirstCoverImage">是否返回第一篇文章封面图</param>
        /// <param name="dbContext"><see cref="KeylolDbContext"/></param>
        /// <param name="cachedData"><see cref="CachedDataProvider"/></param>
        /// <returns>Item1 表示 <see cref="LatestArticleList"/>,Item2 表示总页数,Item3 表示第一篇文章封面图</returns>
        public static async Task <Tuple <LatestArticleList, int, string> > CreateAsync(string pointId, int page,
                                                                                       bool returnPageCount, bool returnFirstCoverImage, KeylolDbContext dbContext, CachedDataProvider cachedData)
        {
            var streamName     = PointStream.Name(pointId);
            var conditionQuery = from feed in dbContext.Feeds
                                 where feed.StreamName == streamName && feed.EntryType == FeedEntryType.ArticleId
                                 join article in dbContext.Articles on feed.Entry equals article.Id
                                 where article.Archived == ArchivedState.None && article.Rejected == false
                                 orderby feed.Id descending
                                 select article;
            var queryResult = await conditionQuery.Select(a => new
            {
                Count      = returnPageCount ? conditionQuery.Count() : 1,
                CoverImage = returnFirstCoverImage ? a.CoverImage : null,
                a.Id,
                a.SidForAuthor,
                a.Title,
                a.PublishTime,
                AuthorIdCode      = a.Author.IdCode,
                AuthorAvatarImage = a.Author.AvatarImage,
                AuthorUserName    = a.Author.UserName,
                PointIdCode       = a.TargetPoint.IdCode,
                PointType         = a.TargetPoint.Type,
                PointAvatarImage  = a.TargetPoint.AvatarImage,
                PointChineseName  = a.TargetPoint.ChineseName,
                PointEnglishName  = a.TargetPoint.EnglishName
            }).TakePage(page, RecordsPerPage).ToListAsync();

            var result = new LatestArticleList(queryResult.Count);

            foreach (var a in queryResult)
            {
                result.Add(new LatestArticle
                {
                    LikeCount         = await cachedData.Likes.GetTargetLikeCountAsync(a.Id, LikeTargetType.Article),
                    CommentCount      = await cachedData.ArticleComments.GetArticleCommentCountAsync(a.Id),
                    SidForAuthor      = a.SidForAuthor,
                    Title             = a.Title,
                    PublishTime       = a.PublishTime,
                    AuthorIdCode      = a.AuthorIdCode,
                    AuthorAvatarImage = a.AuthorAvatarImage,
                    AuthorUserName    = a.AuthorUserName,
                    PointIdCode       = a.PointIdCode,
                    PointType         = a.PointType,
                    PointAvatarImage  = a.PointAvatarImage,
                    PointChineseName  = a.PointChineseName,
                    PointEnglishName  = a.PointEnglishName
                });
            }
            var firstRecord = queryResult.FirstOrDefault(r => !string.IsNullOrWhiteSpace(r.CoverImage));

            return(new Tuple <LatestArticleList, int, string>(
                       result,
                       (int)Math.Ceiling(firstRecord?.Count / (double)RecordsPerPage ?? 1),
                       firstRecord?.CoverImage));
        }
        /// <summary>
        /// 创建 <see cref="FrontpagePage"/>
        /// </summary>
        /// <param name="point">已经查询好的据点对象</param>
        /// <param name="currentUserId">当前登录用户 ID</param>
        /// <param name="dbContext"><see cref="KeylolDbContext"/></param>
        /// <param name="cachedData"><see cref="CachedDataProvider"/></param>
        /// <returns><see cref="FrontpagePage"/></returns>
        public static async Task <FrontpagePage> CreateAsync(Models.Point point, string currentUserId,
                                                             KeylolDbContext dbContext, CachedDataProvider cachedData)
        {
            var frontPage = new FrontpagePage();

            if (point.Type == PointType.Game || point.Type == PointType.Hardware)
            {
                var briefReviews =
                    await BriefReviewList.CreateAsync(point, currentUserId, 1, true, dbContext, cachedData);

                frontPage.BriefReviewCount     = briefReviews.Item2;
                frontPage.BriefReviewPageCount = briefReviews.Item3;
                frontPage.BriefReviews         = briefReviews.Item1;
                frontPage.MediaHeaderImage     = point.MediaHeaderImage;
                frontPage.Media         = Helpers.SafeDeserialize <List <PointMedia> >(point.Media);
                frontPage.SimilarPoints =
                    await SimilarPointList.CreateAsync(point.Id, currentUserId, 1, dbContext, cachedData);

                frontPage.SelectedArticles =
                    await SelectedArticleList.CreateAsync(point.Id, 1, 4, currentUserId, dbContext, cachedData);
            }

            if (point.Type == PointType.Game)
            {
                frontPage.Platforms = await(from relationship in dbContext.PointRelationships
                                            where relationship.SourcePointId == point.Id &&
                                            relationship.Relationship == PointRelationshipType.Platform
                                            orderby relationship.Sid descending
                                            select relationship.TargetPoint.IdCode)
                                      .ToListAsync();

                #region 特性属性

                frontPage.MultiPlayer         = point.MultiPlayer ? true : (bool?)null;
                frontPage.SinglePlayer        = point.SinglePlayer ? true : (bool?)null;
                frontPage.Coop                = point.Coop ? true : (bool?)null;
                frontPage.CaptionsAvailable   = point.CaptionsAvailable ? true : (bool?)null;
                frontPage.CommentaryAvailable = point.CommentaryAvailable ? true : (bool?)null;
                frontPage.IncludeLevelEditor  = point.IncludeLevelEditor ? true : (bool?)null;
                frontPage.Achievements        = point.Achievements ? true : (bool?)null;
                frontPage.Cloud               = point.Cloud ? true : (bool?)null;
                frontPage.LocalCoop           = point.LocalCoop ? true : (bool?)null;
                frontPage.SteamTradingCards   = point.SteamTradingCards ? true : (bool?)null;
                frontPage.SteamWorkshop       = point.SteamWorkshop ? true : (bool?)null;
                frontPage.InAppPurchases      = point.InAppPurchases ? true : (bool?)null;

                #endregion

                frontPage.ChineseAvailability = Helpers.SafeDeserialize <ChineseAvailability>(point.ChineseAvailability);

                if (point.SteamAppId != null)
                {
                    frontPage.AddictedUsers =
                        await AddictedUserList.CreateAsync(currentUserId, point.SteamAppId, 1, dbContext, cachedData);
                }
            }
            else if (point.Type == PointType.Vendor)
            {
                var developerProducts = await ProductPointList.CreateAsync(currentUserId, point.Id,
                                                                           PointRelationshipType.Developer, 3, dbContext, cachedData);

                frontPage.DeveloperProducts     = developerProducts.Item1;
                frontPage.DeveloperProductCount = developerProducts.Item2;

                var publisherProducts = await ProductPointList.CreateAsync(currentUserId, point.Id,
                                                                           PointRelationshipType.Publisher, 3, dbContext, cachedData);

                frontPage.PublisherProducts     = publisherProducts.Item1;
                frontPage.PublisherProductCount = publisherProducts.Item2;

                var resellerProducts = await ProductPointList.CreateAsync(currentUserId, point.Id,
                                                                          PointRelationshipType.Reseller, 3, dbContext, cachedData);

                frontPage.ResellerProducts     = resellerProducts.Item1;
                frontPage.ResellerProductCount = resellerProducts.Item2;

                var manufacturerProducts = await ProductPointList.CreateAsync(currentUserId, point.Id,
                                                                              PointRelationshipType.Manufacturer, 3, dbContext, cachedData);

                frontPage.ManufacturerProducts     = manufacturerProducts.Item1;
                frontPage.ManufacturerProductCount = manufacturerProducts.Item2;
            }
            else if (point.Type == PointType.Category)
            {
                var tagProducts = await ProductPointList.CreateAsync(currentUserId, point.Id,
                                                                     PointRelationshipType.Tag, 3, dbContext, cachedData);

                frontPage.TagProducts     = tagProducts.Item1;
                frontPage.TagProductCount = tagProducts.Item2;

                var seriesProducts = await ProductPointList.CreateAsync(currentUserId, point.Id,
                                                                        PointRelationshipType.Series, 3, dbContext, cachedData);

                frontPage.SeriesProducts     = seriesProducts.Item1;
                frontPage.SeriesProductCount = seriesProducts.Item2;

                var genreProducts = await ProductPointList.CreateAsync(currentUserId, point.Id,
                                                                       PointRelationshipType.Genre, 3, dbContext, cachedData);

                frontPage.GenreProducts     = genreProducts.Item1;
                frontPage.GenreProductCount = genreProducts.Item2;
            }
            else if (point.Type == PointType.Platform)
            {
                var platformProducts = await ProductPointList.CreateAsync(currentUserId, point.Id,
                                                                          PointRelationshipType.Platform, 3, dbContext, cachedData);

                frontPage.PlatformProducts = platformProducts.Item1;
            }

            var latestArticles = await LatestArticleList.CreateAsync(point.Id, 1, true, true, dbContext, cachedData);

            frontPage.LatestArticleHeaderImage = latestArticles.Item3;
            frontPage.LatestArticlePageCount   = latestArticles.Item2;
            frontPage.LatestArticles           = latestArticles.Item1;

            return(frontPage);
        }