Gestion des fichiers utilisateur et pièces jointes (demande/devis)
Espace de stockage personnel géré depuis PostIt, avec attachement par
référence (sans duplication) des fichiers à la demande du client
(NominativeServiceCommand) et au devis du fournisseur (Estimate).
Backend:
- UploadedFile.OwnerId (FK ApplicationUser, nullable) ; les téléversements
vers api/v1/fs créent/mettent à jour une ligne propriétaire.
- Tables de jointure EstimateAttachedFile et QueryAttachedFile (PK composite),
migration UserFileAttachments.
- EstimateApiController: GET/POST/DELETE estimate/{id}/attachments
(fournisseur uniquement, fichier devant appartenir au caller).
- FrontOfficeApiController: GET/POST/DELETE front/query/{id}/attachments
(client uniquement).
- FileSystemApiController: GET fs/file/{fileId} sert les octets depuis le
host Blogs (autorisation propriétaire ou partie au devis/demande via DB
partagée) — le host API ne gère que les métadonnées, contrainte des
conteneurs docker à disques distincts.
Client .NET:
- UserFilesApiClient (list/upload/delete/download) pointant vers BlogsApiUrl.
- DTOs UserDirectoryDto/FileEntryDto/AttachmentDto/FileReceivedDto/UploadFile.
- Méthodes d'attachement sur EstimateApiClient et FrontOfficeApiClient.
PostIt (Avalonia):
- Page « Mes fichiers » (naviguer/téléverser/télécharger/supprimer), servant
aussi de sélecteur pour l'attachement.
- Section pièces jointes dans l'édition du devis (fournisseur) et le détail
de la demande (client), avec ajout depuis My Files, détachement,
téléchargement.
- Enregistrement DI + mapping ViewLocator + bouton d'accès.
Tests:
- EstimateEditionPageViewModelTests mis à jour (nouveau ctor fsClient,
historique d'appels du stub pour les assertions post-envoi).
Nommage HTTP/méthodes en anglais, documentation en français.
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
eb58fc8862
commit
70ab05522b
31 changed files with 6515 additions and 20 deletions
|
|
@ -509,5 +509,9 @@ namespace Yavsc.Models
|
|||
public DbSet<ModerationLog> ModerationLogs { get; set; }
|
||||
|
||||
public DbSet<NominativeServiceCommand> NominativeServiceCommands { get; set; }
|
||||
|
||||
public DbSet<EstimateAttachedFile> EstimateAttachedFiles { get; set; }
|
||||
|
||||
public DbSet<QueryAttachedFile> QueryAttachedFiles { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
13
src/Yavsc.Server/Models/Billing/AttachFileRequest.cs
Normal file
13
src/Yavsc.Server/Models/Billing/AttachFileRequest.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
namespace Yavsc.Models.Billing
|
||||
{
|
||||
/// <summary>
|
||||
/// Corps de requête pour attacher un fichier de l'espace perso à un
|
||||
/// devis (fournisseur) ou à une demande (client), par référence à son
|
||||
/// identifiant <see cref="Yavsc.Models.Blog.UploadedFile"/>.
|
||||
/// </summary>
|
||||
public class AttachFileRequest
|
||||
{
|
||||
/// <summary>Identifiant du fichier référencé.</summary>
|
||||
public long FileId { get; set; }
|
||||
}
|
||||
}
|
||||
35
src/Yavsc.Server/Models/Billing/EstimateAttachedFile.cs
Normal file
35
src/Yavsc.Server/Models/Billing/EstimateAttachedFile.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Models.Blog;
|
||||
|
||||
namespace Yavsc.Models.Billing
|
||||
{
|
||||
/// <summary>
|
||||
/// Pièce jointe d'un devis, attachée **par référence** à un fichier
|
||||
/// de l'espace de stockage personnel du fournisseur
|
||||
/// (<see cref="UploadedFile"/>). Le fichier reste dans l'espace perso
|
||||
/// du fournisseur (pas de duplication) ; seul le lien est persisté.
|
||||
/// Le téléchargement est servi par le host Blogs, qui autorise
|
||||
/// l'appelant (fournisseur ou client du devis) via cette table.
|
||||
/// </summary>
|
||||
[PrimaryKey(nameof(FileId), nameof(EstimateId))]
|
||||
public class EstimateAttachedFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifiant du fichier référencé (partie de la clé composite).
|
||||
/// </summary>
|
||||
public long FileId { get; set; }
|
||||
|
||||
[ForeignKey("FileId")]
|
||||
public virtual UploadedFile File { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifiant du devis auquel le fichier est rattaché
|
||||
/// (partie de la clé composite).
|
||||
/// </summary>
|
||||
public long EstimateId { get; set; }
|
||||
|
||||
[ForeignKey("EstimateId")]
|
||||
public virtual Estimate Estimate { get; set; }
|
||||
}
|
||||
}
|
||||
36
src/Yavsc.Server/Models/Billing/QueryAttachedFile.cs
Normal file
36
src/Yavsc.Server/Models/Billing/QueryAttachedFile.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Models.Blog;
|
||||
|
||||
namespace Yavsc.Models.Billing
|
||||
{
|
||||
/// <summary>
|
||||
/// Pièce jointe d'une demande (NominativeServiceCommand), attachée
|
||||
/// **par référence** à un fichier de l'espace de stockage personnel
|
||||
/// du client (<see cref="UploadedFile"/>). Le fichier reste dans
|
||||
/// l'espace perso du client (pas de duplication) ; seul le lien est
|
||||
/// persisté. Le téléchargement est servi par le host Blogs, qui
|
||||
/// autorise l'appelant (client ou fournisseur de la demande) via
|
||||
/// cette table.
|
||||
/// </summary>
|
||||
[PrimaryKey(nameof(FileId), nameof(CommandId))]
|
||||
public class QueryAttachedFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifiant du fichier référencé (partie de la clé composite).
|
||||
/// </summary>
|
||||
public long FileId { get; set; }
|
||||
|
||||
[ForeignKey("FileId")]
|
||||
public virtual UploadedFile File { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifiant de la demande à laquelle le fichier est rattaché
|
||||
/// (partie de la clé composite).
|
||||
/// </summary>
|
||||
public long CommandId { get; set; }
|
||||
|
||||
[ForeignKey("CommandId")]
|
||||
public virtual NominativeServiceCommand Command { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#nullable enable annotations
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
@ -33,6 +34,19 @@ namespace Yavsc.Models.Blog
|
|||
/// </summary>
|
||||
/// <value></value>
|
||||
public string ContentType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Propriétaire du fichier dans l'espace de stockage personnel.
|
||||
/// Renseigné pour les fichiers téléversés via <c>api/v1/fs</c>
|
||||
/// (référencés par <see cref="EstimateAttachedFile"/> /
|
||||
/// <see cref="QueryAttachedFile"/>). Reste <c>null</c> pour les
|
||||
/// pièces jointes de blog historiques (<see cref="BlogAttachedFile"/>),
|
||||
/// qui ne sont pas adressées par propriétaire.
|
||||
/// </summary>
|
||||
public string? OwnerId { get; set; }
|
||||
|
||||
[ForeignKey("OwnerId")]
|
||||
public virtual ApplicationUser? Owner { get; set; }
|
||||
}
|
||||
|
||||
[PrimaryKey(nameof(FileId), nameof(PostId))]
|
||||
|
|
|
|||
|
|
@ -43,11 +43,29 @@ namespace Yavsc.Server.Models.FileSystem
|
|||
}
|
||||
|
||||
public string DestDir { get; set; }
|
||||
|
||||
|
||||
public string FileName { get; set; }
|
||||
public bool Overridden { get; set; }
|
||||
|
||||
public bool QuotaOffense { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifiant de la ligne <see cref="Yavsc.Models.Blog.UploadedFile"/>
|
||||
/// créée à l'enregistrement du fichier dans l'espace perso
|
||||
/// (<c>api/v1/fs</c>). Nul si aucune métadonnée n'a été persistée.
|
||||
/// </summary>
|
||||
public long? FileId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Taille du fichier reçu (octets), pour le reflet côté métadonnées.
|
||||
/// </summary>
|
||||
public long Length { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type MIME du fichier reçu.
|
||||
/// </summary>
|
||||
public string ContentType { get; set; }
|
||||
|
||||
public string FullName { get => Path.Combine(DestDir, FileName); }
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue