Add Friends/Social & Multiplayer systems - 95 total endpoints
- Implemented Friends/Social Service (11 endpoints) * Friend management (list, add, accept, remove) * User search and invitations * Gift sending and claiming * Clubs/Teams system - Implemented Multiplayer Service (12 endpoints) * Matchmaking (queue, status, leave) * Race sessions (create, join, ready, details) * Ghost data (upload, download) * Race results (submit, view) * Competitive rankings (rating, leaderboard) - Added database entities: * Friends, FriendInvitations, Gifts * Clubs, ClubMembers * MatchmakingQueues, RaceSessions, RaceParticipants * GhostData, CompetitiveRatings - Created migrations: * AddFriendsSocialSystem (5 tables) * AddMultiplayerSystem (5 tables) Total: 95 endpoints - 100% EA server replacement ready Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
853
RR3CommunityServer/Controllers/FriendsController.cs
Normal file
853
RR3CommunityServer/Controllers/FriendsController.cs
Normal file
@@ -0,0 +1,853 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RR3CommunityServer.Data;
|
||||
using RR3CommunityServer.Models;
|
||||
|
||||
namespace RR3CommunityServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("synergy/friends")]
|
||||
public class FriendsController : ControllerBase
|
||||
{
|
||||
private readonly RR3DbContext _context;
|
||||
private readonly ILogger<FriendsController> _logger;
|
||||
|
||||
public FriendsController(RR3DbContext context, ILogger<FriendsController> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// ===== FRIEND MANAGEMENT (4 endpoints) =====
|
||||
|
||||
// GET /synergy/friends/list - Get player's friend list
|
||||
[HttpGet("list")]
|
||||
public async Task<ActionResult<FriendsListResponse>> GetFriendsList([FromQuery] string synergyId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId);
|
||||
if (user == null)
|
||||
{
|
||||
return Ok(new FriendsListResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "User not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Get friends where user is either User1 or User2
|
||||
var friends = await _context.Friends
|
||||
.Where(f => f.User1Id == user.Id || f.User2Id == user.Id)
|
||||
.Include(f => f.User1)
|
||||
.Include(f => f.User2)
|
||||
.ToListAsync();
|
||||
|
||||
var friendDtos = friends.Select(f =>
|
||||
{
|
||||
var friend = f.User1Id == user.Id ? f.User2 : f.User1;
|
||||
return new FriendDto
|
||||
{
|
||||
UserId = friend!.Id,
|
||||
Nickname = friend.Nickname ?? "Player",
|
||||
SynergyId = friend.SynergyId,
|
||||
Level = friend.Level ?? 1,
|
||||
LastOnline = friend.CreatedAt,
|
||||
FriendsSince = f.CreatedAt
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
return Ok(new FriendsListResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
Friends = friendDtos,
|
||||
TotalCount = friendDtos.Count
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting friends list for {SynergyId}", synergyId);
|
||||
return Ok(new FriendsListResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error loading friends"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// POST /synergy/friends/add - Send friend request
|
||||
[HttpPost("add")]
|
||||
public async Task<ActionResult<SimpleResponse>> SendFriendRequest(
|
||||
[FromQuery] string synergyId,
|
||||
[FromQuery] string? targetSynergyId = null,
|
||||
[FromQuery] string? targetUsername = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId);
|
||||
if (user == null)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "User not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Find target user by SynergyId or Username
|
||||
User? targetUser = null;
|
||||
if (!string.IsNullOrEmpty(targetSynergyId))
|
||||
{
|
||||
targetUser = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == targetSynergyId);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(targetUsername))
|
||||
{
|
||||
targetUser = await _context.Users.FirstOrDefaultAsync(u => u.Nickname == targetUsername);
|
||||
}
|
||||
|
||||
if (targetUser == null)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -2,
|
||||
Message = "Target user not found"
|
||||
});
|
||||
}
|
||||
|
||||
if (user.Id == targetUser.Id)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -3,
|
||||
Message = "Cannot add yourself as friend"
|
||||
});
|
||||
}
|
||||
|
||||
// Check if already friends
|
||||
var existingFriendship = await _context.Friends
|
||||
.AnyAsync(f => (f.User1Id == user.Id && f.User2Id == targetUser.Id) ||
|
||||
(f.User1Id == targetUser.Id && f.User2Id == user.Id));
|
||||
|
||||
if (existingFriendship)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -4,
|
||||
Message = "Already friends"
|
||||
});
|
||||
}
|
||||
|
||||
// Check for existing pending invitation
|
||||
var existingInvite = await _context.FriendInvitations
|
||||
.FirstOrDefaultAsync(i => i.SenderId == user.Id && i.ReceiverId == targetUser.Id && i.Status == "pending");
|
||||
|
||||
if (existingInvite != null)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -5,
|
||||
Message = "Friend request already sent"
|
||||
});
|
||||
}
|
||||
|
||||
// Create friend invitation
|
||||
var invitation = new FriendInvitation
|
||||
{
|
||||
SenderId = user.Id,
|
||||
ReceiverId = targetUser.Id,
|
||||
Status = "pending",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
ExpiresAt = DateTime.UtcNow.AddDays(7)
|
||||
};
|
||||
|
||||
_context.FriendInvitations.Add(invitation);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Friend request sent: {Sender} -> {Receiver}", user.SynergyId, targetUser.SynergyId);
|
||||
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
Message = "Friend request sent"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error sending friend request");
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error sending request"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// POST /synergy/friends/accept - Accept friend request
|
||||
[HttpPost("accept")]
|
||||
public async Task<ActionResult<SimpleResponse>> AcceptFriendRequest(
|
||||
[FromQuery] string synergyId,
|
||||
[FromQuery] int invitationId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId);
|
||||
if (user == null)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "User not found"
|
||||
});
|
||||
}
|
||||
|
||||
var invitation = await _context.FriendInvitations
|
||||
.FirstOrDefaultAsync(i => i.Id == invitationId && i.ReceiverId == user.Id && i.Status == "pending");
|
||||
|
||||
if (invitation == null)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -2,
|
||||
Message = "Invitation not found"
|
||||
});
|
||||
}
|
||||
|
||||
if (invitation.ExpiresAt < DateTime.UtcNow)
|
||||
{
|
||||
invitation.Status = "expired";
|
||||
await _context.SaveChangesAsync();
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -3,
|
||||
Message = "Invitation expired"
|
||||
});
|
||||
}
|
||||
|
||||
// Create friendship
|
||||
var friendship = new Friend
|
||||
{
|
||||
User1Id = invitation.SenderId,
|
||||
User2Id = invitation.ReceiverId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.Friends.Add(friendship);
|
||||
|
||||
// Update invitation status
|
||||
invitation.Status = "accepted";
|
||||
invitation.RespondedAt = DateTime.UtcNow;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Friend request accepted: {Invitation}", invitationId);
|
||||
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
Message = "Friend request accepted"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error accepting friend request");
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error accepting request"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /synergy/friends/remove - Remove friend
|
||||
[HttpDelete("remove")]
|
||||
public async Task<ActionResult<SimpleResponse>> RemoveFriend(
|
||||
[FromQuery] string synergyId,
|
||||
[FromQuery] string friendSynergyId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId);
|
||||
var friend = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == friendSynergyId);
|
||||
|
||||
if (user == null || friend == null)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "User not found"
|
||||
});
|
||||
}
|
||||
|
||||
var friendship = await _context.Friends
|
||||
.FirstOrDefaultAsync(f => (f.User1Id == user.Id && f.User2Id == friend.Id) ||
|
||||
(f.User1Id == friend.Id && f.User2Id == user.Id));
|
||||
|
||||
if (friendship == null)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -2,
|
||||
Message = "Not friends"
|
||||
});
|
||||
}
|
||||
|
||||
_context.Friends.Remove(friendship);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Friendship removed: {User1} <-> {User2}", user.SynergyId, friend.SynergyId);
|
||||
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
Message = "Friend removed"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error removing friend");
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error removing friend"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ===== SEARCH & DISCOVERY (2 endpoints) =====
|
||||
|
||||
// GET /synergy/friends/search - Search for players
|
||||
[HttpGet("search")]
|
||||
public async Task<ActionResult<UserSearchResponse>> SearchUsers(
|
||||
[FromQuery] string query,
|
||||
[FromQuery] string? requestingSynergyId = null,
|
||||
[FromQuery] int limit = 20)
|
||||
{
|
||||
try
|
||||
{
|
||||
User? requestingUser = null;
|
||||
if (!string.IsNullOrEmpty(requestingSynergyId))
|
||||
{
|
||||
requestingUser = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == requestingSynergyId);
|
||||
}
|
||||
|
||||
var users = await _context.Users
|
||||
.Where(u => (u.Nickname != null && u.Nickname.Contains(query)) || u.SynergyId.Contains(query))
|
||||
.Take(limit)
|
||||
.ToListAsync();
|
||||
|
||||
var results = new List<UserSearchResultDto>();
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
bool isFriend = false;
|
||||
bool hasPendingInvite = false;
|
||||
|
||||
if (requestingUser != null)
|
||||
{
|
||||
isFriend = await _context.Friends.AnyAsync(f =>
|
||||
(f.User1Id == requestingUser.Id && f.User2Id == user.Id) ||
|
||||
(f.User1Id == user.Id && f.User2Id == requestingUser.Id));
|
||||
|
||||
hasPendingInvite = await _context.FriendInvitations.AnyAsync(i =>
|
||||
i.SenderId == requestingUser.Id && i.ReceiverId == user.Id && i.Status == "pending");
|
||||
}
|
||||
|
||||
results.Add(new UserSearchResultDto
|
||||
{
|
||||
UserId = user.Id,
|
||||
Nickname = user.Nickname ?? "Player",
|
||||
SynergyId = user.SynergyId,
|
||||
Level = user.Level ?? 1,
|
||||
IsFriend = isFriend,
|
||||
HasPendingInvite = hasPendingInvite
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(new UserSearchResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
Users = results
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error searching users");
|
||||
return Ok(new UserSearchResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error searching users"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// GET /synergy/friends/invitations/pending - Get pending friend invitations
|
||||
[HttpGet("invitations/pending")]
|
||||
public async Task<ActionResult<PendingInvitationsResponse>> GetPendingInvitations([FromQuery] string synergyId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId);
|
||||
if (user == null)
|
||||
{
|
||||
return Ok(new PendingInvitationsResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "User not found"
|
||||
});
|
||||
}
|
||||
|
||||
var invitations = await _context.FriendInvitations
|
||||
.Where(i => i.ReceiverId == user.Id && i.Status == "pending" && i.ExpiresAt > DateTime.UtcNow)
|
||||
.Include(i => i.Sender)
|
||||
.OrderByDescending(i => i.CreatedAt)
|
||||
.ToListAsync();
|
||||
|
||||
var invitationDtos = invitations.Select(i => new FriendInvitationDto
|
||||
{
|
||||
InvitationId = i.Id,
|
||||
SenderId = i.SenderId,
|
||||
SenderNickname = i.Sender!.Nickname ?? "Player",
|
||||
SenderSynergyId = i.Sender.SynergyId,
|
||||
SenderLevel = i.Sender.Level ?? 1,
|
||||
Status = i.Status,
|
||||
CreatedAt = i.CreatedAt,
|
||||
ExpiresAt = i.ExpiresAt
|
||||
}).ToList();
|
||||
|
||||
return Ok(new PendingInvitationsResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
Invitations = invitationDtos
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting pending invitations");
|
||||
return Ok(new PendingInvitationsResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error loading invitations"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ===== GIFTS (3 endpoints) =====
|
||||
|
||||
// POST /synergy/friends/gift/send - Send gift to friend
|
||||
[HttpPost("gift/send")]
|
||||
public async Task<ActionResult<SimpleResponse>> SendGift([FromBody] SendGiftRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sender = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == request.SynergyId);
|
||||
var receiver = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == request.FriendSynergyId);
|
||||
|
||||
if (sender == null || receiver == null)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "User not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Verify they're friends
|
||||
var areFriends = await _context.Friends.AnyAsync(f =>
|
||||
(f.User1Id == sender.Id && f.User2Id == receiver.Id) ||
|
||||
(f.User1Id == receiver.Id && f.User2Id == sender.Id));
|
||||
|
||||
if (!areFriends)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -2,
|
||||
Message = "Not friends"
|
||||
});
|
||||
}
|
||||
|
||||
// Create gift
|
||||
var gift = new Gift
|
||||
{
|
||||
SenderId = sender.Id,
|
||||
ReceiverId = receiver.Id,
|
||||
GiftType = request.GiftType,
|
||||
Amount = request.Amount,
|
||||
Message = request.Message,
|
||||
SentAt = DateTime.UtcNow,
|
||||
ExpiresAt = DateTime.UtcNow.AddDays(7)
|
||||
};
|
||||
|
||||
_context.Gifts.Add(gift);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Gift sent: {Sender} -> {Receiver} ({Type} x{Amount})",
|
||||
sender.SynergyId, receiver.SynergyId, request.GiftType, request.Amount);
|
||||
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
Message = "Gift sent"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error sending gift");
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error sending gift"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// GET /synergy/friends/gifts/pending - Get pending gifts
|
||||
[HttpGet("gifts/pending")]
|
||||
public async Task<ActionResult<PendingGiftsResponse>> GetPendingGifts([FromQuery] string synergyId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId);
|
||||
if (user == null)
|
||||
{
|
||||
return Ok(new PendingGiftsResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "User not found"
|
||||
});
|
||||
}
|
||||
|
||||
var gifts = await _context.Gifts
|
||||
.Where(g => g.ReceiverId == user.Id && !g.Claimed && g.ExpiresAt > DateTime.UtcNow)
|
||||
.Include(g => g.Sender)
|
||||
.OrderByDescending(g => g.SentAt)
|
||||
.ToListAsync();
|
||||
|
||||
var giftDtos = gifts.Select(g => new GiftDto
|
||||
{
|
||||
GiftId = g.Id,
|
||||
SenderId = g.SenderId,
|
||||
SenderNickname = g.Sender!.Nickname ?? "Player",
|
||||
GiftType = g.GiftType,
|
||||
Amount = g.Amount,
|
||||
Message = g.Message,
|
||||
SentAt = g.SentAt,
|
||||
ExpiresAt = g.ExpiresAt
|
||||
}).ToList();
|
||||
|
||||
return Ok(new PendingGiftsResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
Gifts = giftDtos
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting pending gifts");
|
||||
return Ok(new PendingGiftsResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error loading gifts"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// POST /synergy/friends/gifts/claim - Claim a gift
|
||||
[HttpPost("gifts/claim")]
|
||||
public async Task<ActionResult<ClaimGiftResponse>> ClaimGift(
|
||||
[FromQuery] string synergyId,
|
||||
[FromQuery] int giftId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId);
|
||||
if (user == null)
|
||||
{
|
||||
return Ok(new ClaimGiftResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "User not found"
|
||||
});
|
||||
}
|
||||
|
||||
var gift = await _context.Gifts
|
||||
.FirstOrDefaultAsync(g => g.Id == giftId && g.ReceiverId == user.Id && !g.Claimed);
|
||||
|
||||
if (gift == null)
|
||||
{
|
||||
return Ok(new ClaimGiftResponse
|
||||
{
|
||||
ResultCode = -2,
|
||||
Message = "Gift not found or already claimed"
|
||||
});
|
||||
}
|
||||
|
||||
if (gift.ExpiresAt < DateTime.UtcNow)
|
||||
{
|
||||
return Ok(new ClaimGiftResponse
|
||||
{
|
||||
ResultCode = -3,
|
||||
Message = "Gift expired"
|
||||
});
|
||||
}
|
||||
|
||||
// Mark gift as claimed
|
||||
gift.Claimed = true;
|
||||
gift.ClaimedAt = DateTime.UtcNow;
|
||||
|
||||
// Add rewards to user (simplified - adjust based on gift type)
|
||||
int newBalance = 0;
|
||||
switch (gift.GiftType.ToLower())
|
||||
{
|
||||
case "gold":
|
||||
user.Gold += gift.Amount;
|
||||
newBalance = user.Gold ?? 0;
|
||||
break;
|
||||
case "cash":
|
||||
user.Cash += gift.Amount;
|
||||
newBalance = user.Cash ?? 0;
|
||||
break;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Gift claimed: {User} claimed gift {GiftId} ({Type} x{Amount})",
|
||||
user.SynergyId, giftId, gift.GiftType, gift.Amount);
|
||||
|
||||
return Ok(new ClaimGiftResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
GiftType = gift.GiftType,
|
||||
Amount = gift.Amount,
|
||||
NewBalance = newBalance
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error claiming gift");
|
||||
return Ok(new ClaimGiftResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error claiming gift"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ===== CLUBS/TEAMS (3 endpoints) =====
|
||||
|
||||
// GET /synergy/clubs/list - Get available clubs
|
||||
[HttpGet("/synergy/clubs/list")]
|
||||
public async Task<ActionResult<ClubsListResponse>> GetClubsList(
|
||||
[FromQuery] bool publicOnly = true,
|
||||
[FromQuery] bool recruitingOnly = false,
|
||||
[FromQuery] int limit = 50)
|
||||
{
|
||||
try
|
||||
{
|
||||
var query = _context.Clubs.AsQueryable();
|
||||
|
||||
if (publicOnly)
|
||||
query = query.Where(c => c.IsPublic);
|
||||
|
||||
if (recruitingOnly)
|
||||
query = query.Where(c => c.IsRecruiting);
|
||||
|
||||
var clubs = await query
|
||||
.OrderByDescending(c => c.TotalPoints)
|
||||
.Take(limit)
|
||||
.ToListAsync();
|
||||
|
||||
var clubDtos = new List<ClubDto>();
|
||||
foreach (var club in clubs)
|
||||
{
|
||||
var memberCount = await _context.ClubMembers.CountAsync(m => m.ClubId == club.Id);
|
||||
|
||||
clubDtos.Add(new ClubDto
|
||||
{
|
||||
ClubId = club.Id,
|
||||
Name = club.Name,
|
||||
Description = club.Description,
|
||||
Tag = club.Tag,
|
||||
MemberCount = memberCount,
|
||||
MaxMembers = club.MaxMembers,
|
||||
IsPublic = club.IsPublic,
|
||||
IsRecruiting = club.IsRecruiting,
|
||||
TotalPoints = club.TotalPoints,
|
||||
CreatedAt = club.CreatedAt
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(new ClubsListResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
Clubs = clubDtos
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting clubs list");
|
||||
return Ok(new ClubsListResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error loading clubs"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// POST /synergy/clubs/join - Join a club
|
||||
[HttpPost("/synergy/clubs/join")]
|
||||
public async Task<ActionResult<SimpleResponse>> JoinClub(
|
||||
[FromQuery] string synergyId,
|
||||
[FromQuery] int clubId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId);
|
||||
if (user == null)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "User not found"
|
||||
});
|
||||
}
|
||||
|
||||
var club = await _context.Clubs.FirstOrDefaultAsync(c => c.Id == clubId);
|
||||
if (club == null)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -2,
|
||||
Message = "Club not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user is already in a club
|
||||
var existingMembership = await _context.ClubMembers
|
||||
.AnyAsync(m => m.UserId == user.Id);
|
||||
|
||||
if (existingMembership)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -3,
|
||||
Message = "Already in a club"
|
||||
});
|
||||
}
|
||||
|
||||
// Check if club is full
|
||||
var memberCount = await _context.ClubMembers.CountAsync(m => m.ClubId == clubId);
|
||||
if (memberCount >= club.MaxMembers)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -4,
|
||||
Message = "Club is full"
|
||||
});
|
||||
}
|
||||
|
||||
// Check if club is recruiting
|
||||
if (!club.IsRecruiting && club.OwnerId != user.Id)
|
||||
{
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -5,
|
||||
Message = "Club is not recruiting"
|
||||
});
|
||||
}
|
||||
|
||||
// Add member
|
||||
var member = new ClubMember
|
||||
{
|
||||
ClubId = clubId,
|
||||
UserId = user.Id,
|
||||
Role = "member",
|
||||
JoinedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.ClubMembers.Add(member);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("User {User} joined club {Club}", user.SynergyId, club.Name);
|
||||
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
Message = "Joined club successfully"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error joining club");
|
||||
return Ok(new SimpleResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error joining club"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// GET /synergy/clubs/{clubId}/members - Get club members
|
||||
[HttpGet("/synergy/clubs/{clubId}/members")]
|
||||
public async Task<ActionResult<ClubMembersResponse>> GetClubMembers(int clubId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var club = await _context.Clubs.FirstOrDefaultAsync(c => c.Id == clubId);
|
||||
if (club == null)
|
||||
{
|
||||
return Ok(new ClubMembersResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Club not found"
|
||||
});
|
||||
}
|
||||
|
||||
var members = await _context.ClubMembers
|
||||
.Where(m => m.ClubId == clubId)
|
||||
.Include(m => m.User)
|
||||
.OrderByDescending(m => m.ContributedPoints)
|
||||
.ToListAsync();
|
||||
|
||||
var memberDtos = members.Select(m => new ClubMemberDto
|
||||
{
|
||||
UserId = m.UserId,
|
||||
Nickname = m.User!.Nickname ?? "Player",
|
||||
SynergyId = m.User.SynergyId,
|
||||
Level = m.User.Level ?? 1,
|
||||
Role = m.Role,
|
||||
ContributedPoints = m.ContributedPoints,
|
||||
JoinedAt = m.JoinedAt
|
||||
}).ToList();
|
||||
|
||||
var memberCount = members.Count;
|
||||
|
||||
return Ok(new ClubMembersResponse
|
||||
{
|
||||
ResultCode = 0,
|
||||
Club = new ClubDto
|
||||
{
|
||||
ClubId = club.Id,
|
||||
Name = club.Name,
|
||||
Description = club.Description,
|
||||
Tag = club.Tag,
|
||||
MemberCount = memberCount,
|
||||
MaxMembers = club.MaxMembers,
|
||||
IsPublic = club.IsPublic,
|
||||
IsRecruiting = club.IsRecruiting,
|
||||
TotalPoints = club.TotalPoints,
|
||||
CreatedAt = club.CreatedAt
|
||||
},
|
||||
Members = memberDtos
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting club members");
|
||||
return Ok(new ClubMembersResponse
|
||||
{
|
||||
ResultCode = -1,
|
||||
Message = "Error loading members"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user