update vm ticket

This commit is contained in:
baodev0
2026-07-14 17:03:38 +07:00
parent f035730a44
commit 401dcc8783
24 changed files with 950 additions and 31 deletions
@@ -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);