165 lines
4.9 KiB
PHP
165 lines
4.9 KiB
PHP
<?php
|
||
|
||
/**
|
||
* 文件功能:会员进退场主题服务
|
||
* 统一解析会员等级主题、用户自定义文案、特效和横幅风格,
|
||
* 避免聊天室进场与离场逻辑在多个位置重复拼装数据。
|
||
*/
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\User;
|
||
|
||
/**
|
||
* 会员进退场与购买成功主题服务
|
||
* 统一生成会员欢迎横幅、离场横幅与购买成功喜报所需的文本、颜色、特效和样式数据。
|
||
*/
|
||
class VipPresenceService
|
||
{
|
||
/**
|
||
* 构造会员进退场主题服务实例。
|
||
*/
|
||
public function __construct(
|
||
private readonly VipService $vipService,
|
||
) {}
|
||
|
||
/**
|
||
* 构建会员入场主题数据。
|
||
*
|
||
* @return array<string, string|null|bool>
|
||
*/
|
||
public function buildJoinTheme(User $user): array
|
||
{
|
||
return $this->buildTheme($user, 'join');
|
||
}
|
||
|
||
/**
|
||
* 构建会员离场主题数据。
|
||
*
|
||
* @return array<string, string|null|bool>
|
||
*/
|
||
public function buildLeaveTheme(User $user): array
|
||
{
|
||
return $this->buildTheme($user, 'leave');
|
||
}
|
||
|
||
/**
|
||
* 构建会员购买成功主题数据。
|
||
*
|
||
* @return array<string, string|null|bool>
|
||
*/
|
||
public function buildPurchaseTheme(User $user): array
|
||
{
|
||
$vipLevel = $user->vipLevel;
|
||
|
||
if (! $user->isVip() || ! $vipLevel) {
|
||
return [
|
||
'enabled' => false,
|
||
'type' => 'purchase',
|
||
'text' => null,
|
||
'color' => null,
|
||
'effect' => null,
|
||
'banner_style' => null,
|
||
'level_name' => null,
|
||
'icon' => null,
|
||
];
|
||
}
|
||
|
||
return [
|
||
'enabled' => true,
|
||
'type' => 'purchase',
|
||
'text' => sprintf(
|
||
'恭喜 %s 成功开通【%s %s】,尊享 VIP 特权,大家掌声欢迎!',
|
||
$user->username,
|
||
$vipLevel->icon ?: '👑',
|
||
$vipLevel->name
|
||
),
|
||
'color' => $vipLevel->color ?: '#f59e0b',
|
||
'effect' => 'fireworks',
|
||
'banner_style' => $vipLevel->joinBannerStyleKey(),
|
||
'level_name' => $vipLevel->name,
|
||
'icon' => $vipLevel->icon,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 统一构建会员进场或离场的主题数据。
|
||
*
|
||
* @param string $type join|leave
|
||
* @return array<string, string|null|bool>
|
||
*/
|
||
private function buildTheme(User $user, string $type): array
|
||
{
|
||
$vipLevel = $user->vipLevel;
|
||
|
||
if (! $user->isVip() || ! $vipLevel) {
|
||
return [
|
||
'enabled' => false,
|
||
'type' => $type,
|
||
'text' => null,
|
||
'color' => null,
|
||
'effect' => null,
|
||
'banner_style' => null,
|
||
'level_name' => null,
|
||
'icon' => null,
|
||
];
|
||
}
|
||
|
||
// 先读取个人自定义文案,只有等级允许时才参与覆盖。
|
||
$customMessage = $type === 'join'
|
||
? $this->formatCustomMessage($user->custom_join_message, $user->username)
|
||
: $this->formatCustomMessage($user->custom_leave_message, $user->username);
|
||
|
||
if (! $user->canCustomizeVipPresence()) {
|
||
$customMessage = null;
|
||
}
|
||
|
||
// 如果用户没有填写自定义文案,则回退到等级模板。
|
||
$templateMessage = $type === 'join'
|
||
? $this->vipService->getJoinMessage($user)
|
||
: $this->vipService->getLeaveMessage($user);
|
||
|
||
// 只有在等级允许自定义时,才允许覆盖默认特效。
|
||
$customEffect = $type === 'join'
|
||
? $user->custom_join_effect
|
||
: $user->custom_leave_effect;
|
||
|
||
if (! $user->canCustomizeVipPresence()) {
|
||
$customEffect = null;
|
||
}
|
||
|
||
return [
|
||
'enabled' => true,
|
||
'type' => $type,
|
||
'text' => $customMessage ?: $templateMessage,
|
||
'color' => $vipLevel->color ?: '#f59e0b',
|
||
'effect' => $this->normalizeEffect($customEffect ?: ($type === 'join' ? $vipLevel->joinEffectKey() : $vipLevel->leaveEffectKey())),
|
||
'banner_style' => $type === 'join' ? $vipLevel->joinBannerStyleKey() : $vipLevel->leaveBannerStyleKey(),
|
||
'level_name' => $vipLevel->name,
|
||
'icon' => $vipLevel->icon,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 格式化用户自定义文案,支持 {username} 占位符。
|
||
*/
|
||
private function formatCustomMessage(?string $message, string $username): ?string
|
||
{
|
||
$message = trim((string) $message);
|
||
|
||
if ($message === '') {
|
||
return null;
|
||
}
|
||
|
||
return str_replace('{username}', $username, $message);
|
||
}
|
||
|
||
/**
|
||
* 把 none 这类占位值转换为 null,方便外部判断是否要播特效。
|
||
*/
|
||
private function normalizeEffect(string $effect): ?string
|
||
{
|
||
return $effect === 'none' ? null : $effect;
|
||
}
|
||
}
|