| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- <?php
- namespace App\Module\Game\Services;
- use App\Module\Game\Models\GameRewardGroup;
- use App\Module\Game\Models\GameRewardItem;
- use App\Module\Game\Models\GameRewardGroupPityCount;
- use Illuminate\Support\Collection;
- /**
- * 保底机制服务类
- *
- * 提供奖励组保底机制的核心功能,包括保底计数管理、概率调整、保底触发等。
- */
- class PityService
- {
- /**
- * 获取用户在指定奖励组的保底计数
- *
- * @param int $userId 用户ID
- * @param int $rewardGroupId 奖励组ID
- * @return Collection
- */
- public static function getUserPityCounts(int $userId, int $rewardGroupId): Collection
- {
- return GameRewardGroupPityCount::where('user_id', $userId)
- ->where('reward_group_id', $rewardGroupId)
- ->with('rewardItem')
- ->get();
- }
- /**
- * 初始化用户的保底计数
- *
- * @param int $userId 用户ID
- * @param int $rewardGroupId 奖励组ID
- * @return void
- */
- public static function initializePityCounts(int $userId, int $rewardGroupId): void
- {
- // 获取奖励组中启用保底的奖励项
- $pityEnabledItems = GameRewardItem::where('group_id', $rewardGroupId)
- ->where('pity_enabled', true)
- ->where('pity_threshold', '>', 0)
- ->get();
- foreach ($pityEnabledItems as $item) {
- // 检查是否已存在保底计数记录
- $existingCount = GameRewardGroupPityCount::where('user_id', $userId)
- ->where('reward_group_id', $rewardGroupId)
- ->where('reward_item_id', $item->id)
- ->first();
- if (!$existingCount) {
- // 创建新的保底计数记录
- GameRewardGroupPityCount::create([
- 'user_id' => $userId,
- 'reward_group_id' => $rewardGroupId,
- 'reward_item_id' => $item->id,
- 'count' => 0,
- 'pity_threshold' => $item->pity_threshold,
- 'last_attempt_at' => now(),
- ]);
- }
- }
- }
- /**
- * 应用保底机制调整奖励权重
- *
- * @param int $userId 用户ID
- * @param Collection $rewardItems 奖励项集合
- * @return Collection 调整后的奖励项集合
- */
- public static function applyPityAdjustments(int $userId, Collection $rewardItems): Collection
- {
- if ($rewardItems->isEmpty()) {
- return $rewardItems;
- }
- $rewardGroupId = $rewardItems->first()->group_id;
- // 初始化保底计数(如果需要)
- self::initializePityCounts($userId, $rewardGroupId);
- // 获取用户的保底计数
- $pityCounts = self::getUserPityCounts($userId, $rewardGroupId);
- $pityCountsMap = $pityCounts->keyBy('reward_item_id');
- // 调整每个奖励项的权重
- return $rewardItems->map(function (GameRewardItem $item) use ($pityCountsMap) {
- $pityCount = $pityCountsMap->get($item->id);
- if ($pityCount && $item->pity_enabled && $item->pity_threshold > 0) {
- // 计算调整后的权重
- $adjustedWeight = $pityCount->getAdjustedWeight(
- $item->weight,
- $item->pity_weight_factor
- );
- // 创建一个新的奖励项实例,避免修改原始数据
- $adjustedItem = clone $item;
- $adjustedItem->weight = $adjustedWeight;
- // 添加保底信息到额外数据中
- $extraData = $adjustedItem->extra_data ?: [];
- $extraData['pity_info'] = [
- 'current_count' => $pityCount->count,
- 'threshold' => $pityCount->pity_threshold,
- 'is_at_threshold' => $pityCount->isAtPityThreshold(),
- 'original_weight' => $item->weight,
- 'adjusted_weight' => $adjustedWeight,
- ];
- $adjustedItem->extra_data = $extraData;
- return $adjustedItem;
- }
- return $item;
- });
- }
- /**
- * 更新保底计数
- *
- * @param int $userId 用户ID
- * @param int $rewardGroupId 奖励组ID
- * @param array $obtainedItemIds 获得的奖励项ID数组
- * @return void
- */
- public static function updatePityCounts(int $userId, int $rewardGroupId, array $obtainedItemIds): void
- {
- // 获取用户的保底计数
- $pityCounts = self::getUserPityCounts($userId, $rewardGroupId);
- foreach ($pityCounts as $pityCount) {
- if (in_array($pityCount->reward_item_id, $obtainedItemIds)) {
- // 获得了该奖励项,重置计数
- $pityCount->resetCount();
- } else {
- // 未获得该奖励项,增加计数
- $pityCount->incrementCount();
- }
- }
- }
- /**
- * 检查是否有保底触发
- *
- * @param int $userId 用户ID
- * @param int $rewardGroupId 奖励组ID
- * @return array 触发保底的奖励项ID数组
- */
- public static function checkPityTriggers(int $userId, int $rewardGroupId): array
- {
- $pityCounts = self::getUserPityCounts($userId, $rewardGroupId);
- $triggeredItems = [];
- foreach ($pityCounts as $pityCount) {
- if ($pityCount->isAtPityThreshold()) {
- $triggeredItems[] = $pityCount->reward_item_id;
- }
- }
- return $triggeredItems;
- }
- /**
- * 获取用户保底状态信息
- *
- * @param int $userId 用户ID
- * @param int $rewardGroupId 奖励组ID
- * @return array
- */
- public static function getPityStatus(int $userId, int $rewardGroupId): array
- {
- $pityCounts = self::getUserPityCounts($userId, $rewardGroupId);
- $status = [];
- foreach ($pityCounts as $pityCount) {
- $rewardItem = $pityCount->rewardItem;
- $status[] = [
- 'reward_item_id' => $pityCount->reward_item_id,
- 'reward_item_name' => $rewardItem ? $rewardItem->getTargetName() : '未知奖励',
- 'current_count' => $pityCount->count,
- 'threshold' => $pityCount->pity_threshold,
- 'progress_percentage' => $pityCount->pity_threshold > 0
- ? round(($pityCount->count / $pityCount->pity_threshold) * 100, 2)
- : 0,
- 'is_at_threshold' => $pityCount->isAtPityThreshold(),
- 'last_attempt_at' => $pityCount->last_attempt_at,
- 'last_hit_at' => $pityCount->last_hit_at,
- ];
- }
- return $status;
- }
- /**
- * 重置用户的所有保底计数
- *
- * @param int $userId 用户ID
- * @param int $rewardGroupId 奖励组ID
- * @return void
- */
- public static function resetAllPityCounts(int $userId, int $rewardGroupId): void
- {
- GameRewardGroupPityCount::where('user_id', $userId)
- ->where('reward_group_id', $rewardGroupId)
- ->update([
- 'count' => 0,
- 'last_hit_at' => now(),
- ]);
- }
- /**
- * 清理过期的保底计数记录
- *
- * @param int $daysOld 清理多少天前的记录
- * @return int 清理的记录数量
- */
- public static function cleanupOldPityCounts(int $daysOld = 90): int
- {
- $cutoffDate = now()->subDays($daysOld);
- return GameRewardGroupPityCount::where('last_attempt_at', '<', $cutoffDate)
- ->where('count', 0)
- ->delete();
- }
- }
|