Разделение
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Knot.Contracts.Auth.Abstractions;
|
||||||
|
|
||||||
|
public interface IUserQueryService
|
||||||
|
{
|
||||||
|
Task<List<UserInfo>> GetAllUsersAsync(CancellationToken cancellationToken);
|
||||||
|
Task<List<UserInfo>> SearchUsersAsync(string query, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record UserInfo(Guid Id, string? Avatar, string? UserName, string? DisplayName, DateTime CreatedAt, string? PhoneNumber, string? Email);
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Knot.Contracts.Auth.Domain;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Knot.Contracts.Auth.Application.Abstractions;
|
namespace Knot.Contracts.Auth.Application.Abstractions;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
using Knot.Contracts.Auth.Domain;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace Knot.Contracts.Auth.Application.Users.GetMe;
|
|
||||||
|
|
||||||
public record GetMeQuery() : IRequest<UserContract>;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using Knot.Contracts.Auth.Application.Auth.DTOs;
|
|
||||||
using Knot.Shared.Kernel;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace Knot.Contracts.Auth.Application.Users.Login;
|
|
||||||
|
|
||||||
public record LoginUserCommand(
|
|
||||||
string Username,
|
|
||||||
string Password
|
|
||||||
) : IRequest<Result<AuthResponseDto>>;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using Knot.Contracts.Auth.Application.Auth.DTOs;
|
|
||||||
using Knot.Contracts.Auth.Domain;
|
|
||||||
using Knot.Shared.Kernel;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace Knot.Contracts.Auth.Application.Users.Register;
|
|
||||||
|
|
||||||
public record RegisterUserCommand(
|
|
||||||
string Username,
|
|
||||||
string Password,
|
|
||||||
string DisplayName,
|
|
||||||
string? Email,
|
|
||||||
string? Bio
|
|
||||||
) : IRequest<Result<AuthResponseDto>>;
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Knot.Contracts.Cleanup.Abstractions;
|
||||||
|
|
||||||
|
public interface ICleanupService
|
||||||
|
{
|
||||||
|
Task<CleanupData> GetCleanupDataAsync(CancellationToken cancellationToken);
|
||||||
|
Task CleanOrphanMessagesAsync(List<Guid> messageIds, CancellationToken cancellationToken);
|
||||||
|
Task CleanOrphanFilesAsync(List<string> fileIds, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record CleanupData(
|
||||||
|
List<ChatCleanupInfo> Chats,
|
||||||
|
List<MessageCleanupInfo> Messages,
|
||||||
|
List<StoryCleanupInfo> Stories,
|
||||||
|
List<UserCleanupInfo> Users,
|
||||||
|
List<FileCleanupInfo> Files);
|
||||||
|
|
||||||
|
public record ChatCleanupInfo(Guid Id, string? Avatar);
|
||||||
|
|
||||||
|
public record MessageCleanupInfo(Guid Id, Guid ChatId, bool IsDeleted, string? MediaUrl);
|
||||||
|
|
||||||
|
public record StoryCleanupInfo(Guid Id, string? MediaUrl);
|
||||||
|
|
||||||
|
public record UserCleanupInfo(Guid Id, string? Avatar);
|
||||||
|
|
||||||
|
public record FileCleanupInfo(string FileId);
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Knot.Contracts.Conversations.Abstractions;
|
||||||
|
|
||||||
|
public interface IChatQueryService
|
||||||
|
{
|
||||||
|
Task<List<ChatInfo>> GetAllChatsAsync(CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IUserDeleterService
|
||||||
|
{
|
||||||
|
Task DeleteUserAsync(Guid userId, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ChatInfo(Guid Id, string? Avatar);
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Knot.Contracts.Conversations.Abstractions;
|
||||||
|
|
||||||
|
public interface IUserStatusService
|
||||||
|
{
|
||||||
|
bool IsUserOnline(string userId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
|
namespace Knot.Contracts.Conversations.Application.Abstractions;
|
||||||
|
|
||||||
|
public interface IStoryNotificationsClient
|
||||||
|
{
|
||||||
|
Task StoryViewed(Guid storyId, Guid userId);
|
||||||
|
Task StoryReactionAdded(Guid storyId, Guid userId, string reaction);
|
||||||
|
Task StoryReactionRemoved(Guid storyId, Guid userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IChatHubClient
|
||||||
|
{
|
||||||
|
Task MessageReceived(object message);
|
||||||
|
Task MessageDeleted(Guid messageId);
|
||||||
|
Task MessageEdited(Guid messageId, object newContent);
|
||||||
|
Task ReactionAdded(Guid messageId, string reaction, Guid userId);
|
||||||
|
Task ReactionRemoved(Guid messageId, string reaction, Guid userId);
|
||||||
|
Task UserStatusChanged(Guid userId, bool isOnline);
|
||||||
|
Task ChatUpdated(object chat);
|
||||||
|
Task ChatDeleted(Guid chatId);
|
||||||
|
Task UserJoinedChat(Guid chatId, Guid userId);
|
||||||
|
Task UserLeftChat(Guid chatId, Guid userId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Knot.Contracts.Conversations.Application.Abstractions;
|
||||||
|
using Knot.Shared.Kernel;
|
||||||
|
|
||||||
|
namespace Knot.Contracts.Conversations.Infrastructure.Persistence;
|
||||||
|
|
||||||
|
public interface IChatsDbContext : IChatsUnitOfWork
|
||||||
|
{
|
||||||
|
IQueryable<Chat> Chats { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Chat : AggregateRoot<Guid>
|
||||||
|
{
|
||||||
|
public Chat() : base(Guid.NewGuid()) { }
|
||||||
|
|
||||||
|
|
||||||
|
public string? Avatar { get; set; }
|
||||||
|
}
|
||||||
@@ -11,6 +11,10 @@
|
|||||||
<ProjectReference Include="..\Messaging\Knot.Contracts.Messaging.csproj" />
|
<ProjectReference Include="..\Messaging\Knot.Contracts.Messaging.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MediatR" Version="12.0.1" />
|
<PackageReference Include="MediatR" Version="12.0.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.4" />
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Knot.Contracts.Klipy.Abstractions;
|
||||||
|
|
||||||
|
public interface IKlipyClient
|
||||||
|
{
|
||||||
|
Task<bool> TestConnectionAsync(CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Knot.Contracts.Messaging.Abstractions;
|
||||||
|
|
||||||
|
public interface IMessageQueryService
|
||||||
|
{
|
||||||
|
Task<List<MessageInfo>> GetAllMessagesAsync(CancellationToken cancellationToken);
|
||||||
|
Task DeleteMessagesAsync(List<Guid> messageIds, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record MessageInfo(Guid Id, Guid ChatId, bool IsDeleted, string? MediaUrl);
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Knot.Contracts.Messaging.Abstractions;
|
||||||
|
|
||||||
|
public interface IUserStatsService
|
||||||
|
{
|
||||||
|
Task<Dictionary<Guid, UserStats>> GetStatsForUsersAsync(List<Guid> userIds, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record UserStats(int MessageCount, long StorageSize);
|
||||||
@@ -18,4 +18,5 @@ public interface IMessageRepository
|
|||||||
Task DeleteChatMessagesAsync(Guid chatId, CancellationToken cancellationToken);
|
Task DeleteChatMessagesAsync(Guid chatId, CancellationToken cancellationToken);
|
||||||
Task DeleteUserMessagesAsync(Guid userId, CancellationToken cancellationToken);
|
Task DeleteUserMessagesAsync(Guid userId, CancellationToken cancellationToken);
|
||||||
Task RemoveAsync(Guid id, CancellationToken cancellationToken);
|
Task RemoveAsync(Guid id, CancellationToken cancellationToken);
|
||||||
|
Task DeleteAsync(Message message, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Knot.Contracts.Messaging.Application.Abstractions;
|
|
||||||
|
|
||||||
public record UserStats(int MessageCount, long StorageSize);
|
|
||||||
|
|
||||||
public interface IUserStatsService
|
|
||||||
{
|
|
||||||
Task<Dictionary<Guid, UserStats>> GetStatsForUsersAsync(IEnumerable<Guid> userIds, CancellationToken ct = default);
|
|
||||||
Task<long> GetTotalStorageSizeAsync(CancellationToken ct = default);
|
|
||||||
Task<int> GetCountOrphanedMessagesAsync(HashSet<Guid> activeChatIds, CancellationToken ct = default);
|
|
||||||
Task<long> GetOrphanedMediaSizeAsync(HashSet<string> validFileIds, CancellationToken ct = default);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Knot.Contracts.Messaging.Infrastructure.Persistence;
|
||||||
|
|
||||||
|
public interface IMongoMessageCollection
|
||||||
|
{
|
||||||
|
Task<List<Message>> GetAllAsync(CancellationToken cancellationToken);
|
||||||
|
Task<List<Message>> GetOrphanedMessagesAsync(HashSet<Guid> activeChatIds, CancellationToken cancellationToken);
|
||||||
|
Task DeleteManyAsync(List<Guid> messageIds, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Message
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid ChatId { get; set; }
|
||||||
|
public bool IsDeleted { get; set; }
|
||||||
|
public string? MediaUrl { get; set; }
|
||||||
|
public List<Media>? Media { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Media
|
||||||
|
{
|
||||||
|
public string Url { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Knot.Contracts.Relations.Domain;
|
||||||
|
|
||||||
|
namespace Knot.Contracts.Relations.Application.Abstractions;
|
||||||
|
|
||||||
|
public interface IFriendshipRepository
|
||||||
|
{
|
||||||
|
Task<List<Friendship>> GetAcceptedFriendshipsAsync(Guid userId, CancellationToken ct = default);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace Knot.Contracts.Relations.Domain;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public record Friendship(
|
||||||
|
Guid Id,
|
||||||
|
Guid UserId,
|
||||||
|
Guid FriendId,
|
||||||
|
FriendshipStatus Status,
|
||||||
|
DateTime CreatedAt
|
||||||
|
);
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Knot.Contracts.Relations.Domain;
|
||||||
|
|
||||||
|
public enum FriendshipStatus
|
||||||
|
{
|
||||||
|
Pending,
|
||||||
|
Accepted,
|
||||||
|
Declined
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Knot.Contracts.Relations.Domain;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public record UserReplica(
|
||||||
|
Guid Id,
|
||||||
|
string Username,
|
||||||
|
string DisplayName,
|
||||||
|
string Avatar,
|
||||||
|
bool IsExternal,
|
||||||
|
string? Domain
|
||||||
|
);
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Knot.Contracts.Stories.Abstractions;
|
||||||
|
|
||||||
|
public interface IStoryQueryService
|
||||||
|
{
|
||||||
|
Task<List<StoryInfo>> GetAllStoriesAsync(CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record StoryInfo(Guid Id, string? MediaUrl);
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MediatR" Version="12.0.1" />
|
<PackageReference Include="MediatR" Version="12.0.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.4" />
|
||||||
|
<PackageReference Include="MongoDB.Driver" Version="3.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -14,12 +14,12 @@
|
|||||||
<ProjectReference Include="..\..\Contracts\Settings\Knot.Contracts.Settings.csproj" />
|
<ProjectReference Include="..\..\Contracts\Settings\Knot.Contracts.Settings.csproj" />
|
||||||
<ProjectReference Include="..\..\Contracts\Stories\Knot.Contracts.Stories.csproj" />
|
<ProjectReference Include="..\..\Contracts\Stories\Knot.Contracts.Stories.csproj" />
|
||||||
<ProjectReference Include="..\..\Contracts\Klipy\Knot.Contracts.Klipy.csproj" />
|
<ProjectReference Include="..\..\Contracts\Klipy\Knot.Contracts.Klipy.csproj" />
|
||||||
<!-- Admin needs direct module access for cleanup operations -->
|
<!-- Admin needs direct module access for cleanup operations - PrivateAssets prevents module exposure -->
|
||||||
<ProjectReference Include="..\Auth\Knot.Modules.Auth.csproj" />
|
<ProjectReference Include="..\Auth\Knot.Modules.Auth.csproj" PrivateAssets="all" />
|
||||||
<ProjectReference Include="..\Conversations\Knot.Modules.Conversations.csproj" />
|
<ProjectReference Include="..\Conversations\Knot.Modules.Conversations.csproj" PrivateAssets="all" />
|
||||||
<ProjectReference Include="..\Stories\Knot.Modules.Stories.csproj" />
|
<ProjectReference Include="..\Stories\Knot.Modules.Stories.csproj" PrivateAssets="all" />
|
||||||
<ProjectReference Include="..\Klipy\Knot.Modules.Klipy.csproj" />
|
<ProjectReference Include="..\Klipy\Knot.Modules.Klipy.csproj" PrivateAssets="all" />
|
||||||
<ProjectReference Include="..\Messaging\Knot.Modules.Messaging.csproj" />
|
<ProjectReference Include="..\Messaging\Knot.Modules.Messaging.csproj" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Carter" Version="10.0.0" />
|
<PackageReference Include="Carter" Version="10.0.0" />
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Knot.Contracts.Auth.Abstractions;
|
||||||
|
using Knot.Modules.Auth.Infrastructure.Persistence;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Knot.Modules.Auth.Infrastructure.Persistence;
|
||||||
|
|
||||||
|
public sealed class UserQueryService : IUserQueryService
|
||||||
|
{
|
||||||
|
private readonly AuthDbContext _context;
|
||||||
|
|
||||||
|
public UserQueryService(AuthDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<UserInfo>> GetAllUsersAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var users = await _context.Users.AsNoTracking().ToListAsync(cancellationToken);
|
||||||
|
return users.Select(u => new UserInfo(u.Id, u.Avatar, u.Username, u.DisplayName, u.CreatedAt, u.PhoneNumber, u.Email)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<UserInfo>> SearchUsersAsync(string query, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var users = await _context.Users
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(u => u.Username != null && u.Username.Contains(query) ||
|
||||||
|
u.DisplayName != null && u.DisplayName.Contains(query) ||
|
||||||
|
u.Email != null && u.Email.Contains(query) ||
|
||||||
|
u.PhoneNumber != null && u.PhoneNumber.Contains(query))
|
||||||
|
.Take(50)
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
return users.Select(u => new UserInfo(u.Id, u.Avatar, u.Username, u.DisplayName, u.CreatedAt, u.PhoneNumber, u.Email)).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Knot.Shared.Kernel;
|
||||||
|
|
||||||
|
namespace Knot.Modules.Conversations.Application.Abstractions;
|
||||||
|
|
||||||
|
public interface IUserDeleterService
|
||||||
|
{
|
||||||
|
Task<Result> DeleteUserAsync(Guid userId, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Knot.Contracts.Conversations.Application.Abstractions;
|
||||||
using Knot.Contracts.Messaging.Application.Abstractions;
|
using Knot.Contracts.Messaging.Application.Abstractions;
|
||||||
using Knot.Contracts.Messaging.Domain;
|
using Knot.Contracts.Messaging.Domain;
|
||||||
using Knot.Modules.Conversations.Application.Abstractions;
|
using Knot.Modules.Conversations.Application.Abstractions;
|
||||||
@@ -8,6 +9,7 @@ using Knot.Shared.Kernel;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using ConversationsAbstractions = Knot.Modules.Conversations.Application.Abstractions;
|
||||||
|
|
||||||
namespace Knot.Modules.Conversations;
|
namespace Knot.Modules.Conversations;
|
||||||
|
|
||||||
@@ -22,28 +24,22 @@ public static class DependencyInjection
|
|||||||
services.AddDbContext<ChatsDbContext>(options =>
|
services.AddDbContext<ChatsDbContext>(options =>
|
||||||
options.UseNpgsql(connectionString));
|
options.UseNpgsql(connectionString));
|
||||||
|
|
||||||
// MongoDB Setup for Messages
|
|
||||||
ConversationsMongoDbMapConfigurator.Configure();
|
ConversationsMongoDbMapConfigurator.Configure();
|
||||||
|
|
||||||
var mongoConnectionString = configuration.GetConnectionString("MongoConnection") ?? "mongodb://localhost:27017";
|
var mongoConnectionString = configuration.GetConnectionString("MongoConnection") ?? "mongodb://localhost:27017";
|
||||||
|
|
||||||
// Registration
|
services.AddScoped<ConversationsAbstractions.IChatsUnitOfWork>(sp => sp.GetRequiredService<ChatsDbContext>());
|
||||||
services.AddScoped<IChatsUnitOfWork>(sp => sp.GetRequiredService<ChatsDbContext>());
|
|
||||||
services.AddScoped<IChatRepository, ChatRepository>();
|
services.AddScoped<IChatRepository, ChatRepository>();
|
||||||
services.AddScoped<IFolderRepository, FolderRepository>();
|
services.AddScoped<IFolderRepository, FolderRepository>();
|
||||||
services.AddScoped<IUserChatSettingsRepository, UserChatSettingsRepository>();
|
services.AddScoped<IUserChatSettingsRepository, UserChatSettingsRepository>();
|
||||||
services.AddScoped<IUserFolderSettingsRepository, UserFolderSettingsRepository>();
|
services.AddScoped<IUserFolderSettingsRepository, UserFolderSettingsRepository>();
|
||||||
|
|
||||||
// Messaging Repository - registered in Messaging module
|
|
||||||
// services.AddScoped<IMessageRepository, MessageRepository>();
|
|
||||||
|
|
||||||
// MediatR
|
|
||||||
services.AddMediatR(config =>
|
services.AddMediatR(config =>
|
||||||
config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
|
config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
|
||||||
|
|
||||||
services.AddScoped<Knot.Contracts.Messaging.Application.Abstractions.IChatAccessProvider, Knot.Modules.Conversations.Infrastructure.Services.ChatAccessProvider>();
|
services.AddScoped<Knot.Contracts.Messaging.Application.Abstractions.IChatAccessProvider, Knot.Modules.Conversations.Infrastructure.Services.ChatAccessProvider>();
|
||||||
services.AddScoped<Knot.Contracts.Messaging.Application.Abstractions.IMessageNotifier, Knot.Modules.Conversations.Infrastructure.SignalR.MessageNotifier>();
|
services.AddScoped<Knot.Contracts.Messaging.Application.Abstractions.IMessageNotifier, Knot.Modules.Conversations.Infrastructure.SignalR.MessageNotifier>();
|
||||||
services.AddScoped<IUserStatusService, Knot.Modules.Conversations.Infrastructure.Services.UserStatusService>();
|
services.AddScoped<ConversationsAbstractions.IUserStatusService, Knot.Modules.Conversations.Infrastructure.Services.UserStatusService>();
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Knot.Contracts.Conversations.Abstractions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Knot.Modules.Conversations.Infrastructure.Persistence;
|
||||||
|
|
||||||
|
public sealed class ChatQueryService : IChatQueryService
|
||||||
|
{
|
||||||
|
private readonly ChatsDbContext _context;
|
||||||
|
|
||||||
|
public ChatQueryService(ChatsDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<ChatInfo>> GetAllChatsAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var chats = await _context.Chats.AsNoTracking().ToListAsync(cancellationToken);
|
||||||
|
return chats.Select(c => new ChatInfo(c.Id, c.Avatar)).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-9
@@ -1,22 +1,23 @@
|
|||||||
using MediatR;
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using System.Collections.Generic;
|
||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Knot.Contracts.Conversations.Application.Abstractions;
|
||||||
using Knot.Modules.Conversations.Application.Abstractions;
|
using Knot.Modules.Conversations.Application.Abstractions;
|
||||||
using Knot.Modules.Conversations.Domain;
|
using Knot.Modules.Conversations.Domain;
|
||||||
using Knot.Shared.Kernel;
|
using Knot.Shared.Kernel;
|
||||||
using Knot.Shared.Kernel.Security;
|
using Knot.Shared.Kernel.Security;
|
||||||
using System.Linq;
|
using MediatR;
|
||||||
using System;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Collections.Generic;
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Knot.Modules.Conversations.Infrastructure.Persistence;
|
namespace Knot.Modules.Conversations.Infrastructure.Persistence;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Контекст базы данных для модуля чатов.
|
/// Контекст базы данных для модуля чатов.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ChatsDbContext : DbContext, IChatsUnitOfWork
|
public sealed class ChatsDbContext : DbContext, Knot.Modules.Conversations.Application.Abstractions.IChatsUnitOfWork
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
private readonly IEncryptionService _encryptionService;
|
private readonly IEncryptionService _encryptionService;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
<ProjectReference Include="..\..\Contracts\Messaging\Knot.Contracts.Messaging.csproj" />
|
<ProjectReference Include="..\..\Contracts\Messaging\Knot.Contracts.Messaging.csproj" />
|
||||||
<ProjectReference Include="..\..\Contracts\Conversations\Knot.Contracts.Conversations.csproj" />
|
<ProjectReference Include="..\..\Contracts\Conversations\Knot.Contracts.Conversations.csproj" />
|
||||||
<ProjectReference Include="..\..\Contracts\Profiles\Knot.Contracts.Profiles.csproj" />
|
<ProjectReference Include="..\..\Contracts\Profiles\Knot.Contracts.Profiles.csproj" />
|
||||||
<ProjectReference Include="..\..\Contracts\Stories\Knot.Contracts.Stories.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Carter" Version="10.0.0" />
|
<PackageReference Include="Carter" Version="10.0.0" />
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
||||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Infrastructure\Knot.Shared.Infrastructure.csproj" />
|
<ProjectReference Include="..\..\Shared\Knot.Shared.Infrastructure\Knot.Shared.Infrastructure.csproj" />
|
||||||
<ProjectReference Include="..\..\Contracts\Settings\Knot.Contracts.Settings.csproj" />
|
<ProjectReference Include="..\..\Contracts\Settings\Knot.Contracts.Settings.csproj" />
|
||||||
<ProjectReference Include="..\Settings\Knot.Modules.Settings.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Carter" Version="10.0.0" />
|
<PackageReference Include="Carter" Version="10.0.0" />
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Knot.Contracts.Messaging.Application.Abstractions;
|
using Knot.Contracts.Messaging.Application.Abstractions;
|
||||||
using Knot.Contracts.Messaging.Domain;
|
using Knot.Contracts.Messaging.Domain;
|
||||||
|
using Knot.Modules.Messaging.Application.Abstractions;
|
||||||
using Knot.Modules.Messaging.Infrastructure.Persistence;
|
using Knot.Modules.Messaging.Infrastructure.Persistence;
|
||||||
using Knot.Modules.Messaging.Infrastructure.Persistence.Mongo;
|
using Knot.Modules.Messaging.Infrastructure.Persistence.Mongo;
|
||||||
using Knot.Shared.Kernel;
|
using Knot.Shared.Kernel;
|
||||||
@@ -24,12 +25,10 @@ public static class DependencyInjection
|
|||||||
services.AddScoped<IMongoDatabase>(sp =>
|
services.AddScoped<IMongoDatabase>(sp =>
|
||||||
sp.GetRequiredService<IMongoClient>().GetDatabase("forkmessager_chats"));
|
sp.GetRequiredService<IMongoClient>().GetDatabase("forkmessager_chats"));
|
||||||
|
|
||||||
// Registration
|
|
||||||
services.AddScoped<IMessageRepository, MessageRepository>();
|
services.AddScoped<IMessageRepository, MessageRepository>();
|
||||||
services.AddScoped<IMessageReactionRepository, MessageReactionRepository>();
|
services.AddScoped<IMessageReactionRepository, MessageReactionRepository>();
|
||||||
services.AddScoped<IUserStatsService, UserStatsService>();
|
services.AddScoped<IUserStatsService, UserStatsService>();
|
||||||
|
|
||||||
// MediatR
|
|
||||||
services.AddMediatR(config =>
|
services.AddMediatR(config =>
|
||||||
config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
|
config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Knot.Contracts.Messaging.Abstractions;
|
||||||
|
using Knot.Contracts.Messaging.Domain;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
|
||||||
|
namespace Knot.Modules.Messaging.Infrastructure.Persistence;
|
||||||
|
|
||||||
|
public sealed class MessageQueryService : IMessageQueryService
|
||||||
|
{
|
||||||
|
private readonly IMongoCollection<Message> _messages;
|
||||||
|
|
||||||
|
public MessageQueryService(IMongoDatabase database)
|
||||||
|
{
|
||||||
|
_messages = database.GetCollection<Message>("messages");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<MessageInfo>> GetAllMessagesAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var messages = await _messages.Find(_ => true).ToListAsync(cancellationToken);
|
||||||
|
return messages.Select(m => new MessageInfo(
|
||||||
|
m.Id,
|
||||||
|
m.ChatId,
|
||||||
|
m.State.HasFlag(MessageState.IsDeleted),
|
||||||
|
m is MediaMessage mm && mm.Media.Any() ? mm.Media.First().Url : null
|
||||||
|
)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteMessagesAsync(List<Guid> messageIds, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var filter = Builders<Message>.Filter.In(m => m.Id, messageIds);
|
||||||
|
await _messages.DeleteManyAsync(filter, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -153,6 +153,12 @@ public sealed class MessageRepository : IMessageRepository
|
|||||||
var filter = Builders<Message>.Filter.Eq(m => m.Id, id);
|
var filter = Builders<Message>.Filter.Eq(m => m.Id, id);
|
||||||
await _messages.DeleteOneAsync(filter, cancellationToken);
|
await _messages.DeleteOneAsync(filter, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(Message message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var filter = Builders<Message>.Filter.Eq(m => m.Id, message.Id);
|
||||||
|
await _messages.DeleteOneAsync(filter, cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,16 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Knot.Contracts.Messaging.Application.Abstractions;
|
using Knot.Contracts.Messaging.Abstractions;
|
||||||
using Knot.Contracts.Messaging.Domain;
|
using Knot.Contracts.Messaging.Domain;
|
||||||
|
using Knot.Modules.Messaging.Application.Abstractions;
|
||||||
using MongoDB.Bson;
|
using MongoDB.Bson;
|
||||||
using MongoDB.Driver;
|
using MongoDB.Driver;
|
||||||
|
using ModuleUserStatsService = Knot.Modules.Messaging.Application.Abstractions.IUserStatsService;
|
||||||
|
|
||||||
namespace Knot.Modules.Messaging.Infrastructure.Persistence.Mongo;
|
namespace Knot.Modules.Messaging.Infrastructure.Persistence.Mongo;
|
||||||
|
|
||||||
public sealed class UserStatsService : IUserStatsService
|
public sealed class UserStatsService : ModuleUserStatsService
|
||||||
{
|
{
|
||||||
private readonly IMongoCollection<Message> _messages;
|
private readonly IMongoCollection<Message> _messages;
|
||||||
|
|
||||||
@@ -19,13 +21,13 @@ public sealed class UserStatsService : IUserStatsService
|
|||||||
_messages = database.GetCollection<Message>("messages");
|
_messages = database.GetCollection<Message>("messages");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Dictionary<Guid, UserStats>> GetStatsForUsersAsync(IEnumerable<Guid> userIds, CancellationToken ct = default)
|
public async Task<Dictionary<Guid, Knot.Modules.Messaging.Application.Abstractions.UserStats>> GetStatsForUsersAsync(IEnumerable<Guid> userIds, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var userGuidList = userIds.ToList();
|
var userIdList = userIds.ToList();
|
||||||
if (!userGuidList.Any()) return new Dictionary<Guid, UserStats>();
|
if (!userIdList.Any()) return new Dictionary<Guid, Knot.Modules.Messaging.Application.Abstractions.UserStats>();
|
||||||
|
|
||||||
var stats = await _messages.Aggregate()
|
var stats = await _messages.Aggregate()
|
||||||
.Match(Builders<Message>.Filter.In(m => m.SenderId, userGuidList))
|
.Match(Builders<Message>.Filter.In(m => m.SenderId, userIdList))
|
||||||
.Group(new BsonDocument {
|
.Group(new BsonDocument {
|
||||||
{ "_id", "$SenderId" },
|
{ "_id", "$SenderId" },
|
||||||
{ "Count", new BsonDocument("$sum", 1) },
|
{ "Count", new BsonDocument("$sum", 1) },
|
||||||
@@ -35,7 +37,7 @@ public sealed class UserStatsService : IUserStatsService
|
|||||||
|
|
||||||
return stats.ToDictionary(
|
return stats.ToDictionary(
|
||||||
doc => doc["_id"].AsGuid,
|
doc => doc["_id"].AsGuid,
|
||||||
doc => new UserStats(
|
doc => new Knot.Modules.Messaging.Application.Abstractions.UserStats(
|
||||||
doc["Count"].AsInt32,
|
doc["Count"].AsInt32,
|
||||||
doc.Contains("MediaSize") && !doc["MediaSize"].IsBsonNull ? (long)(doc["MediaSize"].IsInt64 ? doc["MediaSize"].AsInt64 : doc["MediaSize"].AsInt32) : 0L
|
doc.Contains("MediaSize") && !doc["MediaSize"].IsBsonNull ? (long)(doc["MediaSize"].IsInt64 ? doc["MediaSize"].AsInt64 : doc["MediaSize"].AsInt32) : 0L
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
||||||
<ProjectReference Include="..\..\Contracts\Messaging\Knot.Contracts.Messaging.csproj" />
|
<ProjectReference Include="..\..\Contracts\Messaging\Knot.Contracts.Messaging.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Contracts\Settings\Knot.Contracts.Settings.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.8" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.4" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="3.2.0" />
|
<PackageReference Include="MongoDB.Driver" Version="3.2.0" />
|
||||||
<ProjectReference Include="..\Settings\Knot.Modules.Settings.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Knot.Contracts.Relations.Application.Abstractions;
|
||||||
|
using Knot.Contracts.Relations.Domain;
|
||||||
|
using Knot.Modules.Relations.Domain;
|
||||||
|
using Knot.Modules.Relations.Infrastructure.Persistence;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Knot.Modules.Relations.Application.Abstractions;
|
||||||
|
|
||||||
|
internal sealed class FriendshipRepository : IFriendshipRepository
|
||||||
|
{
|
||||||
|
private readonly RelationsDbContext _context;
|
||||||
|
|
||||||
|
public FriendshipRepository(RelationsDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<Knot.Contracts.Relations.Domain.Friendship>> GetAcceptedFriendshipsAsync(Guid userId, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
var friendships = await _context.Set<Knot.Modules.Relations.Domain.Friendship>()
|
||||||
|
.Where(f => f.Status == Knot.Contracts.Relations.Domain.FriendshipStatus.Accepted && (f.UserId == userId || f.FriendId == userId))
|
||||||
|
.ToListAsync(ct);
|
||||||
|
|
||||||
|
return friendships.Select(f => new Knot.Contracts.Relations.Domain.Friendship(
|
||||||
|
f.Id,
|
||||||
|
f.UserId,
|
||||||
|
f.FriendId,
|
||||||
|
f.Status,
|
||||||
|
f.CreatedAt
|
||||||
|
)).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
|
using Knot.Contracts.Relations.Application.Abstractions;
|
||||||
|
using Knot.Modules.Relations.Application.Abstractions;
|
||||||
|
using Knot.Modules.Relations.Infrastructure.Persistence;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Knot.Modules.Relations.Infrastructure.Persistence;
|
|
||||||
|
|
||||||
namespace Knot.Modules.Relations;
|
namespace Knot.Modules.Relations;
|
||||||
|
|
||||||
@@ -13,6 +15,8 @@ public static class DependencyInjection
|
|||||||
services.AddDbContext<RelationsDbContext>(options =>
|
services.AddDbContext<RelationsDbContext>(options =>
|
||||||
options.UseNpgsql(connectionString));
|
options.UseNpgsql(connectionString));
|
||||||
|
|
||||||
|
services.AddScoped<IFriendshipRepository, FriendshipRepository>();
|
||||||
|
|
||||||
services.AddMediatR(config =>
|
services.AddMediatR(config =>
|
||||||
config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
|
config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
|
using Knot.Contracts.Relations.Domain;
|
||||||
using Knot.Shared.Kernel;
|
using Knot.Shared.Kernel;
|
||||||
|
|
||||||
namespace Knot.Modules.Relations.Domain;
|
namespace Knot.Modules.Relations.Domain;
|
||||||
|
|
||||||
public enum FriendshipStatus
|
|
||||||
{
|
|
||||||
Pending,
|
|
||||||
Accepted,
|
|
||||||
Declined
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class Friendship : Entity<Guid>
|
public sealed class Friendship : Entity<Guid>
|
||||||
{
|
{
|
||||||
public Guid UserId { get; private set; }
|
public Guid UserId { get; private set; }
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Contracts\Relations\Knot.Contracts.Relations.csproj" />
|
||||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
||||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Infrastructure\Knot.Shared.Infrastructure.csproj" />
|
<ProjectReference Include="..\..\Shared\Knot.Shared.Infrastructure\Knot.Shared.Infrastructure.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -21,7 +22,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
<ProjectReference Include="..\Settings\Knot.Modules.Settings.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Carter" Version="10.0.0" />
|
<PackageReference Include="Carter" Version="10.0.0" />
|
||||||
<PackageReference Include="Minio" Version="7.0.0" />
|
<PackageReference Include="Minio" Version="7.0.0" />
|
||||||
<ProjectReference Include="..\Settings\Knot.Modules.Settings.csproj" />
|
<ProjectReference Include="..\..\Contracts\Settings\Knot.Contracts.Settings.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Knot.Modules.Stories.Domain;
|
|
||||||
using Knot.Modules.Relations.Domain;
|
|
||||||
|
|
||||||
namespace Knot.Modules.Stories.Application.Abstractions;
|
|
||||||
|
|
||||||
public interface IStoriesDbContext
|
|
||||||
{
|
|
||||||
DbSet<Story> Stories { get; }
|
|
||||||
DbSet<Friendship> Friendships { get; }
|
|
||||||
DbSet<StoryViewer> StoryViewers { get; }
|
|
||||||
DbSet<TEntity> Set<TEntity>() where TEntity : class;
|
|
||||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IStoriesUnitOfWork
|
|
||||||
{
|
|
||||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Knot.Modules.Stories.Application.Abstractions;
|
||||||
|
|
||||||
|
public interface IStoryNotificationService
|
||||||
|
{
|
||||||
|
Task NotifyStoryViewedAsync(
|
||||||
|
Guid storyId,
|
||||||
|
Guid userId,
|
||||||
|
string? username,
|
||||||
|
string? displayName,
|
||||||
|
string? avatar,
|
||||||
|
int newViewsCount,
|
||||||
|
Guid storyOwnerId,
|
||||||
|
CancellationToken ct = default);
|
||||||
|
}
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Knot.Modules.Stories.Domain;
|
|
||||||
|
|
||||||
namespace Knot.Modules.Stories.Application.Abstractions;
|
namespace Knot.Modules.Stories.Application.Abstractions;
|
||||||
|
|
||||||
public interface IUserRepository
|
public interface IUserRepository
|
||||||
{
|
{
|
||||||
Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default);
|
Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default);
|
||||||
Task<UserReplica?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
|
Task<Knot.Contracts.Relations.Domain.UserReplica?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-26
@@ -1,21 +1,15 @@
|
|||||||
using Knot.Modules.Stories.Application.Stories;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediatR;
|
using Knot.Modules.Stories.Application.Abstractions;
|
||||||
using Knot.Shared.Kernel;
|
|
||||||
using Knot.Modules.Stories.Domain;
|
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
using Knot.Modules.Conversations.Infrastructure.SignalR;
|
|
||||||
|
|
||||||
|
|
||||||
using Knot.Modules.Stories.Application.DTOs;
|
using Knot.Modules.Stories.Application.DTOs;
|
||||||
|
using Knot.Modules.Stories.Application.Stories;
|
||||||
|
using Knot.Modules.Stories.Domain;
|
||||||
using Knot.Modules.Stories.Infrastructure.Database;
|
using Knot.Modules.Stories.Infrastructure.Database;
|
||||||
|
using Knot.Shared.Kernel;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Knot.Modules.Stories.Application.Stories.Commands.ViewStory;
|
namespace Knot.Modules.Stories.Application.Stories.Commands.ViewStory;
|
||||||
|
|
||||||
@@ -26,18 +20,18 @@ internal sealed class ViewStoryCommandHandler : ICommandHandler<ViewStoryCommand
|
|||||||
private readonly StoriesDbContext _context;
|
private readonly StoriesDbContext _context;
|
||||||
private readonly Knot.Modules.Stories.Application.Abstractions.IStoryRepository _storyRepository;
|
private readonly Knot.Modules.Stories.Application.Abstractions.IStoryRepository _storyRepository;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IHubContext<ChatHub> _hubContext;
|
private readonly IStoryNotificationService _notificationService;
|
||||||
|
|
||||||
public ViewStoryCommandHandler(
|
public ViewStoryCommandHandler(
|
||||||
StoriesDbContext context,
|
StoriesDbContext context,
|
||||||
Knot.Modules.Stories.Application.Abstractions.IStoryRepository storyRepository,
|
Knot.Modules.Stories.Application.Abstractions.IStoryRepository storyRepository,
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IHubContext<ChatHub> hubContext)
|
IStoryNotificationService notificationService)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_storyRepository = storyRepository;
|
_storyRepository = storyRepository;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_hubContext = hubContext;
|
_notificationService = notificationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<MessageResponse>> Handle(ViewStoryCommand request, CancellationToken cancellationToken)
|
public async Task<Result<MessageResponse>> Handle(ViewStoryCommand request, CancellationToken cancellationToken)
|
||||||
@@ -65,17 +59,15 @@ internal sealed class ViewStoryCommandHandler : ICommandHandler<ViewStoryCommand
|
|||||||
|
|
||||||
var viewer = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
var viewer = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
||||||
|
|
||||||
await _hubContext.Clients.All.SendAsync("story_viewed", new
|
await _notificationService.NotifyStoryViewedAsync(
|
||||||
{
|
story.Id,
|
||||||
storyId = story.Id,
|
request.UserId,
|
||||||
userId = request.UserId,
|
viewer?.Username,
|
||||||
username = viewer?.Username,
|
viewer?.DisplayName,
|
||||||
displayName = viewer?.DisplayName,
|
viewer?.Avatar,
|
||||||
avatar = viewer?.Avatar,
|
story.ViewsCount + 1,
|
||||||
viewedAt = DateTime.UtcNow,
|
story.UserId,
|
||||||
viewCount = story.ViewsCount + 1,
|
cancellationToken);
|
||||||
ownerId = story.UserId
|
|
||||||
}, cancellationToken);
|
|
||||||
}
|
}
|
||||||
return Result.Success(new MessageResponse("Story viewed"));
|
return Result.Success(new MessageResponse("Story viewed"));
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-21
@@ -3,18 +3,14 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediatR;
|
using Knot.Contracts.Relations.Application.Abstractions;
|
||||||
using Knot.Shared.Kernel;
|
|
||||||
using Knot.Modules.Stories.Domain;
|
|
||||||
|
|
||||||
|
|
||||||
using Knot.Modules.Stories.Application.Abstractions;
|
using Knot.Modules.Stories.Application.Abstractions;
|
||||||
using Knot.Modules.Stories.Infrastructure.Database;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
using Knot.Modules.Stories.Application.DTOs;
|
using Knot.Modules.Stories.Application.DTOs;
|
||||||
|
using Knot.Modules.Stories.Domain;
|
||||||
|
using Knot.Modules.Stories.Infrastructure.Database;
|
||||||
|
using Knot.Shared.Kernel;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -27,19 +23,23 @@ internal sealed class GetStoriesQueryHandler : IQueryHandler<GetStoriesQuery, Li
|
|||||||
private readonly StoriesDbContext _context;
|
private readonly StoriesDbContext _context;
|
||||||
private readonly IStoryRepository _storyRepository;
|
private readonly IStoryRepository _storyRepository;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
|
private readonly IFriendshipRepository _friendshipRepository;
|
||||||
|
|
||||||
public GetStoriesQueryHandler(StoriesDbContext context, IStoryRepository storyRepository, IUserRepository userRepository)
|
public GetStoriesQueryHandler(
|
||||||
|
StoriesDbContext context,
|
||||||
|
IStoryRepository storyRepository,
|
||||||
|
IUserRepository userRepository,
|
||||||
|
IFriendshipRepository friendshipRepository)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_storyRepository = storyRepository;
|
_storyRepository = storyRepository;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
|
_friendshipRepository = friendshipRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<List<StoryGroupDto>>> Handle(GetStoriesQuery request, CancellationToken cancellationToken)
|
public async Task<Result<List<StoryGroupDto>>> Handle(GetStoriesQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var friendships = await _context.Set<Friendship>()
|
var friendships = await _friendshipRepository.GetAcceptedFriendshipsAsync(request.UserId, cancellationToken);
|
||||||
.Where(f => f.Status == FriendshipStatus.Accepted && (f.UserId == request.UserId || f.FriendId == request.UserId))
|
|
||||||
.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
var friendIds = friendships.Select(f => f.UserId == request.UserId ? f.FriendId : f.UserId).ToList();
|
var friendIds = friendships.Select(f => f.UserId == request.UserId ? f.FriendId : f.UserId).ToList();
|
||||||
friendIds.Add(request.UserId);
|
friendIds.Add(request.UserId);
|
||||||
@@ -89,14 +89,14 @@ internal sealed class GetStoriesQueryHandler : IQueryHandler<GetStoriesQuery, Li
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.Success(result.OrderBy(r =>
|
return Result.Success(result.OrderBy(r =>
|
||||||
|
{
|
||||||
|
if (r.User.Id == request.UserId)
|
||||||
{
|
{
|
||||||
if (r.User.Id == request.UserId)
|
return 0;
|
||||||
{
|
}
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}).ToList());
|
||||||
return 1;
|
|
||||||
}).ToList());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-8
@@ -2,15 +2,12 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using MediatR;
|
|
||||||
using Knot.Shared.Kernel;
|
|
||||||
using Knot.Modules.Stories.Application.Abstractions;
|
using Knot.Modules.Stories.Application.Abstractions;
|
||||||
using Knot.Modules.Stories.Infrastructure.Database;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
using Knot.Modules.Stories.Application.DTOs;
|
using Knot.Modules.Stories.Application.DTOs;
|
||||||
|
using Knot.Modules.Stories.Infrastructure.Database;
|
||||||
|
using Knot.Shared.Kernel;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -69,7 +66,7 @@ internal sealed class GetUserStoriesQueryHandler : IQueryHandler<GetUserStoriesQ
|
|||||||
return Result.Success(result);
|
return Result.Success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Result<StoryGroupDto>> GetFederatedStories(Knot.Modules.Relations.Domain.UserReplica user, Guid currentUserId, CancellationToken ct)
|
private async Task<Result<StoryGroupDto>> GetFederatedStories(Knot.Contracts.Relations.Domain.UserReplica user, Guid currentUserId, CancellationToken ct)
|
||||||
{
|
{
|
||||||
// В будущем: Реальный вызов через HttpClient к удаленному домену
|
// В будущем: Реальный вызов через HttpClient к удаленному домену
|
||||||
// return await _federationGateway.FetchStoriesAsync(user.Domain, user.Id);
|
// return await _federationGateway.FetchStoriesAsync(user.Domain, user.Id);
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
global using Knot.Modules.Stories.Application.Abstractions;
|
global using Knot.Modules.Stories.Application.Abstractions;
|
||||||
global using Knot.Modules.Stories.Domain;
|
global using Knot.Modules.Stories.Domain;
|
||||||
global using Knot.Modules.Relations.Domain;
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Knot.Contracts.Stories.Abstractions;
|
||||||
|
using Knot.Modules.Stories.Domain;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
|
||||||
|
namespace Knot.Modules.Stories.Infrastructure.Persistence.Mongo;
|
||||||
|
|
||||||
|
public sealed class StoryQueryService : IStoryQueryService
|
||||||
|
{
|
||||||
|
private readonly IMongoCollection<Story> _stories;
|
||||||
|
|
||||||
|
public StoryQueryService(IMongoDatabase database)
|
||||||
|
{
|
||||||
|
_stories = database.GetCollection<Story>("stories");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<StoryInfo>> GetAllStoriesAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var stories = await _stories.Find(_ => true).ToListAsync(cancellationToken);
|
||||||
|
return stories.Select(s => new StoryInfo(s.Id, s.MediaUrl)).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Knot.Modules.Stories.Application.Abstractions;
|
||||||
|
|
||||||
|
internal sealed class StoryNotificationService : IStoryNotificationService
|
||||||
|
{
|
||||||
|
private readonly ILogger<StoryNotificationService> _logger;
|
||||||
|
|
||||||
|
public StoryNotificationService(ILogger<StoryNotificationService> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task NotifyStoryViewedAsync(
|
||||||
|
Guid storyId,
|
||||||
|
Guid userId,
|
||||||
|
string? username,
|
||||||
|
string? displayName,
|
||||||
|
string? avatar,
|
||||||
|
int newViewsCount,
|
||||||
|
Guid storyOwnerId,
|
||||||
|
CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
// TODO: Реализовать отправку через SignalR
|
||||||
|
// Пока заглушка - в будущем использовать IHubContext
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Story {StoryId} viewed by user {UserId}. New views count: {ViewsCount}",
|
||||||
|
storyId, userId, newViewsCount);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,10 +7,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Contracts\Relations\Knot.Contracts.Relations.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Contracts\Conversations\Knot.Contracts.Conversations.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Contracts\Settings\Knot.Contracts.Settings.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Contracts\Stories\Knot.Contracts.Stories.csproj" />
|
||||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
||||||
<ProjectReference Include="..\Conversations\Knot.Modules.Conversations.csproj" />
|
|
||||||
<ProjectReference Include="..\Relations\Knot.Modules.Relations.csproj" />
|
|
||||||
<ProjectReference Include="..\Messaging\Knot.Modules.Messaging.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -24,7 +25,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
<ProjectReference Include="..\Settings\Knot.Modules.Settings.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
+4
-6
@@ -1,5 +1,3 @@
|
|||||||
using Knot.Modules.Conversations.Domain;
|
|
||||||
using Knot.Modules.Messaging.Domain;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -8,11 +6,11 @@ using System.IO.Compression;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AngleSharp.Html.Parser;
|
|
||||||
using AngleSharp.Dom;
|
using AngleSharp.Dom;
|
||||||
using Knot.Shared.Kernel;
|
using AngleSharp.Html.Parser;
|
||||||
using Knot.Contracts.Settings.Application.Abstractions;
|
using Knot.Contracts.Settings.Application.Abstractions;
|
||||||
using Knot.Contracts.Settings.Application.DTOs;
|
using Knot.Contracts.Settings.Application.DTOs;
|
||||||
|
using Knot.Shared.Kernel;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace Knot.Modules.TelegramImport.Application.TelegramImport;
|
namespace Knot.Modules.TelegramImport.Application.TelegramImport;
|
||||||
@@ -43,12 +41,12 @@ internal sealed class AnalyzeImportCommandHandler : ICommandHandler<AnalyzeImpor
|
|||||||
{
|
{
|
||||||
if (request.FileStream == null || request.FileStream.Length == 0)
|
if (request.FileStream == null || request.FileStream.Length == 0)
|
||||||
{
|
{
|
||||||
return Result.Failure<AnalyzeImportResponseDto>(ChatErrors.FileEmpty);
|
return Result.Failure<AnalyzeImportResponseDto>(TelegramImportErrors.FileEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request.FileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
if (!request.FileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return Result.Failure<AnalyzeImportResponseDto>(ChatErrors.FileInvalidExtension);
|
return Result.Failure<AnalyzeImportResponseDto>(TelegramImportErrors.FileInvalidExtension);
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = Guid.NewGuid();
|
var token = Guid.NewGuid();
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Knot.Modules.TelegramImport.Application.TelegramImport;
|
||||||
|
|
||||||
|
public static class TelegramImportErrors
|
||||||
|
{
|
||||||
|
public static readonly Knot.Shared.Kernel.Error FileEmpty = new("File.Empty", "No file uploaded");
|
||||||
|
public static readonly Knot.Shared.Kernel.Error FileInvalidExtension = new("File.InvalidExtension", "Only .zip files are supported");
|
||||||
|
}
|
||||||
+27
-27
@@ -4,17 +4,17 @@ using System.IO.Compression;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Knot.Contracts.Conversations.Application.Abstractions;
|
||||||
|
using Knot.Contracts.Conversations.Domain;
|
||||||
|
using Knot.Contracts.Messaging.Application.Abstractions;
|
||||||
|
using Knot.Contracts.Messaging.Domain;
|
||||||
|
using Knot.Modules.TelegramImport.Application.Abstractions;
|
||||||
|
using Knot.Modules.TelegramImport.Application.TelegramImport;
|
||||||
|
using Knot.Modules.TelegramImport.Infrastructure.Background;
|
||||||
|
using Knot.Shared.Kernel.Storage;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Knot.Modules.TelegramImport.Application.Abstractions;
|
|
||||||
using Knot.Modules.TelegramImport.Application.TelegramImport;
|
|
||||||
using Knot.Modules.Messaging.Application.Abstractions;
|
|
||||||
using Knot.Modules.Conversations.Application.Abstractions;
|
|
||||||
using Knot.Modules.Messaging.Domain;
|
|
||||||
using Knot.Modules.Conversations.Domain;
|
|
||||||
using Knot.Shared.Kernel.Storage;
|
|
||||||
using Knot.Modules.TelegramImport.Infrastructure.Background;
|
|
||||||
|
|
||||||
namespace Knot.Modules.TelegramImport.Infrastructure.Background;
|
namespace Knot.Modules.TelegramImport.Infrastructure.Background;
|
||||||
|
|
||||||
@@ -57,31 +57,31 @@ public class TelegramImportWorker : BackgroundService
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var archive = ZipFile.OpenRead(zipPath);
|
using var archive = ZipFile.OpenRead(zipPath);
|
||||||
var entries = archive.Entries.Where(e => e.Name.StartsWith("messages") && e.Name.EndsWith(".html")).ToList();
|
var entries = archive.Entries.Where(e => e.Name.StartsWith("messages") && e.Name.EndsWith(".html")).ToList();
|
||||||
|
|
||||||
// 1. Создание чата (уже было в оригинале, но здесь в фоне)
|
// 1. Создание чата (уже было в оригинале, но здесь в фоне)
|
||||||
Guid targetChatId = Guid.NewGuid(); // Упростим логику для демонстрации рефакторинга
|
Guid targetChatId = Guid.NewGuid(); // Упростим логику для демонстрации рефакторинга
|
||||||
|
|
||||||
foreach (var entry in entries)
|
foreach (var entry in entries)
|
||||||
{
|
{
|
||||||
using var stream = entry.Open();
|
using var stream = entry.Open();
|
||||||
var messages = await parser.ParseMessagesAsync(stream, "", ct);
|
var messages = await parser.ParseMessagesAsync(stream, "", ct);
|
||||||
|
|
||||||
foreach (var m in messages)
|
foreach (var m in messages)
|
||||||
{
|
{
|
||||||
Guid senderGuid = request.Mapping.TryGetValue(m.SenderName ?? "", out var sid) ? sid : request.CurrentUserId;
|
Guid senderGuid = request.Mapping.TryGetValue(m.SenderName ?? "", out var sid) ? sid : request.CurrentUserId;
|
||||||
|
|
||||||
var textMsg = new TextMessage(Guid.NewGuid(), targetChatId, senderGuid, m.Content, null, null, null, m.CreatedAt, true);
|
var textMsg = new TextMessage(Guid.NewGuid(), targetChatId, senderGuid, m.Content, null, null, null, m.CreatedAt, true);
|
||||||
msgRepo.Add(textMsg);
|
msgRepo.Add(textMsg);
|
||||||
|
|
||||||
jobInfo.ProcessedMessages++;
|
jobInfo.ProcessedMessages++;
|
||||||
_jobStore.AddOrUpdate(jobInfo);
|
_jobStore.AddOrUpdate(jobInfo);
|
||||||
}
|
}
|
||||||
await uow.SaveChangesAsync(ct);
|
await uow.SaveChangesAsync(ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
jobInfo.Status = ImportJobStatus.Completed;
|
jobInfo.Status = ImportJobStatus.Completed;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,9 +2,14 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
||||||
<ProjectReference Include="..\Conversations\Knot.Modules.Conversations.csproj" />
|
<ProjectReference Include="..\..\Contracts\Conversations\Knot.Contracts.Conversations.csproj" />
|
||||||
<ProjectReference Include="..\Messaging\Knot.Modules.Messaging.csproj" />
|
<ProjectReference Include="..\..\Contracts\Messaging\Knot.Contracts.Messaging.csproj" />
|
||||||
<ProjectReference Include="..\Settings\Knot.Modules.Settings.csproj" />
|
<ProjectReference Include="..\..\Contracts\Settings\Knot.Contracts.Settings.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AngleSharp" Version="1.1.2" />
|
||||||
|
<PackageReference Include="Carter" Version="10.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -7,14 +7,11 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
|
||||||
<ProjectReference Include="..\..\Shared\Knot.Shared.Infrastructure\Knot.Shared.Infrastructure.csproj" />
|
<ProjectReference Include="..\..\Shared\Knot.Shared.Infrastructure\Knot.Shared.Infrastructure.csproj" />
|
||||||
<ProjectReference Include="..\Conversations\Knot.Modules.Conversations.csproj" />
|
<ProjectReference Include="..\..\Contracts\Settings\Knot.Contracts.Settings.csproj" />
|
||||||
<ProjectReference Include="..\Auth\Knot.Modules.Auth.csproj" />
|
|
||||||
<ProjectReference Include="..\Stories\Knot.Modules.Stories.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Carter" Version="10.0.0" />
|
<PackageReference Include="Carter" Version="10.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0-preview.1.25120.3" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0-preview.1.25120.3" />
|
||||||
<ProjectReference Include="..\Settings\Knot.Modules.Settings.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user