feat: add GeneratesUniqueSlug class for unique slug generation
feat: create PostCategoryFactory for generating post category data feat: create PostFactory for generating post data feat: add retry_requested_at column to services table feat: add vcenter provisioning fields to products table feat: add module_host_id to os table feat: create email_notification_templates table for email templates feat: add scenario and format fields to template table feat: add admin fields to transactions table fix: update ticket table columns and types feat: add uuid to ticket table and backfill existing records fix: make ticket_reply user_id nullable feat: create notifications table for user notifications fix: make ticket uuid nullable feat: create post_categories table for blog categories feat: create posts table for blog posts feat: add invoice email template for PDF generation
This commit is contained in:
@@ -7,6 +7,7 @@ use App\Http\Services\vCenter;
|
||||
use App\Jobs\Mail\Customer\VMCreationFailedJob;
|
||||
use App\Jobs\VirtualMachine\Provision\ChangePasswordViaVsphereJob;
|
||||
use App\Services\VmProvisionService;
|
||||
use App\Models\ModuleHost;
|
||||
use App\Models\OrderItem;
|
||||
use App\Models\Os;
|
||||
use App\Models\ModuleVmware;
|
||||
@@ -62,7 +63,7 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
return false;
|
||||
}
|
||||
|
||||
$osModel = Os::with('osGroup.host.datacenter.vcenter')->find($this->item->os_id);
|
||||
$osModel = Os::with(['host.datacenter.vCenter', 'osGroup.host.datacenter.vCenter'])->find($this->item->os_id);
|
||||
|
||||
if (!$osModel) {
|
||||
$errorMessage = "Không tìm thấy OS id={$this->item->os_id} trong OrderItem #{$this->item->id}. os_id không hợp lệ.";
|
||||
@@ -81,20 +82,59 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
}
|
||||
|
||||
$template = $osModel->template_id;
|
||||
$host = $osModel->osGroup?->host; // từ os_group.module_host_id
|
||||
|
||||
// Ưu tiên 1: host gán trực tiếp trên OS
|
||||
$host = $osModel->host;
|
||||
$datacenter = $host?->datacenter;
|
||||
$vcenter = $datacenter?->vcenter;
|
||||
$vcenter = $datacenter?->vCenter;
|
||||
|
||||
// Ưu tiên 2: host từ osGroup (cũ)
|
||||
if (!$host || !$datacenter || !$vcenter) {
|
||||
$groupHost = $osModel->osGroup?->host;
|
||||
$host = $host ?? $groupHost;
|
||||
$datacenter = $datacenter ?? $groupHost?->datacenter;
|
||||
$vcenter = $vcenter ?? $groupHost?->datacenter?->vCenter;
|
||||
|
||||
if ($groupHost) {
|
||||
Log::info('CreateVirtualMachineJob: dùng host từ osGroup làm fallback', [
|
||||
'os_id' => $osModel->id,
|
||||
'os_group_id' => $osModel->os_group_id,
|
||||
'host_id' => $groupHost->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Ưu tiên 3: host từ sản phẩm (cấu hình tab Provisioning)
|
||||
if ((!$host || !$datacenter || !$vcenter) && $product->module_host_id) {
|
||||
$productHost = ModuleHost::with('datacenter.vCenter')->find($product->module_host_id);
|
||||
if ($productHost) {
|
||||
$host = $host ?? $productHost;
|
||||
$datacenter = $datacenter ?? $productHost->datacenter;
|
||||
$vcenter = $vcenter ?? $productHost->datacenter?->vCenter;
|
||||
|
||||
Log::info('CreateVirtualMachineJob: dùng host từ sản phẩm làm fallback', [
|
||||
'product_id' => $product->id,
|
||||
'host_id' => $productHost->id,
|
||||
'datacenter_id' => $productHost->datacenter?->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$vcenter || !$host || !$datacenter) {
|
||||
$errorMessage = "Thiếu thông tin host/datacenter/vcenter từ sản phẩm ID #{$product->id}";
|
||||
$errorMessage = "Thiếu thông tin host/datacenter/vcenter từ sản phẩm ID #{$product->id}. "
|
||||
. "Vui lòng cấu hình vCenter trong tab Provisioning của sản phẩm, "
|
||||
. "hoặc gán host cho OS group của OS được chọn.";
|
||||
Log::error($errorMessage);
|
||||
$telegram->SendErrorMessage([
|
||||
'error' => $errorMessage,
|
||||
'product_id' => $product->id,
|
||||
'order_item_id' => $this->item->id,
|
||||
'os_id' => $this->item->os_id,
|
||||
'os_group_name' => $osModel->osGroup?->name,
|
||||
'product_module_host_id' => $product->module_host_id,
|
||||
'host_id' => $host?->id,
|
||||
'datacenter_id' => $datacenter?->id,
|
||||
'vcenter_id' => $vcenter?->id
|
||||
'vcenter_id' => $vcenter?->id,
|
||||
], 'CreateVirtualMachineJob - Missing Infrastructure');
|
||||
|
||||
if (!$this->isRetry) {
|
||||
@@ -171,7 +211,7 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
]);
|
||||
Log::info('CreateVirtualMachineJob - Result: ' . json_encode($result));
|
||||
if (is_array($result) && ($result['error'] ?? false)) {
|
||||
$errorMessage = 'Tạo máy ảo thất bại: ' . json_encode($result);
|
||||
$errorMessage = $this->translateVcenterError($result);
|
||||
Log::warning($errorMessage);
|
||||
$telegram->SendErrorMessage([
|
||||
'error' => $result['error'] ?? 'Unknown error',
|
||||
@@ -490,6 +530,32 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
* Gửi email thông báo VM đã khởi tạo thành công cho khách hàng.
|
||||
* Gọi ngay sau khi tạo module_vmware — cho cả Linux lẫn Windows.
|
||||
*/
|
||||
/**
|
||||
* Dịch error từ vCenter API sang tiếng Việt dễ hiểu.
|
||||
*/
|
||||
private function translateVcenterError(array $result): string
|
||||
{
|
||||
$data = $result['data'] ?? [];
|
||||
$errorType = is_array($data) ? ($data['error_type'] ?? '') : '';
|
||||
$serverName = $this->item->server_name ?? '';
|
||||
|
||||
$map = [
|
||||
'ALREADY_EXISTS' => "Tên máy chủ \"{$serverName}\" đã tồn tại trong hệ thống. Vui lòng liên hệ hỗ trợ để được xử lý hoặc chọn tên máy chủ khác.",
|
||||
'NOT_FOUND' => 'Không tìm thấy template hoặc tài nguyên trên vCenter. Vui lòng liên hệ hỗ trợ.',
|
||||
'RESOURCE_INACCESSIBLE' => 'Tài nguyên vCenter không khả dụng. Vui lòng thử lại sau hoặc liên hệ hỗ trợ.',
|
||||
'INVALID_ARGUMENT' => 'Thông tin cấu hình máy chủ không hợp lệ. Vui lòng liên hệ hỗ trợ.',
|
||||
'UNABLE_TO_ALLOCATE_RESOURCE' => 'Không đủ tài nguyên để khởi tạo máy chủ. Vui lòng liên hệ hỗ trợ.',
|
||||
'TIMED_OUT' => 'Yêu cầu khởi tạo máy chủ hết thời gian chờ. Vui lòng thử lại sau.',
|
||||
];
|
||||
|
||||
if ($errorType && isset($map[$errorType])) {
|
||||
return $map[$errorType];
|
||||
}
|
||||
|
||||
// Fallback: không để lộ JSON thô ra email khách hàng
|
||||
return "Không thể khởi tạo máy chủ ảo lúc này. Chúng tôi đã ghi nhận và sẽ xử lý trong thời gian sớm nhất. Vui lòng liên hệ hỗ trợ nếu cần.";
|
||||
}
|
||||
|
||||
private function getBillingCycleLabel(?int $months): string
|
||||
{
|
||||
return match ($months) {
|
||||
|
||||
Reference in New Issue
Block a user