902942eee6
continuous-integration/drone/pr Build is passing
Sealed qualifying public classes Migrated database changes
187 lines
7.7 KiB
C#
187 lines
7.7 KiB
C#
using LiteCharms.Features.MidrandBooks.Abstractions;
|
|
using LiteCharms.Features.MidrandBooks.AuthorBooks.Models;
|
|
using LiteCharms.Features.MidrandBooks.Authors.Models;
|
|
using LiteCharms.Features.MidrandBooks.Extensions;
|
|
using LiteCharms.Features.MidrandBooks.Postgres;
|
|
using LiteCharms.Features.Models;
|
|
|
|
namespace LiteCharms.Features.MidrandBooks.Authors;
|
|
|
|
public sealed class AuthorService(IDbContextFactory<MidrandBooksDbContext> contextFactory) : IService
|
|
{
|
|
public async ValueTask<Result<AuthorBook[]>> GetAuthorBooksAsync(long authorId, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
|
|
|
var author = await context.Authors.FirstOrDefaultAsync(a => a.Id == authorId, cancellationToken);
|
|
|
|
if (author is null)
|
|
return Result.Fail<AuthorBook[]>(new Error($"Author with ID {authorId} not found"));
|
|
|
|
var books = await context.Books
|
|
.AsNoTracking()
|
|
.Include(b => b.Author)
|
|
.Include(b => b.Product!.Price)
|
|
.OrderByDescending(b => b.CreatedAt)
|
|
.Where(p => p.AuthorId == authorId)
|
|
.AsSplitQuery()
|
|
.ToArrayAsync(cancellationToken);
|
|
|
|
return books?.Length > 0
|
|
? Result.Ok(books.Select(b => b.ToModel()).ToArray())
|
|
: Result.Fail<AuthorBook[]>(new Error($"No books found for author with ID {authorId}"));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Result.Fail<AuthorBook[]>(new Error(ex.Message).CausedBy(ex));
|
|
}
|
|
}
|
|
|
|
public async ValueTask<Result> UpdateAuthorStatusAsync(long authorId, bool isEnabled, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
|
|
|
var author = await context.Authors.FirstOrDefaultAsync(a => a.Id == authorId, cancellationToken);
|
|
|
|
if (author is null)
|
|
return Result.Fail(new Error($"Author with ID {authorId} not found"));
|
|
|
|
author.UpdatedAt = DateTime.UtcNow;
|
|
author.Enabled = isEnabled;
|
|
|
|
return await context.SaveChangesAsync(cancellationToken) > 0
|
|
? Result.Ok()
|
|
: Result.Fail(new Error($"Failed to change status of author with ID {authorId}"));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
|
}
|
|
}
|
|
|
|
public async ValueTask<Result<Author>> GetAuthorAsync(long authorId, CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
|
|
|
var author = await context.Authors.FirstOrDefaultAsync(a => a.Id == authorId, cancellationToken);
|
|
|
|
return author is not null
|
|
? Result.Ok(author.ToModel())
|
|
: Result.Fail<Author>(new Error($"Author with ID {authorId} not found"));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Result.Fail<Author>(new Error(ex.Message).CausedBy(ex));
|
|
}
|
|
}
|
|
|
|
public async ValueTask<Result<Author[]>> GetAuthors(DateRange range, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
var fromDate = range.From.ToDateTime(TimeOnly.MinValue);
|
|
var toDate = range.To.ToDateTime(TimeOnly.MaxValue);
|
|
|
|
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
|
|
|
var authors = await context.Authors.AsNoTracking()
|
|
.OrderByDescending(o => o.CreatedAt)
|
|
.ThenByDescending(o => o.UpdatedAt)
|
|
.Where(a => a.CreatedAt >= fromDate && a.CreatedAt <= toDate)
|
|
.Take(range.MaxRecords)
|
|
.ToArrayAsync(cancellationToken);
|
|
|
|
return authors?.Length > 0
|
|
? Result.Ok(authors.Select(a => a.ToModel()).ToArray())
|
|
: Result.Fail<Author[]>(new Error("No authors found in the specified date range."));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Result.Fail<Author[]>(new Error(ex.Message).CausedBy(ex));
|
|
}
|
|
}
|
|
|
|
public async ValueTask<Result> UpdateAuthorAsync(long authorId, UpdateAuthor request, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
|
|
|
if (await context.Authors.AnyAsync(a => a.Name == request.Name && a.LastName == request.LastName, cancellationToken))
|
|
return Result.Fail(new Error($"An author with the name {request.Name} {request.LastName} already exists"));
|
|
|
|
if (await context.Authors.AnyAsync(a => a.Email == request.Email, cancellationToken))
|
|
return Result.Fail(new Error($"An author with the email {request.Email} already exists"));
|
|
|
|
var author = await context.Authors.FirstOrDefaultAsync(a => a.Id == authorId, cancellationToken);
|
|
|
|
if (author is null)
|
|
return Result.Fail(new Error($"Author with ID {authorId} not found"));
|
|
|
|
author.UpdatedAt = DateTime.UtcNow;
|
|
author.PublisherType = request.PublisherType;
|
|
author.Company = request.Company;
|
|
author.VatNumber = request.VatNumber;
|
|
author.Name = request.Name;
|
|
author.LastName = request.LastName;
|
|
author.Biography = request.Biography;
|
|
author.Email = request.Email;
|
|
author.Website = request.Website;
|
|
author.ImageUrl = request.ImageUrl;
|
|
author.ThumbnailImageUrl = request.ThumbnailImageUrl;
|
|
author.SocialMedia = request.SocialMedia;
|
|
|
|
return await context.SaveChangesAsync(cancellationToken) > 0
|
|
? Result.Ok()
|
|
: Result.Fail(new Error($"Failed to update author with ID {authorId}"));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
|
}
|
|
}
|
|
|
|
public async ValueTask<Result<long>> CreateAuthorAsync(CreateAuthor request, CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
|
|
|
if(await context.Authors.AnyAsync(a => a.Name == request.Name && a.LastName == request.LastName, cancellationToken))
|
|
return Result.Fail<long>(new Error($"An author with the name {request.Name} {request.LastName} already exists"));
|
|
|
|
if(await context.Authors.AnyAsync(a => a.Email == request.Email, cancellationToken))
|
|
return Result.Fail<long>(new Error($"An author with the email {request.Email} already exists"));
|
|
|
|
var newAuthor = context.Authors.Add(new Entities.Author
|
|
{
|
|
Company = request.Company,
|
|
VatNumber = request.VatNumber,
|
|
PublisherType = request.PublisherType,
|
|
Name = request.Name,
|
|
LastName = request.LastName,
|
|
Biography = request.Biography,
|
|
Email = request.Email,
|
|
Website = request.Website,
|
|
ImageUrl = request.ImageUrl,
|
|
ThumbnailImageUrl = request.ThumbnailImageUrl,
|
|
SocialMedia = request.SocialMedia
|
|
});
|
|
|
|
return await context.SaveChangesAsync(cancellationToken) > 0
|
|
? Result.Ok(newAuthor.Entity.Id)
|
|
: Result.Fail<long>(new Error($"Failed to create author {request.Name} {request.LastName}"));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Result.Fail<long>(new Error(ex.Message).CausedBy(ex));
|
|
}
|
|
}
|
|
}
|