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
4797
src/Yavsc.Org/Migrations/20260924150433_UserFileAttachments.Designer.cs
generated
Normal file
4797
src/Yavsc.Org/Migrations/20260924150433_UserFileAttachments.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
148
src/Yavsc.Org/Migrations/20260924150433_UserFileAttachments.cs
Normal file
148
src/Yavsc.Org/Migrations/20260924150433_UserFileAttachments.cs
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UserFileAttachments : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Path",
|
||||
table: "UploadedFiles",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ContentType",
|
||||
table: "UploadedFiles",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "OwnerId",
|
||||
table: "UploadedFiles",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EstimateAttachedFiles",
|
||||
columns: table => new
|
||||
{
|
||||
FileId = table.Column<long>(type: "bigint", nullable: false),
|
||||
EstimateId = table.Column<long>(type: "bigint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EstimateAttachedFiles", x => new { x.FileId, x.EstimateId });
|
||||
table.ForeignKey(
|
||||
name: "FK_EstimateAttachedFiles_Estimates_EstimateId",
|
||||
column: x => x.EstimateId,
|
||||
principalTable: "Estimates",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_EstimateAttachedFiles_UploadedFiles_FileId",
|
||||
column: x => x.FileId,
|
||||
principalTable: "UploadedFiles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "QueryAttachedFiles",
|
||||
columns: table => new
|
||||
{
|
||||
FileId = table.Column<long>(type: "bigint", nullable: false),
|
||||
CommandId = table.Column<long>(type: "bigint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_QueryAttachedFiles", x => new { x.FileId, x.CommandId });
|
||||
table.ForeignKey(
|
||||
name: "FK_QueryAttachedFiles_NominativeServiceCommands_CommandId",
|
||||
column: x => x.CommandId,
|
||||
principalTable: "NominativeServiceCommands",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_QueryAttachedFiles_UploadedFiles_FileId",
|
||||
column: x => x.FileId,
|
||||
principalTable: "UploadedFiles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UploadedFiles_OwnerId",
|
||||
table: "UploadedFiles",
|
||||
column: "OwnerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EstimateAttachedFiles_EstimateId",
|
||||
table: "EstimateAttachedFiles",
|
||||
column: "EstimateId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_QueryAttachedFiles_CommandId",
|
||||
table: "QueryAttachedFiles",
|
||||
column: "CommandId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_UploadedFiles_AspNetUsers_OwnerId",
|
||||
table: "UploadedFiles",
|
||||
column: "OwnerId",
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_UploadedFiles_AspNetUsers_OwnerId",
|
||||
table: "UploadedFiles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "EstimateAttachedFiles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "QueryAttachedFiles");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_UploadedFiles_OwnerId",
|
||||
table: "UploadedFiles");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OwnerId",
|
||||
table: "UploadedFiles");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Path",
|
||||
table: "UploadedFiles",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ContentType",
|
||||
table: "UploadedFiles",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1394,6 +1394,21 @@ namespace Yavsc.Migrations
|
|||
b.ToTable("Estimates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Billing.EstimateAttachedFile", b =>
|
||||
{
|
||||
b.Property<long>("FileId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("EstimateId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("FileId", "EstimateId");
|
||||
|
||||
b.HasIndex("EstimateId");
|
||||
|
||||
b.ToTable("EstimateAttachedFiles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Billing.EstimateTemplate", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
|
|
@ -1502,6 +1517,21 @@ namespace Yavsc.Migrations
|
|||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Billing.QueryAttachedFile", b =>
|
||||
{
|
||||
b.Property<long>("FileId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("CommandId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("FileId", "CommandId");
|
||||
|
||||
b.HasIndex("CommandId");
|
||||
|
||||
b.ToTable("QueryAttachedFiles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Billing.Signature", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
|
|
@ -1674,16 +1704,23 @@ namespace Yavsc.Migrations
|
|||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("ContentType")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("Length")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("OwnerId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.ToTable("UploadedFiles");
|
||||
});
|
||||
|
||||
|
|
@ -3827,6 +3864,25 @@ namespace Yavsc.Migrations
|
|||
b.Navigation("Query");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Billing.EstimateAttachedFile", b =>
|
||||
{
|
||||
b.HasOne("Yavsc.Models.Billing.Estimate", "Estimate")
|
||||
.WithMany()
|
||||
.HasForeignKey("EstimateId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Yavsc.Models.Blog.UploadedFile", "File")
|
||||
.WithMany()
|
||||
.HasForeignKey("FileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Estimate");
|
||||
|
||||
b.Navigation("File");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Billing.NominativeServiceCommand", b =>
|
||||
{
|
||||
b.HasOne("Yavsc.Models.Workflow.Activity", "Context")
|
||||
|
|
@ -3860,6 +3916,25 @@ namespace Yavsc.Migrations
|
|||
b.Navigation("Regularization");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Billing.QueryAttachedFile", b =>
|
||||
{
|
||||
b.HasOne("Yavsc.Models.Billing.NominativeServiceCommand", "Command")
|
||||
.WithMany()
|
||||
.HasForeignKey("CommandId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Yavsc.Models.Blog.UploadedFile", "File")
|
||||
.WithMany()
|
||||
.HasForeignKey("FileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Command");
|
||||
|
||||
b.Navigation("File");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Billing.Signature", b =>
|
||||
{
|
||||
b.HasOne("Yavsc.Models.Billing.Estimate", "Estimate")
|
||||
|
|
@ -3952,6 +4027,15 @@ namespace Yavsc.Migrations
|
|||
b.Navigation("Post");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Blog.UploadedFile", b =>
|
||||
{
|
||||
b.HasOne("Yavsc.Models.ApplicationUser", "Owner")
|
||||
.WithMany()
|
||||
.HasForeignKey("OwnerId");
|
||||
|
||||
b.Navigation("Owner");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b =>
|
||||
{
|
||||
b.HasOne("Yavsc.Models.Blog.BlogPost", "Post")
|
||||
|
|
|
|||
Loading…
Reference in a new issue