Files
portal-api/app/Support/EmailScenarioRegistry.php
baodev0andClaude Sonnet 5.5 b44a88bb52 feat(service): huỷ dịch vụ, SSL, đổi mật khẩu hosting, lịch sử thanh toán theo hình thức
- Lịch sử thanh toán hoá đơn trả thêm `method` (tự động gia hạn / số dư / admin / chuyển khoản / hoàn tiền), bỏ đuôi `(method)` khỏi ghi chú
- `DnsServer::panelUrl()` = https://<domain>/ dùng cho email cấp hosting và nút Plesk của khách
- Order item trả thêm `uuid`/`type` của dịch vụ để FE dẫn tới trang quản lý
- Yêu cầu huỷ dịch vụ, chứng chỉ SSL, lịch sử gia hạn, đổi mật khẩu hosting cho khách

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 16:40:24 +07:00

255 lines
12 KiB
PHP

<?php
namespace App\Support;
final class EmailScenarioRegistry
{
public const LAYOUT = 'LAYOUT';
public const TRANSACTIONAL = 'TRANSACTIONAL';
public const BUSINESS = 'BUSINESS';
public const INTERNAL = 'INTERNAL';
public const MARKETING = 'MARKETING';
public const CATEGORIES = [
self::LAYOUT => '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<string, bool>, required_variables: array<int, string>}
*/
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<int, string> $required
* @return array<int, string>
*/
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),
));
}
}