- Gửi email theo template cho 1 khách, tab lịch sử email, nhắc gia hạn dịch vụ hàng loạt - Mail lưu from/bcc/sent_by; mẫu MARKETING và mẫu cũ trong TemplateSeeder - Gói hosting: giới hạn, trạng thái, lấy plan Plesk qua CLI gateway - Backfill payment_method và ngày đơn hàng còn thiếu - Bổ sung field CPU/RAM/Disk/Băng thông cho loại sản phẩm vps và cloud Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
132 lines
5.9 KiB
PHP
132 lines
5.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\Admin\Customer\Concerns;
|
|
|
|
use App\Models\Service;
|
|
use App\Support\AutoEmailGate;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
|
|
trait ManagesCustomerServices
|
|
{
|
|
public function getServices(string $uuid): JsonResponse
|
|
{
|
|
return response()->json(['error' => false, 'data' => $this->serviceProvisioning->getServices($uuid)]);
|
|
}
|
|
|
|
public function bulkDeleteServices(string $uuid, Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate(['ids' => 'required|array|min:1', 'ids.*' => 'integer']);
|
|
|
|
$deleted = $this->serviceProvisioning->bulkDeleteServices($uuid, $data['ids']);
|
|
|
|
return response()->json(['error' => false, 'message' => "Đã xoá {$deleted} dịch vụ", 'data' => ['deleted' => $deleted]]);
|
|
}
|
|
|
|
public function updateService(string $uuid, int $serviceId, Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'server_name' => 'nullable|string|max:255',
|
|
'status' => 'nullable|in:pending,active,suspended,expired,cancelled,terminated',
|
|
'auto_renew' => 'nullable|boolean',
|
|
'started_at' => 'nullable|date',
|
|
'expires_at' => 'nullable|date',
|
|
'note' => 'nullable|string',
|
|
'requirements' => 'nullable|string',
|
|
'feedback' => 'nullable|string',
|
|
'price' => 'nullable|numeric|min:0',
|
|
'billing_cycle' => 'nullable|in:monthly,quarterly,semi_annually,annually,biennially,triennially',
|
|
'virtual_machine_id' => 'nullable|integer|exists:virtual_machines,id',
|
|
'os_id' => 'nullable|integer|exists:os,id',
|
|
'vm_cpu' => 'nullable|integer|min:1',
|
|
'vm_ram_mb' => 'nullable|integer|min:1',
|
|
'vm_disk_gb' => 'nullable|integer|min:1',
|
|
'vm_ip' => 'nullable|string|max:45',
|
|
'vm_dns' => 'nullable|string|max:100',
|
|
'vm_user' => 'nullable|string|max:255',
|
|
'vm_password' => 'nullable|string|max:255',
|
|
'vm_port' => 'nullable|integer|min:1|max:65535',
|
|
]);
|
|
|
|
$service = $this->serviceProvisioning->updateService($uuid, $serviceId, $data);
|
|
|
|
return response()->json(['error' => false, 'message' => 'Cập nhật dịch vụ thành công', 'data' => $service]);
|
|
}
|
|
|
|
public function changeServiceIp(string $uuid, int $serviceId, Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate(['ip' => 'required|ip']);
|
|
|
|
abort_if(!Service::where('id', $serviceId)->value('vm_id'), 422, 'Dịch vụ chưa gắn VM.');
|
|
|
|
$service = $this->serviceProvisioning->updateService($uuid, $serviceId, ['vm_ip' => $data['ip']]);
|
|
|
|
return response()->json(['error' => false, 'message' => 'Đã đổi IP thành công', 'data' => $service]);
|
|
}
|
|
|
|
public function changeServicePort(string $uuid, int $serviceId, Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate(['port' => 'required|integer|min:1|max:65535']);
|
|
|
|
abort_if(!Service::where('id', $serviceId)->value('vm_id'), 422, 'Dịch vụ chưa gắn VM.');
|
|
|
|
$service = $this->serviceProvisioning->updateService($uuid, $serviceId, ['vm_port' => $data['port']]);
|
|
|
|
return response()->json(['error' => false, 'message' => 'Đã đổi port thành công', 'data' => $service]);
|
|
}
|
|
|
|
public function notifyServiceRenewal(string $uuid, int $serviceId): JsonResponse
|
|
{
|
|
AutoEmailGate::markManual();
|
|
$this->serviceProvisioning->notifyRenewal($uuid, $serviceId);
|
|
|
|
return response()->json(['error' => false, 'message' => 'Đã gửi thông báo gia hạn đến khách hàng.']);
|
|
}
|
|
|
|
public function createService(string $uuid, Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'product_id' => 'required|integer|exists:products,id',
|
|
'virtual_machine_id' => 'nullable|integer|exists:virtual_machines,id',
|
|
'server_name' => 'nullable|string|max:255',
|
|
'status' => 'nullable|in:pending,active,suspended,expired,cancelled,terminated',
|
|
'auto_renew' => 'nullable|boolean',
|
|
'started_at' => 'nullable|date',
|
|
'expires_at' => 'nullable|date',
|
|
'note' => 'nullable|string',
|
|
'billing_cycle' => 'nullable|in:monthly,quarterly,semi_annually,annually,biennially,triennially',
|
|
'payment_method' => 'required|in:transfer,balance',
|
|
'amount_paid' => 'nullable|numeric|min:0',
|
|
'proof_images' => 'nullable|array',
|
|
'proof_images.*' => 'file|image|max:5120',
|
|
'os_id' => 'nullable|integer|exists:os,id',
|
|
'vm_cpu' => 'nullable|integer|min:1',
|
|
'vm_ram_mb' => 'nullable|integer|min:1',
|
|
'vm_disk_gb' => 'nullable|integer|min:1',
|
|
'vm_ip' => 'nullable|string|max:45',
|
|
'vm_dns' => 'nullable|string|max:100',
|
|
'vm_user' => 'nullable|string|max:255',
|
|
'vm_password' => 'nullable|string|max:255',
|
|
'vm_port' => 'nullable|integer|min:1|max:65535',
|
|
]);
|
|
|
|
// Upload proof images (chuyển khoản) — I/O ở tầng HTTP, service chỉ nhận đường dẫn đã lưu.
|
|
if ($data['payment_method'] === 'transfer' && $request->hasFile('proof_images')) {
|
|
$data['_proof_paths'] = collect($request->file('proof_images'))
|
|
->map(fn($file) => $file->store('payment-proofs', 'public'))
|
|
->all();
|
|
}
|
|
|
|
$service = $this->serviceProvisioning->createService($uuid, $data, $request->ip());
|
|
|
|
return response()->json(['error' => false, 'message' => 'Service created', 'data' => $service], 201);
|
|
}
|
|
|
|
public function listAvailableVms(Request $request): JsonResponse
|
|
{
|
|
$vms = $this->serviceProvisioning->listAvailableVms(trim((string) $request->get('q', '')));
|
|
|
|
return response()->json(['error' => false, 'data' => $vms]);
|
|
}
|
|
}
|