From 0e5832b1fb5a3a724fa17128c3c1286085a35d29 Mon Sep 17 00:00:00 2001 From: pbao9 Date: Fri, 13 Mar 2026 16:28:23 +0700 Subject: [PATCH] feat: Implement product duplication, bulk product status updates and deletion, add client service console ticket retrieval, and update service resource to expose `server_name`. --- .../Api/Admin/Product/ProductController.php | 59 +++++++++++++++++++ .../Api/Client/Service/ServiceController.php | 57 ++++++++++++++++++ .../Client/Service/ServiceClientResource.php | 2 +- routes/api.php | 2 + routes/client.php | 1 + 5 files changed, 120 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/Admin/Product/ProductController.php b/app/Http/Controllers/Api/Admin/Product/ProductController.php index 986d7f5..4e2119d 100644 --- a/app/Http/Controllers/Api/Admin/Product/ProductController.php +++ b/app/Http/Controllers/Api/Admin/Product/ProductController.php @@ -161,6 +161,65 @@ class ProductController extends Controller ]); } + public function duplicate(int $id): JsonResponse + { + $product = Product::with('pricings')->find($id); + + if (!$product) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy sản phẩm.'], 404); + } + + $copy = $product->replicate(['uuid', 'slug']); + $copy->name = $product->name . ' (Copy)'; + $copy->slug = \Illuminate\Support\Str::slug($copy->name) . '-' . time(); + $copy->is_active = false; + $copy->save(); + + // Duplicate pricings + foreach ($product->pricings as $pricing) { + $copy->pricings()->create($pricing->only([ + 'billing_cycle', 'price', 'renewal_price', 'setup_fee', + ])); + } + + return response()->json([ + 'error' => false, + 'message' => 'Nhân bản sản phẩm thành công.', + 'data' => new ProductResource($copy), + ], 201); + } + + public function bulkUpdateStatus(Request $request): JsonResponse + { + $validated = $request->validate([ + 'ids' => 'required|array', + 'ids.*' => 'integer', + 'is_active' => 'required|boolean', + ]); + + $updated = Product::whereIn('id', $validated['ids']) + ->update(['is_active' => $validated['is_active']]); + + $label = $validated['is_active'] ? 'kích hoạt' : 'ẩn'; + + return response()->json([ + 'error' => false, + 'message' => "Đã {$label} {$updated} sản phẩm.", + ]); + } + + public function bulkDestroy(Request $request): JsonResponse + { + $ids = $request->validate(['ids' => 'required|array', 'ids.*' => 'integer'])['ids']; + + $deleted = Product::whereIn('id', $ids)->delete(); + + return response()->json([ + 'error' => false, + 'message' => "Đã xoá {$deleted} sản phẩm.", + ]); + } + public function getOs(int $id): JsonResponse { $product = Product::find($id); diff --git a/app/Http/Controllers/Api/Client/Service/ServiceController.php b/app/Http/Controllers/Api/Client/Service/ServiceController.php index bf339de..9012ae7 100644 --- a/app/Http/Controllers/Api/Client/Service/ServiceController.php +++ b/app/Http/Controllers/Api/Client/Service/ServiceController.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers\Api\Client\Service; use App\Http\Controllers\Controller; use App\Http\Resources\Api\Client\Service\ServiceClientDetailResource; use \App\Http\Resources\Api\Client\Service\ServiceClientResource; +use App\Http\Services\vCenter; +use App\Models\Service; use App\Repositories\Api\ServiceClient\ServiceClientRepository; use App\Services\Api\ServiceClient\ServiceClientService; use Illuminate\Http\Request; @@ -46,4 +48,59 @@ class ServiceController extends Controller ]); } + public function getConsoleTicket(Request $request, string $uuid, vCenter $serviceVCenter) + { + $customer = $request->user(); + + $service = Service::with(['orderItem.order.vmWare.vCenter']) + ->where('uuid', $uuid) + ->where('customer_id', $customer->id) + ->first(); + + if (!$service) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy dịch vụ'], 404); + } + + $vm = $service->orderItem?->order?->vmWare?->first(); + + if (!$vm) { + return response()->json(['error' => true, 'message' => 'Dịch vụ chưa được cấp phát VM'], 404); + } + + $vcenter = $vm->vCenter; + + if (!$vcenter) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy thông tin vCenter'], 404); + } + + if (!$serviceVCenter->loginServer($vcenter)) { + return response()->json(['error' => true, 'message' => 'Không thể đăng nhập vào vCenter']); + } + + $consoleTicket = $serviceVCenter->GetConsoleTickets($vm->vmware_id); + + if ($consoleTicket === 'Login_Again') { + if ($serviceVCenter->loginServer($vcenter)) { + $consoleTicket = $serviceVCenter->GetConsoleTickets($vm->vmware_id); + } else { + return response()->json(['error' => true, 'message' => 'Không thể đăng nhập lại vào vCenter']); + } + } + + if (is_string($consoleTicket)) { + return response()->json(['error' => true, 'message' => "Failed to get console ticket: $consoleTicket"]); + } + + if ($consoleTicket['error']) { + return response()->json(['error' => true, 'message' => $consoleTicket['data']]); + } + + $ticket = basename(parse_url((string) $consoleTicket['data'], PHP_URL_PATH)); + + return response()->json([ + 'error' => false, + 'message' => $ticket, + ]); + } + } \ No newline at end of file diff --git a/app/Http/Resources/Api/Client/Service/ServiceClientResource.php b/app/Http/Resources/Api/Client/Service/ServiceClientResource.php index 9bccd88..f9d2a43 100644 --- a/app/Http/Resources/Api/Client/Service/ServiceClientResource.php +++ b/app/Http/Resources/Api/Client/Service/ServiceClientResource.php @@ -20,7 +20,7 @@ class ServiceClientResource extends JsonResource return [ 'uuid' => $this->uuid, - 'vm_id' => $this->vm_id, + 'server_name' => $this->server_name, 'status' => $this->status, 'auto_renew' => $this->auto_renew, 'started_at' => $this->started_at?->toISOString(), diff --git a/routes/api.php b/routes/api.php index edab06b..1b73e4d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -97,6 +97,8 @@ Route::prefix('v1') Route::get('/', 'index')->middleware('perm:product-service.view'); Route::post('/', 'store')->middleware('perm:product-service.create'); Route::post('import', 'import')->middleware('perm:product-service.create'); + Route::post('bulk-update-status', 'bulkUpdateStatus')->middleware('perm:product-service.edit'); + Route::post('bulk-destroy', 'bulkDestroy')->middleware('perm:product-service.destroy'); Route::controller(ProductController::class)->group(function () { Route::get('os', 'getOsList')->middleware('perm:product-service.view'); diff --git a/routes/client.php b/routes/client.php index c34dddd..6373f94 100644 --- a/routes/client.php +++ b/routes/client.php @@ -39,6 +39,7 @@ Route::prefix('v1')->as('api.')->group(function () { Route::middleware(['api.auth'])->prefix('client-service')->controller(ServiceClientController::class)->as('client-service.')->group(function () { Route::get('/', 'getService')->name('list'); Route::get('{uuid}', 'getDetail')->name('detail'); + Route::get('{uuid}/console-ticket', 'getConsoleTicket')->name('console-ticket'); });