Files
portal-api/app/Jobs/Hosting/ProvisionHostingAccountJob.php
baodev0andClaude Sonnet 5.5 55a834a2f8 feat: nhắc gia hạn QR theo dịch vụ, failover server hosting, CCCD admin, log cấp phát dịch vụ
- Mail campaign: mẫu nhắc gia hạn thanh toán QR, chọn dịch vụ + chu kỳ từng dòng (renewal_services)
- Hosting: nhiều server Plesk cho một gói (service_plan_servers), chuyển server khi server chết
- Admin xem/sửa giấy tờ định danh khách, tuỳ chọn gia hạn cho khách, nhật ký cấp phát dịch vụ
- Đánh dấu mẫu email dịch vụ là non-marketing

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 18:36:41 +07:00

119 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Jobs\Hosting;
use App\Models\Invoice;
use App\Models\OrderItem;
use App\Models\ServicePlan;
use App\Services\Hosting\HostingProvisionFailure;
use App\Services\Hosting\HostingServerSelector;
use App\Services\Plesk\PleskHostingService;
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;
use Throwable;
use App\Support\ServiceProvisionLog;
/**
* Auto-provisions a Plesk hosting account when a hosting order is paid,
* mirroring CreateVirtualMachineJob's role for VPS orders. Reuses the same
* PleskHostingService::provision() the admin "create hosting account" form
* calls, so behavior (customer creation, subscription, notification emails)
* stays identical between manual and automatic provisioning.
*/
class ProvisionHostingAccountJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $timeout = 300;
public int $tries = 1;
public function __construct(public OrderItem $item)
{
}
public function handle(PleskHostingService $plesk, HostingServerSelector $servers, HostingProvisionFailure $failure): void
{
try {
$error = $this->provision($plesk, $servers, $failure);
} catch (Throwable $e) {
$error = $e->getMessage();
}
if ($error !== null) {
$failure->handle($this->item, $error);
}
}
private function provision(PleskHostingService $plesk, HostingServerSelector $servers, HostingProvisionFailure $failure): ?string
{
$service = $this->item->service()->first();
$domainName = $this->item->server_name;
if (! $service || ! $domainName) {
return 'Không tìm thấy dịch vụ hoặc tên miền của đơn hàng.';
}
$servicePlanId = $this->item->product?->service_plan_id;
$servicePlan = $servicePlanId ? ServicePlan::with('plesk')->find($servicePlanId) : null;
$hostingPlan = $servicePlan?->plesk?->plesk_plan_name;
Log::channel('Custom')->info('ProvisionHostingAccountJob resolved service plan', [
'order_item_id' => $this->item->id,
'product_id' => $this->item->product_id,
'product_name' => $this->item->product?->name,
'service_plan_id' => $servicePlanId,
'plesk_plan_name' => $hostingPlan,
]);
if (! $hostingPlan) {
return 'Sản phẩm chưa gắn gói Plesk (service_plan_plesk).';
}
ServiceProvisionLog::record($service, 'provision_started', 'Bắt đầu khởi tạo hosting', ['domain' => $domainName, 'plesk_plan' => $hostingPlan]);
$invoiceId = Invoice::where('order_id', $this->item->order_id)->where('invoice_type', 1)->orderBy('id')->value('id');
$triedServerIds = [];
$lastError = null;
while ($server = $servers->pick($servicePlan, $triedServerIds)) {
$triedServerIds[] = $server->id;
$result = $plesk->provision([
'customer_id' => $this->item->order->customer_id,
'service_id' => $service->id,
'dns_manager_server_id' => $server->id,
'domain_name' => $domainName,
'hosting_plan' => $hostingPlan,
'invoice_id' => $invoiceId,
]);
if (! $result['error']) {
$service->update(['status' => 'active']);
ServiceProvisionLog::record($service, 'provision_completed', 'Khởi tạo hoàn tất — dịch vụ đã hoạt động', ['server_id' => $server->id]);
return null;
}
$lastError = $result['message'] ?? 'Plesk trả về lỗi không rõ.';
$servers->forget($server);
if ($servers->isHealthy($server)) {
return $lastError;
}
$failure->serverFailedOver($this->item, $server, $lastError);
}
return $lastError === null
? 'Gói hosting không có server Plesk nào đang hoạt động.'
: "Tất cả server Plesk của gói đều lỗi. Lỗi cuối: {$lastError}";
}
}