[['goods_id']]] * @param int $userId 用户ID * @return array 获得的抽奖机会信息 * @throws Exception */ public static function checkAndGrantChanceForOrder($orderInfo, $userId) { if (empty($orderInfo) || empty($userId)) { throw new Exception('订单信息或用户ID不能为空'); } $grantedChances = []; try { // 获取所有正在进行的抽奖活动 $activities = LotteryService::getRunningActivities(); foreach ($activities as $activity) { try { $chances = static::processActivityForOrder($activity, $orderInfo, $userId); if ($chances > 0) { $grantedChances[] = [ 'activity_id' => $activity->id, 'activity_name' => $activity->name, 'chances' => $chances, 'granted_time' => time() ]; } } catch (Exception $e) { // 记录错误但不影响其他活动的处理 trace("抽奖机会分发失败 - 活动ID: {$activity->id}, 用户ID: {$userId}, 错误: " . $e->getMessage(), 'error'); } } } catch (Exception $e) { trace("订单抽奖机会分发失败 - 用户ID: {$userId}, 错误: " . $e->getMessage(), 'error'); throw $e; } return $grantedChances; } /** * 充值完成后检查并分发抽奖机会 * * @param array $rechargeInfo 充值信息 ['amount', 'type' => 'recharge'] * @param int $userId 用户ID * @return array 获得的抽奖机会信息 * @throws Exception */ public static function checkAndGrantChanceForRecharge($rechargeInfo, $userId) { if (empty($rechargeInfo) || empty($userId)) { throw new Exception('充值信息或用户ID不能为空'); } $grantedChances = []; try { // 获取所有正在进行的抽奖活动 $activities = LotteryService::getRunningActivities(); foreach ($activities as $activity) { try { $chances = static::processActivityForRecharge($activity, $rechargeInfo, $userId); if ($chances > 0) { $grantedChances[] = [ 'activity_id' => $activity->id, 'activity_name' => $activity->name, 'chances' => $chances, 'granted_time' => time() ]; } } catch (Exception $e) { // 记录错误但不影响其他活动的处理 trace("充值抽奖机会分发失败 - 活动ID: {$activity->id}, 用户ID: {$userId}, 错误: " . $e->getMessage(), 'error'); } } } catch (Exception $e) { trace("充值抽奖机会分发失败 - 用户ID: {$userId}, 错误: " . $e->getMessage(), 'error'); throw $e; } return $grantedChances; } /** * 手动给用户增加抽奖机会(管理员操作) * * @param int $activityId 活动ID * @param int $userId 用户ID * @param int $chances 机会次数 * @param string $reason 原因 * @param int $adminId 管理员ID * @return bool * @throws Exception */ public static function manualGrantChance($activityId, $userId, $chances, $reason = '', $adminId = 0) { if ($chances <= 0) { throw new Exception('抽奖机会数量必须大于0'); } $activity = LotteryActivity::find($activityId); if (!$activity) { throw new Exception('活动不存在'); } $user = User::find($userId); if (!$user) { throw new Exception('用户不存在'); } $detail = [ 'trigger_type' => static::TRIGGER_TYPE_MANUAL, 'reason' => $reason, 'admin_id' => $adminId, 'granted_time' => time() ]; return static::grantChanceToUser($activityId, $userId, $chances, $detail); } /** * 批量初始化用户抽奖机会(活动开始时) * * @param int $activityId 活动ID * @param array $userIds 用户ID数组 * @param int $initChances 初始机会数 * @param int $batchSize 批量处理大小 * @return array 处理结果 * @throws Exception */ public static function batchInitChances($activityId, $userIds, $initChances = 1, $batchSize = null) { if (empty($userIds)) { throw new Exception('用户ID列表不能为空'); } $activity = LotteryActivity::find($activityId); if (!$activity) { throw new Exception('活动不存在'); } $batchSize = $batchSize ?: static::DEFAULT_BATCH_SIZE; $batchSize = min($batchSize, static::MAX_BATCH_SIZE); $result = [ 'total_users' => count($userIds), 'success_count' => 0, 'failed_count' => 0, 'errors' => [] ]; // 分批处理 $batches = array_chunk($userIds, $batchSize); foreach ($batches as $batch) { try { $success = static::batchCreateChances($activityId, $batch, $initChances); if ($success) { $result['success_count'] += count($batch); } else { $result['failed_count'] += count($batch); $result['errors'][] = "批次处理失败: " . implode(',', $batch); } } catch (Exception $e) { $result['failed_count'] += count($batch); $result['errors'][] = "批次处理异常: " . $e->getMessage(); } } return $result; } // ============ 私有处理方法 ============ /** * 处理订单相关的活动 * * @param LotteryActivity $activity 活动对象 * @param array $orderInfo 订单信息 * @param int $userId 用户ID * @return int 获得的抽奖机会数量 */ private static function processActivityForOrder($activity, $orderInfo, $userId) { // 检查用户是否符合活动参与资格 if (!static::checkUserQualification($activity, $userId)) { return 0; } // 获取活动的参与条件 $conditions = static::getValidConditions($activity->id); $totalChances = 0; foreach ($conditions as $condition) { // 跳过充值条件 if ($condition->type == LotteryEnum::CONDITION_TYPE_RECHARGE_AMOUNT) { continue; } $chances = static::processConditionForOrder($condition, $orderInfo, $userId); $totalChances += $chances; } // 如果获得了抽奖机会,记录到数据库 if ($totalChances > 0) { static::grantChanceToUser($activity->id, $userId, $totalChances, [ 'trigger_type' => static::TRIGGER_TYPE_ORDER, 'order_id' => $orderInfo['id'] ?? 0, 'order_amount' => $orderInfo['total_amount'] ?? 0, 'granted_time' => time() ]); } return $totalChances; } /** * 处理充值相关的活动 * * @param LotteryActivity $activity 活动对象 * @param array $rechargeInfo 充值信息 * @param int $userId 用户ID * @return int 获得的抽奖机会数量 */ private static function processActivityForRecharge($activity, $rechargeInfo, $userId) { // 检查用户是否符合活动参与资格 if (!static::checkUserQualification($activity, $userId)) { return 0; } // 获取活动的参与条件 $conditions = static::getValidConditions($activity->id); $totalChances = 0; foreach ($conditions as $condition) { // 只处理充值条件 if ($condition->type != LotteryEnum::CONDITION_TYPE_RECHARGE_AMOUNT) { continue; } $chances = static::processConditionForRecharge($condition, $rechargeInfo, $userId); $totalChances += $chances; } // 如果获得了抽奖机会,记录到数据库 if ($totalChances > 0) { static::grantChanceToUser($activity->id, $userId, $totalChances, [ 'trigger_type' => static::TRIGGER_TYPE_RECHARGE, 'recharge_amount' => $rechargeInfo['amount'] ?? 0, 'granted_time' => time() ]); } return $totalChances; } /** * 处理订单条件 * * @param LotteryCondition $condition 条件对象 * @param array $orderInfo 订单信息 * @param int $userId 用户ID * @return int 获得的抽奖机会数量 */ private static function processConditionForOrder($condition, $orderInfo, $userId) { $chances = 0; switch ($condition->type) { case LotteryEnum::CONDITION_TYPE_BUY_GOODS: if (static::validateGoodsCondition($condition, $orderInfo)) { $chances = static::getRewardTimes($condition); } break; case LotteryEnum::CONDITION_TYPE_ORDER_AMOUNT: if (static::validateOrderAmountCondition($condition, $orderInfo)) { $chances = static::getRewardTimes($condition); // 如果可重复获得,根据金额倍数计算次数 if (static::canRepeat($condition)) { $multiple = floor($orderInfo['total_amount'] / $condition->condition_value); $chances *= $multiple; } } break; case LotteryEnum::CONDITION_TYPE_TOTAL_AMOUNT: if (static::validateAccumulateCondition($condition, $userId, $condition->activity_id)) { // 检查是否已经因为累计消费获得过机会 if (!static::hasGrantedForAccumulate($condition->activity_id, $userId)) { $chances = static::getRewardTimes($condition); } } break; } return $chances; } /** * 处理充值条件 * * @param LotteryCondition $condition 条件对象 * @param array $rechargeInfo 充值信息 * @param int $userId 用户ID * @return int 获得的抽奖机会数量 */ private static function processConditionForRecharge($condition, $rechargeInfo, $userId) { $chances = 0; if (static::validateRechargeCondition($condition, ['type' => 'recharge', 'amount' => $rechargeInfo['amount']])) { $chances = static::getRewardTimes($condition); // 如果可重复获得,根据金额倍数计算次数 if (static::canRepeat($condition)) { $multiple = floor($rechargeInfo['amount'] / $condition->condition_value); $chances *= $multiple; } } return $chances; } // ============ 用户资格检查方法 ============ /** * 检查用户资格 * * @param LotteryActivity $activity 活动对象 * @param int $userId 用户ID * @return bool */ private static function checkUserQualification($activity, $userId) { // 检查用户群体限制 switch ($activity->user_limit_type) { case LotteryEnum::USER_LIMIT_ALL: return true; case LotteryEnum::USER_LIMIT_LEVEL: return static::checkUserLevel($userId, $activity->user_limit_value); case LotteryEnum::USER_LIMIT_TAG: return static::checkUserTag($userId, $activity->user_limit_value); default: return false; } } /** * 检查用户等级 * * @param int $userId 用户ID * @param mixed $limitValue 限制值 * @return bool */ private static function checkUserLevel($userId, $limitValue) { if (empty($limitValue)) { return true; } $user = User::find($userId); if (!$user) { return false; } $limitLevels = is_array($limitValue) ? $limitValue : [$limitValue]; return in_array($user->level, $limitLevels); } /** * 检查用户标签 * * @param int $userId 用户ID * @param mixed $limitValue 限制值 * @return bool */ private static function checkUserTag($userId, $limitValue) { if (empty($limitValue)) { return true; } // TODO: 根据实际的用户标签系统实现 // 这里需要根据具体的用户标签表结构来实现 // 暂时返回true,避免影响现有功能 return true; } /** * 检查是否已因累计消费获得过机会 * * @param int $activityId 活动ID * @param int $userId 用户ID * @return bool */ private static function hasGrantedForAccumulate($activityId, $userId) { $userChance = static::getUserChance($activityId, $userId); if (!$userChance) { return false; } $getDetail = $userChance->get_detail_data; foreach ($getDetail as $detail) { if (($detail['trigger_type'] ?? '') === static::TRIGGER_TYPE_ACCUMULATE) { return true; } } return false; } // ============ 条件验证方法 ============ /** * 验证订单是否满足条件 * * @param LotteryCondition $condition 条件对象 * @param array $orderInfo 订单信息 * @return bool */ public static function validateOrder(LotteryCondition $condition, $orderInfo) { switch ($condition->type) { case LotteryEnum::CONDITION_TYPE_BUY_GOODS: return static::validateGoodsCondition($condition, $orderInfo); case LotteryEnum::CONDITION_TYPE_ORDER_AMOUNT: return static::validateOrderAmountCondition($condition, $orderInfo); case LotteryEnum::CONDITION_TYPE_RECHARGE_AMOUNT: return static::validateRechargeCondition($condition, $orderInfo); default: return false; } } /** * 验证商品条件 * * @param LotteryCondition $condition 条件对象 * @param array $orderInfo 订单信息 * @return bool */ public static function validateGoodsCondition(LotteryCondition $condition, $orderInfo) { if (empty($orderInfo['goods']) || empty($condition->goods_ids_list)) { return false; } $orderGoodsIds = array_column($orderInfo['goods'], 'goods_id'); $conditionGoodsIds = $condition->goods_ids_list; $intersection = array_intersect($orderGoodsIds, $conditionGoodsIds); if ($condition->goods_rule == LotteryEnum::GOODS_RULE_INCLUDE) { // 指定商品参与:订单中必须包含指定商品 return !empty($intersection); } else { // 指定商品不可参与:订单中不能包含指定商品 return empty($intersection); } } /** * 验证订单金额条件 * * @param LotteryCondition $condition 条件对象 * @param array $orderInfo 订单信息 * @return bool */ public static function validateOrderAmountCondition(LotteryCondition $condition, $orderInfo) { $orderAmount = $orderInfo['total_amount'] ?? 0; return $orderAmount >= $condition->condition_value; } /** * 验证充值条件 * * @param LotteryCondition $condition 条件对象 * @param array $orderInfo 订单信息 * @return bool */ public static function validateRechargeCondition(LotteryCondition $condition, $orderInfo) { if (($orderInfo['type'] ?? '') !== 'recharge') { return false; } $rechargeAmount = $orderInfo['amount'] ?? 0; return $rechargeAmount >= $condition->condition_value; } /** * 验证累计消费条件 * * @param LotteryCondition $condition 条件对象 * @param int $userId 用户ID * @param int $activityId 活动ID * @return bool */ public static function validateAccumulateCondition(LotteryCondition $condition, $userId, $activityId) { $activity = LotteryActivity::find($activityId); if (!$activity) { return false; } // 计算活动期间用户累计消费 $totalAmount = Order::where('user_id', $userId) ->where('status', 'paid') ->where('createtime', '>=', $activity->start_time) ->where('createtime', '<=', $activity->end_time) ->sum('total_amount'); return $totalAmount >= $condition->condition_value; } // ============ 条件管理方法 ============ /** * 获取活动的有效条件 * * @param int $activityId 活动ID * @return array */ public static function getValidConditions($activityId) { return LotteryCondition::where('activity_id', $activityId) ->where('status', 1) ->order('id', 'asc') ->select(); } /** * 检查条件是否可重复获得奖励 * * @param LotteryCondition $condition 条件对象 * @return bool */ public static function canRepeat(LotteryCondition $condition) { return $condition->is_repeatable == 1; } /** * 获取奖励次数 * * @param LotteryCondition $condition 条件对象 * @return int */ public static function getRewardTimes(LotteryCondition $condition) { return $condition->reward_times ?: 1; } /** * 批量验证订单条件 * * @param int $activityId 活动ID * @param array $orderInfo 订单信息 * @param int $userId 用户ID * @return array 满足的条件列表 */ public static function batchValidateConditions($activityId, $orderInfo, $userId) { $conditions = static::getValidConditions($activityId); $validConditions = []; foreach ($conditions as $condition) { if (static::validateOrder($condition, $orderInfo)) { $validConditions[] = [ 'condition_id' => $condition->id, 'condition_name' => $condition->name, 'condition_type' => $condition->type, 'condition_type_text' => $condition->type_text, 'reward_times' => static::getRewardTimes($condition), 'can_repeat' => static::canRepeat($condition) ]; } } return $validConditions; } /** * 获取条件统计信息 * * @param int $activityId 活动ID * @return array */ public static function getConditionStatistics($activityId) { $conditions = static::getValidConditions($activityId); $statistics = [ 'total_conditions' => count($conditions), 'goods_conditions' => 0, 'amount_conditions' => 0, 'recharge_conditions' => 0, 'accumulate_conditions' => 0 ]; foreach ($conditions as $condition) { switch ($condition->type) { case LotteryEnum::CONDITION_TYPE_BUY_GOODS: $statistics['goods_conditions']++; break; case LotteryEnum::CONDITION_TYPE_ORDER_AMOUNT: $statistics['amount_conditions']++; break; case LotteryEnum::CONDITION_TYPE_RECHARGE_AMOUNT: $statistics['recharge_conditions']++; break; case LotteryEnum::CONDITION_TYPE_TOTAL_AMOUNT: $statistics['accumulate_conditions']++; break; } } return $statistics; } // ============ 用户机会管理方法 ============ /** * 获取用户抽奖机会 * * @param int $activityId 活动ID * @param int $userId 用户ID * @return LotteryUserChance|null */ public static function getUserChance($activityId, $userId) { return LotteryUserChance::where('activity_id', $activityId) ->where('user_id', $userId) ->find(); } /** * 获取用户在指定活动中的抽奖机会详情 * * @param int $activityId 活动ID * @param int $userId 用户ID * @return array */ public static function getUserChanceDetail($activityId, $userId) { $userChance = static::getUserChance($activityId, $userId); if (!$userChance) { return [ 'total_chances' => 0, 'used_chances' => 0, 'remain_chances' => 0, 'get_detail' => [] ]; } return [ 'total_chances' => $userChance->total_chances, 'used_chances' => $userChance->used_chances, 'remain_chances' => $userChance->remain_chances, 'last_get_time' => $userChance->last_get_time, 'last_use_time' => $userChance->last_use_time, 'get_detail' => $userChance->get_detail_data ]; } /** * 给用户分发抽奖机会 * * @param int $activityId 活动ID * @param int $userId 用户ID * @param int $chances 机会次数 * @param array $detail 获得详情 * @return LotteryUserChance|bool */ private static function grantChanceToUser($activityId, $userId, $chances, $detail) { return static::addChance($activityId, $userId, $chances, $detail); } /** * 增加抽奖机会 * * @param int $activityId 活动ID * @param int $userId 用户ID * @param int $times 机会次数 * @param array $detail 获得详情 * @return LotteryUserChance|bool */ public static function addChance($activityId, $userId, $times = 1, $detail = []) { if ($times <= 0) { return false; } $chance = static::getUserChance($activityId, $userId); if (!$chance) { // 创建新记录 $data = [ 'activity_id' => $activityId, 'user_id' => $userId, 'total_chances' => $times, 'used_chances' => 0, 'remain_chances' => $times, 'last_get_time' => time(), 'get_detail' => json_encode([$detail]) ]; return LotteryUserChance::create($data); } else { // 更新现有记录 $chance->total_chances += $times; $chance->remain_chances += $times; $chance->last_get_time = time(); // 更新获得详情 $getDetail = $chance->get_detail_data; $getDetail[] = $detail; $chance->get_detail = json_encode($getDetail); return $chance->save() ? $chance : false; } } /** * 使用抽奖机会 * * @param LotteryUserChance $userChance 用户机会对象 * @param int $times 使用次数 * @return bool */ public static function useChance(LotteryUserChance $userChance, $times = 1) { if ($userChance->remain_chances < $times) { return false; } $userChance->used_chances += $times; $userChance->remain_chances -= $times; $userChance->last_use_time = time(); return $userChance->save(); } /** * 检查是否有剩余机会 * * @param LotteryUserChance $userChance 用户机会对象 * @param int $times 需要的机会次数 * @return bool */ public static function hasChance(LotteryUserChance $userChance, $times = 1) { return $userChance->remain_chances >= $times; } /** * 重置抽奖机会(用于测试或特殊情况) * * @param LotteryUserChance $userChance 用户机会对象 * @return bool */ public static function resetChance(LotteryUserChance $userChance) { $userChance->used_chances = 0; $userChance->remain_chances = $userChance->total_chances; return $userChance->save(); } // ============ 统计和查询方法 ============ /** * 获取活动总参与人数 * * @param int $activityId 活动ID * @return int */ public static function getActivityParticipants($activityId) { return LotteryUserChance::where('activity_id', $activityId)->count(); } /** * 获取用户在多个活动中的机会统计 * * @param int $userId 用户ID * @param array $activityIds 活动ID数组 * @return array */ public static function getUserChancesStats($userId, $activityIds = []) { $query = LotteryUserChance::where('user_id', $userId); if (!empty($activityIds)) { $query->where('activity_id', 'in', $activityIds); } return $query->field([ 'activity_id', 'total_chances', 'used_chances', 'remain_chances' ]) ->select(); } /** * 获取用户在所有活动中的机会概览 * * @param int $userId 用户ID * @return array */ public static function getUserAllChancesOverview($userId) { $chances = LotteryUserChance::where('user_id', $userId) ->with(['activity']) ->select(); $overview = [ 'total_activities' => count($chances), 'total_chances' => 0, 'total_used' => 0, 'total_remain' => 0, 'activities' => [] ]; foreach ($chances as $chance) { $overview['total_chances'] += $chance->total_chances; $overview['total_used'] += $chance->used_chances; $overview['total_remain'] += $chance->remain_chances; $overview['activities'][] = [ 'activity_id' => $chance->activity_id, 'activity_name' => $chance->activity->name ?? '', 'total_chances' => $chance->total_chances, 'used_chances' => $chance->used_chances, 'remain_chances' => $chance->remain_chances, 'last_get_time' => $chance->last_get_time, 'last_use_time' => $chance->last_use_time ]; } return $overview; } /** * 检查用户是否可以参与活动 * * @param int $activityId 活动ID * @param int $userId 用户ID * @return bool */ public static function canUserParticipate($activityId, $userId) { $userChance = static::getUserChance($activityId, $userId); return $userChance && static::hasChance($userChance, 1); } /** * 获取活动参与用户列表 * * @param int $activityId 活动ID * @param int $page 页码 * @param int $limit 每页数量 * @return array */ public static function getActivityParticipantsList($activityId, $page = 1, $limit = 20) { return LotteryUserChance::where('activity_id', $activityId) ->with(['user']) ->order('createtime', 'desc') ->page($page, $limit) ->select(); } /** * 获取活动参与统计 * * @param int $activityId 活动ID * @return array */ public static function getActivityParticipationStats($activityId) { $stats = LotteryUserChance::where('activity_id', $activityId) ->field([ 'COUNT(*) as total_participants', 'SUM(total_chances) as total_chances_granted', 'SUM(used_chances) as total_chances_used', 'SUM(remain_chances) as total_chances_remain' ]) ->find(); return [ 'total_participants' => $stats['total_participants'] ?? 0, 'total_chances_granted' => $stats['total_chances_granted'] ?? 0, 'total_chances_used' => $stats['total_chances_used'] ?? 0, 'total_chances_remain' => $stats['total_chances_remain'] ?? 0, 'usage_rate' => $stats['total_chances_granted'] > 0 ? round(($stats['total_chances_used'] / $stats['total_chances_granted']) * 100, 2) : 0 ]; } // ============ 批量操作方法 ============ /** * 批量创建用户机会(用于活动启动时) * * @param int $activityId 活动ID * @param array $userIds 用户ID数组 * @param int $times 机会次数 * @return bool */ public static function batchCreateChances($activityId, $userIds, $times = 1) { if (empty($userIds)) { return false; } $data = []; $now = time(); foreach ($userIds as $userId) { $data[] = [ 'activity_id' => $activityId, 'user_id' => $userId, 'total_chances' => $times, 'used_chances' => 0, 'remain_chances' => $times, 'last_get_time' => $now, 'createtime' => $now, 'updatetime' => $now ]; } return LotteryUserChance::insertAll($data); } /** * 批量检查用户资格 * * @param int $activityId 活动ID * @param array $userIds 用户ID数组 * @return array 符合条件的用户ID数组 */ public static function batchCheckUserQualification($activityId, $userIds) { $activity = LotteryActivity::find($activityId); if (!$activity) { return []; } $qualifiedUsers = []; foreach ($userIds as $userId) { if (static::checkUserQualification($activity, $userId)) { $qualifiedUsers[] = $userId; } } return $qualifiedUsers; } /** * 批量验证条件 * * @param int $activityId 活动ID * @param array $orderInfo 订单信息 * @param array $userIds 用户ID数组 * @return array 每个用户满足的条件 */ public static function batchValidateConditionsForUsers($activityId, $orderInfo, $userIds) { $results = []; foreach ($userIds as $userId) { $results[$userId] = static::batchValidateConditions($activityId, $orderInfo, $userId); } return $results; } }