find(); if (empty($zoneGoods)) return '刮奖失败'; self::$awards_number_count = $zoneGoods['awards_number_count']; // 中奖号码数量 self::$direction = $zoneGoods['direction_count']; // 纵向数量 self::$transverse = $zoneGoods['transverse_count']; // 横向数量 self::$awards_icon_dirname = $zoneGoods['awards_icon_dirname']; // 横向数量 $playRes = ZoneGoodsPlay::find($zoneGoods['play_id']); if (empty($playRes)) return '未设置玩法'; self::$play_code = $playRes['play_code']; $zoneParamArr = self::getZoneParam(['zone_goods_id' => $zone_goods_id]); if (empty($zoneParamArr)) return '未设置中奖参数'; self::$patterns = $zoneParamArr; return true; } /** * 出奖 * @param $user_id * @param $zone_goods_id * @return array */ public static function ticketing($user_id, $zone_goods_id) { # 获取刮奖玩法设定规则参数 try { $initMsg = self::ticketingInit($zone_goods_id, $user_id); if ($initMsg !== true) throw new \Exception($initMsg); # 获取刮奖数据 $resData = self::getTextData(); $save_data = $resData['save_data']; $prizes_data = $resData['prizes_data'] ?? []; $prizes_icon_data = $resData['prizes_icon_data'] ?? []; # 解析是否中奖,组合中奖金额 $awards_amount = self::calculateAmount($save_data); # 处理数据返回 $data = self::handleTextData($save_data); # 结算金额 $settlementRes = self::settlementAmount($awards_amount, $save_data, $prizes_data, $prizes_icon_data); if (!$settlementRes['status']) throw new \Exception($settlementRes['msg']); # 返回客户 刮奖数据 、 消费ID 、 code 玩法模板标识 $returnData = [ 'status' => 1, 'data' => $data, 'c_r_id' => $settlementRes['c_r_id'], 'code' => self::$play_code ]; if (!empty($prizes_data)) $returnData['prizes_data'] = $prizes_data; if (!empty($prizes_icon_data)) $returnData['prizes_icon_data'] = $prizes_icon_data; return $returnData; } catch (\Exception $e) { return ['status' => 0, 'msg' => $e->getMessage()]; } } /** * 获取刮奖参数 * @param $where * @return array * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ protected static function getZoneParam($where = []) { $where['status'] = 1; $list = ZoneGoodsParam::where($where) ->field('id,amount,image,probability,awards') ->order('probability') ->select() ->toArray(); $ZoneAmountParam = []; $sumProbability = 0; foreach ($list as &$item) { if ($item['image']) $item['image'] = get_image_url($item['image']); if ($item['awards'] != 1) { # 获取无奖项随机金额 $res = ZoneAmountParam::getRandAmount($ZoneAmountParam); $item['amount'] = $res['amount']; $ZoneAmountParam = $res['data']; } $sumProbability += $item['probability']; } # 概率未补全则自动补全 归1 if ($sumProbability < 1 && strpos($sumProbability,'.') !== false) { for ($i = 1; $i <= 10; $i++) { $residueProbability = 1 - $sumProbability; $res = ZoneAmountParam::getRandAmount($ZoneAmountParam); $list[] = [ 'id' => 0, 'amount' => $res['amount'], 'image' => rand_icon('icon',true), 'probability' => $residueProbability / 10, 'awards' => 0 ]; } } return $list; } /** * 判断余额是否足够 * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ protected static function judgeBalance() { $userModel = User::field('balance')->find(self::$user_id); $balance = $userModel->balance ?: 0; $zoneGoodsModel = ZoneGoods::field('price')->find(self::$zone_goods_id); $price = $zoneGoodsModel->price ?: 0; if ($balance < $price) { return false; } return true; } /** * 计算奖金 * @param $save_data * @return int|mixed */ protected static function calculateAmount(&$save_data) { $awards_amount = 0; foreach ($save_data as $key => $goodsParam) { foreach ($goodsParam as $k => $item) { $save_data[$key][$k]['is_awards'] = 0; if (isset($item['id'])) { $awardsAmountRes = ZoneGoodsParam::getAwardsAmount($item['id']); if ($awardsAmountRes) { $save_data[$key][$k]['is_awards'] = 1; $awards_amount += $awardsAmountRes['amount']; } } } } return $awards_amount; } /** * 结算金额 * @param $awards_amount * @param $text_data * @param $prizes_data * @param $prizes_icon_data * @return array */ protected static function settlementAmount($awards_amount, $text_data, $prizes_data, $prizes_icon_data) { # 开启事务 $connection = Db::connect(); try { $connection->startTrans(); # 扣除余额 $zoneGoodsModel = ZoneGoods::field('price')->find(self::$zone_goods_id); $price = $zoneGoodsModel['price']; # 消费金额 $consumptionBalance = User::decrBalance(self::$user_id, $price); # 判断是否中奖 $awards_status = 0; # 可提余额 $withdrawal_balance = 0; if ($awards_amount > 0) { $awards_status = 1; # 修改用户可提余额 $withdrawal_balance = User::incrWithdrawalBalance(self::$user_id, $awards_amount); } # 消费订单 $c_r_id = ConsumptionRecords::saveRecords( self::$user_id, self::$zone_goods_id, $price, $price, $consumptionBalance, $text_data, $prizes_data, $awards_status, $prizes_icon_data ); # 消费返点 User::addRebateRatioAmount(self::$user_id, $price, 0, $c_r_id); if ($awards_status == 1) { # 中奖做记录 AwardsRecords::createRecords(self::$user_id, $c_r_id, $awards_amount, $withdrawal_balance); } $connection->commit(); # 消费ID return ['status' => 1, 'c_r_id' => $c_r_id]; } catch (\Exception $e) { $connection->rollback(); return ['status' => 0, 'msg' => '操作失败']; } } /** * 选择玩法返回不同刮奖数据 * @return array */ protected static function getTextData() { $returnArr = ['save_data' => []]; switch (self::$play_code) { // 数字 + 中奖号码 case 'digit_number': # 中奖号码 $prizesFiles = []; # 有奖号码图片 # 获取数字图片 $noPrizeFiles = return_image_name('number_icon'); # 无奖号码图片 # 获取随机中奖数字 踢出所在数组 for ($i = 1; $i <= self::$awards_number_count; $i++) { $rand = rand(0,count($noPrizeFiles)-1); $prizesFiles[] = $noPrizeFiles[$rand]; unset($noPrizeFiles[$rand]); sort($noPrizeFiles); } # 传参给方法使用 self::$param = ['noPrizeFiles' => $noPrizeFiles,'prizesFiles' => $prizesFiles]; # 拼接完整连接 foreach ($prizesFiles as &$file_icon) $file_icon = get_image_url($file_icon); $returnArr['save_data'] = self::probabilityAlgorithm('digitNumber'); $returnArr['prizes_data'] = $prizesFiles; break; // 数字 + 图标 case 'digit_icon': $noPrizeFiles = return_image_name('number_icon'); # 无奖号码图片 foreach ($noPrizeFiles as $key => $value) { if (in_array($value,['number_icon/06.png','number_icon/07.png'])) unset($noPrizeFiles[$key]); } sort($noPrizeFiles); # 传参给方法使用 self::$param = ['noPrizeFiles' => $noPrizeFiles]; $returnArr['save_data'] = self::probabilityAlgorithm('digitIcon'); break; // 数字 + 中奖号码 + 图标 case 'digit_number_icon': # 中奖号码 $prizesFiles = []; # 有奖号码图片 # 获取数字图片 $noPrizeFiles = return_image_name('number_icon'); # 无奖号码图片 # 获取随机中奖数字 踢出所在数组 for ($i = 1; $i <= self::$awards_number_count; $i++) { $rand = rand(0,count($noPrizeFiles)-1); $prizesFiles[] = $noPrizeFiles[$rand]; unset($noPrizeFiles[$rand]); sort($noPrizeFiles); } # 传参给方法使用 self::$param = ['noPrizeFiles' => $noPrizeFiles,'prizesFiles' => $prizesFiles]; $returnArr['save_data'] = self::probabilityAlgorithm('digitNumberIcon'); # 拼接完整连接 foreach ($prizesFiles as &$file_icon) $file_icon = get_image_url($file_icon); $returnArr['prizes_data'] = $prizesFiles; # 获取指定有奖图标 $awards_icon = return_image_name(self::$awards_icon_dirname); $rand_icon = []; $randomKeys = array_rand($awards_icon, 4); foreach ($randomKeys as $key) $rand_icon[] = get_image_url($awards_icon[$key]); $prizes_icon_data = [ [$rand_icon[0],$rand_icon[1]], [$rand_icon[2],$rand_icon[3]] ]; $returnArr['prizes_icon_data'] = $prizes_icon_data; break; // 图标 case 'icon': $returnArr['save_data'] = self::probabilityAlgorithm('icon'); break; default : return []; } return $returnArr; } /** * 概率算法 * $patterns = [ * ['image' => 'prize1.png', 'probability' => 0.3], // 奖项图案,概率为30% * ['image' => 'prize2.png', 'probability' => 0.2], // 奖项图案,概率为20% * ['image' => 'no_prize1.png', 'probability' => 0.1], // 奖项图案,概率为10% * ['image' => 'no_prize2.png', 'probability' => 0.32], // 奖项图案,概率为40% * ]; * @param $function * @param $noPrizeFiles * @param $prizesFiles * @return array */ protected static function probabilityAlgorithm($function) { $patterns = self::$patterns; // 计算概率总和 $totalProbability = 0; foreach ($patterns as $pattern) { $totalProbability += $pattern['probability']; } // 归一化处理,并计算累积概率 $accumulatedProbability = 0; foreach ($patterns as &$pattern) { $pattern['probability'] /= $totalProbability; // 归一化处理 $accumulatedProbability += $pattern['probability']; $pattern['accumulatedProbability'] = $accumulatedProbability; // 累积概率 } unset($pattern); $save_data = []; for ($i = 0; $i < self::$direction; $i++) { for ($j = 0; $j < self::$transverse; $j++) { // 每组生成 3 个图案 $selectedPattern = null; $randomNumber = mt_rand() / mt_getrandmax(); // 生成 0 到 1 之间的随机数 foreach ($patterns as $pattern) { if ($randomNumber <= $pattern['accumulatedProbability']) { $selectedPattern = $pattern; break; } } $save_data[$i][] = self::$function($selectedPattern); } } return $save_data; } /** * 数字+中奖号码 玩法 * @param $selectedPattern * @param $param * @return array */ protected static function digitNumber($selectedPattern) { $noPrizeFiles = self::$param['noPrizeFiles']; $prizesFiles = self::$param['prizesFiles']; $noPrizeCount = count($noPrizeFiles); # 有奖 无奖指定图片 if ($selectedPattern['awards'] == '1') { $tempRand = rand(0,self::$awards_number_count -1); $numberImage = $prizesFiles[$tempRand]; } else { $tempRand = rand(0,$noPrizeCount - 1); $numberImage = $noPrizeFiles[$tempRand]; } $numberImage = get_image_url($numberImage); # 金额拼音 $amount = round($selectedPattern['amount']); $pinyin = PinyinNumber::getPinyin($amount,true); $amount_format = number_format($amount); return [ 'id' => $selectedPattern['id'], 'amount' => $amount_format, 'pinyin' => $pinyin, 'image' => $numberImage ]; } /** * 数字+图标 玩法 * @param $selectedPattern // 概率选中数据 * @param $noPrizeFiles // 无奖数字图标组 * @return array */ protected static function digitIcon($selectedPattern) { $noPrizeFiles = self::$param['noPrizeFiles']; $noPrizeCount = count($noPrizeFiles); $numberImage = $selectedPattern['image']; $amount = round($selectedPattern['amount']); # 无奖替换成 数字icon if ($selectedPattern['awards'] != '1') { $tempRand = rand(0,$noPrizeCount - 1); $numberImage = $noPrizeFiles[$tempRand]; } $pinyin = PinyinNumber::getPinyin($amount,true); $numberImage = get_image_url($numberImage); $amount_format = number_format($amount); $image_url = get_image_url($numberImage); return [ 'id' => $selectedPattern['id'], 'amount' => $amount_format, 'pinyin' => $pinyin, 'image' => $image_url ]; } /** * 数字+中奖号码+图标 玩法 * @return array */ protected static function digitNumberIcon($selectedPattern) { $param = self::$param; return self::digitNumber($selectedPattern); } /** * 图标玩法 * @return array */ protected static function icon($selectedPattern) { $amount = round($selectedPattern['amount']); $pinyin = PinyinNumber::getPinyin($amount,true); $amount_format = number_format($amount); $image_url = get_image_url($selectedPattern['image']); return [ 'id' => $selectedPattern['id'], 'amount' => $amount_format, 'pinyin' => $pinyin, 'image' => $image_url ]; } /** * 刮奖完成 * @param $user_id * @param $c_r_id * @return array * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public static function endTicketing($user_id,$c_r_id) { # 验证 $queryWhere = ['user_id' => $user_id, 'id' => $c_r_id]; $ConsumptionRecords = new ConsumptionRecords(); $query = $ConsumptionRecords->where($queryWhere)->field('text_data,zone_goods_id,status,prizes_data')->find(); if (!$query) return ['status' => 0, 'msg' => '数据不存在']; # 解密 $data = unserialize($query['text_data']); $prizes_data = unserialize($query['prizes_data']); $returnData = self::handleTextData($data); $awards_amount = 0; $AwardsRecordsRes = AwardsRecords::where('cr_id',$c_r_id)->find(); if ($AwardsRecordsRes) $awards_amount = round($AwardsRecordsRes['awards_amount']); # 返回 刮奖数据 、 中奖号码、 中奖总金额、 中奖状态 return ['status' => 1, 'msg' => '完成', 'data' => $returnData, 'awards_amount' => $awards_amount, 'prizes_data' => $prizes_data]; } /** * 处理刮奖信息数据 * @param $data * @param $is_awards // 1-删除 0-不删 * @return mixed */ public static function handleTextData($data,$is_awards = 0) { foreach ($data as $key => $value) { foreach ($value as $k => $v) { #$data[$key][$k]['amount'] = rtrim($v['amount'],'.00'); unset($data[$key][$k]['id']); if ($is_awards) { unset($data[$key][$k]['is_awards']); } } } return $data; } /** * 自动刮奖测试机 * @param $user_id * @param $zone_goods_id * @param $count * @return string|void * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public static function auto($user_id = 1,$zone_goods_id = 1,$count = 500) { # 验证 $user = User::find($user_id); if (!$user) return '用户不存在'; $zoneGoods = ZoneGoods::find($zone_goods_id); if (!$zoneGoods) return '刮奖商品不存在'; $zoneGoodsParam = ZoneGoodsParam::where('zone_goods_id',$zone_goods_id)->where('status',1)->find(); if (!$zoneGoodsParam) return '刮奖奖项不存在'; $balance = $user->balance ?: 0; $sumPrice = $count * $zoneGoods['price']; if ($balance < $sumPrice) return '用户余额不足'; # 开始执行 $limit = 1; while ($limit <= $count) { $result = self::ticketing($user_id,$zone_goods_id); if (!$result['status']) return $result['msg']; # 循环执行 $limit++; } return '完成'; } }