Update
This commit is contained in:
24
app/Facades/TrackingAnalytics.php
Normal file
24
app/Facades/TrackingAnalytics.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Facades;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Facade;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method static void trackSearch(string $deviceId, string $searchType, string $searchQuery, ?array $searchFilters = null, ?\Carbon\Carbon $actionAt = null, ?string $userAgent = null, ?string $ipAddress = null, string $platform = 'web')
|
||||||
|
* @method static void trackContentSelection(string $deviceId, string $contentType, int $contentId, string $contentName, string $selectionMethod, ?string $searchQuery = null, ?\Carbon\Carbon $actionAt = null, ?string $userAgent = null, ?string $ipAddress = null, string $platform = 'web')
|
||||||
|
* @method static int trackExport(string $deviceId, ?int $memeId, ?int $memeMediaId, ?int $backgroundMediaId, array $captionTexts, string $exportFormat, string $exportQuality = 'standard', ?\Carbon\Carbon $actionAt = null, ?string $userAgent = null, ?string $ipAddress = null, string $platform = 'web')
|
||||||
|
* @method static void updateExportStatus(int $trackingExportId, string $status, ?string $errorMessage = null, ?\Carbon\Carbon $completedAt = null)
|
||||||
|
* @method static array getDeviceContext()
|
||||||
|
* @method static string generateDeviceId()
|
||||||
|
* @method static void quickTrackSearch(string $searchType, string $searchQuery, ?array $searchFilters = null)
|
||||||
|
* @method static void quickTrackContentSelection(string $contentType, int $contentId, string $contentName, string $selectionMethod, ?string $searchQuery = null)
|
||||||
|
* @method static int quickTrackExport(?int $memeId, ?int $memeMediaId, ?int $backgroundMediaId, array $captionTexts, string $exportFormat, string $exportQuality = 'standard')
|
||||||
|
*/
|
||||||
|
class TrackingAnalytics extends Facade
|
||||||
|
{
|
||||||
|
protected static function getFacadeAccessor(): string
|
||||||
|
{
|
||||||
|
return 'tracking-analytics';
|
||||||
|
}
|
||||||
|
}
|
||||||
69
app/Jobs/TrackContentSelectionJob.php
Normal file
69
app/Jobs/TrackContentSelectionJob.php
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\TrackingContentSelection;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class TrackContentSelectionJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private string $deviceId,
|
||||||
|
private string $contentType,
|
||||||
|
private int $contentId,
|
||||||
|
private string $contentName,
|
||||||
|
private string $selectionMethod,
|
||||||
|
private ?string $searchQuery,
|
||||||
|
private Carbon $actionAt,
|
||||||
|
private ?string $userAgent,
|
||||||
|
private ?string $ipAddress,
|
||||||
|
private string $platform
|
||||||
|
) {
|
||||||
|
$this->onQueue('tracking');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
TrackingContentSelection::create([
|
||||||
|
'device_id' => $this->deviceId,
|
||||||
|
'content_type' => $this->contentType,
|
||||||
|
'content_id' => $this->contentId,
|
||||||
|
'content_name' => $this->contentName,
|
||||||
|
'selection_method' => $this->selectionMethod,
|
||||||
|
'search_query' => $this->searchQuery,
|
||||||
|
'action_at' => $this->actionAt,
|
||||||
|
'user_agent' => $this->userAgent,
|
||||||
|
'ip_address' => $this->ipAddress,
|
||||||
|
'platform' => $this->platform,
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Failed to track content selection', [
|
||||||
|
'device_id' => $this->deviceId,
|
||||||
|
'content_type' => $this->contentType,
|
||||||
|
'content_id' => $this->contentId,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function failed(\Throwable $exception): void
|
||||||
|
{
|
||||||
|
Log::error('TrackContentSelectionJob failed permanently', [
|
||||||
|
'device_id' => $this->deviceId,
|
||||||
|
'content_type' => $this->contentType,
|
||||||
|
'content_id' => $this->contentId,
|
||||||
|
'exception' => $exception->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
74
app/Jobs/TrackExportJob.php
Normal file
74
app/Jobs/TrackExportJob.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\TrackingExport;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class TrackExportJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private string $deviceId,
|
||||||
|
private ?int $memeId,
|
||||||
|
private ?int $memeMediaId,
|
||||||
|
private ?int $backgroundMediaId,
|
||||||
|
private array $captionTexts,
|
||||||
|
private string $exportFormat,
|
||||||
|
private string $exportQuality,
|
||||||
|
private Carbon $actionAt,
|
||||||
|
private ?string $userAgent,
|
||||||
|
private ?string $ipAddress,
|
||||||
|
private string $platform
|
||||||
|
) {
|
||||||
|
$this->onQueue('tracking');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$trackingExport = TrackingExport::create([
|
||||||
|
'device_id' => $this->deviceId,
|
||||||
|
'meme_id' => $this->memeId,
|
||||||
|
'meme_media_id' => $this->memeMediaId,
|
||||||
|
'background_media_id' => $this->backgroundMediaId,
|
||||||
|
'caption_texts' => $this->captionTexts,
|
||||||
|
'export_format' => $this->exportFormat,
|
||||||
|
'export_quality' => $this->exportQuality,
|
||||||
|
'export_status' => 'initiated',
|
||||||
|
'action_at' => $this->actionAt,
|
||||||
|
'user_agent' => $this->userAgent,
|
||||||
|
'ip_address' => $this->ipAddress,
|
||||||
|
'platform' => $this->platform,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $trackingExport->id;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Failed to track export', [
|
||||||
|
'device_id' => $this->deviceId,
|
||||||
|
'meme_id' => $this->memeId,
|
||||||
|
'export_format' => $this->exportFormat,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function failed(\Throwable $exception): void
|
||||||
|
{
|
||||||
|
Log::error('TrackExportJob failed permanently', [
|
||||||
|
'device_id' => $this->deviceId,
|
||||||
|
'meme_id' => $this->memeId,
|
||||||
|
'export_format' => $this->exportFormat,
|
||||||
|
'exception' => $exception->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
65
app/Jobs/TrackSearchJob.php
Normal file
65
app/Jobs/TrackSearchJob.php
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\TrackingSearch;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class TrackSearchJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private string $deviceId,
|
||||||
|
private string $searchType,
|
||||||
|
private string $searchQuery,
|
||||||
|
private ?array $searchFilters,
|
||||||
|
private Carbon $actionAt,
|
||||||
|
private ?string $userAgent,
|
||||||
|
private ?string $ipAddress,
|
||||||
|
private string $platform
|
||||||
|
) {
|
||||||
|
$this->onQueue('tracking');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
TrackingSearch::create([
|
||||||
|
'device_id' => $this->deviceId,
|
||||||
|
'search_type' => $this->searchType,
|
||||||
|
'search_query' => $this->searchQuery,
|
||||||
|
'search_filters' => $this->searchFilters,
|
||||||
|
'action_at' => $this->actionAt,
|
||||||
|
'user_agent' => $this->userAgent,
|
||||||
|
'ip_address' => $this->ipAddress,
|
||||||
|
'platform' => $this->platform,
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Failed to track search', [
|
||||||
|
'device_id' => $this->deviceId,
|
||||||
|
'search_type' => $this->searchType,
|
||||||
|
'search_query' => $this->searchQuery,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function failed(\Throwable $exception): void
|
||||||
|
{
|
||||||
|
Log::error('TrackSearchJob failed permanently', [
|
||||||
|
'device_id' => $this->deviceId,
|
||||||
|
'search_type' => $this->searchType,
|
||||||
|
'search_query' => $this->searchQuery,
|
||||||
|
'exception' => $exception->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
66
app/Jobs/UpdateExportStatusJob.php
Normal file
66
app/Jobs/UpdateExportStatusJob.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\TrackingExport;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class UpdateExportStatusJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private int $trackingExportId,
|
||||||
|
private string $status,
|
||||||
|
private ?string $errorMessage,
|
||||||
|
private ?Carbon $completedAt
|
||||||
|
) {
|
||||||
|
$this->onQueue('tracking');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$trackingExport = TrackingExport::findOrFail($this->trackingExportId);
|
||||||
|
|
||||||
|
$updateData = [
|
||||||
|
'export_status' => $this->status,
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->errorMessage !== null) {
|
||||||
|
$updateData['error_message'] = $this->errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->completedAt !== null) {
|
||||||
|
$updateData['completed_at'] = $this->completedAt;
|
||||||
|
} elseif (in_array($this->status, ['completed', 'failed'])) {
|
||||||
|
$updateData['completed_at'] = now();
|
||||||
|
}
|
||||||
|
|
||||||
|
$trackingExport->update($updateData);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Failed to update export status', [
|
||||||
|
'tracking_export_id' => $this->trackingExportId,
|
||||||
|
'status' => $this->status,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function failed(\Throwable $exception): void
|
||||||
|
{
|
||||||
|
Log::error('UpdateExportStatusJob failed permanently', [
|
||||||
|
'tracking_export_id' => $this->trackingExportId,
|
||||||
|
'status' => $this->status,
|
||||||
|
'exception' => $exception->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
51
app/Models/TrackingContentSelection.php
Normal file
51
app/Models/TrackingContentSelection.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
class TrackingContentSelection extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'device_id',
|
||||||
|
'user_agent',
|
||||||
|
'ip_address',
|
||||||
|
'platform',
|
||||||
|
'content_type',
|
||||||
|
'content_id',
|
||||||
|
'content_name',
|
||||||
|
'search_query',
|
||||||
|
'selection_method',
|
||||||
|
'action_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'action_at' => 'datetime',
|
||||||
|
'created_at' => 'datetime',
|
||||||
|
'updated_at' => 'datetime',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $attributes = [
|
||||||
|
'platform' => 'web',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the related content (polymorphic relationship)
|
||||||
|
*/
|
||||||
|
public function content()
|
||||||
|
{
|
||||||
|
if ($this->content_type === 'meme') {
|
||||||
|
return $this->belongsTo(MemeMedia::class, 'content_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->content_type === 'background') {
|
||||||
|
return $this->belongsTo(BackgroundMedia::class, 'content_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
91
app/Models/TrackingExport.php
Normal file
91
app/Models/TrackingExport.php
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
class TrackingExport extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'device_id',
|
||||||
|
'user_agent',
|
||||||
|
'ip_address',
|
||||||
|
'platform',
|
||||||
|
'meme_id',
|
||||||
|
'meme_media_id',
|
||||||
|
'background_media_id',
|
||||||
|
'caption_texts',
|
||||||
|
'export_format',
|
||||||
|
'export_quality',
|
||||||
|
'export_status',
|
||||||
|
'error_message',
|
||||||
|
'action_at',
|
||||||
|
'completed_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'caption_texts' => 'array',
|
||||||
|
'action_at' => 'datetime',
|
||||||
|
'completed_at' => 'datetime',
|
||||||
|
'created_at' => 'datetime',
|
||||||
|
'updated_at' => 'datetime',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $attributes = [
|
||||||
|
'platform' => 'web',
|
||||||
|
'export_quality' => 'standard',
|
||||||
|
'export_status' => 'initiated',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the associated meme
|
||||||
|
*/
|
||||||
|
public function meme(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Meme::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the associated meme media
|
||||||
|
*/
|
||||||
|
public function memeMedia(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(MemeMedia::class, 'meme_media_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the associated background media
|
||||||
|
*/
|
||||||
|
public function backgroundMedia(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(BackgroundMedia::class, 'background_media_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope for completed exports
|
||||||
|
*/
|
||||||
|
public function scopeCompleted($query)
|
||||||
|
{
|
||||||
|
return $query->where('export_status', 'completed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope for failed exports
|
||||||
|
*/
|
||||||
|
public function scopeFailed($query)
|
||||||
|
{
|
||||||
|
return $query->where('export_status', 'failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope for processing exports
|
||||||
|
*/
|
||||||
|
public function scopeProcessing($query)
|
||||||
|
{
|
||||||
|
return $query->where('export_status', 'processing');
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/Models/TrackingSearch.php
Normal file
33
app/Models/TrackingSearch.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
||||||
|
class TrackingSearch extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'device_id',
|
||||||
|
'user_agent',
|
||||||
|
'ip_address',
|
||||||
|
'platform',
|
||||||
|
'search_type',
|
||||||
|
'search_query',
|
||||||
|
'search_filters',
|
||||||
|
'action_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'search_filters' => 'array',
|
||||||
|
'action_at' => 'datetime',
|
||||||
|
'created_at' => 'datetime',
|
||||||
|
'updated_at' => 'datetime',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $attributes = [
|
||||||
|
'platform' => 'web',
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use App\Models\MemeMedia;
|
use App\Models\MemeMedia;
|
||||||
use App\Observers\MemeMediaObserver;
|
use App\Observers\MemeMediaObserver;
|
||||||
|
use App\Services\TrackingAnalyticsService;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
@@ -13,7 +14,9 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
//
|
$this->app->singleton('tracking-analytics', function ($app) {
|
||||||
|
return new TrackingAnalyticsService();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
205
app/Services/TrackingAnalyticsService.php
Normal file
205
app/Services/TrackingAnalyticsService.php
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Jobs\TrackSearchJob;
|
||||||
|
use App\Jobs\TrackContentSelectionJob;
|
||||||
|
use App\Jobs\TrackExportJob;
|
||||||
|
use App\Jobs\UpdateExportStatusJob;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
class TrackingAnalyticsService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Track a search action
|
||||||
|
*/
|
||||||
|
public function trackSearch(
|
||||||
|
string $deviceId,
|
||||||
|
string $searchType,
|
||||||
|
string $searchQuery,
|
||||||
|
?array $searchFilters = null,
|
||||||
|
?Carbon $actionAt = null,
|
||||||
|
?string $userAgent = null,
|
||||||
|
?string $ipAddress = null,
|
||||||
|
string $platform = 'web'
|
||||||
|
): void {
|
||||||
|
TrackSearchJob::dispatch(
|
||||||
|
$deviceId,
|
||||||
|
$searchType,
|
||||||
|
$searchQuery,
|
||||||
|
$searchFilters,
|
||||||
|
$actionAt ?? now(),
|
||||||
|
$userAgent,
|
||||||
|
$ipAddress,
|
||||||
|
$platform
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track a content selection action
|
||||||
|
*/
|
||||||
|
public function trackContentSelection(
|
||||||
|
string $deviceId,
|
||||||
|
string $contentType,
|
||||||
|
int $contentId,
|
||||||
|
string $contentName,
|
||||||
|
string $selectionMethod,
|
||||||
|
?string $searchQuery = null,
|
||||||
|
?Carbon $actionAt = null,
|
||||||
|
?string $userAgent = null,
|
||||||
|
?string $ipAddress = null,
|
||||||
|
string $platform = 'web'
|
||||||
|
): void {
|
||||||
|
TrackContentSelectionJob::dispatch(
|
||||||
|
$deviceId,
|
||||||
|
$contentType,
|
||||||
|
$contentId,
|
||||||
|
$contentName,
|
||||||
|
$selectionMethod,
|
||||||
|
$searchQuery,
|
||||||
|
$actionAt ?? now(),
|
||||||
|
$userAgent,
|
||||||
|
$ipAddress,
|
||||||
|
$platform
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track an export action
|
||||||
|
*/
|
||||||
|
public function trackExport(
|
||||||
|
string $deviceId,
|
||||||
|
?int $memeId,
|
||||||
|
?int $memeMediaId,
|
||||||
|
?int $backgroundMediaId,
|
||||||
|
array $captionTexts,
|
||||||
|
string $exportFormat,
|
||||||
|
string $exportQuality = 'standard',
|
||||||
|
?Carbon $actionAt = null,
|
||||||
|
?string $userAgent = null,
|
||||||
|
?string $ipAddress = null,
|
||||||
|
string $platform = 'web'
|
||||||
|
): int {
|
||||||
|
return TrackExportJob::dispatchSync(
|
||||||
|
$deviceId,
|
||||||
|
$memeId,
|
||||||
|
$memeMediaId,
|
||||||
|
$backgroundMediaId,
|
||||||
|
$captionTexts,
|
||||||
|
$exportFormat,
|
||||||
|
$exportQuality,
|
||||||
|
$actionAt ?? now(),
|
||||||
|
$userAgent,
|
||||||
|
$ipAddress,
|
||||||
|
$platform
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update export status
|
||||||
|
*/
|
||||||
|
public function updateExportStatus(
|
||||||
|
int $trackingExportId,
|
||||||
|
string $status,
|
||||||
|
?string $errorMessage = null,
|
||||||
|
?Carbon $completedAt = null
|
||||||
|
): void {
|
||||||
|
UpdateExportStatusJob::dispatch(
|
||||||
|
$trackingExportId,
|
||||||
|
$status,
|
||||||
|
$errorMessage,
|
||||||
|
$completedAt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get device context from request
|
||||||
|
*/
|
||||||
|
public function getDeviceContext(): array
|
||||||
|
{
|
||||||
|
$request = request();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'user_agent' => $request->userAgent(),
|
||||||
|
'ip_address' => $request->ip(),
|
||||||
|
'platform' => 'web', // Default for now, can be enhanced for mobile detection
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a device ID from request
|
||||||
|
*/
|
||||||
|
public function generateDeviceId(): string
|
||||||
|
{
|
||||||
|
$request = request();
|
||||||
|
|
||||||
|
// Generate a consistent device ID based on session or create new one
|
||||||
|
if ($request->session()->has('device_id')) {
|
||||||
|
return $request->session()->get('device_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
$deviceId = str()->uuid()->toString();
|
||||||
|
$request->session()->put('device_id', $deviceId);
|
||||||
|
|
||||||
|
return $deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quick track methods with auto device context
|
||||||
|
*/
|
||||||
|
public function quickTrackSearch(string $searchType, string $searchQuery, ?array $searchFilters = null): void
|
||||||
|
{
|
||||||
|
$context = $this->getDeviceContext();
|
||||||
|
$deviceId = $this->generateDeviceId();
|
||||||
|
|
||||||
|
$this->trackSearch(
|
||||||
|
$deviceId,
|
||||||
|
$searchType,
|
||||||
|
$searchQuery,
|
||||||
|
$searchFilters,
|
||||||
|
null,
|
||||||
|
$context['user_agent'],
|
||||||
|
$context['ip_address'],
|
||||||
|
$context['platform']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function quickTrackContentSelection(string $contentType, int $contentId, string $contentName, string $selectionMethod, ?string $searchQuery = null): void
|
||||||
|
{
|
||||||
|
$context = $this->getDeviceContext();
|
||||||
|
$deviceId = $this->generateDeviceId();
|
||||||
|
|
||||||
|
$this->trackContentSelection(
|
||||||
|
$deviceId,
|
||||||
|
$contentType,
|
||||||
|
$contentId,
|
||||||
|
$contentName,
|
||||||
|
$selectionMethod,
|
||||||
|
$searchQuery,
|
||||||
|
null,
|
||||||
|
$context['user_agent'],
|
||||||
|
$context['ip_address'],
|
||||||
|
$context['platform']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function quickTrackExport(?int $memeId, ?int $memeMediaId, ?int $backgroundMediaId, array $captionTexts, string $exportFormat, string $exportQuality = 'standard'): int
|
||||||
|
{
|
||||||
|
$context = $this->getDeviceContext();
|
||||||
|
$deviceId = $this->generateDeviceId();
|
||||||
|
|
||||||
|
return $this->trackExport(
|
||||||
|
$deviceId,
|
||||||
|
$memeId,
|
||||||
|
$memeMediaId,
|
||||||
|
$backgroundMediaId,
|
||||||
|
$captionTexts,
|
||||||
|
$exportFormat,
|
||||||
|
$exportQuality,
|
||||||
|
null,
|
||||||
|
$context['user_agent'],
|
||||||
|
$context['ip_address'],
|
||||||
|
$context['platform']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,6 +85,7 @@
|
|||||||
|
|
||||||
'waits' => [
|
'waits' => [
|
||||||
'redis:default' => 60,
|
'redis:default' => 60,
|
||||||
|
'redis:tracking' => 30,
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -253,6 +254,20 @@
|
|||||||
'nice' => 0,
|
'nice' => 0,
|
||||||
'rest' => 0,
|
'rest' => 0,
|
||||||
],
|
],
|
||||||
|
'supervisor-tracking' => [
|
||||||
|
'connection' => 'redis',
|
||||||
|
'queue' => ['tracking'],
|
||||||
|
'balance' => 'auto',
|
||||||
|
'autoScalingStrategy' => 'time',
|
||||||
|
'maxProcesses' => 2,
|
||||||
|
'maxTime' => 0,
|
||||||
|
'maxJobs' => 0,
|
||||||
|
'memory' => 256,
|
||||||
|
'tries' => 3,
|
||||||
|
'timeout' => 30,
|
||||||
|
'nice' => 0,
|
||||||
|
'rest' => 0,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'local' => [
|
'local' => [
|
||||||
@@ -312,6 +327,20 @@
|
|||||||
'nice' => 0,
|
'nice' => 0,
|
||||||
'rest' => 0,
|
'rest' => 0,
|
||||||
],
|
],
|
||||||
|
'supervisor-tracking' => [
|
||||||
|
'connection' => 'redis',
|
||||||
|
'queue' => ['tracking'],
|
||||||
|
'balance' => 'auto',
|
||||||
|
'autoScalingStrategy' => 'time',
|
||||||
|
'maxProcesses' => 2,
|
||||||
|
'maxTime' => 0,
|
||||||
|
'maxJobs' => 0,
|
||||||
|
'memory' => 256,
|
||||||
|
'tries' => 3,
|
||||||
|
'timeout' => 30,
|
||||||
|
'nice' => 0,
|
||||||
|
'rest' => 0,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('tracking_searches', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
// Common fields
|
||||||
|
$table->string('device_id');
|
||||||
|
$table->text('user_agent')->nullable();
|
||||||
|
$table->string('ip_address')->nullable();
|
||||||
|
$table->enum('platform', ['web', 'ios', 'android'])->default('web');
|
||||||
|
|
||||||
|
// Search-specific fields
|
||||||
|
$table->enum('search_type', ['meme', 'background']);
|
||||||
|
$table->text('search_query');
|
||||||
|
$table->json('search_filters')->nullable();
|
||||||
|
|
||||||
|
// Timestamps
|
||||||
|
$table->timestamp('action_at');
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
// Indexes
|
||||||
|
$table->index(['device_id', 'action_at']);
|
||||||
|
$table->index(['search_type', 'action_at']);
|
||||||
|
$table->index('platform');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('tracking_searches');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('tracking_content_selections', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
// Common fields
|
||||||
|
$table->string('device_id');
|
||||||
|
$table->text('user_agent')->nullable();
|
||||||
|
$table->string('ip_address')->nullable();
|
||||||
|
$table->enum('platform', ['web', 'ios', 'android'])->default('web');
|
||||||
|
|
||||||
|
// Content selection fields
|
||||||
|
$table->enum('content_type', ['meme', 'background']);
|
||||||
|
$table->unsignedBigInteger('content_id');
|
||||||
|
$table->string('content_name');
|
||||||
|
$table->text('search_query')->nullable();
|
||||||
|
$table->enum('selection_method', ['search', 'browse', 'featured', 'recent']);
|
||||||
|
|
||||||
|
// Timestamps
|
||||||
|
$table->timestamp('action_at');
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
// Indexes
|
||||||
|
$table->index(['device_id', 'action_at']);
|
||||||
|
$table->index(['content_type', 'content_id']);
|
||||||
|
$table->index(['selection_method', 'action_at']);
|
||||||
|
$table->index('platform');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('tracking_content_selections');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('tracking_exports', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
// Common fields
|
||||||
|
$table->string('device_id');
|
||||||
|
$table->text('user_agent')->nullable();
|
||||||
|
$table->string('ip_address')->nullable();
|
||||||
|
$table->enum('platform', ['web', 'ios', 'android'])->default('web');
|
||||||
|
|
||||||
|
// Export-specific fields
|
||||||
|
$table->unsignedBigInteger('meme_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('meme_media_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('background_media_id')->nullable();
|
||||||
|
$table->json('caption_texts');
|
||||||
|
$table->enum('export_format', ['mov', 'webm', 'gif', 'webp']);
|
||||||
|
$table->enum('export_quality', ['standard', 'premium'])->default('standard');
|
||||||
|
$table->enum('export_status', ['initiated', 'processing', 'completed', 'failed'])->default('initiated');
|
||||||
|
$table->text('error_message')->nullable();
|
||||||
|
|
||||||
|
// Timestamps
|
||||||
|
$table->timestamp('action_at');
|
||||||
|
$table->timestamp('completed_at')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
// Foreign key constraints
|
||||||
|
$table->foreign('meme_id')->references('id')->on('memes')->onDelete('set null');
|
||||||
|
$table->foreign('meme_media_id')->references('id')->on('meme_medias')->onDelete('set null');
|
||||||
|
$table->foreign('background_media_id')->references('id')->on('background_medias')->onDelete('set null');
|
||||||
|
|
||||||
|
// Indexes
|
||||||
|
$table->index(['device_id', 'action_at']);
|
||||||
|
$table->index(['export_status', 'action_at']);
|
||||||
|
$table->index(['export_format', 'action_at']);
|
||||||
|
$table->index('platform');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('tracking_exports');
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user