Изображения в услугах, поиске, возможность открыть карточку услуги

This commit is contained in:
Халимов Рустам
2026-03-06 23:14:39 +03:00
parent 420a86b92f
commit f5e20c1fb2
16 changed files with 480 additions and 11 deletions
@@ -36,13 +36,16 @@ public record CreateOfferCommand : IRequest<Result<Guid>>
/// </summary>
public JsonDocument? Attributes { get; init; }
public CreateOfferCommand(Guid categoryId, string title, string description, Price price, JsonDocument? attributes)
public List<string>? Images { get; init; }
public CreateOfferCommand(Guid categoryId, string title, string description, Price price, JsonDocument? attributes, List<string>? images)
{
CategoryId = categoryId;
Title = title;
Description = description;
Price = price;
Attributes = attributes;
Images = images;
}
public CreateOfferCommand() { }
@@ -70,7 +73,8 @@ public class CreateOfferCommandHandler : IRequestHandler<CreateOfferCommand, Res
request.Title,
request.Description,
request.Price,
request.Attributes
request.Attributes,
request.Images
);
await _repository.AddAsync(offer, cancellationToken);
@@ -0,0 +1,32 @@
using MediatR;
using Nashel.BuildingBlocks.Application.Abstractions;
using Nashel.Modules.Catalog.Domain.Repositories;
namespace Nashel.Modules.Catalog.Application.Commands;
public record DeleteOfferCommand(Guid OfferId) : IRequest<bool>;
public class DeleteOfferCommandHandler : IRequestHandler<DeleteOfferCommand, bool>
{
private readonly IOfferRepository _repository;
private readonly ICurrentUserService _currentUser;
public DeleteOfferCommandHandler(IOfferRepository repository, ICurrentUserService currentUser)
{
_repository = repository;
_currentUser = currentUser;
}
public async Task<bool> Handle(DeleteOfferCommand request, CancellationToken cancellationToken)
{
var offer = await _repository.GetByIdAsync(request.OfferId, cancellationToken);
if (offer == null) throw new Exception("Offer not found");
if (offer.PerformerId != _currentUser.UserId)
throw new UnauthorizedAccessException("Not your offer");
offer.Delete();
await _repository.UpdateAsync(offer, cancellationToken);
return true;
}
}
@@ -0,0 +1,32 @@
using MediatR;
using Nashel.BuildingBlocks.Application.Abstractions;
using Nashel.Modules.Catalog.Domain.Repositories;
namespace Nashel.Modules.Catalog.Application.Commands;
public record ToggleOfferStatusCommand(Guid OfferId) : IRequest<bool>;
public class ToggleOfferStatusCommandHandler : IRequestHandler<ToggleOfferStatusCommand, bool>
{
private readonly IOfferRepository _repository;
private readonly ICurrentUserService _currentUser;
public ToggleOfferStatusCommandHandler(IOfferRepository repository, ICurrentUserService currentUser)
{
_repository = repository;
_currentUser = currentUser;
}
public async Task<bool> Handle(ToggleOfferStatusCommand request, CancellationToken cancellationToken)
{
var offer = await _repository.GetByIdAsync(request.OfferId, cancellationToken);
if (offer == null) throw new Exception("Offer not found");
if (offer.PerformerId != _currentUser.UserId)
throw new UnauthorizedAccessException("Not your offer");
offer.ToggleActive();
await _repository.UpdateAsync(offer, cancellationToken);
return offer.IsActive;
}
}
@@ -0,0 +1,48 @@
using System.Text.Json;
using MediatR;
using Nashel.BuildingBlocks.Application.Abstractions;
using Nashel.Modules.Catalog.Domain.Aggregates;
using Nashel.Modules.Catalog.Domain.Repositories;
using Nashel.Modules.Catalog.Domain.ValueObjects;
namespace Nashel.Modules.Catalog.Application.Commands;
public record UpdateOfferCommand(
Guid OfferId,
string Title,
string Description,
decimal PriceAmount,
int PriceType, // 0-Fixed, 1-Hourly, 2-Negotiable
Dictionary<string, string>? Attributes,
List<string>? Images) : IRequest<bool>;
public class UpdateOfferCommandHandler : IRequestHandler<UpdateOfferCommand, bool>
{
private readonly IOfferRepository _repository;
private readonly ICurrentUserService _currentUser;
public UpdateOfferCommandHandler(IOfferRepository repository, ICurrentUserService currentUser)
{
_repository = repository;
_currentUser = currentUser;
}
public async Task<bool> Handle(UpdateOfferCommand request, CancellationToken cancellationToken)
{
var offer = await _repository.GetByIdAsync(request.OfferId, cancellationToken);
if (offer == null) throw new Exception("Offer not found");
if (offer.PerformerId != _currentUser.UserId)
throw new UnauthorizedAccessException("Not your offer");
var price = new Price(request.PriceAmount, (Nashel.Modules.Catalog.Domain.Enums.OfferType)request.PriceType);
var jsonAttrs = request.Attributes != null && request.Attributes.Count > 0
? JsonDocument.Parse(JsonSerializer.Serialize(request.Attributes))
: null;
offer.Update(request.Title, request.Description, price, jsonAttrs, request.Images);
await _repository.UpdateAsync(offer, cancellationToken);
return true;
}
}
@@ -0,0 +1,43 @@
using MediatR;
using Nashel.BuildingBlocks.Application.Abstractions;
namespace Nashel.Modules.Catalog.Application.Commands;
public record UploadOfferImageCommand(byte[] Content, string FileName) : IRequest<string>;
public class UploadOfferImageCommandHandler : IRequestHandler<UploadOfferImageCommand, string>
{
private const long MaxFileSize = 5 * 1024 * 1024; // 5MB
private static readonly string[] AllowedExtensions = { ".jpg", ".jpeg", ".png", ".webp" };
private readonly ICurrentUserService _currentUserService;
public UploadOfferImageCommandHandler(ICurrentUserService currentUserService)
{
_currentUserService = currentUserService;
}
public Task<string> Handle(UploadOfferImageCommand request, CancellationToken cancellationToken)
{
var userId = _currentUserService.UserId;
if (userId == null) throw new UnauthorizedAccessException();
if (request.Content.Length > MaxFileSize)
throw new InvalidOperationException("Размер файла не должен превышать 5 МБ");
var extension = Path.GetExtension(request.FileName).ToLowerInvariant();
if (Array.IndexOf(AllowedExtensions, extension) == -1)
throw new InvalidOperationException("Допустимые форматы: JPG, PNG, WEBP");
var base64String = Convert.ToBase64String(request.Content);
var mimeType = extension switch
{
".png" => "image/png",
".webp" => "image/webp",
_ => "image/jpeg"
};
return Task.FromResult($"data:{mimeType};base64,{base64String}");
}
}