积分抽奖是非常常用的一个模块,里面涉及的业务非常复杂,如果需要对接ERP还需要对数据做事务同步等一系列除了,今天主要讲"独占锁"机制,核心是:一个用户点击立即抽奖进行表行级锁定。废话不多说上效果:
上代码:乐观锁与悲观锁的生动举栗讲解
public function lottery()
{
$list = PondSetting::findOne(['store_id' => $this->store_id]);
if ($list['start_time'] > time() || $list['end_time'] < time()) {
return [
'code' => 1,
'msg' => '活动已结束或未开启'
];
}
if ($list->type == 1) {
$start_time = strtotime(date('Y-m-d 00:00:00', time()));
$end_time = $start_time + 86400;
} elseif ($list->type == 2) {
$start_time = $list['start_time'];
$end_time = $list['end_time'];
} else {
return [
'code' => 1,
'msg' => '参数错误'
];
}
$log = PondLog::find()
->where(['store_id' => $this->store_id, 'user_id' => $this->user_id])
->andWhere(['>', 'create_time', $start_time])
->andWhere(['<', 'create_time', $end_time])
->count();
if ($log >= $list['oppty']) {
return [
'code' => 1,
'msg' => '机会已用完'
];
}
if($list->deplete_register > 0){
$user = User::findOne(['id' => $this->user_id, 'store_id' => $this->store_id]);
if($user->integral < $list->deplete_register){
return [
'code' => 1,
'msg' => '积分不足',
];
}
}
$pond = Pond::find()->where(['store_id' => $this->store_id])->all();
$succ = array();
$err = array();
foreach ($pond as $k => $v) {
if ($v->type != 5) {
if ($v->stock > 0) {
$succ[$v->id] = $v->stock;
}
} else {
$err[$v->id] = $v->id;
}
}
// $rand = mt_rand(0,10000);
$rand = $this->random_num(1, 10000);
$max = array_sum($succ);
if (empty($err)) {
if ($max > 0) {
$id = $this->get_rand($succ);
} else {
return [
'code' => 1,
'msg' => '网络异常'
];
}
} else {
if ($rand < $list['probability'] && $max > 0) {
$id = $this->get_rand($succ);
} else {
$id = array_rand($err, 1);
}
}
$form = Pond::findOne([
'store_id' => $this->store_id,
'id' => $id
]);
$pondLog = new PondLog;
$pondLog->store_id = $this->store_id;
$pondLog->user_id = $this->user_id;
$pondLog->type = $form->type;
$pondLog->num = $form->num;
if ($form->type == 1) {
$pondLog->num = 0;
$pondLog->price = floatval($form->price);
}
$pondLog->status = 0;
$pondLog->pond_id = $id;
$pondLog->coupon_id = $form->coupon_id;
if ($form->type == 4) {
$pondLog->attr = $form->attr;
}
$pondLog->gift_id = $form->gift_id;
$pondLog->create_time = time();
$t = \Yii::$app->db->beginTransaction();
if ($form->type != 5) {
$sql = 'select * from ' . Pond::tableName() . ' where store_id = ' . $this->store_id . ' and id = ' . $id . ' for update';
$pond = \Yii::$app->db->createCommand($sql)->queryOne();
//判断库存是否大于0
if ($pond['stock'] > 0) {
//将库存数量减1
$form->stock = $pond['stock'] - 1;
if (!$form->save()) {
$t->rollBack();
return [
'code' => 1,
'msg' => '网络异常'
];
}
} else {
$pondLog->type = 5;
if (empty($err)) {
return [
'code' => 1,
'msg' => '网络异常'
];
} else {
$id = array_rand($err, 1);
};
$pondLog->pond_id = $id;
$pondLog->coupon_id = 0;
$pondLog->attr = '';
$pondLog->num = 0;
$pondLog->gift_id = 0;
$pondLog->price = 0;
}
}
$msg = '成功';
if($list->deplete_register > 0){
$num = (int)$list->deplete_register;
$user = User::findOne(['id' => $this->user_id, 'store_id' => $this->store_id]);
//判断是否对接erp会员积分
if ($user->is_card_user == 1) {
//推送erp会员积分
$integralResult = pushIntegrals($this->store_id, $this->user_id, $num * -1, 17, '九宫格抽奖消费');
if (empty($integralResult) || $integralResult['code'] != 0) {
return [
'code' => 1,
'msg' => $integralResult ? $integralResult['msg'] : '积分不足'
];
}
}
$register = new Register();
$register->store_id = $this->store_id;
$register->user_id = $this->user_id;
$register->register_time = '..';
$register->addtime = time();
$register->continuation = 0;
$register->type = 17;
$register->integral = $num * -1;
//$register->save();
$user->integral -= $num;
if($user->integral < $num){
$msg = '积分不足';
}
if($user->integral < 0){
return [
'code' => 1,
'msg' => '积分不足'
];
}
if(!$user->save()){
$t->rollBack();
return $this->getErrorResponse($user);
}
if(!$register->save()){
$t->rollBack();
return $this->getErrorResponse($register);
}
}
if ($pondLog->save()) {
$t->commit();
$array = [
'oppty' => $list['oppty'] - $log - 1,
'id' => $id,
'p_id' => $pondLog->id,
];
return [
'code' => 0,
'msg' => $msg,
'data' => (object)$array
];
} else {
return $this->getErrorResponse($pondLog);
}
}