362 lines
15 KiB
PHP
362 lines
15 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\Service;
|
|
|
|
use App\DTOs\Order\ClientOrderFilters;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\Api\Order\CheckServerNameRequest;
|
|
use App\Http\Requests\Api\Order\PayOrderRequest;
|
|
use App\Http\Requests\Api\Order\RenewServiceRequest;
|
|
use App\Http\Requests\Api\Order\StoreOrderRequest;
|
|
use App\Http\Resources\Api\Service\Order\InvoiceDetailResource;
|
|
use App\Http\Resources\Api\Service\Order\InvoiceSummaryResource;
|
|
use App\Http\Resources\Api\Service\Order\OrderDetailResource;
|
|
use App\Http\Resources\Api\Service\Order\OrderItemResource;
|
|
use App\Http\Resources\Api\Service\Order\OrderSummaryResource;
|
|
use App\Http\Resources\Api\Service\Order\ServiceSummaryResource;
|
|
use App\Repositories\Api\Order\OrderRepositoryInterface;
|
|
use App\Services\ActivityLogger;
|
|
use App\Services\Api\Order\OrderQueryService;
|
|
use App\Services\Api\Order\OrderService;
|
|
use App\Services\InvoicePaymentService;
|
|
use App\Services\InvoicePdfService;
|
|
use App\Services\AdminPaymentNotifier;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class OrderControllerRefactor extends Controller
|
|
{
|
|
public function __construct(
|
|
private readonly OrderService $orderService,
|
|
private readonly OrderRepositoryInterface $orderRepository,
|
|
private readonly OrderQueryService $orderQueryService,
|
|
private readonly ActivityLogger $activityLogger,
|
|
private readonly InvoicePaymentService $invoicePaymentService,
|
|
private readonly AdminPaymentNotifier $adminPaymentNotifier,
|
|
) {}
|
|
|
|
public function getList(Request $request): JsonResponse
|
|
{
|
|
$orders = $this->orderQueryService->getOrdersForCustomer(
|
|
(int) $request->user()->id,
|
|
ClientOrderFilters::fromArray($request->only(['total_price', 'pending'])),
|
|
);
|
|
|
|
return response()->json(['success' => true, 'data' => $orders]);
|
|
}
|
|
|
|
public function checkServerName(CheckServerNameRequest $request): JsonResponse
|
|
{
|
|
$result = $this->orderQueryService->checkServerName($request->string('name')->toString());
|
|
|
|
return response()->json($result['available'] ? ['available' => true] : $result);
|
|
}
|
|
|
|
public function getClientOrders(Request $request): JsonResponse
|
|
{
|
|
$orders = $this->orderRepository->getOrders((int) $request->user()->id, []);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => OrderSummaryResource::collection($orders->getCollection()->load('invoice')),
|
|
]);
|
|
}
|
|
|
|
public function getClientOrderDetail(Request $request, int $id): JsonResponse
|
|
{
|
|
$order = $this->orderQueryService->findCustomerOrder((int) $request->user()->id, $id);
|
|
|
|
if (!$order) {
|
|
return response()->json(['success' => false, 'message' => 'Đơn hàng không tồn tại.'], 404);
|
|
}
|
|
|
|
return response()->json(['success' => true, 'data' => new OrderDetailResource($order)]);
|
|
}
|
|
|
|
public function storeOrder(StoreOrderRequest $request): JsonResponse
|
|
{
|
|
try {
|
|
$customer = $request->user();
|
|
$data = $request->validated();
|
|
|
|
$product = \App\Models\Product::where('uuid', $data['product_id'])->first();
|
|
if (!$product) {
|
|
return response()->json(
|
|
['success' => false, 'message' => 'Sản phẩm không tìm thấy hoặc đã bị xóa.'],
|
|
404,
|
|
);
|
|
}
|
|
|
|
$data['product_id'] = $product->id;
|
|
$result = $this->orderService->create($data, $customer);
|
|
|
|
$order = $result['order']->load(['orderItems.product', 'invoice', 'customer']);
|
|
|
|
return response()->json(
|
|
[
|
|
'success' => true,
|
|
'message' => 'Tạo đơn hàng thành công.',
|
|
'notice' => 'Dịch vụ VPS sẽ được khởi tạo trong 5-10 phút sau khi thanh toán.',
|
|
'data' => [
|
|
'order' => new OrderDetailResource($order),
|
|
'invoice' => new InvoiceDetailResource($result['invoice']->load(['customer', 'order'])),
|
|
'items' => OrderItemResource::collection($order->orderItems),
|
|
],
|
|
],
|
|
201,
|
|
);
|
|
} catch (\Illuminate\Validation\ValidationException $e) {
|
|
return response()->json(
|
|
['success' => false, 'message' => collect($e->errors())->flatten()->first() ?? $e->getMessage()],
|
|
422,
|
|
);
|
|
} catch (\InvalidArgumentException $e) {
|
|
return response()->json(['success' => false, 'message' => $e->getMessage()], 400);
|
|
} catch (\Throwable $e) {
|
|
\Log::error('OrderControllerRefactor::storeOrder error: ' . $e->getMessage());
|
|
return response()->json(['success' => false, 'message' => 'Có lỗi xảy ra, vui lòng thử lại'], 500);
|
|
}
|
|
}
|
|
|
|
public function payOrder(string $orderNum, PayOrderRequest $request): JsonResponse
|
|
{
|
|
$order = $this->orderRepository->findByOrderNum($orderNum, (int) $request->user()->id);
|
|
|
|
if (!$order) {
|
|
return response()->json(['success' => false, 'message' => 'Đơn hàng không tồn tại.'], 404);
|
|
}
|
|
|
|
try {
|
|
$this->orderService->pay($order, $request->payment_method);
|
|
} catch (\InvalidArgumentException $e) {
|
|
return response()->json(['success' => false, 'message' => $e->getMessage()], 422);
|
|
} catch (\Throwable $e) {
|
|
\Log::error('OrderControllerRefactor::payOrder error: ' . $e->getMessage());
|
|
return response()->json(['success' => false, 'message' => 'Có lỗi xảy ra, vui lòng thử lại'], 500);
|
|
}
|
|
|
|
$this->activityLogger->log(
|
|
event: 'payment',
|
|
logName: 'Order',
|
|
description: "Customer {$request->user()->email} thanh toán đơn hàng #{$order->order_num} qua {$request->payment_method}",
|
|
subject: $order,
|
|
newValues: ['payment_method' => $request->payment_method, 'total' => $order->total],
|
|
);
|
|
|
|
return response()->json(['success' => true, 'message' => 'Thanh toán thành công.']);
|
|
}
|
|
|
|
public function retryOrderProvisioning(Request $request, int $id): JsonResponse
|
|
{
|
|
$customerId = (int) $request->user()->id;
|
|
$order = $this->orderRepository->findForCustomer($id, $customerId);
|
|
|
|
if (!$order) {
|
|
return response()->json(['success' => false, 'message' => 'Đơn hàng không tồn tại.'], 404);
|
|
}
|
|
|
|
$result = $this->orderQueryService->retryProvisioning($order, $customerId);
|
|
|
|
if (!$result['ok']) {
|
|
return response()->json(['success' => false, 'message' => $result['message']], $result['code']);
|
|
}
|
|
|
|
$this->activityLogger->log(
|
|
event: 'retry_provisioning',
|
|
logName: 'Order',
|
|
description: "Customer {$request->user()->email} yêu cầu khởi tạo lại dịch vụ cho đơn hàng #{$order->order_num}",
|
|
subject: $order,
|
|
);
|
|
|
|
return response()->json(['success' => true, 'message' => $result['message']]);
|
|
}
|
|
|
|
public function getClientInvoices(Request $request): JsonResponse
|
|
{
|
|
$invoices = $this->orderQueryService->getCustomerInvoices((int) $request->user()->id);
|
|
|
|
return response()->json(['success' => true, 'data' => InvoiceSummaryResource::collection($invoices)]);
|
|
}
|
|
|
|
public function getClientInvoiceDetail(Request $request, int $id): JsonResponse
|
|
{
|
|
$invoice = $this->orderQueryService->findCustomerInvoice((int) $request->user()->id, $id);
|
|
|
|
if (!$invoice) {
|
|
return response()->json(['success' => false, 'message' => 'Hóa đơn không tồn tại.'], 404);
|
|
}
|
|
|
|
return response()->json(['success' => true, 'data' => new InvoiceDetailResource($invoice)]);
|
|
}
|
|
|
|
public function getClientInvoiceDetailByOrderNum(Request $request, string $orderNumber): JsonResponse
|
|
{
|
|
$order = $this->orderQueryService->findCustomerInvoiceByOrderNum((int) $request->user()->id, $orderNumber);
|
|
|
|
if (!$order?->invoice) {
|
|
return response()->json(['success' => false, 'message' => 'Hóa đơn không tồn tại.'], 404);
|
|
}
|
|
|
|
$order->load('vmWare.os.osGroup', 'vmWare.virtualMachine', 'vmWare.service');
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => new InvoiceDetailResource($order->invoice->setRelation('order', $order)),
|
|
]);
|
|
}
|
|
|
|
public function getClientInvoiceDetailByNumber(Request $request, string $invoiceNumber): JsonResponse
|
|
{
|
|
$invoice = $this->orderQueryService->findCustomerInvoiceByNumber((int) $request->user()->id, $invoiceNumber);
|
|
|
|
if (!$invoice) {
|
|
return response()->json(['success' => false, 'message' => 'Hóa đơn không tồn tại.'], 404);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => new InvoiceDetailResource($invoice),
|
|
'payment_url' => $invoice->clientPath(),
|
|
]);
|
|
}
|
|
|
|
public function payClientInvoiceByNumber(Request $request, string $invoiceNumber): JsonResponse
|
|
{
|
|
$invoice = $this->orderQueryService->findCustomerInvoiceByNumber((int) $request->user()->id, $invoiceNumber);
|
|
|
|
if (!$invoice) {
|
|
return response()->json(['success' => false, 'message' => 'Hóa đơn không tồn tại.'], 404);
|
|
}
|
|
|
|
// Bỏ trống `amount` = trả hết phần còn thiếu. Truyền số nhỏ hơn để trả từng phần khi
|
|
// số dư chưa đủ — hoá đơn giữ nguyên chưa thanh toán cho tới khi thu đủ.
|
|
$validated = $request->validate([
|
|
'amount' => 'nullable|integer|min:1',
|
|
]);
|
|
|
|
try {
|
|
$amountPaidBefore = (int) $invoice->amount_paid;
|
|
$paidInvoice = $this->invoicePaymentService->payWithBalance(
|
|
$invoice,
|
|
$request->user(),
|
|
'balance',
|
|
$validated['amount'] ?? null,
|
|
);
|
|
|
|
$this->activityLogger->log(
|
|
event: 'payment',
|
|
logName: 'Invoice',
|
|
description: "Customer {$request->user()->email} thanh toán hóa đơn #{$paidInvoice->invoice_number}",
|
|
subject: $paidInvoice,
|
|
newValues: ['invoice_number' => $paidInvoice->invoice_number, 'total' => $paidInvoice->total],
|
|
);
|
|
|
|
$outstanding = \App\Services\InvoicePaymentService::outstanding($paidInvoice);
|
|
$this->adminPaymentNotifier->notify(
|
|
$paidInvoice,
|
|
(int) $paidInvoice->amount_paid - $amountPaidBefore,
|
|
'invoice',
|
|
);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => $outstanding > 0
|
|
? 'Đã ghi nhận thanh toán một phần. Còn thiếu ' . number_format($outstanding) . ' VNĐ để kích hoạt dịch vụ.'
|
|
: 'Thanh toán hóa đơn thành công.',
|
|
'data' => new InvoiceDetailResource($paidInvoice),
|
|
]);
|
|
} catch (\InvalidArgumentException $e) {
|
|
return response()->json(['success' => false, 'message' => $e->getMessage()], 422);
|
|
} catch (\Throwable $e) {
|
|
\Log::error('OrderControllerRefactor::payClientInvoiceByNumber error: ' . $e->getMessage());
|
|
return response()->json(['success' => false, 'message' => 'Có lỗi xảy ra, vui lòng thử lại'], 500);
|
|
}
|
|
}
|
|
|
|
public function getClientOrderDetailByOrderNum(Request $request, string $orderNum): JsonResponse
|
|
{
|
|
$order = $this->orderQueryService->findCustomerInvoiceByOrderNum((int) $request->user()->id, $orderNum);
|
|
|
|
if (!$order) {
|
|
return response()->json(['success' => false, 'message' => 'Đơn hàng không tồn tại.'], 404);
|
|
}
|
|
|
|
return response()->json(['success' => true, 'data' => new OrderDetailResource($order)]);
|
|
}
|
|
|
|
public function downloadInvoicePdfByOrderNum(
|
|
Request $request,
|
|
string $orderNum,
|
|
InvoicePdfService $pdfService,
|
|
): Response {
|
|
$order = $this->orderQueryService->findCustomerInvoiceByOrderNum((int) $request->user()->id, $orderNum);
|
|
|
|
if (!$order?->invoice) {
|
|
abort(404, 'Hóa đơn không tồn tại.');
|
|
}
|
|
|
|
$pdf = $pdfService->generate($order->invoice);
|
|
$filename = $pdfService->generateFileName($order->invoice);
|
|
|
|
return response($pdf, 200, [
|
|
'Content-Type' => 'application/pdf',
|
|
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
|
]);
|
|
}
|
|
|
|
public function getClientServices(Request $request): JsonResponse
|
|
{
|
|
$services = $this->orderQueryService->getCustomerServices((int) $request->user()->id);
|
|
|
|
return response()->json(['success' => true, 'data' => ServiceSummaryResource::collection($services)]);
|
|
}
|
|
|
|
public function updateClientServiceAutoRenew(Request $request, int $id): JsonResponse
|
|
{
|
|
$validated = $request->validate(['auto_renew' => ['required', 'boolean']]);
|
|
|
|
$service = \App\Models\Service::query()->where('id', $id)->where('customer_id', $request->user()->id)->first();
|
|
|
|
if (!$service) {
|
|
return response()->json(['success' => false, 'message' => 'Không tìm thấy dịch vụ.'], 404);
|
|
}
|
|
|
|
$service->update(['auto_renew' => (bool) $validated['auto_renew']]);
|
|
|
|
$this->activityLogger->log(
|
|
event: 'updated',
|
|
logName: 'Service',
|
|
description: "Customer {$request->user()->email} " .
|
|
($validated['auto_renew'] ? 'bật' : 'tắt') .
|
|
" tự động gia hạn dịch vụ #{$service->id}",
|
|
subject: $service,
|
|
newValues: ['auto_renew' => (bool) $validated['auto_renew']],
|
|
);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Cập nhật tự động gia hạn thành công.',
|
|
'data' => ['id' => $service->id, 'auto_renew' => (bool) $service->auto_renew],
|
|
]);
|
|
}
|
|
|
|
public function renewService(RenewServiceRequest $request, int $id): JsonResponse
|
|
{
|
|
$result = $this->orderQueryService->renewService(
|
|
$id,
|
|
$request->user(),
|
|
(int) $request->validated()['months'],
|
|
);
|
|
|
|
if (!$result['ok']) {
|
|
return response()->json(
|
|
['success' => false, 'message' => $result['message'], 'data' => $result['data'] ?? null],
|
|
$result['code'],
|
|
);
|
|
}
|
|
|
|
return response()->json(['success' => true, 'message' => $result['message'], 'data' => $result['data']]);
|
|
}
|
|
}
|