update vm ticket
This commit is contained in:
@@ -102,6 +102,75 @@ class PostCategoryController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function trashed(Request $request): JsonResponse
|
||||
{
|
||||
$perPage = min((int) $request->get('per_page', 15), 100);
|
||||
|
||||
$paginator = PostCategory::onlyTrashed()
|
||||
->withCount('posts')
|
||||
->when(
|
||||
$request->filled('keyword'),
|
||||
fn ($q) => $q->where('name', 'like', '%' . $request->string('keyword')->toString() . '%')
|
||||
)
|
||||
->orderByDesc('deleted_at')
|
||||
->paginate($perPage);
|
||||
|
||||
return response()->json([
|
||||
'error' => false,
|
||||
'data' => [
|
||||
'items' => PostCategoryResource::collection($paginator->items()),
|
||||
'total' => $paginator->total(),
|
||||
'per_page' => $paginator->perPage(),
|
||||
'current_page' => $paginator->currentPage(),
|
||||
'last_page' => $paginator->lastPage(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function restore(int $id): JsonResponse
|
||||
{
|
||||
$category = PostCategory::onlyTrashed()->find($id);
|
||||
|
||||
if (!$category) {
|
||||
return response()->json(['error' => true, 'message' => 'Không tìm thấy danh mục.'], 404);
|
||||
}
|
||||
|
||||
$category->restore();
|
||||
$this->revalidation->revalidateDocs();
|
||||
|
||||
return response()->json(['error' => false, 'message' => 'Khôi phục danh mục thành công.']);
|
||||
}
|
||||
|
||||
public function forceDelete(int $id): JsonResponse
|
||||
{
|
||||
$category = PostCategory::onlyTrashed()->find($id);
|
||||
|
||||
if (!$category) {
|
||||
return response()->json(['error' => true, 'message' => 'Không tìm thấy danh mục.'], 404);
|
||||
}
|
||||
|
||||
$category->forceDelete();
|
||||
|
||||
return response()->json(['error' => false, 'message' => 'Xoá vĩnh viễn danh mục thành công.']);
|
||||
}
|
||||
|
||||
public function bulkRestore(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate(['ids' => 'required|array|min:1', 'ids.*' => 'integer']);
|
||||
$count = PostCategory::onlyTrashed()->whereIn('id', $request->ids)->restore();
|
||||
$this->revalidation->revalidateDocs();
|
||||
|
||||
return response()->json(['error' => false, 'message' => "Đã khôi phục {$count} danh mục."]);
|
||||
}
|
||||
|
||||
public function bulkForceDelete(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate(['ids' => 'required|array|min:1', 'ids.*' => 'integer']);
|
||||
$count = PostCategory::onlyTrashed()->whereIn('id', $request->ids)->forceDelete();
|
||||
|
||||
return response()->json(['error' => false, 'message' => "Đã xoá vĩnh viễn {$count} danh mục."]);
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
$category = PostCategory::withCount('posts')->find($id);
|
||||
|
||||
@@ -136,6 +136,97 @@ class PostController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function trashed(Request $request): JsonResponse
|
||||
{
|
||||
$perPage = min((int) $request->get('per_page', 15), 100);
|
||||
|
||||
$paginator = Post::onlyTrashed()
|
||||
->with('category')
|
||||
->when(
|
||||
$request->filled('keyword'),
|
||||
fn ($q) => $q->where('title', 'like', '%' . $request->string('keyword')->toString() . '%')
|
||||
)
|
||||
->orderByDesc('deleted_at')
|
||||
->paginate($perPage);
|
||||
|
||||
return response()->json([
|
||||
'error' => false,
|
||||
'data' => [
|
||||
'items' => PostResource::collection($paginator->items()),
|
||||
'total' => $paginator->total(),
|
||||
'per_page' => $paginator->perPage(),
|
||||
'current_page' => $paginator->currentPage(),
|
||||
'last_page' => $paginator->lastPage(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function restore(int $id): JsonResponse
|
||||
{
|
||||
$post = Post::onlyTrashed()->find($id);
|
||||
|
||||
if (!$post) {
|
||||
return response()->json(['error' => true, 'message' => 'Không tìm thấy bài viết.'], 404);
|
||||
}
|
||||
|
||||
$post->restore();
|
||||
$this->revalidation->revalidateDocs();
|
||||
|
||||
return response()->json(['error' => false, 'message' => 'Khôi phục bài viết thành công.']);
|
||||
}
|
||||
|
||||
public function forceDelete(int $id): JsonResponse
|
||||
{
|
||||
$post = Post::onlyTrashed()->find($id);
|
||||
|
||||
if (!$post) {
|
||||
return response()->json(['error' => true, 'message' => 'Không tìm thấy bài viết.'], 404);
|
||||
}
|
||||
|
||||
$post->forceDelete();
|
||||
|
||||
return response()->json(['error' => false, 'message' => 'Xoá vĩnh viễn bài viết thành công.']);
|
||||
}
|
||||
|
||||
public function bulkRestore(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate(['ids' => 'required|array|min:1', 'ids.*' => 'integer']);
|
||||
$count = Post::onlyTrashed()->whereIn('id', $request->ids)->restore();
|
||||
$this->revalidation->revalidateDocs();
|
||||
|
||||
return response()->json(['error' => false, 'message' => "Đã khôi phục {$count} bài viết."]);
|
||||
}
|
||||
|
||||
public function bulkForceDelete(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate(['ids' => 'required|array|min:1', 'ids.*' => 'integer']);
|
||||
$count = Post::onlyTrashed()->whereIn('id', $request->ids)->forceDelete();
|
||||
|
||||
return response()->json(['error' => false, 'message' => "Đã xoá vĩnh viễn {$count} bài viết."]);
|
||||
}
|
||||
|
||||
public function bulkDelete(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate(['ids' => 'required|array|min:1', 'ids.*' => 'integer']);
|
||||
$count = Post::whereIn('id', $request->ids)->delete();
|
||||
$this->revalidation->revalidateDocs();
|
||||
|
||||
return response()->json(['error' => false, 'message' => "Đã xoá {$count} bài viết."]);
|
||||
}
|
||||
|
||||
public function bulkUpdateStatus(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'ids' => 'required|array|min:1',
|
||||
'ids.*' => 'integer',
|
||||
'status' => 'required|in:draft,published,hidden',
|
||||
]);
|
||||
$count = Post::whereIn('id', $request->ids)->update(['status' => $request->status]);
|
||||
$this->revalidation->revalidateDocs();
|
||||
|
||||
return response()->json(['error' => false, 'message' => "Đã cập nhật trạng thái {$count} bài viết."]);
|
||||
}
|
||||
|
||||
public function syncWordpress(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
|
||||
@@ -110,7 +110,7 @@ class TicketController extends Controller
|
||||
public function reply(Request $request, string $ticket_num)
|
||||
{
|
||||
$request->validate([
|
||||
'message' => ['required', 'string', 'max:2000'],
|
||||
'message' => ['required', 'string'],
|
||||
]);
|
||||
|
||||
$admin = auth('admin')->user();
|
||||
|
||||
@@ -93,6 +93,7 @@ class VCenterController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function storeNode(Request $request, string $type): JsonResponse
|
||||
{
|
||||
$rules = match ($type) {
|
||||
@@ -187,9 +188,9 @@ class VCenterController extends Controller
|
||||
$allHosts = ModuleHost::with(['datastore', 'network'])->get()->groupBy('module_datacenter_id');
|
||||
$allFolders = ModuleFolder::all()->groupBy('module_datacenter_id');
|
||||
// VMs with a host assignment — grouped by host DB id
|
||||
$allVMs = VirtualMachines::whereNotNull('host_id')->get()->groupBy('host_id');
|
||||
$allVMs = VirtualMachines::with('vmware')->whereNotNull('host_id')->get()->groupBy('host_id');
|
||||
// VMs without a host assignment (synced before host_id was tracked) — grouped by datacenter DB id
|
||||
$orphanVMs = VirtualMachines::whereNull('host_id')->get()->groupBy('datacenter_id');
|
||||
$orphanVMs = VirtualMachines::with('vmware')->whereNull('host_id')->get()->groupBy('datacenter_id');
|
||||
|
||||
$tree = $vcenters->map(function ($vc) use ($allHosts, $allFolders, $allVMs, $orphanVMs) {
|
||||
$dcGroups = ($vc->datacenters ?? collect())->groupBy('datacenter_id');
|
||||
@@ -227,8 +228,8 @@ class VCenterController extends Controller
|
||||
'uuid' => $vm->uuid,
|
||||
'vm_id' => $vm->vm_id,
|
||||
'ip' => $vm->ip,
|
||||
'username' => $vm->username,
|
||||
'password' => $vm->password,
|
||||
'username' => $vm->vmware?->user,
|
||||
'password' => $vm->vmware?->plain_password,
|
||||
'power_state' => $vm->power_state,
|
||||
'folder' => $vm->folder,
|
||||
'cpu' => $vm->cpu,
|
||||
@@ -304,6 +305,8 @@ class VCenterController extends Controller
|
||||
'uuid' => $vm->uuid,
|
||||
'vm_id' => $vm->vm_id,
|
||||
'ip' => $vm->ip,
|
||||
'username' => $vm->vmware?->user,
|
||||
'password' => $vm->vmware?->plain_password,
|
||||
'power_state' => $vm->power_state,
|
||||
'folder' => $vm->folder,
|
||||
'cpu' => $vm->cpu,
|
||||
|
||||
@@ -5,6 +5,10 @@ namespace App\Http\Controllers\Api\Client\Ticket;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\Api\Ticket\TicketResource;
|
||||
use App\Http\Resources\Api\Ticket\TicketReplyResource;
|
||||
use App\Jobs\Ticket\TicketJobs;
|
||||
use App\Mail\TicketCreated;
|
||||
use App\Mail\Admin\TicketAdminNotification;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\Notification;
|
||||
use App\Models\TicketReply;
|
||||
@@ -12,6 +16,8 @@ use App\Events\TicketReplyCreated;
|
||||
use App\Repositories\Ticket\TicketRepositoryInterface;
|
||||
use App\Repositories\TicketReply\TicketReplyRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class TicketController extends Controller
|
||||
@@ -60,22 +66,41 @@ class TicketController extends Controller
|
||||
'description' => ['required', 'string', 'min:20'],
|
||||
'room' => ['nullable', 'string'],
|
||||
'data' => ['nullable', 'array'],
|
||||
'images' => ['nullable', 'array', 'max:5'],
|
||||
'images.*' => ['image', 'mimes:jpg,jpeg,png,gif,webp', 'max:5120'],
|
||||
]);
|
||||
|
||||
$customer = auth('client')->user();
|
||||
|
||||
$ticketNum = 'TKT-' . now()->format('Ymd') . '-' . strtoupper(Str::random(5));
|
||||
|
||||
$gallery = [];
|
||||
foreach ($request->file('images', []) as $image) {
|
||||
$path = $image->store('tickets', 'public');
|
||||
$gallery[] = Storage::disk('public')->url($path);
|
||||
}
|
||||
|
||||
$ticket = $this->ticketRepository->create([
|
||||
'uuid' => (string) Str::uuid(),
|
||||
'ticket_num' => $ticketNum,
|
||||
'subject' => $request->input('subject'),
|
||||
'description' => $request->input('description'),
|
||||
'status' => 'open',
|
||||
'room' => $request->input('room'),
|
||||
'data' => $request->input('data') ? json_encode($request->input('data')) : null,
|
||||
'gallery' => $gallery ?: null,
|
||||
'customer_id' => $customer->id,
|
||||
]);
|
||||
|
||||
TicketJobs::dispatch($ticket, $customer);
|
||||
Mail::to($customer->email)->queue(new TicketCreated($ticket, $customer));
|
||||
|
||||
$setting = Setting::where('key', 'setting')->first();
|
||||
$adminEmail = $setting ? (json_decode((string) $setting->value)?->admin_email ?? null) : null;
|
||||
if ($adminEmail) {
|
||||
Mail::to($adminEmail)->queue(new TicketAdminNotification($ticket, $customer));
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'error' => false,
|
||||
'message' => 'Ticket đã được tạo thành công.',
|
||||
@@ -115,7 +140,7 @@ class TicketController extends Controller
|
||||
public function reply(Request $request, string $ticket_num)
|
||||
{
|
||||
$request->validate([
|
||||
'message' => ['required', 'string', 'max:2000'],
|
||||
'message' => ['required', 'string'],
|
||||
]);
|
||||
|
||||
$customer = auth('client')->user();
|
||||
|
||||
@@ -20,8 +20,10 @@ use App\Services\ActivityLogger;
|
||||
use App\Services\Api\Order\OrderQueryService;
|
||||
use App\Services\Api\Order\OrderService;
|
||||
use App\Services\InvoicePaymentService;
|
||||
use App\Services\InvoicePdfService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class OrderControllerRefactor extends Controller
|
||||
{
|
||||
@@ -225,6 +227,34 @@ class OrderControllerRefactor extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
public function getClientOrderDetailByOrderNum(Request $request, string $orderNum): JsonResponse
|
||||
{
|
||||
$order = $this->orderQueryService->findCustomerInvoiceByOrderNum((int) $request->user()->id, $orderNum);
|
||||
|
||||
if (! $order) {
|
||||
return response()->json(['success' => false, 'message' => 'Đơn hàng không tồn tại.'], 404);
|
||||
}
|
||||
|
||||
return response()->json(['success' => true, 'data' => new OrderDetailResource($order)]);
|
||||
}
|
||||
|
||||
public function downloadInvoicePdfByOrderNum(Request $request, string $orderNum, InvoicePdfService $pdfService): Response
|
||||
{
|
||||
$order = $this->orderQueryService->findCustomerInvoiceByOrderNum((int) $request->user()->id, $orderNum);
|
||||
|
||||
if (! $order?->invoice) {
|
||||
abort(404, 'Hóa đơn không tồn tại.');
|
||||
}
|
||||
|
||||
$pdf = $pdfService->generate($order->invoice);
|
||||
$filename = $pdfService->generateFileName($order->invoice);
|
||||
|
||||
return response($pdf, 200, [
|
||||
'Content-Type' => 'application/pdf',
|
||||
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getClientServices(Request $request): JsonResponse
|
||||
{
|
||||
$services = $this->orderQueryService->getCustomerServices((int) $request->user()->id);
|
||||
|
||||
@@ -11,15 +11,18 @@ class TicketResource extends JsonResource
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'uuid' => $this->uuid,
|
||||
'ticket_num' => $this->ticket_num,
|
||||
'subject' => $this->subject,
|
||||
'description' => $this->description,
|
||||
'status' => $this->status,
|
||||
'room' => $this->room,
|
||||
'data' => $this->data ? (is_string($this->data) ? json_decode($this->data, true) : $this->data) : null,
|
||||
'gallery' => $this->gallery ?? [],
|
||||
'customer_id' => $this->customer_id,
|
||||
'customer' => $this->whenLoaded('customer', fn() => [
|
||||
'id' => $this->customer->id,
|
||||
'uuid' => $this->customer->uuid,
|
||||
'fullname' => $this->customer->fullname,
|
||||
'email' => $this->customer->email,
|
||||
]),
|
||||
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace App\Jobs\VirtualMachine;
|
||||
|
||||
use App\Http\Facade\SignRSA;
|
||||
use App\Mail\Customer\VmPasswordChanged;
|
||||
use App\Models\ModuleVmware;
|
||||
use App\Models\vCenter;
|
||||
use App\Services\VmProvisionService;
|
||||
use App\Services\VsphereHostSshService;
|
||||
@@ -14,6 +16,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
/**
|
||||
* ChangeVmPasswordJob
|
||||
@@ -71,6 +74,8 @@ class ChangeVmPasswordJob implements ShouldQueue
|
||||
// Persist encrypted password to DB
|
||||
$provision->updatePassword($this->moduleVmwareId, $this->newPassword);
|
||||
|
||||
$this->notifyCustomer();
|
||||
|
||||
Log::info('ChangeVmPasswordJob: password changed successfully', [
|
||||
'module_vmware_id' => $this->moduleVmwareId,
|
||||
'vm_id' => $this->vmId,
|
||||
@@ -86,6 +91,19 @@ class ChangeVmPasswordJob implements ShouldQueue
|
||||
]);
|
||||
}
|
||||
|
||||
private function notifyCustomer(): void
|
||||
{
|
||||
$vm = ModuleVmware::with('customer')->find($this->moduleVmwareId);
|
||||
if (!$vm?->customer?->email) {
|
||||
return;
|
||||
}
|
||||
|
||||
Mail::to($vm->customer->email)->queue(new VmPasswordChanged([
|
||||
'fullname' => $vm->customer->fullname,
|
||||
'vm_name' => $vm->name,
|
||||
]));
|
||||
}
|
||||
|
||||
private function assertSuccess(string $output): void
|
||||
{
|
||||
if (str_contains(strtoupper(trim($output)), 'SUCCESS')) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail\Admin;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class TicketAdminNotification extends Mailable implements ShouldQueue
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public function __construct(
|
||||
public readonly mixed $ticket,
|
||||
public readonly mixed $customer,
|
||||
) {}
|
||||
|
||||
public function build(): static
|
||||
{
|
||||
return $this
|
||||
->subject("[ADMIN] Ticket mới #{$this->ticket->ticket_num} — {$this->customer->email}")
|
||||
->view('emails.admin.ticket-notification')
|
||||
->with([
|
||||
'ticketNum' => $this->ticket->ticket_num,
|
||||
'subject' => $this->ticket->subject,
|
||||
'room' => $this->ticket->room,
|
||||
'description' => strip_tags($this->ticket->description),
|
||||
'customerName' => trim($this->customer->firstname . ' ' . $this->customer->lastname),
|
||||
'customerEmail' => $this->customer->email,
|
||||
'ticketUrl' => config('app.frontend_url') . '/tickets/' . $this->ticket->ticket_num,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail\Customer;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class VmPasswordChanged extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public function __construct(public array $data) {}
|
||||
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$appName = $this->payload()['company_name'] ?? config('app.name', 'The Gioi So');
|
||||
|
||||
return new Envelope(
|
||||
subject: 'Mật khẩu VM của bạn đã được thay đổi — ' . $appName,
|
||||
);
|
||||
}
|
||||
|
||||
public function content(): Content
|
||||
{
|
||||
$data = $this->payload();
|
||||
|
||||
return new Content(
|
||||
view: 'emails.customer.vm-password-changed',
|
||||
with: [
|
||||
'appName' => $data['company_name'] ?? config('app.name', 'The Gioi So'),
|
||||
'fullName' => $data['fullname'] ?? 'Quý khách',
|
||||
'vmName' => $data['vm_name'] ?? '',
|
||||
'loginUrl' => $data['login_url'] ?? rtrim(config('app.frontend_url', ''), '/') . '/dang-nhap',
|
||||
'supportEmail' => $data['support_email'] ?? config('mail.support_email', 'support@thegioiso.com'),
|
||||
'supportPhone' => $data['support_phone'] ?? config('mail.support_phone', '1900.xxxx'),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function payload(): array
|
||||
{
|
||||
try {
|
||||
$setting = Setting::where('key', 'setting')->first();
|
||||
$settingValue = $setting ? json_decode((string) $setting->value, true) : [];
|
||||
} catch (\Throwable) {
|
||||
$settingValue = [];
|
||||
}
|
||||
|
||||
return array_merge([
|
||||
'company_name' => $settingValue['company'] ?? config('app.name', 'The Gioi So'),
|
||||
'support_email' => $settingValue['email'] ?? config('mail.support_email', 'support@thegioiso.com'),
|
||||
'support_phone' => $settingValue['hotline'] ?? ($settingValue['phone'] ?? config('mail.support_phone', '1900.xxxx')),
|
||||
'login_url' => rtrim(config('app.frontend_url', ''), '/') . '/dang-nhap',
|
||||
], $this->data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class TicketCreated extends Mailable implements ShouldQueue
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public function __construct(
|
||||
public readonly mixed $ticket,
|
||||
public readonly mixed $customer,
|
||||
) {}
|
||||
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: "[#{$this->ticket->ticket_num}] Yêu cầu hỗ trợ của bạn đã được tiếp nhận",
|
||||
);
|
||||
}
|
||||
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
view: 'emails.customer.ticket-created',
|
||||
with: [
|
||||
'fullName' => trim("{$this->customer->firstname} {$this->customer->lastname}") ?: 'Quý khách',
|
||||
'ticketNum' => $this->ticket->ticket_num,
|
||||
'subject' => $this->ticket->subject,
|
||||
'room' => $this->ticket->room,
|
||||
'ticketUrl' => rtrim(config('app.frontend_url', ''), '/') . '/tickets/' . $this->ticket->ticket_num,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ class Ticket extends Model
|
||||
protected $table = 'ticket';
|
||||
|
||||
protected $fillable = [
|
||||
'uuid',
|
||||
'ticket_num',
|
||||
'subject',
|
||||
'customer_id',
|
||||
@@ -20,6 +21,11 @@ class Ticket extends Model
|
||||
'status',
|
||||
'room',
|
||||
'data',
|
||||
'gallery',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'gallery' => 'array',
|
||||
];
|
||||
|
||||
public function customer()
|
||||
|
||||
@@ -25,8 +25,6 @@ class VirtualMachines extends Model
|
||||
'host_id',
|
||||
'ip_pool_id',
|
||||
'ip',
|
||||
'username',
|
||||
'password',
|
||||
];
|
||||
|
||||
public function datacenter()
|
||||
@@ -43,4 +41,9 @@ class VirtualMachines extends Model
|
||||
{
|
||||
return $this->belongsTo(IpPool::class, 'ip_pool_id');
|
||||
}
|
||||
|
||||
public function vmware()
|
||||
{
|
||||
return $this->hasOne(ModuleVmware::class, 'vmware_id', 'vm_id');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user