201 lines
8.2 KiB
PHP
201 lines
8.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Api\Order;
|
|
|
|
use App\Repositories\Api\Service\ServiceRepository;
|
|
use App\Repositories\Api\Invoice\InvoiceRepository;
|
|
use App\Repositories\Api\Order\OrderRepository;
|
|
use Illuminate\Support\Facades\DB;
|
|
use App\Enums\Order\OrderStatus;
|
|
use Exception;
|
|
use App\Jobs\VirtualMachine\Create\CreateVMJobs;
|
|
use App\Models\Log as CustomerLog;
|
|
use App\Models\ModuleIpAddress;
|
|
use App\Models\ModuleVmware;
|
|
use App\Models\Os;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class OrderService
|
|
{
|
|
public function __construct(
|
|
protected OrderRepository $orderRepo,
|
|
protected ServiceRepository $productRepo,
|
|
protected InvoiceRepository $invoiceRepo
|
|
) {}
|
|
|
|
public function create(array $data, $customer)
|
|
{
|
|
// Check for existing pending orders
|
|
if ($this->orderRepo->hasPendingOrder($customer->id)) {
|
|
throw new Exception('Bạn có đơn hàng chưa thanh toán. Vui lòng thanh toán trước khi tạo đơn mới.');
|
|
}
|
|
|
|
$product = $this->productRepo->find($data['product_id']);
|
|
|
|
if (!$product) {
|
|
throw new Exception('Sản phẩm không tồn tại');
|
|
}
|
|
|
|
$price = $this->productRepo->getPricing($product->id, $data['billing_cycle']);
|
|
|
|
if (is_null($price)) {
|
|
throw new Exception('Không tìm thấy giá cho sản phẩm này');
|
|
}
|
|
|
|
// Tính tổng tiền bao gồm addons
|
|
$addonsPrice = 0;
|
|
if (!empty($data['addons'])) {
|
|
foreach ($data['addons'] as $addonData) {
|
|
$addon = \App\Models\Addon::find($addonData['addon_id']);
|
|
if ($addon) {
|
|
$addonsPrice += $addon->price * ($addonData['qty'] ?? 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
$totalPrice = $price + $addonsPrice;
|
|
|
|
// Kiểm tra số dư tài khoản (cột total) trước khi tạo đơn
|
|
if ($customer->total < $totalPrice) {
|
|
throw new Exception('Số dư không đủ để đặt hàng. Vui lòng nạp thêm tiền. Số dư hiện tại: ' . number_format($customer->total) . ' VNĐ, cần: ' . number_format($totalPrice) . ' VNĐ');
|
|
}
|
|
|
|
return DB::transaction(function () use ($data, $customer, $product, $price, $totalPrice) {
|
|
|
|
$order = $this->orderRepo->create([
|
|
'order_num' => \App\Helpers\OrderHelper::generateOrderNumber(),
|
|
'customer_id' => $customer->id,
|
|
'amount' => $price,
|
|
'status' => OrderStatus::PENDING->value
|
|
]);
|
|
|
|
$this->orderRepo->createItem($order->id, [
|
|
'product_id' => $product->id,
|
|
'billing_cycle' => $data['billing_cycle'],
|
|
'server_name' => $data['server_name'] ?? null,
|
|
'os_id' => $data['os_id'] ?? null,
|
|
'price' => $price,
|
|
'addons' => $data['addons'] ?? []
|
|
]);
|
|
|
|
$invoice = $this->invoiceRepo->createFromOrder($order);
|
|
|
|
return [
|
|
'order' => $order,
|
|
'invoice' => $invoice
|
|
];
|
|
});
|
|
}
|
|
|
|
public function pay($order, string $method)
|
|
{
|
|
if (in_array($order->status, [OrderStatus::ACTIVE->value, OrderStatus::PROCESSING->value], true)) {
|
|
throw new Exception('Đơn hàng đã được thanh toán hoặc đang xử lý.');
|
|
}
|
|
|
|
return DB::transaction(function () use ($order, $method) {
|
|
$customer = $order->customer;
|
|
|
|
// Kiểm tra số dư và trừ tiền
|
|
if ($customer->total < $order->amount) {
|
|
throw new Exception('Số dư không đủ để thanh toán. Vui lòng nạp thêm tiền. Số dư hiện tại: ' . number_format($customer->total) . ' VNĐ, cần: ' . number_format($order->amount) . ' VNĐ');
|
|
}
|
|
|
|
$customer->total -= $order->amount;
|
|
$customer->save();
|
|
|
|
CustomerLog::create([
|
|
'agent' => 'api',
|
|
'client_ip' => request()?->ip(),
|
|
'customer_id' => $customer->id,
|
|
'description' => 'Thanh toán đơn hàng #' . $order->order_num
|
|
. ' thành công. Số tiền: ' . number_format((int) $order->amount) . ' VNĐ'
|
|
. '. Số dư sau thanh toán: ' . number_format((int) $customer->total) . ' VNĐ',
|
|
]);
|
|
|
|
// Cho phép thanh toán lại đơn đã hủy bằng cách đưa về pending trước khi process.
|
|
if ($order->status === OrderStatus::CANCELLED->value) {
|
|
$order->status = OrderStatus::PENDING->value;
|
|
$order->save();
|
|
}
|
|
|
|
$processedOrder = $this->orderRepo->processPayment($order, $method);
|
|
|
|
// Sau khi thanh toán, tạo module_vmware rồi dispatch CreateVMJobs.
|
|
foreach ($order->orderItems as $item) {
|
|
if (empty($item->product_id) || empty($item->os_id)) {
|
|
Log::warning('Skip VM job: product_id/os_id is missing', [
|
|
'order_id' => $order->id,
|
|
'order_item_id' => $item->id,
|
|
'product_id' => $item->product_id,
|
|
'os_id' => $item->os_id,
|
|
]);
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
$os = Os::with('osGroup.host')->find($item->os_id);
|
|
|
|
if (!$os || !$os->osGroup || !$os->osGroup->module_host_id) {
|
|
Log::warning('Skip VM job: không tìm thấy OS/OsGroup/Host', [
|
|
'order_id' => $order->id,
|
|
'order_item_id' => $item->id,
|
|
'os_id' => $item->os_id,
|
|
]);
|
|
continue;
|
|
}
|
|
|
|
$ip = ModuleIpAddress::where('module_host_id', $os->osGroup->module_host_id)
|
|
->where('status', 0)
|
|
->lockForUpdate()
|
|
->first();
|
|
|
|
if (!$ip) {
|
|
Log::warning('Skip VM job: không còn IP khả dụng', [
|
|
'order_id' => $order->id,
|
|
'module_host_id' => $os->osGroup->module_host_id,
|
|
]);
|
|
continue;
|
|
}
|
|
|
|
$ip->update(['status' => 1, 'vm_name' => $item->server_name ?? 'VM-' . $order->id]);
|
|
|
|
$moduleVmware = ModuleVmware::create([
|
|
'name' => $item->server_name ?? 'VM-' . $order->id,
|
|
'module_ip_address_id' => $ip->id,
|
|
'port' => 22,
|
|
'order_id' => $order->id,
|
|
'first_payment' => $order->amount,
|
|
'next_payment' => $order->amount,
|
|
'next_due_date' => null,
|
|
'next_invoice' => 0,
|
|
'user' => 'Administrator',
|
|
'password' => '',
|
|
'hostname' => $item->server_name ?? 'vm-' . $order->id,
|
|
'vmware_id' => null,
|
|
'os_id' => $item->os_id,
|
|
'customer_id' => $order->customer_id,
|
|
'status' => 0,
|
|
]);
|
|
|
|
dispatch(new CreateVMJobs($moduleVmware->id))->delay(now()->addSeconds(10));
|
|
|
|
Log::info('Dispatched CreateVMJobs', [
|
|
'order_id' => $order->id,
|
|
'module_vmware_id' => $moduleVmware->id,
|
|
'ip' => $ip->ip,
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error('Lỗi khi tạo module_vmware hoặc dispatch job', [
|
|
'order_id' => $order->id,
|
|
'order_item_id' => $item->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $processedOrder;
|
|
});
|
|
}
|
|
}
|