services->setting->addons->get($data['mall_id'], SudokuEnum::ADDONS_NAME, 'basic'); //统计会员剩余次数,和积分是否足 $user_wallet_model = UserWallet::findOne(['user_id' => $data['user_id']]); $data['score'] = $user_wallet_model['score']; $lock_key = RedisKeyEnum::suffix(RedisKeyEnum::LOCK_SUDOKU , $data['user_id']); $identification = uniqid(); try { if (!LockHelper::lock($lock_key, $identification, 30, 60)) throw new Exception('当前访问人数过多'); //检验活动时间 if(strtotime($config['start_at']) > time()) throw new Exception("活动未开始"); if(strtotime($config['end_at']) < time()) throw new Exception("活动已结束"); $this->checkUser($config,$data); //检验奖品是否都有效 $prizeList = $this->checkprize($data); //计算中奖 $prize_id = $this->computeWin($prizeList); //添加抽奖纪录 $res = $this->savePrizeLog($prize_id,$data,$prizeList,$config); LockHelper::ulock($lock_key,$identification); return $res; } catch (Exception $e) { LockHelper::ulock($lock_key,$identification); $this->setError($e->getMessage()); return false; } } //抽奖纪录 public function savePrizeLog($prize_id,$data,$prizeList,$config){ $trans = Yii::$app->db->beginTransaction(); try { $prize_data = null;// 中奖的奖品 $return['msg'] = ''; $think_data = [];// 谢谢参与奖品初始化 foreach ($prizeList as $k => $v) { if($v['id'] == $prize_id) { $prize_data = $v; } if ($v['type'] == 5) {// 获取一个谢谢参与奖品 $think_data = $v; } } // 如果获取ID是系统补全的话,则直接返回谢谢参与 if ($prize_id == 0 && empty($prize_data)) { $prize_data = $think_data ?? ['type' => 5, 'id' => 0]; } // 当次奖品属于无效奖品时,则会把谢谢参与奖品给替换上去 if (!empty($prize_data['is_valid'])) { $prize_data = $think_data ? $think_data : ['type' => 5,'id' => 0]; } $savedata['mall_id'] = $data['mall_id']; $savedata['sudoku_id'] = $prize_data['id']; $savedata['user_id'] = $data['user_id']; $savedata['type'] = $prize_data['type']; $savedata['created_at'] = time(); switch ($prize_data['type']){ case '1': //奖励金额 $savedata['status'] = 1; $savedata['receive_at'] = time(); $savedata['red_packet_amount'] = $prize_data['red_packet_amount']; $return['msg'] = '恭喜您获得'.$prize_data['red_packet_amount'].'元'; $this->stock($prize_data['id']);//减去库存 $this->balanceOrScore($data,$prize_data['red_packet_amount'],$prize_data['score'],1); break; case '2': //奖励优惠卷 $savedata['status'] = 1; $savedata['coupon_id'] = $prize_data['coupon_id']; $give_coupon_arr[$prize_data['coupon_id']] = 1; $savedata['receive_at'] = time(); //var_dump($prize_data); $return['msg'] = '恭喜您获得一张'.$prize_data['coupon']['name'].'优惠券'; $this->stock($prize_data['id']);//减去库存 $coupon_res = AddonsCouponUserRelate::giveCoupon($data['mall_id'],$data['user_id'],$give_coupon_arr,AddonsCouponEnum::SEND_FROM9); if ($coupon_res === false) {// 优惠券发放失败则直接返回谢谢参与 $savedata['type'] = 5; $return['msg'] = '谢谢参与'; if (!empty($think_data)) { $savedata['sudoku_id'] = $think_data['id']; $prize_data = $think_data; } } break; case '3': //奖励积分 $savedata['status'] = 1; $savedata['score'] = $prize_data['score']; $savedata['receive_at'] = time(); $return['msg'] = '恭喜您获得'.$prize_data['score'].'积分'; $this->stock($prize_data['id']);//减去库存 $this->balanceOrScore($data,null,$prize_data['score'],2); break; case '4': //奖励商品 $savedata['goods_id'] = $prize_data['goods_id']; $savedata['status'] = 0; $return['msg'] = '获得'.$prize_data['goods']['goods_name'].'商品一件,快去领取吧'; $this->stock($prize_data['id']);//减去库存 break; case '5': //谢谢参与 $savedata['status'] = 1; $savedata['receive_at'] = time(); $return['msg'] = '谢谢参与'; break; } $return['id'] = $prize_data['id']; //抽奖扣除积分 $this->useScore($data,$config['use_point']); //保存记录 if(empty($savedata['red_packet_amount'])) $savedata['red_packet_amount'] = 0; if(empty($savedata['score'])) $savedata['score'] = 0; $this->saveSodokuLog($savedata); $trans->commit(); return $return; } catch (Exception $e) { $trans->rollBack(); $this->setError($e->getMessage()); Yii::error('Sodoku savePrizeLog error:'.$e->getMessage().',line:'.$e->getLine()); return false; } } //减去九宫格库存 public function stock($id){ $sudoku_data = AddonsSudoku::findOne([['id' => $id]]); $sudoku_data->stock = $sudoku_data->stock - 1; $sudoku_data->save(); } //九宫格抽奖消耗积分 public function useScore($data,$score){ $insert_wallet [] = ['mall_id' => $data['mall_id'], 'user_id' => $data['user_id'], 'is_frozen' => false, 'wallet_type' => UserWalletService::WALLET_TYPE_SCORE, 'money' => '-'.$score, 'desc' => '九宫格抽奖消耗积分', 'source_table' => SudokuEnum::ADDONS_NAME, 'source_table_id' => $data['user_id'], 'from_type' => UserWalletEnum::FROM_SUDOKU_DEDUCT, 'capital_type' => UserWalletEnum::DEDUCT, ]; $res = UserWalletService::batchHandleByQueue($insert_wallet); //if ($res === false) throw new \Exception(UserWalletService::getStaticError()); } //赠送余额 public function balanceOrScore($data,$balance,$score,$type){ $insert_wallet [] = ['mall_id' => $data['mall_id'], 'user_id' => $data['user_id'], 'is_frozen' => false, 'wallet_type' => $type==1 ? UserWalletService::WALLET_TYPE_BALANCE : UserWalletService::WALLET_TYPE_SCORE, 'money' => $type==1 ? $balance : $score, 'desc' => $type==1 ? "九宫格抽奖奖励余额" : "九宫格抽奖奖励积分", 'source_table' => SudokuEnum::ADDONS_NAME, 'source_table_id' => $data['user_id'], 'from_type' => UserWalletEnum::FROM_SUDOKU_DEDUCT, 'capital_type' => UserWalletEnum::GIVE, ]; $res = UserWalletService::batchHandleByQueue($insert_wallet); //if ($res === false) return $this->error(UserWallet::getStaticError()); } //保存纪录 public function saveSodokuLog($savedata){ $res = AddonsSudokuLog::setData($savedata); if ($res === false) throw new Exception(AddonsSudokuLog::getStaticError()); return true; } //判断会员资格 public function checkUser($config,$data){ //统计会员剩余次数 if($config['type']==1){ $params['where'][] = ['>','created_at',strtotime(date('Y-m-d'))]; } $params['where'][] = ['=','user_id',$data['user_id']]; $count_times = AddonsSudokuLog::count($params); $limit_times = $config['lucky_number'] - $count_times; if($limit_times<=0){ throw new Exception("次数不足"); } if($config['use_point']>$data['score']){ $score_name = \Yii::$app->services->setting->mall->get($data['mall_id'], 'score.score_name') ?? '积分'; throw new Exception($score_name."不足"); } return true; } //检验奖品 public function checkprize($data){ $params['where'][] = ['mall_id'=>$data['mall_id']]; $params['order'] = 'id asc'; $params['with'] = ['goods','coupon']; $list = AddonsSudoku::lists($params,true); if($list){ foreach ($list as $k => $v){ if($v['stock'] <= 0 || $v['probability'] <= 0){ $list[$k]['is_valid'] = '1'; //没库存,概率0 continue; } if($v['stock'] <= 0 && $v['type'] != 5) $list[$k]['is_valid'] = '1'; //九宫格设定的库存已经消耗完 //判断优惠卷是否有效和是否有库存 if($v['type']==2){ if($v['coupon']){ if($v['coupon']['status'] < 1){ $list[$k]['is_valid'] = '1'; //优惠卷已经失效 } }else{ $list[$k]['is_valid'] = '1'; //优惠卷异常,查询不到结果 } } //判断商品是否有效,否有库存是否售卖 if($v['type']==4){ if($v['goods']){ //var_dump($v['goods']);die; if($v['goods']['status'] == '-1') $list[$k]['is_valid'] = '1';//商品已经下架 if($v['goods']['stock'] <= 0) $list[$k]['is_valid'] = '1';//商品库存不足 if($v['goods']['is_on_sale'] == 0) $list[$k]['is_valid'] = '1';//商品已经下架 }else{ $list[$k]['is_valid'] = '1'; //优惠卷异常,查询不到结果 } } } } return $list; } //计算中奖奖品 public function randomsum($prizeList){ $is_valid_sum = 0; $randomarray = []; //var_dump($prizeList); $num = 100; $thank_id = ''; foreach ($prizeList as $k => $v){ if(!$v['is_valid']){ //有效奖品 $max_value = $v['probability'] * $num; if($max_value>0){ for ($i=0;$i<$max_value;$i++) { $randomarray[] = $v['id']; } } }else{ $is_valid_sum = $is_valid_sum + $v['probability']; } if($v['type'] == 5) $thank_id = $v['id']; } if($is_valid_sum > 0 && $thank_id) for ($i=0;$i<$is_valid_sum * $num;$i++) $randomarray[] = $thank_id; shuffle($randomarray); if(!$randomarray) throw new Exception("您来晚了,奖品已经抽取完"); return $randomarray[array_rand($randomarray,1)]; } /** * 根据概率计算可中奖的奖品 * @Author: lun * @DateTime: 2023/8/22 0022 15:50 * @Copyright: copyright (c) 2021 广东七件事集团 * @param $prizeList * @return int|string */ public function computeWin($prizeList) { $arr = []; foreach ($prizeList as $item) { $arr[$item['id']] = $item['probability']; $list[$item['id']] = $item; } $rid = $this->getRand($arr); return $rid; } /** * 获取中奖奖品id * @Author: lun * @DateTime: 2023/8/22 0022 15:51 * @Copyright: copyright (c) 2021 广东七件事集团 * @param $proArr * @return int|string */ public function getRand($proArr) { if (empty($proArr)) return ''; // 计算总概率 $totalProbability = array_sum($proArr); if (empty($totalProbability)) return ''; $proList = []; // 设置需要生成的数组数 foreach ($proArr as $key => $value) { if (!is_numeric($value)) continue; // 去除小数点概率 $value = intval($value); $valueList = array_fill(0, $value, $key); $proList = array_merge($proList, $valueList); } // 当客户设置概率总和小于100时,补全概率 if (count($proList) < 100) $proList = array_merge(array_fill(0, 100 - count($proList), '0'), $proList); // 打乱数组进行随机抽取 shuffle($proList); $randomNumber = array_rand($proList); if (empty($randomNumber)) return ''; return $proList[$randomNumber]; } }