feat: khởi tạo/huỷ domain Plesk qua job, gửi mail hoá đơn qua queue, đính kèm triển khai

- ProvisionDnsDomainJob/SuspendDnsDomainJob: tạo domain admin lên Plesk, huỷ = suspend (is_created=3)
- Khách xem zone DNS được gán ở /dich-vu/domain, quản lý bản ghi A/AAAA/CNAME/TXT/MX/SRV
- send-mail hoá đơn đẩy SendInvoiceMailJob, preview + sửa nội dung, ghi mail.order_id
- invoice_attachments.type (payment_proof/deployment)
- CacheService xoá theo SCAN pattern, Redis không persistent mặc định
- Backfill transactions.public_code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
baodev0
2026-10-11 09:56:18 +07:00
co-authored by Claude Opus 5.5
parent 30898ea46f
commit 94614d9cb3
55 changed files with 1190 additions and 236 deletions
@@ -7,6 +7,7 @@ use App\Http\Resources\Api\Service\VpsServiceCollection;
use App\Http\Resources\Api\Service\Order\InvoiceSummaryResource;
use App\Http\Resources\Api\Client\Service\ServiceClientDetailResource;
use App\Http\Services\vCenter;
use App\Models\DnsDomain;
use App\Models\Order;
use App\Models\Service;
use App\Repositories\Api\ServiceClient\ServiceClientRepository;
@@ -67,6 +68,56 @@ class ServiceController extends Controller
->response();
}
public function indexDomain(Request $request): JsonResponse
{
$customerId = $request->user()->id;
$zones = DnsDomain::query()
->whereHas('customer', fn ($query) => $query->where('customers.id', $customerId))
->get(['id', 'domain_name', 'is_created'])
->keyBy(fn (DnsDomain $zone) => mb_strtolower(trim($zone->domain_name)));
$zoneFields = fn (?DnsDomain $zone) => [
'dns_domain_id' => $zone && !$this->isCancelledZone($zone) ? $zone->id : null,
'cancelled' => $zone !== null && $this->isCancelledZone($zone),
];
$items = Service::query()
->where('customer_id', $customerId)
->where('type', 'domain')
->orderByDesc('id')
->get(['uuid', 'server_name', 'status', 'started_at', 'expires_at'])
->map(fn (Service $service) => [
'domain_name' => $service->server_name,
'service_uuid' => $service->uuid,
'status' => $service->status,
'started_at' => $service->started_at?->toISOString(),
'expires_at' => $service->expires_at?->toISOString(),
...$zoneFields($zones->pull(mb_strtolower(trim((string) $service->server_name)))),
])
->concat($zones->values()->map(fn (DnsDomain $zone) => [
'domain_name' => $zone->domain_name,
'service_uuid' => null,
'status' => null,
'started_at' => null,
'expires_at' => null,
...$zoneFields($zone),
]))
->values();
return response()->json([
'error' => false,
'message' => 'Lấy dữ liệu thành công',
'page_type' => 'service_domain',
'data' => ['items' => $items],
]);
}
private function isCancelledZone(DnsDomain $zone): bool
{
return (int) $zone->is_created === DnsDomain::STATUS_CANCELLED;
}
public function overview(Request $request, OrderQueryService $orderQueryService): JsonResponse
{
$customerId = (int) $request->user()->id;