Фронт

This commit is contained in:
Халимов Рустам
2026-03-27 16:04:46 +03:00
parent 28fb8c25de
commit 030ae1e4e4
19 changed files with 234 additions and 174 deletions

View File

@@ -1,33 +1,23 @@
using Knot.Modules.Profiles.Application.Abstractions;
using Knot.Shared.Kernel.Storage;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Knot.Modules.Profiles.Infrastructure.Database;
/// <summary>
/// Адаптер к S3-хранилищу для аватаров.
/// Инжектирует общий IFileStorageService и передает ему управление.
/// </summary>
internal sealed class AvatarStorageService : IAvatarStorageService
public class AvatarStorageService : IAvatarStorageService
{
private readonly IFileStorageService _storage;
private readonly IFileStorageService _fileStorage;
public AvatarStorageService(IFileStorageService storage)
public AvatarStorageService(IFileStorageService fileStorage)
{
_storage = storage;
_fileStorage = fileStorage;
}
public async Task<string> UploadAsync(Stream stream, string fileName, string contentType, CancellationToken ct = default)
public async Task<string> UploadAsync(Stream content, string fileName, string contentType, CancellationToken ct = default)
{
return await _storage.UploadFileAsync(stream, fileName, contentType);
// IFileStorageService не принимает CancellationToken в UploadFileAsync
return await _fileStorage.UploadFileAsync(content, fileName, contentType);
}
public async Task DeleteAsync(string fileId, CancellationToken ct = default)
{
// Если fileId содержит "/api/files/", обрезаем его до чистого ID
var cleanId = fileId.Replace("/api/files/", "");
await _storage.DeleteFileAsync(cleanId);
// IFileStorageService не принимает CancellationToken в DeleteFileAsync
await _fileStorage.DeleteFileAsync(fileId);
}
}

View File

@@ -1,14 +1,6 @@
using Knot.Modules.Profiles.Application.Abstractions;
using Knot.Modules.Profiles.Domain;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Knot.Modules.Profiles.Infrastructure.Database;
internal sealed class ProfileRepository : IProfileRepository
public class ProfileRepository : IProfileRepository
{
private readonly IMongoCollection<ProfileDocument> _profiles;
@@ -17,9 +9,14 @@ internal sealed class ProfileRepository : IProfileRepository
_profiles = database.GetCollection<ProfileDocument>("profiles");
}
public async Task<ProfileDocument?> GetByIdAsync(Guid userId, CancellationToken ct = default)
public async Task<ProfileDocument?> GetByIdAsync(Guid id, CancellationToken ct = default)
{
return await _profiles.Find(p => p.Id == userId).FirstOrDefaultAsync(ct);
return await _profiles.Find(p => p.Id == id).FirstOrDefaultAsync(ct);
}
public async Task<ProfileDocument?> GetByUsernameAsync(string username, CancellationToken ct = default)
{
return await _profiles.Find(p => p.Username == username).FirstOrDefaultAsync(ct);
}
public async Task AddAsync(ProfileDocument profile, CancellationToken ct = default)
@@ -29,20 +26,19 @@ internal sealed class ProfileRepository : IProfileRepository
public async Task UpdateAsync(ProfileDocument profile, CancellationToken ct = default)
{
await _profiles.ReplaceOneAsync(p => p.Id == profile.Id, profile, new ReplaceOptions { IsUpsert = false }, ct);
await _profiles.ReplaceOneAsync(p => p.Id == profile.Id, profile, new ReplaceOptions { IsUpsert = true }, ct);
}
public async Task<List<ProfileDocument>> SearchAsync(string query, CancellationToken ct = default)
{
if (string.IsNullOrWhiteSpace(query))
return new List<ProfileDocument>();
return await _profiles.Find(_ => true).Limit(50).ToListAsync(ct);
// Простой регистронезависимый поиск по Regex (в реальной системе лучше использовать Text Index)
var filter = Builders<ProfileDocument>.Filter.Or(
Builders<ProfileDocument>.Filter.Regex(p => p.Username, new MongoDB.Bson.BsonRegularExpression(query, "i")),
Builders<ProfileDocument>.Filter.Regex(p => p.DisplayName, new MongoDB.Bson.BsonRegularExpression(query, "i"))
Builders<ProfileDocument>.Filter.Regex(p => p.Username, new BsonRegularExpression(query, "i")),
Builders<ProfileDocument>.Filter.Regex(p => p.DisplayName, new BsonRegularExpression(query, "i"))
);
return await _profiles.Find(filter).Limit(20).ToListAsync(ct);
return await _profiles.Find(filter).Limit(50).ToListAsync(ct);
}
}

View File

@@ -1,12 +1,8 @@
using Knot.Modules.Profiles.Application.Abstractions;
using System.Threading;
using System.Threading.Tasks;
namespace Knot.Modules.Profiles.Infrastructure.Database;
internal sealed class ProfilesUnitOfWork : IProfilesUnitOfWork
public class ProfilesUnitOfWork : IProfilesUnitOfWork
{
// MongoDB updates are atomic per document by default in the driver,
// so for simple ProfileDocument updates, we don't need distributed transactions.
// MongoDB не поддерживает транзакции без репликации в простом виде,
// поэтому UnitOfWork здесь формальный для соответствия интерфейсу.
public Task SaveChangesAsync(CancellationToken ct = default) => Task.CompletedTask;
}