/// <summary> /// 获取时间轴卡片列表 /// </summary> /// <param name="before">起始位置</param> /// <param name="take">获取数量</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <returns><see cref="TimelineCardList"/></returns> public static async Task <TimelineCardList> GetCards(int before, int take, [Injected] KeylolDbContext dbContext, [Injected] CachedDataProvider cachedData) { var currentUserId = StateTreeHelper.GetCurrentUserId(); return(await TimelineCardList.CreateAsync(SubscriptionStream.Name(currentUserId), currentUserId, take, false, dbContext, cachedData, before)); }
/// <summary> /// 创建 <see cref="TimelinePage"/> /// </summary> /// <param name="currentUserId">当前登录用户 ID</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <returns><see cref="TimelinePage"/></returns> public static async Task <TimelinePage> CreateAsync(string currentUserId, KeylolDbContext dbContext, CachedDataProvider cachedData) { return(new TimelinePage { Cards = await TimelineCardList.CreateAsync(SubscriptionStream.Name(currentUserId), currentUserId, 18, false, dbContext, cachedData) }); }
protected override void OnStart(string[] args) { _mqChannel.BasicQos(0, 5, false); var consumer = new EventingBasicConsumer(_mqChannel); consumer.Received += async(sender, eventArgs) => { try { using (var streamReader = new StreamReader(new MemoryStream(eventArgs.Body))) using (var dbContext = new KeylolDbContext()) { var serializer = new JsonSerializer(); var requestDto = serializer.Deserialize <PushHubRequestDto>(new JsonTextReader(streamReader)); string entryId; FeedEntryType entryType; List <string> pointsToPush; List <UserToPush> usersToPush = new List <UserToPush>(); var count = 0; switch (requestDto.Type) { case ContentPushType.Article: { var article = await dbContext.Articles.Where(a => a.Id == requestDto.ContentId) .Select(a => new { a.Id, a.AuthorId, a.AttachedPoints, a.TargetPointId }).SingleAsync(); usersToPush.Add(new UserToPush { UserId = article.AuthorId, SubscriberTimelineReason = "author" }); entryId = article.Id; entryType = FeedEntryType.ArticleId; pointsToPush = Helpers.SafeDeserialize <List <string> >(article.AttachedPoints) ?? new List <string>(); pointsToPush.Add(article.TargetPointId); break; } case ContentPushType.Activity: { var activity = await dbContext.Activities.Where(a => a.Id == requestDto.ContentId) .Select(a => new { a.Id, a.AuthorId, a.AttachedPoints, a.TargetPointId }).SingleAsync(); usersToPush.Add(new UserToPush { UserId = activity.AuthorId, SubscriberTimelineReason = "author" }); entryId = activity.Id; entryType = FeedEntryType.ActivityId; pointsToPush = Helpers.SafeDeserialize <List <string> >(activity.AttachedPoints) ?? new List <string>(); pointsToPush.Add(activity.TargetPointId); if (await AddOrUpdateFeedAsync(LatestActivityStream.Name, entryId, entryType, null, dbContext)) { count++; } break; } case ContentPushType.Like: { var like = await dbContext.Likes.FindAsync(requestDto.ContentId); entryId = like.TargetId; pointsToPush = new List <string>(); usersToPush.Add(new UserToPush { UserId = like.OperatorId, UserTimelineReason = "like", SubscriberTimelineReason = $"like:{like.OperatorId}" }); switch (like.TargetType) { case LikeTargetType.Article: entryType = FeedEntryType.ArticleId; break; case LikeTargetType.Activity: entryType = FeedEntryType.ActivityId; break; default: _mqChannel.BasicAck(eventArgs.DeliveryTag, false); return; } break; } default: throw new ArgumentOutOfRangeException(); } foreach (var user in usersToPush) { if (await AddOrUpdateFeedAsync(UserStream.Name(user.UserId), entryId, entryType, user.UserTimelineReason, dbContext)) { count++; } foreach (var subscriberId in await dbContext.Subscriptions .Where(s => s.TargetId == user.UserId && s.TargetType == SubscriptionTargetType.User) .Select(s => s.SubscriberId).ToListAsync()) { if (await AddOrUpdateFeedAsync(SubscriptionStream.Name(subscriberId), entryId, entryType, user.SubscriberTimelineReason, dbContext)) { count++; } } } foreach (var pointId in pointsToPush) { var point = await dbContext.Points.Where(p => p.Id == pointId) .Select(p => new { p.Id }).SingleOrDefaultAsync(); if (point == null) { continue; } if (await AddOrUpdateFeedAsync(PointStream.Name(point.Id), entryId, entryType, null, dbContext)) { count++; } foreach (var subscriberId in await dbContext.Subscriptions .Where(s => s.TargetId == point.Id && s.TargetType == SubscriptionTargetType.Point) .Select(s => s.SubscriberId).ToListAsync()) { if (await AddOrUpdateFeedAsync(SubscriptionStream.Name(subscriberId), entryId, entryType, $"point:{point.Id}", dbContext)) { count++; } } } _mqChannel.BasicAck(eventArgs.DeliveryTag, false); _logger.Info($"{count} feeds pushed. Content: ({requestDto.Type}) {requestDto.ContentId}"); } } catch (Exception e) { _mqChannel.BasicNack(eventArgs.DeliveryTag, false, false); _logger.Fatal("RabbitMQ unhandled callback exception.", e); } }; _mqChannel.BasicConsume(MqClientProvider.PushHubRequestQueue, false, consumer); }
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(); } } }); }