1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519 |
- <?php
- namespace app\common\Service\Lottery;
- use app\common\model\lottery\LotteryActivity;
- use app\common\model\lottery\LotteryCondition;
- use app\common\model\lottery\LotteryUserChance;
- use app\common\model\lottery\LotteryUserChanceRecord;
- use app\common\model\User;
- use app\common\model\Order;
- use app\common\Enum\LotteryEnum;
- use think\Exception;
- use think\Db;
- use app\common\exception\BusinessException;
- use app\common\Enum\OrderEnum;
- use app\common\Service\Lottery\LotteryActivityService;
- /**
- * 抽奖机会服务类
- * 处理用户获得抽奖机会的逻辑
- *
- * 主要功能:
- * - 订单/充值后自动分发抽奖机会
- * - 条件验证和机会计算
- * - 用户机会管理
- * - 批量操作和统计
- */
- class LotteryChanceService
- {
- // ============ 常量定义 ============
-
- /** @var int 默认批量处理数量 */
- const DEFAULT_BATCH_SIZE = 100;
-
- /** @var int 最大批量处理数量 */
- const MAX_BATCH_SIZE = 1000;
- /**
- * 订单完成后检查并分发抽奖机会(单个活动版本)
- * 查询一个正在进行的活动,返回单个结果
- * @param LotteryActivity $activity 活动对象
- * @param array $orderInfo 订单信息 ['id', 'total_amount', 'goods' => [['goods_id']]]
- * @param int $userId 用户ID
- * @return array|null 获得的抽奖机会信息,如果没有则返回null
- * @throws Exception
- */
- public static function checkAndGrantChanceForOrderOne($activity, $orderInfo, $userId)
- {
- if (empty($orderInfo) || empty($userId)) {
- throw new BusinessException('订单信息或用户ID不能为空');
- }
- // try {
-
-
- // 处理当前活动
- $chances = static::processActivityForOrder($activity, $orderInfo, $userId);
-
- if ($chances > 0) {
- return [
- 'lottery_id' => $activity->id,
- 'lottery_name' => $activity->name,
- 'lottery_type' => $activity->lottery_type,
- 'chances' => $chances,
- 'granted_time' => time()
- ];
- }
-
- return null;
-
- // } catch (Exception $e) {
- // trace("订单抽奖机会分发失败 - 用户ID: {$userId}, 错误: " . $e->getMessage(), 'error');
- // throw new BusinessException('订单抽奖机会分发失败');
- // }
- }
- /**
- * 订单完成后检查并分发抽奖机会 多个的
- * @param array $orderInfo 订单信息 ['id', 'total_amount', 'goods' => [['goods_id']]]
- * @param int $userId 用户ID
- * @return array 获得的抽奖机会信息
- * @throws Exception
- */
- public static function checkAndGrantChanceForOrder($orderInfo, $userId)
- {
- if (empty($orderInfo) || empty($userId)) {
- throw new BusinessException('订单信息或用户ID不能为空');
- }
- $grantedChances = [];
-
- try {
- // 获取所有正在进行的抽奖活动 一段时间 有且只有一个抽奖活动
- $activities = LotteryActivityService::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|null 获得的抽奖机会信息,如果没有则返回null
- * @throws Exception
- */
- public static function checkAndGrantChanceForRechargeOne($rechargeInfo, $userId)
- {
- if (empty($rechargeInfo) || empty($userId)) {
- throw new Exception('充值信息或用户ID不能为空');
- }
- try {
- // 获取当前正在进行的单个抽奖活动
- $activity = LotteryActivityService::getCurrentRunningActivity();
-
- // 如果没有正在进行的活动,返回null
- if (!$activity) {
- return null;
- }
-
- // 处理当前活动
- $chances = static::processActivityForRecharge($activity, $rechargeInfo, $userId);
-
- if ($chances > 0) {
- return [
- 'activity_id' => $activity->id,
- 'activity_name' => $activity->name,
- 'chances' => $chances,
- 'granted_time' => time()
- ];
- }
-
- return null;
-
- } catch (Exception $e) {
- trace("充值抽奖机会分发失败 - 用户ID: {$userId}, 错误: " . $e->getMessage(), 'error');
- throw $e;
- }
- }
- /**
- * 充值完成后检查并分发抽奖机会(多个活动版本)
- *
- * @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 = LotteryActivityService::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 = [
- '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;
- $chanceDetails = [];
- foreach ($conditions as $condition) {
- // 跳过充值条件
- if ($condition->type == LotteryEnum::CONDITION_TYPE_RECHARGE_AMOUNT) {
- continue;
- }
- $result = static::processConditionForOrder($condition, $orderInfo, $userId);
- if ($result['chances'] > 0) {
- $totalChances += $result['chances'];
- $result['detail']['chances'] = $result['chances'];
- $chanceDetails[] = $result['detail'];
- }
- }
- // 如果获得了抽奖机会,记录到数据库
- if ($totalChances > 0) {
- // 为每个条件分别记录
- foreach ($chanceDetails as $detail) {
- static::grantChanceToUser($activity->id, $userId, $detail['chances'] ?? 1, [
- 'condition_id' => $detail['condition_id'],
- 'condition_type' => $detail['condition_type'],
- 'condition_value' => $detail['condition_value'],
- 'order_id' => $orderInfo['id'] ?? 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;
- $chanceDetails = [];
-
- foreach ($conditions as $condition) {
- // 只处理充值条件
- if ($condition->type != LotteryEnum::CONDITION_TYPE_RECHARGE_AMOUNT) {
- continue;
- }
- $result = static::processConditionForRecharge($condition, $rechargeInfo, $userId);
- if ($result['chances'] > 0) {
- $totalChances += $result['chances'];
- $result['detail']['chances'] = $result['chances'];
- $chanceDetails[] = $result['detail'];
- }
- }
- // 如果获得了抽奖机会,记录到数据库
- if ($totalChances > 0) {
- // 为每个条件分别记录
- foreach ($chanceDetails as $detail) {
- static::grantChanceToUser($activity->id, $userId, $detail['chances'] ?? 1, [
- 'condition_id' => $detail['condition_id'],
- 'condition_type' => $detail['condition_type'],
- 'condition_value' => $detail['condition_value'],
- 'recharge_amount' => $rechargeInfo['amount'] ?? 0,
- 'granted_time' => time()
- ]);
- }
- }
- return $totalChances;
- }
- /**
- * 处理订单条件
- *
- * @param LotteryCondition $condition 条件对象
- * @param array $orderInfo 订单信息
- * @param int $userId 用户ID
- * @return array 获得的抽奖机会信息 ['chances' => int, 'detail' => array]
- */
- private static function processConditionForOrder($condition, $orderInfo, $userId)
- {
- $chances = 0;
- $detail = [
- 'condition_id' => $condition->id,
- 'condition_type' => $condition->type,
- 'condition_value' => $condition->condition_value,
- ];
- 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)) {
- // 使用bcdiv进行精确的金额除法运算,然后取整
- $multiple = floor(bcdiv($orderInfo['total_amount'], $condition->condition_value, 2));
- $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' => $chances,
- 'detail' => $detail
- ];
- }
- /**
- * 处理充值条件
- *
- * @param LotteryCondition $condition 条件对象
- * @param array $rechargeInfo 充值信息
- * @param int $userId 用户ID
- * @return array 获得的抽奖机会信息 ['chances' => int, 'detail' => array]
- */
- private static function processConditionForRecharge($condition, $rechargeInfo, $userId)
- {
- $chances = 0;
- $detail = [
- 'condition_id' => $condition->id,
- 'condition_type' => $condition->type,
- 'condition_value' => $condition->condition_value,
- ];
-
- if (static::validateRechargeCondition($condition, ['type' => 'recharge', 'amount' => $rechargeInfo['amount']])) {
- $chances = static::getRewardTimes($condition);
-
- // 如果可重复获得,根据金额倍数计算次数
- if (static::canRepeat($condition)) {
- // 使用bcdiv进行精确的金额除法运算,然后取整
- $multiple = floor(bcdiv($rechargeInfo['amount'], $condition->condition_value, 2));
- $chances *= $multiple;
- }
- }
- return [
- 'chances' => $chances,
- 'detail' => $detail
- ];
- }
- // ============ 用户资格检查方法 ============
- /**
- * 检查用户资格
- *
- * @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)
- {
- $record = LotteryUserChanceRecord::where('activity_id', $activityId)
- ->where('user_id', $userId)
- ->where('get_type', LotteryEnum::CHANCE_GET_TYPE_TOTAL_AMOUNT)
- ->find();
-
- return $record ? true : 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;
-
- // 使用bccomp进行精确的金额比较,返回值:-1(小于)、0(等于)、1(大于)
- return bccomp($orderAmount, $condition->condition_value, 2) >= 0;
- }
- /**
- * 验证充值条件
- *
- * @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;
- // 使用bccomp进行精确的金额比较
- return bccomp($rechargeAmount, $condition->condition_value, 2) >= 0;
- }
- /**
- * 验证累计消费条件
- *
- * @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('order_status', OrderEnum::STATUS_PAY)
- ->where('pay_time', '>=', $activity->start_time)
- ->where('pay_time', '<=', $activity->end_time)
- ->sum('amount');
- // 使用bccomp进行精确的金额比较
- return bccomp($totalAmount, $condition->condition_value, 2) >= 0;
- }
- // ============ 条件管理方法 ============
- /**
- * 获取活动的有效条件
- *
- * @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 array $detail 获得详情
- * @return int 获取类型
- */
- private static function getChanceGetTypeFromDetail($detail)
- {
- // 如果有条件类型,直接使用条件类型对应的获取类型
- if (isset($detail['condition_type'])) {
- switch ($detail['condition_type']) {
- case LotteryEnum::CONDITION_TYPE_BUY_GOODS:
- return LotteryEnum::CHANCE_GET_TYPE_BUY_GOODS;
- case LotteryEnum::CONDITION_TYPE_ORDER_AMOUNT:
- return LotteryEnum::CHANCE_GET_TYPE_ORDER_AMOUNT;
- case LotteryEnum::CONDITION_TYPE_RECHARGE_AMOUNT:
- return LotteryEnum::CHANCE_GET_TYPE_RECHARGE;
- case LotteryEnum::CONDITION_TYPE_TOTAL_AMOUNT:
- return LotteryEnum::CHANCE_GET_TYPE_TOTAL_AMOUNT;
- }
- }
-
- // 如果没有条件类型,检查是否是管理员赠送
- if (isset($detail['admin_id']) && $detail['admin_id'] > 0) {
- return LotteryEnum::CHANCE_GET_TYPE_ADMIN_GRANT;
- }
-
- // 默认返回管理员赠送
- return LotteryEnum::CHANCE_GET_TYPE_ADMIN_GRANT;
- }
- /**
- * 获取用户在指定活动中的抽奖机会详情
- *
- * @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_records' => []
- ];
- }
- // 获取机会获得记录(使用服务类方法)
- $getRecords = static::getUserChanceRecord($userId, $activityId);
- 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_records' => $getRecords
- ];
- }
- /**
- * 给用户分发抽奖机会
- *
- * @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;
- }
- try {
- Db::startTrans();
-
- $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([]) // 保留字段但不使用
- ];
- $chance = LotteryUserChance::create($data);
- } else {
- // 更新现有记录
- $chance->total_chances += $times;
- $chance->remain_chances += $times;
- $chance->last_get_time = time();
- $chance->save();
- }
-
- // 创建获得记录
- $recordData = [
- 'activity_id' => $activityId,
- 'user_id' => $userId,
- 'get_type' => static::getChanceGetTypeFromDetail($detail),
- 'chances' => $times,
- 'condition_id' => $detail['condition_id'] ?? null,
- 'condition_value' => $detail['condition_value'] ?? null,
- 'order_id' => $detail['order_id'] ?? null,
- 'recharge_amount' => $detail['recharge_amount'] ?? null,
- 'admin_id' => $detail['admin_id'] ?? null,
- 'reason' => $detail['reason'] ?? null,
- 'remark' => $detail['remark'] ?? null,
- 'get_time' => time()
- ];
-
- $validation = self::validateChanceRecord($recordData);
- if ($validation !== true) {
- throw new Exception($validation);
- }
-
- LotteryUserChanceRecord::create($recordData);
-
- Db::commit();
- return $chance;
-
- } catch (Exception $e) {
- Db::rollback();
- throw $e;
- }
- }
- /**
- * 使用抽奖机会
- *
- * @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;
- }
- // ============ 机会获取记录管理方法 ============
- /**
- * 获取用户机会获取记录
- *
- * @param int $activityId 活动ID
- * @param int $userId 用户ID
- * @param int $page 页码
- * @param int $limit 每页数量
- * @return array
- */
- public static function getUserChanceRecords($activityId, $userId, $page = 1, $limit = 20)
- {
- return LotteryUserChanceRecord::where('activity_id', $activityId)
- ->where('user_id', $userId)
- ->with(['condition', 'order', 'admin'])
- ->order('get_time desc')
- ->page($page, $limit)
- ->select();
- }
- /**
- * 获取活动机会获取统计
- *
- * @param int $activityId 活动ID
- * @return array
- */
- public static function getActivityChanceRecordStats($activityId)
- {
- $records = LotteryUserChanceRecord::where('activity_id', $activityId)->select();
-
- $stats = [
- 'total_records' => count($records),
- 'total_chances' => 0,
- 'type_stats' => [],
- ];
-
- foreach ($records as $record) {
- $stats['total_chances'] += $record->chances;
-
- $getType = $record->get_type;
- if (!isset($stats['type_stats'][$getType])) {
- $stats['type_stats'][$getType] = [
- 'type' => $getType,
- 'type_text' => LotteryEnum::getChanceGetTypeText($getType),
- 'count' => 0,
- 'chances' => 0,
- ];
- }
-
- $stats['type_stats'][$getType]['count']++;
- $stats['type_stats'][$getType]['chances'] += $record->chances;
- }
-
- return $stats;
- }
- /**
- * 获取用户机会获取统计
- *
- * @param int $userId 用户ID
- * @param int $activityId 活动ID(可选)
- * @return array
- */
- public static function getUserChanceRecord($userId, $activityId = null)
- {
- $query = LotteryUserChanceRecord::where('user_id', $userId);
-
- if ($activityId) {
- $query->where('activity_id', $activityId);
- }
-
- $records = $query->select();
-
- $stats = [
- 'total_records' => count($records),
- 'total_chances' => 0,
- 'type_stats' => [],
- 'recent_records' => [],
- ];
-
- foreach ($records as $record) {
- $stats['total_chances'] += $record->chances;
-
- $getType = $record->get_type;
- if (!isset($stats['type_stats'][$getType])) {
- $stats['type_stats'][$getType] = [
- 'type' => $getType,
- 'type_text' => LotteryEnum::getChanceGetTypeText($getType),
- 'count' => 0,
- 'chances' => 0,
- ];
- }
-
- $stats['type_stats'][$getType]['count']++;
- $stats['type_stats'][$getType]['chances'] += $record->chances;
- }
-
- // 获取最近的记录
- $stats['recent_records'] = LotteryUserChanceRecord::where('user_id', $userId)
- ->with(['activity'])
- ->order('get_time desc')
- ->limit(5)
- ->select();
-
- return $stats;
- }
- /**
- * 批量创建机会获取记录
- *
- * @param array $records 记录数组
- * @return bool
- */
- public static function batchCreateChanceRecords($records)
- {
- if (empty($records)) {
- return false;
- }
-
- // 为每条记录添加创建时间
- foreach ($records as &$record) {
- $record['createtime'] = time();
- $record['get_time'] = $record['get_time'] ?? time();
- }
-
- return LotteryUserChanceRecord::insertAll($records);
- }
- /**
- * 验证机会获取记录数据
- *
- * @param array $data 记录数据
- * @return bool|string true表示验证通过,string表示错误信息
- */
- public static function validateChanceRecord($data)
- {
- // 验证必填字段
- if (empty($data['activity_id'])) {
- return '活动ID不能为空';
- }
-
- if (empty($data['user_id'])) {
- return '用户ID不能为空';
- }
-
- if (empty($data['get_type'])) {
- return '获取类型不能为空';
- }
-
- if (empty($data['chances']) || $data['chances'] <= 0) {
- return '机会次数必须大于0';
- }
-
- // 验证获取类型是否有效
- if (!LotteryEnum::isValidChanceGetType($data['get_type'])) {
- return '无效的获取类型';
- }
-
- // 根据获取类型验证相关字段
- switch ($data['get_type']) {
- case LotteryEnum::CHANCE_GET_TYPE_BUY_GOODS:
- case LotteryEnum::CHANCE_GET_TYPE_ORDER_AMOUNT:
- if (empty($data['order_id'])) {
- return '订单ID不能为空';
- }
- break;
-
- case LotteryEnum::CHANCE_GET_TYPE_RECHARGE:
- if (empty($data['recharge_amount']) || $data['recharge_amount'] <= 0) {
- return '充值金额必须大于0';
- }
- break;
-
- case LotteryEnum::CHANCE_GET_TYPE_ADMIN_GRANT:
- if (empty($data['admin_id'])) {
- return '管理员ID不能为空';
- }
- break;
- }
-
- return true;
- }
- /**
- * 创建单条机会获取记录
- *
- * @param array $data 记录数据
- * @return bool|int 成功返回记录ID,失败返回false
- */
- public static function createChanceRecord($data)
- {
- // 验证数据
- $validation = static::validateChanceRecord($data);
- if ($validation !== true) {
- throw new BusinessException($validation);
- }
-
- // 设置默认值
- $data['createtime'] = time();
- $data['get_time'] = $data['get_time'] ?? time();
-
- $record = new LotteryUserChanceRecord($data);
- if ($record->save()) {
- return $record->id;
- }
-
- return false;
- }
- /**
- * 获取用户在某个活动中的最新记录
- *
- * @param int $activityId 活动ID
- * @param int $userId 用户ID
- * @param int $limit 限制数量
- * @return array
- */
- public static function getUserLatestRecords($activityId, $userId, $limit = 10)
- {
- return LotteryUserChanceRecord::where('activity_id', $activityId)
- ->where('user_id', $userId)
- ->with(['condition', 'order', 'admin'])
- ->order('get_time desc')
- ->limit($limit)
- ->select();
- }
- /**
- * 获取活动中某种获取类型的记录统计
- *
- * @param int $activityId 活动ID
- * @param int $getType 获取类型
- * @return array
- */
- public static function getActivityRecordsByType($activityId, $getType)
- {
- $records = LotteryUserChanceRecord::where('activity_id', $activityId)
- ->where('get_type', $getType)
- ->select();
-
- $stats = [
- 'total_records' => count($records),
- 'total_chances' => 0,
- 'users' => [],
- ];
-
- foreach ($records as $record) {
- $stats['total_chances'] += $record->chances;
-
- if (!isset($stats['users'][$record->user_id])) {
- $stats['users'][$record->user_id] = [
- 'user_id' => $record->user_id,
- 'records' => 0,
- 'chances' => 0,
- ];
- }
-
- $stats['users'][$record->user_id]['records']++;
- $stats['users'][$record->user_id]['chances'] += $record->chances;
- }
-
- return $stats;
- }
- /**
- * 检查用户订单是否已经分发过抽奖机会
- *
- * @param array $orderInfo 订单信息
- * @param int $userId 用户ID
- * @return bool 是否已分发过
- */
- public static function isGrantedChanceForOrder($orderInfo, $userId)
- {
- if (empty($orderInfo) || empty($userId) || empty($orderInfo['id'])) {
- return false;
- }
- // 查询是否存在该订单的机会记录
- $count = LotteryUserChanceRecord::where('user_id', $userId)
- ->where('order_id', $orderInfo['id'])
- ->count();
-
- return $count > 0;
- }
- /**
- * 检查用户是否对指定条件已经获得过抽奖机会(用于防重复)
- *
- * @param int $activityId 活动ID
- * @param int $userId 用户ID
- * @param int $conditionId 条件ID
- * @param string $conditionValue 条件值
- * @return bool 是否已获得过
- */
- public static function isGrantedChanceForCondition($activityId, $userId, $conditionId, $conditionValue = '')
- {
- $where = [
- 'activity_id' => $activityId,
- 'user_id' => $userId,
- 'condition_id' => $conditionId
- ];
- // 如果有条件值,也要匹配
- if (!empty($conditionValue)) {
- $where['condition_value'] = $conditionValue;
- }
- $count = LotteryUserChanceRecord::where($where)->count();
-
- return $count > 0;
- }
- }
|