| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- <?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_MODE;
- use App\Module\Game\Events\RewardGrantedEvent;
- use App\Module\Game\Models\GameRewardGroup;
- use App\Module\Game\Models\GameRewardLog;
- use App\Module\Game\Services\PityService;
- use App\Module\Game\Logics\WeightSelectionReward;
- use App\Module\Game\Logics\IndependentProbabilityReward;
- use App\Module\Game\Logics\RewardProcessors\RewardProcessorDispatcher;
- use Exception;
- use Illuminate\Support\Facades\Log;
- use UCore\Db\Helper;
- use UCore\Exception\LogicException;
- /**
- * 奖励处理逻辑类
- *
- * 负责处理奖励的发放、记录等内部逻辑
- */
- 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,int $multiplier = 1): RewardResultDto
- {
- try {
- // 获取奖励组
- $groupDto = $this->getRewardGroup($groupIdOrCode);
- if (!$groupDto) {
- return RewardResultDto::fail("奖励组不存在: {$groupIdOrCode}");
- }
- // 确定要发放的奖励项
- $rewardItems = $this->determineRewardItems($groupDto, $userId,$multiplier);
- if (empty($rewardItems)) {
- return RewardResultDto::fail("奖励组中没有可发放的奖励项");
- }
- // 检查事务是否已开启
- Helper::check_tr();
- // 发放各类奖励
- foreach ($rewardItems as $item) {
- RewardProcessorDispatcher::process($userId, $item, $sourceType, $sourceId);
- }
- // 记录奖励日志
- $this->logReward($userId, $groupDto->id, $sourceType, $sourceId, $rewardItems);
- // 触发奖励发放事件
- 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) {
- 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
- * @param int|null $userId 用户ID(用于保底机制)
- * @return RewardItemDto[] 要发放的奖励项
- */
- private function determineRewardItems(RewardGroupDto $groupDto, ?int $userId = null,int $multiplier = 1): array
- {
- // 检查奖励模式
- $rewardMode = $groupDto->rewardMode ?? REWARD_MODE::WEIGHT_SELECTION->value;
- if ($rewardMode == REWARD_MODE::INDEPENDENT_PROBABILITY->value) {
- if($multiplier > 1){
- throw new LogicException('倍率,只支持权重模式');
- }
- // 独立概率模式
- return IndependentProbabilityReward::process($groupDto, $userId);
- } else {
- // 权重选择模式(默认)
- return WeightSelectionReward::process($groupDto, $userId,$multiplier);
- }
- }
- /**
- * 记录奖励日志
- *
- * @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
- ]);
- }
- /**
- * 模拟奖励发放(不实际发放,仅返回奖励结果)
- *
- * @param int|string $groupIdOrCode 奖励组ID或编码
- * @return RewardResultDto 奖励结果
- */
- public function simulateReward($groupIdOrCode): RewardResultDto
- {
- try {
- // 获取奖励组
- $groupDto = $this->getRewardGroup($groupIdOrCode);
- if (!$groupDto) {
- return RewardResultDto::fail("奖励组不存在: {$groupIdOrCode}");
- }
- // 确定要发放的奖励项(仅模拟,不实际发放)
- $rewardItems = $this->determineRewardItems($groupDto);
- if (empty($rewardItems)) {
- return RewardResultDto::fail("奖励组中没有可发放的奖励项");
- }
- // 返回模拟结果(不实际发放奖励)
- return RewardResultDto::success(
- 0, // 模拟用户ID
- $groupDto->id,
- $groupDto->code,
- $groupDto->name,
- 'simulate', // 模拟来源类型
- 0, // 模拟来源ID
- $rewardItems
- );
- } catch (\Exception $e) {
- return RewardResultDto::fail("模拟奖励失败: " . $e->getMessage());
- }
- }
- /**
- * 发放奖励(支持保底机制)
- *
- * @param int $userId 用户ID
- * @param int|string $groupIdOrCode 奖励组ID或编码
- * @param string $sourceType 来源类型
- * @param int $sourceId 来源ID
- * @param bool $enablePity 是否启用保底机制
- * @return RewardResultDto 奖励结果
- */
- public function grantRewardWithPity(int $userId, $groupIdOrCode, string $sourceType, int $sourceId, bool $enablePity = true): RewardResultDto
- {
- try {
- // 获取奖励组
- $groupDto = $this->getRewardGroup($groupIdOrCode);
- if (!$groupDto) {
- return RewardResultDto::fail("奖励组不存在: {$groupIdOrCode}");
- }
- // 确定要发放的奖励项(支持保底机制)
- $rewardItems = $this->determineRewardItemsWithPity($groupDto, $userId, $enablePity);
- if (empty($rewardItems)) {
- return RewardResultDto::fail("奖励组中没有可发放的奖励项");
- }
- // 检查事务是否已开启
- Helper::check_tr();
- // 发放各类奖励
- foreach ($rewardItems as $item) {
- RewardProcessorDispatcher::process($userId, $item, $sourceType, $sourceId);
- }
- // 更新保底计数(如果启用保底机制)
- if ($enablePity) {
- $obtainedItemIds = array_map(function ($item) {
- return $item->id;
- }, $rewardItems);
- PityService::updatePityCounts($userId, $groupDto->id, $obtainedItemIds);
- }
- // 记录奖励日志
- $this->logReward($userId, $groupDto->id, $sourceType, $sourceId, $rewardItems);
- // 触发奖励发放事件
- 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) {
- Log::error("发放奖励失败(保底机制)", [
- 'userId' => $userId,
- 'groupIdOrCode' => $groupIdOrCode,
- 'sourceType' => $sourceType,
- 'sourceId' => $sourceId,
- 'enablePity' => $enablePity,
- 'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString()
- ]);
- return RewardResultDto::fail("发放奖励失败: " . $e->getMessage());
- }
- }
- /**
- * 确定要发放的奖励项(支持保底机制)
- *
- * @param RewardGroupDto $groupDto 奖励组DTO
- * @param int $userId 用户ID
- * @param bool $enablePity 是否启用保底机制
- * @return RewardItemDto[] 要发放的奖励项
- */
- private function determineRewardItemsWithPity(RewardGroupDto $groupDto, int $userId, bool $enablePity): array
- {
- // 检查奖励模式
- $rewardMode = $groupDto->rewardMode ?? REWARD_MODE::WEIGHT_SELECTION->value;
- // 如果启用保底机制,传递用户ID;否则传递null
- $userIdForPity = $enablePity ? $userId : null;
- if ($rewardMode == REWARD_MODE::INDEPENDENT_PROBABILITY->value) {
- // 独立概率模式
- return IndependentProbabilityReward::process($groupDto, $userIdForPity);
- } else {
- // 权重选择模式(默认)
- return WeightSelectionReward::process($groupDto, $userIdForPity);
- }
- }
- }
|