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
+15 -10
View File
@@ -39,6 +39,8 @@ class SendMailCampaignRecipient implements ShouldQueue
$customer = $this->customerId ? Customer::find($this->customerId) : null;
$rendered = $renderer->render($campaign, $renderer->recipientValues($campaign, $customer, $this->email));
$cc = EmailRecipients::parse($campaign->cc);
$bcc = EmailRecipients::parse($campaign->bcc);
$mailConfig = Arr::only($campaign->template?->mailConfig() ?? [], ['from_email', 'from_name', 'reply_to', 'reply_to_name']);
$failed = false;
try {
@@ -46,13 +48,13 @@ class SendMailCampaignRecipient implements ShouldQueue
if ($cc) {
$mailer->cc($cc);
}
if ($bcc = EmailRecipients::parse($campaign->bcc)) {
if ($bcc) {
$mailer->bcc($bcc);
}
$failed = $mailer->send(new GenericTemplateMail(
$rendered['subject'],
$rendered['html'],
Arr::only($campaign->template?->mailConfig() ?? [], ['from_email', 'from_name', 'reply_to', 'reply_to_name']),
$mailConfig,
)) === null;
} catch (\Throwable $e) {
$failed = true;
@@ -64,14 +66,17 @@ class SendMailCampaignRecipient implements ShouldQueue
}
MailLog::create([
'campaign_id' => $campaign->id,
'template_id' => $campaign->template_id ?? 0,
'subject' => $rendered['subject'],
'to' => $this->email,
'cc' => implode(', ', $cc),
'customer_id' => $customer?->id,
'message' => $rendered['html'],
'status' => $failed ? 'failed' : 'sent',
'campaign_id' => $campaign->id,
'template_id' => $campaign->template_id ?? 0,
'subject' => $rendered['subject'],
'from_address' => $mailConfig['from_email'] ?? config('mail.from.address'),
'to' => $this->email,
'cc' => implode(', ', $cc),
'bcc' => implode(', ', $bcc),
'customer_id' => $customer?->id,
'sent_by' => $campaign->created_by,
'message' => $rendered['html'],
'status' => $failed ? 'failed' : 'sent',
]);
$this->markDone($campaign, $failed);