♻️ Refactor the publisher loading in posts

This commit is contained in:
LittleSheep 2025-05-31 12:11:45 +08:00
parent 7656a8b298
commit c3095f2a9b
4 changed files with 97 additions and 21 deletions

View File

@ -3,6 +3,7 @@ using DysonNetwork.Sphere.Permission;
using DysonNetwork.Sphere.Publisher;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Query;
using NodaTime;
using Npgsql;
using Quartz;
@ -49,7 +50,7 @@ public class AppDatabase(
public DbSet<PublisherMember> PublisherMembers { get; set; }
public DbSet<PublisherSubscription> PublisherSubscriptions { get; set; }
public DbSet<PublisherFeature> PublisherFeatures { get; set; }
public DbSet<Post.Post> Posts { get; set; }
public DbSet<Post.PostReaction> PostReactions { get; set; }
public DbSet<Post.PostTag> PostTags { get; set; }
@ -64,10 +65,10 @@ public class AppDatabase(
public DbSet<Chat.Message> ChatMessages { get; set; }
public DbSet<Chat.RealtimeCall> ChatRealtimeCall { get; set; }
public DbSet<Chat.MessageReaction> ChatReactions { get; set; }
public DbSet<Sticker.Sticker> Stickers { get; set; }
public DbSet<Sticker.StickerPack> StickerPacks { get; set; }
public DbSet<Wallet.Wallet> Wallets { get; set; }
public DbSet<Wallet.WalletPocket> WalletPockets { get; set; }
public DbSet<Wallet.Order> PaymentOrders { get; set; }
@ -75,14 +76,14 @@ public class AppDatabase(
public DbSet<Developer.CustomApp> CustomApps { get; set; }
public DbSet<Developer.CustomAppSecret> CustomAppSecrets { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var dataSourceBuilder = new NpgsqlDataSourceBuilder(configuration.GetConnectionString("App"));
dataSourceBuilder.EnableDynamicJson();
dataSourceBuilder.UseNetTopologySuite();
dataSourceBuilder.UseNodaTime();
if (configuration.GetValue<bool>("Debug"))
optionsBuilder.EnableSensitiveDataLogging();
@ -104,20 +105,20 @@ public class AppDatabase(
{
Key = "default",
Nodes = new List<string>
{
"posts.create",
"posts.react",
"publishers.create",
"files.create",
"chat.create",
"chat.messages.create",
"chat.realtime.create",
"accounts.statuses.create",
"accounts.statuses.update",
"stickers.packs.create",
"stickers.create"
}.Select(permission =>
PermissionService.NewPermissionNode("group:default", "global", permission, true))
{
"posts.create",
"posts.react",
"publishers.create",
"files.create",
"chat.create",
"chat.messages.create",
"chat.realtime.create",
"accounts.statuses.create",
"accounts.statuses.update",
"stickers.packs.create",
"stickers.create"
}.Select(permission =>
PermissionService.NewPermissionNode("group:default", "global", permission, true))
.ToList()
});
await context.SaveChangesAsync(cancellationToken);
@ -359,4 +360,36 @@ public class AppDatabaseFactory : IDesignTimeDbContextFactory<AppDatabase>
var optionsBuilder = new DbContextOptionsBuilder<AppDatabase>();
return new AppDatabase(optionsBuilder.Options, configuration);
}
}
public static class OptionalQueryExtensions
{
public static IQueryable<T> If<T>(
this IQueryable<T> source,
bool condition,
Func<IQueryable<T>, IQueryable<T>> transform
)
{
return condition ? transform(source) : source;
}
public static IQueryable<T> If<T, TP>(
this IIncludableQueryable<T, TP> source,
bool condition,
Func<IIncludableQueryable<T, TP>, IQueryable<T>> transform
)
where T : class
{
return condition ? transform(source) : source;
}
public static IQueryable<T> If<T, TP>(
this IIncludableQueryable<T, IEnumerable<TP>> source,
bool condition,
Func<IIncludableQueryable<T, IEnumerable<TP>>, IQueryable<T>> transform
)
where T : class
{
return condition ? transform(source) : source;
}
}

View File

@ -40,7 +40,7 @@ public class PostController(
.FilterWithVisibility(currentUser, userFriends, isListing: true)
.CountAsync();
var posts = await query
.Include(e => e.Publisher)
.Include(e => e.RepliedPost)
.Include(e => e.ThreadedPost)
.Include(e => e.ForwardedPost)
.Include(e => e.Attachments)
@ -53,6 +53,7 @@ public class PostController(
.Take(take)
.ToListAsync();
posts = PostService.TruncatePostContent(posts);
posts = await ps.LoadPublishers(posts);
var postsId = posts.Select(e => e.Id).ToList();
var reactionMaps = await ps.GetPostReactionMapBatch(postsId);
@ -106,7 +107,6 @@ public class PostController(
.CountAsync();
var posts = await db.Posts
.Where(e => e.RepliedPostId == id)
.Include(e => e.Publisher)
.Include(e => e.ThreadedPost)
.Include(e => e.ForwardedPost)
.Include(e => e.Attachments)
@ -118,6 +118,7 @@ public class PostController(
.Take(take)
.ToListAsync();
posts = PostService.TruncatePostContent(posts);
posts = await ps.LoadPublishers(posts);
var postsId = posts.Select(e => e.Id).ToList();
var reactionMaps = await ps.GetPostReactionMapBatch(postsId);

View File

@ -271,6 +271,47 @@ public class PostService(
)
);
}
public async Task<List<Post>> LoadPublishers(List<Post> posts)
{
var publisherIds = posts
.Where(e => e.Publisher.AccountId != null)
.SelectMany<Post, Guid?>(e =>
[
e.Publisher.Id,
e.RepliedPost?.Publisher.Id,
e.ForwardedPost?.Publisher.Id,
e.ThreadedPost?.Publisher.Id
])
.Where(e => e != null)
.Distinct()
.ToList();
if (publisherIds.Count == 0) return posts;
var publishers = await db.Publishers
.Where(e => e.AccountId != null && publisherIds.Contains(e.AccountId.Value))
.ToDictionaryAsync(e => e.AccountId!.Value);
foreach (var post in posts)
{
if (publishers.TryGetValue(post.Publisher.Id, out var publisher))
post.Publisher = publisher;
if (post.RepliedPost?.Publisher.Id != null &&
publishers.TryGetValue(post.RepliedPost.Publisher.Id, out var repliedPublisher))
post.RepliedPost.Publisher = repliedPublisher;
if (post.ForwardedPost?.Publisher.Id != null &&
publishers.TryGetValue(post.ForwardedPost.Publisher.Id, out var forwardedPublisher))
post.ForwardedPost.Publisher = forwardedPublisher;
if (post.ThreadedPost?.Publisher.Id != null &&
publishers.TryGetValue(post.ThreadedPost.Publisher.Id, out var threadedPublisher))
post.ThreadedPost.Publisher = threadedPublisher;
}
return posts;
}
}
public static class PostQueryExtensions

View File

@ -23,6 +23,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcb0587797ea44bd6915ede69888c6766291038_003F55_003F277f2d4c_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkServiceCollectionExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4a28847852ee9ba45fd3107526c0a749a733bd4f4ebf33aa3c9a59737a3f758_003FEntityFrameworkServiceCollectionExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F832399abc13b45b6bdbabfa022e4a28487e00_003F7f_003F7aece4dd_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fadcd336f9cde4e71998a851d7eb945bb87e00_003F0c_003F96dc130e_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEvents_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003F20_003F86914b63_003FEvents_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F109293935a4844d5aa1610150b96edcde55000_003Fb7_003F8b7e5594_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb6f0571a6bc744b0b551fd4578292582e54c00_003Fbf_003F44af6d95_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>