Модуль связей и Klipy
This commit is contained in:
@@ -6,9 +6,10 @@ using System.Threading.Tasks;
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Abstractions;
|
||||
|
||||
public interface IRelationsDbContext
|
||||
public interface IContactsDbContext
|
||||
{
|
||||
DbSet<Friendship> Friendships { get; }
|
||||
DbSet<Contact> Contacts { get; }
|
||||
DbSet<UserReplica> UserReplicas { get; }
|
||||
|
||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Knot.Modules.Relations.Application.Contacts;
|
||||
|
||||
public record AcceptContactRequestCommand(Guid UserId, Guid ContactId) : ICommand<Guid>;
|
||||
|
||||
internal sealed class AcceptContactRequestCommandHandler : ICommandHandler<AcceptContactRequestCommand, Guid>
|
||||
{
|
||||
private readonly IContactsDbContext _context;
|
||||
|
||||
public AcceptContactRequestCommandHandler(IContactsDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Result<Guid>> Handle(AcceptContactRequestCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var contact = await _context.Contacts.FirstOrDefaultAsync(c => c.Id == request.ContactId, cancellationToken);
|
||||
if (contact == null || contact.ContactId != request.UserId)
|
||||
{
|
||||
return Result.Failure<Guid>(new Error("Contacts.NotFound", "Contact request not found."));
|
||||
}
|
||||
|
||||
contact.Accept();
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Result.Success(contact.Id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
|
||||
namespace Knot.Modules.Relations.Application.Contacts;
|
||||
|
||||
public record ContactDto(
|
||||
Guid Id,
|
||||
string Username,
|
||||
string DisplayName,
|
||||
string Avatar,
|
||||
bool IsOnline,
|
||||
DateTime? LastSeen,
|
||||
Guid RelationId,
|
||||
bool IsBlocked = false,
|
||||
bool IsExternal = false,
|
||||
string? Domain = null);
|
||||
|
||||
public record GetContactsQuery(Guid UserId) : IQuery<List<ContactDto>>;
|
||||
|
||||
internal sealed class GetContactsQueryHandler : IQueryHandler<GetContactsQuery, List<ContactDto>>
|
||||
{
|
||||
private readonly IContactsDbContext _context;
|
||||
|
||||
public GetContactsQueryHandler(IContactsDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Result<List<ContactDto>>> Handle(GetContactsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var relations = await _context.Contacts
|
||||
.Where(c => (c.UserId == request.UserId || c.ContactId == request.UserId) && c.Status == ContactStatus.Accepted)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var contactIds = relations.Select(c => c.UserId == request.UserId ? c.ContactId : c.UserId).ToList();
|
||||
|
||||
var replicas = await _context.UserReplicas
|
||||
.Where(r => contactIds.Contains(r.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var result = new List<ContactDto>();
|
||||
|
||||
foreach (var replica in replicas)
|
||||
{
|
||||
var rel = relations.First(c => c.UserId == replica.Id || c.ContactId == replica.Id);
|
||||
|
||||
result.Add(new ContactDto(
|
||||
replica.Id,
|
||||
replica.Username,
|
||||
replica.DisplayName,
|
||||
replica.Avatar,
|
||||
false, // Presence handled by another service or real-time
|
||||
null,
|
||||
rel.Id,
|
||||
rel.Status == ContactStatus.Blocked,
|
||||
replica.IsExternal,
|
||||
replica.Domain
|
||||
));
|
||||
}
|
||||
|
||||
return Result.Success(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
|
||||
namespace Knot.Modules.Relations.Application.Contacts;
|
||||
|
||||
public record ContactUserDto(Guid Id, string Username, string DisplayName, string Avatar);
|
||||
public record ContactRequestDto(Guid Id, ContactUserDto User, DateTime CreatedAt);
|
||||
|
||||
public record GetIncomingRequestsQuery(Guid UserId) : IQuery<List<ContactRequestDto>>;
|
||||
|
||||
internal sealed class GetIncomingRequestsQueryHandler : IQueryHandler<GetIncomingRequestsQuery, List<ContactRequestDto>>
|
||||
{
|
||||
private readonly IContactsDbContext _context;
|
||||
|
||||
public GetIncomingRequestsQueryHandler(IContactsDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Result<List<ContactRequestDto>>> Handle(GetIncomingRequestsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var contacts = await _context.Contacts
|
||||
.Where(c => c.ContactId == request.UserId && c.Status == ContactStatus.Pending)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var requesterIds = contacts.Select(c => c.UserId).ToList();
|
||||
var replicas = await _context.UserReplicas
|
||||
.Where(r => requesterIds.Contains(r.Id))
|
||||
.ToDictionaryAsync(r => r.Id, cancellationToken);
|
||||
|
||||
var result = contacts
|
||||
.Where(c => replicas.ContainsKey(c.UserId))
|
||||
.Select(c =>
|
||||
{
|
||||
var user = replicas[c.UserId];
|
||||
return new ContactRequestDto(
|
||||
c.Id,
|
||||
new ContactUserDto(user.Id, user.Username, user.DisplayName, user.Avatar),
|
||||
c.CreatedAt
|
||||
);
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return Result.Success(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Knot.Modules.Relations.Application.Contacts;
|
||||
|
||||
public record RemoveContactCommand(Guid UserId, Guid ContactRelationId) : ICommand;
|
||||
|
||||
internal sealed class RemoveContactCommandHandler : ICommandHandler<RemoveContactCommand>
|
||||
{
|
||||
private readonly IContactsDbContext _context;
|
||||
|
||||
public ContactRelationNotFound() : base("Contacts.NotFound", "Contact relation not found.") { }
|
||||
public ContactRelationUnauthorized() : base("Contacts.Unauthorized", "You are not authorized to remove this contact.") { }
|
||||
|
||||
public RemoveContactCommandHandler(IContactsDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Result> Handle(RemoveContactCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var contact = await _context.Contacts.FirstOrDefaultAsync(c => c.Id == request.ContactRelationId, cancellationToken);
|
||||
if (contact == null)
|
||||
{
|
||||
return Result.Failure(new Error("Contacts.NotFound", "Contact relation not found."));
|
||||
}
|
||||
|
||||
if (contact.UserId != request.UserId && contact.ContactId != request.UserId)
|
||||
{
|
||||
return Result.Failure(new Error("Contacts.Unauthorized", "You are not authorized to remove this contact."));
|
||||
}
|
||||
|
||||
_context.Contacts.Remove(contact);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
|
||||
namespace Knot.Modules.Relations.Application.Contacts;
|
||||
|
||||
public record SendContactRequestCommand(Guid UserId, Guid ContactId) : ICommand;
|
||||
|
||||
internal sealed class SendContactRequestCommandHandler : ICommandHandler<SendContactRequestCommand>
|
||||
{
|
||||
private readonly IContactsDbContext _context;
|
||||
|
||||
public SendContactRequestCommandHandler(IContactsDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Result> Handle(SendContactRequestCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.UserId == request.ContactId)
|
||||
{
|
||||
return Result.Failure(new Error("Contacts.Self", "You cannot add yourself to contacts."));
|
||||
}
|
||||
|
||||
var existing = await _context.Contacts
|
||||
.FirstOrDefaultAsync(f => (f.UserId == request.UserId && f.ContactId == request.ContactId) ||
|
||||
(f.UserId == request.ContactId && f.ContactId == request.UserId), cancellationToken);
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
return Result.Failure(new Error("Contacts.Exists", "Contact relation already exists."));
|
||||
}
|
||||
|
||||
var contact = Contact.CreateRequest(request.UserId, request.ContactId);
|
||||
_context.Contacts.Add(contact);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends;
|
||||
|
||||
public record AcceptFriendRequestCommand(Guid UserId, Guid FriendshipId) : ICommand<Guid>;
|
||||
|
||||
internal sealed class AcceptFriendRequestCommandHandler : ICommandHandler<AcceptFriendRequestCommand, Guid>
|
||||
{
|
||||
private readonly IRelationsDbContext _context;
|
||||
private readonly IRelationsUnitOfWork _unitOfWork;
|
||||
|
||||
public AcceptFriendRequestCommandHandler(IRelationsDbContext context, IRelationsUnitOfWork unitOfWork)
|
||||
{
|
||||
_context = context;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task<Result<Guid>> Handle(AcceptFriendRequestCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var friendship = await _context.Friendships.FindAsync(new object[] { request.FriendshipId }, cancellationToken);
|
||||
if (friendship == null || friendship.FriendId != request.UserId)
|
||||
{
|
||||
return Result.Failure<Guid>(IdentityErrors.FriendsNotFound);
|
||||
}
|
||||
|
||||
friendship.Accept();
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Result.Success(friendship.Id);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends.DTOs;
|
||||
|
||||
public record FriendDto(
|
||||
Guid Id,
|
||||
string Username,
|
||||
string DisplayName,
|
||||
string? Avatar,
|
||||
bool IsOnline,
|
||||
DateTime LastSeen,
|
||||
Guid FriendshipId
|
||||
);
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends.DTOs;
|
||||
|
||||
public record FriendRequestDto(
|
||||
Guid Id,
|
||||
FriendUserDto User,
|
||||
DateTime CreatedAt
|
||||
);
|
||||
|
||||
public record FriendUserDto(
|
||||
Guid Id,
|
||||
string Username,
|
||||
string DisplayName,
|
||||
string? Avatar
|
||||
);
|
||||
@@ -1,6 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends.DTOs;
|
||||
|
||||
public record SendFriendRequest(Guid FriendId);
|
||||
@@ -1,37 +0,0 @@
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends;
|
||||
|
||||
public record DeclineFriendRequestCommand(Guid UserId, Guid FriendshipId) : ICommand;
|
||||
|
||||
internal sealed class DeclineFriendRequestCommandHandler : ICommandHandler<DeclineFriendRequestCommand>
|
||||
{
|
||||
private readonly IRelationsDbContext _context;
|
||||
private readonly IRelationsUnitOfWork _unitOfWork;
|
||||
|
||||
public DeclineFriendRequestCommandHandler(IRelationsDbContext context, IRelationsUnitOfWork unitOfWork)
|
||||
{
|
||||
_context = context;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task<Result> Handle(DeclineFriendRequestCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var friendship = await _context.Friendships.FindAsync(new object[] { request.FriendshipId }, cancellationToken);
|
||||
if (friendship == null || friendship.FriendId != request.UserId)
|
||||
{
|
||||
return Result.Failure(IdentityErrors.FriendsNotFound);
|
||||
}
|
||||
|
||||
friendship.Decline();
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends;
|
||||
|
||||
public record FriendDto(
|
||||
Guid Id,
|
||||
string Username,
|
||||
string DisplayName,
|
||||
string? Avatar,
|
||||
bool IsOnline,
|
||||
DateTime LastSeenAt,
|
||||
Guid FriendshipId
|
||||
);
|
||||
@@ -1,12 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends;
|
||||
|
||||
public record FriendUserDto(Guid Id, string Username, string DisplayName, string? Avatar);
|
||||
|
||||
public record FriendRequestDto(
|
||||
Guid Id,
|
||||
FriendUserDto User,
|
||||
DateTime CreatedAt
|
||||
);
|
||||
@@ -1,10 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends;
|
||||
|
||||
public record FriendshipStatusResponse(
|
||||
string Status,
|
||||
Guid? FriendshipId = null,
|
||||
string? Direction = null
|
||||
);
|
||||
@@ -1,60 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends;
|
||||
|
||||
public record GetFriendsQuery(Guid UserId) : IQuery<List<FriendDto>>;
|
||||
|
||||
internal sealed class GetFriendsQueryHandler : IQueryHandler<GetFriendsQuery, List<FriendDto>>
|
||||
{
|
||||
private readonly IRelationsDbContext _context;
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public GetFriendsQueryHandler(IRelationsDbContext context, IUserRepository userRepository)
|
||||
{
|
||||
_context = context;
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
public async Task<Result<List<FriendDto>>> Handle(GetFriendsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var friendships = await _context.Friendships
|
||||
.Where(f => (f.UserId == request.UserId || f.FriendId == request.UserId) && f.Status == FriendshipStatus.Accepted)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var friendIds = friendships.Select(f => f.UserId == request.UserId ? f.FriendId : f.UserId).ToList();
|
||||
var friends = new List<FriendDto>();
|
||||
|
||||
foreach (var id in friendIds)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(id, cancellationToken);
|
||||
if (user == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var fs = friendships.First(f => f.UserId == id || f.FriendId == id);
|
||||
|
||||
friends.Add(new FriendDto(
|
||||
user.Id,
|
||||
user.Username,
|
||||
user.DisplayName,
|
||||
user.Avatar,
|
||||
false, // IsOnline logic shouldn't be here, but we'll leave default for now
|
||||
DateTime.UtcNow,
|
||||
fs.Id
|
||||
));
|
||||
}
|
||||
|
||||
return Result.Success(friends);
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Knot.Shared.Kernel;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends;
|
||||
|
||||
public record GetFriendshipStatusQuery(Guid CurrentUserId, Guid TargetUserId) : IQuery<FriendshipStatusResponse>;
|
||||
|
||||
internal sealed class GetFriendshipStatusQueryHandler : IQueryHandler<GetFriendshipStatusQuery, FriendshipStatusResponse>
|
||||
{
|
||||
private readonly IRelationsDbContext _context;
|
||||
|
||||
public GetFriendshipStatusQueryHandler(IRelationsDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Result<FriendshipStatusResponse>> Handle(GetFriendshipStatusQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.CurrentUserId == request.TargetUserId)
|
||||
{
|
||||
return Result.Success(new FriendshipStatusResponse("self"));
|
||||
}
|
||||
|
||||
var fs = await _context.Friendships
|
||||
.FirstOrDefaultAsync(f => (f.UserId == request.CurrentUserId && f.FriendId == request.TargetUserId) ||
|
||||
(f.UserId == request.TargetUserId && f.FriendId == request.CurrentUserId), cancellationToken);
|
||||
|
||||
if (fs == null)
|
||||
{
|
||||
return Result.Success(new FriendshipStatusResponse("none"));
|
||||
}
|
||||
|
||||
return Result.Success(new FriendshipStatusResponse(
|
||||
fs.Status.ToString().ToLowerInvariant(),
|
||||
fs.Id,
|
||||
fs.UserId == request.CurrentUserId ? "outgoing" : "incoming"
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends;
|
||||
|
||||
public record GetIncomingRequestsQuery(Guid UserId) : IQuery<List<FriendRequestDto>>;
|
||||
|
||||
internal sealed class GetIncomingRequestsQueryHandler : IQueryHandler<GetIncomingRequestsQuery, List<FriendRequestDto>>
|
||||
{
|
||||
private readonly IRelationsDbContext _context;
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public GetIncomingRequestsQueryHandler(IRelationsDbContext context, IUserRepository userRepository)
|
||||
{
|
||||
_context = context;
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
public async Task<Result<List<FriendRequestDto>>> Handle(GetIncomingRequestsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var friendships = await _context.Friendships
|
||||
.Where(f => f.FriendId == request.UserId && f.Status == FriendshipStatus.Pending)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var requestsList = new List<FriendRequestDto>();
|
||||
foreach (var fs in friendships)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(fs.UserId, cancellationToken);
|
||||
if (user == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
requestsList.Add(new FriendRequestDto(
|
||||
fs.Id,
|
||||
new FriendUserDto(user.Id, user.Username, user.DisplayName, user.Avatar),
|
||||
fs.CreatedAt
|
||||
));
|
||||
}
|
||||
return Result.Success(requestsList);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends;
|
||||
|
||||
public record GetOutgoingRequestsQuery(Guid UserId) : IQuery<List<FriendRequestDto>>;
|
||||
|
||||
internal sealed class GetOutgoingRequestsQueryHandler : IQueryHandler<GetOutgoingRequestsQuery, List<FriendRequestDto>>
|
||||
{
|
||||
private readonly IRelationsDbContext _context;
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public GetOutgoingRequestsQueryHandler(IRelationsDbContext context, IUserRepository userRepository)
|
||||
{
|
||||
_context = context;
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
public async Task<Result<List<FriendRequestDto>>> Handle(GetOutgoingRequestsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var friendships = await _context.Friendships
|
||||
.Where(f => f.UserId == request.UserId && f.Status == FriendshipStatus.Pending)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var requestsList = new List<FriendRequestDto>();
|
||||
foreach (var fs in friendships)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(fs.FriendId, cancellationToken);
|
||||
if (user == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
requestsList.Add(new FriendRequestDto(
|
||||
fs.Id,
|
||||
new FriendUserDto(user.Id, user.Username, user.DisplayName, user.Avatar),
|
||||
fs.CreatedAt
|
||||
));
|
||||
}
|
||||
return Result.Success(requestsList);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Knot.Shared.Kernel;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends;
|
||||
|
||||
public record RemoveFriendCommand(Guid UserId, Guid FriendshipId) : ICommand;
|
||||
|
||||
internal sealed class RemoveFriendCommandHandler : ICommandHandler<RemoveFriendCommand>
|
||||
{
|
||||
private readonly IRelationsDbContext _context;
|
||||
private readonly IRelationsUnitOfWork _unitOfWork;
|
||||
|
||||
public RemoveFriendCommandHandler(IRelationsDbContext context, IRelationsUnitOfWork unitOfWork)
|
||||
{
|
||||
_context = context;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task<Result> Handle(RemoveFriendCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var friendship = await _context.Friendships.FindAsync(new object[] { request.FriendshipId }, cancellationToken);
|
||||
if (friendship == null || (friendship.UserId != request.UserId && friendship.FriendId != request.UserId))
|
||||
{
|
||||
return Result.Failure(IdentityErrors.FriendsNotFound);
|
||||
}
|
||||
|
||||
_context.Friendships.Remove(friendship);
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Knot.Modules.Relations.Domain;
|
||||
using Knot.Shared.Kernel;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using Knot.Modules.Relations.Application.Abstractions;
|
||||
namespace Knot.Modules.Relations.Application.Friends;
|
||||
|
||||
public record SendFriendRequestCommand(Guid UserId, Guid FriendId) : ICommand;
|
||||
|
||||
internal sealed class SendFriendRequestCommandHandler : ICommandHandler<SendFriendRequestCommand>
|
||||
{
|
||||
private readonly IRelationsDbContext _context;
|
||||
private readonly IRelationsUnitOfWork _unitOfWork;
|
||||
|
||||
public SendFriendRequestCommandHandler(IRelationsDbContext context, IRelationsUnitOfWork unitOfWork)
|
||||
{
|
||||
_context = context;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task<Result> Handle(SendFriendRequestCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.UserId == request.FriendId)
|
||||
{
|
||||
return Result.Failure(IdentityErrors.FriendsSelf);
|
||||
}
|
||||
|
||||
var existing = await _context.Friendships
|
||||
.FirstOrDefaultAsync(f => (f.UserId == request.UserId && f.FriendId == request.FriendId) ||
|
||||
(f.UserId == request.FriendId && f.FriendId == request.UserId), cancellationToken);
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
return Result.Failure(IdentityErrors.FriendsExists);
|
||||
}
|
||||
|
||||
var friendship = Friendship.Create(request.UserId, request.FriendId);
|
||||
_context.Friendships.Add(friendship);
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user