feat: email gửi tay/nhắc gia hạn, gói hosting, sửa dữ liệu sản phẩm

- Gửi email theo template cho 1 khách, tab lịch sử email, nhắc gia hạn dịch vụ hàng loạt
- Mail lưu from/bcc/sent_by; mẫu MARKETING và mẫu cũ trong TemplateSeeder
- Gói hosting: giới hạn, trạng thái, lấy plan Plesk qua CLI gateway
- Backfill payment_method và ngày đơn hàng còn thiếu
- Bổ sung field CPU/RAM/Disk/Băng thông cho loại sản phẩm vps và cloud

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
baodev0
2026-10-09 11:32:05 +07:00
co-authored by Claude Opus 5.5
parent 8651dde6b1
commit 30898ea46f
59 changed files with 1771 additions and 108 deletions
@@ -16,6 +16,8 @@ use App\Repositories\Api\Domain\DomainOrderRepository;
use App\Services\Api\Domain\DomainOrderService;
use App\Services\Api\Domain\DomainRegistrantService;
use App\Services\Email\EmailTemplateService;
use App\Support\EmailTemplateShell;
use App\Support\MailGlobals;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
@@ -278,22 +280,46 @@ class AdminDomainOrderController extends Controller
return;
}
$setting = \App\Models\Setting::where('key', 'setting')->first();
$settingValue = $setting ? json_decode((string) $setting->value, true) : [];
app(EmailTemplateService::class)->send($scenario, $customer->email, [
'company_name' => $settingValue['company'] ?? config('app.name'),
'company_logo' => $settingValue['logo'] ?? '',
'domain_name' => $domainName,
'client_name' => $customer->fullname,
'order_num' => $orderNum,
'reason' => $reason,
...MailGlobals::values(),
...$this->domainHandoverVariables($order),
'domain_name' => $domainName,
'client_name' => $customer->fullname,
'customer_email' => $customer->email,
'order_num' => $orderNum,
'reason' => $reason,
]);
} catch (\Throwable $e) {
Log::error('AdminDomainOrderController: không thể gửi email khách hàng: ' . $e->getMessage());
}
}
/**
* @return array<string, string>
*/
private function domainHandoverVariables(Order $order): array
{
$service = $order->orderItems->first()?->service?->fresh();
if (!$service) {
return [];
}
return [
'registered_at' => $service->started_at?->format('d/m/Y') ?? '',
'expires_at' => $service->expires_at?->format('d/m/Y') ?? '',
'kyc_block' => $service->kyc_url ? $this->kycBlock($service->kyc_url) : '',
];
}
private function kycBlock(string $kycUrl): string
{
return EmailTemplateShell::heading('📋 Lưu ý quan trọng')
. EmailTemplateShell::paragraph('Căn cứ Khoản 2 Điều 9 và Khoản 6 Điều 21 Thông tư số 48/2025/TT-BKHCN ngày 25/12/2025 của Bộ Khoa học và Công nghệ về Quản lý và sử dụng tài nguyên Internet, chủ thể đăng ký tên miền là cá nhân có trách nhiệm hoàn thiện hồ sơ đăng ký thông qua hình thức xác thực điện tử (eKYC) trong vòng <strong>03 ngày</strong> kể từ thời điểm nhận được email này.')
. EmailTemplateShell::button(htmlspecialchars($kycUrl, ENT_QUOTES, 'UTF-8'), '✅ Xác thực eKYC ngay')
. EmailTemplateShell::note('Việc không hoàn thành xác thực đúng hạn có thể ảnh hưởng đến quyền sử dụng tên miền của Quý khách.', '⏰ Lưu ý:');
}
// =========================================================================
// Private formatters
// =========================================================================