feat(r3.2): bibliothèque de documents (FileStorage réel) + analytics

- FileStorage : putObject/getObjectStream/removeObject, bucket créé au
  démarrage — MinIO confiné à son implémentation (règle ESLint intacte)
- documents : upload multipart (PDF/JPG/PNG, 20 Mo max, rattachement
  appareil OU OT requis, permission d'édition sur la CIBLE), liste
  filtrable, téléchargement STREAMÉ par l'API (MinIO jamais exposé),
  suppression ; e2e : octets téléchargés identiques aux octets envoyés
- analytics : GET /analytics/summary dérivé du réel — coûts/mois
  (mouvements + main-d'œuvre figés), pannes par organe (bilans codés),
  taux de préventif, durée moyenne de résolution, top équipements
- générateur OpenAPI : query params, multipart, réponse binaire (71 ops)
- CI : service MinIO (bitnami) sur les jobs api et e2e
- test de régression du tri « Interventions récentes » rendu déterministe
  (positions absolues instables sous 12 suites parallèles) ; 58 tests,
  6 runs complets consécutifs verts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
pr-daaif
2026-07-16 21:24:04 +01:00
parent 32eb20b5a0
commit 2ffdd13cf4
23 changed files with 1652 additions and 30 deletions

View File

@@ -4,6 +4,20 @@ Trace chronologique des sessions (la plus récente en premier). Le **playbook**
---
## 2026-07-16 — Pr. Daaif (+ Claude) — R3.2 : bibliothèque de documents + analytics
**Actions**
- **`FileStorage` gagne ses vraies opérations** (put/stream/remove ; bucket créé au démarrage — aucune étape manuelle au déploiement) ; MinIO reste confiné à son implémentation (règle ESLint).
- **Bibliothèque** : upload multipart (PDF/JPG/PNG, 20 Mo max, rattachement appareil OU OT **requis**, permission d'édition sur la cible), liste filtrable, **téléchargement streamé par l'API** (MinIO jamais exposé — topologie du runbook respectée), suppression. Test e2e : les octets téléchargés sont IDENTIQUES aux octets envoyés.
- **Analytics** (`GET /analytics/summary`, permission ANALYTICS) : tout est dérivé du réel — coûts par mois depuis les mouvements/main-d'œuvre figés, **pannes par organe depuis les bilans codés**, taux de préventif (grilles terminées/générées), durée moyenne de résolution, top équipements en coût.
- Générateur OpenAPI étendu (query params, multipart, réponse binaire) — 71 opérations. **CI : service MinIO ajouté** (jobs api + e2e, image bitnami).
- **58 tests verts** (92 % / 73,9 %) ; test de régression du tri des « Interventions récentes » rendu **déterministe** (les positions absolues étaient instables sous 12 suites parallèles) — 6 runs complets consécutifs verts.
**Prochaine étape** : R3.3 — écrans web (stock, fiche pièce, BC, coûts sur fiche OT, statistiques, tiers, bibliothèque, taux dans Personnes), puis recette R3 + déploiement + tag.
---
## 2026-07-16 — Pr. Daaif (+ Claude) — R3.1 : socle backend de la gestion
**Actions**

View File

@@ -956,6 +956,205 @@
}
}
},
"/documents": {
"get": {
"operationId": "listDocuments",
"summary": "Bibliothèque (filtrable par appareil ou OT)",
"tags": [
"documents"
],
"parameters": [
{
"name": "assetId",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "workOrderId",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "kind",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
}
],
"security": [
{
"bearerAuth": []
}
],
"responses": {
"200": {
"description": "Liste",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DocumentsResponse"
}
}
}
}
}
},
"post": {
"operationId": "uploadDocument",
"summary": "Téléverser (PDF/JPG/PNG, 20 Mo max, rattachement appareil OU OT requis)",
"tags": [
"documents"
],
"security": [
{
"bearerAuth": []
}
],
"requestBody": {
"required": true,
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"file": {
"type": "string",
"format": "binary"
},
"kind": {
"type": "string"
},
"assetId": {
"type": "string"
},
"workOrderId": {
"type": "string"
}
},
"required": [
"file"
]
}
}
}
},
"responses": {
"201": {
"description": "Document rangé",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Document"
}
}
}
},
"400": {
"description": "Type/taille refusé ou rattachement manquant"
}
}
}
},
"/documents/{id}/download": {
"get": {
"operationId": "downloadDocument",
"summary": "Télécharger — streamé par lAPI (MinIO jamais exposé)",
"tags": [
"documents"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
}
}
],
"security": [
{
"bearerAuth": []
}
],
"responses": {
"200": {
"description": "Fichier"
},
"404": {
"description": "Inconnu"
}
}
}
},
"/documents/{id}": {
"delete": {
"operationId": "deleteDocument",
"summary": "Supprimer (permission dédition sur la cible)",
"tags": [
"documents"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
}
}
],
"security": [
{
"bearerAuth": []
}
],
"responses": {
"204": {
"description": "Supprimé"
},
"404": {
"description": "Inconnu"
}
}
}
},
"/analytics/summary": {
"get": {
"operationId": "getAnalyticsSummary",
"summary": "Le tableau de la direction : coûts, pannes par organe (bilans), préventif, top équipements",
"tags": [
"analytics"
],
"security": [
{
"bearerAuth": []
}
],
"responses": {
"200": {
"description": "Synthèse",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AnalyticsSummary"
}
}
}
}
}
}
},
"/partners": {
"get": {
"operationId": "listPartners",
@@ -4344,6 +4543,314 @@
},
"additionalProperties": false
},
"DocumentsResponse": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"documents": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"kind": {
"type": "string",
"enum": [
"NOTICE",
"CERTIFICATE",
"PHOTO",
"OTHER"
]
},
"fileName": {
"type": "string"
},
"size": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"contentType": {
"type": "string"
},
"assetReference": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workOrderReference": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"uploadedByName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"id",
"kind",
"fileName",
"size",
"contentType",
"assetReference",
"workOrderReference",
"uploadedByName",
"createdAt"
],
"additionalProperties": false
}
}
},
"required": [
"documents"
],
"additionalProperties": false
},
"Document": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"kind": {
"type": "string",
"enum": [
"NOTICE",
"CERTIFICATE",
"PHOTO",
"OTHER"
]
},
"fileName": {
"type": "string"
},
"size": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"contentType": {
"type": "string"
},
"assetReference": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workOrderReference": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"uploadedByName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"id",
"kind",
"fileName",
"size",
"contentType",
"assetReference",
"workOrderReference",
"uploadedByName",
"createdAt"
],
"additionalProperties": false
},
"AnalyticsSummary": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"monthCost": {
"type": "number"
},
"previousMonthCost": {
"type": "number"
},
"closed12m": {
"type": "object",
"properties": {
"total": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"preventive": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
},
"required": [
"total",
"preventive"
],
"additionalProperties": false
},
"preventiveRate": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"avgResolutionDays": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"failuresByComponent": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"count": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
},
"required": [
"label",
"count"
],
"additionalProperties": false
}
},
"costsByMonth": {
"type": "array",
"items": {
"type": "object",
"properties": {
"month": {
"type": "string"
},
"total": {
"type": "number"
}
},
"required": [
"month",
"total"
],
"additionalProperties": false
}
},
"topAssets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"reference": {
"type": "string"
},
"siteName": {
"type": "string"
},
"correctives": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"partsCost": {
"type": "number"
},
"laborCost": {
"type": "number"
},
"total": {
"type": "number"
}
},
"required": [
"reference",
"siteName",
"correctives",
"partsCost",
"laborCost",
"total"
],
"additionalProperties": false
}
}
},
"required": [
"monthCost",
"previousMonthCost",
"closed12m",
"preventiveRate",
"avgResolutionDays",
"failuresByComponent",
"costsByMonth",
"topAssets"
],
"additionalProperties": false
},
"PartnersResponse": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",