feat(vm): hoàn tiền khi cấp VM lỗi, theo dõi job queue, uuid dịch vụ cho ticket, chuẩn hoá RAM addon

- VmProvisionException/VmProvisionRefund xử lý lỗi cấp phát VM
- QueueJobInspector + QueueMonitorController xem chi tiết job
- Thêm service uuid cho ticket, thời lượng cho activity_logs
- Migration chuẩn hoá RAM addon sang MB, trạng thái deleting cho snapshot

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
baodev0
2026-10-30 11:54:05 +07:00
co-authored by Claude Sonnet 5.5
parent 7136bec265
commit 62596d9063
32 changed files with 514 additions and 107 deletions
@@ -18,6 +18,11 @@ use Illuminate\Support\Facades\Log;
*/
class VmIpReservationService
{
public function hasFreeIp(): bool
{
return $this->availableIps()->exists();
}
/**
* @return array{reservation:ModuleIpAddress,ip_pool_id:int,gateway:string,cidr:int}|null
*/
@@ -25,8 +30,7 @@ class VmIpReservationService
{
return DB::transaction(function () use ($networkId, $hostId, $dns) {
// ip_pools.status is the single source of truth for availability.
$ipPool = IpPool::where('module_network_id', $networkId)
->whereIn('status', ['free', 'reserved'])
$ipPool = $this->availableIps()
->lockForUpdate()
->inRandomOrder()
->first();
@@ -44,7 +48,6 @@ class VmIpReservationService
// module_ip_address mirrors ip_pools — update the existing synced row
// (created when the IP was added to ip_pools) instead of inserting a new one.
$reservation = ModuleIpAddress::where('ip', $ipPool->ip_address)
->where('module_network_id', $networkId)
->lockForUpdate()
->first();
@@ -101,6 +104,13 @@ class VmIpReservationService
});
}
private function availableIps()
{
return IpPool::whereIn('status', ['free', 'reserved'])
->whereNotNull('gateway')
->where('gateway', '!=', '');
}
public function freeIpsInNetwork(int $networkId)
{
return IpPool::where('module_network_id', $networkId)