mirror of
https://github.com/lkddi/Xboard.git
synced 2026-04-14 02:50:52 +08:00
335 lines
9.6 KiB
PHP
335 lines
9.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Exceptions\ApiException;
|
|
use App\Http\Resources\PlanResource;
|
|
use App\Models\GiftCardCode;
|
|
use App\Models\GiftCardTemplate;
|
|
use App\Models\GiftCardUsage;
|
|
use App\Models\Plan;
|
|
use App\Models\TrafficResetLog;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class GiftCardService
|
|
{
|
|
protected readonly GiftCardCode $code;
|
|
protected readonly GiftCardTemplate $template;
|
|
protected ?User $user = null;
|
|
|
|
public function __construct(string $code)
|
|
{
|
|
$this->code = GiftCardCode::where('code', $code)->first()
|
|
?? throw new ApiException('兑换码不存在');
|
|
|
|
$this->template = $this->code->template;
|
|
}
|
|
|
|
/**
|
|
* 设置使用用户
|
|
*/
|
|
public function setUser(User $user): self
|
|
{
|
|
$this->user = $user;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 验证兑换码
|
|
*/
|
|
public function validate(): self
|
|
{
|
|
$this->validateIsActive();
|
|
|
|
$eligibility = $this->checkUserEligibility();
|
|
if (!$eligibility['can_redeem']) {
|
|
throw new ApiException($eligibility['reason']);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 验证礼品卡本身是否可用 (不检查用户条件)
|
|
* @throws ApiException
|
|
*/
|
|
public function validateIsActive(): self
|
|
{
|
|
if (!$this->template->isAvailable()) {
|
|
throw new ApiException('该礼品卡类型已停用');
|
|
}
|
|
|
|
if (!$this->code->isAvailable()) {
|
|
throw new ApiException('兑换码不可用:' . $this->code->status_name);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 检查用户是否满足兑换条件 (不抛出异常)
|
|
*/
|
|
public function checkUserEligibility(): array
|
|
{
|
|
if (!$this->user) {
|
|
return [
|
|
'can_redeem' => false,
|
|
'reason' => '用户信息未提供'
|
|
];
|
|
}
|
|
|
|
if (!$this->template->checkUserConditions($this->user)) {
|
|
return [
|
|
'can_redeem' => false,
|
|
'reason' => '您不满足此礼品卡的使用条件'
|
|
];
|
|
}
|
|
|
|
if (!$this->template->checkUsageLimit($this->user)) {
|
|
return [
|
|
'can_redeem' => false,
|
|
'reason' => '您已达到此礼品卡的使用限制'
|
|
];
|
|
}
|
|
|
|
return ['can_redeem' => true, 'reason' => null];
|
|
}
|
|
|
|
/**
|
|
* 使用礼品卡
|
|
*/
|
|
public function redeem(array $options = []): array
|
|
{
|
|
if (!$this->user) {
|
|
throw new ApiException('未设置使用用户');
|
|
}
|
|
|
|
return DB::transaction(function () use ($options) {
|
|
$actualRewards = $this->template->calculateActualRewards($this->user);
|
|
|
|
if ($this->template->type === GiftCardTemplate::TYPE_MYSTERY) {
|
|
$this->code->setActualRewards($actualRewards);
|
|
}
|
|
|
|
$this->giveRewards($actualRewards);
|
|
|
|
$inviteRewards = null;
|
|
if ($this->user->invite_user_id && isset($actualRewards['invite_reward_rate'])) {
|
|
$inviteRewards = $this->giveInviteRewards($actualRewards);
|
|
}
|
|
|
|
$this->code->markAsUsed($this->user);
|
|
|
|
GiftCardUsage::createRecord(
|
|
$this->code,
|
|
$this->user,
|
|
$actualRewards,
|
|
array_merge($options, [
|
|
'invite_rewards' => $inviteRewards,
|
|
'multiplier' => $this->calculateMultiplier(),
|
|
])
|
|
);
|
|
|
|
return [
|
|
'rewards' => $actualRewards,
|
|
'invite_rewards' => $inviteRewards,
|
|
'code' => $this->code->code,
|
|
'template_name' => $this->template->name,
|
|
];
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 发放奖励
|
|
*/
|
|
protected function giveRewards(array $rewards): void
|
|
{
|
|
$userService = app(UserService::class);
|
|
|
|
if (isset($rewards['balance']) && $rewards['balance'] > 0) {
|
|
if (!$userService->addBalance($this->user->id, $rewards['balance'])) {
|
|
throw new ApiException('余额发放失败');
|
|
}
|
|
}
|
|
|
|
if (isset($rewards['transfer_enable']) && $rewards['transfer_enable'] > 0) {
|
|
$this->user->transfer_enable = ($this->user->transfer_enable ?? 0) + $rewards['transfer_enable'];
|
|
}
|
|
|
|
if (isset($rewards['device_limit']) && $rewards['device_limit'] > 0) {
|
|
$this->user->device_limit = ($this->user->device_limit ?? 0) + $rewards['device_limit'];
|
|
}
|
|
|
|
if (isset($rewards['reset_package']) && $rewards['reset_package']) {
|
|
if ($this->user->plan_id) {
|
|
app(TrafficResetService::class)->performReset($this->user, TrafficResetLog::SOURCE_GIFT_CARD);
|
|
}
|
|
}
|
|
|
|
if (isset($rewards['plan_id'])) {
|
|
$plan = Plan::find($rewards['plan_id']);
|
|
if ($plan) {
|
|
$userService->assignPlan(
|
|
$this->user,
|
|
$plan,
|
|
$rewards['plan_validity_days'] ?? 0
|
|
);
|
|
}
|
|
} else {
|
|
// 只有在不是套餐卡的情况下,才处理独立的有效期奖励
|
|
if (isset($rewards['expire_days']) && $rewards['expire_days'] > 0) {
|
|
$userService->extendSubscription($this->user, $rewards['expire_days']);
|
|
}
|
|
}
|
|
|
|
// 保存用户更改
|
|
if (!$this->user->save()) {
|
|
throw new ApiException('用户信息更新失败');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 发放邀请人奖励
|
|
*/
|
|
protected function giveInviteRewards(array $rewards): ?array
|
|
{
|
|
if (!$this->user->invite_user_id) {
|
|
return null;
|
|
}
|
|
|
|
$inviteUser = User::find($this->user->invite_user_id);
|
|
if (!$inviteUser) {
|
|
return null;
|
|
}
|
|
|
|
$rate = $rewards['invite_reward_rate'] ?? 0.2;
|
|
$inviteRewards = [];
|
|
|
|
$userService = app(UserService::class);
|
|
|
|
// 邀请人余额奖励
|
|
if (isset($rewards['balance']) && $rewards['balance'] > 0) {
|
|
$inviteBalance = intval($rewards['balance'] * $rate);
|
|
if ($inviteBalance > 0) {
|
|
$userService->addBalance($inviteUser->id, $inviteBalance);
|
|
$inviteRewards['balance'] = $inviteBalance;
|
|
}
|
|
}
|
|
|
|
// 邀请人流量奖励
|
|
if (isset($rewards['transfer_enable']) && $rewards['transfer_enable'] > 0) {
|
|
$inviteTransfer = intval($rewards['transfer_enable'] * $rate);
|
|
if ($inviteTransfer > 0) {
|
|
$inviteUser->transfer_enable = ($inviteUser->transfer_enable ?? 0) + $inviteTransfer;
|
|
$inviteUser->save();
|
|
$inviteRewards['transfer_enable'] = $inviteTransfer;
|
|
}
|
|
}
|
|
|
|
return $inviteRewards;
|
|
}
|
|
|
|
/**
|
|
* 计算倍率
|
|
*/
|
|
protected function calculateMultiplier(): float
|
|
{
|
|
return $this->getFestivalBonus();
|
|
}
|
|
|
|
/**
|
|
* 获取节日加成倍率
|
|
*/
|
|
private function getFestivalBonus(): float
|
|
{
|
|
$festivalConfig = $this->template->special_config ?? [];
|
|
$now = time();
|
|
|
|
if (
|
|
isset($festivalConfig['start_time'], $festivalConfig['end_time']) &&
|
|
$now >= $festivalConfig['start_time'] &&
|
|
$now <= $festivalConfig['end_time']
|
|
) {
|
|
return $festivalConfig['festival_bonus'] ?? 1.0;
|
|
}
|
|
|
|
return 1.0;
|
|
}
|
|
|
|
/**
|
|
* 获取兑换码信息(不包含敏感信息)
|
|
*/
|
|
public function getCodeInfo(): array
|
|
{
|
|
$info = [
|
|
'code' => $this->code->code,
|
|
'template' => [
|
|
'name' => $this->template->name,
|
|
'description' => $this->template->description,
|
|
'type' => $this->template->type,
|
|
'type_name' => $this->template->type_name,
|
|
'icon' => $this->template->icon,
|
|
'background_image' => $this->template->background_image,
|
|
'theme_color' => $this->template->theme_color,
|
|
],
|
|
'status' => $this->code->status,
|
|
'status_name' => $this->code->status_name,
|
|
'expires_at' => $this->code->expires_at,
|
|
'usage_count' => $this->code->usage_count,
|
|
'max_usage' => $this->code->max_usage,
|
|
];
|
|
if ($this->template->type === GiftCardTemplate::TYPE_PLAN) {
|
|
$plan = Plan::find($this->code->template->rewards['plan_id']);
|
|
if ($plan) {
|
|
$info['plan_info'] = PlanResource::make($plan)->toArray(request());
|
|
}
|
|
}
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* 预览奖励(不实际发放)
|
|
*/
|
|
public function previewRewards(): array
|
|
{
|
|
if (!$this->user) {
|
|
throw new ApiException('未设置使用用户');
|
|
}
|
|
|
|
return $this->template->calculateActualRewards($this->user);
|
|
}
|
|
|
|
/**
|
|
* 获取兑换码
|
|
*/
|
|
public function getCode(): GiftCardCode
|
|
{
|
|
return $this->code;
|
|
}
|
|
|
|
/**
|
|
* 获取模板
|
|
*/
|
|
public function getTemplate(): GiftCardTemplate
|
|
{
|
|
return $this->template;
|
|
}
|
|
|
|
/**
|
|
* 记录日志
|
|
*/
|
|
protected function logUsage(string $action, array $data = []): void
|
|
{
|
|
Log::info('礼品卡使用记录', [
|
|
'action' => $action,
|
|
'code' => $this->code->code,
|
|
'template_id' => $this->template->id,
|
|
'user_id' => $this->user?->id,
|
|
'data' => $data,
|
|
'ip' => request()->ip(),
|
|
'user_agent' => request()->userAgent(),
|
|
]);
|
|
}
|
|
}
|