New_Micro_Learning/backend/app/Modules/Certificates/Application/IssueCertificate.php

56 خطوط
5.1 KiB
PHP

<?php
namespace App\Modules\Certificates\Application;
use App\Models\User;
use App\Modules\Courses\Domain\CourseVersion;
use Dompdf\Dompdf;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\SvgWriter;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
final class IssueCertificate
{
public function issue(User $learner, CourseVersion $version, ?string $templateId = null, ?int $expiresInMonths = null): string
{
$existing = DB::table('certificates')->where('user_id', $learner->getKey())->where('course_version_id', $version->getKey())->first();
if ($existing) {
return $existing->id;
}
$profile = DB::table('organization_profiles')->where('organization_id', $learner->organization_id)->first();
$template = $templateId ? DB::table('certificate_templates')->where('organization_id', $learner->organization_id)->where('id', $templateId)->first() : DB::table('certificate_templates')->where('organization_id', $learner->organization_id)->where('is_default', true)->where('is_active', true)->first();
$id = (string) Str::ulid();
$code = Str::lower(Str::random(32));
$number = 'ML-'.now()->format('Y').'-'.strtoupper(substr($id, -8));
$url = rtrim((string) config('app.frontend_url'), '/').'/certificate/verify/'.$code;
$snapshot = ['schemaVersion' => 1, 'learnerName' => $learner->name, 'courseTitle' => $version->title, 'organizationName' => $learner->organization?->name, 'signatory' => $profile?->certificate_signatory, 'primaryColor' => $profile?->primary_color ?? '#5b3fd3', 'accentColor' => $profile?->accent_color ?? '#09bdd1', 'verificationUrl' => $url, 'template' => $template ? json_decode($template->canvas, true) : null];
$qr = (new SvgWriter)->write(new QrCode(data: $url))->getString();
$html = $this->html($snapshot, $number, now()->toDateString(), $qr);
$dompdf = new Dompdf(['isRemoteEnabled' => false]);
$dompdf->loadHtml($html, 'UTF-8');
$dompdf->setPaper('A4', 'landscape');
$dompdf->render();
$disk = (string) config('exports.disk', 'local');
$path = 'certificates/'.$learner->organization_id.'/'.$id.'.pdf';
Storage::disk($disk)->put($path, $dompdf->output());
DB::transaction(function () use ($id, $learner, $version, $template, $code, $number, $expiresInMonths, $snapshot, $disk, $path): void {
DB::table('certificates')->insert(['id' => $id, 'organization_id' => $learner->organization_id, 'user_id' => $learner->getKey(), 'course_version_id' => $version->getKey(), 'template_id' => $template?->id, 'serial' => (string) Str::uuid(), 'verification_code' => $code, 'certificate_number' => $number, 'issued_at' => now(), 'expires_at' => $expiresInMonths ? now()->addMonths($expiresInMonths) : null, 'snapshot' => json_encode($snapshot), 'disk' => $disk, 'path' => $path, 'created_at' => now(), 'updated_at' => now()]);
DB::table('in_app_notifications')->insert(['id' => (string) Str::ulid(), 'organization_id' => $learner->organization_id, 'recipient_id' => $learner->getKey(), 'actor_id' => null, 'type' => 'certificate.issued', 'title' => 'گواهی‌نامه جدید صادر شد', 'body' => 'گواهی‌نامه دوره «'.$version->title.'» آماده دریافت است.', 'target_url' => '/learn/progress', 'entity_type' => 'certificate', 'entity_id' => $id, 'data' => json_encode(['certificateNumber' => $number]), 'created_at' => now(), 'updated_at' => now()]);
});
return $id;
}
/** @param array<string, mixed> $snapshot */
private function html(array $snapshot, string $number, string $issued, string $qr): string
{
$color = htmlspecialchars((string) $snapshot['primaryColor']);
$safe = fn (mixed $value) => htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
$qrData = 'data:image/svg+xml;base64,'.base64_encode($qr);
return '<!doctype html><html lang="fa" dir="rtl"><head><meta charset="utf-8"><style>@page{margin:10mm}body{font-family:DejaVu Sans,sans-serif;color:#172033;margin:0}.certificate{height:180mm;border:4px solid '.$color.';padding:18mm;box-sizing:border-box;text-align:center;position:relative}.mark{font-size:18px;color:'.$color.'}.title{font-size:34px;color:'.$color.';margin:18px}.name{font-size:30px;margin:18px}.course{font-size:23px}.meta{position:absolute;bottom:16mm;left:18mm;right:18mm;display:flex;justify-content:space-between;text-align:right;font-size:12px}.qr{width:82px;height:82px}</style></head><body><main class="certificate"><div class="mark">MicroLearn · '.$safe($snapshot['organizationName'] ?? '').'</div><h1 class="title">گواهی‌نامه پایان دوره</h1><p>گواهی می‌شود</p><div class="name">'.$safe($snapshot['learnerName']).'</div><p>دوره</p><div class="course">'.$safe($snapshot['courseTitle']).'</div><div class="meta"><div>شماره: '.$safe($number).'<br>تاریخ صدور: '.$safe($issued).'<br>امضاکننده: '.$safe($snapshot['signatory'] ?? 'مدیریت آموزش').'</div><img class="qr" src="'.$qrData.'"></div></main></body></html>';
}
}