Files
portal-api/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerServices.php
T
baodev0andClaude Opus 5.5 30898ea46f feat: email gửi tay/nhắc gia hạn, gói hosting, sửa dữ liệu sản phẩm
- 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>
2026-10-09 11:32:05 +07:00

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]);
}
}