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>
128 lines
5.4 KiB
C#
128 lines
5.4 KiB
C#
using MaBibli.Shared.Isbn;
|
|
using MaBibli.Shared.Textes;
|
|
|
|
namespace MaBibli.Shared.Entites;
|
|
|
|
/// <summary>
|
|
/// Une revue — magazine, périodique —, et non l'un de ses numéros.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <b>Pourquoi une table à part et non une ligne de <c>Livres</c> ?</b> Même raisonnement que
|
|
/// pour <see cref="LivreSouhaite"/> et <see cref="Serie"/>, et il vaut d'être répété : logée
|
|
/// dans <c>Livres</c>, une revue entrerait <b>mécaniquement</b> dans le catalogue, ses
|
|
/// compteurs, la détection de doublons, les séries et les bibliographies par auteur — et il
|
|
/// faudrait écrire « et qui n'est pas une revue » à chaque lecture. Un invariant qu'on réécrit
|
|
/// partout finit par être oublié quelque part.
|
|
/// <para>
|
|
/// <b>Une fiche par revue, les numéros à l'intérieur.</b> C'est ce qui évite le défaut redouté
|
|
/// dès le premier lot : douze numéros d'un même magazine ne font pas douze fiches identiques,
|
|
/// puisqu'ils partagent la fiche de leur revue.
|
|
/// </para>
|
|
/// <para>
|
|
/// ⚠️ L'<see cref="Issn"/> est un identifiant de <b>revue</b>, jamais un ISBN. Ranger un code
|
|
/// <c>977</c> dans <c>Livre.Isbn</c> ferait échouer tout lookup ultérieur sur cette fiche —
|
|
/// c'est précisément ce que cette table évite.
|
|
/// </para>
|
|
/// </remarks>
|
|
public class Revue
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public string Titre { get; set; } = string.Empty;
|
|
|
|
/// <summary>Titre mis à plat. Index <b>unique</b> : une revue, une fiche.</summary>
|
|
public string TitreNormalise { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// ISSN sous sa forme canonique à tiret (<c>2466-6718</c>), quand on le connaît.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Facultatif : une revue se catalogue à la main sans que personne n'ait scanné son
|
|
/// code-barres. Son unicité est portée par un index <b>partiel</b> — deux <c>NULL</c> sont
|
|
/// distincts pour SQLite, une unicité simple laisserait donc passer autant de revues sans
|
|
/// ISSN qu'on veut, ce qui est justement le comportement voulu.
|
|
/// </remarks>
|
|
public string? Issn { get; set; }
|
|
|
|
public string? Editeur { get; set; }
|
|
|
|
public DateTime DateAjout { get; set; }
|
|
|
|
/// <summary>Trace de saisie. <b>Pas une frontière</b> : les revues sont communes au foyer.</summary>
|
|
public string? AjoutePar { get; set; }
|
|
|
|
public List<NumeroRevue> Numeros { get; set; } = [];
|
|
|
|
public void RecalculerFormes()
|
|
{
|
|
Titre = Titre.Trim();
|
|
TitreNormalise = NormalisationTexte.Normaliser(Titre);
|
|
// ⚠️ L'ISSN est rangé sous sa forme À TIRET, et ce n'est pas de la cosmétique : c'est
|
|
// celle que produit le code-barres (CodePeriodique.IssnDepuis) et celle qu'interroge
|
|
// bib.issn à la BnF. Un ISSN tapé « 24666718 » ne se serait rapproché de rien.
|
|
Issn = FormatageIssn.Canonique(Issn);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Un numéro possédé d'une revue.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <b>Décidé le 2026-08-19 : un numéro est recensé, rien de plus.</b> Ni prêt, ni statut de
|
|
/// lecture — ces deux mécanismes sont attachés à <see cref="Livre"/> par clé étrangère, et les
|
|
/// rouvrir demanderait une seconde table de prêts ou une parenté commune, pour un usage non
|
|
/// confirmé. Le choix est <b>réversible</b> : les ajouter plus tard ne détruit rien de ce qui
|
|
/// est enregistré ici.
|
|
/// </remarks>
|
|
public class NumeroRevue
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public int RevueId { get; set; }
|
|
|
|
public Revue? Revue { get; set; }
|
|
|
|
/// <summary>
|
|
/// Le numéro tel qu'il est imprimé (« 43 », « hors-série 7 »). <b>Obligatoire</b> :
|
|
/// sans lui, deux numéros de la même revue seraient indiscernables.
|
|
/// </summary>
|
|
public string Numero { get; set; } = string.Empty;
|
|
|
|
/// <summary>Forme mise à plat, porteuse de l'unicité au sein de la revue.</summary>
|
|
public string NumeroNormalise { get; set; } = string.Empty;
|
|
|
|
/// <summary>Date de parution, quand elle est connue. En UTC comme toutes les dates.</summary>
|
|
public DateTime? DateParution { get; set; }
|
|
|
|
/// <summary>Note libre : « dossier sur l'eau », « prêté à Paul », « acheté en gare ».</summary>
|
|
public string? Note { get; set; }
|
|
|
|
/// <summary>
|
|
/// URL d'une image de couverture, collée à la main.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// ⚠️ <b>Aucune source ne peut la fournir</b>, contrairement aux livres : l'ISSN désigne la
|
|
/// <i>revue</i>, pas la parution, et OpenLibrary n'a donc rien à en dire. C'est pourquoi ce
|
|
/// champ n'est alimenté que par une URL saisie — et pourquoi l'écran n'en propose aucune.
|
|
/// <para>
|
|
/// ⚠️ Une image de numéro doit être <b>connue du relais</b>
|
|
/// (<c>GET /api/couvertures</c>) au même titre que celle d'un livre ou d'une envie, sans
|
|
/// quoi elle s'afficherait en ligne et jamais hors-ligne — exactement le défaut corrigé le
|
|
/// 2026-08-20 pour les hébergeurs sans CORS.
|
|
/// </para>
|
|
/// </remarks>
|
|
public string? CoverUrl { get; set; }
|
|
|
|
/// <summary>Articles annoncés à la une, dans l'ordre de saisie.</summary>
|
|
public List<ArticleUne> Articles { get; set; } = [];
|
|
|
|
public DateTime DateAjout { get; set; }
|
|
|
|
public void RecalculerFormes()
|
|
{
|
|
Numero = Numero.Trim();
|
|
NumeroNormalise = NormalisationTexte.Normaliser(Numero);
|
|
CoverUrl = string.IsNullOrWhiteSpace(CoverUrl) ? null : CoverUrl.Trim();
|
|
}
|
|
}
|