:3
This commit is contained in:
@@ -0,0 +1,768 @@
|
||||
using LiteDB;
|
||||
using static Mocha2023.Classes.DBs.DBClasses.ClubDBClasses;
|
||||
|
||||
namespace Mocha2023.Classes.DBs
|
||||
{
|
||||
public static class ClubDB
|
||||
{
|
||||
private const int MaxCreatedClubsPerAccount = 100;
|
||||
private static readonly object Sync = new();
|
||||
private static readonly LiteDatabase Database =
|
||||
new(Path.Combine(Program.dataDir, "DBs", "Clubs.db"));
|
||||
|
||||
public static readonly ILiteCollection<ClubRecord> Clubs =
|
||||
Database.GetCollection<ClubRecord>("Clubs");
|
||||
public static readonly ILiteCollection<ClubMembershipRecord> Memberships =
|
||||
Database.GetCollection<ClubMembershipRecord>("Memberships");
|
||||
public static readonly ILiteCollection<HomeClubRecord> HomeClubs =
|
||||
Database.GetCollection<HomeClubRecord>("HomeClubs");
|
||||
|
||||
public static readonly IReadOnlyList<string> CategoryTags = new[]
|
||||
{
|
||||
"Social",
|
||||
"Creative",
|
||||
"Competitive",
|
||||
"Casual",
|
||||
"Educational",
|
||||
"Entertainment",
|
||||
"Lifestyle"
|
||||
};
|
||||
|
||||
private static bool _legacyDataRepaired;
|
||||
|
||||
static ClubDB()
|
||||
{
|
||||
Clubs.EnsureIndex(value => value.CreatorAccountId);
|
||||
Clubs.EnsureIndex(value => value.Name);
|
||||
Clubs.EnsureIndex(value => value.State);
|
||||
Memberships.EnsureIndex(value => value.ClubId);
|
||||
Memberships.EnsureIndex(value => value.AccountId);
|
||||
Memberships.EnsureIndex(value => value.MembershipType);
|
||||
|
||||
RepairLegacyData();
|
||||
}
|
||||
|
||||
public static void RepairLegacyData()
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
if (_legacyDataRepaired)
|
||||
return;
|
||||
|
||||
int clubsChanged = 0;
|
||||
int membershipsChanged = 0;
|
||||
int malformedMembershipsRemoved = 0;
|
||||
|
||||
foreach (ClubMembershipRecord membership in Memberships.FindAll().ToList())
|
||||
{
|
||||
if (membership.ClubId <= 0 || membership.AccountId <= 0)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(membership.Id))
|
||||
Memberships.Delete(membership.Id);
|
||||
malformedMembershipsRemoved++;
|
||||
continue;
|
||||
}
|
||||
|
||||
string expectedId = MembershipId(membership.ClubId, membership.AccountId);
|
||||
if (string.Equals(membership.Id, expectedId, StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
ClubMembershipRecord? existing = Memberships.FindById(expectedId);
|
||||
ClubMembershipType mergedType = existing == null
|
||||
? membership.MembershipType
|
||||
: (ClubMembershipType)Math.Max(
|
||||
(int)existing.MembershipType,
|
||||
(int)membership.MembershipType);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(membership.Id))
|
||||
Memberships.Delete(membership.Id);
|
||||
|
||||
UpsertMembershipUnsafe(
|
||||
membership.ClubId,
|
||||
membership.AccountId,
|
||||
mergedType);
|
||||
membershipsChanged++;
|
||||
}
|
||||
|
||||
foreach (ClubRecord club in Clubs.FindAll().ToList())
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
club.Name ??= string.Empty;
|
||||
club.Description ??= string.Empty;
|
||||
club.MainImageName = NormalizeImageName(club.MainImageName);
|
||||
club.AdditionalImages ??= new List<string>();
|
||||
club.CategoryTags ??= new List<string>();
|
||||
|
||||
string resolvedPrimary = ResolveLegacyPrimaryCategory(
|
||||
club.PrimaryCategory,
|
||||
club.CategoryTags);
|
||||
List<string> normalizedTags = NormalizeTags(
|
||||
new[] { resolvedPrimary }
|
||||
.Concat(club.CategoryTags))
|
||||
.ToList();
|
||||
|
||||
if (normalizedTags.Count == 0)
|
||||
normalizedTags.Add(resolvedPrimary);
|
||||
|
||||
if (!string.Equals(
|
||||
club.PrimaryCategory,
|
||||
resolvedPrimary,
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[CLUB CATEGORY REPAIR] club={club.ClubId} " +
|
||||
$"old={club.PrimaryCategory ?? "null"} new={resolvedPrimary} " +
|
||||
$"tags={string.Join(',', club.CategoryTags)}");
|
||||
club.PrimaryCategory = resolvedPrimary;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!club.CategoryTags.SequenceEqual(
|
||||
normalizedTags,
|
||||
StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
club.CategoryTags = normalizedTags;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
ClubMembershipRecord? creatorMembership =
|
||||
Memberships.FindById(MembershipId(
|
||||
club.ClubId,
|
||||
club.CreatorAccountId));
|
||||
|
||||
if (club.CreatorAccountId > 0 &&
|
||||
creatorMembership?.MembershipType != ClubMembershipType.Creator)
|
||||
{
|
||||
UpsertMembershipUnsafe(
|
||||
club.ClubId,
|
||||
club.CreatorAccountId,
|
||||
ClubMembershipType.Creator);
|
||||
membershipsChanged++;
|
||||
}
|
||||
|
||||
int actualMemberCount = Memberships.Count(value =>
|
||||
value.ClubId == club.ClubId &&
|
||||
value.MembershipType >= ClubMembershipType.Member);
|
||||
|
||||
if (club.MemberCount != actualMemberCount)
|
||||
{
|
||||
club.MemberCount = actualMemberCount;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
club.UpdatedAt = DateTime.UtcNow;
|
||||
Clubs.Update(club);
|
||||
clubsChanged++;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (HomeClubRecord home in HomeClubs.FindAll().ToList())
|
||||
{
|
||||
ClubRecord? club = Clubs.FindById(home.ClubId);
|
||||
if (club == null)
|
||||
{
|
||||
HomeClubs.Delete(home.AccountId);
|
||||
continue;
|
||||
}
|
||||
|
||||
ClubMembershipRecord? membership =
|
||||
Memberships.FindById(MembershipId(
|
||||
home.ClubId,
|
||||
home.AccountId));
|
||||
|
||||
if (membership == null ||
|
||||
membership.MembershipType < ClubMembershipType.Member)
|
||||
{
|
||||
ClubMembershipType type =
|
||||
club.CreatorAccountId == home.AccountId
|
||||
? ClubMembershipType.Creator
|
||||
: ClubMembershipType.Member;
|
||||
UpsertMembershipUnsafe(home.ClubId, home.AccountId, type);
|
||||
membershipsChanged++;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ClubRecord club in Clubs.FindAll().ToList())
|
||||
{
|
||||
int actualMemberCount = Memberships.Count(value =>
|
||||
value.ClubId == club.ClubId &&
|
||||
value.MembershipType >= ClubMembershipType.Member);
|
||||
|
||||
if (club.MemberCount == actualMemberCount)
|
||||
continue;
|
||||
|
||||
club.MemberCount = actualMemberCount;
|
||||
club.UpdatedAt = DateTime.UtcNow;
|
||||
Clubs.Update(club);
|
||||
clubsChanged++;
|
||||
}
|
||||
|
||||
_legacyDataRepaired = true;
|
||||
|
||||
Console.WriteLine(
|
||||
$"[CLUB REPAIR] clubs={clubsChanged} " +
|
||||
$"memberships={membershipsChanged} " +
|
||||
$"malformedRemoved={malformedMembershipsRemoved}");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryCreate(
|
||||
long creatorAccountId,
|
||||
string name,
|
||||
string description,
|
||||
string? mainImageName,
|
||||
ClubVisibility visibility,
|
||||
ClubJoinability joinability,
|
||||
bool allowJuniors,
|
||||
int minLevel,
|
||||
ClubType clubType,
|
||||
IEnumerable<string>? categoryTags,
|
||||
out ClubRecord? club,
|
||||
out string? error)
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
club = null;
|
||||
error = null;
|
||||
|
||||
string normalizedName = name.Trim();
|
||||
|
||||
ClubRecord? existingOwnedClub = Clubs.FindAll().FirstOrDefault(value =>
|
||||
value.CreatorAccountId == creatorAccountId &&
|
||||
value.State != ClubState.MarkedForDelete &&
|
||||
string.Equals(
|
||||
value.Name,
|
||||
normalizedName,
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (existingOwnedClub != null)
|
||||
{
|
||||
UpsertMembershipUnsafe(
|
||||
existingOwnedClub.ClubId,
|
||||
creatorAccountId,
|
||||
ClubMembershipType.Creator);
|
||||
|
||||
club = existingOwnedClub;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Clubs.Count(value =>
|
||||
value.CreatorAccountId == creatorAccountId &&
|
||||
value.State != ClubState.MarkedForDelete) >=
|
||||
MaxCreatedClubsPerAccount)
|
||||
{
|
||||
error = "club_creation_limit";
|
||||
return false;
|
||||
}
|
||||
|
||||
List<string> tags = NormalizeTags(categoryTags).ToList();
|
||||
if (tags.Count == 0)
|
||||
tags.Add("Social");
|
||||
|
||||
club = new ClubRecord
|
||||
{
|
||||
ClubId = NextClubId(),
|
||||
Name = normalizedName,
|
||||
Description = description.Trim(),
|
||||
MainImageName = NormalizeImageName(mainImageName),
|
||||
MemberCount = 1,
|
||||
PrimaryCategory = tags[0],
|
||||
Visibility = visibility,
|
||||
Joinability = joinability,
|
||||
AllowJuniors = allowJuniors,
|
||||
MinLevel = Math.Clamp(minLevel, 0, 50),
|
||||
ClubType = clubType,
|
||||
CreatorAccountId = creatorAccountId,
|
||||
CategoryTags = tags,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
Clubs.Insert(club);
|
||||
UpsertMembershipUnsafe(
|
||||
club.ClubId,
|
||||
creatorAccountId,
|
||||
ClubMembershipType.Creator);
|
||||
HomeClubs.Upsert(new HomeClubRecord
|
||||
{
|
||||
AccountId = creatorAccountId,
|
||||
ClubId = club.ClubId,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static ClubRecord? Get(long clubId)
|
||||
{
|
||||
lock (Sync)
|
||||
return Clubs.FindById(clubId);
|
||||
}
|
||||
|
||||
public static List<ClubRecord> GetCreatedBy(long accountId)
|
||||
{
|
||||
lock (Sync)
|
||||
return Clubs.Find(value =>
|
||||
value.CreatorAccountId == accountId &&
|
||||
value.State != ClubState.MarkedForDelete)
|
||||
.OrderByDescending(value => value.CreatedAt)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static List<ClubRecord> GetMemberClubs(long accountId)
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
HashSet<long> clubIds = Memberships.Find(value =>
|
||||
value.AccountId == accountId &&
|
||||
value.MembershipType >= ClubMembershipType.Member)
|
||||
.Select(value => value.ClubId)
|
||||
.ToHashSet();
|
||||
|
||||
long? homeClubId = HomeClubs.FindById(accountId)?.ClubId;
|
||||
|
||||
return Clubs.Find(value => value.State == ClubState.Active)
|
||||
.Where(value =>
|
||||
clubIds.Contains(value.ClubId) ||
|
||||
value.CreatorAccountId == accountId ||
|
||||
homeClubId == value.ClubId)
|
||||
.OrderByDescending(value => value.MemberCount)
|
||||
.ThenBy(value => value.Name)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<ClubRecord> Search(
|
||||
string? category,
|
||||
string? query,
|
||||
int sort,
|
||||
int skip,
|
||||
int count) =>
|
||||
SearchWithTotal(category, query, sort, skip, count).Results;
|
||||
|
||||
public static (List<ClubRecord> Results, int TotalResults) SearchWithTotal(
|
||||
string? category,
|
||||
string? query,
|
||||
int sort,
|
||||
int skip,
|
||||
int count)
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
IEnumerable<ClubRecord> clubs = Clubs.Find(value =>
|
||||
value.State == ClubState.Active &&
|
||||
value.Visibility == ClubVisibility.Public);
|
||||
|
||||
string? normalizedCategory = NormalizeCategory(category);
|
||||
if (!string.IsNullOrWhiteSpace(normalizedCategory))
|
||||
{
|
||||
clubs = clubs.Where(value =>
|
||||
ClubMatchesCategory(value, normalizedCategory));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
string trimmedQuery = query.Trim();
|
||||
clubs = clubs.Where(value =>
|
||||
(value.Name ?? string.Empty).Contains(
|
||||
trimmedQuery,
|
||||
StringComparison.OrdinalIgnoreCase) ||
|
||||
(value.Description ?? string.Empty).Contains(
|
||||
trimmedQuery,
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
clubs = sort switch
|
||||
{
|
||||
1 => clubs.OrderByDescending(value => value.CreatedAt),
|
||||
2 => clubs.OrderBy(value => value.CreatedAt),
|
||||
_ => clubs.OrderByDescending(value => value.MemberCount)
|
||||
.ThenBy(value => value.Name)
|
||||
};
|
||||
|
||||
List<ClubRecord> all = clubs.ToList();
|
||||
List<ClubRecord> page = all
|
||||
.Skip(Math.Max(0, skip))
|
||||
.Take(Math.Clamp(count, 1, 100))
|
||||
.ToList();
|
||||
|
||||
return (page, all.Count);
|
||||
}
|
||||
}
|
||||
|
||||
public static ClubMembershipRecord? GetMembership(long clubId, long accountId)
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
ClubMembershipRecord? membership =
|
||||
Memberships.FindById(MembershipId(clubId, accountId));
|
||||
|
||||
if (membership != null)
|
||||
return membership;
|
||||
|
||||
ClubRecord? club = Clubs.FindById(clubId);
|
||||
if (club?.CreatorAccountId != accountId)
|
||||
return null;
|
||||
|
||||
UpsertMembershipUnsafe(
|
||||
clubId,
|
||||
accountId,
|
||||
ClubMembershipType.Creator);
|
||||
return Memberships.FindById(MembershipId(clubId, accountId));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<ClubMembershipRecord> GetMemberships(
|
||||
long clubId,
|
||||
bool includePending = false)
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
return Memberships.Find(value => value.ClubId == clubId &&
|
||||
(includePending || value.MembershipType >= ClubMembershipType.Member))
|
||||
.OrderByDescending(value => value.MembershipType)
|
||||
.ThenBy(value => value.CreatedAt)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SetMembership(
|
||||
long clubId,
|
||||
long accountId,
|
||||
ClubMembershipType membershipType)
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
if (Clubs.FindById(clubId) == null)
|
||||
return false;
|
||||
UpsertMembershipUnsafe(clubId, accountId, membershipType);
|
||||
RefreshMemberCountUnsafe(clubId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool RemoveMembership(long clubId, long accountId)
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
ClubRecord? club = Clubs.FindById(clubId);
|
||||
if (club == null || club.CreatorAccountId == accountId)
|
||||
return false;
|
||||
Memberships.Delete(MembershipId(clubId, accountId));
|
||||
if (HomeClubs.FindById(accountId)?.ClubId == clubId)
|
||||
HomeClubs.Delete(accountId);
|
||||
RefreshMemberCountUnsafe(clubId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SetHomeClub(long accountId, long? clubId)
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
if (!clubId.HasValue || clubId.Value <= 0)
|
||||
return HomeClubs.Delete(accountId);
|
||||
|
||||
ClubMembershipRecord? membership = GetMembership(clubId.Value, accountId);
|
||||
if (membership == null ||
|
||||
membership.MembershipType < ClubMembershipType.Member)
|
||||
return false;
|
||||
|
||||
HomeClubs.Upsert(new HomeClubRecord
|
||||
{
|
||||
AccountId = accountId,
|
||||
ClubId = clubId.Value,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static ClubRecord? GetHomeClub(long accountId)
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
HomeClubRecord? home = HomeClubs.FindById(accountId);
|
||||
return home == null ? null : Clubs.FindById(home.ClubId);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Update(ClubRecord club)
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
club.Name = (club.Name ?? string.Empty).Trim();
|
||||
club.Description = (club.Description ?? string.Empty).Trim();
|
||||
club.MainImageName = NormalizeImageName(club.MainImageName);
|
||||
|
||||
string primary = NormalizeCategory(club.PrimaryCategory) ??
|
||||
NormalizeTags(club.CategoryTags).FirstOrDefault() ??
|
||||
"Social";
|
||||
List<string> normalizedTags = NormalizeTags(
|
||||
new[] { primary }
|
||||
.Concat(club.CategoryTags ?? new List<string>()))
|
||||
.ToList();
|
||||
if (normalizedTags.Count == 0)
|
||||
normalizedTags.Add(primary);
|
||||
|
||||
club.PrimaryCategory = primary;
|
||||
club.CategoryTags = normalizedTags;
|
||||
club.MinLevel = Math.Clamp(club.MinLevel, 0, 50);
|
||||
club.UpdatedAt = DateTime.UtcNow;
|
||||
return Clubs.Update(club);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Delete(long clubId, long accountId)
|
||||
{
|
||||
lock (Sync)
|
||||
{
|
||||
ClubRecord? club = Clubs.FindById(clubId);
|
||||
if (club == null || club.CreatorAccountId != accountId)
|
||||
return false;
|
||||
|
||||
Clubs.Delete(clubId);
|
||||
foreach (ClubMembershipRecord membership in
|
||||
Memberships.Find(value => value.ClubId == clubId).ToList())
|
||||
{
|
||||
Memberships.Delete(membership.Id);
|
||||
}
|
||||
foreach (HomeClubRecord home in
|
||||
HomeClubs.Find(value => value.ClubId == clubId).ToList())
|
||||
{
|
||||
HomeClubs.Delete(home.AccountId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static ClubSummaryDto ToSummary(ClubRecord club) =>
|
||||
ToSummary(club, null);
|
||||
|
||||
public static ClubSummaryDto ToSummary(
|
||||
ClubRecord club,
|
||||
long? viewerAccountId)
|
||||
{
|
||||
ClubMembershipType membershipType = ClubMembershipType.None;
|
||||
if (viewerAccountId.HasValue)
|
||||
{
|
||||
membershipType = GetMembership(
|
||||
club.ClubId,
|
||||
viewerAccountId.Value)?.MembershipType ??
|
||||
(club.CreatorAccountId == viewerAccountId.Value
|
||||
? ClubMembershipType.Creator
|
||||
: ClubMembershipType.None);
|
||||
}
|
||||
|
||||
string primary = NormalizeCategory(club.PrimaryCategory) ??
|
||||
NormalizeTags(club.CategoryTags).FirstOrDefault() ??
|
||||
"Social";
|
||||
List<string> normalizedTags = NormalizeTags(
|
||||
new[] { primary }
|
||||
.Concat(club.CategoryTags ?? new List<string>()))
|
||||
.ToList();
|
||||
if (normalizedTags.Count == 0)
|
||||
normalizedTags.Add(primary);
|
||||
|
||||
return new ClubSummaryDto
|
||||
{
|
||||
ClubId = club.ClubId,
|
||||
Name = club.Name ?? string.Empty,
|
||||
Description = club.Description ?? string.Empty,
|
||||
MainImageName = NormalizeImageName(club.MainImageName),
|
||||
State = (int)club.State,
|
||||
MemberCount = club.MemberCount,
|
||||
PrimaryCategory = primary,
|
||||
CategoryTags = normalizedTags,
|
||||
Visibility = (int)club.Visibility,
|
||||
Joinability = (int)club.Joinability,
|
||||
AllowJuniors = club.AllowJuniors,
|
||||
MinLevel = club.MinLevel,
|
||||
ClubChatEnabled = club.ClubChatEnabled,
|
||||
ClubhouseRoomId = club.ClubhouseRoomId,
|
||||
ClubType = (int)club.ClubType,
|
||||
CreatorAccountId = club.CreatorAccountId,
|
||||
ClubMembershipType = (int)membershipType,
|
||||
CreatedAt = club.CreatedAt,
|
||||
UpdatedAt = club.UpdatedAt
|
||||
};
|
||||
}
|
||||
|
||||
public static ClubMembershipDto ToMembership(ClubMembershipRecord membership) => new()
|
||||
{
|
||||
AccountId = membership.AccountId,
|
||||
ClubId = membership.ClubId,
|
||||
ClubMembershipType = (int)membership.MembershipType,
|
||||
CreatedAt = membership.CreatedAt
|
||||
};
|
||||
|
||||
public static ClubDetailsDto ToDetails(ClubRecord club, long? viewerAccountId)
|
||||
{
|
||||
int myType = viewerAccountId.HasValue
|
||||
? (int)(GetMembership(club.ClubId, viewerAccountId.Value)?.MembershipType ??
|
||||
ClubMembershipType.None)
|
||||
: (int)ClubMembershipType.None;
|
||||
|
||||
return new ClubDetailsDto
|
||||
{
|
||||
Club = ToSummary(club, viewerAccountId),
|
||||
CategoryTags = ToSummary(club).CategoryTags,
|
||||
AdditionalImages = (club.AdditionalImages ?? new List<string>())
|
||||
.Select((imageName, imageIndex) => new ClubImageDto
|
||||
{
|
||||
ImageName = imageName,
|
||||
ImageIndex = imageIndex
|
||||
})
|
||||
.ToList(),
|
||||
CreatorPermissions = Permissions(club.ClubId, ClubMembershipType.Creator),
|
||||
CoOwnerPermissions = Permissions(club.ClubId, ClubMembershipType.CoOwner),
|
||||
ModeratorPermissions = Permissions(club.ClubId, ClubMembershipType.Moderator),
|
||||
MyMembershipType = myType
|
||||
};
|
||||
}
|
||||
|
||||
public static ClubPermissionsDto Permissions(
|
||||
long clubId,
|
||||
ClubMembershipType membershipType)
|
||||
{
|
||||
bool moderator = membershipType >= ClubMembershipType.Moderator;
|
||||
bool coOwner = membershipType >= ClubMembershipType.CoOwner;
|
||||
return new ClubPermissionsDto
|
||||
{
|
||||
ClubId = clubId,
|
||||
MembershipType = (int)membershipType,
|
||||
CanInvite = moderator,
|
||||
CanKick = moderator,
|
||||
CanBan = moderator,
|
||||
CanModify = coOwner,
|
||||
CanManageRoles = coOwner,
|
||||
CanManageClubhouse = coOwner
|
||||
};
|
||||
}
|
||||
|
||||
private static string ResolveLegacyPrimaryCategory(
|
||||
string? storedPrimary,
|
||||
IEnumerable<string>? storedTags)
|
||||
{
|
||||
string? primary = NormalizeCategory(storedPrimary);
|
||||
List<string> tags = NormalizeTags(storedTags).ToList();
|
||||
|
||||
if (primary == null)
|
||||
return tags.FirstOrDefault() ?? "Social";
|
||||
|
||||
if (string.Equals(primary, "Social", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string? recovered = tags.FirstOrDefault(value =>
|
||||
!string.Equals(value, "Social", StringComparison.OrdinalIgnoreCase));
|
||||
if (!string.IsNullOrWhiteSpace(recovered))
|
||||
return recovered;
|
||||
}
|
||||
|
||||
return primary;
|
||||
}
|
||||
|
||||
private static bool ClubMatchesCategory(
|
||||
ClubRecord club,
|
||||
string normalizedCategory)
|
||||
{
|
||||
string primary = NormalizeCategory(club.PrimaryCategory) ?? "Social";
|
||||
if (string.Equals(primary, normalizedCategory,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
|
||||
return (club.CategoryTags ?? new List<string>())
|
||||
.Select(NormalizeCategory)
|
||||
.Any(tag => string.Equals(
|
||||
tag,
|
||||
normalizedCategory,
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static long NextClubId() =>
|
||||
Clubs.Count() == 0 ? 1 : Clubs.Max(value => value.ClubId) + 1;
|
||||
|
||||
private static string MembershipId(long clubId, long accountId) =>
|
||||
$"{clubId}:{accountId}";
|
||||
|
||||
private static void UpsertMembershipUnsafe(
|
||||
long clubId,
|
||||
long accountId,
|
||||
ClubMembershipType membershipType)
|
||||
{
|
||||
string id = MembershipId(clubId, accountId);
|
||||
ClubMembershipRecord record = Memberships.FindById(id) ??
|
||||
new ClubMembershipRecord
|
||||
{
|
||||
Id = id,
|
||||
ClubId = clubId,
|
||||
AccountId = accountId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
record.Id = id;
|
||||
record.ClubId = clubId;
|
||||
record.AccountId = accountId;
|
||||
record.MembershipType = membershipType;
|
||||
record.UpdatedAt = DateTime.UtcNow;
|
||||
Memberships.Upsert(record);
|
||||
}
|
||||
|
||||
private static void RefreshMemberCountUnsafe(long clubId)
|
||||
{
|
||||
ClubRecord? club = Clubs.FindById(clubId);
|
||||
if (club == null)
|
||||
return;
|
||||
club.MemberCount = Memberships.Count(value =>
|
||||
value.ClubId == clubId &&
|
||||
value.MembershipType >= ClubMembershipType.Member);
|
||||
club.UpdatedAt = DateTime.UtcNow;
|
||||
Clubs.Update(club);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> NormalizeTags(IEnumerable<string>? tags) =>
|
||||
(tags ?? Array.Empty<string>())
|
||||
.Select(NormalizeCategory)
|
||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||
.Select(value => value!)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Take(3);
|
||||
|
||||
private static string? NormalizeCategory(string? category)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(category))
|
||||
return null;
|
||||
|
||||
return category.Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"social" => "Social",
|
||||
"hangout" => "Social",
|
||||
"creative" => "Creative",
|
||||
"art" => "Creative",
|
||||
"competitive" => "Competitive",
|
||||
"casual" => "Casual",
|
||||
"games" => "Casual",
|
||||
"roleplay" => "Casual",
|
||||
"educational" => "Educational",
|
||||
"education" => "Educational",
|
||||
"entertainment" => "Entertainment",
|
||||
"quest" => "Entertainment",
|
||||
"music" => "Entertainment",
|
||||
"lifestyle" => "Lifestyle",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private static string NormalizeImageName(string? imageName)
|
||||
{
|
||||
string normalized = (imageName ?? string.Empty).Trim().Replace('\\', '/');
|
||||
if (string.IsNullOrWhiteSpace(normalized) ||
|
||||
normalized.Length > 260 ||
|
||||
normalized.Contains("..", StringComparison.Ordinal) ||
|
||||
Uri.TryCreate(normalized, UriKind.Absolute, out _))
|
||||
return "DefaultClubImage2k.jpg";
|
||||
return normalized.TrimStart('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user