'Bố cục chung', self::TRANSACTIONAL => 'Transactional', self::BUSINESS => 'Business', self::INTERNAL => 'Internal', self::MARKETING => 'Marketing', ]; private const SYSTEM_PERMISSIONS = [ 'edit_content' => true, 'delete' => false, 'change_key' => false, 'disable' => false, ]; private const CUSTOM_PERMISSIONS = [ 'edit_content' => true, 'delete' => true, 'change_key' => true, 'disable' => true, ]; private const LAYOUT_SLOT_TOKEN = '{{$content}}'; public const SCENARIOS = [ 'layout' => [ 'label' => 'Bố cục Header/Footer (mặc định)', 'category' => self::LAYOUT, 'group' => 'LAYOUT', 'required_variables' => ['content'], ], 'verify_account_welcome' => [ 'label' => 'Mã xác thực tài khoản', 'category' => self::TRANSACTIONAL, 'group' => 'AUTH', 'required_variables' => ['verify_code'], ], 'customer_password_reset_link' => [ 'label' => 'Link đặt lại mật khẩu khách hàng', 'category' => self::TRANSACTIONAL, 'group' => 'AUTH', 'required_variables' => ['reset_url'], ], 'customer_two_factor_code' => [ 'label' => 'Mã xác thực 2 lớp (2FA)', 'category' => self::TRANSACTIONAL, 'group' => 'SECURITY', 'required_variables' => ['verify_code'], ], 'password_reset_admin' => [ 'label' => 'Admin cấp lại mật khẩu', 'category' => self::TRANSACTIONAL, 'group' => 'SECURITY', 'required_variables' => ['password'], ], 'customer_welcome' => [ 'label' => 'Chào mừng khách hàng mới', 'category' => self::TRANSACTIONAL, 'group' => 'ACCOUNT', 'required_variables' => [], ], 'vm_credentials' => [ 'label' => 'Khởi tạo VPS (thông tin truy cập)', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['service_host', 'service_username', 'service_password'], ], 'hosting_information' => [ 'label' => 'Thông tin truy cập Hosting', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['service_host', 'service_username', 'service_password'], ], 'hosting_account_created' => [ 'label' => 'Hosting đã khởi tạo', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['domain_name'], ], 'ip_allocated' => [ 'label' => 'Cấp phát địa chỉ IP', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['ip_table'], ], 'ip_revoked' => [ 'label' => 'Thu hồi địa chỉ IP', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['ip_rows'], ], 'vm_suspended' => [ 'label' => 'VPS bị tạm ngưng', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['vm_name'], ], 'service_expiry' => [ 'label' => 'Dịch vụ sắp hết hạn', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['product_name', 'service_due_exp'], ], 'service_renewal_reminder' => [ 'label' => 'Nhắc gia hạn dịch vụ', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['product_name', 'expires_at'], ], 'service_renewed' => [ 'label' => 'Dịch vụ đã được gia hạn', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['product_name', 'expires_at'], ], 'auto_renew_failed' => [ 'label' => 'Tự động gia hạn thất bại (thiếu số dư)', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['product_name', 'invoice_number'], ], 'vm_creation_failed' => [ 'label' => 'Khởi tạo VPS thất bại', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['order_num'], ], 'vm_rebuild_failed' => [ 'label' => 'Rebuild VPS thất bại', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['vm_name'], ], 'vm_password_changed' => [ 'label' => 'Đổi mật khẩu VPS', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['vm_name', 'new_password_row'], ], 'snapshot_action' => [ 'label' => 'Thao tác Snapshot VPS', 'category' => self::BUSINESS, 'group' => 'SERVICE', 'required_variables' => ['vm_name', 'snapshot_name'], ], 'invoice_notification' => [ 'label' => 'Thông báo hoá đơn / gia hạn cước', 'category' => self::BUSINESS, 'group' => 'BILLING', 'required_variables' => ['invoice_number', 'total'], ], 'invoice_payment_reminder' => [ 'label' => 'Nhắc thanh toán hoá đơn', 'category' => self::BUSINESS, 'group' => 'BILLING', 'required_variables' => ['invoice_number', 'outstanding'], ], 'customer_deposit_success' => [ 'label' => 'Nạp tiền thành công', 'category' => self::BUSINESS, 'group' => 'BILLING', 'required_variables' => ['transaction_code', 'amount_formatted'], ], 'manual_deposit_status' => [ 'label' => 'Trạng thái nạp tiền thủ công', 'category' => self::BUSINESS, 'group' => 'BILLING', 'required_variables' => ['transaction_code', 'status_label'], ], 'order_created' => [ 'label' => 'Xác nhận đơn hàng', 'category' => self::BUSINESS, 'group' => 'ORDER', 'required_variables' => ['order_num'], 'permissions' => ['disable' => true], ], 'domain_order_received' => [ 'label' => 'Đơn đăng ký tên miền đã ghi nhận', 'category' => self::BUSINESS, 'group' => 'ORDER', 'required_variables' => ['domain_name', 'order_num'], ], 'domain_order_approved' => [ 'label' => 'Bàn giao tên miền (đã duyệt)', 'category' => self::BUSINESS, 'group' => 'ORDER', 'required_variables' => ['domain_name'], 'permissions' => ['disable' => true], ], 'domain_order_rejected' => [ 'label' => 'Tên miền bị từ chối', 'category' => self::BUSINESS, 'group' => 'ORDER', 'required_variables' => ['domain_name', 'reason'], 'permissions' => ['disable' => true], ], 'ticket_created' => [ 'label' => 'Ticket hỗ trợ mới', 'category' => self::BUSINESS, 'group' => 'SUPPORT', 'required_variables' => ['ticket_num'], ], 'admin_order_notification' => [ 'label' => '[Admin] Đơn hàng mới', 'category' => self::INTERNAL, 'group' => 'ORDER', 'required_variables' => ['order_num'], ], 'admin_payment_received' => [ 'label' => '[Admin] Khách hàng thanh toán', 'category' => self::INTERNAL, 'group' => 'BILLING', 'required_variables' => ['reference', 'amount_formatted'], ], 'admin_service_renewed' => [ 'label' => '[Admin] Dịch vụ được gia hạn', 'category' => self::INTERNAL, 'group' => 'SERVICE', 'required_variables' => ['product_name', 'expires_at'], ], 'admin_domain_order_new' => [ 'label' => '[Admin] Đơn đăng ký tên miền mới', 'category' => self::INTERNAL, 'group' => 'ORDER', 'required_variables' => ['domain_name', 'order_num'], ], 'admin_hosting_account_created' => [ 'label' => '[Admin] Hosting account mới', 'category' => self::INTERNAL, 'group' => 'SERVICE', 'required_variables' => ['domain_name'], ], 'admin_trial_registration_request' => [ 'label' => '[Admin] Yêu cầu dùng thử VPS', 'category' => self::INTERNAL, 'group' => 'SERVICE', 'required_variables' => ['customer_email'], ], 'admin_manual_deposit_pending' => [ 'label' => '[Admin] Yêu cầu nạp tiền chờ duyệt', 'category' => self::INTERNAL, 'group' => 'BILLING', 'required_variables' => ['transaction_code', 'amount_formatted'], ], 'admin_ticket_notification' => [ 'label' => '[Admin] Ticket hỗ trợ mới', 'category' => self::INTERNAL, 'group' => 'SUPPORT', 'required_variables' => ['ticket_num'], ], 'admin_customer_service_renewed' => [ 'label' => '[Admin] Khách hàng gia hạn dịch vụ', 'category' => self::INTERNAL, 'group' => 'SERVICE', 'required_variables' => ['product_name', 'expires_at'], ], 'admin_service_cancellation_request' => [ 'label' => '[Admin] Khách yêu cầu hủy dịch vụ', 'category' => self::INTERNAL, 'group' => 'SERVICE', 'required_variables' => ['customer_name', 'cancel_reason'], ], 'admin_login_alert' => [ 'label' => '[Admin] Có người đăng nhập trang quản trị', 'category' => self::INTERNAL, 'group' => 'SECURITY', 'required_variables' => ['admin_username', 'client_ip', 'login_time'], ], ]; public static function find(?string ...$identifiers): ?string { foreach ($identifiers as $identifier) { if ($identifier !== null && isset(self::SCENARIOS[$identifier])) { return $identifier; } } return null; } /** * @return array{registered: bool, scenario: string|null, label: string|null, category: string, group: string|null, permissions: array, required_variables: array} */ public static function describe(?string ...$identifiers): array { $scenario = self::find(...$identifiers); $definition = $scenario ? self::SCENARIOS[$scenario] : null; return [ 'registered' => $definition !== null, 'scenario' => $scenario, 'label' => $definition['label'] ?? null, 'category' => $definition['category'] ?? self::MARKETING, 'group' => $definition['group'] ?? null, 'permissions' => $definition ? [...self::SYSTEM_PERMISSIONS, ...($definition['permissions'] ?? [])] : self::CUSTOM_PERMISSIONS, 'required_variables' => $definition['required_variables'] ?? [], ]; } /** * @param array $required * @return array */ public static function missingVariables(array $required, ?string $subject, ?string $message): array { $content = $subject . "\n" . $message; $used = MailPlaceholders::extract($content); return array_values(array_filter( $required, fn (string $key) => $key === 'content' ? !str_contains((string) $message, self::LAYOUT_SLOT_TOKEN) : !in_array($key, $used, true), )); } }