322 lines
11 KiB
PHP
322 lines
11 KiB
PHP
<?php
|
||
|
||
namespace app\czg\app\controller;
|
||
|
||
use app\api\model\CommonInfo;
|
||
use app\api\model\DiscSpinningRecord;
|
||
use app\api\model\Orders;
|
||
use app\api\model\TbUser;
|
||
use app\api\model\TbUserBlacklist;
|
||
use app\common\controller\Frontend;
|
||
use app\common\library\DatabaseRoute;
|
||
use app\exception\CzgException;
|
||
use app\exception\SysException;
|
||
use app\queue\redis\DiscReceiveQueue;
|
||
use extend\ba\Random;
|
||
use think\db\exception\DuplicateException;
|
||
use think\facade\Db;
|
||
use support\Log;
|
||
class DiscSpinningController extends Frontend
|
||
{
|
||
protected array $noNeedLogin = ['draw'];
|
||
|
||
protected array $noNeedPermission = [];
|
||
|
||
public function initialize(): void
|
||
{
|
||
parent::initialize();
|
||
}
|
||
|
||
/**
|
||
* 抽奖
|
||
*/
|
||
public function draw()
|
||
{
|
||
$params = $this->request->get();
|
||
$userInfo = $this->auth->getUser();
|
||
Log::info('抽奖'. json_encode($userInfo));
|
||
|
||
debounce("user:disc-spinning:interval:limit:user:".$userInfo['user_id'], 2);
|
||
$userId = $userInfo['user_id'];
|
||
$resp = [];
|
||
runWithLock("user:disc-spinning:limit:user:lock:".$this->getUserId(), 60, function () use ($params, $userId, &$resp, $userInfo) {
|
||
DatabaseRoute::transactionXa(function () use ($params, $userId, &$resp, $userInfo) {
|
||
try {
|
||
// 查询抽奖次数
|
||
$count = DiscSpinningRecord::countDraw($userId);
|
||
// 免费两次之后校验实名
|
||
if ($count == 2 && !TbUser::checkEnable($userInfo)) {
|
||
$this->error('请实名认证后继续抽奖');
|
||
}
|
||
|
||
Log::info("用户id: $userId, 抽奖次数: $count");
|
||
// 订单抽奖
|
||
if (!isset($params['source']) || $params['source'] == 1) {
|
||
|
||
$drawCount = (new CommonInfo())->getByCodeToInt(901);
|
||
if ($count >= $drawCount) {
|
||
$this->error('当日可抽奖次数已超限');
|
||
}
|
||
|
||
// 校验订单笔数
|
||
$orders = Orders::selectOrdersByDay($userId);
|
||
if (!$orders) {
|
||
throw new SysException("无可用抽奖机会");
|
||
}
|
||
$params['sourceId'] = $orders['orders_id'];
|
||
}else{
|
||
$this->error("八嘎");
|
||
}
|
||
|
||
if (!isset($params['sourceId'])) {
|
||
throw new CzgException("异常请求");
|
||
}
|
||
|
||
$draws = self::draws($count + 1, $orders['pay_money'], $params['sourceId'], $userId, $params['source']);
|
||
$resp = $draws;
|
||
// $this->receive($draws);
|
||
pushQueue(DiscReceiveQueue::class, [
|
||
'draws' => $resp
|
||
]);
|
||
}catch (DuplicateException $e) {
|
||
(new TbUserBlacklist())->addBlackUser($userId, "重复抽奖");
|
||
throw $e;
|
||
}
|
||
|
||
});
|
||
});
|
||
|
||
$resp = convertToCamelCase($resp);
|
||
$resp['img'] = $resp['img_url'] ?? '';
|
||
$this->successWithData($resp);
|
||
}
|
||
|
||
public static function draws($drawCount, $orderAmount, $sourceId, $userId, $source)
|
||
{
|
||
$result = [
|
||
'name' => '谢谢惠顾',
|
||
'source_id' => $sourceId,
|
||
'user_id' => $userId,
|
||
'img_url' => '',
|
||
'type' => 1,
|
||
'number' => 1,
|
||
'draw_day' => date('Y-m-d'),
|
||
'create_time' => date('Y-m-d H:i:s'),
|
||
'source' => $source == 1 ? 'order' : 'task'
|
||
];
|
||
|
||
// 查询奖项
|
||
$prizes = Db::name('disc_spinning')->where([
|
||
'disc_type' => $source
|
||
])->order('number', 'asc')->select()->toArray();
|
||
|
||
if (empty($prizes)) {
|
||
DatabaseRoute::getDb('disc_spinning_record', $userId, true, true)->insert($result);
|
||
return $result;
|
||
}
|
||
|
||
// 获取最大概率
|
||
$maxNumber = array_reduce($prizes, function ($carry, $prize) {
|
||
return bccomp($prize['number'], $carry) > 0 ? $prize['number'] : $carry;
|
||
}, '0');
|
||
|
||
// 最大值为 0,直接谢谢惠顾
|
||
if (bccomp($maxNumber, '0') === 0) {
|
||
$record = $result;
|
||
DatabaseRoute::getDb('disc_spinning_record', $userId, true, true)->insert($record);
|
||
return $record;
|
||
}
|
||
|
||
// 获取随机数(整数)
|
||
if ($maxNumber > 1) {
|
||
$randomNum = rand(0, (int)$maxNumber - 1);
|
||
} else {
|
||
$randomNum = rand(0, (int)$maxNumber); // 或者其它默认值
|
||
}
|
||
|
||
// 查询奖励金额列表(模拟 redis 逻辑)
|
||
$amounts = Db::name('disc_spinning_amount')->where([
|
||
'status' => 1,
|
||
'type' => $source
|
||
])->order('max_amount', 'asc')->select()->toArray();
|
||
|
||
// 按 num 分组
|
||
$amountMaps = [];
|
||
foreach ($amounts as $item) {
|
||
$num = isset($item['num']) ? intval($item['num']) : 0;
|
||
$amountMaps[$num][] = $item;
|
||
}
|
||
|
||
// 按照 drawCount 向下查找匹配组
|
||
$filteredAmounts = [];
|
||
if (!empty($amountMaps)) {
|
||
for ($i = $drawCount; $i >= 0; $i--) {
|
||
if (isset($amountMaps["{$i}"])) {
|
||
$filteredAmounts = $amountMaps["{$i}"];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 抽奖逻辑
|
||
$finalPrize = null;
|
||
foreach ($prizes as $prize) {
|
||
if (bccomp((string)$randomNum, $prize['number']) < 0) {
|
||
if ($prize['type'] == 2) {
|
||
// 金额类奖品
|
||
$maxAmount = (new CommonInfo())->getByCodeToInt(900); // 最大金额限制
|
||
$baseRandom = mt_rand() / mt_getrandmax();
|
||
$baseRandom = bccomp($baseRandom, "1", 2) ? 0.99 : $baseRandom;
|
||
$baseAmount = 0;
|
||
$resultAmount = 0;
|
||
|
||
foreach ($filteredAmounts as $amount) {
|
||
if ($baseRandom < $amount['random']) {
|
||
$resultAmount = $baseAmount + (mt_rand() / mt_getrandmax()) * $amount['max_amount'];
|
||
break;
|
||
}
|
||
$baseAmount = $amount['max_amount'];
|
||
}
|
||
|
||
if ($resultAmount < 0.01) {
|
||
$resultAmount = 0.01;
|
||
}
|
||
$randomFactor = mt_rand(50, 90) / 100; // 生成 0.5 到 0.9 的随机数
|
||
$resultAmount = $resultAmount * $randomFactor;
|
||
$resultAmount += $orderAmount;
|
||
if ($resultAmount > $maxAmount) {
|
||
$resultAmount = $maxAmount;
|
||
}
|
||
|
||
|
||
$finalPrize = [
|
||
'name' => $prize['name'],
|
||
'type' => 2,
|
||
'number' => round($resultAmount, 2),
|
||
'id' => $prize['id'],
|
||
'url' => $prize['url'] ?? ''
|
||
];
|
||
break;
|
||
} else {
|
||
// 非金额奖品
|
||
if ($source != 1) {
|
||
$finalPrize = [
|
||
'name' => $prize['name'],
|
||
'type' => $prize['type'],
|
||
'number' => 1,
|
||
'id' => $prize['id'],
|
||
'url' => $prize['url'] ?? ''
|
||
];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!$finalPrize) {
|
||
// 没抽中任何奖品,默认“谢谢惠顾”
|
||
$finalPrize = [
|
||
'name' => '谢谢惠顾',
|
||
'type' => 1,
|
||
'number' => 1,
|
||
'id' => null,
|
||
'url' => ''
|
||
];
|
||
}
|
||
|
||
// 构建记录
|
||
$record = [
|
||
'id' => Random::generateRandomPrefixedId(19),
|
||
'name' => $finalPrize['name'],
|
||
'source_id' => $sourceId,
|
||
'user_id' => $userId,
|
||
'img_url' => $finalPrize['url'] ?? '',
|
||
'type' => $finalPrize['type'],
|
||
'number' => $finalPrize['number'],
|
||
'draw_day' => date('Y-m-d'),
|
||
'create_time' => date('Y-m-d H:i:s'),
|
||
'source' => $source == 1 ? 'order' : 'task'
|
||
];
|
||
|
||
// 保存
|
||
DatabaseRoute::getDb('disc_spinning_record', $userId, true)->insert($record);
|
||
|
||
$record['discSpinningId'] = $finalPrize['id'];
|
||
return $record;
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* 获取大转盘抽奖次数
|
||
*/
|
||
public function drawCount()
|
||
{
|
||
$get = $this->request->get();
|
||
if(empty($get['source'])) {
|
||
$soure = 1;
|
||
}else {
|
||
$soure = $get['source'];
|
||
}
|
||
$user = $this->auth->getUser();
|
||
$drawCount = (int)CommonInfo::where(['type' => 901])->find()->value;
|
||
$data['sum'] = $drawCount;
|
||
if(!empty($soure) && $soure != 1) {
|
||
if($soure == 2) {
|
||
$sourceType = 'taskW';
|
||
}else {
|
||
$sourceType = 'taskM';
|
||
}
|
||
$spinningCount = DatabaseRoute::getDb('disc_spinning_record', $user['user_id'])->where(['source' => $sourceType])->count();
|
||
if($spinningCount != null && $spinningCount > 0) {
|
||
$data['count'] = 0;
|
||
}else {
|
||
$i = DiscSpinningRecord::countTaskDisc($user['user_id'], $soure);
|
||
$data['count'] = $i>0?1:0;
|
||
}
|
||
}else {
|
||
$i = DatabaseRoute::getDb('disc_spinning_record', $user['user_id'])->where(['source' => 'order'])->where([
|
||
'draw_day' => ['between', date('Y-m-d 00:00:00'), date('Y-m-d 11:59:59')]
|
||
])->count();
|
||
if($drawCount - $i > 0) {
|
||
$data['count'] = DiscSpinningRecord::selectOrdersCountStatisticsByDay($user['user_id'], $drawCount - $i);
|
||
}else {
|
||
$data['count'] = 0;
|
||
}
|
||
}
|
||
$this->success('ok', $data);
|
||
}
|
||
|
||
|
||
// 查询大转盘
|
||
public function selectDiscSpinning()
|
||
{
|
||
$get = $this->request->get();
|
||
if(empty($get['source'])) {
|
||
$this->error('参数不完整');
|
||
}
|
||
$page = 1;
|
||
$limit = 20;
|
||
$db = Db::connect(config('think-orm.search_library'));
|
||
$list = $db->name('disc_spinning')
|
||
->where(['disc_type' => $get['source']]);
|
||
$count = $list->count();
|
||
$list = $list->order('disc_type', 'asc')
|
||
->order('odds', 'asc')
|
||
->limit(page($page, $limit), $limit)
|
||
->select()->toArray();
|
||
foreach ($list as $k => &$v) {
|
||
$v['odds'] = null;
|
||
$v['number'] = null;
|
||
$v['discType'] = null;
|
||
$v['img'] = $v['url'];
|
||
}
|
||
$this->success('ok', [
|
||
'currPage' => $page,
|
||
'pageSize' => $limit,
|
||
'records' => convertToCamelCase($list),
|
||
'totalCount' => $count,
|
||
'totalPage' => ceil($count / $limit),
|
||
]);
|
||
}
|
||
} |