Module Projection IA¶
Objectif¶
Projection IA permet a une societe de generer des visuels assistes par IA a partir d'une ou deux images. Le module couvre :
- generation d'une image modifiee ;
- fusion d'une image principale avec une image de reference ;
- controle d'acces via la feature
FEATURE_PROJECTION_IAet les permissionsprojection ia/projection porte entrée; - debit de credits image avant generation ;
- remboursement automatique du debit si la generation echoue ;
- historique temporaire des generations ;
- attribution de credits par renouvellement d'abonnement ;
- administration des packs, des credits par plan et des credits manuels ;
- notifications email lors des creditations.
Le module remplace progressivement l'ancien vocabulaire projection porte entrée / door_projection. Des alias existent encore dans le code et dans les migrations.
Carte technique¶
flowchart TD
Router["app/core/Router.php"] --> Controller["AiProjectionController"]
Controller --> Service["AiProjectionService"]
Controller --> Credits["AiProjectionCreditModel"]
Controller --> Logs["AiProjectionLogModel"]
Service --> OpenAI["OpenAIClient"]
Credits --> Notifications["AiProjectionCreditNotificationService"]
Notifications --> Mailer["MailerService"]
Logs --> Uploads["public/uploads/ai_projection/history/<societe_id>/"]
View["app/views/projection-ia.php"] --> Js["public/assets/js/projection-ia.js"]
Js --> Controller
CronCredits["ai_projection_subscription_credits_cron.php"] --> Credits
CronCleanup["ai_projection_history_cleanup_cron.php"] --> Logs
CronNotifications["ai_projection_credit_notifications_cron.php"] --> Notifications
Fichiers a connaitre¶
| Role | Fichiers |
|---|---|
| Routage | app/core/Router.php |
| Controleur | app/controllers/AiProjectionController.php |
| Service IA | app/services/ai_projection/AiProjectionService.php |
| Client OpenAI | app/services/catalog/OpenAIClient.php |
| Credits | app/models/AiProjectionCreditModel.php |
| Historique | app/models/AiProjectionLogModel.php |
| Notifications credits | app/services/AiProjectionCreditNotificationService.php |
| Vue | app/views/projection-ia.php |
| Frontend | public/assets/js/projection-ia.js |
| SQL | app/bd/projection-ia.sql |
| Crons | app/cron/ai_projection_subscription_credits_cron.php, app/cron/ai_projection_history_cleanup_cron.php, app/cron/ai_projection_credit_notifications_cron.php |
Routes¶
Toutes les routes verifiees passent par :
Auth::check();requireFeatureAccess(FEATURE_PROJECTION_IA, APP_DIR . '/dashboard');- puis
AiProjectionController.
| Route | Methode | Controleur | Usage |
|---|---|---|---|
projection-ia |
GET | index() |
Page complete. |
projection-ia/generate |
POST | generate() |
Generation IA avec debit credit. |
projection-ia/logs |
GET | logs() |
Logs admin. |
projection-ia/settings |
GET | settings() |
Parametres admin : credits par plan, packs, logs. |
projection-ia/plan-credits |
POST | savePlanCredits() |
Enregistrer les credits par renouvellement de plan. |
projection-ia/credit-packs |
GET/POST | manageCreditPacks() |
Lister les packs actifs, creer/modifier/supprimer cote admin. |
projection-ia/companies/search |
GET | searchCompanies() |
Recherche de societes pour credit admin. |
projection-ia/credit-company |
POST | creditCompany() |
Credit manuel d'une ou plusieurs societes. |
projection-ia/history |
GET | history() |
Historique visible par la societe courante. |
projection-ia/history-entry |
GET | historyEntry() |
Detail d'une generation. |
projection-ia/history/delete |
POST | deleteHistory() |
Suppression d'une entree d'historique. |
projection-porte-entree |
GET | redirection | Redirige vers projection-ia?mode=image_merge. |
projection-porte-entree/generate |
POST | generate() |
Alias legacy, force mode=image_merge si absent. |
projection-porte-entree/logs |
GET | logs() |
Alias legacy. |
Point de vigilance verifie : le bloc de routes Projection IA est duplique deux fois de suite dans Router.php. Comme il s'agit d'une chaine elseif, le second bloc est normalement inatteignable. Toute modification de route doit tenir compte de cette duplication pour eviter une correction appliquee a un seul des deux blocs.
Acces et permissions¶
Le controleur refait un controle applicatif via enforceProjectionAccess() :
Pour une requete AJAX, l'echec retourne un JSON success=false. Pour une page, l'utilisateur est redirige vers le dashboard.
Les actions admin appellent ensuite requireAdmin() :
settings();savePlanCredits();manageCreditPacks()en POST ;searchCompanies();creditCompany();logs().
manageCreditPacks() en GET reste accessible aux utilisateurs autorises pour afficher les packs actifs.
Modes de generation¶
AiProjectionService expose deux modes.
| Mode | Constante | Images requises | Usage |
|---|---|---|---|
single_edit |
MODE_SINGLE_EDIT |
primary_image |
Modifier une seule image selon des instructions. |
image_merge |
MODE_IMAGE_MERGE |
primary_image + secondary_image |
Appliquer un detail, style ou materiau de la seconde image sur la premiere. |
Compatibilite legacy :
door_swapest normalise versimage_merge;- les anciens champs upload
facade_imageetdoor_imagesont encore acceptes comme fallback pourprimary_imageetsecondary_image.
Configuration OpenAI et images¶
Variables lues :
| Variable | Defaut | Usage |
|---|---|---|
OPENAI_API_KEY |
aucun | Obligatoire pour generer. |
OPENAI_BASE_URL |
https://api.openai.com/v1 |
Base API du client OpenAI. |
OPENAI_AI_PROJECTION_MODEL |
aucun | Modele prioritaire pour Projection IA. |
OPENAI_DOOR_PROJECTION_MODEL |
aucun | Fallback legacy si le modele Projection IA est absent. |
AI_PROJECTION_MAX_IMAGE_SIZE_MB |
10 |
Taille upload max, borne basse a 1 Mo. |
DOOR_PROJECTION_MAX_IMAGE_SIZE_MB |
10 |
Fallback legacy. |
AI_PROJECTION_MAX_IMAGE_DIMENSION |
1536 |
Dimension max avant compression, borne basse a 512 px. |
DOOR_PROJECTION_MAX_IMAGE_DIMENSION |
1536 |
Fallback legacy. |
OPENAI_DOCUMENT_EXTRACTION_TIMEOUT |
180 |
Timeout aussi utilise par OpenAIClient. |
Formats acceptes par AiProjectionService :
image/jpeg;image/png;image/webp.
Avant l'appel API, le service peut redimensionner l'image avec GD :
- si
imagecreatefromstring()existe ; - si l'image depasse
AI_PROJECTION_MAX_IMAGE_DIMENSION; - sortie temporaire en JPEG qualite 85 ;
- nettoyage des fichiers temporaires en
finally.
Si GD n'est pas disponible ou si le redimensionnement echoue, l'image originale est transmise au client OpenAI.
Flux : generation¶
POST /projection-ia/generate suit cet ordre :
- Verifier l'acces Projection IA.
- Normaliser
mode. - Construire les fichiers uploades :
primary_imageou fallbackfacade_image;secondary_imageou fallbackdoor_image.- Debiter la societe via
AiProjectionCreditModel::debitCurrentSocieteForGeneration($mode). - Si le debit echoue, retourner
success=falseavec le solde etcost_per_generation. - Appeler
AiProjectionService::generate(). - Enregistrer l'historique via
AiProjectionLogModel::recordGeneration(). - Retourner le resultat IA et le nouveau solde.
- Si une exception survient apres le debit, appeler
refundGenerationDebit()avec l'id d'historique de debit.
Le cout d'une generation est fixe dans AiProjectionCreditModel::GENERATION_COST = 1.
Les admins ont un bypass credit :
getCurrentSocieteCreditSummary()retournecredit_bypass=true;debitCurrentSocieteForGeneration()ne cree pas de ligne de debit et ne diminue pas le solde.
Credits image¶
Solde societe¶
app/bd/projection-ia.sql ajoute sur societes :
ai_projection_credit_balance INT UNSIGNED NOT NULL DEFAULT 0;ai_projection_credit_last_awarded_period_end DATETIME NULL.
AiProjectionCreditModel utilise SELECT ... FOR UPDATE dans lockSocieteCreditState() pour eviter les debits/credits concurrents sur une meme societe.
Historique credits¶
Table ai_projection_credit_history :
operation_type: debit, credit, refund, etc. ;amount;balance_before;balance_after;source_type;source_id;justification.
Sources verifiees :
generation: debit d'une generation ;generation_refund: remboursement automatique apres echec ;subscription_bonus: attribution liee au renouvellement d'abonnement ;admin_adjustment: credit manuel admin.
Credits par abonnement¶
Table ai_projection_plan_credits :
plan_id;credits_per_renewal;active;updated_by;updated_at.
getSocietesEligibleForSubscriptionRenewalCredits() selectionne les societes :
- role
artisan; current_period_endnon nul ;- plan actif dans
ai_projection_plan_credits; credits_per_renewal > 0;- pas encore creditees pour cette periode (
current_period_end > ai_projection_credit_last_awarded_period_endou champ nul).
creditSubscriptionRenewal() met a jour en transaction :
- le solde ;
ai_projection_credit_last_awarded_period_end;- l'historique ;
- puis declenche une notification de credit.
Credits admin¶
creditCompany() permet :
- credit d'une selection de societes ;
- credit de toutes les societes si
all_societiesest present ; - compatibilite legacy avec
societe_id.
Regles verifiees :
- montant strictement positif ;
- motif obligatoire ;
- notification envoyee apres commit.
Packs¶
Table ai_projection_credit_packs :
name;price;credit_amount;bonus;price_id;active;sort_order.
Le modele permet de lister les packs actifs pour l'interface, et de creer/modifier/supprimer les packs cote admin.
Historique des generations¶
Table ai_projection_usage_logs :
societe_id,membre_id;mode;response_time_ms;tokens_consumed;instructions;primary_image_path;secondary_image_path;result_image_path;result_mime_type;expires_at;created_at.
Retention :
AiProjectionLogModel::HISTORY_RETENTION_DAYS = 7;- chaque generation expire a
created_at + 7 jours; - les lectures d'historique declenchent
purgeExpiredHistorySilently(); - le cron de purge appelle
purgeExpiredHistory(2000).
Stockage fichiers :
- chemin relatif :
ai_projection/history/<societe_id>/; - chemin public :
public/uploads/ai_projection/history/<societe_id>/; - images sources copiees avec prefixe
source_primary_etsource_secondary_; - resultat stocke avec prefixe
result_.
deleteHistoryEntry() doit supprimer la ligne et les fichiers associes. purgeExpiredHistory() supprime les fichiers des lignes expirees puis les lignes.
Acces historique :
- une societe voit ses lignes par
societe_id; - un admin peut voir les lignes de sa societe active ou celles rattachees a son
membre_idselon le contexte ; - seules les lignes non expirees avec
result_image_pathnon vide sont renvoyees.
Notifications de credit¶
AiProjectionCreditNotificationService cree sa table de stockage a l'instanciation via ensureStorageSchema() :
ai_projection_credit_notifications :
societe_id;source_type;payload_json;status:pending,processing,sent,failed;retry_count;next_retry_at;last_error;sent_at;- timestamps.
Comportement :
queueNotification()insere une lignepending;sendNotificationById()passe la ligne enprocessing, recharge le payload puis envoie ;- si l'envoi reussit, statut
sent; - en cas d'echec, retry apres 300 s ;
- abandon a partir de
MAX_RETRIES = 3.
Le destinataire est resolu a partir de la societe cible. Le service cherche un email valide pour construire et envoyer un message via MailerService.
Crons¶
| Cron | Role | Commande CLI |
|---|---|---|
ai_projection_subscription_credits_cron.php |
Attribue les credits d'abonnement aux societes eligibles. | php app/cron/ai_projection_subscription_credits_cron.php |
ai_projection_history_cleanup_cron.php |
Purge l'historique expire et les fichiers associes. | php app/cron/ai_projection_history_cleanup_cron.php |
ai_projection_credit_notifications_cron.php |
Traite les notifications de credits en attente. | php app/cron/ai_projection_credit_notifications_cron.php |
ai_projection_subscription_credits_cron.php journalise :
AI_PROJECTION_CREDITS_CRON_START;AI_PROJECTION_CREDITS_CRON_SOCIETE_START;AI_PROJECTION_CREDITS_CRON_SUCCESS;AI_PROJECTION_CREDITS_CRON_ERROR;AI_PROJECTION_CREDITS_CRON_FINISH;AI_PROJECTION_CREDITS_CRON_CRITICAL_ERROR.
ai_projection_history_cleanup_cron.php journalise :
AI_PROJECTION_HISTORY_CLEANUP_START;AI_PROJECTION_HISTORY_CLEANUP_FINISH;AI_PROJECTION_HISTORY_CLEANUP_ERROR.
Migrations et compatibilite legacy¶
projection-ia.sql fait plus qu'une creation de tables :
- cree ou met a jour la permission
projection ia; - migre
projection porte entréeversprojection iasi necessaire ; - cree/met a jour la feature id
43nommeeProjection IA; - lie la feature aux plans
17,18,19,20; - renomme
door_projection_usage_logsversai_projection_usage_logssi l'ancienne table existe et pas la nouvelle ; - convertit les anciens modes vides ou
door_swapenimage_merge; - ajoute les colonnes manquantes de l'historique de facon idempotente ;
- ajoute les colonnes de credits sur
societes; - cree les tables de credits.
Ne pas separer ces migrations sans verifier l'etat de la base cible : elles portent la compatibilite entre l'ancien module porte d'entree et le module Projection IA.
Frontend¶
app/views/projection-ia.php injecte notamment :
- configuration des modes ;
- resume credit ;
- packs de credits ;
- donnees pour les panneaux admin ;
- la page
currentPage = projection-ia.
public/assets/js/projection-ia.js gere :
- selection du mode ;
- uploads
primary_imageetsecondary_image; - appel
projection-ia/generate; - affichage du resultat ;
- resume du solde et cout par generation ;
- historique ;
- parametres admin ;
- packs ;
- credit manuel de societes ;
- tiroirs/modales et validations UI.
Points de vigilance¶
- Le debit credit se fait avant l'appel OpenAI. Garder le remboursement automatique en cas d'exception, sinon une erreur fournisseur consommera un credit.
recordGeneration()retournefalsesans bloquer la reponse si l'historique ne peut pas etre stocke. Une generation peut donc reussir sans apparaitre dans l'historique.- Les images sources et resultats sont stockes temporairement 7 jours. Ne pas allonger la retention sans revoir volume disque, donnees personnelles et purge.
- Le routage Projection IA est duplique dans
Router.php. Nettoyer ce doublon avant une refonte de routes reduirait le risque d'ecart. - Le client OpenAI utilise aussi
OPENAI_DOCUMENT_EXTRACTION_TIMEOUT. Modifier ce timeout impacte potentiellement d'autres usages du client catalogue. - La table
ai_projection_credit_notificationsest creee par le service PHP, pas parprojection-ia.sql. Sur un environnement strict, verifier que l'utilisateur SQL applicatif a le droitCREATE TABLE. - Les variables legacy
OPENAI_DOOR_PROJECTION_MODEL,DOOR_PROJECTION_MAX_IMAGE_SIZE_MB,DOOR_PROJECTION_MAX_IMAGE_DIMENSIONsont encore lues comme fallback. Les supprimer demande une migration de configuration.
Verification conseillee¶
- Lancer
php -l app/cron/ai_projection_subscription_credits_cron.php. - Verifier
.env:OPENAI_API_KEY,OPENAI_AI_PROJECTION_MODEL, limites image. - En compte non admin avec credits, generer en
single_editpuis verifier : - solde decremente ;
- ligne
ai_projection_credit_historysourcegeneration; - ligne
ai_projection_usage_logs; - fichiers dans
public/uploads/ai_projection/history/<societe_id>/. - Simuler une erreur OpenAI apres debit et verifier le remboursement
generation_refund. - Tester
image_mergeavec deux images. - Tester un solde insuffisant : pas d'appel IA attendu.
- En admin, charger
settings, modifier credits par plan et packs. - Lancer les trois crons en CLI et verifier les logs
ActivityLogger.
Questions ouvertes¶
- Aucun test automatise dedie au module n'a ete identifie dans les fichiers inspectes.
- Le cycle d'achat reel des packs depend probablement d'un autre module de paiement ; ici, le modele expose les packs mais ne traite pas l'achat.
- La creation dynamique de
ai_projection_credit_notificationsdans le service devrait idealement etre rapprochee des migrations SQL pour rendre les environnements plus previsibles.