WeightSelectionReward.php 5.4 KB

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