- 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>
854 lines
27 KiB
C#
854 lines
27 KiB
C#
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"
|
|
});
|
|
}
|
|
}
|
|
}
|