feat: add domain registration form and email notifications

- Create a new Blade view for domain registration (`registrant-tochuc.blade.php`) to capture organization and manager details.
- Implement email templates for new domain orders for both admin (`domain-order-new.blade.php`) and customers (`domain-order-received.blade.php`).
- Add CSV exports for client and domain data.
- Introduce tests for customer mass assignment, password reset, login throttling, and permission gating.
- Ensure service renewals use current pricing and validate ownership for virtual machines.
- Validate mass assignment behavior in service model.
This commit is contained in:
baodev0
2026-08-22 17:30:55 +07:00
parent eec2c5906e
commit c377c7e195
58 changed files with 3063 additions and 418 deletions
@@ -8,6 +8,7 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\Api\Admin\Domain\ConfirmRegistrationRequest;
use App\Http\Requests\Api\Admin\Domain\RejectAndRefundRequest;
use App\Http\Requests\Api\Admin\Domain\UpdateKycUrlRequest;
use App\Http\Requests\Api\Admin\Domain\UpdateRegistrantInfoRequest;
use App\Mail\Customer\DomainOrderStatusMail;
use App\Models\Notification;
use App\Models\Order;
@@ -16,16 +17,19 @@ use App\Models\Setting;
use App\Models\Template;
use App\Repositories\Api\Domain\DomainOrderRepository;
use App\Services\Api\Domain\DomainOrderService;
use App\Services\Api\Domain\DomainRegistrantService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class AdminDomainOrderController extends Controller
{
public function __construct(
private readonly DomainOrderRepository $repo,
private readonly DomainOrderService $service,
private readonly DomainOrderRepository $repo,
private readonly DomainOrderService $service,
private readonly DomainRegistrantService $registrantService,
) {}
public function pendingCount(): JsonResponse
@@ -188,6 +192,65 @@ class AdminDomainOrderController extends Controller
}
}
public function getRegistrant(int $id): JsonResponse
{
$order = $this->repo->findDomainOrder($id);
$service = $order?->orderItems->first()?->service;
if (!$order || !$service) {
return response()->json([
'error' => true,
'message' => 'Không tìm thấy đơn hàng domain.',
], 404);
}
return response()->json([
'error' => false,
'data' => $this->registrantService->get($service, $order->customer),
]);
}
public function updateRegistrant(UpdateRegistrantInfoRequest $request, int $id): JsonResponse
{
$order = $this->repo->findDomainOrder($id);
$service = $order?->orderItems->first()?->service;
if (!$order || !$service) {
return response()->json([
'error' => true,
'message' => 'Không tìm thấy đơn hàng domain.',
], 404);
}
$data = $request->validated();
$service = $this->registrantService->update($service, $data['registrant_type'], $data['registrant_info']);
return response()->json([
'error' => false,
'message' => 'Cập nhật thông tin đăng ký thành công.',
'data' => $this->formatService($service),
]);
}
public function downloadRegistrantDocx(int $id): Response
{
$order = $this->repo->findDomainOrder($id);
$item = $order?->orderItems->first();
$service = $item?->service;
abort_if(!$order || !$service, 404, 'Không tìm thấy đơn hàng domain.');
$registrant = $this->registrantService->get($service, $order->customer);
$docx = $this->registrantService->renderDocx($item->domain_name, $registrant['type'], $registrant['info']);
$filename = 'phieu-dang-ky-' . $item->domain_name . '.docx';
return response($docx, 200, [
'Content-Type' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
]);
}
// =========================================================================
// Private helpers
// =========================================================================
@@ -280,6 +343,8 @@ class AdminDomainOrderController extends Controller
'registrar_reference' => $service->registrar_reference,
'kyc_url' => $service->kyc_url,
'rejection_reason' => $service->rejection_reason,
'registrant_type' => $service->registrant_type,
'registrant_info' => $service->registrant_info,
'auto_renew' => $service->auto_renew,
'started_at' => $service->started_at?->toIso8601String(),
'expires_at' => $service->expires_at?->toIso8601String(),