LotteryService
是抽奖系统的核心服务类,负责处理抽奖的核心业务逻辑,包括抽奖执行、奖品管理、活动状态管理等功能。
最新优化: 系统现已支持三种抽奖方式的统一处理:
所有抽奖方式都采用统一的"先记录参与,再更新结果"的流程,确保数据一致性和可追溯性。
namespace app\common\Service\lottery;
class LotteryService
{
// ============ 核心抽奖方法 ============
public static function drawLottery($activityId, $userId, $triggerType, $triggerOrderId, $triggerAmount)
// ============ 三种抽奖方式处理方法 ============
// 统一处理入口
private static function handleLotteryByType($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount)
private static function createParticipationRecord($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount)
// 即抽即中
private static function executeInstantDraw($drawRecord, $activity)
// 定时开奖
private static function handleTimeLottery($drawRecord, $activity)
public static function processScheduledLotteries() // 定时任务入口
private static function executeScheduledDraw($activity)
private static function executeDrawForRecord($drawRecord, $activity)
// 按人数开奖
private static function handlePeopleLottery($drawRecord, $activity)
// ============ 奖品管理方法 ============
public static function hasPrizeStock(LotteryPrize $prize, $quantity)
public static function decreasePrizeStock(LotteryPrize $prize, $quantity)
public static function getAvailableExchangeCode(LotteryPrize $prize)
public static function markExchangeCodeUsed(LotteryPrize $prize, $code)
public static function getValidPrizes($activityId)
public static function isPrizeUnlocked(LotteryPrize $prize, $currentPeopleCount)
// ============ 活动状态管理方法 ============
public static function isActivityRunning(LotteryActivity $activity)
public static function isActivityEnded(LotteryActivity $activity)
public static function isActivityNotStarted(LotteryActivity $activity)
public static function isActivitySuspended(LotteryActivity $activity)
public static function isActivityCancelled(LotteryActivity $activity)
public static function isInDrawTime(LotteryActivity $activity)
public static function getRunningActivities()
public static function getNotStartedActivities()
public static function getEndedActivities()
public static function getDisplayableActivities()
public static function isValidActivityStatus(LotteryActivity $activity)
public static function isValidLotteryType(LotteryActivity $activity)
// ============ 用户机会管理方法 ============
public static function getUserChances($activityId, $userId)
// ============ 抽奖算法和辅助方法 ============
private static function executeLotteryAlgorithm($prizes)
private static function buildWinInfo($prize)
private static function buildPrizeValue($prize)
private static function updateActivityStats($activity, $isWin)
private static function buildDrawResult($drawRecord, $prize, $winRecord)
}
功能: 执行抽奖的核心方法,包含完整的抽奖流程验证和处理。
参数:
$activityId
(int): 活动ID$userId
(int): 用户ID$triggerType
(int): 触发类型,默认1$triggerOrderId
(int|null): 触发订单ID$triggerAmount
(float|null): 触发金额返回值: array
抽奖结果
异常: Exception
各种验证失败异常
使用示例:
try {
$result = LotteryService::drawLottery(1, 123, 1, 456, 100.00);
echo "抽奖成功: " . json_encode($result);
} catch (Exception $e) {
echo "抽奖失败: " . $e->getMessage();
}
抽奖流程:
三种抽奖方式处理:
功能: 私有方法,处理抽奖的核心业务逻辑。
流程:
功能: 基于概率权重的抽奖算法实现。
算法原理:
使用示例:
$prizes = LotteryService::getValidPrizes($activityId);
$selectedPrize = LotteryService::executeLotteryAlgorithm($prizes);
功能: 根据活动的开奖方式(lottery_type)分流到不同的处理逻辑。
流程:
createParticipationRecord()
创建参与记录lottery_type
分流处理:
LOTTERY_TYPE_INSTANT
→ executeInstantDraw()
LOTTERY_TYPE_TIME
→ handleTimeLottery()
LOTTERY_TYPE_PEOPLE
→ handlePeopleLottery()
功能: 所有抽奖方式的统一入口,负责消耗用户机会并创建抽奖记录。
流程:
DRAW_STATUS_PARTICIPATED
)功能: 立即执行抽奖并返回结果。
流程:
使用场景: 适用于需要立即反馈结果的抽奖活动。
功能: 处理用户参与定时开奖活动。
流程:
功能: 公开方法,供定时任务调用,批量处理到期的定时开奖活动。
流程:
executeScheduledDraw()
功能: 为指定活动执行定时开奖处理。
流程:
DRAW_STATUS_PARTICIPATED
)executeDrawForRecord()
功能: 为单个抽奖记录执行开奖逻辑。
流程:
定时任务配置示例:
# 每分钟检查一次定时开奖活动
* * * * * /usr/bin/php /path/to/project/think lottery:process-scheduled
功能: 处理按人数开奖的抽奖活动。
流程:
lottery_people_num
)返回信息:
使用场景: 适用于需要聚集一定人数才开奖的活动,增加参与积极性。
const DRAW_STATUS_PARTICIPATED = 1; // 已参与(等待开奖)
const DRAW_STATUS_WIN = 2; // 已中奖
const DRAW_STATUS_NO_WIN = 3; // 未中奖
DRAW_STATUS_PARTICIPATED
DRAW_STATUS_WIN
或 DRAW_STATUS_NO_WIN
DRAW_STATUS_NO_WIN
功能: 检查奖品库存是否充足。
参数:
$prize
(LotteryPrize): 奖品对象$quantity
(int): 需要数量,默认1返回值: bool
库存是否充足
功能: 减少奖品库存并增加中奖次数。
参数:
$prize
(LotteryPrize): 奖品对象$quantity
(int): 减少数量,默认1返回值: bool
操作是否成功
功能: 获取奖品中可用的兑换码。
参数:
$prize
(LotteryPrize): 奖品对象返回值: string|null
可用兑换码或null
功能: 将兑换码标记为已使用状态。
参数:
$prize
(LotteryPrize): 奖品对象$code
(string): 兑换码返回值: bool
操作是否成功
功能: 获取活动的有效奖品列表(库存大于0且状态正常)。
参数:
$activityId
(int): 活动ID返回值: Collection
奖品集合
功能: 检查奖品是否已按人数解锁。
参数:
$prize
(LotteryPrize): 奖品对象$currentPeopleCount
(int): 当前参与人数返回值: bool
是否已解锁
功能: 检查活动是否处于进行中状态。
参数:
$activity
(LotteryActivity): 活动对象返回值: bool
是否正在进行
功能: 检查活动是否已结束。
参数:
$activity
(LotteryActivity): 活动对象返回值: bool
是否已结束
功能: 检查活动是否还未开始。
参数:
$activity
(LotteryActivity): 活动对象返回值: bool
是否未开始
功能: 检查活动是否已暂停。
参数:
$activity
(LotteryActivity): 活动对象返回值: bool
是否已暂停
功能: 检查活动是否已取消。
参数:
$activity
(LotteryActivity): 活动对象返回值: bool
是否已取消
功能: 检查当前时间是否在活动的抽奖时间范围内。
参数:
$activity
(LotteryActivity): 活动对象返回值: bool
是否在抽奖时间内
功能: 获取所有正在进行的抽奖活动。
返回值: Collection
活动集合
功能: 获取所有未开始的抽奖活动。
返回值: Collection
活动集合
功能: 获取所有已结束的抽奖活动。
返回值: Collection
活动集合
功能: 获取可显示的活动(排除逻辑状态)。
返回值: Collection
活动集合
功能: 验证活动状态是否有效。
参数:
$activity
(LotteryActivity): 活动对象返回值: bool
状态是否有效
功能: 验证开奖方式是否有效。
参数:
$activity
(LotteryActivity): 活动对象返回值: bool
开奖方式是否有效
功能: 获取用户在指定活动中的抽奖机会信息。
参数:
$activityId
(int): 活动ID$userId
(int): 用户ID返回值: array
机会信息
返回格式:
[
'total_chances' => 10, // 总机会数
'used_chances' => 3, // 已使用机会数
'remain_chances' => 7, // 剩余机会数
'last_get_time' => 1234567890, // 最后获得时间
'last_use_time' => 1234567890 // 最后使用时间
]
功能: 验证用户是否符合活动参与资格。
验证内容:
功能: 检查用户等级是否符合限制条件。
功能: 检查用户标签是否符合限制条件。
功能: 检查用户是否已达到参与次数上限。
功能: 检查用户是否已为指定订单进行过抽奖。
功能: 构建中奖记录的详细信息。
功能: 根据奖品类型构建奖品价值信息。
功能: 根据奖品类型自动发放奖品。
功能: 发放优惠券类型的奖品。
功能: 发放红包类型的奖品。
功能: 发放兑换码类型的奖品。
功能: 发放商城商品类型的奖品。
功能: 更新活动的抽奖和中奖统计。
功能: 构建返回给客户端的抽奖结果。
// 用户抽奖
try {
$result = LotteryService::drawLottery(1, 123);
if ($result['is_win']) {
echo "恭喜中奖!奖品:" . $result['prize']['name'];
} else {
echo "很遗憾,未中奖";
}
} catch (Exception $e) {
echo "抽奖失败:" . $e->getMessage();
}
$activity = LotteryActivity::find(1);
if (LotteryService::isActivityRunning($activity)) {
echo "活动正在进行中";
} elseif (LotteryService::isActivityEnded($activity)) {
echo "活动已结束";
} elseif (LotteryService::isActivityNotStarted($activity)) {
echo "活动未开始";
}
$chances = LotteryService::getUserChances(1, 123);
echo "剩余抽奖机会:" . $chances['remain_chances'];
$prize = LotteryPrize::find(1);
if (LotteryService::hasPrizeStock($prize, 1)) {
echo "奖品库存充足";
} else {
echo "奖品库存不足";
}
活动不存在或未开始
- 活动状态验证失败不在抽奖时间内
- 时间验证失败用户不符合参与条件
- 用户资格验证失败没有抽奖机会
- 机会不足已达到参与次数上限
- 次数限制该订单已参与过抽奖
- 重复参与操作太频繁,请稍后再试
- 并发控制暂无可抽取的奖品
- 奖品库存不足奖品库存不足
- 库存验证失败抽奖机会使用失败
- 机会使用失败