/// <summary> /// 创建 <see cref="OnSalePointList"/> /// </summary> /// <param name="currentUserId">当前登录用户 ID</param> /// <param name="page">分页页码</param> /// <param name="returnPageCount">是否返回总页数</param> /// <param name="returnFirstHeaderImage">是否返回第一个据点头部图</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <returns>Item1 表示 <see cref="OnSalePointList"/>,Item2 表示总页数,Item3 表示第一个据点头部图,Item4 表示第二个据点头部图</returns> public static async Task <Tuple <OnSalePointList, int, string, string> > CreateAsync(string currentUserId, int page, bool returnPageCount, bool returnFirstHeaderImage, KeylolDbContext dbContext, CachedDataProvider cachedData) { SteamCrawlerProvider.UpdateOnSalePoints(); var conditionQuery = from feed in dbContext.Feeds where feed.StreamName == OnSalePointStream.Name join point in dbContext.Points on feed.Entry equals point.Id orderby feed.Id descending select point; var queryResult = await conditionQuery.Select(p => new { Count = returnPageCount ? conditionQuery.Count() : 1, HeaderImage = returnFirstHeaderImage ? p.HeaderImage : null, p.Id, p.IdCode, p.ThumbnailImage, p.ChineseName, p.EnglishName, p.SteamPrice, p.SteamDiscountedPrice, p.SteamAppId }).TakePage(page, RecordsPerPage).ToListAsync(); var result = new OnSalePointList(queryResult.Count); foreach (var p in queryResult) { result.Add(new PointBasicInfo { IdCode = p.IdCode, ThumbnailImage = p.ThumbnailImage, ChineseName = p.ChineseName, EnglishName = p.EnglishName, AverageRating = (await cachedData.Points.GetRatingsAsync(p.Id)).AverageRating, SteamPrice = p.SteamPrice, SteamDiscountedPrice = p.SteamDiscountedPrice, InLibrary = string.IsNullOrWhiteSpace(currentUserId) || p.SteamAppId == null ? (bool?)null : await cachedData.Users.IsSteamAppInLibraryAsync(currentUserId, p.SteamAppId.Value) }); } var pointWithHeaders = queryResult.Where(r => !string.IsNullOrWhiteSpace(r.HeaderImage)).ToList(); var firstRecord = pointWithHeaders.FirstOrDefault(); var secondRecord = pointWithHeaders.Skip(1).FirstOrDefault(); return(new Tuple <OnSalePointList, int, string, string>( result, (int)Math.Ceiling(firstRecord?.Count / (double)RecordsPerPage ?? 1), firstRecord?.HeaderImage, secondRecord?.HeaderImage)); }
/// <summary> /// 创建 <see cref="CurrentUser"/> /// </summary> /// <param name="user">用户对象</param> /// <param name="userManager"><see cref="KeylolUserManager"/></param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="coupon"><see cref="CouponProvider"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <returns><see cref="CurrentUser"/></returns> public static async Task <CurrentUser> CreateAsync(KeylolUser user, KeylolUserManager userManager, KeylolDbContext dbContext, CouponProvider coupon, CachedDataProvider cachedData) { // 每日访问奖励 if (DateTime.Now.Date > user.LastDailyRewardTime.Date) { user.LastDailyRewardTime = DateTime.Now; user.FreeLike = 5; // 免费认可重置 try { await dbContext.SaveChangesAsync(); await coupon.UpdateAsync(user, CouponEvent.每日访问); } catch (DbUpdateConcurrencyException) { } } // Steam 游戏记录更新 SteamCrawlerProvider.UpdateUserSteamGameRecords(user.Id); // Steam 好友列表更新 SteamCrawlerProvider.UpdateUserSteamFrineds(user.Id); return(new CurrentUser { Id = user.Id, UserName = user.UserName, IdCode = user.IdCode, Roles = (await userManager.GetRolesAsync(user.Id)).ToList(), AvatarImage = user.AvatarImage, MessageCount = await cachedData.Messages.GetUserUnreadMessageCountAsync(user.Id), Coupon = user.Coupon, PreferredPointName = user.PreferredPointName, OpenInNewWindow = user.OpenInNewWindow }); }
/// <summary> /// 创建 <see cref="IntelPage"/> /// </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="IntelPage"/></returns> public static async Task <IntelPage> CreateAsync(Models.Point point, string currentUserId, KeylolDbContext dbContext, CachedDataProvider cachedData) { var intelPage = new IntelPage(); if (point.Type == PointType.Game || point.Type == PointType.Hardware) { var vendorPointsGroup = await(from relationship in dbContext.PointRelationships where relationship.SourcePointId == point.Id && (relationship.Relationship == PointRelationshipType.Developer || relationship.Relationship == PointRelationshipType.Manufacturer || relationship.Relationship == PointRelationshipType.Publisher || relationship.Relationship == PointRelationshipType.Reseller) group relationship.Relationship by new { relationship.TargetPoint.Id, relationship.TargetPoint.IdCode, relationship.TargetPoint.AvatarImage, relationship.TargetPoint.ChineseName, relationship.TargetPoint.EnglishName }) .ToListAsync(); intelPage.VendorPoints = vendorPointsGroup.Select(g => new VendorPoint { IdCode = g.Key.IdCode, AvatarImage = g.Key.AvatarImage, ChineseName = g.Key.ChineseName, EnglishName = g.Key.EnglishName, Roles = string.Join("、", g.Select(r => { switch (r) { case PointRelationshipType.Developer: return("开发厂"); case PointRelationshipType.Publisher: return("发行商"); case PointRelationshipType.Manufacturer: return("制造厂"); case PointRelationshipType.Reseller: return("代理商"); default: return(r.ToString()); } })) }).ToList(); var vendorIds = vendorPointsGroup.Select(g => g.Key.Id).ToList(); var vendorPointStaffQueryResult = await(from staff in dbContext.PointStaff where vendorIds.Contains(staff.PointId) select new { staff.Staff.Id, staff.Staff.HeaderImage, staff.Staff.IdCode, staff.Staff.AvatarImage, staff.Staff.UserName, PointChineseName = staff.Point.ChineseName, PointEnglishName = staff.Point.EnglishName }).ToListAsync(); intelPage.VenderPointStaff = new List <VendorPointStaff>(vendorPointStaffQueryResult.Count); foreach (var u in vendorPointStaffQueryResult) { intelPage.VenderPointStaff.Add(new VendorPointStaff { Id = u.Id, HeaderImage = u.HeaderImage, IdCode = u.IdCode, AvatarImage = u.AvatarImage, UserName = u.UserName, PointChineseName = u.PointChineseName, PointEnglishName = u.PointEnglishName, IsFriend = string.IsNullOrWhiteSpace(currentUserId) ? (bool?)null : await cachedData.Users.IsFriendAsync(currentUserId, u.Id), Subscribed = string.IsNullOrWhiteSpace(currentUserId) ? (bool?)null : await cachedData.Subscriptions.IsSubscribedAsync(currentUserId, u.Id, SubscriptionTargetType.User) }); } intelPage.PointStaff = await PointStaffList.CreateAsync(point.Id, currentUserId, dbContext, cachedData); intelPage.GenrePoints = await GenrePointList.CreateAsync(point.Id, currentUserId, dbContext, cachedData); intelPage.TagPoints = await TagPointList.CreateAsync(point.Id, currentUserId, dbContext, cachedData); } if (point.Type == PointType.Game) { intelPage.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 特性属性 intelPage.MultiPlayer = point.MultiPlayer ? true : (bool?)null; intelPage.SinglePlayer = point.SinglePlayer ? true : (bool?)null; intelPage.Coop = point.Coop ? true : (bool?)null; intelPage.CaptionsAvailable = point.CaptionsAvailable ? true : (bool?)null; intelPage.CommentaryAvailable = point.CommentaryAvailable ? true : (bool?)null; intelPage.IncludeLevelEditor = point.IncludeLevelEditor ? true : (bool?)null; intelPage.Achievements = point.Achievements ? true : (bool?)null; intelPage.Cloud = point.Cloud ? true : (bool?)null; intelPage.LocalCoop = point.LocalCoop ? true : (bool?)null; intelPage.SteamTradingCards = point.SteamTradingCards ? true : (bool?)null; intelPage.SteamWorkshop = point.SteamWorkshop ? true : (bool?)null; intelPage.InAppPurchases = point.InAppPurchases ? true : (bool?)null; #endregion intelPage.ChineseAvailability = Helpers.SafeDeserialize <ChineseAvailability>(point.ChineseAvailability); SteamCrawlerProvider.UpdateSteamSpyData(point.Id); intelPage.PublishDate = point.PublishDate; intelPage.PointCreateTime = point.CreateTime; intelPage.PreOrderDate = point.PreOrderDate; intelPage.ReleaseDate = point.ReleaseDate; if (point.SteamAppId != null) { intelPage.OwnerCount = point.OwnerCount; intelPage.OwnerCountVariance = point.OwnerCountVariance; intelPage.TotalPlayerCount = point.TotalPlayerCount; intelPage.TotalPlayerCountVariance = point.TotalPlayerCountVariance; intelPage.TwoWeekPlayerCount = point.TwoWeekPlayerCount; intelPage.TwoWeekPlayerCountVariance = point.TwoWeekPlayerCountVariance; intelPage.Ccu = point.Ccu; intelPage.AveragePlayedTime = point.AveragePlayedTime; intelPage.TwoWeekAveragePlayedTime = point.TwoWeekAveragePlayedTime; intelPage.MedianPlayedTime = point.MedianPlayedTime; } } return(intelPage); }
private static void AutoSubscribe(string userId) { Task.Run(async() => { using (var dbContext = new KeylolDbContext()) using (var userManager = new KeylolUserManager(dbContext)) { var redis = Global.Container.GetInstance <RedisProvider>(); var cachedData = new CachedDataProvider(dbContext, redis); if (await SteamCrawlerProvider.UpdateUserSteamGameRecordsAsync(userId, dbContext, userManager, redis, cachedData)) { var games = await(from record in dbContext.UserSteamGameRecords where record.UserId == userId join point in dbContext.Points on record.SteamAppId equals point.SteamAppId orderby record.TotalPlayedTime select new { PointId = point.Id, record.LastPlayTime }).ToListAsync(); var gamePointIds = games.Select(g => g.PointId).ToList(); var mostPlayedPointIds = gamePointIds.Take(3).ToList(); var recentPlayedPointIds = games.Where(g => !mostPlayedPointIds.Contains(g.PointId)) .OrderByDescending(g => g.LastPlayTime) .Select(g => g.PointId).Take(3).ToList(); var categoryPointIds = await(from relationship in dbContext.PointRelationships where gamePointIds.Contains(relationship.SourcePointId) && (relationship.Relationship == PointRelationshipType.Tag || relationship.Relationship == PointRelationshipType.Series) group 1 by relationship.TargetPointId into g orderby g.Count() descending select g.Key).Take(3).ToListAsync(); var pointIds = mostPlayedPointIds.Concat(recentPlayedPointIds).Concat(categoryPointIds).ToList(); foreach (var pointId in pointIds) { await cachedData.Subscriptions.AddAsync(userId, pointId, SubscriptionTargetType.Point); } var pointFeedStreams = pointIds.Select(PointStream.Name).ToList(); var feeds = await(from feed in dbContext.Feeds where pointFeedStreams.Contains(feed.StreamName) orderby feed.Id descending group new { feed.Id, feed.StreamName } by new { feed.Entry, feed.EntryType } into g orderby g.Max(f => f.Id) select g).Take(120).ToListAsync(); var subscriptionStream = SubscriptionStream.Name(userId); foreach (var feed in feeds) { var properties = new SubscriptionStream.FeedProperties { Reasons = feed.Select(f => f.StreamName.Split(':')[1]).Distinct() .Select(id => $"point:{id}").ToList() }; dbContext.Feeds.Add(new Models.Feed { StreamName = subscriptionStream, Entry = feed.Key.Entry, EntryType = feed.Key.EntryType, Properties = JsonConvert.SerializeObject(properties) }); } await dbContext.SaveChangesAsync(); } } }); }
/// <summary> /// 创建 <see cref="PointBasicInfo"/> /// </summary> /// <param name="currentUserId">当前登录用户 ID</param> /// <param name="point">据点对象</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <returns><see cref="PointBasicInfo"/></returns> public static async Task <PointBasicInfo> CreateAsync(string currentUserId, Point point, KeylolDbContext dbContext, CachedDataProvider cachedData) { var basicInfo = new PointBasicInfo { Id = point.Id, IdCode = point.IdCode, Logo = point.Logo, ThemeColor = point.ThemeColor, LightThemeColor = point.LightThemeColor, Type = point.Type, HeaderImage = point.HeaderImage, AvatarImage = point.AvatarImage, ChineseName = point.ChineseName, EnglishName = point.EnglishName, Subscribed = string.IsNullOrWhiteSpace(currentUserId) ? (bool?)null : await cachedData.Subscriptions.IsSubscribedAsync(currentUserId, point.Id, SubscriptionTargetType.Point) }; if (point.Type == PointType.Game || point.Type == PointType.Hardware) { var rating = await cachedData.Points.GetRatingsAsync(point.Id); basicInfo.OneStarCount = rating.OneStarCount; basicInfo.TwoStarCount = rating.TwoStarCount; basicInfo.ThreeStarCount = rating.ThreeStarCount; basicInfo.FourStarCount = rating.FourStarCount; basicInfo.FiveStarCount = rating.FiveStarCount; basicInfo.AverageRating = rating.AverageRating; basicInfo.Categories = (await(from relationship in dbContext.PointRelationships where relationship.SourcePointId == point.Id && relationship.Relationship == PointRelationshipType.Tag select new { relationship.TargetPoint.IdCode, relationship.TargetPoint.ChineseName, relationship.TargetPoint.EnglishName }) .ToListAsync()) .Select(p => new PointBasicInfo { IdCode = p.IdCode, ChineseName = string.IsNullOrWhiteSpace(p.ChineseName) ? p.EnglishName : p.ChineseName }) .ToList(); basicInfo.Vendors = (await(from relationship in dbContext.PointRelationships where relationship.SourcePointId == point.Id && relationship.Relationship == PointRelationshipType.Developer || relationship.Relationship == PointRelationshipType.Manufacturer select new { relationship.TargetPoint.IdCode, relationship.TargetPoint.ChineseName, relationship.TargetPoint.EnglishName }) .ToListAsync()) .Select(p => new PointBasicInfo { IdCode = p.IdCode, ChineseName = p.ChineseName, EnglishName = p.EnglishName }) .ToList(); basicInfo.TitleCoverImage = point.TitleCoverImage; #region 商店信息 basicInfo.SteamAppId = point.SteamAppId; basicInfo.SteamPrice = point.SteamPrice; basicInfo.SteamDiscountedPrice = point.SteamDiscountedPrice; basicInfo.SonkwoProductId = point.SonkwoProductId; basicInfo.SonkwoPrice = point.SonkwoPrice; basicInfo.SonkwoDiscountedPrice = point.SonkwoDiscountedPrice; basicInfo.UplayLink = EmptyToNull(point.UplayLink); basicInfo.UplayPrice = EmptyToNull(point.UplayPrice); basicInfo.XboxLink = EmptyToNull(point.XboxLink); basicInfo.XboxPrice = EmptyToNull(point.XboxPrice); basicInfo.PlayStationLink = EmptyToNull(point.PlayStationLink); basicInfo.PlayStationPrice = EmptyToNull(point.PlayStationPrice); basicInfo.OriginLink = EmptyToNull(point.OriginLink); basicInfo.OriginPrice = EmptyToNull(point.OriginPrice); basicInfo.WindowsStoreLink = EmptyToNull(point.WindowsStoreLink); basicInfo.WindowsStorePrice = EmptyToNull(point.WindowsStorePrice); basicInfo.AppStoreLink = EmptyToNull(point.AppStoreLink); basicInfo.AppStorePrice = EmptyToNull(point.AppStorePrice); basicInfo.GooglePlayLink = EmptyToNull(point.GooglePlayLink); basicInfo.GooglePlayPrice = EmptyToNull(point.GooglePlayPrice); basicInfo.GogLink = EmptyToNull(point.GogLink); basicInfo.GogPrice = EmptyToNull(point.GogPrice); basicInfo.BattleNetLink = EmptyToNull(point.BattleNetLink); basicInfo.BattleNetPrice = EmptyToNull(point.BattleNetPrice); #endregion } else { var childPoints = await dbContext.PointRelationships .Where(r => r.TargetPointId == point.Id) .GroupBy(r => r.SourcePointId) .Select(g => g.Key) .ToListAsync(); basicInfo.ProductCount = childPoints.Count; double totalScore = 0; var validRatingCount = 0; foreach (var childPointId in childPoints) { var rating = (await cachedData.Points.GetRatingsAsync(childPointId)).AverageRating; if (rating == null) { continue; } totalScore += rating.Value; validRatingCount++; } if (validRatingCount > 0) { basicInfo.AverageRating = Math.Round(totalScore / validRatingCount, 1); } } if (point.Type == PointType.Game && point.SteamAppId != null) { SteamCrawlerProvider.UpdatePointPrice(point.Id); basicInfo.TotalPlayedTime = string.IsNullOrWhiteSpace(currentUserId) ? null : (await dbContext.UserSteamGameRecords .Where(r => r.UserId == currentUserId && r.SteamAppId == point.SteamAppId) .SingleOrDefaultAsync())?.TotalPlayedTime; basicInfo.KeylolAveragePlayedTime = Math.Round(await dbContext.UserSteamGameRecords .Where(r => r.SteamAppId == point.SteamAppId) .Select(r => r.TotalPlayedTime) .DefaultIfEmpty() .AverageAsync(), 1); basicInfo.InLibrary = string.IsNullOrWhiteSpace(currentUserId) ? (bool?)null : await cachedData.Users.IsSteamAppInLibraryAsync(currentUserId, point.SteamAppId.Value); } return(basicInfo); }