feat(guild): invite state machine
Add surrogate PK (IDENTITY bigint) to GuildInvite for wire invite_id. Implement InviteAsync, CancelInviteAsync, RejectInviteAsync, ListOutgoingInvitesAsync, ListInvitedGuildsAsync in GuildService. Wire 5 endpoints: invite_user_list, invited_guild_list, invite, cancel_invite, reject_invite in GuildController. Migration: AddGuildInviteSurrogatePk — drops composite PK, adds Id IDENTITY column, adds unique index on (GuildId, InviteeViewerId). 6 integration tests in GuildInviteFlowTests all green. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -175,21 +175,72 @@ public sealed class GuildController : SVSimController
|
||||
=> Task.FromResult<ActionResult<GuildFriendListResponse>>(new GuildFriendListResponse { Friends = new() });
|
||||
|
||||
[HttpPost("invite_user_list")]
|
||||
public Task<ActionResult<GuildInviteUserListResponse>> InviteUserList([FromBody] BaseRequest _, CancellationToken ct)
|
||||
=> Task.FromResult<ActionResult<GuildInviteUserListResponse>>(new GuildInviteUserListResponse { Users = new() });
|
||||
public async Task<ActionResult<GuildInviteUserListResponse>> InviteUserList([FromBody] BaseRequest _, CancellationToken ct)
|
||||
{
|
||||
if (!TryGetViewerId(out var viewerId)) return Unauthorized();
|
||||
var entries = await _guild.ListOutgoingInvitesAsync(viewerId, ct);
|
||||
return new GuildInviteUserListResponse
|
||||
{
|
||||
Users = entries.Select(e => new GuildOutgoingInviteDto
|
||||
{
|
||||
ViewerId = e.InviteeViewerId,
|
||||
Name = e.Name,
|
||||
EmblemId = e.EmblemId,
|
||||
CountryCode = e.CountryCode,
|
||||
Rank = e.Rank,
|
||||
DegreeId = e.DegreeId,
|
||||
InviteId = e.InviteId,
|
||||
InviteTime = new DateTimeOffset(e.CreatedAt, TimeSpan.Zero).ToUnixTimeSeconds(),
|
||||
}).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost("invited_guild_list")]
|
||||
public Task<ActionResult<GuildInvitedGuildListResponse>> InvitedGuildList([FromBody] GuildInvitedGuildListRequest req, CancellationToken ct)
|
||||
=> Task.FromResult<ActionResult<GuildInvitedGuildListResponse>>(new GuildInvitedGuildListResponse { List = new() });
|
||||
public async Task<ActionResult<GuildInvitedGuildListResponse>> InvitedGuildList([FromBody] GuildInvitedGuildListRequest req, CancellationToken ct)
|
||||
{
|
||||
if (!TryGetViewerId(out var viewerId)) return Unauthorized();
|
||||
var entries = await _guild.ListInvitedGuildsAsync(viewerId, ct);
|
||||
return new GuildInvitedGuildListResponse
|
||||
{
|
||||
List = entries.Select(e => new GuildReceivedInviteDto
|
||||
{
|
||||
GuildId = e.Guild.GuildId,
|
||||
GuildName = e.Guild.Name,
|
||||
Description = e.Guild.Description,
|
||||
GuildEmblemId = e.Guild.EmblemId,
|
||||
JoinCondition = (int)e.Guild.JoinCondition,
|
||||
Activity = (int)e.Guild.Activity,
|
||||
MemberNum = e.MemberNum,
|
||||
LeaderName = e.LeaderName,
|
||||
LeaderViewerId = e.Guild.LeaderViewerId,
|
||||
InviteId = e.InviteId,
|
||||
}).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost("invite")]
|
||||
public Task<ActionResult<EmptyResponse>> Invite([FromBody] GuildInviteRequest req, CancellationToken ct) => Stub();
|
||||
public async Task<ActionResult<EmptyResponse>> Invite([FromBody] GuildInviteRequest req, CancellationToken ct)
|
||||
{
|
||||
if (!TryGetViewerId(out var viewerId)) return Unauthorized();
|
||||
var r = await _guild.InviteAsync(viewerId, req.InvitedViewerId, ct);
|
||||
return r.IsOk ? new EmptyResponse() : MapErrorToWire(r);
|
||||
}
|
||||
|
||||
[HttpPost("cancel_invite")]
|
||||
public Task<ActionResult<EmptyResponse>> CancelInvite([FromBody] GuildCancelInviteRequest req, CancellationToken ct) => Stub();
|
||||
public async Task<ActionResult<EmptyResponse>> CancelInvite([FromBody] GuildCancelInviteRequest req, CancellationToken ct)
|
||||
{
|
||||
if (!TryGetViewerId(out var viewerId)) return Unauthorized();
|
||||
var r = await _guild.CancelInviteAsync(viewerId, req.InviteId, ct);
|
||||
return r.IsOk ? new EmptyResponse() : MapErrorToWire(r);
|
||||
}
|
||||
|
||||
[HttpPost("reject_invite")]
|
||||
public Task<ActionResult<EmptyResponse>> RejectInvite([FromBody] GuildRejectInviteRequest req, CancellationToken ct) => Stub();
|
||||
public async Task<ActionResult<EmptyResponse>> RejectInvite([FromBody] GuildRejectInviteRequest req, CancellationToken ct)
|
||||
{
|
||||
if (!TryGetViewerId(out var viewerId)) return Unauthorized();
|
||||
var r = await _guild.RejectInviteAsync(viewerId, req.InviteId, ct);
|
||||
return r.IsOk ? new EmptyResponse() : MapErrorToWire(r);
|
||||
}
|
||||
|
||||
[HttpPost("join")]
|
||||
public Task<ActionResult<GuildJoinResponse>> Join([FromBody] GuildJoinEndpointRequest req, CancellationToken ct)
|
||||
|
||||
Reference in New Issue
Block a user