From b5ff54c0e7ae2f5ea61480774fbb59f6557e0a3d Mon Sep 17 00:00:00 2001 From: wanghongjun <1445693971@qq,com> Date: Wed, 22 Nov 2023 15:42:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=88=AE=E5=A5=96=E4=B8=AD=E5=A5=96?= =?UTF-8?q?=E6=A6=82=E7=8E=87=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/logic/ZoneLogic.php | 745 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 745 insertions(+) create mode 100644 app/logic/ZoneLogic.php diff --git a/app/logic/ZoneLogic.php b/app/logic/ZoneLogic.php new file mode 100644 index 0000000..324c19a --- /dev/null +++ b/app/logic/ZoneLogic.php @@ -0,0 +1,745 @@ +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' => self::$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): array + { + # 获取刮奖玩法设定规则参数 + 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 = []): array + { + $where['status'] = 1; + $list = ZoneGoodsParam::where($where) + ->field('id,amount,image,probability,awards') + ->order('probability') + ->select() + ->toArray(); + + $ZoneAmountParam = []; + $sumProbability = 0; + $sumCount = self::$direction * self::$transverse; + + $judgeRateData = self::judgeUserRate(); + self::$judgeRateData = $judgeRateData; + + foreach ($list as &$item) { + # 判断是否有奖 + $item = self::settingNoPrize($item,$judgeRateData,$sumCount); + if (!$item['id']) continue; + + 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']; + } + $item['probability'] = round($item['probability'] / $sumCount,14); + $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(): bool + { + $userModel = User::field('id,aid,balance,withdrawal_balance,rate')->find(self::$user_id); + $balance = $userModel->balance ?: 0; + self::$userData = $userModel->toArray(); + + $zoneGoodsModel = ZoneGoods::field('zone_id,price,direction_count,transverse_count,play_id')->find(self::$zone_goods_id); + $price = $zoneGoodsModel->price ?: 0; + self::$zoneGoodsData = $zoneGoodsModel->toArray(); + +// if ($balance < $price) { +// $withdrawal_balance = $userModel->withdrawal_balance ?: 0; +// $balance = bcadd($balance,$withdrawal_balance,2); +// } + + 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']; + AwardsLog::createLog(self::$zone_goods_id,self::$user_id,$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): array + { + # 开启事务 + $connection = Db::connect(); + try { + + $connection->startTrans(); + + # 扣除余额 + $userData = self::$userData; + $zoneGoodsData = self::$zoneGoodsData; + $price = $zoneGoodsData['price']; + + # 消费金额 + $consumptionBalance = 0; // 余额消费剩余金额 + $balancePrice = 0; // 余额实际支付金额 + $consumptionWithdrawalBalance = 0;// 可提消费剩余金额 + $withdrawalBalancePrice = 0;// 可提余额实际支付金额 + # 余额小于金额 且可提余额能补足 + if ($userData['balance'] < $price) { + + if ($userData['balance'] > 0) { + $balancePrice = $userData['balance']; + $consumptionBalance = User::decrBalance(self::$user_id, $userData['balance']); + } + # 剩余余额 + $residue_price = bcsub($price,$userData['balance']); + $withdrawalBalancePrice = $residue_price; + $consumptionWithdrawalBalance = User::decrWithdrawalBalance(self::$user_id,$residue_price); + } else { + $consumptionBalance = User::decrBalance(self::$user_id, $price); + $balancePrice = $price; + } + + # 判断是否中奖 + $awards_status = 0; + # 可提余额 + $withdrawal_balance = 0; + if ($awards_amount > 0) { + + $awards_status = 1; + + # 修改用户可提余额 + $withdrawal_balance = User::incrWithdrawalBalance(self::$user_id, $awards_amount); + } + + # 消费订单 + $otherData = [ + 'prizes_data' => $prizes_data, + 'awards_status' => $awards_status, + 'prizes_icon_data' => $prizes_icon_data + ]; + # 余额消费记录 - 余额大于0 + if ($userData['balance'] > 0) { + $otherData['actual_type'] = 1; + $c_r_id = ConsumptionRecords::saveRecords(self::$user_id, self::$zone_goods_id, $price, $balancePrice, $consumptionBalance, $text_data, $otherData); + } + # 可提余额消费记录 - 余额小于金额 + if ($userData['balance'] < $price) { + $otherData['actual_type'] = 2; + $c_r_id = ConsumptionRecords::saveRecords(self::$user_id, self::$zone_goods_id, $price, $withdrawalBalancePrice, $consumptionWithdrawalBalance, $text_data, $otherData); + } + + # 消费返点 + 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); + } + + # 使用金额增加 + RechargeRecords::incrUsageAmount([['id', '=', self::$rechargeAmountId]],$price); + + $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 + * @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 = []; + $sumAmount = 0; + 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; + } + } + + # 限制奖金 + if ($selectedPattern['id'] > 0) { + $sumAmount += $selectedPattern['amount']; + } + $selectedPattern = self::restrictAmount($selectedPattern,$sumAmount); + + $save_data[$i][] = self::$function($selectedPattern); + } + } + return $save_data; + } + + /** + * 数字+中奖号码 玩法 + * @param $selectedPattern + * @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 // 概率选中数据 + * @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; + } + + /** + * 判断是否控制出奖金额 + * @return array|int[] + */ + protected static function judgeUserRate():array + { + try { + $userData = self::$userData; + $rate = $userData['rate']; + $residue_amount = 0; + + # 最近一次充值金额 + $lastRechargeAmount = RechargeRecords::lastRechargeAmount([['user_id','=',self::$user_id]]); + self::$rechargeAmountId = $lastRechargeAmount['id']; + + if ($lastRechargeAmount) { + + $recharge_time = $lastRechargeAmount['recharge_time']; // 充值时间 + $recharge_amount = $lastRechargeAmount['recharge_amount']; // 充值金额 + $estimate_awards_amount = round($recharge_amount * $rate,2); // 预估中奖金额 + + # 充值后消费金额 + $AwardsRecords = new AwardsRecords(); + $res = $AwardsRecords->where('user_id',self::$user_id) + ->where('create_time','>=',$recharge_time) + ->field('sum(awards_amount) as sum_amount') + ->find(); + $sum_amount = $res->sum_amount ?: 0; + + # 当前中奖比率 + $residue_amount = round($estimate_awards_amount - $sum_amount,2); + + if ($residue_amount <= 0) { + + # 不能中奖了 + + return ['status' => 1]; + } + + } + + } catch (\Exception $e) { + + } + + return [ + 'status' => 0, + 'residue_amount' => $residue_amount + ]; + } + + /** + * 设置无奖场景 + * @param $item + * @param $judgeRate + * @param $sumCount + * @return mixed + */ + protected static function settingNoPrize($item,$judgeRate,$sumCount) + { + if ($judgeRate['status'] == 1) { + $item['id'] = 0; + $item['awards'] = 0; + } else { + if ($item['amount'] > $judgeRate['residue_amount']) { + $item['id'] = 0; + $item['awards'] = 0; + } else { + //if (($item['amount'] * $sumCount) < $judgeRate['residue_amount']) { + $zoneGoodsData = self::$zoneGoodsData; + $ceilAmount = ceil($item['amount'] / $zoneGoodsData['price']); + if ($ceilAmount >= 5) { + $item['probability'] = $item['probability'] * 2; + }elseif($item['amount'] > $zoneGoodsData['price']) { + $rand = rand_float(0,$item['probability'] * 3); + $item['probability'] += $rand; + } else { + $item['probability'] += rand_float(0,1 - $item['probability']); + } + //} + } + } + return $item; + } + + /** + * 限制奖金 + * @param $selectedPattern + * @param $sumAmount + * @return mixed + */ + protected static function restrictAmount($selectedPattern,$sumAmount) + { + $judgeRateData = self::$judgeRateData; + if (!$judgeRateData['status'] && $sumAmount >= $judgeRateData['residue_amount']) { + $selectedPattern['id'] = 0; + $selectedPattern['awards'] = 0; + } + return $selectedPattern; + } + + /** + * 自动刮奖测试机 + * @param int $user_id + * @param int $zone_goods_id + * @param int $count + * @return string|void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function auto(int $user_id = 1,int $zone_goods_id = 1,int $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; +// if ($user->withdrawal_balance) { +// $balance += $user->withdrawal_balance; +// } + $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 '完成'; + } +} \ No newline at end of file