Swarm/DysonNetwork.Sphere/Publisher/PublisherService.cs
LittleSheep d7d4fde06a Wallet, payment, developer apps, feature flags of publishers
♻️ Simplified the permission check of chat room, realm, publishers
2025-05-15 00:26:15 +08:00

191 lines
6.5 KiB
C#

using DysonNetwork.Sphere.Post;
using DysonNetwork.Sphere.Storage;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using NodaTime;
namespace DysonNetwork.Sphere.Publisher;
public class PublisherService(AppDatabase db, FileService fs, IMemoryCache cache)
{
public async Task<Publisher> CreateIndividualPublisher(
Account.Account account,
string? name,
string? nick,
string? bio,
CloudFile? picture,
CloudFile? background
)
{
var publisher = new Publisher
{
Type = PublisherType.Individual,
Name = name ?? account.Name,
Nick = nick ?? account.Nick,
Bio = bio ?? account.Profile.Bio,
Picture = picture ?? account.Profile.Picture,
Background = background ?? account.Profile.Background,
AccountId = account.Id,
Members = new List<PublisherMember>
{
new()
{
AccountId = account.Id,
Role = PublisherMemberRole.Owner,
JoinedAt = Instant.FromDateTimeUtc(DateTime.UtcNow)
}
}
};
db.Publishers.Add(publisher);
await db.SaveChangesAsync();
if (publisher.Picture is not null) await fs.MarkUsageAsync(publisher.Picture, 1);
if (publisher.Background is not null) await fs.MarkUsageAsync(publisher.Background, 1);
return publisher;
}
public async Task<Publisher> CreateOrganizationPublisher(
Realm.Realm realm,
Account.Account account,
string? name,
string? nick,
string? bio,
CloudFile? picture,
CloudFile? background
)
{
var publisher = new Publisher
{
Type = PublisherType.Organizational,
Name = name ?? realm.Slug,
Nick = nick ?? realm.Name,
Bio = bio ?? realm.Description,
Picture = picture ?? realm.Picture,
Background = background ?? realm.Background,
RealmId = realm.Id,
Members = new List<PublisherMember>
{
new()
{
AccountId = account.Id,
Role = PublisherMemberRole.Owner,
JoinedAt = Instant.FromDateTimeUtc(DateTime.UtcNow)
}
}
};
db.Publishers.Add(publisher);
await db.SaveChangesAsync();
if (publisher.Picture is not null) await fs.MarkUsageAsync(publisher.Picture, 1);
if (publisher.Background is not null) await fs.MarkUsageAsync(publisher.Background, 1);
return publisher;
}
public class PublisherStats
{
public int PostsCreated { get; set; }
public int StickerPacksCreated { get; set; }
public int StickersCreated { get; set; }
public int UpvoteReceived { get; set; }
public int DownvoteReceived { get; set; }
public int SubscribersCount { get; set; }
}
private const string PublisherStatsCacheKey = "PublisherStats_{0}";
private const string PublisherFeatureCacheKey = "PublisherFeature_{0}_{1}";
public async Task<PublisherStats?> GetPublisherStats(string name)
{
var cacheKey = string.Format(PublisherStatsCacheKey, name);
if (cache.TryGetValue(cacheKey, out PublisherStats? stats))
return stats;
var publisher = await db.Publishers.FirstOrDefaultAsync(e => e.Name == name);
if (publisher is null) return null;
var postsCount = await db.Posts.Where(e => e.Publisher.Id == publisher.Id).CountAsync();
var postsUpvotes = await db.PostReactions
.Where(r => r.Post.Publisher.Id == publisher.Id && r.Attitude == PostReactionAttitude.Positive)
.CountAsync();
var postsDownvotes = await db.PostReactions
.Where(r => r.Post.Publisher.Id == publisher.Id && r.Attitude == PostReactionAttitude.Negative)
.CountAsync();
var stickerPacksId = await db.StickerPacks.Where(e => e.Publisher.Id == publisher.Id).Select(e => e.Id)
.ToListAsync();
var stickerPacksCount = stickerPacksId.Count;
var stickersCount = await db.Stickers.Where(e => stickerPacksId.Contains(e.PackId)).CountAsync();
var subscribersCount = await db.PublisherSubscriptions.Where(e => e.PublisherId == publisher.Id).CountAsync();
stats = new PublisherStats
{
PostsCreated = postsCount,
StickerPacksCreated = stickerPacksCount,
StickersCreated = stickersCount,
UpvoteReceived = postsUpvotes,
DownvoteReceived = postsDownvotes,
SubscribersCount = subscribersCount,
};
cache.Set(cacheKey, stats, TimeSpan.FromMinutes(5));
return stats;
}
public async Task SetFeatureFlag(Guid publisherId, string flag)
{
var featureFlag = await db.PublisherFeatures
.FirstOrDefaultAsync(f => f.PublisherId == publisherId && f.Flag == flag);
if (featureFlag == null)
{
featureFlag = new PublisherFeature
{
PublisherId = publisherId,
Flag = flag,
};
db.PublisherFeatures.Add(featureFlag);
}
else
{
featureFlag.ExpiredAt = SystemClock.Instance.GetCurrentInstant();
}
await db.SaveChangesAsync();
cache.Remove(string.Format(PublisherFeatureCacheKey, publisherId, flag));
}
public async Task<bool> HasFeature(Guid publisherId, string flag)
{
var cacheKey = string.Format(PublisherFeatureCacheKey, publisherId, flag);
if (cache.TryGetValue(cacheKey, out bool isEnabled))
return isEnabled;
var now = SystemClock.Instance.GetCurrentInstant();
var featureFlag = await db.PublisherFeatures
.FirstOrDefaultAsync(f =>
f.PublisherId == publisherId && f.Flag == flag &&
(f.ExpiredAt == null || f.ExpiredAt > now)
);
if (featureFlag is not null) isEnabled = true;
cache.Set(cacheKey, isEnabled, TimeSpan.FromMinutes(5));
return isEnabled;
}
public async Task<bool> IsMemberWithRole(Guid publisherId, Guid accountId, PublisherMemberRole requiredRole)
{
var member = await db.Publishers
.Where(p => p.Id == publisherId)
.SelectMany(p => p.Members)
.FirstOrDefaultAsync(m => m.AccountId == accountId);
return member != null && member.Role >= requiredRole;
}
}