isPublished()) { throw new BizException('抽奖活动未发布'); } if ($drawActivity->isUnstart()) { throw new BizException('抽奖活动未开始'); } if ($drawActivity->isClosed()) { throw new BizException('抽奖活动已结束'); } (new DrawTicketService())->change($user, $drawActivity, -1, '活动抽奖'); $prize = $this->getDrawActivityPrize($drawActivity); // 如果限制了奖品数量,则需扣除奖品库存 if ($prize->limited) { $prize->update([ 'stock' => DB::raw('stock-1'), 'winnings' => DB::raw('winnings+1'), ]); } else { $prize->increment('winnings', 1); } $drawLog = DrawLog::create([ 'user_id' => $user->id, 'draw_activity_id' => $drawActivity->id, 'draw_activity_prize_id' => $prize->id, 'status' => match ($prize->type) { DrawPrizeType::None => DrawLogStatus::Completed, default => DrawLogStatus::Pending, }, ]); $drawLog->setRelation('prize', $prize); // 自动发放奖品 if ($prize->auto_send) { // 优惠券 $this->sendPrize($drawLog); } return $drawLog; } /** * @param \App\Models\DrawActivity $drawActivity * @return \App\Models\DrawActivityPrize */ protected function getDrawActivityPrize(DrawActivity $drawActivity): DrawActivityPrize { $drawActivityPrizes = $drawActivity->prizes()->get(); // 过滤权重为0或库存不足的奖品 $prizes = $drawActivityPrizes->filter(function ($item) { if ($item->weight === 0 || ($item->limited && $item->stock === 0)) { return false; } return true; }); $max = $prizes->sum('weight'); if ($max > 0) { $rand = mt_rand($min = 1, $max); foreach ($prizes as $prize) { if ($rand <= $prize->weight + $min) { return $prize; } $min += $prize->weight; } } throw new BizException('抽奖活动异常'); } /** * 发放活动奖品 */ public function sendPrize(DrawLog $drawLog) { if (!$drawLog->isPending()) { throw new BizException("发放奖品失败: 奖品已发放"); } // 发放奖品 $prize = $drawLog->prize; // 发放优惠券 if ($prize->type == DrawPrizeType::Coupon) { if (!$prize->price_type_id) { throw new BizException("发放奖品失败: 奖品(".$prize->name.")未关联优惠券"); } $coupon = Coupon::find($prize->price_type_id); if (!$coupon) { throw new BizException("发放奖品失败: 奖品(".$prize->name.")关联优惠券不存在"); } (new CouponService())->sendCoupon($drawLog->user, $coupon, 1); } $drawLog->update([ 'status' => DrawLogStatus::Completed, ]); } }