| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- <?php
- namespace App\Module\Game\Logics;
- use App\Module\Game\Dtos\RewardGroupDto;
- use App\Module\Game\Dtos\RewardItemDto;
- use App\Module\Game\Dtos\RewardResultDto;
- use App\Module\Game\Enums\REWARD_TYPE;
- use App\Module\Game\Events\RewardGrantedEvent;
- use App\Module\Game\Models\GameRewardGroup;
- use App\Module\Game\Models\GameRewardItem;
- use App\Module\Game\Models\GameRewardLog;
- use App\Module\GameItems\Services\ItemService;
- use Exception;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Log;
- /**
- * 奖励处理逻辑类
- *
- * 负责处理奖励的发放、记录等内部逻辑
- */
- class RewardLogic
- {
- /**
- * 获取奖励组
- *
- * @param int|string $groupIdOrCode 奖励组ID或编码
- * @return RewardGroupDto|null
- */
- public function getRewardGroup($groupIdOrCode): ?RewardGroupDto
- {
- $query = GameRewardGroup::with('rewardItems');
- if (is_numeric($groupIdOrCode)) {
- $group = $query->find($groupIdOrCode);
- } else {
- $group = $query->where('code', $groupIdOrCode)->first();
- }
- if (!$group) {
- return null;
- }
- return RewardGroupDto::fromModel($group, true);
- }
- /**
- * 发放奖励
- *
- * @param int $userId 用户ID
- * @param int|string $groupIdOrCode 奖励组ID或编码
- * @param string $sourceType 来源类型
- * @param int $sourceId 来源ID
- * @return RewardResultDto 奖励结果
- */
- public function grantReward(int $userId, $groupIdOrCode, string $sourceType, int $sourceId): RewardResultDto
- {
- try {
- // 获取奖励组
- $groupDto = $this->getRewardGroup($groupIdOrCode);
- if (!$groupDto) {
- return RewardResultDto::fail("奖励组不存在: {$groupIdOrCode}");
- }
- // 确定要发放的奖励项
- $rewardItems = $this->determineRewardItems($groupDto);
- if (empty($rewardItems)) {
- return RewardResultDto::fail("奖励组中没有可发放的奖励项");
- }
- // 开始事务
- DB::beginTransaction();
- // 发放各类奖励
- foreach ($rewardItems as $item) {
- $this->processRewardItem($userId, $item);
- }
- // 记录奖励日志
- $this->logReward($userId, $groupDto->id, $sourceType, $sourceId, $rewardItems);
- // 提交事务
- DB::commit();
- // 触发奖励发放事件
- event(new RewardGrantedEvent($userId, $groupDto->id, $groupDto->code, $sourceType, $sourceId, $rewardItems));
- // 返回成功结果
- return RewardResultDto::success(
- $userId,
- $groupDto->id,
- $groupDto->code,
- $groupDto->name,
- $sourceType,
- $sourceId,
- $rewardItems
- );
- } catch (Exception $e) {
- // 回滚事务
- DB::rollBack();
- Log::error("发放奖励失败", [
- 'userId' => $userId,
- 'groupIdOrCode' => $groupIdOrCode,
- 'sourceType' => $sourceType,
- 'sourceId' => $sourceId,
- 'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString()
- ]);
- return RewardResultDto::fail("发放奖励失败: " . $e->getMessage());
- }
- }
- /**
- * 确定要发放的奖励项
- *
- * @param RewardGroupDto $groupDto 奖励组DTO
- * @return RewardItemDto[] 要发放的奖励项
- */
- private function determineRewardItems(RewardGroupDto $groupDto): array
- {
- $items = $groupDto->items;
-
- // 如果不是随机发放,返回所有奖励项
- if (!$groupDto->isRandom) {
- return $items;
- }
-
- // 如果是随机发放,按权重随机选择指定数量的奖励项
- $selectedItems = [];
- $guaranteedItems = [];
- $normalItems = [];
-
- // 先分离必中项和普通项
- foreach ($items as $item) {
- if ($item->isGuaranteed) {
- $guaranteedItems[] = $item;
- } else {
- $normalItems[] = $item;
- }
- }
-
- // 先选择必中项
- $selectedItems = $guaranteedItems;
-
- // 如果必中项数量已经达到或超过随机数量,直接返回必中项
- if (count($selectedItems) >= $groupDto->randomCount) {
- return array_slice($selectedItems, 0, $groupDto->randomCount);
- }
-
- // 计算剩余需要选择的数量
- $remainingCount = $groupDto->randomCount - count($selectedItems);
-
- // 如果没有普通项,直接返回必中项
- if (empty($normalItems)) {
- return $selectedItems;
- }
-
- // 按权重随机选择普通项
- $totalWeight = array_sum(array_map(function ($item) {
- return $item->weight;
- }, $normalItems));
-
- // 如果总权重为0,随机选择
- if ($totalWeight <= 0) {
- shuffle($normalItems);
- $selectedNormalItems = array_slice($normalItems, 0, $remainingCount);
- } else {
- // 按权重随机选择
- $selectedNormalItems = [];
- for ($i = 0; $i < $remainingCount; $i++) {
- if (empty($normalItems)) {
- break;
- }
-
- $randomWeight = mt_rand(1, $totalWeight * 100) / 100;
- $currentWeight = 0;
-
- foreach ($normalItems as $key => $item) {
- $currentWeight += $item->weight;
- if ($randomWeight <= $currentWeight) {
- $selectedNormalItems[] = $item;
- $totalWeight -= $item->weight;
- unset($normalItems[$key]);
- $normalItems = array_values($normalItems);
- break;
- }
- }
- }
- }
-
- // 合并必中项和选中的普通项
- return array_merge($selectedItems, $selectedNormalItems);
- }
- /**
- * 处理单个奖励项
- *
- * @param int $userId 用户ID
- * @param RewardItemDto $item 奖励项
- * @return void
- */
- private function processRewardItem(int $userId, RewardItemDto $item): void
- {
- switch ($item->rewardType) {
- case REWARD_TYPE::ITEM:
- // 发放物品奖励
- ItemService::addItem($userId, $item->targetId, $item->quantity, [
- 'param1' => $item->param1,
- 'param2' => $item->param2,
- 'source' => 'reward',
- 'extra_data' => $item->extraData
- ]);
- break;
-
- case REWARD_TYPE::CURRENCY:
- // 发放货币奖励
- // 这里需要调用货币服务,根据实际情况实现
- // CurrencyService::addCurrency($userId, $item->targetId, $item->quantity, [
- // 'source' => 'reward',
- // 'param1' => $item->param1,
- // 'param2' => $item->param2
- // ]);
- break;
-
- case REWARD_TYPE::PET_EXP:
- // 发放宠物经验奖励
- // 这里需要调用宠物服务,根据实际情况实现
- // PetService::addExp($userId, $item->targetId, $item->quantity);
- break;
-
- case REWARD_TYPE::PET_ENERGY:
- // 发放宠物体力奖励
- // 这里需要调用宠物服务,根据实际情况实现
- // PetService::addEnergy($userId, $item->targetId, $item->quantity);
- break;
-
- default:
- // 其他类型奖励,记录日志
- Log::warning("未处理的奖励类型", [
- 'userId' => $userId,
- 'rewardType' => $item->rewardType,
- 'targetId' => $item->targetId,
- 'quantity' => $item->quantity
- ]);
- break;
- }
- }
- /**
- * 记录奖励日志
- *
- * @param int $userId 用户ID
- * @param int $groupId 奖励组ID
- * @param string $sourceType 来源类型
- * @param int $sourceId 来源ID
- * @param RewardItemDto[] $items 发放的奖励项
- * @return GameRewardLog
- */
- private function logReward(int $userId, int $groupId, string $sourceType, int $sourceId, array $items): GameRewardLog
- {
- // 将DTO转换为可存储的数组
- $itemsData = array_map(function (RewardItemDto $item) {
- return [
- 'id' => $item->id,
- 'reward_type' => $item->rewardType,
- 'target_id' => $item->targetId,
- 'param1' => $item->param1,
- 'param2' => $item->param2,
- 'quantity' => $item->quantity,
- 'extra_data' => $item->extraData
- ];
- }, $items);
- // 创建日志记录
- return GameRewardLog::create([
- 'user_id' => $userId,
- 'group_id' => $groupId,
- 'source_type' => $sourceType,
- 'source_id' => $sourceId,
- 'reward_items' => $itemsData
- ]);
- }
- }
|