find($consumeGroupCode) : GameConsumeGroup::with($withItems ? ['consumeItems'] : [])->where('code', $consumeGroupCode)->first(); if (!$consumeGroup) { return null; } return ConsumeGroupDto::fromModel($consumeGroup, $withItems); } catch (\Exception $e) { Log::error('获取消耗组失败', [ 'consume_group_code' => $consumeGroupCode, 'error' => $e->getMessage() ]); return null; } } /** * 获取消耗组并转换为Protobuf Deduct对象 * * @param int|string $consumeGroupCode 消耗组ID或编码 * @return \Uraus\Kku\Common\Deduct|null Protobuf Deduct对象,不存在时返回null */ public static function getConsumeGroupAsDeduct($consumeGroupCode): ?\Uraus\Kku\Common\Deduct { $consumeGroupDto = self::getConsumeGroup($consumeGroupCode, true); if (!$consumeGroupDto) { return null; } return ProtobufConverter::convertConsumeGroupToDeduct($consumeGroupDto); } /** * 检查用户是否满足消耗组的条件 * * @param int $userId 用户ID * @param string|int $consumeGroupCode 消耗组编码或ID * @param float $multiplier 倍数,用于验证几倍消耗,默认为1 * @return array 检查结果,包含success字段表示是否满足条件,message字段表示错误信息 */ public static function checkConsume(int $userId, $consumeGroupCode, float $multiplier = 1.0): Res { try { // 获取消耗组 /** * @var GameConsumeGroup $consumeGroup */ $consumeGroup = is_numeric($consumeGroupCode) ? GameConsumeGroup::find($consumeGroupCode) : GameConsumeGroup::where('code', $consumeGroupCode)->first(); if (!$consumeGroup) { return Res::error("消耗组不存在: {$consumeGroupCode}"); } // 获取消耗组中的所有消耗项 $consumeItems = $consumeGroup->consumeItems; if ($consumeItems->isEmpty()) { return Res::success(); } // 检查每个消耗项 foreach ($consumeItems as $item) { $checkResult = self::checkConsumeItem($userId, $item, $multiplier); if (!$checkResult['success']) { return Res::error($checkResult['message'], $checkResult); } } // 所有条件都满足,返回成功; 返回消耗组信息 return Res::success('', ConsumeGroupService::consumeItems2Array($consumeItems) ); } catch (\Exception $e) { Log::error('检查消耗条件失败', [ 'user_id' => $userId, 'consume_group' => $consumeGroupCode, 'error' => $e->getMessage() ]); return Res::error('检查消耗条件时发生错误: ' . $e->getMessage()); } } /** * 执行消耗 * * @param int $userId 用户ID * @param string|int $consumeGroupCode 消耗组编码或ID * @param string $source 消耗来源 * @param int $sourceId 消耗来源ID * @param bool $check 是否先检查消耗条件 * @param float $multiplier 倍数,用于执行几倍消耗,默认为1 * @return Res 执行结果 */ public static function executeConsume(int $userId, $consumeGroupCode, REWARD_SOURCE_TYPE $source, int $sourceId = 0, $check = true, float $multiplier = 1.0): Res { if ($check) { // 先检查是否满足消耗条件(使用相同的倍数) $checkResult = self::checkConsume($userId, $consumeGroupCode, $multiplier); if (!$checkResult->success) { return $checkResult; } } try { // 获取消耗组 /** * @var GameConsumeGroup $consumeGroup */ $consumeGroup = is_numeric($consumeGroupCode) ? GameConsumeGroup::find($consumeGroupCode) : GameConsumeGroup::where('code', $consumeGroupCode)->first(); if (!$consumeGroup) { return Res::error("消耗组不存在: {$consumeGroupCode}"); } // 获取消耗组中的所有消耗项 $consumeItems = $consumeGroup->consumeItems; if ($consumeItems->isEmpty()) { return Res::success('消耗组为空,无需执行消耗', [ 'consume_group' => [ 'id' => $consumeGroup->id, 'code' => $consumeGroup->code, 'name' => $consumeGroup->name ], 'list' => [] ]); } // 验证事务是否已开启(由调用者负责事务管理) \UCore\Db\Helper::check_tr(); // 执行每个消耗项 foreach ($consumeItems as $item) { $consumeResult = ProcessorDispatcher::process($userId, $item, $source->valueString(), $sourceId, $multiplier); if (!$consumeResult['success']) { return Res::error($consumeResult['message'], $consumeResult); } } return Res::success('消耗执行成功', [ 'consume_group' => [ 'id' => $consumeGroup->id, 'code' => $consumeGroup->code, 'name' => $consumeGroup->name ], 'list' => ConsumeGroupService::consumeItems2Array($consumeItems) ]); } catch (\Exception $e) { Log::error('执行消耗失败', [ 'user_id' => $userId, 'consume_group' => $consumeGroupCode, 'source' => $source, 'source_id' => $sourceId, 'error' => $e->getMessage() ]); return Res::error('执行消耗时发生错误: ' . $e->getMessage()); } } /** * 检查单个消耗项 * * @param int $userId 用户ID * @param GameConsumeItem $consumeItem 消耗项 * @param float $multiplier 倍数,用于验证几倍消耗,默认为1 * @return array 检查结果 */ protected static function checkConsumeItem(int $userId, GameConsumeItem $consumeItem, float $multiplier = 1.0): array { switch ($consumeItem->consume_type) { case CONSUME_TYPE::ITEM->value: $result = ItemConsume::checkItemConsume($userId,$consumeItem,$multiplier); // 将 Res 对象转换为数组格式 return [ 'success' => $result->success, 'message' => $result->message, 'item_id' => $result->data['item_id'] ?? null, 'required' => $result->data['required'] ?? null, 'actual' => $result->data['actual'] ?? null ]; case CONSUME_TYPE::FUND_CONFIG->value: $result = FundConfigConsume::checkFundConfigConsume($userId,$consumeItem,$multiplier); // 将 Res 对象转换为数组格式 return [ 'success' => $result->success, 'message' => $result->message, 'fund_config_id' => $result->data['fund_config_id'] ?? null, 'required' => $result->data['required'] ?? null, 'actual' => $result->data['actual'] ?? null ]; case CONSUME_TYPE::CURRENCY->value: return CurrencyConsume::checkCurrencyConsume($userId,$consumeItem,$multiplier); case CONSUME_TYPE::FUND_CONFIGS->value: $result = FundConfigsConsume::checkFundConfigsConsume($userId,$consumeItem,$multiplier); // 将 Res 对象转换为数组格式 return [ 'success' => $result->success, 'message' => $result->message, 'fund_config_ids' => $result->data['fund_config_ids'] ?? null, 'required' => $result->data['required'] ?? null, 'actual' => $result->data['actual'] ?? null ]; default: return [ 'success' => false, 'message' => "不支持的消耗类型: {$consumeItem->consume_type}" ]; } } }