本文講解內容
針對兩類發獎需求的四種抽獎邏輯及細節
一般H5抽獎活動的發獎需求分為
1.一定中獎(獎品庫存不空的情況下)
2.不一定中獎
發獎介面的最終實現要求
1.獎品不超發
2.唯一獎品單次發放
3.對併發有一定的限制
介面實戰
1.根據獎品開放時間進行抽獎
public function award($openid)
{
$award = Award::find()->where([`openid` => ``])
->andWhere([`>`, `open_at`, 0])->andWhere([`<`, `open_at`, time()])
->orderBy(`open_at ASC`)->limit(1)->one();
if (!empty($award)) {
$res = Award::updateAll(
[
`openid` => $openid
],
`code = :code AND openid = :openid`,
[
`:code` => $award[`code`],
`:openid` => ``
]
);
if ($res) {
return ArrayHelper::toArray($award);
}
}
return [];
}
這種方式,多使用者併發情況下,會出現多個使用者相同獎品,由於update語句限制,拿到相同獎品碼的使用者中只有一人能中得獎品。
2.在開放時間的基礎上加上型別概率
public function randAward($openid)
{
$number = rand(0, 100);
$type = 5;
if ($number < 10) {
$type = 1;
} else if ($number < 30) {
$type = 2;
} else if ($number < 70) {
$type = 3;
} else if ($number < 80) {
$type = 4;
}
$award = Award::find()->where([`openid` => ``])
->andWhere([`>`, `open_at`, 0])->andWhere([`<`, `open_at`, time()])
->andWhere([`type` => $type])
->orderBy(`open_at ASC`)->limit(1)->one();
if (!empty($award)) {
$res = Award::updateAll(
[
`openid` => $openid
],
`code = :code AND openid = :openid`,
[
`:code` => $award[`code`],
`:openid` => ``
]
);
if ($res) {
return ArrayHelper::toArray($award);
}
}
return [];
}
這種方式,也會出現多個使用者相同獎品,但加上type限制後,使用者被分散在各個型別中,未中獎概率會比上面的例子低。
3.利用Redis獎品池的概念進行發獎
public function redisAward($openid)
{
try {
$redis = Yii::$app->redis->client();
$code = $redis->LPop(self::AWARD_LIST_KEY);
} catch (Exception $err) {
return [];
}
$res = Award::updateAll(
[
`openid` => $openid
],
`code = :code AND openid = :openid`,
[
`:code` => $code,
`:openid` => ``
]
);
if ($res) {
$award = Award::find()->where([`code` => $code])->limit(1)->one();
return ArrayHelper::toArray($award);
}
return [];
}
這種利用預先生成獎品池的方式,獎品池不空的情況下,每個使用者都會取走不同獎品碼,要注意的是 前期生成獎品池及後期操作獎品池時,防止獎品碼複用
4.根據獎品開放時間(型別)進行抽獎,換成用sql語句進行發獎
public function sqlAward($openid)
{
$sql = "UPDATE award SET openid = :openid
WHERE open_at > 0 AND openid = `` AND open_at < :time
ORDER BY open_at ASC LIMIT 1";
$res = Yii::$app->db->createCommand($sql, [`:time` => time(), `:openid` => $openid])->execute();
if ($res) {
return Award::find()->where([`openid` => $openid])->limit(1)->asArray()->one();
}
return [];
}
一定中獎需求下,建議採用Redis獎品池或者sql語句進行update
以上四種方式在多使用者併發的情況下帶來不一樣的結果
除了多使用者併發,還會出現惡刷情況,就是同一使用者併發請求
這種情況應該在真正進入抽獎邏輯之前進行限制
可以根據實際需求搭配以下方式進行限制
public function actionAward()
{
$openid = `okjkLj7-UL4gXknY9bDkFn0O6Jos`;
$redis = Yii::$app->redis->client();
// 使用者單次數
if (!$redis->sAdd(self::USER_LIST_KEY, $openid)) {
return [];
}
return $this->sqlAward($openid);
}
也可以限制抽獎人數
public function actionAward()
{
$openid = CommonTool::randString(32);
try {
$redis = Yii::$app->redis->client();
// 抽獎使用者數量
$list = $redis->sMembers(self::USER_LIST_KEY);
if (count($list) > 1000) {
return ;
}
} catch (Exception $err) {
}
$award = $this->sqlAward($openid);
}
寫在最後的最後
H5活動抽獎介面需要注意幾點
1.檢查使用者有效性
2.限制單使用者訪問次數
3.使用概率讓使用者分流,從而控制真正進入抽獎邏輯的請求
4.記錄抽獎領獎等相關操作的時間裝置IP等..
5.控制獎品的分佈(時間,插空,概率等)
6.做好索引關係