215 lines
9.3 KiB
PHP
215 lines
9.3 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'],
|
|
],
|
|
'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'],
|
|
],
|
|
'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'],
|
|
],
|
|
'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' => 'Tên miền được 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_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'],
|
|
],
|
|
];
|
|
|
|
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),
|
|
));
|
|
}
|
|
}
|