Gestion de bibliothèque personnelle auto-hébergée : catalogue, prêts, scan de code-barres, consultation hors-ligne. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
4.4 KiB
C#
100 lines
4.4 KiB
C#
using MaBibli.Api.Services.Prets;
|
|
using MaBibli.Shared.Dtos;
|
|
|
|
namespace MaBibli.Api.Endpoints;
|
|
|
|
public static class PretsEndpoints
|
|
{
|
|
/// <summary>
|
|
/// Prêts : qui a quoi, depuis quand, et l'historique de ce qui est revenu.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <b>Aucun point d'entrée ne filtre sur l'utilisateur courant</b>, et aucun n'enregistre qui
|
|
/// a saisi le prêt. La bibliothèque est commune : tout le foyer voit les mêmes prêts, et
|
|
/// n'importe qui peut noter le retour d'un livre qu'il a récupéré.
|
|
/// <para>
|
|
/// L'emprunteur est du <b>texte libre</b> : ce sont des personnes extérieures au foyer, sans
|
|
/// compte YunoHost. Ne jamais le rapprocher d'un identifiant d'utilisateur.
|
|
/// </para>
|
|
/// </remarks>
|
|
public static IEndpointRouteBuilder MapPretsEndpoints(this IEndpointRouteBuilder routes)
|
|
{
|
|
var prets = routes.MapGroup("/api/prets").WithTags("Prêts");
|
|
|
|
prets.MapGet("/en-cours", async (IServicePrets service, CancellationToken ct) =>
|
|
Results.Ok(await service.EnCoursAsync(ct)))
|
|
.WithName("ListerPretsEnCours")
|
|
.WithSummary("Les livres qui ne sont pas à la maison, du prêt le plus ancien au plus récent.")
|
|
.WithDescription(
|
|
"L'ordre est volontairement croissant : ce qu'on cherche ici, c'est le livre "
|
|
+ "sorti depuis des mois qu'on avait oublié, pas celui prêté hier.")
|
|
.Produces<IReadOnlyList<PretDto>>();
|
|
|
|
prets.MapPost("/{id:int}/retour", async (
|
|
int id,
|
|
RetourPret retour,
|
|
IServicePrets service,
|
|
CancellationToken ct) =>
|
|
{
|
|
var resultat = await service.RendreAsync(id, retour, ct);
|
|
|
|
if (resultat.EstIntrouvable)
|
|
{
|
|
return Results.NotFound();
|
|
}
|
|
|
|
return resultat.Erreur is not null
|
|
? Results.BadRequest(new { erreur = resultat.Erreur })
|
|
: Results.Ok(resultat.Pret);
|
|
})
|
|
.WithName("MarquerPretRendu")
|
|
.WithSummary("Clôt un prêt en posant sa date de retour. Le prêt n'est pas supprimé : il devient l'historique.")
|
|
.Produces<PretDto>()
|
|
.Produces(StatusCodes.Status400BadRequest)
|
|
.Produces(StatusCodes.Status404NotFound);
|
|
|
|
var parLivre = routes.MapGroup("/api/livres/{livreId:int}/prets").WithTags("Prêts");
|
|
|
|
parLivre.MapGet("/", async (int livreId, IServicePrets service, CancellationToken ct) =>
|
|
{
|
|
var historique = await service.HistoriqueAsync(livreId, ct);
|
|
return historique is null ? Results.NotFound() : Results.Ok(historique);
|
|
})
|
|
.WithName("HistoriquePretsLivre")
|
|
.WithSummary("Tous les prêts de ce livre, clos comme en cours, du plus récent au plus ancien.")
|
|
.WithDescription(
|
|
"Une liste vide signifie « jamais prêté » ; un livre inconnu répond 404. "
|
|
+ "Les prêts clos ne disparaissent jamais : c'est la raison d'être de la table.")
|
|
.Produces<IReadOnlyList<PretDto>>()
|
|
.Produces(StatusCodes.Status404NotFound);
|
|
|
|
parLivre.MapPost("/", async (
|
|
int livreId,
|
|
EnregistrementPret saisie,
|
|
IServicePrets service,
|
|
CancellationToken ct) =>
|
|
{
|
|
var resultat = await service.PreterAsync(livreId, saisie, ct);
|
|
|
|
if (resultat.EstIntrouvable)
|
|
{
|
|
return Results.NotFound();
|
|
}
|
|
|
|
return resultat.Erreur is not null
|
|
? Results.BadRequest(new { erreur = resultat.Erreur })
|
|
: Results.Created($"/api/prets/{resultat.Pret!.Id}", resultat.Pret);
|
|
})
|
|
.WithName("PreterLivre")
|
|
.WithSummary("Prête ce livre à quelqu'un.")
|
|
.WithDescription(
|
|
"Refusé si le livre est déjà sorti (un exemplaire ne se prête pas deux fois) ou "
|
|
+ "s'il est au format numérique (une fiche d'ebook n'a pas d'exemplaire à confier).")
|
|
.Produces<PretDto>(StatusCodes.Status201Created)
|
|
.Produces(StatusCodes.Status400BadRequest)
|
|
.Produces(StatusCodes.Status404NotFound);
|
|
|
|
return routes;
|
|
}
|
|
}
|