$activityId, 'user_id' => $userId, 'prize_id' => $prizeId, 'is_win' => $isWin ? 1 : 0, 'trigger_type' => $triggerType, 'trigger_order_id' => $triggerOrderId, 'trigger_amount' => $triggerAmount, 'win_info' => $winInfo ? json_encode($winInfo) : '', 'draw_ip' => request()->ip(), 'draw_time' => time(), 'device_info' => request()->header('user-agent', '') ]; return LotteryDrawRecord::create($data); } /** * 获取用户抽奖次数 */ public static function getUserDrawCount($activityId, $userId, $timeRange = null) { $query = LotteryDrawRecord::where('activity_id', $activityId) ->where('user_id', $userId); if ($timeRange) { if (isset($timeRange['start'])) { $query->where('draw_time', '>=', $timeRange['start']); } if (isset($timeRange['end'])) { $query->where('draw_time', '<=', $timeRange['end']); } } return $query->count(); } /** * 获取用户中奖次数 */ public static function getUserWinCount($activityId, $userId, $timeRange = null) { $query = LotteryDrawRecord::where('activity_id', $activityId) ->where('user_id', $userId) ->where('is_win', 1); if ($timeRange) { if (isset($timeRange['start'])) { $query->where('draw_time', '>=', $timeRange['start']); } if (isset($timeRange['end'])) { $query->where('draw_time', '<=', $timeRange['end']); } } return $query->count(); } /** * 获取活动抽奖统计 */ public static function getActivityDrawStats($activityId, $timeRange = null) { $query = LotteryDrawRecord::where('activity_id', $activityId); if ($timeRange) { if (isset($timeRange['start'])) { $query->where('draw_time', '>=', $timeRange['start']); } if (isset($timeRange['end'])) { $query->where('draw_time', '<=', $timeRange['end']); } } return [ 'total_draw' => $query->count(), 'total_win' => $query->where('is_win', 1)->count(), 'total_people' => $query->distinct('user_id')->count() ]; } /** * 获取用户抽奖记录列表 */ public static function getUserDrawRecords($userId = 0, $activityId = null,$status = 0, $page = 1, $pageSize = 20) { $query = LotteryDrawRecord::where('user_id', $userId); if ($activityId) { $query->where('activity_id', $activityId); } if ($status) { $query->where('status', $status); } // 1. 先分页查询抽奖记录 $records = $query->field('id,activity_id,user_id,prize_id,is_win,trigger_type,trigger_order_id,trigger_amount, draw_time,status') ->order('draw_time', 'desc') ->paginate($pageSize, false, ['page' => $page]); if (empty($records) || $records->isEmpty()) { return $records; } // 2. 收集关联ID $activityIds = []; $prizeIds = []; $drawRecordIds = []; foreach ($records as $record) { $activityIds[] = $record->activity_id; $prizeIds[] = $record->prize_id; if ($record->is_win) { $drawRecordIds[] = $record->id; } } // 去重 $activityIds = array_unique($activityIds); $prizeIds = array_unique($prizeIds); $drawRecordIds = array_unique($drawRecordIds); // 3. 批量查询关联数据 $activities = []; if (!empty($activityIds)) { $activityList = LotteryActivity::whereIn('id', $activityIds) ->field('id,name,status,lottery_type,lottery_time') ->select(); foreach ($activityList as $activity) { $activities[$activity->id] = $activity; } } $prizes = []; if (!empty($prizeIds)) { $prizeList = LotteryPrize::whereIn('id', $prizeIds) ->field('id,name,image,activity_id,type') ->select(); foreach ($prizeList as $prize) { $prizes[$prize->id] = $prize; } } $winRecords = []; if (!empty($drawRecordIds)) { $winRecordList = LotteryWinRecord::whereIn('draw_record_id', $drawRecordIds) ->field('id,draw_record_id,deliver_status,deliver_time,exchange_code,receiver_name,receiver_mobile,receiver_address,express_company,express_number,exchange_code') ->select(); foreach ($winRecordList as $winRecord) { $winRecords[$winRecord->draw_record_id] = $winRecord; } } // 4. 将关联数据作为字段附加到记录中 foreach ($records as $record) { // 附加活动信息字段 $activity = $activities[$record->activity_id] ?? null; $record->activity_name = $activity ? $activity->name : ''; $record->activity_status = $activity ? $activity->status : 0; $record->activity_lottery_type = $activity ? $activity->lottery_type : 0; $record->activity_lottery_time = $activity ? $activity->lottery_time : 0; // 附加奖品信息字段 $prize = $prizes[$record->prize_id] ?? null; $record->prize_name = $prize ? $prize->name : ''; $record->prize_image = $prize ? cdnurl($prize->image) : ''; $record->prize_type = $prize ? $prize->type : 0; // 附加中奖记录信息字段(仅限中奖记录) if ($record->is_win) { $winRecord = $winRecords[$record->id] ?? null; $record->win_record_id = $winRecord ? $winRecord->id : 0; $record->deliver_status = $winRecord ? $winRecord->deliver_status : 0; $record->deliver_time = $winRecord ? $winRecord->deliver_time : 0; $record->exchange_code = $winRecord ? $winRecord->exchange_code : ''; $record->receiver_name = $winRecord ? $winRecord->receiver_name : ''; $record->receiver_mobile = $winRecord ? $winRecord->receiver_mobile : ''; $record->receiver_address = $winRecord ? $winRecord->receiver_address : ''; $record->express_company = $winRecord ? $winRecord->express_company : ''; $record->express_number = $winRecord ? $winRecord->express_number : ''; } else { $record->win_record_id = 0; $record->deliver_status = 0; $record->deliver_time = 0; $record->exchange_code = ''; $record->receiver_name = ''; $record->receiver_mobile = ''; $record->receiver_address = ''; $record->express_company = ''; $record->express_number = ''; } // 添加触发类型文本 $record->trigger_type_text = LotteryEnum::getTriggerTypeMap($record->trigger_type); } return $records; } /** * 获取抽奖记录详情 * * @param int $drawRecordId 抽奖记录ID * @param int $userId 用户ID(可选,用于验证权限) * @return LotteryDrawRecord|null 抽奖记录详情,包含关联信息 */ public static function getDrawRecordDetail($drawRecordId, $userId = 0) { // 1. 查询抽奖记录基础信息 $query = LotteryDrawRecord::where('id', $drawRecordId); // 如果指定了用户ID,则验证权限 if ($userId) { $query->where('user_id', $userId); } $record = $query->field('id,activity_id,user_id,prize_id,is_win,trigger_type,trigger_order_id, trigger_amount,draw_time,remark,status,createtime') ->find(); if (!$record) { return null; } // 2. 查询关联的活动信息 $activity = LotteryActivity::where('id', $record->activity_id) ->field('id,name,type,status,lottery_type,lottery_time') ->find(); // 3. 查询关联的奖品信息 $prize = null; if ($record->prize_id > 0) { $prize = LotteryPrize::where('id', $record->prize_id) ->field('id,name,image,type') ->find(); } // 4. 查询关联的中奖记录信息(仅限中奖记录) $winRecord = null; if ($record->is_win) { $winRecord = LotteryWinRecord::where('draw_record_id', $record->id) ->field('id,draw_record_id,prize_name,prize_type,deliver_status, deliver_time,receiver_name,receiver_mobile,receiver_address, express_company,express_number,exchange_code,code_used_time') ->find(); } // 5. 将关联数据作为字段附加到记录中 // 附加活动信息字段 $record->activity_name = $activity ? $activity->name : ''; $record->activity_type = $activity ? $activity->type : 0; $record->activity_status = $activity ? $activity->status : 0; $record->activity_lottery_type = $activity ? $activity->lottery_type : 0; $record->activity_lottery_time = $activity ? $activity->lottery_time : 0; // 附加奖品信息字段 $record->prize_name = $prize ? $prize->name : ''; $record->prize_image = $prize ? cdnurl($prize->image) : ''; $record->prize_type = $prize ? $prize->type : 0; // 附加中奖记录信息字段 if ($winRecord) { $record->win_record_id = $winRecord->id; $record->win_prize_name = $winRecord->prize_name; $record->win_prize_type = $winRecord->prize_type; $record->deliver_status = $winRecord->deliver_status; $record->deliver_time = $winRecord->deliver_time; $record->receiver_name = $winRecord->receiver_name; $record->receiver_mobile = $winRecord->receiver_mobile; $record->receiver_address = $winRecord->receiver_address; $record->express_company = $winRecord->express_company; $record->express_number = $winRecord->express_number; $record->exchange_code = $winRecord->exchange_code; $record->code_used_time = $winRecord->code_used_time; } else { $record->win_record_id = 0; $record->win_prize_name = ''; $record->win_prize_type = 0; $record->deliver_status = 0; $record->deliver_time = 0; $record->receiver_name = ''; $record->receiver_mobile = ''; $record->receiver_address = ''; $record->express_company = ''; $record->express_number = ''; $record->exchange_code = ''; $record->code_used_time = 0; } return $record; } /** * 检查用户是否已为指定订单抽奖 */ public static function hasUserDrawnForOrder($activityId, $userId, $orderId) { return LotteryDrawRecord::where('activity_id', $activityId) ->where('user_id', $userId) ->where('trigger_order_id', $orderId) ->count() > 0; } /** * 获取用户抽奖历史统计 */ public static function getUserDrawHistoryStats($userId) { $stats = LotteryDrawRecord::where('user_id', $userId) ->field([ 'COUNT(*) as total_draws', 'SUM(is_win) as total_wins', 'COUNT(DISTINCT activity_id) as total_activities' ]) ->find(); return [ 'total_draws' => $stats['total_draws'] ?? 0, 'total_wins' => $stats['total_wins'] ?? 0, 'total_activities' => $stats['total_activities'] ?? 0, 'win_rate' => $stats['total_draws'] > 0 ? round(($stats['total_wins'] / $stats['total_draws']) * 100, 2) : 0 ]; } /** * 获取活动抽奖排行榜 */ public static function getActivityDrawRanking($activityId, $limit = 50) { return LotteryDrawRecord::where('activity_id', $activityId) ->field('user_id, COUNT(*) as draw_count, SUM(is_win) as win_count') ->with('user') ->group('user_id') ->order('draw_count desc, win_count desc') ->limit($limit) ->select(); } /** * 获取活动抽奖时间分布统计 */ public static function getActivityDrawTimeDistribution($activityId, $dateRange = null) { $query = LotteryDrawRecord::where('activity_id', $activityId); if ($dateRange) { if (isset($dateRange['start'])) { $query->where('draw_time', '>=', $dateRange['start']); } if (isset($dateRange['end'])) { $query->where('draw_time', '<=', $dateRange['end']); } } return $query->field('DATE(FROM_UNIXTIME(draw_time)) as date, COUNT(*) as draw_count, SUM(is_win) as win_count') ->group('date') ->order('date asc') ->select(); } // ============ 中奖记录相关方法 ============ /** * 获取中奖记录详情 */ public static function getWinRecordDetail($winRecordId, $userId = 0) { $query = LotteryWinRecord::where('id', $winRecordId); if ($userId) { $query->where('user_id', $userId); } return $query->find(); } /** * 创建中奖记录 */ public static function createWinRecord($drawRecordId, $activityId, $userId, $prizeId, $prizeName, $prizeType, $prizeValue = []) { $data = [ 'draw_record_id' => $drawRecordId, 'activity_id' => $activityId, 'user_id' => $userId, 'prize_id' => $prizeId, 'prize_name' => $prizeName, 'prize_type' => $prizeType, 'prize_value' => is_array($prizeValue) ? json_encode($prizeValue) : $prizeValue, 'deliver_status' => LotteryEnum::DELIVER_STATUS_PENDING ]; return LotteryWinRecord::create($data); } /** * 标记中奖记录为发放成功 */ public static function markWinRecordAsDelivered(LotteryWinRecord $winRecord, $deliverInfo = []) { $winRecord->deliver_status = LotteryEnum::DELIVER_STATUS_SUCCESS; $winRecord->deliver_time = time(); if ($deliverInfo) { $winRecord->deliver_info = json_encode($deliverInfo); } return $winRecord->save(); } /** * 标记中奖记录为发放失败 */ public static function markWinRecordAsFailed(LotteryWinRecord $winRecord, $reason = '') { $winRecord->deliver_status = LotteryEnum::DELIVER_STATUS_FAILED; $winRecord->fail_reason = $reason; return $winRecord->save(); } /** * 设置中奖记录收货地址 */ public static function setWinRecordDeliveryAddress(LotteryWinRecord $winRecord, $name, $mobile, $address) { $winRecord->receiver_name = $name; $winRecord->receiver_mobile = $mobile; $winRecord->receiver_address = $address; return $winRecord->save(); } /** * 设置中奖记录快递信息 */ public static function setWinRecordExpressInfo(LotteryWinRecord $winRecord, $company, $number) { $winRecord->express_company = $company; $winRecord->express_number = $number; return $winRecord->save(); } /** * 设置中奖记录兑换码 */ public static function setWinRecordExchangeCode(LotteryWinRecord $winRecord, $code) { $winRecord->exchange_code = $code; return $winRecord->save(); } /** * 标记中奖记录兑换码已使用 */ public static function markWinRecordCodeUsed(LotteryWinRecord $winRecord) { $winRecord->code_used_time = time(); return $winRecord->save(); } /** * 获取待发放的中奖记录 */ public static function getPendingWinRecords($limit = 100) { return LotteryWinRecord::where('deliver_status', LotteryEnum::DELIVER_STATUS_PENDING) ->order('createtime', 'asc') ->limit($limit) ->select(); } /** * 获取用户中奖记录 */ public static function getUserWinRecords($userId, $page = 1, $limit = 20) { return LotteryWinRecord::where('user_id', $userId) ->with(['activity', 'prize']) ->order('createtime', 'desc') ->page($page, $limit) ->select(); } /** * 获取活动中奖统计 */ public static function getActivityWinStats($activityId) { return [ 'total_win' => LotteryWinRecord::where('activity_id', $activityId)->count(), 'delivered' => LotteryWinRecord::where('activity_id', $activityId) ->where('deliver_status', LotteryEnum::DELIVER_STATUS_SUCCESS) ->count(), 'pending' => LotteryWinRecord::where('activity_id', $activityId) ->where('deliver_status', LotteryEnum::DELIVER_STATUS_PENDING) ->count(), 'failed' => LotteryWinRecord::where('activity_id', $activityId) ->where('deliver_status', LotteryEnum::DELIVER_STATUS_FAILED) ->count() ]; } /** * 批量处理待发放的中奖记录 */ public static function batchProcessPendingWinRecords($limit = 50) { $pendingRecords = static::getPendingWinRecords($limit); $processedCount = 0; foreach ($pendingRecords as $winRecord) { try { $prize = LotteryPrize::find($winRecord->prize_id); if ($prize && $prize->deliver_type == LotteryEnum::DELIVER_TYPE_AUTO) { static::autoDeliverPrize($winRecord, $prize); $processedCount++; } } catch (Exception $e) { static::markWinRecordAsFailed($winRecord, $e->getMessage()); trace('自动发放奖品失败: ' . $e->getMessage(), 'error'); } } return $processedCount; } /** * 手动发放奖品(管理员操作) */ public static function manualDeliverPrize($winRecordId, $deliverInfo = [], $adminId = 0) { $winRecord = LotteryWinRecord::find($winRecordId); if (!$winRecord) { throw new Exception('中奖记录不存在'); } if ($winRecord->deliver_status != LotteryEnum::DELIVER_STATUS_PENDING) { throw new Exception('该记录已处理,无法重复发放'); } $deliverInfo['admin_id'] = $adminId; $deliverInfo['manual_deliver_time'] = time(); return static::markWinRecordAsDelivered($winRecord, $deliverInfo); } /** * 取消中奖记录 */ public static function cancelWinRecord($winRecordId, $reason = '', $adminId = 0) { $winRecord = LotteryWinRecord::find($winRecordId); if (!$winRecord) { throw new Exception('中奖记录不存在'); } $winRecord->deliver_status = LotteryEnum::DELIVER_STATUS_CANCELLED; $winRecord->fail_reason = $reason; $winRecord->cancel_time = time(); $winRecord->cancel_admin_id = $adminId; return $winRecord->save(); } /** * 自动发放奖品 */ public static function autoDeliverPrize($winRecord, $prize) { try { switch ($prize->type) { case LotteryEnum::PRIZE_TYPE_COUPON: static::deliverCoupon($winRecord, $prize); break; case LotteryEnum::PRIZE_TYPE_REDPACK: static::deliverRedPacket($winRecord, $prize); break; case LotteryEnum::PRIZE_TYPE_CODE: static::deliverExchangeCode($winRecord, $prize); break; case LotteryEnum::PRIZE_TYPE_SHOP_GOODS: static::deliverGoods($winRecord, $prize); break; } } catch (Exception $e) { static::markWinRecordAsFailed($winRecord, $e->getMessage()); } } /** * 发放优惠券 */ private static function deliverCoupon($winRecord, $prize) { // 这里调用优惠券发放接口 // 示例代码,需要根据实际优惠券系统实现 static::markWinRecordAsDelivered($winRecord, ['coupon_id' => $prize->coupon_id]); } /** * 发放红包 */ private static function deliverRedPacket($winRecord, $prize) { // 这里调用红包发放接口 // 示例代码,需要根据实际红包系统实现 static::markWinRecordAsDelivered($winRecord, ['amount' => $prize->amount]); } /** * 发放兑换码 */ private static function deliverExchangeCode($winRecord, $prize) { $code = LotteryService::getAvailableExchangeCode($prize); if ($code) { static::setWinRecordExchangeCode($winRecord, $code); LotteryService::markExchangeCodeUsed($prize, $code); static::markWinRecordAsDelivered($winRecord, ['exchange_code' => $code]); } else { throw new Exception('兑换码已用完'); } } /** * 发放商城商品 */ private static function deliverGoods($winRecord, $prize) { // 这里可以自动加入购物车或创建订单 // 示例代码,需要根据实际商城系统实现 static::markWinRecordAsDelivered($winRecord, [ 'goods_id' => $prize->goods_id, 'goods_sku_id' => $prize->goods_sku_id ]); } }