netgescon-day0/app/Modules/StampeRate/Controllers/HistoryController.php

762 lines
27 KiB
PHP

<?php
namespace App\Modules\StampeRate\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Cache;
use App\Http\Controllers\Controller;
use App\Services\HookManager;
use Carbon\Carbon;
/**
* Controller per la gestione della cronologia e tracking
* Modulo: Stampe Rate Avanzate
*
* Gestisce cronologia stampe, tracking email, statistiche,
* report e tutte le funzionalità di monitoraggio del modulo
*/
class HistoryController extends Controller
{
protected $hookManager;
public function __construct()
{
$this->middleware('auth')->except(['trackEmailOpen', 'trackDownload', 'unsubscribe']);
$this->middleware('permission:stampe_rate_history_view')->only(['index', 'dashboard', 'emailTracking']);
$this->middleware('permission:stampe_rate_reports_view')->only(['reports', 'monthlyReport', 'yearlyReport']);
$this->middleware('permission:stampe_rate_export')->only(['export', 'exportCSV', 'exportExcel']);
$this->middleware('permission:stampe_rate_admin')->only(['cleanup', 'archive']);
$this->hookManager = app(HookManager::class);
}
/**
* Dashboard cronologia principale
*/
public function index(Request $request)
{
try {
$this->hookManager->execute('stampe_rate.history.before_index', [$request]);
// Filtri cronologia
$query = DB::table('stampe_rate_history as h')
->leftJoin('stampe_rate as s', 'h.stampa_id', '=', 's.id')
->leftJoin('users as u', 'h.created_by', '=', 'u.id')
->select([
'h.*',
's.nome as stampa_nome',
's.tipo_documento',
'u.name as utente_nome'
])
->where('h.deleted_at', null)
->orderBy('h.created_at', 'desc');
// Filtri
if ($request->filled('date_from')) {
$query->where('h.created_at', '>=', $request->get('date_from'));
}
if ($request->filled('date_to')) {
$query->where('h.created_at', '<=', $request->get('date_to') . ' 23:59:59');
}
if ($request->filled('action')) {
$query->where('h.action', $request->get('action'));
}
if ($request->filled('user_id')) {
$query->where('h.created_by', $request->get('user_id'));
}
if ($request->filled('search')) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('s.nome', 'like', "%{$search}%")
->orWhere('h.dettagli', 'like', "%{$search}%")
->orWhere('u.name', 'like', "%{$search}%");
});
}
$cronologia = $query->paginate(50);
// Statistiche rapide
$stats = $this->getStatsRapide();
// Lista utenti per filtro
$utenti = DB::table('users')
->whereIn('id', function ($query) {
$query->select('created_by')
->from('stampe_rate_history')
->whereNotNull('created_by')
->distinct();
})
->select('id', 'name')
->orderBy('name')
->get();
$this->hookManager->execute('stampe_rate.history.after_index', [$cronologia, $stats]);
return view('modules.stampe-rate.history.index', compact('cronologia', 'stats', 'utenti'));
} catch (\Exception $e) {
Log::error("Errore cronologia stampe: " . $e->getMessage());
return back()->with('error', 'Errore nel caricamento della cronologia: ' . $e->getMessage());
}
}
/**
* Dashboard statistiche avanzate
*/
public function dashboard()
{
try {
$this->hookManager->execute('stampe_rate.history.before_dashboard');
// Statistiche generali
$statsGenerali = [
'stampe_totali' => DB::table('stampe_rate')->where('deleted_at', null)->count(),
'stampe_oggi' => DB::table('stampe_rate')->whereDate('created_at', today())->count(),
'stampe_settimana' => DB::table('stampe_rate')->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()])->count(),
'stampe_mese' => DB::table('stampe_rate')->whereMonth('created_at', now()->month)->whereYear('created_at', now()->year)->count()
];
// Email statistics
$emailStats = [
'inviate_totali' => DB::table('stampe_rate_email_log')->count(),
'inviate_oggi' => DB::table('stampe_rate_email_log')->whereDate('sent_at', today())->count(),
'aperte_totali' => DB::table('stampe_rate_email_log')->whereNotNull('opened_at')->count(),
'click_totali' => DB::table('stampe_rate_email_log')->whereNotNull('clicked_at')->count()
];
// Grafici dati ultimi 30 giorni
$graficoStampe = $this->getGraficoStampeUltimi30Giorni();
$graficoEmail = $this->getGraficoEmailUltimi30Giorni();
// Top template utilizzati
$topTemplate = DB::table('stampe_rate as s')
->leftJoin('stampe_templates as t', 's.template_id', '=', 't.id')
->select('t.nome', DB::raw('COUNT(*) as utilizzi'))
->where('s.deleted_at', null)
->groupBy('t.id', 't.nome')
->orderBy('utilizzi', 'desc')
->limit(10)
->get();
// Errori recenti
$erroriRecenti = DB::table('stampe_rate_history')
->where('action', 'error')
->where('created_at', '>=', now()->subDays(7))
->orderBy('created_at', 'desc')
->limit(10)
->get();
return view('modules.stampe-rate.history.dashboard', compact(
'statsGenerali',
'emailStats',
'graficoStampe',
'graficoEmail',
'topTemplate',
'erroriRecenti'
));
} catch (\Exception $e) {
Log::error("Errore dashboard history: " . $e->getMessage());
return back()->with('error', 'Errore nel caricamento della dashboard: ' . $e->getMessage());
}
}
/**
* Tracking email inviate
*/
public function emailTracking(Request $request)
{
try {
$query = DB::table('stampe_rate_email_log as e')
->leftJoin('stampe_rate as s', 'e.stampa_id', '=', 's.id')
->leftJoin('users as u', 'e.sent_by', '=', 'u.id')
->select([
'e.*',
's.nome as stampa_nome',
'u.name as inviato_da'
])
->orderBy('e.sent_at', 'desc');
// Filtri
if ($request->filled('status')) {
$status = $request->get('status');
switch ($status) {
case 'sent':
$query->whereNotNull('e.sent_at');
break;
case 'opened':
$query->whereNotNull('e.opened_at');
break;
case 'clicked':
$query->whereNotNull('e.clicked_at');
break;
case 'failed':
$query->whereNotNull('e.failed_at');
break;
}
}
if ($request->filled('date_from')) {
$query->where('e.sent_at', '>=', $request->get('date_from'));
}
if ($request->filled('date_to')) {
$query->where('e.sent_at', '<=', $request->get('date_to') . ' 23:59:59');
}
if ($request->filled('search')) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('e.recipient_email', 'like', "%{$search}%")
->orWhere('e.recipient_name', 'like', "%{$search}%")
->orWhere('s.nome', 'like', "%{$search}%");
});
}
$emailLog = $query->paginate(50);
// Statistiche email
$emailStats = [
'totali' => DB::table('stampe_rate_email_log')->count(),
'inviate' => DB::table('stampe_rate_email_log')->whereNotNull('sent_at')->count(),
'aperte' => DB::table('stampe_rate_email_log')->whereNotNull('opened_at')->count(),
'cliccate' => DB::table('stampe_rate_email_log')->whereNotNull('clicked_at')->count(),
'fallite' => DB::table('stampe_rate_email_log')->whereNotNull('failed_at')->count(),
'tasso_apertura' => $this->calcolaTassoApertura(),
'tasso_click' => $this->calcolaTassoClick()
];
return view('modules.stampe-rate.history.email-tracking', compact('emailLog', 'emailStats'));
} catch (\Exception $e) {
Log::error("Errore tracking email: " . $e->getMessage());
return back()->with('error', 'Errore nel caricamento del tracking email: ' . $e->getMessage());
}
}
/**
* Dettaglio tracking singola email
*/
public function emailTrackingDetail($invioId)
{
try {
$invio = DB::table('stampe_rate_email_log as e')
->leftJoin('stampe_rate as s', 'e.stampa_id', '=', 's.id')
->leftJoin('users as u', 'e.sent_by', '=', 'u.id')
->select([
'e.*',
's.nome as stampa_nome',
's.tipo_documento',
'u.name as inviato_da'
])
->where('e.id', $invioId)
->first();
if (!$invio) {
return redirect()->route('stampeRate.history.email.tracking')
->with('error', 'Invio email non trovato');
}
// Cronologia tracking per questa email
$trackingHistory = DB::table('stampe_rate_email_tracking')
->where('email_log_id', $invioId)
->orderBy('tracked_at', 'desc')
->get();
// Allegati inviati
$allegati = DB::table('stampe_rate_email_attachments')
->where('email_log_id', $invioId)
->get();
return view('modules.stampe-rate.history.email-detail', compact('invio', 'trackingHistory', 'allegati'));
} catch (\Exception $e) {
Log::error("Errore dettaglio email: " . $e->getMessage());
return back()->with('error', 'Errore nel caricamento del dettaglio: ' . $e->getMessage());
}
}
/**
* Report e statistiche avanzate
*/
public function reports(Request $request)
{
try {
$tipoReport = $request->get('tipo', 'mensile');
$dataInizio = $request->get('data_inizio', now()->startOfMonth()->format('Y-m-d'));
$dataFine = $request->get('data_fine', now()->endOfMonth()->format('Y-m-d'));
$reportData = $this->generaReport($tipoReport, $dataInizio, $dataFine);
return view('modules.stampe-rate.history.reports', compact('reportData', 'tipoReport', 'dataInizio', 'dataFine'));
} catch (\Exception $e) {
Log::error("Errore report: " . $e->getMessage());
return back()->with('error', 'Errore nella generazione del report: ' . $e->getMessage());
}
}
/**
* Report mensile
*/
public function monthlyReport(Request $request)
{
$mese = $request->get('mese', now()->month);
$anno = $request->get('anno', now()->year);
return $this->generaReportMensile($mese, $anno);
}
/**
* Report annuale
*/
public function yearlyReport(Request $request)
{
$anno = $request->get('anno', now()->year);
return $this->generaReportAnnuale($anno);
}
/**
* Report personalizzato
*/
public function customReport(Request $request)
{
try {
$parametri = $request->validate([
'data_inizio' => 'required|date',
'data_fine' => 'required|date|after:data_inizio',
'metriche' => 'required|array',
'formato' => 'required|in:html,pdf,excel,csv'
]);
$reportData = $this->generaReportPersonalizzato($parametri);
if ($parametri['formato'] === 'html') {
return view('modules.stampe-rate.history.custom-report', compact('reportData', 'parametri'));
} else {
return $this->esportaReport($reportData, $parametri['formato']);
}
} catch (\Exception $e) {
Log::error("Errore report personalizzato: " . $e->getMessage());
return back()->with('error', 'Errore nella generazione del report: ' . $e->getMessage());
}
}
/**
* Export cronologia
*/
public function export(Request $request)
{
try {
$formato = $request->get('formato', 'csv');
$filtri = $request->only(['date_from', 'date_to', 'action', 'user_id']);
$dati = $this->getCronologiaPerExport($filtri);
switch ($formato) {
case 'csv':
return $this->exportCSV($dati);
case 'excel':
return $this->exportExcel($dati);
case 'pdf':
return $this->exportPDF($dati);
default:
throw new \Exception('Formato non supportato');
}
} catch (\Exception $e) {
Log::error("Errore export cronologia: " . $e->getMessage());
return back()->with('error', 'Errore nell\'export: ' . $e->getMessage());
}
}
/**
* Export CSV
*/
public function exportCSV($dati = null)
{
if (!$dati) {
$dati = $this->getCronologiaPerExport();
}
$filename = 'cronologia_stampe_' . date('Y-m-d_H-i-s') . '.csv';
$headers = [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="' . $filename . '"'
];
return response()->stream(function () use ($dati) {
$handle = fopen('php://output', 'w');
// Header CSV
fputcsv($handle, [
'ID',
'Data/Ora',
'Azione',
'Stampa',
'Utente',
'Dettagli',
'IP'
]);
// Dati
foreach ($dati as $record) {
fputcsv($handle, [
$record->id,
$record->created_at,
$record->action,
$record->stampa_nome ?? 'N/A',
$record->utente_nome ?? 'Sistema',
$record->dettagli,
$record->ip_address
]);
}
fclose($handle);
}, 200, $headers);
}
/**
* Export Excel
*/
public function exportExcel($dati = null)
{
// Implementazione Excel con PhpSpreadsheet se necessario
return $this->exportCSV($dati); // Fallback a CSV per ora
}
/**
* Cleanup cronologia vecchia
*/
public function cleanup(Request $request)
{
try {
$giorni = $request->get('giorni', 90);
$dataLimite = now()->subDays($giorni);
$this->hookManager->execute('stampe_rate.history.before_cleanup', [$giorni]);
$eliminati = DB::table('stampe_rate_history')
->where('created_at', '<', $dataLimite)
->delete();
// Cleanup email log
$emailEliminati = DB::table('stampe_rate_email_log')
->where('sent_at', '<', $dataLimite)
->delete();
$this->hookManager->execute('stampe_rate.history.after_cleanup', [$eliminati, $emailEliminati]);
// Invalida cache
Cache::tags(['stampe_rate_history'])->flush();
return response()->json([
'success' => true,
'message' => "Eliminati {$eliminati} record di cronologia e {$emailEliminati} log email"
]);
} catch (\Exception $e) {
Log::error("Errore cleanup cronologia: " . $e->getMessage());
return response()->json(['error' => 'Errore nel cleanup: ' . $e->getMessage()], 500);
}
}
/**
* Archivia cronologia vecchia
*/
public function archive(Request $request)
{
try {
$giorni = $request->get('giorni', 365);
$dataLimite = now()->subDays($giorni);
// Esporta prima di archiviare
$datiDaArchiviare = DB::table('stampe_rate_history')
->where('created_at', '<', $dataLimite)
->get();
if ($datiDaArchiviare->count() > 0) {
$archiveFile = 'archive_' . date('Y-m-d_H-i-s') . '.json';
$archivePath = 'archives/stampe_rate/' . $archiveFile;
Storage::disk('local')->put($archivePath, json_encode($datiDaArchiviare));
// Elimina i record archiviati
$eliminati = DB::table('stampe_rate_history')
->where('created_at', '<', $dataLimite)
->delete();
return response()->json([
'success' => true,
'message' => "Archiviati {$eliminati} record in {$archiveFile}"
]);
} else {
return response()->json([
'info' => 'Nessun record da archiviare'
]);
}
} catch (\Exception $e) {
Log::error("Errore archiviazione: " . $e->getMessage());
return response()->json(['error' => 'Errore nell\'archiviazione: ' . $e->getMessage()], 500);
}
}
/**
* Tracking apertura email (route pubblica)
*/
public function trackEmailOpen($trackingId)
{
try {
// Trova l'email log
$emailLog = DB::table('stampe_rate_email_log')
->where('tracking_id', $trackingId)
->first();
if ($emailLog && !$emailLog->opened_at) {
// Aggiorna apertura
DB::table('stampe_rate_email_log')
->where('id', $emailLog->id)
->update([
'opened_at' => now(),
'opened_ip' => request()->ip(),
'opened_user_agent' => request()->userAgent()
]);
// Log tracking
DB::table('stampe_rate_email_tracking')->insert([
'email_log_id' => $emailLog->id,
'action' => 'opened',
'tracked_at' => now(),
'ip_address' => request()->ip(),
'user_agent' => request()->userAgent()
]);
$this->hookManager->execute('stampe_rate.email.opened', [$emailLog->id]);
}
// Ritorna pixel 1x1 trasparente
return response(base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'))
->header('Content-Type', 'image/gif')
->header('Cache-Control', 'no-cache, no-store, must-revalidate')
->header('Pragma', 'no-cache')
->header('Expires', '0');
} catch (\Exception $e) {
Log::error("Errore tracking email: " . $e->getMessage());
return response('', 200);
}
}
/**
* Tracking download allegati
*/
public function trackDownload($trackingId)
{
try {
// Implementa tracking download
DB::table('stampe_rate_email_tracking')->insert([
'tracking_id' => $trackingId,
'action' => 'download',
'tracked_at' => now(),
'ip_address' => request()->ip(),
'user_agent' => request()->userAgent()
]);
return response()->json(['tracked' => true]);
} catch (\Exception $e) {
Log::error("Errore tracking download: " . $e->getMessage());
return response()->json(['error' => false], 200);
}
}
/**
* Unsubscribe link
*/
public function unsubscribe($token)
{
try {
// Trova email dal token
$emailLog = DB::table('stampe_rate_email_log')
->where('unsubscribe_token', $token)
->first();
if ($emailLog) {
// Aggiorna stato unsubscribe
DB::table('stampe_rate_email_log')
->where('id', $emailLog->id)
->update(['unsubscribed_at' => now()]);
// Aggiungi a blacklist se necessario
DB::table('email_blacklist')->insertOrIgnore([
'email' => $emailLog->recipient_email,
'reason' => 'unsubscribed',
'created_at' => now()
]);
return view('modules.stampe-rate.unsubscribe-success');
} else {
return view('modules.stampe-rate.unsubscribe-error');
}
} catch (\Exception $e) {
Log::error("Errore unsubscribe: " . $e->getMessage());
return view('modules.stampe-rate.unsubscribe-error');
}
}
/**
* Webhook per status email esterni
*/
public function webhookEmailStatus(Request $request)
{
try {
$data = $request->all();
// Log webhook per debugging
Log::info("Webhook email status ricevuto", $data);
// Processa webhook in base al provider
if (isset($data['provider'])) {
switch ($data['provider']) {
case 'sendgrid':
$this->processaSendGridWebhook($data);
break;
case 'mailchimp':
$this->processaMailChimpWebhook($data);
break;
default:
Log::warning("Provider webhook sconosciuto: " . $data['provider']);
}
}
return response()->json(['status' => 'ok']);
} catch (\Exception $e) {
Log::error("Errore webhook email: " . $e->getMessage());
return response()->json(['error' => 'Webhook processing failed'], 500);
}
}
// Metodi di supporto privati
private function getStatsRapide()
{
return [
'azioni_oggi' => DB::table('stampe_rate_history')->whereDate('created_at', today())->count(),
'azioni_settimana' => DB::table('stampe_rate_history')->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()])->count(),
'errori_settimana' => DB::table('stampe_rate_history')->where('action', 'error')->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()])->count(),
'email_inviate_oggi' => DB::table('stampe_rate_email_log')->whereDate('sent_at', today())->count()
];
}
private function getGraficoStampeUltimi30Giorni()
{
return DB::table('stampe_rate')
->selectRaw('DATE(created_at) as data, COUNT(*) as count')
->where('created_at', '>=', now()->subDays(30))
->groupBy('data')
->orderBy('data')
->get();
}
private function getGraficoEmailUltimi30Giorni()
{
return DB::table('stampe_rate_email_log')
->selectRaw('DATE(sent_at) as data, COUNT(*) as inviate, COUNT(opened_at) as aperte')
->where('sent_at', '>=', now()->subDays(30))
->groupBy('data')
->orderBy('data')
->get();
}
private function calcolaTassoApertura()
{
$totali = DB::table('stampe_rate_email_log')->whereNotNull('sent_at')->count();
$aperte = DB::table('stampe_rate_email_log')->whereNotNull('opened_at')->count();
return $totali > 0 ? round(($aperte / $totali) * 100, 2) : 0;
}
private function calcolaTassoClick()
{
$totali = DB::table('stampe_rate_email_log')->whereNotNull('sent_at')->count();
$cliccate = DB::table('stampe_rate_email_log')->whereNotNull('clicked_at')->count();
return $totali > 0 ? round(($cliccate / $totali) * 100, 2) : 0;
}
private function generaReport($tipo, $dataInizio, $dataFine)
{
// Implementa generazione report base
return [
'periodo' => "{$dataInizio} - {$dataFine}",
'stampe_create' => DB::table('stampe_rate')->whereBetween('created_at', [$dataInizio, $dataFine])->count(),
'email_inviate' => DB::table('stampe_rate_email_log')->whereBetween('sent_at', [$dataInizio, $dataFine])->count()
];
}
private function getCronologiaPerExport($filtri = [])
{
$query = DB::table('stampe_rate_history as h')
->leftJoin('stampe_rate as s', 'h.stampa_id', '=', 's.id')
->leftJoin('users as u', 'h.created_by', '=', 'u.id')
->select([
'h.*',
's.nome as stampa_nome',
'u.name as utente_nome'
]);
// Applica filtri
foreach ($filtri as $campo => $valore) {
if ($valore) {
switch ($campo) {
case 'date_from':
$query->where('h.created_at', '>=', $valore);
break;
case 'date_to':
$query->where('h.created_at', '<=', $valore . ' 23:59:59');
break;
case 'action':
$query->where('h.action', $valore);
break;
case 'user_id':
$query->where('h.created_by', $valore);
break;
}
}
}
return $query->orderBy('h.created_at', 'desc')->get();
}
private function processaSendGridWebhook($data)
{
// Implementa processing SendGrid webhook
Log::info("Processing SendGrid webhook", $data);
}
private function processaMailChimpWebhook($data)
{
// Implementa processing MailChimp webhook
Log::info("Processing MailChimp webhook", $data);
}
/**
* API: Ottiene info tracking per AJAX
*/
public function getTrackingInfo($invioId)
{
try {
$info = DB::table('stampe_rate_email_log')
->where('id', $invioId)
->select(['sent_at', 'opened_at', 'clicked_at', 'failed_at'])
->first();
return response()->json(['tracking' => $info]);
} catch (\Exception $e) {
Log::error("Errore API tracking: " . $e->getMessage());
return response()->json(['error' => 'Errore nel caricamento tracking'], 500);
}
}
}