ai = new AIService(); $this->email = new EmailService(); } /** * Crea una nuova segnalazione e tenta classificazione AI (max 10s) * * @param int $orgId * @param int $userId * @param string $userEmail * @param string $userRole * @param array $data [tipo, priorita, descrizione, page_url, attachment] * @return array Il record inserito con eventuali campi AI già popolati */ public function createReport( int $orgId, int $userId, string $userEmail, string $userRole, array $data ): array { $tipo = $data['tipo'] ?? 'bug'; $priorita = $data['priorita'] ?? 'media'; $descrizione = $data['descrizione'] ?? ''; $pageUrl = $data['page_url'] ?? ''; $attachment = $data['attachment'] ?? null; Database::query( 'INSERT INTO feedback_reports (organization_id, user_id, user_email, user_role, page_url, tipo, priorita, descrizione, attachment) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', [$orgId, $userId, $userEmail, $userRole, $pageUrl, $tipo, $priorita, $descrizione, $attachment] ); $reportId = (int) Database::lastInsertId(); // Classificazione AI con timeout 10s $this->classifyWithAI($reportId, $tipo, $descrizione); return Database::fetchOne( 'SELECT * FROM feedback_reports WHERE id = ?', [$reportId] ); } /** * Chiama AI per classificare la segnalazione e aggiorna il DB * Fallisce silenziosamente (non blocca la risposta all'utente) */ public function classifyWithAI(int $reportId, string $tipo, string $descrizione): void { try { $result = $this->ai->classifyFeedback($tipo, $descrizione); Database::query( 'UPDATE feedback_reports SET ai_categoria = ?, ai_priorita = ?, ai_suggerimento = ?, ai_risposta = ?, ai_processed = 1 WHERE id = ?', [ $result['categoria'] ?? null, $result['priorita'] ?? null, $result['suggerimento'] ?? null, $result['risposta_utente'] ?? null, $reportId, ] ); } catch (\Throwable $e) { error_log("FeedbackService::classifyWithAI failed for report #{$reportId}: " . $e->getMessage()); } } /** * Broadcast email a tutti i membri attivi dell'org quando status → risolto */ public function broadcastResolution(int $reportId, int $orgId): void { $report = Database::fetchOne( 'SELECT tipo, descrizione, ai_risposta, nota_admin FROM feedback_reports WHERE id = ?', [$reportId] ); if (!$report) { return; } // Tutti gli utenti attivi dell'org $members = Database::fetchAll( 'SELECT u.email, u.name FROM users u JOIN user_organizations uo ON uo.user_id = u.id WHERE uo.organization_id = ? AND u.is_active = 1', [$orgId] ); if (empty($members)) { return; } $tipoLabel = $this->translateTipo($report['tipo']); $excerpt = mb_strimwidth($report['descrizione'], 0, 120, '…'); $resolution = $report['nota_admin'] ?: $report['ai_risposta'] ?: 'Segnalazione risolta dal team tecnico.'; foreach ($members as $member) { $this->email->sendFeedbackResolved( $member['email'], $tipoLabel . ': ' . $excerpt, $resolution ); } } private function translateTipo(string $tipo): string { return match ($tipo) { 'bug' => 'Bug', 'ux' => 'Miglioramento UX', 'funzionalita' => 'Richiesta funzionalità', 'domanda' => 'Domanda', default => 'Altro', }; } }