update logic
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Api\Order;
|
||||
|
||||
use App\DTOs\Order\ClientOrderFilters;
|
||||
use App\Enums\Invoice\InvoiceStatus;
|
||||
use App\Enums\Order\OrderStatus;
|
||||
use App\Helpers\OrderHelper;
|
||||
use App\Jobs\VirtualMachine\Action\VMJobs;
|
||||
use App\Jobs\VirtualMachine\Provision\RetryProvisioningOrchestrator;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\ModuleVmware;
|
||||
use App\Models\Order;
|
||||
use App\Models\OrderItem;
|
||||
use App\Models\Service;
|
||||
use App\Models\VirtualMachines;
|
||||
use App\Repositories\Api\Order\OrderRepository;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class OrderQueryService
|
||||
{
|
||||
public function __construct(private readonly OrderRepository $orderRepository)
|
||||
{
|
||||
}
|
||||
|
||||
public function getOrdersForCustomer(int $customerId, ClientOrderFilters $filters): LengthAwarePaginator
|
||||
{
|
||||
return $this->orderRepository->getOrders($customerId, $filters->toArray());
|
||||
}
|
||||
|
||||
public function checkServerName(string $name): array
|
||||
{
|
||||
$exists = OrderItem::where('server_name', $name)->exists()
|
||||
|| Service::where('server_name', $name)->exists()
|
||||
|| ModuleVmware::where('name', $name)->exists()
|
||||
|| ModuleVmware::where('hostname', $name)->exists()
|
||||
|| VirtualMachines::where('name', $name)->exists();
|
||||
|
||||
return [
|
||||
'available' => ! $exists,
|
||||
'message' => $exists
|
||||
? 'Tên server này đã được sử dụng, vui lòng chọn tên khác.'
|
||||
: null,
|
||||
];
|
||||
}
|
||||
|
||||
public function findCustomerOrder(int $customerId, int $id): ?Order
|
||||
{
|
||||
return Order::query()
|
||||
->with(['invoice', 'orderItems.product', 'orderItems.service', 'customer'])
|
||||
->where('customer_id', $customerId)
|
||||
->where('id', $id)
|
||||
->first();
|
||||
}
|
||||
|
||||
public function getCustomerInvoices(int $customerId)
|
||||
{
|
||||
return Invoice::query()
|
||||
->with(['order.orderItems.product'])
|
||||
->where('customer_id', $customerId)
|
||||
->orderByDesc('created_at')
|
||||
->get();
|
||||
}
|
||||
|
||||
public function findCustomerInvoice(int $customerId, int $id): ?Invoice
|
||||
{
|
||||
return Invoice::query()
|
||||
->with([
|
||||
'order.orderItems.product',
|
||||
'order.vmWare.ipAddress',
|
||||
'order.vmWare.os',
|
||||
'customer',
|
||||
'attachments',
|
||||
])
|
||||
->where('customer_id', $customerId)
|
||||
->where('id', $id)
|
||||
->first();
|
||||
}
|
||||
|
||||
public function findCustomerInvoiceByOrderNum(int $customerId, string $orderNumber): ?Order
|
||||
{
|
||||
return Order::query()
|
||||
->with([
|
||||
'invoice.invoiceItems',
|
||||
'invoice.attachments',
|
||||
'invoice.customer',
|
||||
'orderItems.product',
|
||||
'customer',
|
||||
'vmWare.ipAddress',
|
||||
'vmWare.os',
|
||||
])
|
||||
->where('customer_id', $customerId)
|
||||
->where('order_num', $orderNumber)
|
||||
->first();
|
||||
}
|
||||
|
||||
public function getCustomerServices(int $customerId)
|
||||
{
|
||||
return Service::query()
|
||||
->with(['product', 'addons', 'invoice', 'orderItem', 'vmware.ipAddress', 'vmware.os'])
|
||||
->where('customer_id', $customerId)
|
||||
->orderByDesc('created_at')
|
||||
->get();
|
||||
}
|
||||
|
||||
public function findCustomerService(int $customerId, string $uuid): ?Service
|
||||
{
|
||||
return Service::query()
|
||||
->with(['product', 'addons', 'invoice', 'orderItem', 'vmware.ipAddress', 'vmware.os', 'vmware.vCenter'])
|
||||
->where('uuid', $uuid)
|
||||
->where('customer_id', $customerId)
|
||||
->first();
|
||||
}
|
||||
|
||||
public function canRetryProvisioning(Service $service): bool
|
||||
{
|
||||
if ($service->status !== 'pending' || $service->retry_requested_at) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$paidAt = $service->invoice?->paid_at;
|
||||
|
||||
return $paidAt ? Carbon::parse($paidAt)->diffInMinutes(now()) >= 180 : false;
|
||||
}
|
||||
|
||||
public function retryProvisioning(Order $order, int $customerId): array
|
||||
{
|
||||
$order = Order::where('id', $order->id)
|
||||
->with(['invoice', 'orderItems.service'])
|
||||
->where('customer_id', $customerId)
|
||||
->first();
|
||||
|
||||
if (! $order) {
|
||||
return ['ok' => false, 'message' => 'Đơn hàng không tồn tại.', 'code' => 404];
|
||||
}
|
||||
|
||||
if (! $order->invoice || (int) $order->invoice->status !== InvoiceStatus::PAID->value) {
|
||||
return ['ok' => false, 'message' => 'Đơn hàng chưa được thanh toán.', 'code' => 400];
|
||||
}
|
||||
|
||||
$paidAt = $order->invoice->paid_at ? Carbon::parse($order->invoice->paid_at) : null;
|
||||
if (! $paidAt || $paidAt->diffInMinutes(now()) < 180) {
|
||||
return ['ok' => false, 'message' => 'Vui lòng chờ khoảng 3 tiếng sau khi thanh toán mới có thể khởi tạo lại dịch vụ.', 'code' => 400];
|
||||
}
|
||||
|
||||
$service = $order->orderItems->first()?->service;
|
||||
if (! $service || (int) $service->customer_id !== $customerId) {
|
||||
return ['ok' => false, 'message' => 'Không tìm thấy dịch vụ hợp lệ để khởi tạo lại.', 'code' => 400];
|
||||
}
|
||||
|
||||
if ($service->status !== 'pending') {
|
||||
return ['ok' => false, 'message' => 'Dịch vụ đã được khởi tạo hoặc không ở trạng thái chờ.', 'code' => 400];
|
||||
}
|
||||
|
||||
if ($service->retry_requested_at) {
|
||||
return ['ok' => false, 'message' => 'Dịch vụ đã được yêu cầu khởi tạo lại một lần. Vui lòng liên hệ kỹ thuật qua mailto:kythuat@tgs.com.vn', 'code' => 400];
|
||||
}
|
||||
|
||||
$service->update(['retry_requested_at' => now()]);
|
||||
RetryProvisioningOrchestrator::dispatch($order->id, $customerId);
|
||||
|
||||
return ['ok' => true, 'message' => 'Đã yêu cầu khởi tạo lại dịch vụ. Hệ thống sẽ kiểm tra và xử lý trong vài phút.'];
|
||||
}
|
||||
|
||||
public function renewService(int $serviceId, int $customerId, int $months): array
|
||||
{
|
||||
$customer = \App\Models\Customer::find($customerId);
|
||||
|
||||
$service = Service::query()
|
||||
->with(['orderItem.product'])
|
||||
->where('id', $serviceId)
|
||||
->where('customer_id', $customerId)
|
||||
->whereNotIn('status', ['terminated', 'cancelled'])
|
||||
->first();
|
||||
|
||||
if (! $service) {
|
||||
return ['ok' => false, 'message' => 'Không tìm thấy dịch vụ hoặc dịch vụ đã bị hủy.', 'code' => 404];
|
||||
}
|
||||
|
||||
$orderItem = $service->orderItem;
|
||||
if (! $orderItem || (int) $orderItem->price <= 0) {
|
||||
return ['ok' => false, 'message' => 'Không thể xác định giá gia hạn dịch vụ.', 'code' => 400];
|
||||
}
|
||||
|
||||
$price = (int) $orderItem->price * $months;
|
||||
|
||||
if ($customer->total < $price) {
|
||||
return ['ok' => false, 'message' => sprintf('Số dư không đủ. Hiện có: %s VNĐ, cần: %s VNĐ.', number_format($customer->total), number_format($price)), 'code' => 400];
|
||||
}
|
||||
|
||||
$needsVmStart = $service->vm_id !== null;
|
||||
|
||||
$invoice = DB::transaction(function () use ($customer, $service, $orderItem, $price, $months) {
|
||||
$customer->total -= $price;
|
||||
$customer->save();
|
||||
|
||||
\App\Models\Log::create([
|
||||
'agent' => 'api',
|
||||
'client_ip' => request()?->ip(),
|
||||
'customer_id' => $customer->id,
|
||||
'description' => sprintf('Gia hạn dịch vụ #%s. Số tiền: %s VNĐ. Số dư còn: %s VNĐ.', $service->server_name ?? $service->id, number_format($price), number_format($customer->total)),
|
||||
]);
|
||||
|
||||
$invoice = Invoice::create([
|
||||
'invoice_number' => OrderHelper::generateOrderNumber('INV'),
|
||||
'customer_id' => $customer->id,
|
||||
'order_id' => null,
|
||||
'invoice_type' => 2,
|
||||
'sub_total' => $price,
|
||||
'discount_amount' => 0,
|
||||
'tax' => 0,
|
||||
'total' => $price,
|
||||
'status' => InvoiceStatus::PAID->value,
|
||||
'paid_at' => now(),
|
||||
'payment_method' => 'credit',
|
||||
]);
|
||||
|
||||
\App\Models\InvoiceItem::create([
|
||||
'invoice_id' => $invoice->id,
|
||||
'product_id' => $service->product_id,
|
||||
'type' => 'renewal',
|
||||
'description' => 'Gia hạn dịch vụ: ' . ($orderItem->product?->name ?? $service->server_name ?? ''),
|
||||
'months' => $months,
|
||||
'quantity' => 1,
|
||||
'unit_price' => $price,
|
||||
'total_price' => $price,
|
||||
]);
|
||||
|
||||
$baseDate = $service->expires_at && $service->expires_at->isFuture() ? $service->expires_at : now();
|
||||
$expiresAt = Carbon::parse($baseDate)->addMonths($months);
|
||||
|
||||
$service->update([
|
||||
'status' => 'active',
|
||||
'expires_at' => $expiresAt,
|
||||
'suspended_at' => null,
|
||||
'invoice_id' => $invoice->id,
|
||||
]);
|
||||
|
||||
return $invoice;
|
||||
});
|
||||
|
||||
if ($needsVmStart) {
|
||||
dispatch(new VMJobs((int) $service->vm_id, 'Start', false))->delay(now()->addSeconds(5));
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'message' => 'Gia hạn thành công. Dịch vụ đã được kích hoạt lại.',
|
||||
'data' => [
|
||||
'invoice_number' => $invoice->invoice_number,
|
||||
'total' => (float) $invoice->total,
|
||||
'expires_at' => $service->fresh()?->expires_at?->toISOString(),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user