150 lines
4.8 KiB
PHP
150 lines
4.8 KiB
PHP
<?php
|
|
/**
|
|
* FeedbackService — Logica segnalazioni & classificazione AI
|
|
*
|
|
* Responsabilità:
|
|
* - Persistenza segnalazioni (INSERT/UPDATE feedback_reports)
|
|
* - Classificazione asincrona via AIService::classifyFeedback()
|
|
* - Broadcast email agli utenti dell'org quando una segnalazione è risolta
|
|
*/
|
|
|
|
require_once APP_PATH . '/config/database.php';
|
|
require_once APP_PATH . '/services/AIService.php';
|
|
require_once APP_PATH . '/services/EmailService.php';
|
|
|
|
class FeedbackService
|
|
{
|
|
private AIService $ai;
|
|
private EmailService $email;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->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.full_name AS 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',
|
|
};
|
|
}
|
|
}
|