feat(guild): breakup + emblem_list + others_info

Implements GuildService.BreakupAsync (soft-delete guild, hard-delete
dependents, clear viewer GuildId pointers). Wires /guild/breakup,
/guild/emblem_list, /guild/others_info in GuildController. Fixes
ToDetailDto to accept and populate leader_name from the member list.
Adds GuildServiceBreakupTests (3 tests: cascade delete, permission
guard, leader_name HTTP regression).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-27 12:32:04 -04:00
parent f7f68d03a7
commit cb5fc8d2d6
3 changed files with 210 additions and 12 deletions

View File

@@ -132,8 +132,24 @@ public sealed class GuildService : IGuildService
public Task<GuildOpResult> UpdateEmblemAsync(long viewerId, long emblemId, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task<GuildOpResult> BreakupAsync(long viewerId, CancellationToken ct = default)
=> throw new NotImplementedException();
public async Task<GuildOpResult> BreakupAsync(long viewerId, CancellationToken ct = default)
{
var membership = await _members.GetMembershipAsync(viewerId, ct);
if (membership is null) return new(GuildOpResultCode.NotInGuild);
if (membership.Role != GuildRole.Leader) return new(GuildOpResultCode.PermissionDenied);
var now = DateTime.UtcNow;
var members = await _members.ListByGuildAsync(membership.GuildId, ct);
foreach (var m in members) await _viewers.ClearGuildIdAsync(m.ViewerId, ct);
await _chatMessages.DeleteAllForGuildAsync(membership.GuildId, ct);
await _invites.DeleteAllForGuildAsync(membership.GuildId, ct);
await _joinRequests.DeleteAllForGuildAsync(membership.GuildId, ct);
await _members.DeleteAllForGuildAsync(membership.GuildId, ct);
await _guilds.MarkBrokenUpAsync(membership.GuildId, now, ct);
return GuildOpResult.Ok;
}
public Task<GuildOpResult> InviteAsync(long callerViewerId, long targetViewerId, CancellationToken ct = default)
=> throw new NotImplementedException();