WeightSelectionReward.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. <?php
  2. namespace App\Module\Game\Logics;
  3. use App\Module\Game\Dtos\RewardGroupDto;
  4. use App\Module\Game\Dtos\RewardItemDto;
  5. use App\Module\Game\Logics\Reward\Item;
  6. use App\Module\Game\Services\PityService;
  7. use App\Module\Game\Models\GameRewardItem;
  8. use Illuminate\Support\Facades\Log;
  9. /**
  10. * 权重选择模式奖励处理
  11. */
  12. class WeightSelectionReward
  13. {
  14. /**
  15. * 处理权重选择模式奖励
  16. *
  17. * @param RewardGroupDto $groupDto 奖励组DTO
  18. * @param int|null $userId 用户ID(用于保底机制)
  19. * @return RewardItemDto[] 要发放的奖励项
  20. */
  21. public static function process(RewardGroupDto $groupDto, ?int $userId = null): array
  22. {
  23. $items = $groupDto->items;
  24. // 应用保底机制调整(如果有用户ID)
  25. if ($userId !== null) {
  26. $items = self::applyPityAdjustments($userId, $items);
  27. }
  28. // 如果不是随机发放,返回所有奖励项
  29. if (!$groupDto->isRandom) {
  30. return array_map([Item::class, 'processQuantity' ], $items);
  31. }
  32. // 随机发放:按权重选择指定数量的奖励项
  33. return self::processRandomSelection($groupDto, $items);
  34. }
  35. /**
  36. * 处理随机选择
  37. */
  38. private static function processRandomSelection(RewardGroupDto $groupDto, array $items): array
  39. {
  40. $randomCount = $groupDto->randomCount;
  41. // 分离必中项和普通项
  42. $guaranteedItems = [];
  43. $normalItems = [];
  44. foreach ($items as $item) {
  45. if ($item->isGuaranteed) {
  46. $guaranteedItems[] = $item;
  47. } else {
  48. $normalItems[] = $item;
  49. }
  50. }
  51. // 先选择必中项
  52. $selectedItems = array_map([ self::class, 'processQuantity' ], $guaranteedItems);
  53. // 如果必中项数量已经达到或超过随机数量,直接返回必中项
  54. if (count($selectedItems) >= $randomCount) {
  55. return array_slice($selectedItems, 0, $randomCount);
  56. }
  57. // 计算剩余需要选择的数量
  58. $remainingCount = $randomCount - count($selectedItems);
  59. // 如果没有普通项,直接返回必中项
  60. if (empty($normalItems)) {
  61. return $selectedItems;
  62. }
  63. // 按权重随机选择普通项
  64. $selectedNormalItems = self::selectByWeight($normalItems, $remainingCount);
  65. // 合并必中项和选中的普通项
  66. return array_merge($selectedItems, $selectedNormalItems);
  67. }
  68. /**
  69. * 按权重随机选择奖励项
  70. */
  71. private static function selectByWeight(array $items, int $count): array
  72. {
  73. $selectedItems = [];
  74. $availableItems = $items;
  75. for ($i = 0; $i < $count; $i++) {
  76. if (empty($availableItems)) {
  77. break;
  78. }
  79. // 计算总权重
  80. $totalWeight = array_sum(array_map(function ($item) {
  81. return $item->weight;
  82. }, $availableItems));
  83. if ($totalWeight <= 0) {
  84. // 如果总权重为0,随机选择
  85. shuffle($availableItems);
  86. $selectedItems[] = self::processQuantity(array_shift($availableItems));
  87. continue;
  88. }
  89. // 生成随机权重
  90. $randomWeight = mt_rand(1, $totalWeight * 100) / 100;
  91. $currentWeight = 0;
  92. // 选择奖励项
  93. foreach ($availableItems as $index => $item) {
  94. $currentWeight += $item->weight;
  95. if ($randomWeight <= $currentWeight) {
  96. $selectedItems[] = self::processQuantity($item);
  97. unset($availableItems[$index]);
  98. $availableItems = array_values($availableItems);
  99. break;
  100. }
  101. }
  102. }
  103. return $selectedItems;
  104. }
  105. /**
  106. * 处理奖励项数量(支持随机数量)
  107. * @deprecated
  108. */
  109. private static function processQuantity(RewardItemDto $item): RewardItemDto
  110. {
  111. return Item::processQuantity($item);
  112. }
  113. /**
  114. * 应用保底机制调整
  115. */
  116. private static function applyPityAdjustments(int $userId, array $items): array
  117. {
  118. try {
  119. // 将DTO转换为模型集合以便保底服务处理
  120. $rewardItemModels = collect($items)->map(function ($itemDto) {
  121. return GameRewardItem::find($itemDto->id);
  122. })->filter();
  123. // 应用保底机制调整权重
  124. $adjustedItems = PityService::applyPityAdjustments($userId, $rewardItemModels);
  125. // 将调整后的模型转换回DTO
  126. return $adjustedItems->map(function ($model) {
  127. return RewardItemDto::fromModel($model);
  128. })->toArray();
  129. } catch (\Exception $e) {
  130. Log::warning("权重选择模式:保底机制调整失败", [
  131. 'userId' => $userId,
  132. 'error' => $e->getMessage()
  133. ]);
  134. return $items; // 返回原始项目
  135. }
  136. }
  137. }