findOrFail($recipeId); // 检查配方是否激活 if (!$recipe->is_active) { throw new ValidateException("配方未激活"); } // 检查用户是否可以合成该配方 $canCraft = $recipe->canCraftByUser($userId); if (!$canCraft['can_craft']) { throw new ValidateException($canCraft['reason']); } // 检查消耗组是否存在 if (!$recipe->consume_group_id || !$recipe->consumeGroup) { throw new ValidateException("配方消耗组不存在"); } // 检查奖励组是否存在 if (!$recipe->reward_group_id || !$recipe->rewardGroup) { throw new ValidateException("配方奖励组不存在"); } // 获取合成数量 // 开启事务 DB::beginTransaction(); // 执行消耗组 $consumeResult = ConsumeService::executeConsume( $userId, $recipe->consume_group_id, REWARD_SOURCE_TYPE::CRAFT, $recipeId, true, // 检查消耗条件 $quantity // 使用数量作为倍数 ); if (!$consumeResult->success) { throw new LogicException("消耗失败: " . $consumeResult->message); } // 判断合成是否成功(基于配方成功率) $isSuccess = self::rollCraftSuccess($recipe->success_rate); $rewardItems = []; // 如果合成成功,执行奖励组 if ($isSuccess) { $rewardResult = RewardService::grantReward( $userId, $recipe->reward_group_id, REWARD_SOURCE_TYPE::CRAFT, $recipeId, $quantity ); if ($rewardResult->success) { $rewardItems = $rewardResult->items; } else { // 奖励发放失败,视为合成失败 $isSuccess = false; } } // 更新用户配方使用记录 $userRecipe = ItemUserRecipe::firstOrCreate( ['user_id' => $userId, 'recipe_id' => $recipeId], ['is_unlocked' => true, 'unlock_time' => now(), 'craft_count' => 0] ); $userRecipe->incrementCraftCount($quantity); // 创建合成记录 $craftLog = new ItemCraftLog([ 'user_id' => $userId, 'recipe_id' => $recipeId, 'materials' => $consumeResult->data['consumed'] ?? [], 'result_item_id' => $isSuccess && !empty($rewardItems) ? $rewardItems[0]->targetId : null, 'result_instance_id' => null, 'result_quantity' => $isSuccess && !empty($rewardItems) ? $rewardItems[0]->quantity : 0, 'is_success' => $isSuccess, 'craft_time' => now(), 'ip_address' => $options['ip_address'] ?? request()->ip(), 'device_info' => $options['device_info'] ?? request()->userAgent(), ]); $craftLog->save(); // 提交事务 DB::commit(); // 返回结果 return Res::success(); // return [ // 'success' => $isSuccess, // 'consumed' => $consumeResult['consumed'] ?? [], // 'rewards' => $isSuccess ? $rewardItems : [] // ]; } catch (\Exception $e) { // 回滚事务 if (DB::transactionLevel() > 0) { DB::rollBack(); } Log::error('合成物品失败', [ 'user_id' => $userId, 'recipe_id' => $recipeId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return Res::error('合成失败: ' . $e->getMessage()); } } /** * 获取用户可合成配方列表 * * @param int $userId 用户ID * @param array $filters 过滤条件 * @return Collection 配方列表 */ public static function getUserAvailableRecipes(int $userId, array $filters = []): Collection { $query = ItemRecipe::with(['consumeGroup', 'rewardGroup', 'conditionGroup']) ->where('is_active', true) ->whereHas('userRecipes', function ($q) use ($userId) { $q->where('user_id', $userId) ->where('is_unlocked', true); }); // 应用过滤条件 if (isset($filters['category_id'])) { $query->where('category_id', $filters['category_id']); } return $query->orderBy('sort_order', 'asc')->get(); } /** * 解锁用户配方 * * @param int $userId 用户ID * @param int $recipeId 配方ID * @return bool 是否成功 */ public static function unlockRecipe(int $userId, int $recipeId): bool { try { $recipe = ItemRecipe::findOrFail($recipeId); // 检查配方是否激活 if (!$recipe->is_active) { return false; } // 检查是否已解锁 $userRecipe = ItemUserRecipe::where('user_id', $userId) ->where('recipe_id', $recipeId) ->first(); if ($userRecipe && $userRecipe->is_unlocked) { return true; // 已经解锁 } // 创建或更新用户配方记录 if (!$userRecipe) { $userRecipe = new ItemUserRecipe([ 'user_id' => $userId, 'recipe_id' => $recipeId, 'is_unlocked' => true, 'unlock_time' => now(), 'craft_count' => 0 ]); $userRecipe->save(); } else { $userRecipe->is_unlocked = true; $userRecipe->unlock_time = now(); $userRecipe->save(); } return true; } catch (\Exception $e) { Log::error('解锁配方失败', [ 'user_id' => $userId, 'recipe_id' => $recipeId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return false; } } /** * 根据成功率判断合成是否成功 * * @param float $successRate 成功率(百分比,100.00 = 100%) * @return bool 是否成功 */ private static function rollCraftSuccess(float $successRate): bool { // 成功率小于等于0,必定失败 if ($successRate <= 0) { return false; } // 成功率大于等于100,必定成功 if ($successRate >= 100) { return true; } // 生成1-100的随机数进行概率判断 $randomNumber = mt_rand(1, 100); return $randomNumber <= $successRate; } }